跳转到主要内容

FSD M2 · 任务与拆解

← 总则 | 附录 → | 上游:PRD §4 M2 实现落点:backend/internal/domain/task/(model/repo/service/service_gen)+ cmd/server/task_handlers.go(38 handlers 全集)+ cmd/server/task_assembly.go(LLM/模板装配)+ cmd/server/routes.go:324-361(挂载)。事实基准=Go 代码;冻结版 qmtv/server/index.js:228-366/routes2.js/routes3.js 仅差异对拍参照。

M2 任务与拆解

38 端点全量(基础信封见总则 §2.2)。本域是创作主链的起点:剧本 → 拆解(规则/LLM 双引擎)→ 场景/分镜 → 资产提取与生图。计费面集中在拆解(token 制/按次 20)与图片生成(动态价);SSE 仅 split 一类事件;上传面 2 条(slots/assets,20MB)。属主判定全 SQL 谓词,命中失败一律 404 不用 403。分镜双表通路中本域只写 shots(director_shots 归 M10,零触及)。

F2.01 任务列表

对应 PRD:§4 M2 · GET /api/tasks | 状态:实现 | 实现落点:task_handlers.go:51-56、task/repo.go:120-125 | UI:/dashboard(DashboardView 任务列表)| 场景挂钩:E2E-DASH-01(A1 回归锁:wire=驼峰 shotCount)/E2E-NAV-01

1 功能定义

当前用户任务列表倒序读,每行附镜数(shotCount 驼峰 wire——二十六期 A1 回归根因,e2e 回归锁钉死)。

2 触发条件与前置状态
  • 进入拆解台/任务列表刷新 → GET /api/tasks(Bearer);降级模式 503。
3 输入与校验

无参数。身份取自 JWT claims。

4 处理流程

SELECT t.*, (子查询 COUNT shots) AS shot_count … WHERE t.user_id=? ORDER BY t.created_at DESC → TaskWithCount 数组。宽扫描 t.* 依赖 0002 DDL 收紧(W8b 守卫 integration_test.go:461-484)。

5 输出与结果状态

200 {"ok":true,"data":[TaskWithCount…]}。行 wire:id/user_id/title/status/error/script_type/face_style/min_duration/max_duration/notes/extract_status/assets_data/split_status/style_id/extract_scope/extract_notes/created_at/updated_at(蛇形)+ shotCount(驼峰,model.go:60-63);script_text 指针 nil→JSON null。空列表 data:null(gorp nil slice——冻结恒 [],DIF-29-1)。

6 状态流转

无(纯读)。status 取值域见 F2.03 §6。

7 边界与异常
条件系统行为提示/码
无任务200 data:null(非 [],DIF-29-1)—
DB 错误500internal server error
8 权限与数据规则
  • 权限:登录态;WHERE user_id=? 谓词内联。
  • 数据规则:倒序 created_at DESC(无次级排序键,同秒行序可能抖动——冻结同构)。

F2.02 剧本拆解·旧版同步

对应 PRD:§4 M2 · POST /api/tasks | 状态:实现 | 实现落点:task_handlers.go:62-82、task/service.go:148-167/301-327 | UI:拆解台旧版提交入口| 场景挂钩:无(V2 为主链,本端点为冻结 legacy 保真)

1 功能定义

同步规则拆解:扣固定 20 积分 → 确定性 preprocess 切分场次/分镜/资产 → 立即返回统计。无 LLM、无异步。

2 触发条件与前置状态
  • 拆解台提交(legacy 形态)→ POST /api/tasks(Bearer)。
3 输入与校验
字段来源必填校验
titleJSON body否缺省「未命名」,截 60 rune(冻结 INSERT 用「未命名剧本」而台账 desc 用「未命名」——Go 把 desc 缺省同化进 title,DIF-29-13)
script_textJSON body是TrimSpace=="" → 400 剧本内容不能为空(冻结 index.js:236 同文案)
4 处理流程

扣费在插任务前,Insert 失败不退(service.go:159-163,冻结同序)。三次 Insert 均 _ = 吞错(service.go:314/318/324)。INSERT 显式写 18 列零值(script_type:''/min_duration:0/split_status:'' 等覆盖 DDL 默认——冻结走 SQLite DEFAULT,DIF-29-5)。

5 输出与结果状态

200 {"ok":true,"data":{"id","sceneCount","shotCount"}}(sceneCount/shotCount 驼峰,task_handlers.go:77-81;冻结 index.js:252 同)。

6 状态流转

tasks.status:插入即 'complete'(无 processing 中间态);split_status:INSERT 显式写 ''(18 列零值,DIF-29-5——非 DDL 默认 'done');本端点不消费该列。

7 边界与异常
条件系统行为提示/码
剧本空白400剧本内容不能为空
余额 <20402积分不足(零台账)
Mock 拆解确定性:场次按「第X场」标记切块(无标记按空行 >10 rune);每场 34 镜;人物资产 23 汉字冒号启发式 ≤8;提示词走 promptcenter 模板(tpl 查库单级,景别 index%4 轮转)—
8 权限与数据规则
  • 权限:登录态;创建即本人(user_id 取 claims)。
  • 数据规则:写 tasks/shots/assets/shot_images/credit_records 五表。

