问题8 登录系统: - node:sqlite 账号存储(scrypt 哈希 + HMAC 签名 token),preset admin 改为 e2hang/evan1115 - /api/auth/register|login|me,me 返回 room 供重联 - 网关拦截 create/join 需登录(401),列表保持开放 踢人逻辑修正(问题2 延伸): - kickMove 真正释放座位(folded=false, name=null)而非锁死 - war/mill 去掉永久封禁语义 管理员越权:管理员 token 可删任意房间、踢任意房间内的人 单房间约束(用户新需求): - 同一用户同时只能在一个房间,create/join 他房返回 409 - 踢人后若房间仅剩踢人者则自动解散并释放其记录(修复 2 人局踢光对手后被困 409) - 陈旧房间记录兜底清理 UI 清理:移除"改名"按钮与"你的名字"输入框;删除房间按钮仅房主/管理员可见 docs: 新增 12-auth-kick-single-room.md 汇总本会话全部需求/根因/修复/边界测试 Co-Authored-By: Claude (CodeBuddy) <noreply@codebuddy.ai>
49 lines
5.1 KiB
Markdown
49 lines
5.1 KiB
Markdown
# 11 - 实施计划(依次修改)
|
||
|
||
> 按用户要求“依次计入 docs/texas 用 md,然后制定计划依次修改”。本文件为执行顺序与每步交付物。
|
||
> 所有改动以**不破坏现有单测/类型检查**为前提,每步附最小验证。
|
||
|
||
## 阶段 0:基础设施(server 房间管理 + 登录后端)
|
||
> 影响全局,先做。对应问题 2、问题 8 后端。
|
||
|
||
- [x] **2.1 房间内存在线表 + 心跳路由** (`packages/server/src/rooms.ts` 已实现:`presence` Map + `POST /api/rooms/:id/heartbeat` + 10min 自动关房 sweep)。
|
||
- [x] **2.2 删除房间路由** `POST /api/rooms/:id/delete`(仅房主),`db.wipe` + 清 presence。
|
||
- [x] **2.3 踢人路由** `POST /api/rooms/:id/kick`(仅房主,body `{target}`):清 metadata + 调游戏内 `kick` move(engine 层已单测验证 seated=false)。
|
||
- [x] **2.4 固化房主** engine `host()` 改读 `G.hostId`(setup 写入 `'0'`)。
|
||
- [x] **8 后端** `/api/auth/register|login|me`(`packages/server/src/auth-db.ts` + `auth.ts`:node:sqlite 持久化,scrypt 哈希 + 随机盐,HMAC-sha256 签名 token;预设 admin 账号从 env/常量 seed)。`index.ts` 网关拦截"创建/加入房间"未带 token 的请求返回 401,列表/查询接口保持开放。
|
||
|
||
## 阶段 1:通用 UI 接房间管理 + 登录前端
|
||
- [x] **2.5 客户端** `LobbyRoomList`:房主显示“删除房间”“踢人”按钮;挂载后定时 `heartbeat`(3min);房间被删 → `onRoomGone` 清状态 + 提示。lobby-client 已加 `roomsInit/heartbeat/delete/kick`。
|
||
- [x] **8 前端** `App.tsx` 加 `LoginBar`(登录/注册切换,错误回显);token 存 `localStorage`(`tts-like.token`/`tts-like.username`);进房时把 token 注入 `createMatch`/`joinMatch` 的 `Authorization` 头;登录名作默认 `playerName`(可手动改昵称,改后不再被登录名覆盖);未登录时列表区提示"创建/加入房间需先登录"。`lobby-client` 已加 `register/login/me` 方法。
|
||
- [x] **单房间约束(用户新需求)** `rooms.ts` 新增中间件(mountApiPrefix 之前注册):拦截 `create`/`join`/`leave`,同一登录用户同时只能在一个房间——已在别房则 `create`/`join` 返回 **409**(中文提示先离开);`create`/`join` 成功登记 `userRoom`/`roomPlayers`,`leave`/`kick`/`delete` 成功清除对应记录。`/api/auth/me` 现返回 `room`,供客户端重联时权威确认所在房间;客户端 `config` 经 `localStorage` 持久化,刷新即自动重连原房间。`LobbyRoomList` 用 `errMsg` 把 409 文案友好展示。
|
||
|
||
## 阶段 2:改名刷新(问题 1)
|
||
- [x] **1.1 engine** 新增 `renameMove`(沿用 `playerID` 信任模型),注册 `rename` move。
|
||
- [x] **1.2 ui** `HoldemBoard` 加 effect:`playerName` 变化且已入座 → `onMove('rename', [playerName])`。
|
||
- [x] 验证:改名后牌桌两处同步刷新(座位名 + 结果区)。
|
||
|
||
## 阶段 3:游戏循环(问题 3、4、5)
|
||
- [x] **3.1 engine 通用** 各游戏加 `kick` move(清 `seated`);Holdem 加 `rebuy` / `leaveRoom` / `resetRoom`(房主)moves(单测覆盖)。
|
||
- [x] **3.2 ui** Holdem:筹码≤0 显示“补码/退出”;房主 `waiting` 显示“重置房间”;War/Mill 加“再来一局”(rematch move)。
|
||
- [x] **4.1 engine 修 `roundComplete`** `list.length===1` 分支加 `acted` 判定(单测回归)。
|
||
- [x] **4.2 engine 加固 `buildPots`** 空层结转 orphanCarry 并入下一层,避免退错人(归属权)。
|
||
- [x] **4.3 单测** `holdem.test.ts` 扩至 40 例(含 roundComplete、buildPots、kick/rebuy/leave/reset)。
|
||
- [x] **5.1 验证测试** 下注顺序单测证明:preflop=UTG、postflop=SB、每次加注重置轮询(结论:原代码正确)。
|
||
|
||
## 阶段 4:展示细节(问题 6、9、10)
|
||
- [x] **6.1 engine** `resolveHand` 摊牌亮所有未弃玩家牌(含 all-in 双方);HoldemBoard 在 `G.myHand` 为 null(已清)时回落 `revealedHands[me]`,结算后保留自己牌可见。
|
||
- [x] **6.2 ui** 座位展示 revealedHands(含自己),结算区展示赢家。
|
||
- [x] **9.1 ui+engine** 滑块 `step=5`、number 归一 5 的倍数;`raiseMove` 拒绝非 5 倍数(全下除外,单测覆盖)。
|
||
- [x] **10.1 ui** 座位显示 `本手总下 {totalBetThisHand}`。
|
||
|
||
## 依赖与风险
|
||
- 阶段 0 的“踢人改 G”依赖 `Master` dispatch:boardgame.io `Server()` 未直接暴露 `master`,
|
||
需确认能否经 `db` + 自建 `Master` 实例触发 move;若不可行,降级为“仅清 metadata + 游戏内 passive 检测
|
||
(该座位长时间无动作则自动 seated=false)”。
|
||
- 问题 5 大概率为“无需改逻辑,仅文档澄清 + 加测试证据”;若测试暴露 `endTurn({next})` 在 `ActivePlayers.ALL` 下异常,则升级为逻辑修复。
|
||
|
||
## 不建议现在做(避免过度工程)
|
||
- 账号落盘持久化(先用内存,与现有 InMemory 房间一致)。→ **已改为 SQLite 落盘**(node:sqlite 内置,无原生编译;房间 metadata 仍为 InMemory,重启即清空,账号跨重启保留)。
|
||
- 自动轮回的定时器自动开(用显式“再来一局”按钮,避免无人时误开)。
|
||
- OAuth / 第三方登录。
|