dev-plan-5.md 16.4 KB

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

需求见 docs/todo/dressup-dance-room.md(下称"需求文档")第 3.3 节模式 三、第 5 节内容审核、第 7.4 节"我的作品"、第 9 节第 5 条;设计见 docs/todo/dressup-dance-room-mvcp.md(下称 MVCP)M.3/M.7/M.8/M.9/M.11/ M.13;架构见 docs/structure/architecture.md(下称架构文档)2.3/2.6/2.7 节。第一(T1-T10)、第二(T11-T17)、第三(T18-T22)、第四期(T23-T30) 均已完成并提交。任务编号接着往下编。

工时是粗估,单位人天,用来排先后不用来考核。


0. 第五期范围(需求文档 9 节第 5 条)

做:付费传人物(模式三)+ 自动人脸黑名单审核 + 人物生命周期(购买 N 个 10 秒片段 / 播完自动下线 / 记录保留可续播)+ 排播优先级改造(付费 新人物插队播放,不参与投票)+ 个人中心"我的作品"页面 + 接入 fal.ai 实时算力路径(4.3 节双算力策略,这一条同时补上第四期 T27 遗留的一个 缺口——见下方 §0.1)+ 后台播放管理增强(插播/置顶/跳过/暂停填充/ 拉黑)。

0.1 一个需要先说清楚的事:T27(模式二)从未真正接入 fal.ai

dev-plan-4.md 当时的 T27 验收标准里写了"fal.ai 路径:图片先转发到 fal 可达的存储…再提交生成",但实际完成的 T27 直接复用了 submitGeneration ——这条路径始终只走 H3 Console,没有走 fal.ai。也就是说现在付费 传衣服(模式二)用户跟免费衣柜用户排的是同一条约 700s 的生成队列, 没有拿到需求文档 4.3 节承诺的"付费=fal.ai 实时速度(约 4s 级别)"。

原因:.env 里从未配置过 FAL_API_KEY(fal API key 目前仍只在 newtask.html 前端手填,架构文档 1.2 节早就点名这是技术债),没有凭证 就没法真正调用/验证 fal.ai。这不是"忘了写",是"当时没有可用凭证,选 了功能完整但慢的路径先跑通"——跟 T24 处理 Stripe 缺 key 时的思路一致: 先用可工作的路径把主链路跑通,快慢是后续优化。

本期把这条债务一起还上(T33),而不是留到"第六期"——因为模式三本身 也需要走 fal.ai(同一份需求 4.3 节),两条付费路径的实时算力接入天然 应该一起做,不该再拖一次。

同样的约束仍然存在:.env 里没有 FAL_API_KEY。T33 会把 forwardToFalAssets/submitGeneration 的 fal.ai 分支代码写完,但真正 验证需要你提供 fal.ai 的 API key(跟 T24 等 Stripe key 是同一种情况) ——没有 key 之前,T27/T35(模式二/三)会继续退化到走 H3 Console(能跑, 只是慢),不阻塞开发。

明确不做(留到之后再评估,不是本期范围):

  • 支付渠道从 Stripe 拓展到支付宝/微信支付(需求 7.3 节:"需要时再加一 个入口即可",本期不预先做)。
  • "我的作品"页面的第三方实名认证(需求 §8 第 7 条,明确说"看实际运营 风险再评估,不预设一定要做")。
  • 参考跳舞视频/配乐正式版权素材替换(需求 §8 第 3 条,运营层面的素材 采购,不是开发任务)。

1. 已确认的技术决策 + 需要你决定的开放问题