F2.03 剧本拆解·V2 双引擎

对应 PRD:§4 M2 · POST /api/tasks/v2 | 状态:实现 | 实现落点:task_handlers.go:140-160、task/service.go:241-299/330-366 | UI:拆解台主提交(DashboardView)| 场景挂钩:E2E-DASH-01(Mock 同步分支:toast「拆解完成」+镜数>0)

1 功能定义

token 制计费的剧本拆分:LLM 可用走异步拆解(SSE split 事件+站内通知,失败回退规则拆解);LLM 不可用走规则拆解同步返回。计费 ceil(tokens/1000) 上限 1。

2 触发条件与前置状态
  • 拆解台提交(主链)→ POST /api/tasks/v2(Bearer)。
  • LLM 可用判定 llm.Config.Available()(三元全非空,llm.go:24);配置三级链 active_model_text→model_endpoints→KV llm_*(task_assembly.go:49-59)。
3 输入与校验
字段来源必填校验
titleJSON body否缺省「未命名」截 60
script_textJSON body是空 → 400 剧本内容不能为空。唯一校验
script_type / face_styleJSON body否orDefault 缺省 domestic/asian_chinese——无冻结二值化归一(任意值透传,DIF-29-14)
min_duration / max_durationJSON body否maxInt(入参,1)/maxInt(入参,3)
notes / style_idJSON body否style_id 缺省 realfilm
4 处理流程

runLLMSplit 后台 goroutine defer recover() 兜底(service.go:332);重建三删序 ClearShotImages→DeleteShotsByTask→DeleteRootAssetsByTask(service.go:411-419)。

5 输出与结果状态

V2Result 键序 id, sceneCount(omitempty), shotCount(omitempty), async, engine, cost, tokens_estimated(service.go:243-251):LLM 分支无 sceneCount/shotCount 键(冻结同);Mock 分支 async:false + 两计数。

6 状态流转
  • tasks.status:processing(仅本端点插入)→ complete(Mock/LLM 完成/回退成功/restart/启动恢复)|failed(LLM 双重失败、mark-failed)。
  • tasks.split_status:DDL 默认 'done'|'processing'(LLM 可用)→ 'done'(完成/回退)|'failed'(双重失败)。代码无 'idle'/'fallback' 写入点(PRD :142 状态机表含此二态——PRD 该行已加注「冻结与 Go 均无写入点」,用户裁决〔三十期〕判定非系统需要不实现:前端零消费〔DashboardView 仅认 processing/failed〕+fallback 系同步内联回退无可观察窗口,勿照抄亦勿实现)。
  • 服务重启悬挂行:RecoverProcessingSplits(service.go:444-463)扫 split_status='processing' → fillWithMock 直插不删基座 → 恢复完成,通知「拆解已回退完成」,无 SSE 帧。
7 边界与异常
条件系统行为提示/码
剧本空白400剧本内容不能为空
余额不足402积分不足
LLM 失败不回 HTTP(异步已 200);回退规则拆解+通知+SSE fallback—
回退再失败status/split_status 双 failed(已扣费不退——冻结同)—
token 估算rune 数×1.6(冻结 UTF-16 .length,BMP 外计数不同,DIF-29-15);计费 ceil(冻结 round——credits.TextCost 正确 round 实现存续未用 billing.go:118-131,DIF-29-6)—
模型名缺配置台账 desc 出现「…tokens / ,-N)」空串形态(冻结缺行回「星芒文本引擎」,DIF-29-7)—
8 权限与数据规则
  • 权限:登录态;写 tasks/shots/assets/shot_images/credit_records/notifications。
  • 数据规则:TryDeduct 带 task_id=id(冻结 routes2.js:158 同);通知两文案冻结逐字(service.go:352-353/359-361)。

F2.04 任务详情读取(:id / full / full2)

对应 PRD:§4 M2 · GET /api/tasks/:id、GET /api/tasks/:id/full、GET /api/tasks/:id/full2 | 状态:实现 | 实现落点:task_handlers.go:84-97/275-297、service_gen.go:300-427 | UI:VideoView/DashboardView 任务详情| 场景挂钩:E2E-NAV-01(读面巡检)

1 功能定义

任务详情三形态:基础(shots+原始 assets)、full(assets 带图 URL+assets_data 缓存)、full2(variants 衍生体+风格名/缩略图+组间尾帧衔接)。

2 触发条件与前置状态
  • 详情页/分镜工作台加载 → 三端点(Bearer);任务不存在或非本人 → 404。
3 输入与校验

路径 :id。无 body。

