# QMTV 移植契约规格（PRD）v1

LLMS 索引： [llms.txt](/llms.txt)

---

> **文档定位**：本文是 qmtv 后端 Go+MySQL 移植的**需求契约唯一权威**——以冻结版 Node 实现（`qmtv/server/`，2026-10-17）的**实际行为**为基准逐条登记，外加已裁决的修正项。Go 版若与本文不符，以本文为准；本文与冻结代码不符处=修正项章节。上游设计参照：`qmtv/docs/01-系统设计方案.md`（冻结）。
>
> 后续 FSD 以 Go 实现代码为事实基准反向对账本文；本文修订走版本记录（§0.1）。

## 0.1 修订记录

| 版本 | 日期 | 修订说明 | 作者 |
|------|------|----------|------|
| v1 | 2026-10-17 | 初版：239 端点全清单 + 6 修正项 + 全局契约 | Claude |
| v1.1 | 2026-10-17 | 前端项目立项：新建 `frontend/`（初始复制 qmtv 前端，行为对等），演进轨道 vue-query/vitest/TS（§1 前端行更新 + 新增 §7）；C3/C4/§2.1 token query 表述按冻结实际行为修正（全局兜底，非白名单） | Claude |
| v1.2 | 2026-10-18 | P1 期首核查勘误 + 裁决落册：C4/§2.1 修正为冻结三分事实（v1.1 的「全局兜底、与冻结一致」表述被核查证伪——冻结 authRequired 仅 Bearer/events 纯 query/images/:id query 优先）+ 登记 Go 裁决（FromRequest 保持 Bearer 优先 + query 全局兜底）；F1 兼容影响改写（前端 api.js:24 补丁须同期）；F2 谓词微调；新增 F7（邮箱校验统一）/F8（素材对象存储）；M1 端点数 6→9（补登 logout/credit-records）；§2.2 rr_ 共用注记 | Claude |
| v1.3 | 2026-10-18 | P2 立项定稿：F2 尾注更新（RunWithRetry 实测不引入——CAS 谓词仅 credits>=? 下 affected=0 ⇔ 余额不足，qmtv/pms 双侧零生产调用）；F8 补 file_path 布局差异（ObjectKey 日期子目录 vs 冻结扁平）；新增 F9（退款防重唯一口，修冻结双退款漏洞）；§6 启动恢复注记 P2 范围拆分（workbench 随 M11）；跨聚合扣款时序定稿（§6，不引 actor/outbox 对齐冻结同步流） | Claude |
| v1.3-pending | 2026-10-18 | **W10 对账预告（pending，正文暂不改）**：M2 端点数实测 38（routes.go M2 组实装口径），§3 表 :33/§4 :118/合计 :45 的「66」系 P0 统计残留——W10 收尾时三处一并校准；增量端点逐条清单移 `docs/API-增量.md` 累加 | Claude |
| v1.3-pending | 2026-10-18 | **C1 裁决落地（storage 默认面）**：dev 模板默认直连 MinIO（192.168.50.190:9000+bucket qmtv+public_base，凭据 LAN 实例默认不入生产）；**F8 增记**：MinIO 模式落库 file_path=终态绝对 URL（pathOfFunc 写路径拼 public_base，前端逐字消费；桶/域迁移需一次性 UPDATE file_path），local 模式仍 `/files/` 相对（=冻结形态）——F8 原文「落库相对+出站现场拼」按此修订 | Claude |
| v1.3-pending | 2026-10-18 | **W10 对账预告（M3，pending，正文暂不改）**：M3 端点数实测 **19**（index.js 9+routes2 3+routes3 7），§3 表/§4 标题的「38」系 P0 统计残留——render-all 实属 M4、retry 无用户端点；`/api/models/active` 归属补登 M3（PRD 全文未列该端点，W10 一并补登） | Claude |
| v1.3-pending | 2026-10-18 | **M3 实现差异登记（W4 期首核查 B5/B6 补录，W10 入正文）**：①expires_at=C7 例外全达成（冻结 toISOString() 入库透传；Go ISOTime 响应 ISO，落库仍 DATETIME(3)）②SmartVideo ⑩ 分辨率白名单外值（如 480p）冻结原样透传 vs Go 归一 1080p、ratio null 冻结传 null vs Go 归一 16:9（Go 收紧向，安全）③SSE progress 冻结每次 fetch 前发 vs Go fetch 后发（tries 命中集合相同）④tag 打标价冻结固定 COSTS.image=10 vs Go 随 F6 settings 覆盖价 | Claude |
| v1.3-pending | 2026-10-18 | **M4 实现差异登记（W5 期首核查补录，W10 入正文）**：①history 响应 `v.*` 列序：冻结 created_at 在 error 之后（db.js CREATE 后 ALTER 折叠序）vs Go DDL/struct 排末位——键集一致、前端按键取值无感，严格逐字节对拍不齐；②assets-deliver 条目名 index_no=0：冻结 JS truthy→`asset_` vs Go 原指针非 nil→`shot000_`，W6 期已修对齐（`!= nil && !=0`）；③export repo 接口实为 16 方法（基线「17」系计数漂移） | Claude |
| v1.3-pending | 2026-10-18 | **W6/W8 实现差异登记（期首核查 B2 + W8 集成抽样补录，W10 入正文）**：①W6 逐段容错差异——冻结 videos 段无 catch（异常即崩启动，SQLite 同步 API 副作用非设计）vs Go 统一逐段容错不挡启动；②workbench finished_at 冻结 `datetime('now','localtime')` vs Go `NOW(3)`（方言惯例）；③§6 :196「workbench_tasks 扫描随 M11 落地」已过时——W6 已内联实现（正文 W10 校准一并改）；④W8 集成抽样抓获：task 恢复扫描 `SELECT *` 遇可空列 NULL 整段静默 no-op（Task 宽结构体 5 个非指针可空列），已修窄投影 SplitRecoverRow（语义等价）；同族脆弱仍在 task.Task/Shot 宽扫描面（生产 InsertTask 全列写不可达，人工/迁移行可达）——指针化 vs 维持全列写纪律待裁决；⑤seed Go 版一步到位差异：producer@xingmang.cn 独立账号（referral_code=QMXINGMANG01，与 demo 并存）+ 台账单笔 init（沿 SQL seed 惯例；冻结 demo 为 0 元 register 形态） | Claude |
| v1.3-pending | 2026-10-18 | **A1 落地（W8 十续期首核查，行④同族首例生产可达已裁决根治）**：seed.go 部分列 INSERT 沿冻结 seed.js:46 形态致 producer 示例任务 5 可空列落 NULL——ListByUser/ByID 宽扫描 500（集成实测红坐实）；用户裁决 **DDL 收紧**：delta/0002 tasks 5 列+shots 2 列 UPDATE 回填+MODIFY NOT NULL（MySQL TEXT 无字面 DEFAULT——漏写列 strict mode 显式红=守卫语义）+seed INSERT 补列+full 重拼；**C7 例外不动**：videos.prompt/error 保持 `*string` NULL 透传（冻结语义）；director_shots（M10）/workbench_tasks（M11）待域落地随域裁决；行④「生产不可达」陈述由本行取代；同票：prompt_templates 补种 5 行（对齐冻结 ensureSeed 存储面，Go prompts 内置不接线产物无差） | Claude |
| v1.4 | 2026-10-18 | **W10 对账落地（十一续期首核查后收尾，pending 行 :15-:21 内容全部入正文，行本身保留不改写）**：①端点数校准——M2 66→38、M3 38→19（routes.go 实装亲数口径，storyboard/generate 归 M2 组、models/active 补登 M3、M4 勘误「GET /api/export」实为 `GET /api/export/:id` 轮询面），合计 242→**239**（模块数直加恰合 v1/CLAUDE.md 实测冻结 239——原表直加 286 从未自洽），§3 表新增「Go 已实现」列（基础 3+M1~M4 76=79，M5~M12 全零=未排期）；②M9 归属勘误（scriptlib.js 9+studio.js 16=25，二十五端点并非全在 scriptlib.js）+M9 节 Go 现状注记（404 系排期现状非回归）；③M3/M4 实现差异、W6 容错与 NOW(3) 方言、恢复扫描窄投影、A1 DDL 收紧与 seed 一步到位差异全部入 §4/§5/§6 正文；④正文 pending 字样转正（§2.2 au_ 勘误/F8 定稿）；⑤§6 workbench「随 M11」过时注记修正（W6 已四扫描全落地） | Claude |
| v1.5 | 2026-10-18 | **M9 落地 + uid 前缀全量复核（十三续）**：§3 表 M9 行 Go 已实现 25（合计 79→104）；§2.2 uid 前缀表全量复核回填（冻结 47 前缀亲数——P0 误写 8 处勘误：lib_→sl_/nar_→na_/dp_→dpr_/rch_→ro_/rc_→cm_/wd_→w_/af_→fd_/w_→wk_ 错位，补缺 7 个 an_/ex_/ih_/inv_/me_/oa_/om_，ids_test.go 全量对拍守卫钉死）；M9 实现差异七项入 §4 M9 节（upload 修复语义等）；narrations type='video' 扣 30 走 F9 同源 TryDeduct（taskId 空串=冻结 null） | Claude |
| v1.6 | 2026-10-18 | **十四续期首核查回写（A2B7C2→修复落地）**：§2.2 补登 `pt_`（B1，原 46/47）；§4 M9 差异增 ⑧writers search TrimSpace 保持+登记（B5 用户裁决）/⑨narrations 台账 task_id `''` vs null（B7）——**二十一期⑤收口（0003 列 NULL 化）**：credits 域 INSERT 绑定 `''→NULL` 单点收口，无关联台账行恒 NULL 对齐冻结 `taskId||null`；「admin 8 端点」勘误 9（B3）；seed SQL 佣金/公告字面量对齐冻结前缀 cm_/an_（B2，dev-up 重建时生效） | Claude |
| v1.7 | 2026-10-19 | **十五续（M12 管理后台落地 + 期首核查勘误）**：①期首核查 2026101823（A0B2C0）勘正——API-增量「admin 8」→9 两处（B-新1）、NEXT-SESSION「25 mutation」→24 口径（B-新2，归档段不回改）；②**M12 亲数勘误 36→39**（41 物理 admin 路由 − withdrawals 2 归 M7；instruction-versions 通配展开+DELETE 漏登补齐），§3 合计 239→**242**（v1.3 校准时 M12 残留未清；v1 原合计 242 系巧合重合），根 CLAUDE.md 规模基线行同步；③M12 域落地 `internal/domain/admin` 39 端点（A 用户积分 8/B 生成审计 3/C 公告 3/D 组织审批 3/F 计费配置 4/G 模型接入 6/H 提示词中心 12；死端点 model-settings×2+instruction-versions/active 照抄；ErrNoUser 吞形保冻结信封；api_key 明文回显照抄+安全注记；instruction-versions DELETE 按修后语义打 prompt_templates——冻结查死表 instruction_versions 恒 404，F3 同制登记）；④**存量真 bug 修复**：modelCfg.setting() `key` 未反引号恒 ERROR 1064（P0 起读侧模型配置链 LLMConfig/endpointByPurpose/genBilling.models 全哑，Mock 优先架构掩盖）——集成链「读侧即时生效」断言首证抓获，反引号一处修复；⑤跨域共表写归主域：task.AdminRetrySplit（retry-split 不删根资产=冻结原样）/generation.ClaimManualRelease（CAS+CONCAT(COALESCE) 一步）；E1 面零改动，migrations 53 不变，delta 无 0003 | Claude |
| v1.8 | 2026-10-19 | **十六续（期首核查 A1B2C0 修复 + M7 分销提现落地 + 亲数勘误）**：①期首核查 2026101900（十五续 bc216dd）——A1 ResetPassword 校验顺序与冻结相反（Go 先判长度后查存在 vs 冻结 index.js:140-142 先查存在），用户裁决修序对齐（admin/service.go + service_test 用例先红后绿）；B1 §4 M12 补登记 ⑨providers/publish 反向死调用不实现=echo 404 兜底（上期计划承诺「PRD 登记一句」漏落）；B2 根 CLAUDE.md 勘正日期微疵；②**M7 亲数勘误 12→11**（冻结 index.js :598-:638/:708-:737 亲数恰 11、§4 清单自列即 11——P0 统计残留第四例），§3 合计 242→**241**（上期 239→242 勘误时 M7 残留未清，亲数为准），M7 冻结源文件列勘正（11 条全在 index.js，recharge.js 仅承载返佣写入属 M8 链）、API-增量 合计节与根 CLAUDE.md 基线行同步；③M7 域落地 `internal/domain/distribution` 11 端点全量（integration 第七链 DistributionChain；零 credits 依赖/零 DDL/零 SSE；W_FLOW 严格邻接禁跳转；available「cancelled/rejected 不释放」冻结谎言照抄+集成双断言钉死；settings 双回退 shape/980 硬编码/exchanges 死态 quirks 逐字；差异 ①~⑩ 入 §4 M7 节）；AdminView 提现 tab 404 面转绿（前端零改动）；migrations 53 不变，delta 无 0003 | Claude |
| v1.9 | 2026-10-19 | **十七续（期首核查 A0B2C0 回写 + M8 充值返佣落地 + 亲数勘误）**：①期首核查 2026101903（十六续 c2b2a29）——A 类 0 项；B1「service_test 13 场景」实为 10 测试函数（断言点无缺失，计数漂移，本段起以 10 为权威口径）；B2 integCleanup 未扩 distribution 四表（M8 链落地时收口）；②**M8 亲数勘误 5→8**（recharge.js 亲数恰 8、§4 清单自列即 8——P0 统计残留第五例，§4 清单一直正确而标题/§3 行少算，与 M7 同型反向），§3 合计 241→**244**（十六续 242→241 勘误时 M8 残留未清，亲数为准），根 CLAUDE.md 基线行同步；③M8 域落地 `internal/domain/recharge` 8 端点全量（integration 第八链 RechargeChain：config 对拍/下单 rate 换算/凭证真 multipart 落盘/approve 到账对账+两级返佣 8·2/资格联动 ≥980+已开不重复/reject 双截断/admin list 过滤/403/404；commission_records 唯一写入方收口=M7 差异①；approveOrder 冻结死导出不建恢复链；差异 ①~⑧ 入 §4 M8 节）；integCleanup 扩 distribution 四表+recharge_orders（B2 收口）；RechargeView/AdminView 充值面 404 转绿（前端零改动）；migrations 53 不变，delta 无 0003 | Claude |
| v1.10 | 2026-10-19 | **十八续（期首核查 A1B2C0 修复 + M6 组织协同落地 + 亲数勘误）**：①期首核查 2026101907（十七续 f27cd9a）——A1 recharge service_test 宣称 12 函数实为 11+三处承诺断言面缺失（rel2 缺失正向对拍/SSE payload/round2 小数域），补强至 12 函数（恰合宣称口径，历史段无需回改）+负样本演练两发先红后绿；B1 API-增量 头注「241」漏勘（本期随 235 一并收口）；B2 归档计划「85」旧口径（append-only 登记权威口径，十七续段 82 为准）；②**M6 亲数勘误 38→29**（冻结 index.js 组织段 9+enterprise.js 20=29、§4 清单自列即 29——P0 统计残留第六例，与 M7 同型「标题多算清单对」），§3 合计 244→**235**，根 CLAUDE.md 基线行「四轮亲数勘误」；③M6 域落地 `internal/domain/org` 单包多文件七件 29 端点全量（四零域：零 admin/零上传/零扣费/零 SSE 零通知；join 三写禁事务直译；权限分层 owner 严格 vs adminOrOwner 唯一面；normalizeMaterialPerms 联动纯函数；invite code crypto/rand 8 位大写 base36；integration 第九链 OrgChain：approve 建组织→invite/join→角色三哨兵→dashboard available==balance 钉死→material-perms 联动→showcase 恒 ok→share/search/401；integCleanup 扩组织八表+users.org_id 复位先于删 users，孤儿终验五零；**执行期抓获 service percent 公式错**（×1000 应 ×10——测试先红抓获即修）；gorp 嵌套 struct 平铺教训两发〔Org 平铺/OrgMemRef 补 db tag〕）；OrganizationView 等五视图 404 面转绿（前端零改动）；差异 ①~⑩ 入 §4 M6 节；migrations 53 不变，delta 无 0003 | Claude |
| v1.11 | 2026-10-19 | **十九续（期首核查 A3B4C1 修复 + C1 补投影 + M10 导演与融合落地）**：①期首核查 2026101908（十八续 9cab5dd）——A1 projectRank wire 键 `json:"amount-kind"` 勘误 `kind`（前端 OrganizationView「类型」列恒空白；wire 键集断言守卫自此入惯例：结构体字段断言不走 json tag）；A2「孤儿终验五零」宣称无载体——TestIntegration_IntegCleanupLeavesNoOrphans 守卫落地（负样本注释清理 SQL 先红坐实）；A3 perms_test 补非空无 view 归空正向用例+用例名勘误；B1 测试计数宣称漂移两处（service_test 实 20 函数/perms_test 实 2 函数 14 用例）本段为权威口径；B4 冻结底册 05 手册头注 239 旧口径+/api/api/ 双前缀 9 处记遗留（冻结区不回写）；②**C1 用户裁决补齐 shared-projects script_text 投影**（冻结 dp.* 全列）+**A4 勘正十八续误加谓词**（projectRank `task_id != ''`/shared-projects `video_id != ''`——冻结 IS NOT NULL 对空串放行，「独立生成 / 工具」聚合行曾被整行挡掉；MySQL NOT NULL 列上 IS NOT NULL 即冻结字面）；M6 差异 ⑪ 入 §4（assign 响应 null vs ""）；③**M10 亲数复核 28 清点清白**（director.js 11+fusion.js 17——§4 清单补列 convert/:eid 文字漏列而合计自洽）；④M10 域落地 `internal/domain/director` 单包多文件 28 端点全量（integration 第十链 DirectorChain 扣费对账贯穿；双表通路 generation.ShotView.Tbl 显式传递+DirectorShotVideo；**执行期抓获 InsertShot 冻结字面 0 错位存量 bug**〔F3 同族——MySQL strict 恒 1265，修后语义登记〕+gorp 无字段列投影教训〔AssetJoined 实修〕；splitEpisodes 双阈值/fusion cost=3/convert 裸 pid 等 quirk 逐字）；差异 ①~⑭ 入 §4 M10 节；integCleanup 扩 director 三表+孤儿终验扩七零；§3 Go 已实现 191→**219**（剩 M5 4/M11 12 共 16）；migrations 53 不变，delta 无 0003 | Claude |
| v1.14 | 2026-10-19 | **二十六期（期首核查 A1B3C0 修复 + e2e 基建 + spark 缺口补齐）**：①期首核查 2026101923（二十五期 6ee480f）——A1 shotCount「修后登记」实为回归（GET /tasks wire=驼峰 shotCount〔TaskWithCount json:"shotCount"，冻结 index.js:230 map 增强同源〕，二十五期误改蛇形 t.shot_count 恒显 0 镜；types Task 接口同错；DashboardView.test fixture 键 shotCount 反证；先红后绿修复+grep 守卫入 ci 3.6）+B 类 3 项回写（二十五期段「镜数恒空显」定性错误权威勘误/eslint+ci.sh 头注标签过时/interface 计数 86 口径）；②e2e 基建（Playwright 双配置断言册+截图管线/dev-up env 参数化 qmtv_e2e:3301/e2e-verify 六项自检/docs/test/ 两册/冒烟 8 场景 13 用例全绿）；③**M13 spark 域补齐**（internal/domain/spark 三件+routes 2 条+errRespond 哨兵+service_test/repo_gorp_test/integration 第十六链 SparkChain——e2e 巡检抓获的移植缺口，**P0 统计残留第八例**，合计 237→**239**+根 CLAUDE.md 基线行「八轮亲数勘误」）；④SparkView faqs 死块填充真实文案（用户裁决）；⑤ci 第四档 --e2e（显式 opt-in） | Claude |
| v1.13 | 2026-10-19 | **二十一期（期首核查 A3B4C0 修复 + 质量轨六项全裁落地）**：①期首核查 2026101920（二十续 dd0e1eb）——A1 Mock 评分 content 维整数除法截断（冻结 Math.round）/A2 runEval SSE done 帧 total 恒 0（Mock int 断言丢失）/A3 评估报告生成时间带时区后缀（%v 打 LocalTime 走 time.Time.String()）三处先红后绿修复+B 类回写（API-增量首句 235→237 第三次同型+守卫、PRD M11 ⑧⑭ 补登、stale 注释）；②质量轨六项（用户裁决）：②api_key 明文回显收敛 `json:"-"`（前端零消费实证，M12 ④注记更新）；③Refund 唯一口形态收口（task refundImage/evaluation 两处/admin ReleaseVideo 域外 release Add 归零+grep 守卫）；④Task.ScriptText 指针化（0002 后唯一残余可空列）；⑤**delta 0003** credit_records.task_id NULL 化（credits 域 `''→NULL` 绑定收口，M9 ⑨ 同步终结，打破「无 0003」边界正当推翻）；⑥**delta 0004** 积分/佣金全链 DECIMAL(12,2) 七列（pay 佣金原值入台账亦入余额对齐冻结 REAL 亲和，M8 ①/M7 ⑥ 终结；credits 台账边界 float64 化，billing 价格整数面不动）；⑦promptcenter 模板接线收口（shot_image/shot_video/asset_*×3 单级+eval_deep/eval_quick，split 三级沿 SplitTemplate 勿并；差异⑪/M10 ⑪/M11 ⑤ 终结，集成链「配置即生效」断言）；集成对账 assertBal InDelta 化+⑤ NULL 行端到端断言；守卫③「gorp 禁指针 db 字段」下 Record.TaskID 保持值类型+COALESCE 读侧 | Claude |
| v1.12 | 2026-10-19 | **二十续（期首核查 A3B4C1 修复 + 第七例亲数勘误 + M11/M5 收官落地）**：①期首核查 2026101910（十九续 bde1232）——A1 ref-videos 上传绑定结构体漏 form tag（multipart 恒不命中，前端当前未消费）；A2 batch-generate `ids:[]` MySQL `IN ()` 语法错 500 vs 冻结 400「所选资产均已生成」（SQLite 空列表容忍的方言边界）；A3 episodes 详情 404 'episode not found' vs 冻结 :116 专属 'not found'（共享哨兵分叉——仅此路由映射）；B1 service_test 计数漂移（宣称 15 实 18）+B2 perms_test 口径 14→15（A3 补行未联动）+B3 孤儿终验口径三说并存+B4 M10 差异 ⑮ 补登（NULL vs '' 形态+Mock 台账 desc 参数化）；②**M11 亲数勘误 12→14**（viral-analysis 2 条 index.js:490-513 无模块行认领而 §4 清单自列——P0 统计残留第七例，与 M8 同型反向），§3 合计 235→**237**，根 CLAUDE.md 基线行「五轮亲数勘误」 | Claude |

