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

584 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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