换装跳舞直播间 — 第二期开发任务规划
需求见 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_uidcookie,跟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,第五期)