4 处理流程
  • :id:ByID(WHERE id=? AND user_id=?)→ ShotsWithSlots(shots ORDER index_no + 槽位按 shot 分组 ORDER slot_no + refAssetCount=COUNT assets)→ 原始 assets(无 image_url,task_handlers.go:95——冻结 index.js:261 同 quirk)。
  • full:ByID → ShotsWithSlots → AssetsByTask(LEFT JOIN images 取 image_url)→ assets_data=缓存优先(坏 JSON 宽容 nil)否则从根资产现拼(5 键 item:id/name/description/image_id/image_url——冻结塞原始全列行,DIF-29-16)。
  • full2:再加 VariantsByTask(parent_id<>’’)合并进缓存/fallback;tail_by_group 组间尾帧 map(组内最后有图镜 image_id,首镜且 groupNo>1 配 prev_tail_image_id,service_gen.go:355-372);style_name=StyleName(19 风格目录缺省「真人电影」)、style_thumb 幂等落盘 style_<id>.svg(不在目录→null)。
5 输出与结果状态
  • :id → {"task","shots":[ShotView],"assets":[原始行]};shots 空为 [](make 初始化)、slots/assets 空 null(DIF-29-1)。
  • full → {"task","shots","assets","assets_data"}。
  • full2 → {"task":{…Task,"style_name","style_thumb"},"shots","assets","variants","assets_data","tail_by_group"}——style_name 嵌套在 task 内(前端 curTask.style_name 消费)。
6 状态流转

无(纯读)。

7 边界与异常
条件系统行为提示/码
非本人/不存在404not found
assets_data 缓存坏 JSON静默忽略走 fallback 拼装—
style_id 不在目录style_name=「真人电影」、style_thumb=null—
8 权限与数据规则
  • 权限:登录态 + 任务属主。
  • 数据规则:ShotView 含 slots/refAssetCount(驼峰)/prev_tail_image_id(full2 组首镜);variants 键映射 parent_id→item 数组。

F2.05 任务生命周期管理(title/delete/duplicate/mark-failed/restart)

对应 PRD:§4 M2 · PUT /api/tasks/:id/title、DELETE /api/tasks/:id、POST /api/tasks/:id/duplicate、POST /api/tasks/:id/mark-failed、POST /api/tasks/:id/restart | 状态:实现 | 实现落点:task_handlers.go:99-138、task/service.go:168-230/583-587、task/repo.go:146-175/234-248 | UI:任务卡操作菜单| 场景挂钩:无独立 e2e

1 功能定义

任务五个管理动作:改名、删除(三删序+孤儿保留)、复制(含分镜/资产快照)、标记失败、重启清图。

2 触发条件与前置状态
  • 任务卡操作 → 对应端点(Bearer);title/delete/duplicate 404 面,mark-failed 无存在性检查。
3 输入与校验
端点输入校验
PUT /tasks/:id/title{"title":string}截 60 rune;无 404 面(0 行仍 200,冻结同)
DELETE /tasks/:id—ByID 谓词内联;0 行 → 404 not found
POST /tasks/:id/duplicate—ByID 404 面
POST /tasks/:id/mark-failed—无校验(0 行也 200,冻结 routes2.js:250 同)
POST /tasks/:id/restart—ByID 404 面
4 处理流程
  • title:UPDATE tasks SET title=?, updated_at=NOW(3) WHERE id=? AND user_id=?。
  • delete:三删序 shots→assets→tasks(子查询属主谓词);不删 shot_images/image_histories/images/videos(孤儿保留,repo.go:151 注释冻结 §1A)。
  • duplicate:新任务 title=原题+"-副本" 截 60(DIF-29-10),status/script_text 沿源(nil 透传);逐镜复制(image_id/video_id 清空);仅复制源镜存在 slot_no=1 的槽位(service.go:200-204,冻结每镜无条件插——DIF-29-10);assets 全复制但 parent_id 不带(衍生变根资产,冻结 index.js:290-292 同 quirk)。
  • mark-failed:UPDATE tasks SET status='failed';不触 split_status。
  • restart:RestartClears 四步——shots.image_id=’’+status=‘idle’、shot_images.image_id=’’、tasks status=‘complete’+extract_status=’’+assets_data=’’、assets.image_id=’’;保留 shots/assets/槽位行(NULL 语义翻译 ‘’,model.go:4-5)。
5 输出与结果状态

五者均 {"ok":true,"data":true};duplicate 另返 {"id":"<新任务>"}。

6 状态流转
  • delete:行删除(级联仅 shots/assets 两层)。
  • mark-failed:complete|processing → failed。
  • restart:任意 → complete;shots done → idle;extract_status → ''。
7 边界与异常
条件系统行为提示/码
title 改他人任务0 行 200 true(无存在性检查)—
delete/mark-failed 非本人delete 404;mark-failed 0 行 200not found
duplicate 长 title截 60(冻结不截,DIF-29-10)—
8 权限与数据规则
  • 权限:登录态;delete/duplicate/restart 谓词内联属主。
  • 数据规则:restart 是「清成果不删结构」语义——与 delete(删结构留孤儿)互补。

F2.06 智能资产提取(extract / extract2 / extract-settings)