1.1 已确认

  • 人物生命周期字段 schema 第一期就建好了(T1,characters 表的 totalSegments/remainingSegments/status,见 MVCP M.3),本期不需要 改 schema,只是第一次真正用上非 null 的取值。
  • 排播优先级走 M.8 定义的 priorityInbox 通道:ensureDanceSchedule 每个 tick 优先消费 priorityInbox(paid_boost 产物),消费完/为空 才回落到现有"danceQueue 里挑最高票 → 随机去重填充"逻辑(架构文档 2.6 节已确认,本文档只是落地)。这是对现有选片算法的新增分支,不 是重写——pickBestIndexByStatus 那条投票排序路径完全不动,模式一 /二的免费内容继续走它。
  • blacklist(M.11)和"人脸黑名单"是两张概念上独立的东西:前者是 运营手工拉黑的本站实体(用户/资产/IP),后者是模型侧对上传照片本身 的自动识别过滤(moderateFaceBlacklist,T32)。两者都要做,但互不 替代——见 MVCP M.11 的警告。
  • credit_ledger/pricing_config 复用第四期的机制不改:模式三的 spend_character_per_segment 走 pricing.json 里 M.12 早就定义好的 segmentsPerUnit 字段(扣了多少积分换算成几个 10 秒片段),落 credit_ledger 的方式跟模式二完全一样,T23 的 spend/refund/ topup/adjust 四个函数不需要再加新方法。
  • fal.ai 分支是"有 key 就用,没 key 就退化",不是新建一套并行系统: submitGeneration 加一个内部路由:付费请求(模式二/三)且 FAL_API_KEY 已配置 → 走 fal;其余情况(免费模式一、或付费但没配 key)→ 走现有 H3 Console 路径。调用方(T27 现有代码、T35 新代码)不 用感知这条路由逻辑,同 T20/T24 的可插拔函数设计一脉相承。

1.2 需要你决定,但不阻塞开工

# 开放问题 现在怎么办
1 人脸识别黑名单供应商(需求文档 §8 第 4 条,"用什么模型/服务做涉政等敏感人物识别过滤,需要调研候选方案"——目前完全没有候选) 这条我想先跟你确认默认策略,而不是直接照抄 T26 的"无供应商时默认放行":T26 审核的是衣服图片(低敏感度素材,误放行的后果很小);这里审核的是真人人脸+"涉政人物"过滤,误放行的后果是被过滤对象的形象直接进入生成并可能公开播放。默认放行 vs 默认更保守(比如未配置供应商时直接拒绝所有人物上传,模式三先不接受新用户上传,只能等供应商定了再放开)是两种完全不同的产品/风险取舍,我不该替你做这个决定——见下面的问题。等你确认后 T32 的可插拔函数按你选的默认值实现
2 fal.ai API key(FAL_API_KEY) 同 T24 处理 Stripe key 的方式:T33 把 fal.ai 集成代码写完(forwardToFalAssets、submitGeneration 的 fal 分支),真正调用一次 fal.ai 验证需要你提供 key;没有 key 时模式二/三继续退化到 H3 Console(能跑,只是慢,不阻塞其余任务)
3 一步法 prompt 在 fal 模型上是否同样成立(需求文档 §8 第 1 条历史遗留问题:"S1 的一步法/attribute_transfer 写法只在 H3 Console 上验证过,接入 fal 时需要先复核") 这条必须拿到 #2 的 key 后才能真的验证(需要真实调用 fal 的 minimax/h3* 端点跑一次换装跳舞视频,对比 H3 Console 的产出)——T33 会把这条验证步骤写进验收标准,但没有 key 就没法执行,不是代码能自己解决的
4 模式三的具体积分数值(充值多少积分对应几个 10 秒片段,pricing.json 的 segmentsPerUnit) 跟 T23 处理 spend_garment 一样:先填占位数值(比如 segmentsPerUnit: 1 表示每份 spend_character_per_segment 积分买一个片段),机制打好,数值上线前再改

2. 依赖关系

flowchart TD
  T31[T31 人物生命周期字段落地] --> T36[T36 播放消耗配额+到期下线]
  T32[T32 人脸黑名单审核] --> T35[T35 模式三后端]
  T33[T33 fal.ai 集成] --> T35
  T33 --> T27fix[回填 T27: 模式二走 fal 分支]
  T34[T34 排播优先级 paid_boost/priorityInbox] --> T35
  T31 --> T35
  T35 --> T36
  T35 --> T37[T37 dance.html 传人 tab 接线]
  T35 --> T38[T38 我的作品页面]
  T39[T39 播放管理增强 + 后台拉黑]

  classDef ready fill:#d4edda,stroke:#28a745
  class T31,T32,T33,T34,T39 ready