## 1. 系统概览

AI 短剧内容生产线（拆解-出图-成片）+ 内容社区 + 积分商业化。单体后端，角色三档：游客（公开面）/ 用户（user）/ 管理员（admin）。

| 项 | 值 |
|----|----|
| Go 后端 | `backend/`，监听 **:3300**（与冻结版同端口，前端/代理零改动） |
| 数据库 | MySQL 8 单库 **qmtv_main**（utf8mb4/InnoDB） |
| 前端 | `frontend/` 独立项目（v1.1 立项）：**初始 = 复制 `qmtv/src` 原样（行为对等零改动）**，dev :5173（vite proxy /api /files → :3300）；终态 Go :3300 托管 `frontend/dist`。`qmtv/` 冻结区转为对照基准，演进轨道见 §7 |
| 静态 | `/files/*` ← 素材目录（maxAge 1h）；`/*` ← dist SPA fallback |
| 模型接入 | 管理后台注册 OpenAI 兼容端点（model_endpoints 表），无 Key 全站 Mock，**零环境变量接 AI** |

模块编号 M1~M13（v1.14 起含 M13 火花计划——原 M1~M12 对齐冻结代码 12 个路由文件，spark 2 条系漏登第八例补入，全文引用）。「端点数」列 = 冻结目标口径（v1.3 W10 校准：M2/M3 的 P0 统计残留已按冻结实存改；v1.7 十五续：M12 36→39 亲数勘误——冻结三文件 41 条物理 admin 路由，withdrawals 2 条归 M7；v1.8 十六续：M7 12→11 亲数勘误——P0 统计残留第四例；v1.9 十七续：M8 5→8 亲数勘误——P0 统计残留第五例，§4 清单自列 8 而标题/本表少算，与 M7 同型反向；v1.10 十八续：M6 38→29 亲数勘误——P0 统计残留第六例，冻结 index.js 组织段 9+enterprise.js 20=29、§4 清单自列即 29 而标题/本表多算 9，与 M7 同型「标题多算」；v1.12 二十续：M11 12→14 亲数勘误——P0 统计残留第七例，viral-analysis 2 条（index.js:490-513）物理夹在 M5 通知段与 M6 组织段之间不属于任何模块行、§4 M11 清单自列 6+6+2=14——与 M8 同型反向；v1.14 二十六期：**新增 M13 火花计划 2 条**——e2e 全站巡检抓获 Go 零 spark 路由回溯冻结源码，index.js:739-766「火花计划」段 2 条端点从未入任何模块行亦未计入历史合计（P0 统计残留第八例，与第七例同型「无模块行认领」）；「Go 已实现」列 = `backend/cmd/server/routes.go` 实装口径（2026-10-19 亲数）：

