dev-plan-2.md 9.75 KB

换装跳舞直播间 — 第二期开发任务规划

需求见 docs/todo/dressup-dance-room.md(下称"需求文档")第 3 节模式一、 第 5.1 节年龄确认;设计见 docs/todo/dressup-dance-room-mvcp.md(下称 "MVCP")V.1.1/M.8/P.1;架构见 docs/structure/architecture.md(下称 "架构文档")2.1/2.5/2.6 节。第一期(后台三库 + 冷启动批量生成)任务表 见 docs/todo/dev-plan.md,T1-T10 已全部完成并提交 (59c7398/4d437a4/7361786 等)。

2026-09-02 更新:T11-T17(本文档全部任务)已完成并提交 (719fdbb),6/6 测试通过。同一天 T1-T10 也补上了端到端实测(真实 H3 生成,见 dev-plan.md §2.3 的更新说明):1 人物/10 件衣服/3 条 参考视频、11 条真实视频落库,衣柜已经有真实内容可选,不再是空的。 唯一没达成的是 dev-plan.md 原定的"≥30 条"量化标准(M.7 去重缓存 按人物×衣服算,10 件衣服上限就是 10 条不重复视频,要 30 条得配 30 件衣服)——但这不阻塞 T15 的"选衣服→生成→播放"链路,因为那条链路本 来就是按需实时生成(命中缓存的直接播,没缓存的现起 H3 任务),不依赖 提前批量铺好 30 条。

工时是粗估,单位人天,用来排先后不用来考核。任务编号接着第一期的 T1-T10 往下编,避免和 dev-plan.md 混淆。


0. 第二期范围(需求文档 9 节第 2 条)

做:dance.html 上线(含首次进入的年龄确认+免责声明,5.1 节), 接入"衣柜选衣服"免费模式(3.1 节)+ 相同人物+衣服组合去重缓存(4.6 节,M.7 唯一索引已在 T8 落地),复用现状排播队列基础设施思路但要为 dance 频道重新实现一份独立的 schedule/promptQueue(架构文档 2.1/2.5 节:两条频道各自维护自己的时刻表,不是共享一份)。此阶段全部免费内容 仍按 v1 现有投票机制排序,不涉及"付费插队"这条新规则(那是 priority: paid_boost,架构文档 2.6 节,第五期才有)。

明确不做(留到后续分期,见需求文档 9 节 3-5):

  • 模式二(付费传衣服)、模式三(付费传人物)——两者都要求账号+积分体系 先落地(第三/四期),本期不做上传+审核+扣积分链路。
  • 用户注册/登录(sessions 完整实现,M.10)——本期沿用现有匿名 viewer_uid cookie,跟 live.html 一致(架构文档 2.7 节:"免费模式一 不强制登录")。
  • 积分、pricing_config、充值、退款(第四期)。
  • blacklist(M.11)人工拉黑表、人脸黑名单审核(第四/五期;模式一本身 不接受用户上传内容,风险面本来就比模式二/三小得多,可以先不接这道 口子)。
  • admin-playback.html(插播/置顶/暂停填充,6.5 节)——锦上添花,不是 第二期出口条件,见第 4 节"明确不在第二期范围"。
  • fal.ai 接入(第五期)。

1. 依赖关系

flowchart TD
  T11[T11 dance 频道独立播出状态机] --> T15[T15 POST /api/dance/pick]
  T12[T12 GET /api/dance/wardrobe] --> T16[T16 衣柜 tab 前端接线]
  T15 --> T16
  T13[T13 dance.html 页面骨架] --> T16
  T13 --> T14[T14 年龄确认弹窗]
  T11 --> T17[T17 点赞/在线人数]
  T13 --> T17

  classDef ready fill:#d4edda,stroke:#28a745
  class T11,T12,T13,T14 ready

T11/T12/T13/T14 互不依赖,可以并行开工;T15 需要 T11 的时刻表先能收 videoId 入队;T16 是前端接线,需要 T12/T13/T15 都有了才能真正跑通端 到端(但页面骨架本身可以先搭起来,用假数据联调);T17 依赖 T11(复用它 的 tick)和 T13(页面上要有展示位)。


2. 任务表

