feat(media): 作品保存恢复 - 按原任务重试交付并避免重复生成计费

This commit is contained in:
ddCat
2026-09-22 15:47:24 +08:00
parent acbb354e97
commit 48a82ded74
49 changed files with 1710 additions and 50 deletions

View File

@@ -15,7 +15,14 @@ description: 影策后端核心持久化表与状态边界
## 结构迁移与版本
当前结构版本为 33。版本 33 `oauth_state_accepted_terms` 为 `o_auth_states` 增加非空布尔列 `accepted_terms`,默认 `false`;保留旧 state 的身份、PKCE、跳转目标及有效期,不替历史流程补造同意记录。升级前备份,按既有流程执行 `migrate-schema up` 和 `verify` 后再启动新后端。旧版本二进制不支持版本 33,回退需恢复匹配的数据库备份。
当前结构版本为 34。版本 34 `task_media_recovery` 为 `tasks` 增加 `media_recovery_json`(text)和 `media_stage`(最长 24 字)。版本 33 `oauth_state_accepted_terms` 为 `o_auth_states` 增加非空布尔列 `accepted_terms`,默认 `false`,不替历史流程补造同意记录。升级前备份,按既有流程执行 `migrate-schema up` 和 `verify` 后再启动新后端。旧版本二进制不支持版本 34,回退需恢复匹配的数据库备份。
### 生成成功后的作品保存恢复
- 使用原任务主键、计费订单及现有 worker 租约,不新增执行批次或第二套队列。`media_stage` 为 `download | upload | local_save | register | checkpoint | completed`,表示当前结果的保存阶段,不改变上游生成日志的成功状态。
- `media_recovery_json` 使用已有 settings-key 加密,记录媒体地址、暂存文件名、资源 ID 和有界重试计数;不保存媒体 base64,不向任务 JSON/摘要返回该字段。数据库与 `.settings-key` 必须一起备份;该字段计入任务存储用量。
- 下载前写入恢复记录,下载后记录暂存文件,上传后记录资源。通过任务 ID 和输出索引生成稳定的资源上传键、素材 ID;任务成功、结果记录及素材登记在同一数据库事务提交,登记失败则不发布任务成功。
- 记录保留在终态任务中,成功后清理对应暂存文件;任务的既有保留/删除策略不变。结构迁移只增加列,不扫描日志,不修改历史任务、结果或账单。没有恢复记录的历史失败任务不会自动变为可恢复。
### 云端 Agent 运行时

View File

@@ -13,6 +13,18 @@ description: 信封、错误码与 OpenAPI
模型 HTTP 失败及经过 Provider 错误转换的业务失败,在通用提示后追加经过过滤的 `;上游:具体原因`,供任务错误和画布卡片展示内容安全、提示词、尺寸等可操作信息。只读取上游 `error` / `message` / `msg` / `detail` 消息字段,不回传整个 JSON;消息中的 HTTP(S) URL 隐藏,包含余额、账务、凭据、请求诊断或 HTML 的消息不追加原文,保留通用分类提示。后台响应拦截规则仍对最终用户文案生效;不会修改历史任务错误或改变重试、退款策略。
## 生成成功后的重试保存
`POST /tasks/:id/recover-media` 无需请求体。当前用户只能恢复自己有保存恢复记录的失败任务;重入队仍返回原任务 ID,不请求重新生成、不新建计费订单。已排队、运行或完成时幂等返回当前任务。取消任务、已退款订单、任务/账单归属不一致、项目已删除或归档时拒绝恢复。
任务详情和摘要新增 `mediaStage`、`canRecoverMedia`。后者表示失败任务存在保存恢复记录,实际操作仍校验权限、账单、项目及重试额度。每任务最多手动恢复三次,两次之间至少一分钟,每次手动操作只执行一次保存尝试;接口另受用户维度任务请求限流约束。普通 `/tasks/:id/retry` 不接受带保存恢复记录的任务,避免误走重新生成。
保存网络故障在原 worker 中最多自动重试四次(15 秒、1 分钟、3 分钟、10 分钟并加抖动;超过 20 分钟不再安排自动重试),使用 `next_poll_at` 释放 worker,而不是在请求内睡眠。不可重试错误直接保留为保存失败。画布和任务中心显示“作品已生成,正在保存”或“作品保存未完成”,失败按钮为“重试保存”。完成后客户端消费原任务的结果。
请求明细保留上游生成与结果下载的独立状态,新增 `upload`、`local_save`、`register` 内部非计费明细;列表和详情通过 `mediaStage` 展示交付摘要。`recordType=request` 排除这些保存明细和下载,`all` 查看全部阶段,`download` 仍仅查看下载。视频下载失败不会再覆盖生成请求的成功记录。
此恢复能力接入声明式媒体协议返回 URL/data URL 的任务路径;旧协议自行下载、协议二进制下载端点以及无恢复记录的历史失败任务不自动补录。历史任务需先核实上游结果可用性、资源归属及原账单,再单独制定修复动作。
## 素材彻底删除
`DELETE /assets/:id` 表示用户确认后的彻底删除,不依赖服务端当前记录是否已经同步为 `archived`。接口仍校验登录用户和素材归属,但不会因画布、任务、项目或历史快照仍有引用而拒绝;事务会删除素材及对应历史资源索引,并通过删除 Outbox 清理未被其他素材复用的物理对象。其他素材共享的同一物理对象继续保留。移入回收站仍通过素材写入接口把状态更新为 `archived`,不能用 DELETE 代替可恢复归档。