| 模块 | 冻结源文件 | 端点数 | Go 已实现 | 职责 |
|------|-----------|-------|----------|------|
| M1 认证与用户 | index.js（认证段） | 9 | 9（P1） | 注册/登录/refresh/登出/me/积分流水分页/改密/改邮箱/工作台状态（v1.2 清点修正：原漏登 logout 与 credit-records） |
| M2 任务与拆解 | index.js + routes2 + routes3（task 段） | 38 | 38（P2 W3） | 任务 CRUD、双引擎拆解、资产/分镜/重拆/场次（v1.3 W10 校准：66 系 P0 统计残留，实存 38——含 `POST /api/images/storyboard/generate`，routes.go 沿冻结物理段归 M2 组） |
| M3 生成与素材 | index.js + routes2 + routes3（生成段） | 19 | 19（P2 W4） | 图片/视频生成、打标、独立工具、音频配音（v1.3 W10 校准：38 系 P0 统计残留，实存 19——含公开面 style-presets/faq 与 `GET /api/models/active` 补登；storyboard/generate 归 M2 组，剪映属 M4） |
| M4 导出交付 | index.js + routes2 + routes3（导出段） | 10 | 10（P2 W5） | ZIP 交付/剪映草稿/导出任务 |
| M5 通知公告 | index.js | 4 | 4（W20） | 通知/未读数/已读/公告（读侧并入 notification 包——hub 同包零新建） |
| M6 组织协同 | index.js（组织段 :513-:597）+ enterprise.js | 29 | 29（P2 W17） | 组织/成员/素材库/成员组/权限组/看板/作品墙/搜索（v1.10 十八续勘误 38→29：冻结亲数恰 29 条〔组织段 9+企业面 20〕、§4 清单自列即 29——原 38 系 P0 统计残留第六例，与 M2/M3/M12/M7/M8 同型「标题多算」） |
| M7 分销提现 | index.js（v1.8 勘误：11 条端点全在 index.js :598-:638/:708-:737，recharge.js 仅承载返佣写入逻辑属 M8 approve 链） | 11 | 11（P2 W15） | 分销关系/返佣/提现状态机/兑换（v1.8 十六续勘误 12→11：亲数恰 11 条、§4 清单自列即 11——原 12 系 P0 统计残留，与 M2/M3/M12 同型第四例） |
| M8 充值 | recharge.js（8 条全在此文件 :28-:120，挂载点 index.js:27 `app.use('/api', recharge.router)`，admin 4 条系 router 内写死 /admin/ 前缀拼出） | 8 | 8（P2 W16） | 订单/凭证/收款码/审核+两级返佣写入链（v1.9 十七续勘误 5→8：亲数恰 8 条、§4 清单自列即 8——原 5 系 P0 统计残留第五例，与 M2/M3/M12/M7 同型；§4 清单 8 条一直正确而标题少算，与 M7「标题多算」同型反向） |
| M9 剧本库与投稿 | scriptlib.js + studio.js | 25 | 25（P2 W13） | 剧本市集/投稿工作流/编剧档案/解说成片（亲数核验：scriptlib.js 9 + studio.js 16 = 25，v1.3 W10 归属勘误——二十五端点并非全在 scriptlib.js） |
| M10 导演与融合 | director.js + fusion.js | 28 | 28（W18） | 导演工作台/分集分镜/项目资源中心/融合生图 |
| M11 评估与工具 | evaluation.js + workbench.js + index.js:490-513（viral——v1.12 勘正归入） | 14 | 14（W20） | 剧本评估流水线/工作台工具/审片/爆款分析（v1.12：12→14 亲数勘误第七例） |
| M12 管理后台 | index.js(admin) + llmroutes.js + routes2(admin) | 39 | 39（P2 W14） | 用户/积分/公告/组织审批/提现审核/模型接入/计费配置/提示词中心（v1.7 十五续勘误 36→39：41 物理路由 − withdrawals 2 归 M7，instruction-versions 通配展开+DELETE 漏登补齐——原 36 系 P0 统计残留，与 M2/M3 同型；withdrawals 2 端点随 M7 期落地） |
| M13 火花计划 | index.js:739-766（火花计划段——v1.14 补登） | 2 | 2（W26） | join 报名（置位+台账+100+通知）/me 激励投影（submissions 三计数+三阶梯+环形 next_tier）（v1.14：PRD 清单从未登记——P0 统计残留第八例，与第七例同型「无模块行认领」） |
| 基础 | index.js（SSE/health/release-mode） | 3 | 3（P0） | SSE 事件流/健康检查/发布模式 |
| **合计** | | **239** | **239** | （v1.14 二十六期校准：M13 火花计划 2 条补登后**模块数直加=239**〔3+9+38+19+10+4+29+11+8+25+39+28+14+2〕；v1.12 直加=237 系 spark 2 条未入任何模块行的第八例残留——亲数为准。**Go 已实现 239 = 全量收官**（W26 spark 2 落地；另有 SPA fallback 非 API）；v1.10 直加=235 系 viral 2 条未入任何模块行的第七例残留——亲数为准。v1.12「Go 已实现 237=全量收官」系第八例残留口径（v1.14 勘误作废——W20 时 spark 2 条未在册）；v1.9「直加=244」含 M6 统计残留未清——亲数为准。Go 已实现 219 = 基础 3 + M1~M4 76 + M6 29（W17）+ M7 11（W15）+ M8 8（W16）+ M9 25 + M10 28（W18）+ M12 39；另有 SPA fallback 非 API） |

## 2. 全局契约（Go 版逐字节兼容面）

| # | 契约 | 规格 | 违反后果 |
|---|------|------|---------|
| C1 | 响应信封 | 成功 `{"ok":true,"data":<业务数据>}`；失败 HTTP 4xx/5xx + `{"error":"<中文消息>"}`。前端 `api.js` 解包 `.data`、抛 `{status,message}` | 前端全面崩 |
| C2 | 健康检查 | `GET /api/health` 裸 `{"ok":true,"name":"QMTV"}`（不走信封） | 监控误判 |
| C3 | 鉴权 | `Authorization: Bearer <token>`；token query 兜底与公开面见 §2.1 | 401 面 |
| C4 | token query | **冻结实为三分**（v1.2 勘误）：一般端点 `authRequired` 仅 Bearer（`util.js:49`，不认 query）；`GET /api/events` 纯 query（`index.js:151`）；`GET /api/images/:id` query 优先+Bearer 兜底（`index.js:427`，单端点内联）。**Go 裁决（C3，2026-10-18）**：`FromRequest` 统一 Bearer 优先 + query 兜底（全局）——`/api/events?token=`（SSE）与 `/api/images/:id?token=`（img 无法带 header）单通道请求全兼容；行为面略宽于冻结（一般端点也接受 query），差异显式登记，守卫 `scripts/frozen_contract_check.py` 断言三分事实 | 素材 401 |
| C5 | SSE | `GET /api/events`：`text/event-stream`，先发 `event: connected\ndata: {}\n\n`，25s 心跳 `: hb\n\n`，帧 `event: <name>\ndata: <JSON>\n\n`；五类事件 video/split/recharge/workbench/eval（payload 见 §4 各模块） | 实时进度失效 |
| C6 | uid 格式 | `<前缀>+UUIDv4(带连字符)`；前端存在 `slice` 截取依赖（routes2.js:368、enterprise.js:40）。前缀常量表见 §2.2 | 前端截取错位 |
| C7 | 时间格式 | `YYYY-MM-DD HH:MM:SS` 本地时间字符串（SQLite 遗产，前端按此渲染）。Go 用 `DATETIME(3)`+DSN `parseTime=true&loc=Local`+`LocalTime` 类型序列化，**禁 RFC3339** | 时间显示错 |
| C8 | 密码哈希 | scrypt `N=16384,r=8,p=1,keyLen=32`，salt=16B hex，存储 `salt:hash` | 存量账号无法登录 |
| C9 | 错误文案 | 中文消息有前端正则依赖（如 `/所有者/`、`/密码/`、429 `操作过于频繁，请稍后再试`、402 `积分不足`）；错误码面（401/402/403/404/429/500/502）与冻结版一致 | 前端提示错乱 |
| C10 | 限流 | 内存桶：register 5 次/h/IP、login 20 次/min/IP（Go 版保持内存实现，集群期集中化） | 行为漂移 |
| C11 | 上传 | multipart，`fixFilename` latin1→utf8 文件名修复必须移植；模块限额：index 20MB/routes3 50MB/fusion 60MB/workbench 80MB/recharge 8MB/evaluation 10MB/scriptlib 2MB×20/llmroutes 2MB/director zip 20MB；JSON body 5MB | 中文文件名乱码 |
| C12 | JWT | 三段标准 HS256（新签发，exp 秒）+ 两段 legacy 解析兼容（exp>1e12 按毫秒），claims `{uid,rol,exp}`；secret：`jwt.secret` 配置优先，缺省生成持久化 `data/.jwt_secret` 等价物（bin 外持久文件） | 全员掉线 |

### 2.1 鉴权矩阵

| 级别 | 端点 |
|------|------|
| 公开 | POST /api/auth/register、POST /api/auth/login、POST /api/auth/refresh、GET /api/health、GET /api/system/release-mode、GET /api/announcements/active、GET /api/style-presets、GET /api/support/faq |
| token query（Go：FromRequest 统一 Bearer 优先+query 兜底；此二端点为最小依赖端点） | GET /api/events、GET /api/images/:id |
| Bearer（user） | 其余全部 |
| Bearer + admin | `/api/admin/*` 全部 + 提现审核/充值审核/组织审批/投稿审核等 admin 端点（各模块表内标注 🔒） |

