Files
gamegroup2/docs/plans/2026-04-17-game-group-v2-design.md
T
congsh 3ae141ba56 fix: member status visibility, team creation improvements, join approval flow
- Fix other members' status not visible due to users collection viewRule restriction
- Fix empty status treated as 'away' instead of 'idle' in membersByStatus
- Auto-set creator to 'in_team' status when creating team session
- Filter current user from idle members invite list
- Fix group store isGroupOwner using pb.authStore instead of localStorage
- Add nginx no-cache headers for index.html
- Add join_requests collection migration and join approval flow
- Update groups collection rules and add requireApproval field
- Add Memory types for Phase 2 planning

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 10:42:11 +08:00

15 KiB
Raw Blame History

Game Group V2 设计文档

日期: 2026-04-17 版本: v2.0 目标: 重构游戏小队约玩系统,简化组队和投票流程,改进多群组隔离性


一、项目概述

1.1 现有问题分析

基于现有 game-group 项目的使用反馈:

  • 组队麻烦 - 原有匹配机制难以避免时间对齐问题,朋友之间闹矛盾会被强制匹配到一起
  • 投票麻烦 - 投票流程过于复杂
  • 隔离性不足 - 只有一个群组,缺乏多群组支持

1.2 设计目标

  • 握手式匹配 - 类似 Steam 邀请机制,自主选择是否加入
  • 多群组支持 - 每人可加入 3-10 个群组(管理员可配置)
  • 临时小组 - 组队后创建临时小组,游戏结束后解散
  • 轻量级后端 - 使用自建 BaaS 服务(PocketBase),部署在 NAS

二、技术架构

2.1 技术栈

层级 技术选型 说明
后端 PocketBase Go 语言,单文件可执行,内置 SQLite
数据库 SQLite 轻量级,适合 NAS 部署
前端 Vue 3 + TypeScript 保持原有技术栈
UI 框架 Element Plus 保持原有技术栈
样式 Tailwind CSS 保持原有技术栈
实时通信 WebSocket PocketBase 内置支持

2.2 部署架构

NAS 服务器
├── PocketBase (端口可配置,默认 8090)
│   ├── SQLite 数据库文件
│   └── 上传文件存储
└── 前端静态文件 (可选 NAS Web 服务器)

三、数据模型

3.1 核心数据集合

// users - 用户
{
  id: string
  username: string
  email: string
  avatar: string
  status: "idle" | "working" | "in_team" | "away"  // 用户状态
  statusNote: string                                // 状态备注/拒绝原因
  maxGroups: number                                 // 可加入群组数(默认5
  workdays: number[]                                // 工作日 [1-7]
  workStartTime: string                             // 工作开始时间 "HH:mm"
  nextWorkTime: number                              // 下次工作时间戳
  points: number                                    // 积分(二期)
  createdAt: date
  updatedAt: date
}

// groups - 群组(长期)
{
  id: string
  name: string
  description: string
  owner: string (userId)
  members: string[] (userIds)
  maxMembers: number
  honors: array (二期 - 荣誉墙)
  createdAt: date
  updatedAt: date
}

// teamSessions - 临时小组(短期)
{
  id: string
  sourceGroup: string (groupId)
  name: string
  gameName: string
  members: string[] (userIds)
  status: "recruiting" | "playing" | "finished" | "dissolved"
  createdAt: date
  dissolvedAt: date
}

// invitations - 邀请记录
{
  id: string
  from: string (userId)
  to: string (userId)
  teamSession: string (teamSessionId)
  status: "pending" | "accepted" | "rejected"
  rejectReason: string
  createdAt: date
  respondedAt: date
}

// games - 游戏库
{
  id: string
  name: string
  platform: "PC" | "PS5" | "Xbox" | "Switch" | "Mobile"
  tags: string[]
  cover: string (url)
  popularCount: number
  createdAt: date
}

3.2 二期数据集合

// appointments - 预约系统
{
  id: string
  groupId: string
  gameName: string
  scheduledTime: date
  initiator: string (userId)
  participants: array
    [{ userId, status: "going" | "maybe" | "not_going" }]
  note: string
  status: "pending" | "confirmed" | "cancelled"
  createdAt: date
}

// pointsHistory - 积分记录
{
  id: string
  userId: string
  groupId: string
  type: "team_joined" | "game_completed" | "appointment_attended" | "abandoned"
  amount: number
  reason: string
  createdAt: date
}

// honors - 荣誉墙
{
  id: string
  userId: string
  groupId: string
  title: string
  description: string
  icon: string
  awardedAt: date
}

// memories - 多媒体记忆(音视频等)
{
  id: string
  groupId: string
  uploader: string (userId)
  title: string
  description: string
  file: string (PocketBase file field)
  fileType: "image" | "video" | "audio" | "other"
  size: number (bytes)
  createdAt: date
}

3.3 三期数据集合

// ledgers - 账目
{
  id: string
  groupId: string
  type: "income" | "expense" | "settlement"
  amount: number
  category: string
  description: string
  createdBy: string (userId)
  relatedMembers: string[] (userIds)
  settled: boolean
  occurredAt: date
  createdAt: date
}