View File

@@ -9,7 +9,18 @@ description: 热点回源保护、并发租约、故障边界和上线验收
任务中心打开详情只读取任务和日志,不在该路径调用画布结果同步。后台同步遇到短剧领域项目任务(`domainProjectId` 等于任务 `projectId`)或没有生成节点关联时跳过画布写回,避免把短剧项目 ID 当作画布 ID 查询。
管理员请求明细列表及 CSV 导出支持 `recordType`:省略或 `request` 默认仅请求(继续隐藏视频轮询并排除下载);`download` 只包含 `request_kind=download`;`all` 包含请求、轮询与下载。非法值返回参数错误,管理员权限校验保持不变;该筛选不改变统计聚合接口。
管理员请求明细列表及 CSV 导出支持 `recordType`:省略或 `request` 默认仅请求(隐藏视频轮询,并排除下载、上传、本地保存、素材登记明细);`download` 只包含 `request_kind=download`;`all` 包含全部阶段。非法值返回参数错误,管理员权限校验保持不变;该筛选不改变统计聚合接口。
## 媒体交付恢复的资源边界
声明式协议已经返回作品地址后,先加密保存任务恢复记录,再执行下载、存储、素材登记。交付错误不进入模型路由切换,不创建新生成任务。已下载文件优先复用;资源已就绪时跳过下载和上传,只补登记。配置为 OSS 的生成结果上传失败会明确保留错误,不像普通文件上传那样自动降级到本地存储;普通上传行为不变。
- 单进程同时最多两个媒体交付操作,满载时通过原任务 `next_poll_at` 延迟领取,不启动无界 goroutine 或额外轮询器。此限制不是跨实例全局限制,集群仍依赖既有任务租约与调度限制。
- 下载流式写入数据目录的 `media-staging`,单次下载最多三分钟,文件上限取运行时生成文件限制与 512 MiB 中较小者;暂存目录逻辑占用上限为 512 MiB,先预留空间再写入。
- 成功后立即清理对应暂存文件;超过 24 小时的暂存文件在下次分配空间时清理,不为闲置服务常驻一个清理循环。暂存文件不可用时只尝试原地址,不重新生成;只有内联结果且文件丢失时明确失败。
- 多实例切换时,暂存文件只有位于共享数据卷才可直接复用;没有共享暂存文件但有有效源地址时可以重下载。数据目录和 settings-key 需要稳定保留,不能以临时容器目录代替持久化配置。
- 出站下载沿用 SSRF 防护;同源才附带渠道鉴权。恢复地址仅加密留存,下载明细隐藏路径和查询参数,错误日志不写入原始带签名的 URL。
- 自动重试有次数和时间窗口上限,手动重试也有限额。源地址过期、权限问题、配额不足等不会无限循环;不承诺已经过期且无暂存文件的作品一定可恢复。
章节提取采用独立提示词操作 `chapter_assets_extract`,受保护输出契约为 `chapter-assets/v1`,要求 `characters`、`scenes`、`props` 三个数组。场景和道具要求非空的 `name`、`description`、`prompt`;角色继续执行原角色契约校验。写入候选资产时场景映射为 `environment`,继续使用现有服务的项目归属、去重与写入校验,不新增数据库字段。

View File

@@ -5,6 +5,17 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
# 待测试
## 原任务 ID 媒体保存恢复
- [x] 故障注入:下载 503 后服务实例重建继续原任务、OSS 上传失败保留暂存文件及资源 ID、素材登记异常使任务成功事务回滚,登记恢复无需再次下载。
- [x] 防重复与权限:不触发模型路由切换、不新增账单、重复点击幂等、跨用户/已取消/已退款/项目删除拒绝恢复、过期租约写入拒绝、自动/手动重试预算与暂存容量校验。
- [x] 后端 `app`、`repository`、`database`、`handler` 四包回归通过;最后补验媒体恢复、路由与请求日志专项测试,以及 schema 34 幂等迁移和恢复接口鉴权/归属冒烟。测试使用隔离数据库。
- [x] 前端类型检查与 31 项生成显示、画布结果同步、生成提交及重新生成相关测试通过。
- [ ] 在真实画布和任务中心触发下载失败、OSS 上传失败、登记失败,确认各阶段提示、刷新后“重试保存”、结果回写及资产库仅一条;真实浏览器验收尚未执行。
- [ ] 使用真实 OSS 配置进行网络中断/凭证修复后的恢复验收;当前上传故障测试使用不可达端点,不代表真实 OSS 成功链路已验证。
- [ ] PostgreSQL 部署执行 schema 34 升级及多实例租约/共享暂存卷验收。未修改当前本地业务数据库、历史失败任务或账单。
- [ ] 无恢复记录的历史失败任务单独核实源地址有效期与计费状态;本功能不从旧日志自动回填,不承诺恢复已过期的源文件。
## Live2D 运行库随部署交付
- Cubism Core 已纳入版本控制,保留原始版权声明并附第三方许可说明;前端构建和静态镜像直接包含运行文件,不再要求部署者手工安装。