15 KiB
后台任务中心:设计、实施状态与路线图
更新:2026-08-30
适用范围:套图生成、单张 AI 生图 / AI 编辑、批量添加水印,以及未来的耗时导出和批处理。
阅读顺序:接手相关开发前,先读本文,再读image-asset-lifecycle.md与api.md。
1. 结论与边界
“单图弹窗等待”、“套图生成”和“批量水印”本质都是耗时、可失败、需要恢复的后台操作,不应分别实现异步逻辑。项目统一采用:
Web API
└─ 在数据库创建父任务 + 子项,立即返回任务 ID
独立 Worker
└─ 领取一个子项 → 调模型 / 合成水印 / 保存图片 → 回写进度与结果
试算页任务入口(当前)/ 任务中心(后续)
└─ 查询状态、恢复进度、取消、重试、打开结果
当前是本地单机、SQLite 架构,不引入 Redis、Celery 或分布式队列。数据库驱动的独立 Worker 已能覆盖断开弹窗、刷新页面、Web 服务重启、模型长耗时和失败重试,复杂度与收益最平衡。未来扩展到多机器或多实例时,再迁移 PostgreSQL 并评估 Redis / Celery / RQ 等队列。
本文中的“任务中心”指统一的数据模型、调度和用户可见的任务体验;不意味着每一种任务各有一套后台实现。
2. 产品体验
2.1 目标体验
单张“AI 生图 / AI 编辑”弹窗只负责配置,不负责等待:
- 用户填写要求、选择模型,点击“提交任务”。
- 页面立即提示“已加入任务队列”,弹窗关闭;用户可继续编辑商品或离开当前页面。
- 页面提供“处理中(N)”入口,任务完成后图片自动出现在“生图记录”。
- 套图与批量水印同样进入任务中心,可查看进度、失败原因、取消、重试和结果。
单张 AI 生图和 AI 编辑保留不同入口、提示词与来源图,但后端都属于 image_edit 单图生成任务;区别只在任务快照和标题。
2.2 统一状态
| 用户看到的状态 | 父任务 / 子项状态 | 含义 |
|---|---|---|
| 排队中 | queued |
等待 Worker 领取。 |
| 处理中 | running |
正在生成、下载或合成水印。 |
| 等待模型结果 | 子项 waiting_provider |
已提交给第三方模型,Worker 后续轮询同一个模型任务。 |
| 已完成 | done |
所有子项成功。 |
| 部分完成 | partial |
至少一个成功,且有失败或取消项。 |
| 已失败 | failed |
没有成功项,失败原因可查看与重试。 |
| 已取消 | cancelled |
用户取消;后续结果不会回写商品。 |
2.3 当前已呈现的界面
- 单图弹窗提交成功后关闭,生成进度由“生成结果”区域继续跟踪。
- 套图在“生成结果”区域展示排队、进行、完成、取消、失败重试。
- 水印设置浮层展示本次批量水印进度、取消、全部失败项重试和单图重试。
- 试算页右下角“录入商品”上方有固定“任务 / 处理中(N)”入口;点击打开当前商品的右侧任务抽屉,统一显示套图、单图与水印任务。
- 两类任务均会在刷新试算页后恢复跟踪。
尚未完成的是应用级全局“任务中心”:当前抽屉只展示当前商品,且生成结果区和水印设置浮层仍保留上下文状态提示。见第 9 节第三阶段。
3. 统一数据模型
3.1 已实现表
background_jobs(父任务)
一条用户可感知的后台操作,关键字段包括:
| 字段 | 用途 |
|---|---|
id、product_id、kind |
任务身份、归属商品和任务类型。商品删除后 product_id 置空,避免任务反向复活商品。 |
status、priority、cancel_requested |
调度与取消控制。 |
provider、model、platform、lang、ratio、style_set |
生图任务的可恢复执行上下文。 |
request_payload |
不可变请求快照:商品上下文、参考图、提示词、风格、水印配置等。 |
total、completed、failed、error |
整体进度和概要错误。 |
created_at、started_at、completed_at、updated_at |
任务历史、排序和诊断。 |
background_job_items(子项)
可独立领取、失败和重试的最小单元:
| 场景 | 父任务 | 子项 |
|---|---|---|
| 套图生成 | 一次套图请求 | 一张计划生成图 |
| 单张 AI 生图 / 编辑 | 一次编辑请求 | 唯一的一张图 |
| 批量添加水印 | 一次批量操作 | 一张商品素材 |
子项保存 payload、状态、尝试次数、租约、结果 URL、生成回写素材 ID、错误、下次执行时间,以及第三方 provider_task_id / provider_task_kind。
3.2 任务类型
| 类型 | 当前状态 | 说明 |
|---|---|---|
image_suite |
已实施 | 套图按“每张图一个子项”执行。 |
image_edit |
已实施 | 单张 AI 生图 / AI 编辑。 |
watermark_batch |
已实施 | 已选素材逐张生成当前水印版。 |
export_zip / 批量下载 |
待开发 | 可复用同一父/子任务模型处理长时间 ZIP。 |
| 孤儿文件清理 | 待开发 | 后台扫描并回收存储异常残留。 |
3.3 可选诊断表(未实施)
未来新增 background_job_events,记录“提交模型”“获取 provider task ID”“轮询失败”“租约恢复”“用户取消”“人工重试”等事件。它不参与调度,作用是排障、审计和任务中心时间线;当前诊断信息主要存于子项错误文本和服务日志。
4. 执行、恢复与一致性
4.1 Worker 执行流程(已实施)
start.command 会额外启动 python server/worker.py。Worker 通过数据库租约领取一个符合条件的子项:
queued / waiting_provider
→ Worker 写入 running + 租约
→ 执行一次推进
├─ 模型未完成:持久化 provider_task_id → waiting_provider → 延迟下次轮询
├─ 成功:保存图片 / 回写素材 → done
├─ 可诊断错误:failed
└─ 已取消或商品不存在:cancelled
→ 从所有子项重新计算父任务进度与终态
Worker 异常退出时,过期租约会重新回到队列;Web 服务重启不影响已提交的数据库任务。
4.2 第三方模型恢复(已实施范围)
- Pcore、RightAPI、通义万相:提交后先持久化
provider_task_id,Worker 重启后继续轮询同一模型任务,不重复扣费。 - 豆包、同步千问等同步接口:供应商不提供可恢复的外部任务 ID。若进程恰好在请求期间中断,只能重试,无法保证供应商侧幂等。
- 仍待完善的极小窗口:若“已向供应商提交”与“成功写入
provider_task_id”之间进程崩溃,当前无法完全避免重复提交。第三阶段应引入供应商幂等键;不支持幂等键时,把子项标为“待人工确认”,不盲目重复扣费。
4.3 图片生命周期配合(已实施)
批量水印任务创建时会冻结:
asset_id;- 当时的
current_storage_key/ 当前图片 URL; - 水印配置快照。
Worker 始终基于素材 original 无水印版本合成新的 current 水印版。执行时若素材已删除或 current 版本已经变化,子项失败而不会用旧任务覆盖新图片;单项重试会重新冻结该素材的最新 current 版本。数据库成功切换到新 current 后,才尝试删除旧水印对象。
详细的原图、当前版、删除回收规则见 image-asset-lifecycle.md。
4.4 删除与取消(已实施)
删除商品前会取消该商品所有未完成任务。Worker 在落盘和回写素材前再次检查取消标志与商品是否存在;若商品或素材已删除,只丢弃/清理本次临时结果,绝不“复活”商品或素材记录。
5. 调度与优先级
当前实现
当前为一个独立 Worker 进程,按子项粒度领取,排序为优先级优先、再按创建时间:
| 类型 | 当前优先级 | 含义 |
|---|---|---|
image_edit |
10 |
用户临时单图编辑优先。 |
image_suite |
0 |
常规套图。 |
watermark_batch |
-10 |
低优先级,不应优先于模型生成。 |
这已避免“十张套图作为整体长期占住任务”的问题:套图也是逐张领取,单图编辑可插队。
后续目标(未实施)
AI 生成与水印不应长期共用单线程吞吐:
- AI 按供应商设置串行或小并发及速率限制;单图编辑优先。
- 批量水印使用独立逻辑队列 / Worker 池,建议并发约 2,不阻塞模型任务。
- 调度增加公平性:连续处理 2–3 个高优先级单图后,给套图一个执行机会,避免套图饿死。
在 SQLite 单机阶段,这可先通过两个启动参数不同的 Worker 和类型过滤实现;迁移 PostgreSQL 后再做跨进程可靠并发领取。
6. API 与前端契约
当前接口
| 接口 | 返回 / 用途 |
|---|---|
POST /api/suite/generate |
返回 { suite_id },创建 image_suite。 |
POST /api/suite/image-edit |
返回 { suite_id },创建 image_edit。 |
POST /api/assets/watermark |
返回 { job_id, total },创建 watermark_batch。 |
GET /api/suites/{id} |
通用任务详情、父任务进度、逐项状态和错误。 |
GET /api/products/{id}/suites?active_only=true |
恢复活动的套图 / 单图生成任务。 |
GET /api/products/{id}/watermark-jobs?active_only=true |
恢复活动的水印任务。 |
POST /api/suites/{id}/cancel |
取消任务中尚未完成的子项。 |
POST /api/suites/{id}/retry |
重试全部失败子项。 |
POST /api/suites/{id}/items/{item_id}/retry |
仅重试指定失败子项。 |
GET /api/suites/{id} 是历史命名;它已经承载水印任务。全局任务中心落地时应新增语义更通用的 GET /api/background-jobs/{id}(保留旧接口兼容)。
当前前端轮询
前端使用递归 setTimeout,在上一轮请求完成后才安排下一轮;网络失败采用指数退避并使用 AbortController 取消页面卸载或任务切换后的请求。浏览器会保存最近任务 ID,刷新后还会从服务端查询商品活动任务,因此不依赖单次页面生命周期。
7. 已完成阶段
| 阶段 | 内容 | 状态 |
|---|---|---|
| 第一阶段:AI 任务可靠性 | 任务表与迁移;独立 Worker;套图与单图编辑迁入持久化任务;provider task ID 持久化;取消、失败重试、刷新恢复。 | ✅ 已完成 |
| 第二阶段:批量水印复用 | watermark_batch 逐素材子项;后台合成和版本切换;整体进度、取消、全部失败重试、单项重试、刷新恢复。 |
✅ 已完成 |
| 数据安全配合 | 商品删除取消任务;写入前二次检查;水印任务冻结素材 current 版本,拒绝旧任务覆盖新状态。 | ✅ 已完成 |
| 第三阶段:前端任务中心(第一步) | 当前商品固定任务入口、右侧任务抽屉、任务进度、取消、失败重试、跳转生成结果。 | ✅ 已完成 |
已验证:Python 编译、前端 TypeScript/生产构建;模拟存储下的“创建水印任务 → Worker 完成 → 素材 current 回写”以及“单个失败项按最新版本重试”。真实模型与七牛仍应按第 10 节进行人工回归。
8. 当前限制
- 任务中心 UI 尚未独立成页面或全局抽屉,任务入口仍分散在生成结果和水印设置中。
background_job_events尚未建立,复杂问题主要依赖日志与错误摘要。- 当前一个 Worker 统一领取 AI 与水印任务,优先级有效,但没有独立并发池和公平调度。
- 移除水印仍是短时同步接口;大量素材场景可迁入
watermark_batch同一框架。 - SQLite 适合本机单用户,不适合多机器或高并发抢占。
- 同步供应商无法做到提交中的完全幂等恢复;异步供应商的“提交后未保存 provider ID”窗口尚未用幂等键闭合。
- 历史任务和失败临时文件尚未设置保留期与定期清理策略。
9. 待开发阶段
第三阶段:前端任务中心后续完善
- 已完成:在试算页右下角“录入商品”上方提供“任务 / 处理中(N)”入口,打开当前商品任务抽屉。
- 已完成:抽屉合并套图、单图编辑、水印任务,展示逐图失败、取消、重试和“查看结果”。
- 待开发:按类型、状态筛选,展示更细的
waiting_provider时间线与预计耗时。 - 待开发:新增通用列表接口,例如
GET /api/background-jobs?product_id=&status=&kind=;旧/suites/*继续兼容。 - 待开发:单图弹窗提交后提供“查看任务”快捷入口,并逐步移除弹窗自身的轮询职责。
第四阶段:可靠性、调度与诊断
- 新增
background_job_events与结构化日志,记录供应商、模型、耗时、错误类别和重试。 - 引入供应商幂等键或“待人工确认”状态,关闭提交模型后的重复扣费窗口。
- 拆分 AI 与水印的逻辑 Worker 池,配置并发、供应商限流和公平调度。
- 增加任务历史保留期、失败临时文件清理和七牛孤儿对象扫描。
- 把移除水印、耗时 ZIP 导出、批量下载逐步迁入任务中心。
第五阶段:多实例基础设施
前提是产品确实需要多用户、多机器或更高吞吐:
- SQLite 迁移 PostgreSQL。
- 采用 PostgreSQL 行级锁 /
SKIP LOCKED强化子项领取。 - 再评估 Redis + Celery、RQ 或专业队列;不要在本地单机阶段提前引入。
10. 回归与运维清单
每次修改任务系统后,至少验证:
./start.command会同时启动 Web、前端和 Worker;手动启动 server 时必须另开python server/worker.py。- 单图编辑提交后可关闭弹窗;刷新试算页后仍能看到任务和最终图片。
- 套图多张任务中插入单图编辑,单图应优先领取,套图不丢进度。
- 批量水印中删除商品或单个素材,Worker 不得回写/复活资源。
- 改水印任务执行期间再次修改同一素材,旧任务必须失败或跳过,不能覆盖新 current。
- Pcore / 通义等异步模型在重启 Worker 后继续轮询原
provider_task_id,不再次提交。 - 失败项的全部重试与单项重试都能恢复状态;单项水印重试使用最新素材版本。
- 真实存储环境检查旧水印对象仅在新 current 数据库提交后删除;失败时不会丢失原图。
11. 代码入口
| 位置 | 责任 |
|---|---|
server/models/job.py |
父任务、子项 ORM 模型。 |
server/migrations/versions/b5d2c9a3e1f7_add_background_jobs.py |
任务表迁移。 |
server/services/jobs.py |
创建、领取、租约恢复、状态聚合、取消与重试。 |
server/services/job_worker.py |
执行生成和批量水印子项。 |
server/worker.py、start.command |
独立 Worker 命令与启动编排。 |
server/api/suite.py |
套图、单图编辑和通用任务查询接口。 |
server/api/materials.py |
批量水印创建和水印任务列表。 |
studio/src/pages/trial/TrialSuitePanel.tsx |
试算页任务恢复、轮询、取消与重试入口。 |
studio/src/pages/trial/AiImageGenModal.tsx |
单图任务配置与提交入口。 |