// ledgerShares - 分摊明细
{
  id: string
  ledgerId: string
  userId: string
  shareAmount: number
  paidAmount: number
  status: "pending" | "paid" | "waived"
}

// assets - 资产
{
  id: string
  groupId: string
  name: string
  type: "game_account" | "console" | "equipment"
  credentials: string (加密)
  status: "available" | "borrowed" | "maintenance"
  currentBorrower: string (userId)
  note: string
  createdAt: date
}

// borrowLogs - 借还记录
{
  id: string
  assetId: string
  borrower: string (userId)
  borrowTime: date
  returnTime: date
  conditionNote: string
  status: "active" | "returned" | "damaged"
}

3.4 四期数据集合

// blacklist - 黑名单
{
  id: string
  groupId: string
  reporter: string (userId)
  targetGameId: string
  reason: string
  category: "behavior" | "cheating" | "abandonment" | "other"
  description: string
  evidence: string[] (urls)
  status: "pending" | "approved" | "rejected"
  reviewedBy: string (userId)
  reviewedNote: string
  createdAt: date
  reviewedAt: date
}

// bets - 竞猜
{
  id: string
  groupId: string
  title: string
  type: "winner" | "score" | "custom"
  options: array
    [{ id, name, odds }]
  participants: object (userId -> optionId)
  stakeAmount: number
  status: "open" | "closed" | "settled"
  resultOptionId: string
  createdBy: string (userId)
  closeTime: date
  createdAt: date
  settledAt: date
}

// betParticipations - 下注记录
{
  id: string
  betId: string
  userId: string
  selectedOptionId: string
  stakeAmount: number
  winAmount: number
  status: "pending" | "won" | "lost"
}

四、核心功能设计

4.1 用户状态(中文化)

状态值 显示 图标
idle 空闲 🟢
working 工作中 🔴
in_team 组队中 🔵
away 离开

4.2 握手匹配流程

┌─────────────────────────────────────────────────────────────┐
│                      状态管理与匹配                          │
└─────────────────────────────────────────────────────────────┘

1. 用户设置状态
   用户 → 点击"我空了" → status: idle
   用户 → 设置工作时间 → workSchedule 更新
   定时任务 → 检查 nextWorkTime → 自动变更为 working

2. 查看空闲成员
   进入群组 → 显示 members 中 status=idle 的用户
   实时订阅 → members 状态变更时自动更新列表

3. 发起邀请
   选择空闲成员 → 创建 teamSession (status: recruiting)
   批量创建 invitations (status: pending)
   被邀请者收到实时通知

4. 响应邀请
   接受 → invitation.status: accepted, user.status: in_team
         加入 teamSession.members
   拒绝 → invitation.status: rejected, 填写 rejectReason
         发送者看到拒绝原因

5. 组队完成
   所有受邀者响应 → teamSession.status: playing
   显示临时小组信息

6. 解散小组
   任何人点击"游戏结束" → teamSession.status: dissolved
   所有成员 status 恢复原状

4.3 工作时间自动管理

用户设置:
- workdays: [1,2,3,4,5] (周一到周五)
- workStartTime: "09:00"

系统计算:
- nextWorkTime = 下一个工作日的 09:00
- 定时检查: 当前时间 >= nextWorkTime ? 自动变更为 working
- 手动改状态后: nextWorkTime = 下一个工作日的 09:00

4.4 邀请机制

  • 一对一邀请 - 逐个发起,可连续邀请
  • 拒绝原因隐私 - 只有发起邀请的人能看到拒绝原因
  • 实时通知 - 使用 PocketBase 实时订阅推送邀请

五、前端界面设计

5.1 页面结构

登录/注册页
    ↓
主布局
├── 侧边栏
│   ├── 群组列表
│   ├── 工作时间设置
│   └── 个人状态切换
├── 主内容区
│   ├── 当前群组视图
│   │   ├── 空闲成员列表
│   │   │   └── 每个成员: 头像 + 状态 + "邀请"按钮
│   │   ├── 游戏选择
│   │   └── 我的临时小组
│   │       └── 成员状态 + 游戏结束按钮
│   └── 游戏库页面
│       ├── 游戏列表/搜索
│       └── 热门游戏
└── 通知中心
    └── 邀请通知 (接受/拒绝)

5.2 核心交互

  1. 状态切换

    • 顶部状态指示器,点击切换
    • 工作时间弹窗设置
  2. 邀请流程

    • 点击成员旁"邀请" → 选择游戏 → 创建临时小组 → 发送邀请
    • 已邀请的成员显示"等待响应"
  3. 响应邀请

    • 通知中心弹出邀请卡片
    • 显示:发起人、游戏、其他已加入成员
    • 按钮:接受 / 拒绝(输入原因)
  4. 临时小组

    • 显示所有已加入成员
    • 谁都可以点"游戏结束"解散

六、功能分期

