# 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 / 第三方登录。