diff --git a/docs/tasks/T-612.md b/docs/tasks/T-612.md new file mode 100644 index 0000000..9fb47d6 --- /dev/null +++ b/docs/tasks/T-612.md @@ -0,0 +1,95 @@ +--- +id: T-612 +title: AI工场生图运行态分级,保留当前项目非冲突操作 +phase: 7 +deps: [T-594, T-604, T-608, T-609] +status: TODO +created: 2026-07-13 +--- + +## 问题 / 背景 + +⑥ AI工场点击「开始生成」或「继续查询任务」后,`ImageStudioTab.start_generation()` / `resume_generation_jobs()` 都经由通用 `_start_worker()` 进入 `_set_running(True)`。当前 `_set_running()` 会同时禁用商品列表、蝦皮原主图、照片池、主图/详情图终选、提示词模板、提示词编辑、生成参数、开始/继续查询、导出和项目操作,只留下「取消」。 + +这不是 cmhub 网络等待导致 GUI 卡死:网络工作在 `QThread` 中。它是早期把所有 AI工场 worker 当作同一种“全页互斥操作”的保守实现,用来避免: + +- 在单一 `_running_worker` / `_running_thread` 模型下同时启动两个耗时 worker; +- 生成回调按 `current_project` 刷新时,用户同时删除项目或开始另一项写操作; +- 用户误以为生成中的提示词或源图变更会影响已经提交给 cmhub 的本轮任务。 + +但图片生成/继续查询通常持续数分钟,全页变灰妨碍运营在当前商品内浏览照片池、整理终选、修改下一轮提示词。T-608 已经为单张蝦皮原图下载拆除了全页禁用,生图仍沿用旧的全局锁定策略,体验不一致。 + +## 方案 + +### 1. 区分操作类型,保留“单一耗时 worker”边界 + +在 `app/gui/tabs/image_studio.py` 中把通用运行态从单一布尔值扩展为明确的操作类型,例如:`generate`、`resume`、`pull_images`、`export`。运行期间仍只允许一个由 `_start_worker()` 管理的耗时 worker,不能借“局部可用”引入两个生成、轮询、拉图或导出 worker 并发。 + +- `ImageStudioGenerateJobsWorker` 与 `ImageStudioResumeJobsWorker` 使用“生成运行态”; +- 拉取蝦皮主图、导出终选维持现有较严格的互斥范围; +- T-608 的单张原图下载队列继续使用专用路径,不回退到 `_start_worker()`,不受本任务破坏; +- 线程引用仍只在 `thread.finished` 后释放,继续遵守 T-604 的生命周期修复。 + +### 2. 生图期间只禁用冲突操作 + +处于“生成运行态”时: + +- **必须禁用**:开始生成、继续查询任务、拉取蝦皮主图、导出终选、删除项目、会启动完整原图下载的原图区操作;这些操作会再启动 worker、改变线上主图快照、移动导出文件或删除当前 worker 仍在写入的项目。 +- **保持可用**:当前项目的照片池浏览、源图选择、主图/详情图终选排序、提示词模板切换/编辑/保存、类型/数量/比例的下一轮设置,以及打开当前项目文件夹。 +- 当前这一轮的源图、提示词、类型、数量、比例在创建 `ImageStudioGenerateJobsWorker` 时已经快照传入;运行中修改上述控件只影响**下一轮**,不得改变已提交或正在轮询的 cmhub job。 +- 在生成设置区显示中文轻量提示,例如「本轮任务已固定;此处修改将在下一轮生成时生效」,避免用户误解。 +- 第一版不开放切换商品项目。项目切换会重置缩略图缓存、`current_project`、`selected_source_asset_id` 和列表内容,且现有完成/进度回调仍依赖当前 tab 状态;保留禁用项目列表可避免旧项目的余额、进度或刷新事件显示到新项目。后续若要支持跨项目浏览,必须先让全部回调按启动时捕获的 `project_id` 隔离,再单独立任务。 + +取消按钮继续始终可用;点击后只请求 worker 在安全边界停止,控件在 worker 正常完成、失败或停止并完成清理后恢复。 + +### 3. 回调与状态恢复保持幂等 + +- `_finish_worker()`、`worker.failed`、`worker.finished` 以及 `thread.finished` 的顺序不得导致控件提前恢复或重复恢复;继续复用 T-604 的 `_worker_error_handled` 与线程引用清理规则。 +- 生成部分成功、部分失败、用户停止、cmhub 轮询失败和导出/拉图失败后,都按各自操作类型恢复对应控件,而不是无条件套用“生成态”或“全局空闲态”。 +- 不在 GUI 主线程等待 cmhub,也不在后台线程直接操作 Qt 控件。 + +### 4. 文档同步 + +更新 `docs/routes.md` 中⑥ AI工场的生成运行口径:生图期间禁止新建冲突 worker,但可继续整理当前项目的下一轮设置与终选;原图下载、项目切换、拉取、导出和删除的边界保持明确。 + +## 验收要点 + +- 点击「开始生成」或「继续查询任务」后,取消按钮可用,开始/继续查询/拉取/导出/删除项目/原图区下载操作不可用。 +- 生图期间,照片池、主图终选、详情图终选、提示词模板、提示词编辑和下一轮类型/数量/比例控件不再整体变灰,可正常操作。 +- 运行中修改提示词或参数后,本轮已创建的 worker 仍使用启动时的快照;下一轮生成才读取新值。 +- 生图期间不能启动第二个耗时 worker,不能切换或删除项目,不能从原图区触发新的完整原图下载。 +- 停止、完成、部分失败和 worker 异常后,控件按空闲态正确恢复;不会出现 QThread 提前释放、重复错误弹窗或残留禁用状态。 +- T-608 的单张原图下载仍只局部更新,不会因为本任务重新把 AI工场全页变灰。 +- 不改 cmhub submit/poll/download、计费、job 持久化、SQLite schema、CDP、Chrome、蝦皮读取/更新或①至⑤模块。 + +## 测试要求 + +更新或新增 `tests/test_gui.py`: + +- 覆盖生成运行态下各控件的精确启用/禁用清单;特别断言照片池、终选、提示词和参数可用,而开始/继续查询/拉取/导出/删除/原图区下载不可用。 +- 覆盖运行中修改 prompt/参数不会改变已构造 worker 的参数,只影响下一次 `start_generation()`。 +- 覆盖取消、成功、失败、`finished(ok=False)` 与 `thread.finished` 各顺序下,运行态和线程引用均正确恢复且错误只处理一次。 +- 覆盖原图下载专用队列不受生成运行态重构影响。 + +验证命令: + +```bash +python -m ruff check app tests main.py +py -3.10 -m compileall app main.py +py -3.10 -m unittest discover -s tests +git diff --check +``` + +人工验收:启动一轮等待时间较长的生图,在等待期间编辑下一轮提示词、调整数量、拖动已有候选图到终选并点击取消;确认本轮任务参数不变、下一轮使用新设置,且没有生成第二个后台 worker。 + +## 边界(不改什么) + +- 不实现多项目并行生成、跨项目浏览、多个全局 worker 并发或后台静默重提交流程。 +- 不改变单张原图下载的最大并发、重试、项目隔离和关闭清理规则。 +- 不改变照片池中失败任务卡的展示语义;该问题由 T-613 单独处理。 +- 不增加 cmhub API 字段、重试次数、超时、计费规则、数据库字段或新的设置入口。 +- 不改变任何 CDP、Shopee、Chrome 登录或上传逻辑。 + +## 执行记录 + +(完成后记录实现、验证命令与人工验收结果。) diff --git a/docs/tasks/T-613.md b/docs/tasks/T-613.md new file mode 100644 index 0000000..d4bd465 --- /dev/null +++ b/docs/tasks/T-613.md @@ -0,0 +1,101 @@ +--- +id: T-613 +title: AI工场照片池与失败生图任务分层展示 +phase: 7 +deps: [T-594, T-607, T-612] +status: TODO +created: 2026-07-13 +--- + +## 问题 / 背景 + +当前 `ImageStudioTab._fill_pool_grid()` 将可用图片资产与 `pending`、`submitted`、`running`、`failed`、`expired`、`cancelled` 的生图 job 一并放进「照片池」网格。失败 job 使用 `_job_icon("failed")` 生成中间写着「败」的占位缩略图。 + +这能避免失败任务悄悄消失,并保留 cmhub `task_id` 供 T-594 的“继续查询任务”恢复,防止下载失败时重新 submit 或重复扣点。但它把“可选图片”和“不可用任务记录”混为同一类卡片: + +- 运营会把照片池理解为可选择、可拖入终选的图片集合,却看到没有实际图片的「败」卡; +- 单个「败」字无法解释是上游生成失败、轮询超时、结果下载失败、本地保存失败、过期还是用户停止; +- 卡片没有明确告诉用户该任务是否可以继续查询,还是必须重新生成并可能再次扣点; +- 失败卡当前的视觉主色与禁用态/空白占位接近,警示性不足。 + +## 方案 + +### 1. 照片池只展示可用图片资产 + +「照片池」只保留本地文件存在且可解码使用的 `original`、`generated_main`、`generated_detail` 资产。照片池内的卡片仍支持选源图、预览、拖入主图/详情图终选和已有右键操作。 + +不再把 job 状态卡插入照片池,因此失败、生成中、已提交、取消和过期任务不能被误认为一张候选图片,也不能拖拽或加入终选。 + +### 2. 新增独立“生成任务”状态区 + +在照片池附近增加紧凑的「生成任务」状态区,仅展示尚未产出可用图片的 job:`pending`、`submitted`、`running`、`failed`、`expired`、`cancelled`。没有此类任务时该区隐藏,不占用照片池空间。 + +- 每张状态卡显示完整中文状态,例如「等待提交」「已提交」「生成中」「生成失败」「任务过期」「已停止」;不得只显示单个「败」「过」「停」字符。 +- `failed` / `expired` 使用明确的危险语义色和失败图标;`pending` / `submitted` / `running` 使用中性或进行中语义色;`cancelled` 使用中性停用色。不得把失败任务渲染成可用图片、普通空白或无含义灰色占位。 +- 卡片显示经 `diagnostics.redact_log_text()` 脱敏、截断后的中文原因摘要;例如「下载图片失败,可继续查询」「上游生成失败,需要重新生成」。不得显示 API 路径、远程图片 URL、密钥、Cookie、完整堆栈或英文技术异常。 +- 卡片保留已有的扣点、余额、`call_id` 等运营可理解的计费信息;没有该字段时不显示占位技术文本。 +- 照片池标题或状态区标题显示简短汇总,例如「可用图片 8 张 · 未完成/异常任务 1 个」,方便用户知道为什么少了一张候选图。 + +### 3. 恢复动作必须符合计费语义 + +- 继续保留项目级「继续查询任务」入口。对于已保存 `task_id` 的 job,继续查询只能 poll/download 既有任务,不能再次 submit、不能重复扣点;T-594 既有口径不变。 +- 状态卡应明确说明恢复方式:可恢复 job 显示“可继续查询”;已收到终态上游失败、过期或用户停止的 job 显示“需要重新生成,可能再次扣点”。 +- 本任务不把“重新生成”伪装成无成本重试,也不在卡片点击时自动创建新 job。用户需要重新生成时,仍从现有生成设置显式发起新一轮。 +- 如现有持久化字段不足以可靠区分“可继续查询”和“必须重新生成”,先补充最小、可迁移的 job 恢复语义字段或由服务层提供明确分类;不得仅靠匹配错误文案字符串猜测。 + +### 4. 状态变化与项目隔离 + +- job 成功下载、保存并产出可用资产后,任务状态卡从「生成任务」区移除,对应真实图片进入照片池。 +- job 失败、取消、过期后保留状态记录,直到用户成功恢复、显式重新生成或后续单独的清理策略处理;不得为了视觉整洁静默删除失败证据。 +- 项目切换、T-612 的生成运行态、T-608 的原图下载队列和 T-604 的线程清理不能让旧项目状态卡污染当前项目。 + +### 5. 文档同步 + +更新 `docs/routes.md`,明确“照片池 = 可用图片资产”,“生成任务 = 进行中/失败任务状态”,并写明继续查询不重复提交、不重复扣点,重新生成可能产生新的计费。 + +## 验收要点 + +- 失败生成后,照片池不再出现灰色单字「败」占位图;照片池中只能看到可用本地图片。 +- 独立「生成任务」区显示“生成失败”及脱敏后的中文原因摘要,失败卡使用危险语义色且不可选源图、不可拖入终选。 +- 已提交、生成中、取消、过期等未产出图片任务也在状态区以完整中文状态显示;没有此类任务时状态区隐藏。 +- 任务成功并保存图片后,状态卡消失,图片进入照片池;失败记录不会悄悄丢失。 +- 对保存了 `task_id` 的可恢复任务,用户能明确知道可用「继续查询任务」恢复,且恢复不会重复 submit 或扣点。 +- 对终态失败/过期/停止任务,界面明确提示需重新生成且可能再次扣点;不把它错误标记为可恢复。 +- 原因摘要、日志和 tooltip 不泄露 cmhub URL、接口路径、密钥、Cookie、完整堆栈或英文技术错误。 +- 不影响 T-612 的生图运行态分级、T-608 的原图下载队列、T-594 的重启续查和计费保护、终选拖拽以及导出。 + +## 测试要求 + +更新或新增: + +- `tests/test_gui.py` + - 断言照片池只包含可用资产;`failed` / `submitted` / `running` 等 job 不再以照片池图片项出现。 + - 覆盖生成任务区的空态隐藏、进行中、失败、过期、停止和成功转入照片池。 + - 覆盖失败卡完整中文状态、危险语义色、脱敏原因摘要、不可拖拽/不可加入终选和计费信息显示。 + - 覆盖项目切换后状态卡与照片池不串项目。 +- `tests/test_image_studio_generation.py` / `tests/test_image_studio.py` + - 覆盖可恢复与不可恢复任务的明确分类;可恢复路径只 poll/download,不重新 submit 或扣点。 + - 如新增恢复语义字段,覆盖 schema 初始化、迁移、旧 job 默认兼容和状态转换。 + +验证命令: + +```bash +python -m ruff check app tests main.py +py -3.10 -m compileall app main.py +py -3.10 -m unittest discover -s tests +git diff --check +``` + +人工验收:模拟一轮含成功、下载失败、上游失败、停止和生成中的任务,确认运营能清楚区分“已有可用图片”“可继续查询”“需要重新生成”,且不会把失败任务拖入终选。 + +## 边界(不改什么) + +- 不新增本地图片导入、图片编辑、自动上传蝦皮或终选规则。 +- 不改变 cmhub 的 submit/poll/download 接口、超时、并发、点数价格、余额或退点契约。 +- 不实现点击失败卡即自动重新生成;新的生成必须通过既有显式生成操作启动。 +- 不通过解析自然语言错误字符串判断恢复方式;若需区分,使用服务层明确状态或可迁移字段。 +- 不改①至⑤模块、Chrome/CDP、账号登录、数据库中无关表或历史图片物理清理策略。 + +## 执行记录 + +(完成后记录实现、验证命令与人工验收结果。)