Files
gamegroup2/docs/plans/2026-04-17-game-group-v2-design.md
T

584 lines
15 KiB
Markdown
Raw Normal View 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 核心数据集合
```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