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

127 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-608
title: AI工场主图重新拉取确认、非阻塞原图下载与区域比例优化
phase: 7
deps: [T-607]
status: DONE
created: 2026-07-11
---
## 问题 / 背景
⑥AI工场当前点击「拉取蝦皮主图」时,会直接刷新线上主图 URL 快照;即使该店铺/商品在本地已有完整原图,也没有让用户确认。用户容易误解“重新拉取”是否会覆盖或删除本地图片。
另外,用户单击一张尚未下载的蝦皮原图时,`ImageStudioDownloadOriginalWorker` 仍走 AI工场通用 `_start_worker()`。通用 worker 会把整个 tab 的商品列表、原图区、照片池、提示词、终选和生成按钮全部禁用,直到单张图片下载结束,导致一张原图网络慢时整个工作台不可操作。
原图区目前高度约 174~202px,商品列表最小宽度 220px。用户希望压缩原图区和商品列表,把更多空间分给照片池与图片区;但若把三列商品列表机械压至约 110px,店铺名、商品ID、更新时间将无法可靠阅读。
本任务目标:**在重新拉取前说明本地历史图片边界;把原图完整下载改为可重试、局部非阻塞的后台任务;压缩不影响可读性的区域并把空间让给照片池。**
## 方案
### 1. 本地已有原图时确认“重新拉取”
修改 `app/gui/tabs/image_studio.py`,在启动 `ImageStudioPullImagesWorker` 前:
- 按当前账号别名和商品ID查询对应 AI工场项目;项目已软删除但同一账号/商品ID可恢复时,也要纳入本地原图检查。
- 检查该项目 `original` 资产中实际存在的 `local_path` 文件数量;只有数量大于 0 才弹确认框。
- 确认框使用中文,并准确说明语义,例如:
```text
本地已保存 3 张蝦皮原主图。是否重新拉取线上主图?
重新拉取会刷新当前线上主图列表;不会删除本地图片、生成记录或终选记录。
```
- 主操作文案使用「重新拉取」,不要使用“覆盖本地图片”。取消后不启动 Chrome/CDP/worker,不修改 SQLite。
- 沿用 `sync_original_asset_urls()` 当前安全规则:相同远程 URL 复用既有资产和本地文件;不再出现在蝦皮当前主图列表的旧原图可标记为当前列表外/缺失,但不物理删除本地文件、生成记录或终选记录。
- 若刷新会使历史原图不再属于“当前线上主图列表”,UI 不得把它误说成文件已删除;历史生成图/终选引用继续保留,必要时在 tooltip/状态中说明“历史原图”。本任务不新增永久清理或删除历史图片行为。
### 2. 单张原图下载改为局部非阻塞任务
修改 `app/gui/workers.py` 与 `app/gui/tabs/image_studio.py`:
- `ImageStudioDownloadOriginalWorker` 增加结构化进度事件:开始、重试、成功、最终失败;失败重试 2 次,即最多尝试 3 次,退避等待建议为 1 秒、2 秒,等待发生在后台线程。
- 单张下载不要再调用通用 `_start_worker()` / `_set_running(True)`;新增 AI工场原图下载专用启动和清理路径。
- 以 `asset_id` 保存独立 worker/QThread 引用,直到各自 `thread.finished` 才释放,遵守 T-604 的线程生命周期规则;不允许同一图片重复创建下载任务。
- 同一项目完整原图下载最大并发为 2。超过并发上限的点击进入内存队列,卡片显示“等待下载”;队列任务在空位出现后自动开始。不得同时启动九个完整原图下载。
- 下载过程中仅更新对应原图卡片(等待下载/下载中/重试中/下载失败/已下载)和原图区状态 label;其他商品、照片池、模板、提示词、终选、生成和导出控件保持可操作。
- 原图区标题「蝦皮原主图」右侧增加运行状态 label:
- `正在下载蝦皮原主图 #3`
- `蝦皮原主图 #3 下载失败,正在重试 1/2`
- 多张并行时显示汇总,例如 `正在下载蝦皮原主图 #2、#5(2/2)`。
- 最终失败不弹阻塞式错误框;卡片和右侧 label 显示中文失败状态,用户可再次单击该卡片重新排队。不得向用户展示远程 URL、Cookie、堆栈或英文网络错误。
- 下载完成后只刷新当前仍匹配该 `project_id` 的 UI:原图进入照片池、可选择为源图;若用户已切换项目,后台写入仍可完成,但不得污染新项目的图片网格或状态 label。
### 3. 与重新拉取、删除和关闭的并发边界
- 当前项目存在原图完整下载中的任务时,禁止删除该项目,并给出中文说明;避免软删除后仍有下载 worker 回写当前项目。
- 同一项目存在原图下载中/排队任务时,点击「拉取蝦皮主图」应提示先等待当前下载队列完成或取消队列,避免刷新 URL 快照与原图落盘并发冲突。
- 切换到其他商品项目不阻塞;状态 label 跟随当前项目展示,回调按项目ID隔离。
- 关闭 AI工场或程序时,取消尚未开始的队列;已启动 worker 继续遵守安全线程清理,不保留失效的 GUI 回调。
- 本任务不改变完整原图下载的安全校验、文件原子写入、目录布局、CDP 打开/关闭商品页或登录检测。
### 4. 压缩原图区和商品列表,空间让给照片池
- 将原图缩略图尺寸和卡片网格缩小约一半:原图区高度目标约 94~110px,缩略图约 48px;在 1280×820 常用窗口下优先让 9 张原图紧凑单行展示,较窄窗口可稳定换行或滚动,不重叠。
- 原图区减少的垂直空间全部由照片池获取;照片池保持可滚动网格、源图高亮、任务状态和拖入终选能力。
- 商品列表不机械从 220px 压到约 110px。为保留店铺、商品ID、更新时间的可读性,最小宽度调整到约 150px,并调整 `QSplitter` 初始比例,把释放出的横向空间优先给“蝦皮原主图 + 照片池”中间区域,而不是挤压生成设置。
- 在 1280×820 及较窄窗口下验证:店铺名、商品ID、按钮和状态 label 不重叠;原图区、照片池和生成设置都可操作。
## 验收要点
- 对本地已有完整原图的店铺/商品点击「拉取蝦皮主图」时,先显示中文重新拉取确认;取消后不启动 worker、不写库、不刷新 UI。
- 确认重新拉取后,当前线上主图快照刷新;本地图片文件、生成记录、终选记录不被物理删除,相同 URL 的本地原图可复用。
- 单击未下载原图后,整页不会变灰;只有对应卡片进入下载状态,原图区右侧显示中文进度。
- 单张失败后自动重试 2 次;最终失败不弹阻塞错误框,可再次单击重新排队,且不泄露 URL/英文网络错误。
- 快速点击多张原图时,同一图片只下载一次,完整原图实际并发不超过 2,超出的卡片显示等待下载。
- 下载完成的原图进入照片池并可选为源图;项目切换后不会把旧项目的下载结果显示到新项目。
- 下载中或排队时,当前项目不能删除,也不能重新拉取同一项目;其他不冲突的 AI工场操作保持可用。
- 原图区高度约缩小一半,照片池获得对应空间;商品列表缩窄但店铺、商品ID、更新时间仍可扫读。
- 不改 CDP 选择器、Chrome 启动/登录策略、cmhub 生图协议、①至⑤模块或图片文件物理清理语义。
## 测试要求
更新或新增:
- `tests/test_gui.py`
- 覆盖本地已有原图时显示重新拉取确认;取消不创建 pull worker,确认后使用当前账号/商品ID。
- 覆盖无本地原图时直接拉取,不多弹确认。
- 覆盖单张下载不调用全局 `_set_running(True)`,其他主要控件保持启用。
- 覆盖同 asset 去重、最多并发 2、第三张进入队列、成功/失败/重试后卡片和原图区状态 label 更新。
- 覆盖项目切换、删除保护、重新拉取保护和窗口关闭后的下载任务清理。
- 覆盖原图区与商品列表尺寸约束、1280×820 下不重叠。
- `tests/test_image_studio_images.py` / `tests/test_workers.py`
- mock 远程下载失败,覆盖初次 + 2 次重试、退避不在 GUI 线程、最终中文失败语义。
- 覆盖相同 URL 重新拉取复用本地资产,不物理删除历史文件。
验证命令:
```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
```
人工验收建议:
- 准备一个已有 1~9 张本地主图的项目,分别验证取消/确认重新拉取后的列表与本地文件。
- 模拟一张下载慢、一张下载失败、一张排队,确认可继续编辑提示词、查看照片池、切换商品;最终卡片状态和右侧 label 正确。
- 在 1280×820 与较窄窗口下检查原图区、照片池、商品列表和生成设置不重叠。
## 边界(不改什么)
- 不新增永久删除、历史图片清理、回收站或本地图片覆盖功能。
- 不改 `image_studio_projects/assets/jobs/selections` schema,除非实现局部下载状态确实无法用内存状态表达且先补充迁移与兼容说明。
- 不改 `app/editor.py`、CDP 读取蝦皮主图选择器、Chrome 前台/后台策略、登录检测或验证码边界。
- 不改 cmhub 生图提交、轮询、下载、计费和 BYOK 逻辑。
- 不在用户日志、提示框、tooltip 中暴露完整远程 URL、Cookie、密钥、堆栈或英文网络错误。
- 不改①至⑤模块,不新增本地图片导入入口。
## 执行记录
- 2026-07-11:在 `ImageStudioTab` 增加本地原图计数与重新拉取确认;同一项目存在原图下载/排队时,拦截重新拉取和软删除。
- 2026-07-11:将单张原图完整下载从通用全局 worker 拆为专用内存队列;同图去重、最多并发 2、失败自动重试 2 次(最多 3 次尝试),仅刷新原图卡片和原图区状态,不再禁用整个 AI工场。
- 2026-07-11:增加下载中/等待/重试/失败状态、项目切换隔离、关闭时取消未开始队列和已启动 worker 的取消请求;压缩原图区与商品列表尺寸,将空间让给照片池。
- 2026-07-11:新增/更新 GUI 与 worker 回归测试。干净 worktree 验证通过:`python -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`py -3.10 -m unittest discover -s tests`(386 项)、`git diff --check`。主工作区另有未提交默认提示词文件变更,导致其全量测试中 3 项默认提示词断言失败,未纳入本任务修改。