对应 PRD:§4 M2 · POST /api/tasks/:id/assets/extract、POST /api/tasks/:id/assets/extract2、PUT /api/tasks/:id/extract-settings | 状态:实现 | 实现落点:task_handlers.go:174-208、service_gen.go:34-159 | UI:拆解台资产区「智能提取」| 场景挂钩:无 e2e(人工清单 T2 族)

1 功能定义

对拆解产出的角色/场景/道具资产批量生图:v1 按资产类型模板出图并写 assets_data 缓存;v2 支持 scope 范围+自定义要求+风格前缀;extract-settings 持久化提取范围与要求。

2 触发条件与前置状态
  • 资产区按钮 → extract/extract2(Bearer);设置面板保存 → extract-settings。前置:任务已有资产行。
3 输入与校验
端点输入校验
extract (v1)无 body资产为空 → 400 该任务没有可提取的资产
extract2 (v2)无 bodyscope 内(根资产 ∩ extract_scope)为空 → 400 提取范围内没有资产(可在自定义提取设置中调整范围)
extract-settings{"scope":[...],"notes":string|null}scope 白名单过滤 character/scene/prop 后 join(空数组→’’,冻结同);notes 非 null 才覆盖
4 处理流程
  • 计费:unit=ImageCost(settings image_generation 默认 10);v1 cost=unit×资产数(含衍生体),desc 智能资产提取(N 个资产 × 10,-<cost>);v2 cost 同式,desc 智能资产提取 ×N(风格:<风格名>,-<cost>);type=image。
  • v2 prompt=「视觉风格:<风格名>。」+AssetPrompt+(ExtractNotes?\n[生成要求] <notes>)。
  • v1 失败 502 文案金额用总额(冻结 v1 用单价——退款金额本身正确,DIF-29-2);v2 失败退全额(冻结 v2 传总额笔误的保真对齐,service_gen.go:121-123 注释)。
5 输出与结果状态

{"extract_status":"done","assets_data":{"characters":[],"scenes":[],"props":[]}};item 键 id/name/description/image_id/image_url/prompt——v2 冻结显式带 variants:[],Go omitempty 键缺失(DIF-29-3)。

6 状态流转

tasks.extract_status:'' → 'done'(成功路径先行置位,失败不回滚——下次重试覆盖);assets_data 缓存同步覆盖。

7 边界与异常
条件系统行为提示/码
无资产400该任务没有可提取的资产
scope 内为空400提取范围内没有资产(可在自定义提取设置中调整范围)
余额不足402积分不足
单张生成失败退单价 continue + 最终 502图片生成失败:<cause>(<cost> 积分已退回)
8 权限与数据规则
  • 权限:登录态+任务属主;写 images/assets/tasks/credit_records。
  • 数据规则:v2 targets=根资产(parent_id=’’)∩scope;衍生体在 v1 中也计费(含衍生体)。

F2.07 AI 重新拆解(resplit)

对应 PRD:§4 M2 · POST /api/tasks/:id/resplit | 状态:实现 | 实现落点:task_handlers.go:514-533、service.go:488-573 | UI:分镜区「AI 重新拆解」| 场景挂钩:无 e2e

1 功能定义

按自然语言指令对已有分镜做密度调整重切:更多/细致/丰富→每场 5 镜、精简/减少/合并→每场 2 镜、缺省 3~4;固定扣 20 积分。

2 触发条件与前置状态
  • 分镜区操作 → POST /api/tasks/:id/resplit(Bearer);前置:任务存在(不要求 complete)。
3 输入与校验
字段来源必填校验
instructionJSON body否trim 后密度匹配;无 400 面
4 处理流程

ByID → 扣 20(desc AI 重新拆解(<instruction截16>)(-20),空指令 AI 重新拆解(-20),type=preprocess)→ 三删重建(同 F2.03)→ preprocess 密度调参重切 → 重插 shots(每组首镜 independent 余 anchor_prev)+slot1 → SetSplitInstruction(存 trim 值)。

5 输出与结果状态

{"instruction","sceneCount","shotCount"}——instruction 回显的是请求原始值非 trim 值(task_handlers.go:524-532;冻结回显 trim 后值,DIF-29-8)。

6 状态流转

shots 全量重建(id 全新);split_instruction 列更新;status/split_status 不变。

7 边界与异常
条件系统行为提示/码
非本人404not found
余额 <20402积分不足
指令无密度词缺省密度 3~4 镜/场—
8 权限与数据规则
  • 权限:登录态+属主;写 shots/shot_images/tasks/credit_records。

F2.08 分镜图批量与续生成(continue / auto-generate / cancel / storyboard-batch)

对应 PRD:§4 M2 · POST /api/tasks/:id/continue、POST /api/tasks/:id/scenes/auto-generate、POST /api/tasks/:id/scenes/auto-generate/cancel、POST /api/tasks/:id/storyboard-batch | 状态:实现 | 实现落点:task_handlers.go:299-334/346-357/535-553、service.go:590-703、service_gen.go:634-695 | UI:分镜区批量操作| 场景挂钩:无 e2e(人工清单 T2/T3 族)

