№ · 任务清单 CH.07.07
主页 · 📃 任务清单 · 2026-07-13-to-14-p2p-realtime-bidding

2026-07-13-to-14-p2p-realtime-bidding

📅 2026-07-07 · 📦 6.4 KB · 📃 任务清单 · 📝 MD 文档 🆕 14d 🏷️ Buff🏷️ 联机🏷️ 计划🏷️ 拍卖🏷️ 时间

⟦tag:v2|task|2026-07-13-to-14-p2p-realtime-bidding⟧ kind: task id: 2026-07-13-to-14-p2p-realtime-bidding lane: R7 title: P2P 实时竞价 + 反狙击(Day 1-2 主,8h) objective: 收口 W29 5 日计划 R7 lane——把 W28 落地的联机 P2P 房间骨架补成"实时可竞价",含 4 态状态机 + 反狙击 + 最小出价单位 + 最小 UI。 scope: docs/plans/active/2026-07-13-5日开发计划-v1.md § 1, docs/design/active/联机P2P房间骨架-v1.md, docs/specs/engineering/code-structure-splitting-rules-v1.md, src/data/multiplayer/**(只新增), src/utils/multiplayer/**(只新增), src/components/multiplayer-lobby/**(只新增) acceptance:

  • src/data/multiplayer/realtimeBid.ts 落盘,导出 RealtimeBidState / RealtimeBidEvent / RealtimeBidSnapshot 3 个 type
  • src/utils/multiplayer/realtimeBidding.ts 落盘,纯函数支持 4 态状态机 waiting → bidding → antiSnipe → settled 单步推进
  • 反狙击:最后 30s 内出价 → 自动延长 30s(纯函数可测,无 setTimeout 副作用)
  • 最小出价单位:minIncrement = currentBid * 0.05(纯函数,无副作用)
  • 房主 / 客机出价广播:函数签名支持 applyBid(roomState, bidEvent),WebRTC 留 v1.4
  • src/components/multiplayer-lobby/RealtimeBidPanel.tsx 落盘,显示当前 bid + 倒计时 + 出价输入框(只读 UI,真实广播留 v1.4)
  • 单元测试:realtimeBidding.regression.ts 跑 5 场景全过 = ① 正常出价 ② 反狙击触发 ③ 反狙击连续触发(链式) ④ 流拍自然结束 ⑤ 异常 bid 拒绝
  • cmd /c npm run build 通过,npm run lint 无新增错误
  • src/data/auction/** / src/utils/auctionRules.ts / src/components/auction/** 任何代码被改动
  • RealtimeBidStateAuctionState 平级无字段交叉引用
  • src/App.tsx 主导航被改动(只动 multiplayer tab 入口,R10 lane 范围) current state: documented blockers: none next action: 主会话 7.13 上午按本卡 prompt 段派工,短工回执时按 ## 回执模板 段格式 summary:
  • 这是 W29 5 日计划 R7 lane(2026-07-13 Day 1 + 2026-07-14 Day 2 主轴),跟 R9 胜利数值拍板并行
  • 关键路径上接 W28 R3 联机 P2P 房间骨架(已落),下接 R8 5s 流拍 + 断线重连(Day 3 主轴)。
  • 不实现 WebRTC 实装 / 不实现真实网络广播 / 不实现恶意抬价保护 / 不做表情包。
  • 风险:跟 auctionRules.ts 冲突 → 新目录 + RealtimeBidState 平级 type,本卡全程不碰 auction/

prompt(给短工 / 子会话的指令)

角色:[短工]#联机-P2P实时竞价@v1.0 lane:R7 时间窗:2026-07-13 上午 9:00 ~ 2026-07-14 下午 18:00(8h 工作量,跨两天) 承接文档:docs/plans/active/2026-07-13-5日开发计划-v1.md § 1

任务清单(按顺序做)

  1. type 层:src/data/multiplayer/realtimeBid.ts 导出 3 个 type

    • RealtimeBidState:联合类型 'waiting' | 'bidding' | 'antiSnipe' | 'settled'
    • RealtimeBidEvent:{ kind: 'startBid' | 'placeBid' | 'extendAntiSnipe' | 'settle'; payload: { bidderId: string; amount: number; timestamp: number } }
    • RealtimeBidSnapshot:{ state: RealtimeBidState; currentBid: number; currentBidder: string | null; deadlineAt: number; extends: number }
  2. 状态机层:src/utils/multiplayer/realtimeBidding.ts 导出 4 个纯函数

    • applyEvent(snapshot, event): RealtimeBidSnapshot —— 单步推进,严格按 waiting → bidding → antiSnipe → settled 顺序
    • computeMinIncrement(currentBid: number): number —— 返回 currentBid * 0.05
    • shouldAntiSnipe(snapshot, now: number): boolean —— now >= deadlineAt - 30_000state === 'bidding'
    • extendDeadline(snapshot, now: number): RealtimeBidSnapshot —— deadlineAt = max(deadlineAt, now + 30_000),extends += 1
    • 禁止 setTimeout / setInterval / Date.now() 直接调用,全部接受 now: number 参数注入(纯函数)
  3. UI 层:src/components/multiplayer-lobby/RealtimeBidPanel.tsx

    • 接收 snapshot: RealtimeBidSnapshot + onBid: (amount: number) => void props
    • 显示当前 bid + 倒计时(useState + useEffectdeadlineAt - Date.now())
    • 出价输入框:校验 amount >= snapshot.currentBid + computeMinIncrement(snapshot.currentBid),否则禁用提交
    • 4 态反射:waiting 显「等待开始」/ bidding 显「出价中」/ antiSnipe 显「反狙击 +X」/ settled 显「已结束」
    • 接主导航,做真实广播(onBid 回调留给 R10 集成时接线)
  4. 测试层:src/utils/multiplayer/realtimeBidding.regression.ts

    • 5 场景:① 正常出价 ② 反狙击触发 ③ 反狙击连续触发(链式,3 次) ④ 流拍自然结束(now > deadlineAt 无新出价) ⑤ 异常 bid 拒绝(amount < minIncrement 返回原 snapshot)
    • 用项目现有的 node --test 或 vitest 跑通,输出 PASS/FAIL
  5. 验收

    • cmd /c npm run build 通过
    • cmd /c npm run lint 无新增错误(tsc --noEmit 沿用 AGENTS.md § 7.7 旧 26+ 错误不修)
    • 5 场景测试全过

文件边界(强制)

  • ✅ 新增:src/data/multiplayer/realtimeBid.ts
  • ✅ 新增:src/utils/multiplayer/realtimeBidding.ts
  • ✅ 新增:src/utils/multiplayer/realtimeBidding.regression.ts
  • ✅ 新增:src/components/multiplayer-lobby/RealtimeBidPanel.tsx
  • ❌ 不碰:src/data/auction/** / src/utils/auctionRules.ts / src/components/auction/**
  • ❌ 不碰:src/App.tsx 主导航(R10 lane 范围)
  • ❌ 不碰:src/types.ts / src/data/items/** / src/utils/items/** / story/**

不做(明示)

  • ❌ WebRTC 实装(留 v1.4)
  • ❌ 真实网络广播 / WebSocket(留 v1.4+)
  • ❌ 恶意抬价保护 / 反作弊
  • ❌ 表情包 / 弹幕
  • ❌ 历史出价回放

风险 + 缓解

  • auctionRules.ts 5s 流拍语义冲突 → 严格遵守文件边界,新目录平级实现,RealtimeBidStateAuctionState 无任何交叉引用
  • 状态机死锁(比如 settled 后还能 placeBid)→ applyEvent 严格校验当前态,非法转移抛 Error('illegal transition')

回执模板(短工完成时按这个格式回报主会话)

[R7 #联机-P2P实时竞价@v1.0 回执]
- 状态:✅ 完成 / ⚠️ 部分完成 / ❌ 阻塞
- 新增文件:<列出 4 个文件实际路径>
- 5 场景测试:<PASS/FAIL 列表>
- `npm run build`:✅ / ❌
- `npm run lint`:无新增 / 新增 N 个
- 风险 / 阻塞:<如有,列出;如无,写 "无">
- 残余风险:<如有,列出;如无,写 "无">
- 建议给 R8 接力点:<一句话>

(End of R7 task card)