Files
cmshoppe/docs/tasks/T-640.md
T

7.1 KiB
Raw Blame History

id, title, phase, deps, status, created
id title phase deps status created
T-640 商品套图单张失败重试保持当前轮结果 7
T-639
DONE 2026-07-16

问题 / 背景

⑥「商品套图」一轮生成中有单张图片失败时,用户点击失败图片卡片的「重试」,当前实现会把这次单张操作当成一轮全新的套图生成:

  • retry_job() 重新组装单条 spec 后直接调用完整的 start_generation();
  • start_generation() 会清空 current_job_ids、重置本轮进度并退出历史视图;
  • worker 会新建一条 job,随后 GUI 只把这个新 job 识别为当前轮;
  • 原本同轮已经成功的图片会从「本轮」结果区消失,用户看到的是只有一张图片的“第二轮生成”。

新建 job 保留失败历史、计费和诊断记录是正确的,但「失败重试」在产品语义上应当是修复当前轮中的失败槽位,不应表现为重新开始一整轮。成功图片的「重新生成」与失败图片的「重试」也应保持不同语义。

方案

1. 分离活动生成 job 与当前结果 job

修改 app/gui/tabs/product_suite.py 的 SuiteTaskState:

  • current_job_ids 继续表示「本轮结果区当前有效的图片槽位」;
  • 新增仅用于当前运行生命周期的 generation_job_ids,表示本次实际提交、被进度/停止/终态看门狗跟踪的 job;
  • 正常整轮生成时同时重建两组 ID;
  • 单张失败重试时只清空 generation_job_ids,保留其他 current_job_ids;
  • T-639 的终态快照、停止、线程结束和看门狗统一只读取 generation_job_ids,不得把当前轮其他历史成功 job 重复计入本次重试汇总。

这些字段只保存在 GUI 内存,不修改 SQLite schema。

2. 单张重试替换失败槽位

为 start_generation() 增加明确的单张重试上下文:

  • retry_job() 传入原失败 job ID,而不是把单条 spec 当作普通整轮生成;
  • worker 仍创建新的 job,不覆盖或删除原失败 job;
  • worker 首次返回新 job ID 后,在 current_job_ids 中把原失败 ID 原位替换为新 job ID;
  • 当前轮其他成功 job ID 保持不变;
  • 如果重试再次失败或被停止,新的失败/停止 job 继续占据该槽位,用户可以再次重试;
  • 「历史生成」继续列出原失败 job 与每次新建的重试 job,保留完整审计记录。

当前结果区按 current_job_ids 的槽位顺序展示,避免新建重试 job 因数据库更新时间排序而跳到第一张。

3. 区分重试与整轮生成反馈

单张失败重试期间:

  • 进度只统计本次重试的 1 张,不重新统计当前轮全部图片;
  • 不清空当前轮结果区,不切换成只有一张图片的新一轮;
  • 完成弹窗使用「图片重试完成」或「图片重试已停止」等中文文案;
  • 汇总显示本次重试成功、失败、停止数量,不使用「本轮共 1 张」误导用户这是第二轮套图;
  • 状态栏使用「图片重试成功 / 图片重试失败 / 图片重试已停止」;
  • 原整轮「商品套图生成完成」与停止反馈保持不变。

从「历史生成」发起重试时保持历史视图,避免自动跳到一个无法表达原历史轮次的临时“本轮”;新 job 会按历史排序显示。

4. 操作边界

  • 只有 failed/expired/cancelled 卡片显示的「重试」使用当前轮修复语义;
  • 成功图片右键「重新生成」继续视为主动新增变体,不在本任务中改成失败槽位替换;
  • 重试会重新调用 cmhub,可能再次扣点;不伪装为恢复原请求;
  • 不删除旧失败 job、旧错误、计费记录或已成功图片;
  • 不修改 cmhub 请求、并发上限、提示词、图片下载、套图数量和项目存储结构。

验收要点

  • 一轮 N 张中有 1 张失败,点击该卡片「重试」后,当前结果区仍保留其余 N-1 张成功图片。
  • 新 job ID 到达后,原失败卡片在原位置被新重试卡片替换,当前结果区仍为 N 个槽位。
  • 重试成功后当前结果区显示 N 张成功图片,不表现为只有 1 张图片的第二轮。
  • 重试再次失败或停止时,该槽位显示最新状态,并可再次重试。
  • 原失败 job 和新重试 job 都可在「历史生成」中查看。
  • 本次重试的进度、停止、线程结束和终态看门狗只统计新 job,不重复统计当前轮其他成功 job。
  • 从历史视图点击重试时保持历史视图,新 job 正常进入历史列表。
  • 正常整轮生成、历史生成、手动停止、T-639 终态兜底和旧 run_token 隔离不回归。
  • 用户可见文案全部为中文,不暴露接口路径、图片 URL、提示词全文或密钥。

测试

  • tests/test_product_suite_gui.py:
    • 当前轮失败卡片重试后保留其他成功 job;
    • 新 job 原位替换失败 job,结果顺序稳定;
    • 活动 job 快照只包含重试 job;
    • 重试完成使用独立中文汇总;
    • 历史视图发起重试时不自动退出历史;
    • 正常整轮生成仍重建当前结果。
  • 运行:
py -3.10 -m unittest tests.test_product_suite_gui
py -3.10 -m unittest discover -s tests
py -3.10 -m ruff check app tests main.py
py -3.10 -m compileall app main.py
git diff --check

边界(不改什么)

  • 不修改 SQLite schema,不新增 retry_of_job_id 或持久化轮次表。
  • 不删除或覆盖原失败 job,不修改历史计费记录。
  • 不修改 cmhub API、提交/轮询/下载协议和失败重试次数。
  • 不修改套图提示词模板、分类数量、比例、原图管理或导出逻辑。
  • 不修改 CDP、蝦皮主图拉取、①导入采集、②AI生成、③更新蝦皮、④账号管理或⑤设置。

执行记录

  • 2026-07-16:SuiteTaskState 新增内存态 generation_job_ids、generation_mode 与 generation_retry_job_id。generation_job_ids 专门供当前活动请求的进度、停止、线程结束和终态看门狗使用;current_job_ids 只表示当前结果区的有效槽位,解决单张重试时旧成功 job 被错误纳入本次汇总的问题。未修改 SQLite schema。
  • 2026-07-16:失败、过期或停止卡片的「重试」改为显式单张重试模式。worker 仍新建 job 保留历史和计费记录;新 job ID 返回后会原位替换当前结果区中的旧失败 job,其他成功图片保持不变。当前结果改按 current_job_ids 顺序展示;历史视图继续展示原失败与每次重试记录,从历史发起重试时不会强制退出历史视图。
  • 2026-07-16:重试运行状态、停止确认、进度文案和完成弹窗改为「图片重试」语义;重试成功、失败、停止和未完整结束分别给中文反馈。正常整轮生成、停止、T-639 run_token 隔离与终态兜底保持原行为。
  • 2026-07-16:补齐当前轮原位替换、历史记录保留、活动快照只统计新 job、重试再次失败仍可继续重试、历史视图保持和真实 QThread 完整退出测试。相关 42 项测试通过;在不包含用户未提交默认提示词改名的临时隔离 worktree 中,全量 535 项 unittest 通过,Ruff、compileall 与 git diff --check 均通过。