1 功能定义

四个批量/续生成入口:continue 补缺图镜、auto-generate 带连续性提示词串联生成、cancel 纯 stub 停止提示、storyboard-batch 按模式(全部/补缺/选中)批量生图。

2 触发条件与前置状态
  • 分镜区批量按钮 → 对应端点(Bearer)。
3 输入与校验
端点输入校验
continue无 body404 面;无缺图镜时 cost=0 不扣(len>0 才 TryDeduct)
auto-generate无 body404 面;len=0 也 TryDeduct(0) 落 0 元台账行(冻结 util.js:86-91 同 quirk)
auto-generate/cancel无 body纯 stub 无校验
storyboard-batch{"mode":"missing"|"selected"|"all","shot_ids":[]}mode 空→missing;空列表 → 400 没有缺失的图片(missing)/未选择任何分镜(selected);all+ids 取交集
4 处理流程
  • continue:pending=缺图镜(image_id=’’/NULL)→ cost=unit×len(desc 继续生成未完成分镜 ×N(-<cost>))→ 逐镜 Generate(“16:9”,“分镜<index_no>”) → settleShotImage(InsertImage kind=storyboard+SetShotImage done+slot1 回填+InsertHistory)。
  • auto-generate:list=pending 空则全量镜;prompt 追加连续性段——非首镜 \n[连续性] 与上一镜头(<prevURL||前帧>)保持人物/光影/机位连续。、首镜 \n[起始镜头] 由智能体判断开场构图。;desc 故事板全自动串联生成 ×N(-<cost>)。
  • cancel:{"cancelled":true,"note":"已停止后续镜头的排队(已产生的费用不退)"},不写任何表。
  • storyboard-batch:desc 批量生图(全部|仅补缺失|选中 ×N,-<cost>);连续性措辞与 auto-generate 不同源(「与上一镜(<prevURL||前帧>)保持一致。」/首镜「智能体判断开场构图」);失败退单价(冻结传总额系笔误,Go 统一单价——PRD v1.3 已登记,service_gen.go:632-633)。

settle 失败(DB 错)时该张已生成不退费、仅 failed++(service.go:616-619/689-694,冻结同)。

5 输出与结果状态

continue/auto-generate/storyboard-batch → {"generated":N,"failed":N};cancel → 上述 stub 体。

6 状态流转

成功镜:shots.status idle → done(SetShotImage);失败镜 status 不动(仅计数)。

7 边界与异常
条件系统行为提示/码
非本人404not found
余额不足402积分不足
selected 空数组400未选择任何分镜
missing 无缺图400没有缺失的图片
单张失败退单价 continue502 图片生成失败:…
8 权限与数据规则
  • 权限:登录态+属主;写 images/shots/shot_images/image_histories/credit_records。
  • 数据规则:三批量端点计价均为动态 unit(settings 可调)——与 F2.09 的静态价不同源。

F2.09 单镜故事板生成(images/storyboard/generate)

对应 PRD:§4 M2 · POST /api/images/storyboard/generate(v1.3 W10 归组 M2——物理在冻结 index.js 生成段)| 状态:实现 | 实现落点:task_handlers.go:420-441、service_gen.go:581-623 | UI:分镜卡「生成」/「2K 预览」| 场景挂钩:间接 E2E-NAV-01

1 功能定义

单镜故事板图/2K 预览生成:静态价(图 10/预览 15,无 settings 键),可选关联 shot 回填。

2 触发条件与前置状态
  • 分镜卡生成按钮 → POST /api/images/storyboard/generate(Bearer)。
3 输入与校验
字段来源必填校验
shot_idJSON body否非空→ShotByID+属主校验(err→404 shot not found);空则跳过校验也可用(冻结 index.js:345 同)
promptJSON body否缺省取 shot.ImagePrompt
ratioJSON body否—
previewJSON body否true→2K 形态
4 处理流程

计费 cost=10(COSTS.image)/15(COSTS.image_2k 恒定无 settings 键,billing.go:72-73);kind=‘storyboard’/‘preview_2k’;desc 分镜图生成(-10)/2K预览(-15)(冻结「2K预览生成(-15)」缺「生成」二字,DIF-29-9);task_id=shot 有则其 task 否则 ‘’。Generate → 失败退同额+502;成功 InsertImage(prompt=请求原值非解析值)→ sh != nil && !preview 才回填 shots/slot1/histories(linkShotImage 不重复插 images——W3 冒烟双插主键冲突修,service_gen.go:615-621 注释)。

5 输出与结果状态

{"id","url"}。

6 状态流转

关联 shot 非预览时:shots.status → done、slot1 回填、histories +1。