### 2.2 uid 前缀常量表（infra/ids 集中定义）

`u_` 用户、`t_` 任务、`shot_` 分镜、`as_` 资产、`img_` 图片、`si_` 分镜图槽（index.js:249/:289）、`v_` 视频、`vid_` Mock 视频落盘文件名（assets.js:39，与行 id `v_` 解耦）、`cr_` 积分流水、`nt_` 通知、`rt_` 刷新令牌、`e_` 导出（v1.5 勘误：原写 ex_，冻结 index.js:458 实证）、`wb_` 工作台任务（workbench.js:55）、`ev_` 评估项目（evaluation.js:248）、`dpr_` 导演项目（v1.5 勘误：原写 dp_，冻结 director.js:274 + fusion.js:230 实证；含 `dir_+projectId` 伪外键挂 assets.task_id）、`dsh_` 导演分镜（director.js:68）、`dep_` 导演分集（director.js:99 + fusion.js:235）、`ih_` 图片历史（index.js:324/:356 + director.js:214）、`sl_` 剧本库（v1.5 勘误：原写 lib_，冻结 scriptlib.js:131/:222/:247 + studio.js:146 实证）、`su_` 剧本使用记录（scriptlib.js:206）、`sub_` 投稿（studio.js:53）、`wp_` 写手档案（studio.js:90/:143）、`na_` 解说任务（v1.5 勘误：原写 nar_，冻结 studio.js:167 实证）、`ro_` 充值单（v1.5 勘误：原写 rch_，冻结 recharge.js:40 实证）、`up_` 上传文件、`fu_` 融合项（fusion.js:160）、`rv_` 参考视频（fusion.js:210 + workbench.js:193）、`au_` 音频（v1.3 勘误：P0 误写 aud_，冻结 routes3.js:391 实证）、`vo_` 配音、`tts_` TTS（assets.js:65）、`wk_` 作品（v1.5 勘误：原写 w_，冻结 enterprise.js:69 works 实证）、`cm_` 佣金（v1.5 勘误：原写 rc_，冻结 recharge.js:72 实证）、`w_` 提现（v1.5 勘误：原写 wd_，冻结 index.js:621 实证——`w_`/`wk_` 曾错位）、`ex_` 佣金兑换（index.js:634）、`va_` 爆款分析（index.js:507）、`org_` 组织、`pg_` 权限组、`pt_` 提示词模板（promptCenter.js:36 冻结实证；Go prompts 内置不读表，存储面见 prompt_templates 补种注记——十四续 B1 补登）、`mg_` 成员组、`fd_` 素材夹（v1.5 勘误：原写 af_，冻结 enterprise.js:93 实证）、`al_` 资产库（routes3.js:75）、`an_` 公告（seed.js:24 + index.js:678）、`inv_` 组织邀请（index.js:539）、`me_` 自定义模型（models.js:19/:61）、`oa_` 组织申请（index.js:516）、`om_` 组织成员（index.js:547/:698）、`st_` 风格（静态 id）、`rr_` 审片报告（**与 referral_relations 共用前缀**——冻结两表均 `uid('rr_')`，UUID 空间无冲突，v1.2 注记）。
> v1.5 已按 `grep -ohE "uid\('[a-z_0-9]+'\)" qmtv/server/*.js | sort -u` 全量复核回填（冻结 47 前缀亲数），Go 侧 51 常量（含基建 4）以 `backend/internal/infra/ids/ids_test.go` 全量对拍守卫钉死（TestPrefixes_FrozenContract）。

## 3. 修正项（冻结行为 → Go 行为）

| # | 项 | 冻结行为 | Go 行为 | 兼容影响 |
|---|----|---------|---------|---------|
| F1 | refreshToken 真轮换 | `refresh_tokens` 从不 INSERT，`/api/auth/refresh` 恒 401（靠 72h access token 掩盖） | 登录签发 refreshToken 落库（SHA-256 hash 唯一）；刷新=校验（未撤销未过期）→撤销旧+签发新（轮换，`replaced_by` 串链）；登出/改密撤销全部；**refresh 响应增 refreshToken 字段** | refresh 端点从必 401 变可用；**前端 `api.js:24` 须消费轮换返回的新 refreshToken——P1 同期补丁（W9a）**：冻结版前端刷新后复用旧值，真轮换下首次刷新即撤销旧 token，旧前端将在 72h 后被强制登出；存量用户重登一次 |
| F2 | 原子扣款 | `tryDeduct` check-then-set（SELECT 后 UPDATE）非原子，并发可透支为负 | 单条 CAS：`UPDATE users SET credits=credits-?, version=version+1 WHERE id=? AND credits>=?`（version 仅自增不入谓词——MySQL 行锁下 credits 谓词已原子防超扣，v1.2 微调），affected=0 判余额不足。**v1.3 尾注（P1 实测+P2 定稿）**：该谓词下 affected=0 ⇔ 余额不足，不存在版本冲突路径——`optimistic.RunWithRetry` 不引入（qmtv/pms 双侧零生产调用，无原型）；跨聚合时序定稿见 §6 | 前端无感；并发下不再超扣 |
| F3 | export_tasks.shot_count | INSERT 引用该列但建表缺失（新库导出必报错的存量 bug） | 建表含 `shot_count INT NOT NULL DEFAULT 0` | 修复 |
| F4 | providers 死表 | 建表但无任何代码读写 | 裁剪不建 | 无 |
| F5 | JWT 标准化 | 两段式 `body.sig`、exp 毫秒、`!==` 非常量时间比对 | 三段 HS256、exp 秒、hmac.Equal 常量时间；**双格式解析一个发布周期**（两段+exp>1e12 判毫秒） | 新旧 token 并存过期（≤72h 自然过渡） |
| F6 | 计费双层口径统一入口 | `util.COSTS`（老接口硬编码）与 system_settings（routes2 覆盖价）并存，视频价两套 | 保留双层语义但统一经 `billing` 服务读取（COSTS=代码兜底默认，settings 键存在则覆盖）；键名清单冻结：`trial_credits,text_tokens_per_credit,image_generation,video_pull,video_per_second,image_model,video_model,text_model,image_model_fallback,provider_routing`（v1.7 勘误：`image_model_fallback` 冻结 routes2.js:34 原文名，本行曾误写 image_fallback_model）+ 评估价 `eval_quick,eval_deep` | 老接口价格若有 settings 覆盖则行为变化（对齐管理端配置的预期语义，PRD 评审已确认） |
| F7 | 邮箱校验统一（v1.2 新增） | register 用损坏正则 `/^[^@s]+@[^@s]+.[^@s]+$/`（`index.js:80`——`\s` 丢失反斜杠，实际仅要求「@ 后非空」，垃圾邮箱可注册）；PUT /api/auth/email 用严格版（`index.js:130`） | 统一严格版 `/^[^@\s]+@[^@\s]+\.[^@\s]+$/`（单一校验器，register/email 共用） | 垃圾邮箱注册被拒；正常邮箱无感（用户裁决 2026-10-18） |
| F8 | 素材对象存储（v1.2 新增，C4 裁决；v1.3 按落库终态定稿） | 本地磁盘 `data/files` + `/files` 无鉴权 static + `file_path` 落库存 `/files/...` 相对 URL | `infra/storage` 三实现：MinIO（192.168.50.190 共享实例，独立 bucket `qmtv`）/本地磁盘（files.dir 兜底）/Noop（endpoint 空降级 ErrNotConfigured）；**落库 file_path：MinIO 模式 public_base 非空=终态绝对 URL（C1 裁决，前端逐字消费；桶/域迁移需一次性 UPDATE file_path），public_base 空=相对 `/{bucket}/{key}`；local 模式=`/files/` 相对（=冻结形态）**；`ObjectKey` 日期分目录+随机 hex 防穿越。**v1.3 布局差异登记**：冻结产物扁平落 `files/`（`img_x.svg`），Go ObjectKey 产 `files/yyyyMMdd/x.svg` 日期子目录（同 LocalDir 混居）——前端只整串消费 URL 无感，`/files` 静态映射目录树天然可达 | 前端 URL 消费不变；备份语义变「DB+桶」两件套（DEPLOY 同步）；P1 无 HTTP 消费者（M8 留后续期），接口本期定型 |
| F9 | 退款防重唯一口（v1.3 新增，修冻结漏洞） | 退款=`addCredits(cost,'release')` 散布 providers 三处+启动恢复+管理端；`videos.refunded` 置位仅覆盖 Mock 失败与轮询失败两路——**视频提交失败（providers.js:129）与启动恢复（index.js:800）退款不置位**，其后仍满足管理端 `status='failed' AND NOT refunded` 条件可二次退款；images 无防重列（同步路径天然无二次退款面，维持不设防） | 新增 `credits.Refund` 唯一退款口：videos 路径先 CAS `UPDATE videos SET refunded=1 WHERE id=? AND refunded=0`（affected=0 ⇒ 已退，幂等跳过）再退积分；全部失败路径（providers 提交/轮询/下载失败、启动恢复）收口于此。置位与退款两步无事务（禁事务铁律），中断窗口=补偿式一致性既有边界 | 修复双退款漏洞；存量演示库若有「已 failed 未置位」行，启动恢复首跑会补置位+补退款（风险登记 P2 计划） |

## 4. 端点契约清单（M1~M13 + 基础）

> 表列：`方法 路径`｜鉴权（默认 Bearer，🔒=admin，公开面标注 ○）｜一句话契约。请求/响应字段以冻结代码为准，FSD 阶段逐模块细化（含字段表/校验规则/错误码）。

### 基础

| 端点 | 鉴权 | 契约 |
|------|------|------|
| GET /api/health | ○ | C2 裸格式 |
| GET /api/events | token query | SSE 流（C5），连接先发 connected |
| GET /api/system/release-mode | ○ | `{ok,data:{mode:'stable'}}` |

### M1 认证与用户（9，v1.2 清点修正）

| 端点 | 鉴权 | 契约 |
|------|------|------|
| POST /api/auth/register | ○ 限流5/h | 送 200 积分（trial_credits 可配，F6 Billing 读取——冻结硬编码不读 settings，属刻意差异）；invite_code 命中建分销关系+邀得 50 积分；referral_code=`QM+6位随机大写`；F7 邮箱严格版 |
| POST /api/auth/login | ○ 限流20/min | 返回 `{token,refreshToken,user}`（F1：refreshToken 真实签发） |
| POST /api/auth/refresh | ○ | F1 真轮换；响应含新 refreshToken；停用用户 401（安全收敛） |
| GET /api/auth/me | Bearer | publicUser 字段集：id/email/nickname/role/credits/org_id/workspace_version/referral_code/affiliate_enabled/created_at |
| POST /api/auth/logout | Bearer | 冻结无服务端状态清除；Go F1：撤销该用户全部 refreshToken（v1.2 补登） |
| GET /api/auth/credit-records | Bearer | 全局积分流水分页（size 20，`{list,total,page}`，created_at DESC,id DESC）（v1.2 补登） |
| POST /api/auth/change-password | Bearer | 改密后撤销全部 refreshToken |
| PUT /api/auth/email | Bearer | 需密码确认；F7 严格版 |
| PUT /api/auth/workspace-state | Bearer | 工作台版本状态存 users.workspace_version |

### M2 任务与拆解（38，v1.3 W10 校准：标题「66」系 P0 统计残留）

> 端点实装清单见 `docs/API-增量.md` M2 节（38 条逐条）。归组注记（v1.3 W10）：`POST /api/images/storyboard/generate` 物理位于冻结 index.js 生成段，routes.go 沿冻结分组归 M2 组（:141）——本节正文按实装口径收录。