# 任务 依赖 验收标准 工时
T11 dance 频道独立播出状态机:新增 dance-state.json({ schedule, promptQueue },结构参照 live-state.json 但按 MVCP M.8 的要求——schedule 条目存 videoId 引用,不内联整份 clip 快照,读取时 join library.json);ensureSchedule/tick 逻辑参照 v1(server.js 现有 schedule/promptQueue/ensureSchedule),改成只从 library.json 里 channel === "dance" 的 clip 里选片 T1(已完成,读写模式复用)、T8(已完成,产出的 clip 已带 channel:"dance") 独立于 live.html 的 schedule 运行,互不干扰;进程重启后时刻表从 dance-state.json 恢复,不会因为读到旧的内联 clip 快照而跟 library.json 当前数据不一致(这是 M.8 强调的坑);新增 GET /api/dance/live/state,返回结构镜像 /api/live/state(current/upNext/queue) 1.5
T12 GET /api/dance/wardrobe:公开只读接口,列出 garments 里 isActive === true 的条目(复用 dressup-store.js 的 garments store,不需要 admin 鉴权——这是观众端接口) T4(已完成) 下架的衣服不出现在返回列表里;无需登录/cookie 也能拿到 200 0.3
T13 dance.html 页面骨架(MVCP V.1.1):顶部条(Logo/在线人数占位)+ 左侧衣柜 tab(先只做这一个 tab,传衣服/传人 tab 留空占位,第三/四期再接)+ 播放器容器按 9:16 设计(不是 live.html 现有横向容器拉伸)+ 右侧点赞/排队展示(无文字输入框)+ 移动端底部 tab 切换 无 页面能独立打开(不依赖登录);播放器容器在桌面和一个典型移动端视口下都不出现大黑边;跟 live.html 视觉语言一致(同一套 style.css/组件风格) 1.5
T14 首次进入年龄确认弹窗(5.1 节):非模态强制(不能通过点击背景关闭,必须点"已满18岁"才能继续),确认后写 cookie(不需要登录态,架构文档 2.7 节:免费模式一不强制登录,这条确认走 v1 昵称弹窗同款的纯前端 cookie 记忆套路),刷新页面不再弹 T13 首次进入必弹;确认一次后关闭浏览器再打开(cookie 还在)不再弹;显式展示一句免责声明文案 0.3
T15 POST /api/dance/pick {garmentId}(P.1 流程):取当前 characters 里 isDefault === true 的那条(T3 已支持"只有一条默认"),调用 T8 已实现的 submitGeneration({characterId, garmentId})(server.js,已支持 M.7 去重缓存);命中缓存或生成完成后,把 videoId 推进 T11 的 dance promptQueue(而不是止步于"落进 library.json"——T8 原本只做到落库,这里要接上"排入队列"这一步,是 T8 的直接延伸) T11 提交一件下架的衣服(isActive:false)返回 400;同一衣服两个不同 cookie 各提交一次,第二次是缓存命中、秒级返回并排入队列(不重新起 H3 任务);进程重启后,仍在 generating 状态的 pick 请求能续上(复用 T8 resumeDressupGenerations 的机制,不用重写) 1
T16 衣柜 tab 前端接线:调 T12 拿列表渲染缩略图网格,点击调 T15 提交,提交后在"生成中"区域展示一条状态(复用 live.html 现有"生成中"列表的交互思路——promptQueue 里 status 驱动 UI,不是重新设计一套状态展示);播放器轮询 T11 的 /api/dance/live/state 按 endAt 精确切片(同 live.html 现有逻辑) T12, T13, T15 选一件衣服 → 出现"生成中"提示 → 完成后自动进入播放队列,不需要手动刷新页面;两个浏览器 tab 看到的"当前播放"一致(服务端权威时钟,架构文档 1.2 节这条铁律在 dance 频道也要成立) 1
T17 点赞 + 在线人数(dance 频道独立计数):复用 v1 POST /api/queue/vote/:id/在线人数统计的逻辑,但作用域限定在 dance 频道自己的 promptQueue/viewer 心跳(不能跟 live.html 共用同一份计数,否则"在线人数"会把两个频道的观众混在一起) T11, T13 点赞数变化影响 dance 免费队列的排序(v1 现有投票机制,本期不做付费插队);live.html 和 dance.html 分别打开,两边"在线人数"独立变化,互不叠加 0.5

总工时约 6.1 人天,T11/T12/T13/T14 四条(约 3.6 人天)可以完全并 行;T15-T17(约 2.5 人天)依赖前面几条但彼此间也有一定并行空间(T15 后端 和 T16 前端骨架可以同时开工,最后再联调)。


3. 第二期出口

  • dance.html 能独立打开,首次进入弹年龄确认,确认后不再弹;
  • 衣柜 tab 能看到后台已上架的衣服(依赖第一期出口"批量生成 ≥30 条" 先落地,不然衣柜是空的,见本文档开头的提示);
  • 免费选一件衣服 → 提交 → 排入独立于 live.html 的播出队列 → 播放;
  • 同一件衣服被多个观众选中时,第二次起命中缓存,秒级排队,不重新起 GPU 任务;
  • 点赞/在线人数在 dance 频道内正常工作,且跟 live.html 的计数互不 干扰;
  • 全程不涉及登录、积分、上传、审核。

达成后才进第三期(用户注册体系,见需求文档 9 节第 3 条)。


4. 明确不在第二期范围

避免执行时范围蔓延:

  • 模式二/三(付费传衣服/传人)的任何后端或前端部分(第三/四期,需要先 有账号+积分)
  • 用户注册/登录 sessions 完整实现(第三期)
  • 积分、pricing_config、充值、退款(第四期)
  • blacklist(M.11)CRUD 后台页面、人脸黑名单审核(第四/五期)
  • admin-playback.html 插播/置顶/暂停填充(6.5 节,非本期出口条件, 有余力可顺手做但不阻塞验收)
  • "我的作品"页面(7.4 节,依赖账号体系,第五期)
  • fal.ai 接入、付费插队排播优先级(priority: paid_boost,第五期)