7 边界与异常
条件系统行为提示/码
shot_id 给了但不存在/非本人404shot not found
余额不足402积分不足
生成失败退款+502分镜图生成(-10)失败自动退回族文案
8 权限与数据规则
  • 权限:登录态;shot 关联时校验属主。
  • 数据规则:静态价与 F2.08 动态价刻意不同源(billing.go:72-73 注释);Mock label=desc 全串(冻结 label「分镜图」/「2K预览」——影响 Mock SVG 文件名,DIF-29-9)。

F2.10 场景分组管理(scenes2 / rename / delete)

对应 PRD:§4 M2 · GET /api/tasks/:id/scenes2、PUT /api/tasks/:id/scenes/:groupNo、DELETE /api/tasks/:id/scenes/:groupNo | 状态:实现 | 实现落点:task_handlers.go:472-512、repo.go:356-371 | UI:分镜区场景分组栏| 场景挂钩:无 e2e

1 功能定义

场景分组聚合读与改名/删除:scenes2 按 group_no 聚合统计,rename/delete 按 group_no 批量作用。

2 触发条件与前置状态
  • 分镜区场景栏 → 三端点(Bearer)。
3 输入与校验
端点输入校验
GET scenes2—ByID 404 面
PUT scenes/:groupNo{"scene_title":string} 截 60groupNo 非数字→归 0(task_handlers.go:493-494;冻结 NaN 绑定 TypeError 500——Go 200 零行,DIF-29-11)
DELETE scenes/:groupNo—同上;0 行也 200
4 处理流程
  • scenes2:SELECT group_no, MIN(scene_title), COUNT(*) shot_count, MIN(index_no) first_index … GROUP BY group_no ORDER BY group_no(MIN(scene_title) 字典序最小而非首镜标题——冻结同 SQL quirk 保真)。
  • rename:UPDATE shots SET scene_title=? WHERE task_id=? AND group_no=?。
  • delete:DELETE FROM shots WHERE task_id=? AND group_no=?。
5 输出与结果状态

scenes2 → [{"group_no","scene_title","shot_count","first_index"}](全蛇形,空→null DIF-29-1);rename/delete → data true。

6 状态流转

delete 组删除后 shots 行消失(槽位/histories 孤儿保留)。

7 边界与异常
条件系统行为提示/码
groupNo=abc折 0 匹配零行 200(DIF-29-11)—
组不存在rename/delete 0 行 200—
8 权限与数据规则
  • 权限:登录态+属主(task_id 谓词)。

F2.11 分镜编辑与槽位(shots / slots / shot-images / histories / shots2)

对应 PRD:§4 M2 · 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 | 状态:实现 | 实现落点:task_handlers.go:359-470、service.go:492-501/570-578、service_gen.go:504-559、repo.go:322-354/408-437/465-470 | UI:分镜卡编辑器| 场景挂钩:无独立 e2e

1 功能定义

单镜六动作:三提示词编辑、删除、参考槽位添加(上传或生图)、槽位删除(slot1 保护)、历史读、分辨率参数。

2 触发条件与前置状态
  • 分镜卡操作 → 六端点(Bearer);slots 上传限额 20MB(up20)。
3 输入与校验
端点输入校验
PUT /shots/:idimage_prompt/video_prompt/transition_mode 均可空指针null=跳过该键(COALESCE 部分更新契约 §6);属主两步查(ShotByID+task 属主)失败 404 shot not found
DELETE /shots/:id—404 面;不删槽位/histories/images
POST /shots/:id/slotsmultipart file + form prompt/kind/ratio/label无文件≠错误——无文件走生图分支扣 10
DELETE /shot-images/:slotId—slot_no=1 → 400 故事板槽位不可删除;其余 404 面
GET /shots/:id/histories—404 面
PUT /shots2/:id/params{"resolution":string}非 720p/1080p 静默忽略仍 200(冻结 routes3.js:341-343 同)
4 处理流程
  • slots 双路:有文件→images.Upload 零费;无文件→静态价 10(desc 镜头图片生成(-10),task_id=’’)→Generate→失败退+502。成功:slotNo=MAX(slot_no)+1、InsertImage(kind=orDefault(kind,“anchor”))、InsertShotImages(label=orDefault(label,“参考图”));生成路另 SetShotImage(done)+InsertHistory(_ = 吞错)。
  • shotOwned(service.go:492-501):ShotByID err→ErrShotNotFound;task 属主 err→同——两步均 404 shot not found。
5 输出与结果状态
  • slots → {"slot_no":int,"image_id","url"};histories → [{"id","shot_id","image_id","prompt","created_at","file_path"}](空→null DIF-29-1);其余 data true。
6 状态流转
  • 生图路 slots/SetShotImage:shots.status → done。
  • params:resolution 列更新(白名单内才生效)。
7 边界与异常
条件系统行为提示/码
shot 非本人404shot not found(六端点同哨兵)
删 slot1400故事板槽位不可删除
slots 无文件且余额不足402积分不足
slots 生图失败退单价502
resolution=480p静默忽略 200—
8 权限与数据规则
  • 权限:登录态+shot→task 属主链。
  • 数据规则:本域写 images 表为跨域共表直插(10 列形态,repo.go:551-556,主归属 generation——总则 §1 术语);histories 行 id 前缀 si_(冻结 ih_;ids.PrefixImageHistory=“ih_” 仅 director 域消费(repo.go:319 InsertImageHistory),前缀双轨并存——DIF-29-4,99-附录:57)。