CRUD：GET/POST /api/tasks、GET/PUT(title)/DELETE /api/tasks/:id、POST /api/tasks/:id/duplicate。
拆解双引擎：POST /api/tasks/v2（llm.isAvailable→异步 LLM 拆解/Mock 同步，split_status 状态机 idle/processing/done/failed/fallback——注〔三十期〕：idle/fallback 两态冻结与 Go 均无写入点，实现差异见 FSD m02 F2.03 §6；实际三态 done/processing/failed，DDL 默认 done）、POST /api/tasks/:id/resplit（20 积分）、POST /api/tasks/:id/restart、POST /api/tasks/:id/continue（对缺图镜头按 image_cost 补扣）、POST /api/tasks/:id/mark-failed 🔒、POST /api/tasks/:id/scenes/auto-generate(+ /cancel)。
资产：POST /api/tasks/:id/assets/extract 与 /extract2（2K，image_cost×N）、PUT /api/tasks/:id/extract-settings、POST /api/assets/:id/image/upload、POST /api/assets/:id/regenerate、POST /api/assets/:id/derive（衍生变体，parent_id）。
分镜：GET /api/tasks/:id/full 与 /full2（尾帧参考版）、GET /api/tasks/:id/scenes2、PUT/DELETE /api/tasks/:id/scenes/:groupNo、PUT /api/shots/:id、DELETE /api/shots/:id、POST /api/shots/:id/slots、DELETE /api/shot-images/:slotId、GET /api/shots/:id/histories、PUT /api/shots2/:id/params、POST /api/tasks/:id/storyboard-batch（批量补图）、POST /api/images/storyboard/generate（单镜补图，v1.3 W10 归组注记见上）。
资产库联动：GET/POST /api/asset-library、POST /api/asset-library/:id/share、DELETE /api/asset-library/:id、POST /api/tasks/:id/library-apply/:libId。
积分明细：GET /api/tasks/:id/credit-records。

### M3 生成与素材（19，v1.3 W10 校准：标题「38」系 P0 统计残留——render-all 实属 M4、retry 无用户端点）

> 端点实装清单见 `docs/API-增量.md` M3 节（19 条逐条）。`POST /api/images/storyboard/generate` 归 M2 组（见 M2 归组注记）。

图片：POST /api/images/generate（COSTS.image，失败 providers 内部退款）、GET/DELETE /api/images/:id、GET /api/images、POST /api/images/upload。
视频：POST /api/videos/generate（COSTS.video）、POST /api/videos/generate-v2（videoCost(secs)=video_pull+per_second×secs，secs 1-15，reso 720p/1080p）、POST /api/videos/:id/cancel（cancelled 不退款）、POST /api/videos/tag（10×镜数）、GET /api/videos、GET /api/standalone-videos。
音频：GET/POST /api/audios、DELETE /api/audios/:id、POST/GET /api/videos/:id/voiceover（Mock WAV 字节级兼容）。
公开/辅助面：GET /api/style-presets ○（19 种风格预设）、GET /api/support/faq ○（静态 6 条）、GET /api/models/active（getBilling 投影——v1.3 W10 补登，PRD 原文全篇未列该端点；消费方 DashboardView/VideoView/Director3DView 三处〔三十期勘正，Director3DView 系 M10 落地后新增〕）。
异步契约：有 Key 时 video 提交→轮询（8s 首查/10s 步进/90 次上限）→SSE `video` 事件（done/progress/failed）；失败 `refunded=1` 防重退款。

**实现差异（v1.3 W10 入正文，Go 实装 vs 冻结行为，全部显式登记）**：①expires_at=C7 例外全达成（冻结 toISOString() 入库透传；Go ISOTime 响应 ISO、落库仍 DATETIME(3)）；②SmartVideo ⑩ 分辨率白名单外值（如 480p）冻结原样透传 vs Go 归一 1080p、ratio null 冻结传 null vs Go 归一 16:9（Go 收紧向，安全）；③SSE progress 冻结每次 fetch 前发 vs Go fetch 后发（tries 命中集合相同）；④tag 打标价冻结固定 COSTS.image=10 vs Go 随 F6 settings 覆盖价。

### M4 导出交付（10）

POST /api/export（2 积分×完成段数，shot_count）、GET /api/export/:id（导出任务轮询面）、GET /api/export/task/:taskId（查重防重复扣费）、GET /api/tasks/:id/export/assets-deliver（2K 资产 ZIP）、GET /api/tasks/:id/export/deliver（全量 ZIP）、POST /api/tasks/:id/export/tag-all、POST /api/tasks/:id/export/render-all（Σ videoCost 批量渲染）、GET /api/tasks/:id/export/history、PUT /api/tasks/:id/export/scenes/:sid、GET /api/tasks/:id/jianying-draft（剪映草稿 JSON）。

**实现差异（v1.3 W10 入正文）**：①history 响应 `v.*` 列序——冻结 created_at 在 error 之后（db.js CREATE 后 ALTER 折叠序）vs Go DDL/struct 排末位：键集一致、前端按键取值无感，严格逐字节对拍不齐；②assets-deliver 条目名 index_no=0——冻结 JS truthy→`asset_` vs Go `!= nil && != 0`→`shot000_`（W6 期已修对齐冻结 truthy 语义）；③export repo 接口实为 16 方法（W5 基线「17」系计数漂移）；④F3 修正兑现——冻结 export_tasks 无 shot_count 列而 INSERT 显式引用（导出必报错存量 bug），Go DDL 含该列按修后语义（P0 裁决）。勘误（v1.3 W10）：本节原列「GET /api/export」系 P0 清单误计——冻结实存的是 `GET /api/export/:id` 轮询端点，无列表端点。

### M5 通知公告（4）

GET /api/notifications、GET /api/notifications/unread-count、POST /api/notifications/read、GET /api/announcements/active（○；前端 localStorage `qmtv_dismissed` 由前端自理）。

> **✅ M5 落地（2026-10-19 W20，`internal/domain/notification` 读侧——hub 同包零新建）**：App.vue 铃铛三面/AccountView 摘要 404 转绿（前端零改动）。**实现差异登记**：①`read` MySQL 保留字全 SQL 反引号（auth/repo.go:130 先例同制）；②POST read 无 id=整用户全标已读+恒 ok(true) 无 404 面（id 不属于自己也静默——冻结 :480-483 quirk 照抄）；③announcements/active **公开端点无 amw**（冻结 :485 无 authRequired）；④通知零 SSE（前端 60s 轮询 unread-count）+读侧行形态兼容五源写入（auth/admin/recharge/seed/evaluation 自持 INSERT）。 | Claude |

### M6 组织协同（29，v1.10 十八续勘误：标题「38」系 P0 统计残留——冻结亲数恰 29 条〔index.js 组织段 9 + enterprise.js 20〕，下清单即全量；与 M7 12→11 同型「标题多算清单对」第六例）

组织：POST /api/organizations/applications、GET /api/organizations/me、PUT /api/organizations/me/credit-allocation-mode（pooled/independent）、POST /api/organizations/me/invites、POST /api/organizations/join、DELETE /api/organizations/me/members/:memberId、PUT /api/organizations/me/members/:memberId/role（非 owner 拒，文案含「所有者」）、POST /api/director3d/:id/share、GET /api/organizations/shared-projects。
企业：GET /api/organizations/me/dashboard（冻结积分+14 天趋势+双排行）、GET/POST /api/showcase、DELETE /api/showcase/:id、素材库目录树 GET/POST /api/organizations/me/material/folders(+DELETE :id)、GET /api/organizations/me/materials、POST /api/organizations/me/material/assign、成员组 GET/POST /api/organizations/me/groups(+DELETE)、权限组 GET/POST/PUT/DELETE /api/organizations/me/perm-groups*、PUT /api/organizations/me/members/:memberId/assign、GET/PUT /api/organizations/me/material-perms*、GET /api/search（全站搜索）。
容量：素材 10GB 配额（images.file_size/audios.size 聚合）。

> **✅ M6 落地（2026-10-19，`backend/internal/domain/org` 单包多文件七件 29 端点全量）**：integration 第九链 OrgChain（M12 approve 建组织→invite/join 全链→角色面三哨兵→dashboard available==balance 钉死→material-perms 联动端到端→showcase 恒 ok→share/search/401 面）。**实现差异登记**：①credit-allocation-mode 死端点照抄（前端零消费方，mode 非 independent 一律 pooled）；②dashboard `available` 恒=balance 无任何过滤+frozen 仅 eval_projects status='running'（M11 未落地恒 0）+projectRank `dir_` 前缀死分支（实产 dpr_ 永不命中且 slice(4) 偏移亦错）——三处冻结谎言/死分支照抄不修，集成链钉死；③works.cover_path/asset_folders.org_id/member_groups.leader_id 等 Go DDL NOT NULL DEFAULT '' vs 冻结可空——读侧 NULLIF 还原 null（recharge ⑦ 先例）；④素材 10GB 配额系**估算法**非真 SUM（images.sum(file_size)+NULL 数×80KB+audios.sum(size)+videos 数×2MB 系数硬编码，plan 恒「基础档」，percent toFixed(1) 等价 Round(pct×10)/10）；⑤org_applications company/contact/reason 冻结不 slice 不 trim——Go VARCHAR(128) strict 溢出报错（SQLite 动态类型照收），超长输入行为差异；⑥invite code 冻结 Math.random base36 截 8 位大写 vs Go crypto/rand 同字母表（形态等价 ^[A-Z0-9]{8}$，值域随机性不属契约面）；⑦organizations.credits/org_members.quota/credit_allocation_mode 三列冻结无读写消费方（pooled/independent 分账完全未接线）——空转照抄；⑧DELETE /showcase/:id 与 DELETE /organizations/me/groups/:id 不查行数恒 ok:true（删他人/不存在均静默）；groups DELETE 无 404 面（403 后恒 ok）；⑨members target 不存在与 target 是 owner 合并同一 400「无法移除」（非 404 quirk）；⑩invites 文案「仅组织管理员可邀请」而判定 role='owner' 严格（quirk 照抄，:553 移除面同款）；⑪PUT members/:memberId/assign 响应 group_id/perm_group_id 缺省时冻结回 `null` vs Go 回 `""`（响应面未走读侧 NULLIF 还原——前端 assign 后重拉列表非回归，十九续 B3 登记）。**十九续勘正（2026-10-19）**：dashboard projectRank 谓词十八续误加 `task_id != ''` 曾把空串消耗聚合行（冻结 :37 IS NOT NULL 放行，以缺省名「独立生成 / 工具」kind=工具 GROUP BY 上榜）整行挡掉——已还原逐字+集成钉守（A4）；shared-projects video_id 谓词同批还原 `IS NOT NULL`（MySQL NOT NULL 列上即冻结字面）；shared-projects 投影补 `script_text`（冻结 `dp.*` 全列，组织共享面下发全剧本即冻结语义，用户裁决补齐）；projectRank wire 键 `json:"amount-kind"` 勘误 `kind`（A1，前端 OrganizationView 「类型」列曾恒空白——wire 键集断言守卫自此入惯例）。 | Claude |

### M7 分销提现（11，v1.8 十六续勘误：标题「12」系 P0 统计残留——冻结亲数恰 11 条〔下清单即全量〕，与 M2 66→38/M3 38→19/M12 36→39 同型第四例）

GET /api/distribution/overview、GET /api/distribution/downlines、GET /api/distribution/records、GET/POST /api/distribution/withdrawals（状态机 pending→reviewing→topay→paid→done / rejected / cancelled）、POST /api/distribution/withdrawals/:id/cancel、POST /api/distribution/withdrawals/:id/confirm、GET/POST /api/distribution/exchanges、PUT /api/admin/withdrawals/:id 🔒、GET /api/admin/withdrawals 🔒。返佣：充值到账两级返佣（commission_rate/level2_rate 可配，默认 8%/2%）；满 980 元自动开 affiliate_enabled。