第一期(MVP

目标: 核心组队功能

  • 用户认证(注册/登录)
  • 状态管理(空闲/工作中/组队中/离开)
  • 工作时间设定
  • 群组管理(创建/加入/退出)
  • 握手式邀请(发起/接受/拒绝)
  • 临时小组(创建/解散)
  • 游戏库(列表/搜索/热门)
  • 实时状态同步

第二期

目标: 预约 + 积分 + 荣誉 + 记忆

  • 预约系统(简化投票)
  • 积分系统(获取/记录)
  • 荣誉墙(自动授予)
  • 多媒体记忆(支持音视频等多媒体文件的上传、下载与在线预览)

第三期

目标: 财务 + 资产

  • 账目管理(记录/分摊/结算)
  • 资产管理(登记/借用/归还)

第四期

目标: 黑名单 + 竞猜

  • 黑名单系统(举报/审核)
  • 竞猜系统(创建/下注/开奖)

七、项目文件结构

game-group-v2/
├── backend/
│   ├── pocketbase/
│   │   ├── pb_migrations/     # 数据库迁移文件
│   │   ├── pb_data/           # 数据存储(SQLite
│   │   └── pocketbase         # 可执行文件
│   ├── .env                   # 配置(端口等)
│   └── docker-compose.yml     # 可选:容器化部署
│
├── frontend/
│   ├── src/
│   │   ├── api/
│   │   │   ├── pocketbase.ts  # PB 实例初始化
│   │   │   ├── users.ts       # 用户相关
│   │   │   ├── groups.ts      # 群组相关
│   │   │   ├── sessions.ts    # 临时小组相关
│   │   │   ├── invitations.ts # 邀请相关
│   │   │   └── games.ts       # 游戏库相关
│   │   │   ├── appointments.ts    # 预约(二期)
│   │   │   ├── ledgers.ts         # 账目(三期)
│   │   │   ├── assets.ts          # 资产(三期)
│   │   │   ├── blacklist.ts       # 黑名单(四期)
│   │   │   └── bets.ts            # 竞猜(四期)
│   │   ├── components/
│   │   │   ├── common/        # 通用组件
│   │   │   ├── layout/        # 布局组件
│   │   │   └── team/          # 组队相关组件
│   │   │       ├── StatusToggle.vue      # 状态切换
│   │   │       ├── IdleMembersList.vue   # 空闲成员
│   │   │       ├── InviteButton.vue      # 邀请按钮
│   │   │       ├── InvitationCard.vue    # 邀请卡片
│   │   │       ├── TeamSessionPanel.vue  # 临时小组面板
│   │   │       └── WorkScheduleModal.vue # 工作时间设置
│   │   ├── views/
│   │   │   ├── Login.vue
│   │   │   ├── Register.vue
│   │   │   ├── GroupView.vue      # 群组主页
│   │   │   └── GamesLibrary.vue   # 游戏库
│   │   ├── stores/
│   │   │   ├── user.ts        # 用户状态
│   │   │   ├── group.ts       # 当前群组
│   │   │   └── team.ts        # 当前临时小组
│   │   ├── types/
│   │   │   ├── user.ts
│   │   │   ├── group.ts
│   │   │   ├── session.ts
│   │   │   └── game.ts
│   │   ├── router/
│   │   ├── utils/
│   │   └── App.vue
│   ├── .env                   # API_URL 等配置
│   ├── package.json
│   └── vite.config.ts
│
└── docs/
    ├── design.md              # 本设计文档
    ├── api.md                 # API 文档
    └── migration.md           # 迁移指南

八、配置说明

8.1 后端环境变量

# .env
PB_PORT=8090              # PocketBase 端口(可配置)
PB_DATA=./pb_data         # 数据存储路径
PB_MIGRATIONS=./pb_migrations

8.2 前端环境变量

# .env
VITE_PB_URL=http://your-nas-ip:8090

九、从原项目迁移

9.1 保留的设计

功能 原实现 新实现
用户认证 JWT + NestJS PocketBase 内置
权限管理 自定义 Guard PocketBase API Rules
实时通信 - PocketBase 实时订阅
前端框架 Vue 3 + Element Plus 保持不变

9.2 主要改进

方面 原实现 新实现
匹配方式 自动匹配 握手式邀请
投票 复杂投票 快速响应(去/可能/不去)
群组 单群组 多群组 + 临时小组
数据库 MySQL SQLite
后端 NestJS PocketBase

十、附录

10.1 状态码对照表

类别 显示
用户状态 idle 空闲 🟢
working 工作中 🔴
in_team 组队中 🔵
away 离开
临时小组 recruiting 招募中
playing 游戏中
finished 已结束
dissolved 已解散
邀请 pending 等待响应
accepted 已接受
rejected 已拒绝

10.2 积分规则(二期)

行为 积分
成功组队 +10
完成游戏 +20
准时赴约 +15
放鸽子 -30

10.3 荣誉徽章(二期)

条件 徽章
组队100次 组队达人
连续10次赴约 全勤玩家
积分TOP1 群组之星

文档版本: v1.0 更新日期: 2026-04-17