T31/T32/T33/T34/T39 互不依赖,可以完全并行开工;T35 是模式三的核心, 依赖前四者都好(桩/占位实现可以先联调,不必等真实供应商/key);T36 依赖 T31+T35;T37/T38 依赖 T35 的接口稳定。


3. 任务表

# 任务 依赖 验收标准 工时
T31 人物生命周期字段落地:characters 表 totalSegments/remainingSegments/status 从"建好没用"变成"真的读写"——admin.html 人物库列表新增这三列的只读展示(运营排查用);store.js 层不需要改,字段已经在 无 现有 admin_seed 人物这三个字段继续是 null/active(不受影响);能手动构造一条 totalSegments: 3, remainingSegments: 3, status: "active" 的人物记录并在 admin.html 正确显示 0.5
T32 人脸黑名单审核:moderateFaceBlacklist(imagePath) 可插拔函数(同 T26 moderateGarmentImage 的接口形状),默认策略按你在 §1.2 问题 1 的回答实现;每次调用写一条 moderation_log(assetType: character_upload) 你对 §1.2 问题 1 的回答 每次调用都落 moderation_log;未配置供应商时的行为跟你选定的默认策略一致(不是我自己假设的"默认放行") 1
T33 fal.ai 集成:forwardToFalAssets(absPath)(同 T2 forwardToH3Assets 的思路,但目标是 fal 自己的存储/签名机制——需要先读 fal.ai 文档确认它接受本地文件转发的具体方式,不能假设跟 H3 Console 的 /v1/assets 一样);FAL_API_KEY 从 newtask.html 前端手填迁移到服务端 .env(架构文档 1.2 节技术债);submitGeneration 加路由分支:付费请求 + key 已配置 → fal,否则 → 现有 H3 Console 路径 无(但真实验证依赖 §1.2 问题 2 的 key) 无 key 时:所有生成请求行为跟现在完全一致(H3 Console,回归测试见 T27/dance-channel 的既有测试不能红);有 key 时(需要你提供后才能跑这一条):一次真实 fal 调用产出的换装跳舞视频人脸/换装/动作三项跟 H3 Console 产出比对合格(§1.2 问题 3),且落库 backend: "fal" 2
T34 排播优先级改造:dance-state.json 加 priorityInbox(M.8),ensureDanceSchedule 每个 tick 优先消费它(不经过 pickBestIndexByStatus 投票排序),消费完/为空才回落现有逻辑;generated_videos(library.json clip)加 `priority: normal \ paid_boost` 字段 无 写一条 priorityInbox 记录,下一个 tick 必须先播它,即使 danceQueue 里有票数更高的免费内容在排队;现有模式一/二的投票排序行为不受影响(回归:既有 dance-channel.test.js 不能红)
T35 模式三后端:POST /api/dance/upload-character(需登录+emailVerified,同 T27 鉴权规则)——人物照走 T2 上传+EXIF清理 → T32 人脸黑名单审核 → 不通过直接 400 且不扣积分 → 通过则按 pricing_config 的 spend_character_per_segment×segmentsPerUnit 换算 totalSegments(需求 3.3 第 3 点,充多少积分买几个片段)→ T23 扣积分 → 自动打标(复用 T25 describeGarmentImage 的接口模式,新增 describeCharacterImage)→ characters.create({source:"user_paid", ownerUserId, totalSegments, remainingSegments: totalSegments, status:"active", ...}) → 衣服可选(body 带 garmentId 就用它,否则随机挑一件衣柜库服装,需求 3.3 第 4 点)→ 参考跳舞视频固定随机(3.3 第 5 点,复用现有 pickRefDanceVideo)→ 提交生成(T33 fal 优先,回退 H3)→ 产物 priority: "paid_boost" 直接进 T34 的 priorityInbox,不进投票队列 → 生成失败退还积分(同 T27 的 refund_stage2_fail 套路) T31, T32, T33, T34 提交一张人脸照能走完整条链路,产出的人物 totalSegments 跟花的积分对得上(改改 pricing.json 数值验证换算逻辑不是写死的);人脸黑名单拒绝的照片不创建 characters 记录、不扣积分;生成完成后的产物直接进下一个 tick 播放(插队,不用等投票);故意让生成失败能验证到积分退回 2
T36 播放配额消耗 + 到期下线:ensureDanceSchedule(或 markPlayed)播放一条 characterId 对应 source:"user_paid" 人物的 clip 时,该人物 remainingSegments -= 1;归零时 status 置 "expired" 并从排播候选池移除(pickNextDanceClip/去重缓存查询都要跳过 expired 人物),但记录/已生成视频不删除(需求 3.3 第 7 点,架构文档 2.7 节) T31, T35 播放 N 次后(N = 该人物 totalSegments)人物自动从排播池消失,后续免费/其他付费请求不会再抽中它;characters.json 里这条记录和它名下的 generated_videos 全部还在,只是 status: expired;再充值续播(T38 之外的后续能力,这里只要求"已生成视频还能被找到复用,不需要重新生成"这条底线成立) 1
T37 dance.html"传人" tab 接线(当前占位"开发中敬请期待",同 T28 套路):未登录/未验证先引导登录;选图 + 可选衣服 + 展示"充值 N 积分 = M 个 10 秒片段"换算提示(读 T33 前实现的 /api/pricing)→ 二次确认 → 调 T35 → "生成中"状态展示 T35 未登录点"传人"先弹登录;确认框显示的换算跟 pricing_config 一致;提交后能看到生成中状态,完成后直接插播而不是进普通排队列表(跟模式一/二的"进队列"视觉上要有区分,用户能感知到自己是付费插队) 1.5
T38 "我的作品"个人中心页面(需求 7.4 节):新页面(如 my-works.html)展示当前登录用户名下的 user_paid 人物/衣服及其生成视频,status: expired 的人物依然可见可查(不是软删除后不可访问,需求 7.4 节明确要求) T35 登录用户能看到自己上传的人物/衣服列表;某个人物片段耗尽变 expired 后,仍能在这个页面看到它和它的历史视频;未登录访问跳去登录 1.5
T39 播放管理增强 + 后台拉黑(需求 6.5 节):admin.html/dance.html 后台加"插播/置顶下一条""手动跳过当前""暂停自动填充"控制;新增 POST /api/admin/blacklist(M.11,`targetType: user\ character\ garment\ generated_video\

总工时约 10.5 人天。T31/T32/T33/T34/T39 可以完全并行(约 6 人天), 关键路径是 T32+T33+T34→T35→T36/T37/T38,约 6.5 人天。


4. 第五期出口

  • 第一~四期全部行为不受影响(免费模式一、付费模式二、账号、积分/ 充值继续照常工作);
  • 上传一张人脸照片能走完整条模式三链路:人脸黑名单审核 → 按积分换算 片段数扣费 → 自动打标 → 生成 → 插队播放(不经过投票);
  • 人物播完购买的片段数后自动下线,记录/视频保留,用户能在"我的作品" 页面继续查看;
  • fal.ai 真实凭证到位后(你提供 key),模式二/三的付费请求实测走 fal.ai 实时路径,等待时间从 H3 Console 的约 700s 降到 fal 的约 4s 级别(这条是"到位后验证",key 不到位不算出口条件未达成——跟 T24 处理 Stripe 一样,功能链路完整跑通即算完成,真实供应商验证是独立 的收尾步骤);
  • 管理员能手工拉黑用户/资产/IP,命中的请求在提交入口直接拒绝;能插播 /跳过/暂停自动填充。

达成后第五期即完成需求文档 9 节列出的全部五期范围;后续(人脸识别真实 供应商选型落地、参考视频版权素材替换、支付渠道拓展、"我的作品"实名 认证)都是运营/采购/风险评估层面的收尾,不再是本项目待开发的功能缺口。


5. 明确不在第五期范围

  • 支付宝/微信支付等 Stripe 之外的支付渠道;
  • "我的作品"页面的第三方实名认证;
  • 参考跳舞视频/配乐正式版权素材采购与替换(运营任务,后台字段已预留);
  • 多人物同时设为"直播默认人物"(第一期已确认的"全库最多一条"限制不 在本期重新评估)。