> **✅ M7 落地（2026-10-19，`backend/internal/domain/distribution` 单包多文件 11 端点全量）**：integration 第七链 DistributionChain（seed 对拍 40 佣金/0.08-0.02-100/状态机全迁移/cancelled·rejected **不释放额度**端到端钉死/exchanges 占扣/403/404 面）。**实现差异登记**：①withdrawals/exchanges 冻结的是佣金台账而非 users.credits——POST 两写面不触 credits/credit_records/通知/SSE，域零 credits 依赖（返佣写入方在 M8 approve 链，未落地前仅 seed 行可达）；②available 分母 `SUM(withdrawals.credits)` **无 status 过滤**——cancelled/rejected 工单永不释放额度，:715 note「收益已解锁」与行为相悖系冻结谎言照抄不修（集成链 ⑨⑩ 双断言钉死）；③overview pending_withdraw 冻结双 IN 冗余（四态 AND 三态）→ Go 等价单 IN 三态（结果逐位一致，repo 注释钉行号）；④settings 回退三体 quirk：overview 无行回退 `{0.2,0.05,100}`（无 id 键）与 POST 提现面回退仅 `{min_withdraw:100}` 各自独立（Go 两 shape 不共用），种子行 0.08/0.02/100 覆盖表默认；⑤affiliate_threshold 硬编码 980（非 system_settings affiliate_threshold_cny，与 recharge.js:25 不同源）照抄；⑥credits 请求绑定 float64（冻结 `+req.body.credits` 强转）入库 int64（MySQL BIGINT 舍入 vs SQLite 动态类型可存小数——前端常态整数无差）——**二十一期⑥ DECIMAL(12,2) 收口（0004）**：withdrawals/exchanges credits 原值入库，差异终结；⑦reviewed_at 冻结 now()=UTC 'YYYY-MM-DD HH:MM:SS' 字符串 → Go `time.Now().UTC()` 显式（与 auth LocalTime 本地时先例刻意不同源）；⑧admin PUT 状态机 W_FLOW 严格邻接校验禁跳转（终态无键恒 400），动态文案 `不允许从「X」流转到「Y」` W_ZH miss 原样回显英文态；⑨exchanges.status 恒 'pending'（全冻结无 UPDATE，死态照抄）；⑩withdrawals 2 条 admin 端点在本域（M12 裁决归 M7），AdminView 提现 tab 404 面转绿。

### M8 充值（8，v1.9 十七续勘误：标题「5」系 P0 统计残留第五例——清单自列恰 8 条而标题少算，与 M7「标题多算」同型反向）

> **✅ M8 落地（2026-10-19，`backend/internal/domain/recharge` 单包多文件 8 端点全量）**：integration 第八链 RechargeChain（config 对拍/下单 rate 换算/凭证真 multipart 落盘/approve 到账对账+两级返佣 8·2/资格联动 ≥980 自动开通+已开不重复/reject 双截断/admin list status 过滤/403/404 面）；integCleanup 扩 distribution 四表+recharge_orders（十七续 B2 收口，M8 链起共享清尾）。前端零改动（RechargeView/AdminView 充值面 404 转绿）。**实现差异登记**：①commission_records 唯一写入方在本域 approve 链（M7 差异①就此收口），order_amount/commission 绑定 float64→MySQL BIGINT round——冻结 SQLite INTEGER 列收 REAL 靠亲和性存原值（如 33 元二级佣金冻结存 0.66 并入账 0.66）——**二十一期⑥ DECIMAL(12,2) 全链收口（0004）：pay 佣金原值入台账亦入余额**，差异终结；②reviewed_at/reviewed_by——冻结 now()=UTC 'YYYY-MM-DD HH:MM:SS'（recharge.js:93 与 M7 同源）→Go time.Now().UTC() 显式（M7 差异⑦同制）；③credits.Add ErrNoUser 显式 vs 冻结 addCredits 静默（util.js:66）——approve 链容错对齐（返佣 inviter 不存在冻结照写台账继续），其余 DB 错误照冻结 throw 传播；④settings 值非数字冻结 NaN→JSON null，Go 回退默认（settings 经 admin 白名单管理常态数字串不可达）；⑤affiliate_threshold 读 system_settings affiliate_threshold_cny 默认 980——与 M7 overview 硬编码 980 两处刻意不同源（M7 差异⑤），勿统一；⑥approveOrder 冻结导出「供重启复用」系死代码零消费方——Go 不建恢复链；⑦proof_path Go DDL NOT NULL DEFAULT '' vs 冻结可空——读侧 NULLIF 还原 null 透传（export normalizeExportTask 先例同制）；⑧二级返佣 from_user_id 恒=充值人（冻结 :78/:80 两级 pay 均传 o.user_id，非链上级）。

GET /api/recharge/config（1 元=N 积分 settings 面+收款码+推广阈值 980 默认）、GET/POST /api/recharge/orders（金额两位舍入/≥1 元/≤10 万；orderNo='RCH'+毫秒+3 位随机）、POST /api/recharge/orders/:id/proof（凭证上传 8MB，multer 两路由共用限额）、POST /api/admin/recharge-orders/:id/approve 🔒（到账+两级返佣+SSE recharge 事件+单笔达标自动开推广资格）、POST /api/admin/recharge-orders/:id/reject 🔒（note 落库截 200/通知截 100 缺省「凭证有误」两处不同源）、GET /api/admin/recharge-orders 🔒（status 过滤，'全部' 或空=不过滤，LIMIT 200）、POST /api/admin/pay-qrcode 🔒。

### M9 剧本库与投稿（25）

> **Go 实现现状（v1.3 W10 盘点，P2 排期范围外）**：M9 全部 25 端点 Go 零实现——前端剧本库弹窗/市集对 Go 后端 404 系排期现状非回归（迁移前后行为一致）。归属亲数勘误：scriptlib.js 9（genres/list/:id/use 四件 + admin script-library GET/POST/PUT/DELETE 四件 + upload 一件）+ studio.js 16（market/scripts 二件、投稿 submissions 四件、编剧 writer-profile 二件、writers 二件、admin/submissions 审核二件、admin cover+rating 二件、tools/narrations 二件）= 25。
>
> **✅ W13 落地（2026-10-18，`backend/internal/domain/scriptlib` 25 端点全量）**：routes.go M9 组恰 25 条亲数；AdminMiddleware 接线 9 admin 端点（403 forbidden；十四续 B3 勘误「8」=scriptlib 5+studio 4 亲数）；integration 第五链 ScriptlibChain（ensureSeed 首触/submissions 全链/admin review publish 多表写/narrations 扣费 30 对账恒等式）；前端消费面 URL 不变零改动（Dashboard 弹窗/ScriptLibraryView/StudioView/WritersView/AdminView/IndependentToolsView 404 面转绿）。**实现差异登记**：①admin upload 修后语义——冻结 scriptlib.js:248 run 传 16 参数对 9 占位符，better-sqlite3 绑定超占位恒 throw RangeError（已实测坐实），**冻结版 upload 端点从未可用**，Go 按 9 列 INSERT 修后实现（F3 同制）；②cover_path 落库走 F8 口径（MinIO 绝对 URL/local /files/ 相对），冻结恒 /files/ 相对；③upload GBK 回退用 GB18030 解码（WHATWG TextDecoder('gbk') label 实走 gb18030 算法）；④upload/cover 限额=路由级 BodyLimit（45mb/10mb）+handler 单文件校验（2MB/超 20 文件按 multer 语义 500），冻结 multer fileSize 单文件限制；⑤length()→CHAR_LENGTH（MySQL length 为字节数）；⑥char_count/preview/catalog 截断按 rune（JS UTF-16 BMP 等价，gateway.Err 300 rune 先例同制）；⑦go.mod 无新增依赖（golang.org/x/text 已在 indirect 转直接）；⑧writers 列表 search Go 侧 TrimSpace 后 LIKE（冻结 studio.js:105 直用原值不 trim）——用户裁决保持+登记（检索体验更稳；market 端点 search 与冻结一致不 trim，两处刻意不同源，repo.go 注释在）；⑨narrations 扣费台账 task_id 落 `''` 而非冻结 null（credits.Record.TaskID 为 string 非 *string，沿既有 credits 模型形态——**二十一期⑤已收口**：0003 列 NULL 化+域内 `''→NULL` 绑定；冻结 studio.js:158 tryDeduct 第 4 参 null）——credit-records 响应形态微差，待 credits 模型指针化时一并（十四续 B7 正文差异清单补登，v1.5 修订行已有同义注记）。

市集：GET /api/script-library/genres（含 markets/audiences/forms 配置）、GET /api/script-library（五维筛选/排序）、GET /api/script-library/:id（目录+试读）、POST /api/script-library/:id/use（7 日热度）、GET /api/market/scripts、GET /api/market/scripts/:id。种子幂等回填逻辑（ensureSeed）保留。
投稿：GET/POST /api/studio/submissions、PUT /api/studio/submissions/:id、POST /api/studio/submissions/:id/submit（draft→pending）、编剧档案 GET/POST /api/studio/writer-profile、GET /api/writers、GET /api/writers/:id。
管理：GET/POST /api/admin/script-library、POST /api/admin/script-library/upload（20 文件×2MB）、PUT/DELETE /api/admin/script-library/:id、POST /api/admin/script-library/:id/cover、PUT /api/admin/script-library/:id/rating、GET /api/admin/submissions、POST /api/admin/submissions/:id/review 🔒（needs_change/rejected/published；publish 自动建档+入库）。
解说：POST/GET /api/tools/narrations（30 积分，Mock WAV）。

### M10 导演与融合（28）

导演：GET/POST /api/director3d、DELETE /api/director3d/:id、GET /api/director3d/:id、POST /api/director3d/:id/resplit、GET /api/director3d/episodes/:eid、POST /api/director3d/:id/episodes/:eid/extract-assets、POST /api/director3d/:id/episodes/:eid/shots（确定性 9 字段分镜）、PUT /api/director3d/shots/:id、POST /api/director3d/shots/:id/generate-image(-video)、GET /api/director3d/:id/export（ZIP）、POST /api/director3d/import。**双表通路**：director_shots 与 shots 并存，providers 按 `shot._tbl` 路由（Go 版以表名字段显式传递）。
融合：GET/POST /api/director3d/:id/assets、POST /api/director3d/assets/:id/generate（3 积分）、POST /api/director3d/:id/assets/batch-generate、POST/DELETE /api/director3d/assets/:id/queue*、GET /api/director3d/:id/queue（持久化队列 recoverQueue）、POST /api/director3d/:id/assets/import、POST/GET /api/director3d/:id/fusion*（融合生图）、POST /api/director3d/:id/remix（30 积分一键转剧）、GET/POST /api/director3d/:id/ref-videos*、DELETE /api/director3d/ref-videos/:id、POST /api/director3d/:id/convert/:eid（30 积分一键转剧——v1.11 勘正补列，原清单文字漏列而合计 28 自洽）。