F2.12 资产图编辑与衍生(assets upload / regenerate / derive)

对应 PRD:§4 M2 · POST /api/assets/:id/image/upload、POST /api/assets/:id/regenerate、POST /api/assets/:id/derive | 状态:实现 | 实现落点:task_handlers.go:213-272、service_gen.go:162-276 | UI:资产卡操作| 场景挂钩:无 e2e

1 功能定义

资产图三动作:手动上传替换(同步 assets_data 缓存)、局部修改生图、衍生变体(新建子资产行)。

2 触发条件与前置状态
  • 资产卡操作 → 三端点(Bearer);upload 限额 20MB。
3 输入与校验
端点输入校验
uploadmultipart file无文件 → 400 缺少文件(冻结 routes2.js:217 同文案);资产 404 not found
regenerate{"instructions":string}trim 空 → 400 请填写局部修改说明
derive{"variant_desc":string}trim 空 → 400 请描述变体(如:换一套服装 / 中老年形象)
4 处理流程
  • upload:AssetByID(JOIN tasks 属主)→ Upload(FixFilename)→ InsertImage(kind=‘asset’) → UpdateAssetImage → 同步 assets_data 缓存(读改写 JSON 三组逐项改 image_id/image_url,坏 JSON 静默跳过)。
  • regenerate:扣 unit(desc 资产图局部编辑:<名>(-<unit>))→ prompt=AssetPrompt+\n[局部修改] <ins>(其余部分保持原图不变) → Generate → 失败退+502 → InsertImage(kind=‘asset_edit’) → UpdateAssetImage(不写缓存,冻结同)。
  • derive:扣 unit(desc 角色衍生:<名>·<variant截12>(-<unit>))→ 先插子资产行(name=<名>_衍生<N>,parent_id=源)→ prompt=AssetPrompt+\n[衍生变体] <v>。保持角色五官与身份一致,仅按描述改变形态。视觉风格:<风格名>。 → Generate → 失败退+502(子资产行残留无回滚——冻结同序 routes3.js:170-172)→ InsertImage(kind=‘asset_variant’) → UpdateAssetImage(子)。
5 输出与结果状态

upload/regenerate → {"image_id","url"};derive → {"asset_id","image_id","url","name"}。

6 状态流转
  • derive:assets 树新增子节点(parent_id 链)。
  • upload:assets.image_id 替换+缓存同步;regenerate 仅替换 image_id。
7 边界与异常
条件系统行为提示/码
缺文件400缺少文件
说明/变体描述空400上述两句
余额不足402积分不足
生成失败退单价502 图片生成失败:…
derive 生成失败退款但子资产行残留—
8 权限与数据规则
  • 权限:登录态+资产→任务属主链(AssetByID JOIN tasks)。
  • 数据规则:FixFilename 处理中文文件名(总则坑表);缓存同步只覆盖命中的 name 项。

F2.13 任务台账与资产库(credit-records / library-apply / asset-library)

对应 PRD:§4 M2 · GET /api/tasks/:id/credit-records、POST /api/tasks/:id/library-apply/:libId、GET/POST /api/asset-library、POST /api/asset-library/:id/share、DELETE /api/asset-library/:id | 状态:实现 | 实现落点:task_handlers.go:555-631、service_gen.go:705-764、repo.go:569-605 | UI:任务详情台账页/素材库页| 场景挂钩:E2E-CRD-01(台账键形同源 credits.Record)

1 功能定义

任务维度台账对账读(LIMIT 200+汇总)、资产库条目应用到任务、素材库个人/组织共享 CRUD。

2 触发条件与前置状态
  • 任务详情台账 tab → credit-records;素材库页 → asset-library 四动作;「应用到任务」→ library-apply。
3 输入与校验
端点输入校验
GET credit-records—任务 404 not found
POST library-apply/:libId—任务 → 404 task not found;库条目 → 404 asset not found(双哨兵分叉)
GET /asset-library—orgIDOf 查失败按无组织
POST /asset-library{"name","type","description","image_id","asset_id"}trim(name)=="" → 400 名称必填;asset_id 查不到静默忽略;type 缺省 character
POST …/:id/share—非本人 404 not found;无组织 → 400 请先加入企业组织
DELETE …/:id—不存在也 200(service_gen.go:743-746 注释)
4 处理流程
  • credit-records:credits.RecordsByTask(LIMIT 200 ORDER created_at DESC,id DESC)+ 汇总(负额和+by_type 分组,service.go:79-93)。
  • library-apply:双 404 面 → InsertAssets(desc=lib.Description+(来自资产库),image_id 沿库)。
  • asset-library 列表:(l.user_id=? OR (org_shared=1 AND l.user_id IN (SELECT id FROM users WHERE org_id=?))) ORDER created_at DESC LIMIT 200 JOIN users 取 owner_email(共享子查询走 users.org_id 单组织模型——冻结 org_members 双向 JOIN,语义等价登记 repo.go:574)。
  • share:翻转 org_shared → 返回 {"org_shared":0|1}(整数)。
