问题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>
5.1 KiB
5.1 KiB
11 - 实施计划(依次修改)
按用户要求“依次计入 docs/texas 用 md,然后制定计划依次修改”。本文件为执行顺序与每步交付物。 所有改动以不破坏现有单测/类型检查为前提,每步附最小验证。
阶段 0:基础设施(server 房间管理 + 登录后端)
影响全局,先做。对应问题 2、问题 8 后端。
- 2.1 房间内存在线表 + 心跳路由 (
packages/server/src/rooms.ts已实现:presenceMap +POST /api/rooms/:id/heartbeat+ 10min 自动关房 sweep)。 - 2.2 删除房间路由
POST /api/rooms/:id/delete(仅房主),db.wipe+ 清 presence。 - 2.3 踢人路由
POST /api/rooms/:id/kick(仅房主,body{target}):清 metadata + 调游戏内kickmove(engine 层已单测验证 seated=false)。 - 2.4 固化房主 engine
host()改读G.hostId(setup 写入'0')。 - 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 接房间管理 + 登录前端
- 2.5 客户端
LobbyRoomList:房主显示“删除房间”“踢人”按钮;挂载后定时heartbeat(3min);房间被删 →onRoomGone清状态 + 提示。lobby-client 已加roomsInit/heartbeat/delete/kick。 - 8 前端
App.tsx加LoginBar(登录/注册切换,错误回显);token 存localStorage(tts-like.token/tts-like.username);进房时把 token 注入createMatch/joinMatch的Authorization头;登录名作默认playerName(可手动改昵称,改后不再被登录名覆盖);未登录时列表区提示"创建/加入房间需先登录"。lobby-client已加register/login/me方法。 - 单房间约束(用户新需求)
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)
- 1.1 engine 新增
renameMove(沿用playerID信任模型),注册renamemove。 - 1.2 ui
HoldemBoard加 effect:playerName变化且已入座 →onMove('rename', [playerName])。 - 验证:改名后牌桌两处同步刷新(座位名 + 结果区)。
阶段 3:游戏循环(问题 3、4、5)
- 3.1 engine 通用 各游戏加
kickmove(清seated);Holdem 加rebuy/leaveRoom/resetRoom(房主)moves(单测覆盖)。 - 3.2 ui Holdem:筹码≤0 显示“补码/退出”;房主
waiting显示“重置房间”;War/Mill 加“再来一局”(rematch move)。 - 4.1 engine 修
roundCompletelist.length===1分支加acted判定(单测回归)。 - 4.2 engine 加固
buildPots空层结转 orphanCarry 并入下一层,避免退错人(归属权)。 - 4.3 单测
holdem.test.ts扩至 40 例(含 roundComplete、buildPots、kick/rebuy/leave/reset)。 - 5.1 验证测试 下注顺序单测证明:preflop=UTG、postflop=SB、每次加注重置轮询(结论:原代码正确)。
阶段 4:展示细节(问题 6、9、10)
- 6.1 engine
resolveHand摊牌亮所有未弃玩家牌(含 all-in 双方);HoldemBoard 在G.myHand为 null(已清)时回落revealedHands[me],结算后保留自己牌可见。 - 6.2 ui 座位展示 revealedHands(含自己),结算区展示赢家。
- 9.1 ui+engine 滑块
step=5、number 归一 5 的倍数;raiseMove拒绝非 5 倍数(全下除外,单测覆盖)。 - 10.1 ui 座位显示
本手总下 {totalBetThisHand}。
依赖与风险
- 阶段 0 的“踢人改 G”依赖
Masterdispatch:boardgame.ioServer()未直接暴露master, 需确认能否经db+ 自建Master实例触发 move;若不可行,降级为“仅清 metadata + 游戏内 passive 检测 (该座位长时间无动作则自动 seated=false)”。 - 问题 5 大概率为“无需改逻辑,仅文档澄清 + 加测试证据”;若测试暴露
endTurn({next})在ActivePlayers.ALL下异常,则升级为逻辑修复。
不建议现在做(避免过度工程)
- 账号落盘持久化(先用内存,与现有 InMemory 房间一致)。→ 已改为 SQLite 落盘(node:sqlite 内置,无原生编译;房间 metadata 仍为 InMemory,重启即清空,账号跨重启保留)。
- 自动轮回的定时器自动开(用显式“再来一局”按钮,避免无人时误开)。
- OAuth / 第三方登录。