> **✅ M10 落地（2026-10-19，`backend/internal/domain/director` 单包多文件 28 端点全量〔director.js 11+fusion.js 17——亲数 11+17=28 清点清白〕）**：integration 第十链 DirectorChain（建项目→分集→分镜→分镜图/视频扣费对账恒等式贯穿→资产提取/生成/队列→融合/改创→参考视频→export ZIP 魔数→import ZIP→一键转剧→resplit→属主 404/401）。**实现差异登记**：①splitEpisodes 双阈值（resplit 1200/fusion CreateProject 800）刻意不同源勿统一（合参参数化）；②splitScenes 锚点前残余段亦成场景（「第一集 开端」头成 scene 目标——冻结 lookahead split 语义照抄，集成实测 5 目标坐实）；③「道具：」的「道具」二字亦命中 character 正则（冻结 :140 quirk 照抄）；④**InsertShot 冻结字面 0 错位**（:179 列 15 VALUES 16——字面 0 落 resolution、attempts 落 resolution 字符串，SQLite 动态类型容忍的存量 bug；F3 同族）——MySQL strict 恒 1265，Go 按修后语义 15 列 15 ?（resolution=草稿值/attempts=0）登记；⑤**videoSmart Mock 分支 UPDATE shots 硬编码**（providers.js:91 真实分支才按 _tbl 路由）——导演分镜 Mock 生成后分镜行不回填（0 行静默），Go 按 _tbl 意图语义打 director_shots（ShotView.Tbl 显式传递——PRD 双表通路约定落地）；⑥episodes 面 eid↔project 无关联校验（ep 按 eid 查活、project 按属主查——跨项目 eid 可达 quirk 照抄）；convert 例外（:256 WHERE id AND project_id 关联逐字）；⑦fusion cost=3 / convert flat=30 硬编码（非 ImageCost 面）+convert taskId=裸 p.id（非 dir_ 前缀，:259 照抄）；⑧convert 空场次：冻结 scene.split TypeError 恒 500（fallback 对象调 split）——Go 按意图语义以 ep.title 单场生成登记；⑨extract-assets 逐张生成失败 continue 不中断（冻结 providers.imageSmart 内部退款等价形态——域内 Refund 唯一口退单价 nil claim，调用方不重复退）；⑩assets/images 行 task_id='dir_'+project_id 约定（M6 projectRank 聚合面依赖）；⑪promptcenter 模板查库面沿 M2 同源不查 prompt_templates（内置默认渲染——getShotImagePrompt 冻结有 getActive 查库，M2 起未接线，差异沿袭）——**二十一期⑦已接线收口**（shot_image/shot_video/asset_*×3 单级查库+内置默认回退，split 三级沿 SplitTemplate）；⑫buildShotFields JSON 键序 Go map 字典序 vs JS 插入序（JSON 对象语义等价，前端按键取）；⑬AssetJoined 等 JOIN 投影不选无字段列（gorp 反射报错——M6 GetWriterJoinedAt 同族教训，集成链抓获实修）；⑭multipart 两面 import 20MB/ref-videos 60MB（up60 新档位；upload 处理器内单文件上限双保险）；⑮**空串形态与 Mock 台账 desc 两处漏登补记**（二十续 B4）：assets/ref_videos 的 description/note 空串冻结恒写 ''（fusion.js:41/:212）vs Go 落 NULL（wire null vs ""——前端 `||''` 消费无感）；videoSmart Mock 失败退款台账 desc 参数化「导演分镜视频失败自动退回」vs 冻结硬编码「视频生成失败自动退回」（providers.js Mock catch——⑤只登记了 UPDATE 表名面）；冒烟面 404 属主过滤（英文短文案）+403 episodes/extract 属主面逐字。 | Claude |

### M11 评估与工具（14——v1.12 勘正：evaluation.js 6+workbench.js 6+index.js viral 2；原清单 :208 自列 6+6+2=14 而标题少算，P0 统计残留第七例与 M8 同型反向）

评估：GET /api/evaluations/config、POST /api/evaluations/upload（txt/md/docx，UTF-8/GBK 自动识别，10MB）、GET /api/evaluations、GET /api/evaluations/:id、POST /api/evaluations（quick 100/deep 300 可配，8 角色×3 阶段流水线，冻结 cost 语义）、GET /api/evaluations/:id/report（txt 下载）；SSE `eval` 事件（stage/done/failed）；失败全额退款。
工作台：POST /api/workbench/tasks（转绘 20/去字幕 10/转高清 15）、GET /api/workbench/tasks（recoverWorkbench）、POST/GET /api/tasks/:id/review*、POST/GET /api/director3d/:id/review*（审片 10 积分，双入口共用 review_reports，scope classic/director）。
爆款分析：GET/POST /api/viral-analysis（COSTS.viral=5）。

> **✅ M11 落地（2026-10-19 W20，`internal/domain/evaluation` 单包多文件 14 端点全量〔workbench 6+eval 6+viral 2——v1.12 第七例勘正〕）**：integration 第十一链 EvaluationChain（config 对拍→upload 真上传→扣 100 台账逐字+taskID=ev id→Mock 异步收口 waitFor done 不退款→report 下载头+体→列表定列/详情全列→404 双哨兵）+第十二链 WorkbenchNotifyChain（workbench 守卫三面→video_id 引用 done 成片 Mock done 扣 20 对账→审片双入口 404 两文案分叉+扣 10 台账+空分镜先退对冲→viral 扣 5→M5 四端点 unread-count 联动+read 单条/全标+announcements 公开/401）。**实现差异登记**：①costOf settings 值为 0 也回退默认 quirk 照抄（冻结 `+r.value||def`）；②engine 两字面不同源勿统一（config『规则(Mock)』vs POST『规则』）；③mode 非法静默归 quick 无 400；④runEval finished_at UTC 形态照抄（冻结 :181 toISOString 变形——与 created_at localtime 分裂同源）+Mock 节奏 sleep 1100/1500/900 照抄（mockSleep 可注入）；⑤LLM 通道 evalLLM 接线（modelCfg 三元+llm.Client.Chat——M12 反引号修复后已生效），模板查库不接线沿差异⑪先例（EVAL_PROMPT 兜底）——**二十一期⑦已接线**（eval_deep/eval_quick active 查库，content 空回退 EVAL_PROMPT；写侧 CATEGORIES 八键无此二键入口=冻结同 quirk 照抄）；⑥workbench watchVideo 状态丢失/tries>180 **不 emit SSE** 照抄（冻结 :84 无 bus.emit）+超时不退款照抄（providers 已退语义——与 generation PollTimeout 先例不同源勿统一）；⑦审片零 SSE 零通知+reviewShots target/scope 死变量不建+classic 'task not found' vs director 'not found' 404 文案分叉照抄+空分镜先退台账「审片退回：无分镜（+10）」逐字；⑧viral Math.random→math/rand+五列 INSERT（Go DDL status/version 惯例列默认）+title slice255 防御截断（冻结 SQLite VARCHAR 无约束存全量——MySQL strict 1406 防护，方言适配，二十一期 B2 补登）；⑨**recoverWorkbench 整体迁移 evaluation.RecoverStuck**（recovery/repo.go 预定注释兑现——四方法+BenchRef/BenchFailedErrorText 移除、WorkbenchRecoverer 窄接口注入；**装配序教训：deps.eval 必须先于 recovery 建立的首跑 nil panic 被集成 RecoveryChain 抓获**）；⑩workbench POST 双载荷形态（multipart 80MB up80 新档/JSON——WorkbenchView :131/:133 两形态）+taskId 无=空串（冻结 undefined——narrations ⑥ 同族沿袭）；⑪审片跨域共表读 COALESCE(image_prompt,'')（冻结 `||''` 消费语义——**集成链 NULL 直插行抓获存量炸点**：gorp Scan NULL→string 恒错，shots 表真实 NULL 行同炸）；⑫workbench_tasks 可空列裁决=**不收紧**（source_path/video_id 二选一语义可空；DDL NOT NULL DEFAULT '' 即冻结 NULL 形态——wire null vs "" 随 M10 ⑮ 口径），delta 无 0003 维持；⑬GET /workbench/tasks out_url 双条件冗余+`...w` 展开 JOIN 伴列（out_path/video_status）照抄；⑭LLM 边缘面两处微差（二十一期核查 B4 登记）：parseEval 重复 dims key 累加 vs 冻结 `find` 首个（evaluation.js:125——仅 LLM 畸形输出可达）；报告模板缺失键 `%v`→`<nil>` vs 冻结 `||''`（:271——Mock 全键不达）。 | Claude |

### M12 管理后台（39）

用户与积分：GET /api/admin/summary、GET /api/admin/users、PUT /api/admin/users/:id、POST /api/admin/users/:id/password、GET /api/admin/credit-grant-records、POST /api/admin/credit-grant-records、GET /api/admin/credit-records/export（CSV）、GET /api/admin/runtime/tasks（运行时任务观测）。
生成审计：GET /api/admin/generation-audit/failures、POST /api/admin/generation-audit/videos/:id/release（人工退款，校验 refunded 防重）、POST /api/admin/generation-audit/tasks/:id/retry-split。
公告：GET/POST /api/admin/announcements、PUT /api/admin/announcements/:id。
组织审批：GET /api/admin/organization-applications、POST /api/admin/organization-applications/:id/approve(-reject)。
计费配置：GET/PUT /api/admin/credit-settings（白名单键，见 F6）、GET/PUT /api/admin/model-settings。
模型接入：GET/POST /api/admin/model-endpoints、PUT/DELETE /api/admin/model-endpoints/:id、POST /api/admin/model-endpoints/:id/activate、POST /api/admin/model-endpoints/:id/test（text→chat 8 token/image→红圆真生成/video→可达性，45s 超时；api_key 列表掩码前 6 位）。
提示词中心：GET/POST /api/admin/prompts、POST /api/admin/prompts/upload、PUT/DELETE /api/admin/prompts/:id、POST /api/admin/prompts/:id/activate（互斥生效版）；指令版本 GET/POST /api/admin/instruction-versions、PUT/DELETE /api/admin/instruction-versions/:id、GET /api/admin/instruction-versions/active、POST /api/admin/instruction-versions/:id/activate（v1.7 通配展开补登 PUT/DELETE :id 两条；DELETE 404 `not found` 与 prompts DELETE 的 `版本不存在` 刻意不同源照抄）。

**Go 实现差异登记（v1.7 W14，`internal/domain/admin` 单包多文件沿 M9 先例）**：①withdrawals 2 端点（GET/PUT /api/admin/withdrawals）随 M7 期落地，本期 404 系排期现状非回归；②死端点照抄：GET/PUT /admin/model-settings、GET /admin/instruction-versions/active 前端零调用（契约对齐，防演进踩空）；③ErrNoUser 吞形：grant/release 传不存在用户冻结 addCredits 静默 no-op 通知照发——Go credits.Add 显式 ErrNoUser 由管理台吞掉保冻结信封；④api_key 列表明文回显照抄（冻结 models.js:36 含原文+掩码双键）——管理台泄漏面，沿 C2 先例登记安全注记，未来收敛另立修正项（**二十一期②已收敛**：EndpointRow.APIKey 改 `json:"-"` 仅下发 api_key_masked/has_key——前端 AdminView.vue:206 只消费 masked 零消费实证，wire 与冻结分叉一处）；⑤instruction-versions DELETE 按修后语义打 prompt_templates（404 `not found`/400 文案逐字保持）——冻结查删 instruction_versions 死表（迁移后恒空→恒 404），F3 同制；⑥跨域共表写归表主域：retry-split 经 task.AdminRetrySplit（不删根资产=冻结原样，重复重拆根资产重复系冻结行为）、release 认领经 generation.ClaimManualRelease（CAS refunded=0+CONCAT(COALESCE(error,'')) 追加一步，修冻结先查后改的并发窗，F9 同向）；⑦**读侧衔接零失效逻辑与冻结同构，但 P0 起 modelCfg.setting() `key` 未反引号恒 1064——读侧配置链全哑的存量真 bug 由本期集成断言抓获并修复**（见 v1.7 修订行④）；⑧prompt_templates 面不做 in-band ensureSeed（冻结 promptCenter.ensureSeed legacy 迁移在 Go 无源库）：cmd/seed 表级哨兵启动补种 5 行已覆盖，管理台 CRUD 纯存储面；⑨providers/publish 反向死调用不实现=echo 404 兜底即冻结形态（AdminView 对应调用在冻结版同样无后端端点，前端零改动面）。

### M13 火花计划（2——v1.14 补登：index.js:739-766「火花计划」段；P0 统计残留第八例，与第七例同型「无模块行认领」——二十六期 e2e 全站巡检抓获 Go 零 spark 路由后回溯冻结源码定界）