5 输出与结果状态
  • credit-records → {"list":[Record…],"summary":{"total_spent","by_type"},"task_title"};Record wire id,user_id,task_id,amount,balance_after,type,description,created_at(task_id NULL 行 wire “")。
  • asset-library → [LibraryView…](+owner_email,空→null DIF-29-1);add → {"id"};apply → {"asset_id"}。
6 状态流转
  • library-apply:assets 表新增行(任务下)。
  • share:org_shared 0⇄1 翻转。
7 边界与异常
条件系统行为提示/码
任务不存在credit-records 404 not found;apply 404 task not found—
库条目不存在apply 404 asset not found—
名称空400名称必填
无组织 share400请先加入企业组织
删不存在条目200 true—
8 权限与数据规则
  • 权限:登录态;asset-library 写面全部 user_id=? 谓词(share 的组织可见性靠 org_shared 列+列表子查询)。
  • 数据规则:台账读面与 M1 F1.06 全局流水同源 credits 域;LIMIT 200。

M2 端点覆盖对账

#端点功能节
1GET /api/tasksF2.01
2POST /api/tasksF2.02
3POST /api/tasks/v2F2.03
4GET /api/tasks/:idF2.04
5PUT /api/tasks/:id/titleF2.05
6DELETE /api/tasks/:idF2.05
7POST /api/tasks/:id/duplicateF2.05
8POST /api/tasks/:id/assets/extractF2.06
9POST /api/tasks/:id/assets/extract2F2.06
10PUT /api/tasks/:id/extract-settingsF2.06
11GET /api/tasks/:id/fullF2.04
12GET /api/tasks/:id/full2F2.04
13POST /api/tasks/:id/mark-failedF2.05
14POST /api/tasks/:id/restartF2.05
15POST /api/tasks/:id/continueF2.08
16POST /api/tasks/:id/scenes/auto-generateF2.08
17POST /api/tasks/:id/scenes/auto-generate/cancelF2.08
18GET /api/tasks/:id/scenes2F2.10
19PUT /api/tasks/:id/scenes/:groupNoF2.10
20DELETE /api/tasks/:id/scenes/:groupNoF2.10
21POST /api/tasks/:id/resplitF2.07
22POST /api/tasks/:id/storyboard-batchF2.08
23GET /api/tasks/:id/credit-recordsF2.13
24POST /api/tasks/:id/library-apply/:libIdF2.13
25PUT /api/shots/:idF2.11
26DELETE /api/shots/:idF2.11
27POST /api/shots/:id/slotsF2.11
28DELETE /api/shot-images/:slotIdF2.11
29GET /api/shots/:id/historiesF2.11
30PUT /api/shots2/:id/paramsF2.11
31POST /api/assets/:id/image/uploadF2.12
32POST /api/assets/:id/regenerateF2.12
33POST /api/assets/:id/deriveF2.12
34POST /api/images/storyboard/generateF2.09
35GET /api/asset-libraryF2.13
36POST /api/asset-libraryF2.13
37POST /api/asset-library/:id/shareF2.13
38DELETE /api/asset-library/:idF2.13

38/38 对账 routes.go:324-361 与 PRD §4 M2。注:/tasks/:id/export/* 6 条共享前缀归 M4 分册(echo radix 静态段优先语义见 m04);director_shots 双表通路另一半归 M10。

DIF 新发现(本期取证,PRD §3/§4 未登记——入 99-附录 B):DIF-29-1 空列表 wire null vs 冻结 [](六读面)/DIF-29-2 extract v1 失败 502 文案金额用总额(冻结单价,退款额正确)/DIF-29-3 extract2 item 缺 variants 键/DIF-29-4 image_histories 前缀 si_ vs 冻结 ih_(ids.go:60 PrefixImageHistory 定义未用)/DIF-29-5 legacy/duplicate INSERT 18 列零值覆盖 DDL 默认/DIF-29-6 V2 计费 ceil vs 冻结 round(TextCost 正确实现并存未用)/DIF-29-7 V2 台账模型名可空串/DIF-29-8 resplit 回显原始 instruction/DIF-29-9 preview 台账 desc 缺「生成」+Mock label 偏差/DIF-29-10 duplicate 截 60+槽位条件复制/DIF-29-11 groupNo 非数字冻结 500 vs Go 200/DIF-29-12 bind 失败文案统一化/DIF-29-13 legacy 缺省标题「未命名」vs 冻结「未命名剧本」/DIF-29-14 V2 script_type/face_style 无二值化/DIF-29-15 token 估算 rune vs UTF-16/DIF-29-16 full fallback 拼装 5 键收窄。