# 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 核心数据集合 ```javascript // 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 二期数据集合 ```javascript // 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 三期数据集合 ```javascript // 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 四期数据集合 ```javascript // 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) **目标**: 核心组队功能 - [x] 用户认证(注册/登录) - [x] 状态管理(空闲/工作中/组队中/离开) - [x] 工作时间设定 - [x] 群组管理(创建/加入/退出) - [x] 握手式邀请(发起/接受/拒绝) - [x] 临时小组(创建/解散) - [x] 游戏库(列表/搜索/热门) - [x] 实时状态同步 ### 第二期 **目标**: 预约 + 积分 + 荣誉 + 记忆 - [ ] 预约系统(简化投票) - [ ] 积分系统(获取/记录) - [ ] 荣誉墙(自动授予) - [ ] 多媒体记忆(支持音视频等多媒体文件的上传、下载与在线预览) ### 第三期 **目标**: 财务 + 资产 - [ ] 账目管理(记录/分摊/结算) - [ ] 资产管理(登记/借用/归还) ### 第四期 **目标**: 黑名单 + 竞猜 - [ ] 黑名单系统(举报/审核) - [ ] 竞猜系统(创建/下注/开奖) --- ## 七、项目文件结构 ``` 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 # .env PB_PORT=8090 # PocketBase 端口(可配置) PB_DATA=./pb_data # 数据存储路径 PB_MIGRATIONS=./pb_migrations ``` ### 8.2 前端环境变量 ```env # .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