报名激励：POST /api/spark/join（joined 守卫 400「您已加入火花计划」逐字 → users.spark_joined 置位 → 台账 kind='spark' desc「加入火花计划奖励（+100）」→ 通知「火花计划报名成功」→ {joined:true,bonus:100}）；GET /api/spark/me（submissions 三计数：total 系 status!='draft' / published / pending；三阶梯投影 min-max-per-name-perk〔星火 1-2 部 100/炬火 3-5 部 150/燎原 6+ 部 200〕+ reward_earned=published×per + next_tier 环形取下一档——燎原的下一档回星火）。

**Go 实现（v1.14 W26，`internal/domain/spark` 单包三件）**：逐字对齐无差异；errRespond 哨兵 ErrAlreadyJoined→400；集成第十六链 SparkChain（初态星火回退→join 三动作对账→重复 400→阶梯联动 submissions 直插→401 面）。


## 5. 数据模型概览

52 表终态（qmtv_main 单库）——分类/逐表裁决/视图清单见 `docs/数据库表设计-qmtv.md`（W9）；DDL 权威 = `backend/migrations/full/qmtv_main_full.sql`。状态表 22 张（version 乐观锁+entity_lifecycle_event 载体）、日志台账 4、字典配置 6、技术 1（refresh_tokens 重构）、资源明细 15、新增基建 4、裁剪 1（providers）。

**P2 DDL 收紧（delta/0002，2026-10-18 落地）**：tasks 5 可空 TEXT 列+shots 2 prompt 列 UPDATE 回填后 MODIFY NOT NULL（MySQL TEXT 无字面 DEFAULT——漏写列 strict mode 显式红=守卫语义）；seed.go tasks INSERT 全列写恒 `''`；C7 例外不动（videos.prompt/error 保持 `*string` NULL 透传冻结语义）；director_shots（M10）/workbench_tasks（M11）待域落地随域裁决。**seed Go 版一步到位差异**：producer@xingmang.cn 独立演示账号（referral_code=QMXINGMANG01，与 demo 并存）+台账单笔 init（沿 SQL seed 惯例；冻结 demo 为 0 元 register 标记行形态）+prompt_templates 表级 COUNT 哨兵补种 5 行模板（Go prompts 内置不读表，纯存储面对齐产物无差）。**恢复扫描窄投影**：task 恢复 `SELECT *` 遇可空列 NULL 整段静默 no-op 已修（SplitRecoverRow 5 列窄投影，语义等价）——W8 集成抽样抓获。

## 6. 非功能约束

- 性能：SSE 心跳 25s；视频轮询 8s 首查/10s 步进/15min 上限；workbench 轮询 3s/5s/180 次；LLM timeout 240s；图片供应商 180s；模型测试 45s。
- 启动恢复四扫描：videos.processing→failed+退款、tasks.split_status=processing→Mock 回退、workbench_tasks.processing→按 video 状态收口、assets.queue_status=generating→回 queued。**v1.3 P2 范围注记（W10 更新：四扫描已全部落地）**：Go P2 W6 已内联实现全部四处（退款走 F9 唯一口 ClaimVideoRefund 认领置位）+幂等可重复启动——原注记「workbench_tasks 随 M11」已过时。**实现差异登记（v1.3 W10 入正文）**：①逐段容错——冻结 videos 段无 catch（异常即崩启动，SQLite 同步 API 副作用非设计）vs Go 统一逐段容错不挡启动；②workbench finished_at 冻结 `datetime('now','localtime')` vs Go `NOW(3)`（方言惯例）；③恢复扫描用 SplitRecoverRow 窄投影（§5，W8 集成抽样抓获后修）。
- **跨聚合扣款时序（v1.3 定稿，取代 TECH.md §5 actor+outbox 草案原则）**：对齐冻结同步流——`handler 内 credits.TryDeduct（CAS）→ 业务落库（videos/images 行）→ providers 调用 → 失败 → credits.Refund（F9 唯一口）`；异步轮询 goroutine 持 ctx，重启丢轮询由启动恢复兜底（与冻结 setTimeout 语义等价）。不引 user actor 串行与 outbox 补偿：CAS 行锁已防超扣（冻结单进程无跨进程面）；RunWithRetry/isConflict 无生产原型（qmtv/pms 双侧零调用）。`internal/actor`、`internal/event`（outbox relay）维持 P0 骨架预留不强接。
- Mock 保真：SVG 渐变图/动画 SVG 视频/手写 WAV（16kHz/16bit/mono，voice 基频 female 523/male 330/child 659/narrator 392Hz）/19 种风格缩略图幂等文件名——Go 版产物对前端不可区分。
- 种子：admin@qmtv.com/admin123456（10 万积分）、demo@qmtv.com/demo123456（1335 积分+示例任务）、distribution_settings(0.08/0.02/100)、2 公告；幂等（有 admin 即跳过）。

## 7. 前端项目与演进轨道（v1.1 立项）

> 裁决（2026-10-17）：推翻「前端零改动、直接用 qmtv/ 冻结前端」口径——新建 `frontend/` 独立前端项目。选型依据与优劣对比见 `prd/前端技术对比-qmtv-v1.md`（§2.3 可迁移经验与 §5 钉子表继续有效）。

### 7.1 项目形态（初始 = 复制，行为对等）

| 项 | 内容 |
|----|------|
| 位置 | 仓库根 `frontend/`（与 `backend/` 对称，非冻结区） |
| 原样复制 | `src/`（31 文件 9668 行：根 5 + components 2 + views 24）、`index.html`、`vite.config.js`（proxy /api /files → :3300 不变）——探查证实 src/ 零路径耦合（import 仅 vue/vue-router + 相对路径，api.js 纯同源 `/api`），复制即用 |
| 裁剪重写 | `package.json`：仅保留前端 4 依赖（vue、vue-router + dev 的 vite、@vitejs/plugin-vue；原 6 依赖中 better-sqlite3/express/multer/jszip 属后端，jszip 前端也未用）；scripts 仅 dev/build/preview |
| 排除 | server/、data/、dist/、node_modules/、docs/、shots/、一次性脚本、package-lock.json（重装生成） |

### 7.2 演进三件（后续期渐进落地，吸收 pms 模式而非换框架）

| # | 项 | 治理什么 | 起步方式 |
|---|----|---------|---------|
| E1 | **@tanstack/vue-query** 服务端状态层 | 各页面自管 loading/缓存/失效的复杂度爆炸（Dashboard 1328 行单文件的根因）；等价获得 pms React Query 191 处 useQuery 的声明式红利 | 从 Dashboard/Workbench 类多数据源页面开始接线 |
| E2 | **vitest + @vue/test-utils** | 零测试欠账：242 端点契约无前端侧断言 | 新代码带测试起步；参照 pms「页面测试近 1:1」与 `docs/测试规划-qmtv.md` 范式（全 mock，不连外部服务） |
| E3 | **TypeScript 渐进类型化**（✅ 二十五期 2026-10-19 落地：31 文件全量 TS 化，strict 闸门 ci 3.8） | 劣势 #1「无类型，接口契约只存在于约定」 | `allowJs` 混用，从 `api.js`/`router.js` 类型化起步，.js 按文件渐进改 .ts；纪律：api 返回类型显式收敛、禁 `as any` 兜底 |

### 7.3 什么是 TS（TypeScript）

**TypeScript = JavaScript 超集 + 静态类型系统**（微软 2012 年发布）。所有合法 JS 都是合法 TS；它在 JS 基础上加了「编译期类型检查」这一层：

- **错误发现时机前移**：纯 JS 的类型错误运行时才爆（用户点按钮才发现 `undefined`）；TS 在编辑器/编译期立刻红线。
- **运行时零开销**：类型只是标注，编译（tsc / vite 内置 esbuild）时全部擦除，产出纯 JS——浏览器不认识也不需要认识 TS。
- **契约代码化**：接口形状写成类型，传错即编译失败，无需等运行时验证。

```ts
interface User { id: string; credits: number }        // API 契约的类型化

function deduct(u: User, n: number): boolean {
  return u.credits >= n;   // 若误写 u.cridets：TS 编译期报错；纯 JS 要等运行时 undefined 比较才出错
}

deduct("不是User对象", 5);  // ❌ TS 编译期拒绝；纯 JS 静默放行，线上才炸
```

- **渐进迁移**：`allowJs` 允许 .js/.ts 混用，可按文件逐个改后缀，不必 big-bang——对 qmtv 前端 9668 行 JS 的存量是唯一现实路径。
- **对本项目的意义**：PRD 里 242 端点的请求/响应契约目前只存在于本文档文字；E3 落地后变成代码里的编译期断言（api 函数返回类型显式收敛），契约漂移在 CI 就被拦下。

### 7.4 约束

- §2 全局契约 C1~C12 对 `frontend/` 全部继续有效——前端演进的是内部形态，不改后端兼容面。
- 端口：dev :5173 与冻结版同端口（同一时刻只跑一个；需要并行时 vite 自动 +1）；生产终态 Go :3300 托管 `frontend/dist`。
- 演进**不反向解冻** `qmtv/`：冻结区保持对照基准原样。

---

反链：

- [QMTV 功能规格说明书（FSD）v1.0 · 总则](/qmtv/fsd-qmtv/00-%E6%80%BB%E5%88%99/)
- [FSD 附录 · PRD 对照 / 代码域索引 / 差异台账](/qmtv/fsd-qmtv/99-%E9%99%84%E5%BD%95/)
- [FSD M1 · 认证与用户](/qmtv/fsd-qmtv/m01-%E8%AE%A4%E8%AF%81%E4%B8%8E%E7%94%A8%E6%88%B7/)
- [FSD M2 · 任务与拆解](/qmtv/fsd-qmtv/m02-%E4%BB%BB%E5%8A%A1%E4%B8%8E%E6%8B%86%E8%A7%A3/)
- [FSD M3 · 生成与素材](/qmtv/fsd-qmtv/m03-%E7%94%9F%E6%88%90%E4%B8%8E%E7%B4%A0%E6%9D%90/)
- [FSD M4 · 导出交付](/qmtv/fsd-qmtv/m04-%E5%AF%BC%E5%87%BA%E4%BA%A4%E4%BB%98/)
- [FSD M5 · 通知公告](/qmtv/fsd-qmtv/m05-%E9%80%9A%E7%9F%A5%E5%85%AC%E5%91%8A/)
- [FSD M6 · 组织协同](/qmtv/fsd-qmtv/m06-%E7%BB%84%E7%BB%87%E5%8D%8F%E5%90%8C/)
- [FSD M7 · 分销提现](/qmtv/fsd-qmtv/m07-%E5%88%86%E9%94%80%E6%8F%90%E7%8E%B0/)
- [FSD M8 · 充值](/qmtv/fsd-qmtv/m08-%E5%85%85%E5%80%BC/)
- [FSD M9 · 剧本库与投稿](/qmtv/fsd-qmtv/m09-%E5%89%A7%E6%9C%AC%E5%BA%93%E4%B8%8E%E6%8A%95%E7%A8%BF/)
- [FSD M10 · 导演与融合](/qmtv/fsd-qmtv/m10-%E5%AF%BC%E6%BC%94%E4%B8%8E%E8%9E%8D%E5%90%88/)
- [FSD M11 · 评估与工具](/qmtv/fsd-qmtv/m11-%E8%AF%84%E4%BC%B0%E4%B8%8E%E5%B7%A5%E5%85%B7/)
- [FSD M12 · 管理后台](/qmtv/fsd-qmtv/m12-%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0/)
- [FSD M13 · 火花计划](/qmtv/fsd-qmtv/m13-%E7%81%AB%E8%8A%B1%E8%AE%A1%E5%88%92/)
- [产品](/qmtv/product/)
