feat(gui): replace AI studio with product suite

This commit is contained in:
chengma
2026-07-14 09:53:13 +08:00
parent fb873aae90
commit bc115ba0d7
27 changed files with 3453 additions and 81 deletions
+4 -4
View File
@@ -4,10 +4,10 @@
## 一句话定位
蝦皮圈優化助手(代号 cmshopee)是一个 Windows 本地桌面自动化工具(PySide6,当前正式界面显示 5 个工作流 Tab),让运营管理多个 Shopee 卖家账号,并用 CDP 驱动 Chrome + AI 批量改商品标题、换商品封面。AI工场图片候选工作区的代码与数据能力保留,但当前暂不在主界面展示入口。
蝦皮圈優化助手(代号 cmshopee)是一个 Windows 本地桌面自动化工具(PySide6,当前正式界面显示 6 个工作流 Tab),让运营管理多个 Shopee 卖家账号,并用 CDP 驱动 Chrome + AI 批量改商品标题、换商品封面;⑥「商品套图」用于按账号与商品生成本地电商套图,不自动上传蝦皮。
主流水线(工作流优先顺序):
**① 导入采集 → ② AI生成 → ③ 更新蝦皮 → ④ 账号管理 → ⑤ 设置**。⑥ AI工场是保留的图片项目/照片池/cmhub 托管生图工作区,当前隐藏主界面入口且不自动上传蝦皮。
**① 导入采集 → ② AI生成 → ③ 更新蝦皮 → ④ 账号管理 → ⑤ 设置 → ⑥ 商品套图**。①~③是 Excel 批量更新主流水线;⑥是独立的本地商品套图工作区,复用 cmhub 托管生图与既有 `image_studio_*` 数据,不自动上传蝦皮。
目标闭环:④ 配账号并登录 → ① 导入 Excel(按“别名”列关联账号)、采集旧标题/旧封面并回写 → ② 用提示词 AI 生成新标题/新封面(不设逐条确认阶段)→ ③ 对已生成任务点击「开始更新」,弹窗确认后批量改标题+换封面并点「更新」提交 → 结果实时存 SQLite、批量回写原 Excel。
@@ -30,7 +30,7 @@
## 当前阶段
当前项目处于:**V0 单账号流程已验证,正在实现 V1 多账号 + Excel + AI 的 5 Tab GUI 工作台;AI工场入口暂时隐藏**。
当前项目处于:**V0 单账号流程已验证,V1 多账号 + Excel + AI 的 6 Tab GUI 工作台持续收口中;⑥商品套图已接入**。
优先路径:
@@ -65,7 +65,7 @@
**V1 当前 coding 目标**:
- 当前正式界面为 5 Tab 工作台:① 导入采集 → ② AI生成 → ③ 更新蝦皮 → ④ 账号管理 → ⑤ 设置;⑥ AI工场的代码与本地数据保留,但主界面入口暂时隐藏,不自动提交线上。
- 当前正式界面为 6 Tab 工作台:① 导入采集 → ② AI生成 → ③ 更新蝦皮 → ④ 账号管理 → ⑤ 设置 → ⑥ 商品套图。旧 `ImageStudioTab` 只保留内部兼容,主界面入口由 `ProductSuiteTab` 替代;⑥只生成和管理本地图片,不自动提交线上。
- GUI 固定为 PySide6;后台采集/生成/更新用 `QObject` worker + `QThread` + signal 回传进度。
- 多账号管理;账号以独立 user-data-dir 隔离。③ 更新默认串行,提供「检查本轮更新」按钮;⑤ 可开启按账号并行和设置每批最大更新条数。
- Excel 导入/回写 + SQLite 实时落库 + 本地图片目录。
+3 -2
View File
@@ -32,9 +32,9 @@
| 换封面 | 上传一张本地图片,自动设为商品封面(满 9 张时必须先确认本地旧封面备份存在,再删原封面并上传) | P0 |
| 提交更新 | 在显式确认后点击「更新」,把改动提交到线上;原型脚本默认不提交 | P0 |
### V1 当前目标(多账号 + Excel + AI,5 Tab 流水线)
### V1 当前目标(多账号 + Excel + AI,6 Tab 工作台)
5 个 Tab,顺序:① 导入采集 → ② AI生成 → ③ 更新蝦皮 → ④ 账号管理 → ⑤ 设置。
6 个 Tab,顺序:① 导入采集 → ② AI生成 → ③ 更新蝦皮 → ④ 账号管理 → ⑤ 设置 → ⑥ 商品套图。
| 功能 | 用户能做什么 | 优先级 |
| --- | --- | --- |
@@ -49,6 +49,7 @@
| 检查本轮更新 / 运行日志 / 多账号并行(③/⑤) | ③ 提供「检查本轮更新」按钮,不打开 Shopee、不提交、不改任务状态,只显示当前筛选范围、店铺分布、预计分批、更新内容和会更新字段;点击「检查本轮更新」或「开始更新」时清空③界面旧日志,只显示本轮检查/更新日志;真实更新写运行日志;可在⑤开启多账号并行,同账号内仍串行 | V2 已接入,检查按钮已接入 |
| 结果存储与回写 | 各阶段结果实时存 SQLite;该文件全部完成后把旧/新数据+状态批量回写原 Excel | P0 |
| 设置(⑤) | AI 模型管理(下拉+新增/删除/详情/测试连接,至少各一个文本+图像模型);标题/图片大模型角色选择;分辨率(512/1k/2k/4k,普通默认 cmhub 模式下只控制生图尺寸,返回超时只读展示实际口径:标题 600 秒 / 图片 900 秒;direct 兼容路径仍按分辨率映射);并发/重试/jpg质量,其中 cmhub 生图请求与图片下载实际并发最大 5,超过后内部限流且运行日志展示实际并发;图片目录/Chrome 路径/端口;设置页采用居中内容区、适度左右留白和响应式三列表单,长字段跨列显示;点击「保存设置」成功后弹轻量提示框 | P0 |
| 商品套图(⑥) | 以账号+商品ID建立本地图片项目;可拉取蝦皮主图或添加本地原图,按白底图/场景图/卖点图及自定义分类生成套图;支持多任务并行、历史结果、单图重试、删除撤销;只写本地项目,不自动上传或修改蝦皮 | P1 |
| 首次引导保护 | 未配账号、对应账号 Chrome 未启动或未登录时,① ③ 执行按钮禁用/执行前拦截并提示去④;③ 不自动打开缺失账号 Chrome,必须中止本轮更新 | P0 |
### 后续迭代
+1 -1
View File
@@ -30,7 +30,7 @@
## 二、决策记录与演进
- **CDP 自研而非 playwright**:当前已验证根目录 `cdp.py`,正式代码迁入 `app/cdp.py`;它零重依赖、完全可控,并已在开发环境绕开了代理(`*_proxy` 指向本地 :1080)和 Chrome 的 Origin 403 两个坑。未来若交互复杂度大幅上升,再评估 playwright。
- **GUI 选 PySide6**:V1 是 6 Tab 运营工作台,包含任务表格、筛选、图片预览、后台采集/生成/更新、AI工场照片池、进度与停止。当前环境已安装 PySide6,且 Tkinter 不可用;Qt 的 `QThread`/signal-slot 比 Tkinter 手动 queue/after 更适合长任务回传 UI。
- **GUI 选 PySide6**:V1 是 6 Tab 运营工作台,包含任务表格、筛选、图片预览、后台采集/生成/更新、商品套图多任务、进度与停止。⑥采用原生 `ProductSuiteTab`,不引入 QtWebEngine;Qt 的 `QThread`/signal-slot 用于长任务回传 UI。
- **存储拆两层**:应用设置进 `data/config.json`,账号/任务/结果进 SQLite。判据:少量人改无需查询 → 配置文件;成行增长要查询/导出 → DB。同一事实只存一处,不重复。取代早期的 `accounts.json` 方案。
- **Excel 用 openpyxl**:运营用真实 .xlsx;stdlib 无法读写 xlsx,引入一个轻依赖比改用 CSV 更贴合用户习惯。
- **多账号隔离用独立 user-data-dir,不用 Chrome profile**:profile 共享同一 user-data-dir/进程/调试端口,无法每账号独立 CDP 与并行;独立 user-data-dir 才契合自动化。详见 [架构 3.0](04-architecture.md)。
+17 -14
View File
@@ -5,14 +5,14 @@
## 一、系统结构
Windows 本地桌面自动化工具,无后端服务。当前正式 GUI 显示 5 个工作流 Tab;AI工场图片工作区的代码、数据库与本地资产能力保留,但主界面入口暂时隐藏。
Windows 本地桌面自动化工具,无后端服务。当前正式 GUI 显示 6 个工作流 Tab;⑥商品套图使用新的原生 PySide6 界面,并复用既有 `image_studio_*` 数据与生图服务。
```text
运营(人)
|
v
GUI(PySide6 QTabWidget,当前显示 5 Tab)
① 导入采集 ② AI生成 ③ 更新蝦皮 ④ 账号管理 ⑤ 设置
GUI(PySide6 QTabWidget,当前显示 6 Tab)
① 导入采集 ② AI生成 ③ 更新蝦皮 ④ 账号管理 ⑤ 设置 ⑥ 商品套图
|
v
核心模块(Python)
@@ -25,8 +25,9 @@ GUI(PySide6 QTabWidget,当前显示 5 Tab)
├── cdp CDP 客户端(连接、找/开 tab、执行 JS、拖拽)
├── editor 登录检测 / 采集旧标题旧封面 / 改标题 / 换封面 / 点更新
├── ai 文本生成(提示词+旧标题→新标题)/ 图像生成(提示词+旧封面→新封面)
├── image_studio AI工场项目/资产/job/终选顺序数据服务
├── image_studio_images 远程原图安全下载、缩略图、原图落盘
├── image_studio 商品套图项目/资产/job数据服务(兼容旧AI工场终选)
├── product_suite 套图设置归一化、数量计算、完整提示词与job规划
├── image_studio_images 远程原图安全下载、本地原图导入、生成图废纸篓
├── image_studio_generation cmhub 托管多图异步 submit/poll/download 编排
└── image_studio_export 终选图片本地 JPEG 转码、目录安全导出
|
@@ -39,8 +40,8 @@ Shopee 卖家中心页面 / 本地图片目录
真实组件:
- GUI 入口:根目录 `main.py` 调用 `app/gui/` 包(PySide6 + `QMainWindow` + `QTabWidget`,当前正式界面显示 5 Tab);包入口 `app/gui/__init__.py` 提供 `main()` 并兼容 `from app import gui` / `from app.gui import MainWindow`;也支持 `python -m app`。`ImageStudioTab` 及其服务层仍保留在代码和发布包中,但当前不加入顶层 `QTabWidget`。
- 核心模块统一放在正式代码包 `app/`:`appconfig.py`、`db.py`、`excel.py`、`config.py`、`accounts.py`、`chrome.py`、`editor.py`、`workers.py`、`ai.py`、`prompts.py`、`image_studio.py`、`image_studio_images.py`、`image_studio_generation.py`、`image_studio_export.py`;CDP 底座迁入 `app/cdp.py`(当前根目录 `cdp.py` 为已验证来源)。
- GUI 入口:根目录 `main.py` 调用 `app/gui/` 包(PySide6 + `QMainWindow` + `QTabWidget`,当前正式界面显示 6 Tab);包入口 `app/gui/__init__.py` 提供 `main()` 并兼容 `from app import gui` / `from app.gui import MainWindow`;也支持 `python -m app`。第六 Tab 只创建 `ProductSuiteTab`;旧 `ImageStudioTab` 保留内部兼容,但不加入顶层 `QTabWidget`。
- 核心模块统一放在正式代码包 `app/`:`appconfig.py`、`db.py`、`excel.py`、`config.py`、`accounts.py`、`chrome.py`、`editor.py`、`workers.py`、`ai.py`、`prompts.py`、`image_studio.py`、`product_suite.py`、`image_studio_images.py`、`image_studio_generation.py`、`image_studio_export.py`;CDP 底座迁入 `app/cdp.py`(当前根目录 `cdp.py` 为已验证来源)。
- 已验证脚本(重构进模块):`prototypes/demo.py`、`prototypes/set_title.py`、`prototypes/set_cover.py`、`prototypes/get_title.py`、`prototypes/cookies.py`、`prototypes/inspect_images.py`、`prototypes/grab.py`。
- 外部依赖:本机 Google Chrome;Shopee;AI 服务(文本+图像;普通产品默认 cmhub 网关,由 `data/config.json` 的 `ai.cmhub` + `data/config/cmhub.json` 配置;direct 直连模型清单仅作为内部兼容/手工回滚路径保留);`openpyxl`。
@@ -63,7 +64,7 @@ imported → collected → generated → applied
## 三、职责划分
**GUI(当前显示 5 Tab)**:见 [routes.md](routes.md)。只做交互与预览,不写业务逻辑;耗时操作走 PySide6 `QObject` worker + `QThread`,用 signal 回主线程刷新 UI。**① 采集点击后会为本轮匹配到的账号自动确保 Chrome 就绪:已打开则复用,未打开才启动;随后只检测登录态,未登录账号的任务跳过并汇总提示去 ④人工登录。③ 更新蝦皮仍是线上提交高风险链路:执行前只检测账号 Chrome/CDP/登录态,不自动启动缺失账号 Chrome。⑥ AI工场入口当前隐藏;其只读拉图、下载图片和 cmhub 托管生成代码仍保留,且不会自动上传蝦皮。**
**GUI(当前显示 6 Tab)**:见 [routes.md](routes.md)。只做交互与预览,不写业务逻辑;耗时操作走 PySide6 `QObject` worker + `QThread`,用 signal 回主线程刷新 UI。**① 采集点击后会为本轮匹配到的账号自动确保 Chrome 就绪:已打开则复用,未打开才启动;随后只检测登录态,未登录账号的任务跳过并汇总提示去 ④人工登录。③ 更新蝦皮仍是线上提交高风险链路:执行前只检测账号 Chrome/CDP/登录态,不自动启动缺失账号 Chrome。⑥商品套图的只读拉图、原图下载/导入、AI帮写和 cmhub 生成均在独立 worker 中运行;多任务可并行,且不会自动上传蝦皮。**
**核心模块**
@@ -84,7 +85,7 @@ imported → collected → generated → applied
- cmhub 网关 Key → `data/config/cmhub.json`,schema `{ "api_key": "..." }`;`config.json` 只保存 Base URL、别名和超时,不保存 Key。
- 业务数据(账号、任务、各阶段结果)→ SQLite `data/cmshopee.db`。
- 图片(采集的旧封面、AI 生成的新封面)→ `data/images/`(路径记在 DB)。
- 提示词 → 标题当前工作文本存单文件 `data/title_prompt.txt`;标题命名模板存 `data/prompts/title/<名称>.txt`;封面命名模板存 `data/prompts/cover/<名称>.txt`;AI工场完整提示词模板存 `data/prompts/image_studio/<名称>.txt`。
- 提示词 → 标题当前工作文本存单文件 `data/title_prompt.txt`;标题命名模板存 `data/prompts/title/<名称>.txt`;封面命名模板存 `data/prompts/cover/<名称>.txt`;旧 AI工场模板目录 `data/prompts/image_studio/<名称>.txt` 仅保留兼容,⑥商品套图直接把卖点文本与结构化设置保存在项目表。
- 登录态 → 各账号 `data/chrome_user_data_dir/<slug>/`。
T-538 后统一数据根为 `data/`:打包版默认 `<exe目录>/data`,源码运行默认项目根 `data/`。`config.json` 中 `user_data_root`、`image_dir`、`db_path` 默认仍保存为 `chrome_user_data_dir`、`images`、`cmshopee.db` 等相对值,运行时由 `appconfig` 解析到 `data/` 下;绝对路径作为高级自定义仍按原值使用。启动时会迁移 T-524 旧包的 exe 顶层数据到 `data/`,并检测 `data/` 可写。
@@ -141,7 +142,7 @@ T-538 后统一数据根为 `data/`:打包版默认 `<exe目录>/data`,源
`ai` 段只放**选择 + 全局生成参数**:
- `backend`:内部字段,取值仍支持 `cmhub` / `direct`;普通产品默认 `cmhub`,⑤设置页不再展示「AI 后端」label 或 direct/cmhub 下拉,保存设置固定写 `cmhub`。`direct` 仅保留为内部兼容/手工回滚路径。
- `cmhub`:cmhub 网关配置,`base_url` 为网关根地址,保存和请求前会规整为 scheme+host(+port),去掉 `/api`、`/api/v1`、其它路径、查询串和片段;`title_alias` / `image_alias` 为 `GET /api/v1/models` 发现的能力别名,`connect_timeout` 为连接超时;API Key 不在此处保存。⑤设置页与⑥AI工场只展示 cmhub 托管“默认档 / 高质量档 / 省点档”、展示名、用途和扣点提示,不展示 OpenAI Key、Provider URL、上游接口路径或直连模型 slug;执行层仍只保存 alias。
- `cmhub`:cmhub 网关配置,`base_url` 为网关根地址,保存和请求前会规整为 scheme+host(+port),去掉 `/api`、`/api/v1`、其它路径、查询串和片段;`title_alias` / `image_alias` 为 `GET /api/v1/models` 发现的能力别名,`connect_timeout` 为连接超时;API Key 不在此处保存。⑤设置页展示托管档位、别名和扣点提示;⑥商品套图直接使用已保存的生图 alias,不展示 OpenAI Key、Provider URL、上游接口路径或直连模型 slug。
- `default_text_model` / `default_image_model`:仅 direct 内部兼容模式下引用 `ai_models.json` 里的模型名(标题用文本模型、封面用图像模型);普通 cmhub 模式不读取这些模型定义,⑤设置页不再展示标题/图片模型角色下拉。
- `generate_mode`:②「生成内容」下拉的主字段,取值 `title` / `cover` / `title_cover`,分别表示只生成标题、只生成封面、生成标题和封面;默认 `title`,避免用户无意产生封面生成成本。
- `generate_cover`:旧兼容字段;保存配置时仍写回,值由 `generate_mode` 推导。旧配置 `false` 会迁移为 `title`,`true` 会迁移为 `title_cover`。GUI 和生成逻辑以 `generate_mode` 为准。
@@ -419,7 +420,9 @@ data/images/<batch_id>/<slug>/<task_id>_<item_id>_new.<ext> # AI 生成的新
- 进度:标题和图片两条进度分开显示;只生成标题时图片进度显示本轮未生成/0 张,并在运行日志写明本轮生成内容。
- 任一组件生成后 `stage=generated`;**不设逐条人工审核阶段**。若只有标题,③可选择只更新标题;若只有封面,③可选择只更新封面,②标题状态仍为待生成,后续补标题会保留已有封面且不重复生图。双击任务弹窗查看旧封面、新封面和历史候选图;T-577 后弹窗内「重置图片」只清当前任务 `new_cover_path` 并归档旧图,不启动单条 `GenerateWorker`,用户退出后用状态筛选「待生成」批量补生成封面。②「重置生成结果」提供标题/封面/全部的多选或当前筛选范围重置,默认不删除本地新封面文件;已生成且未提交线上的新标题可在②表格本地微调。
- 并发数、重试、分辨率、jpg 质量、模型/Key 均来自 ⑤ 设置(`data/config.json` 的 `ai` 段;Key 存 `data/config/cmhub.json` 或 direct 兼容清单)。T-547 后标题并发和图片并发都限制为 1..5,失败重试次数限制为 0..10;旧 `config.json` 或手工配置的超限值会在加载/保存时夹紧。⑤仍只展示一个「图片并发」设置;cmhub 模式下②运行日志显示“图片并发 X,cmhub实际生图并发 Y,下载并发 Y”。
- ⑥AI工场固定使用⑤保存的 cmhub 生图 alias。生成区显示“cmhub 托管默认档 / 高质量档 / 省点档”、当前生图别名、余额和扣点;档位是产品说明层,真实模型可由 cmhub 后台调整,客户端不保存 OpenAI slug。
- ⑥商品套图固定使用⑤保存的 cmhub 生图 alias;平台、国家地区、输出语言、比例、分类、商品ID、参考图序号和卖点文本由 `product_suite.build_suite_prompt()` 组成每个 job 的完整提示词。比例同时传入 `image_studio_generation.run_jobs(aspect_ratio=...)`,最终进入 cmhub 请求与输出资产元数据。
- `image_studio_projects.suite_settings_json` 持久化套图设置,旧数据库由 `db.init_db()` 原位补列,默认 `{}`;`draft_prompt` 继续保存卖点文本。`image_studio_assets` 中有效商品原图最多16张,历史 missing 记录不占有效名额;手工原图不会因再次同步蝦皮 URL 被误标 missing。
- 第六 Tab 的多个 `SuiteTaskState` 各自保留 generation/pull/import/AI/download worker 与线程引用;切换任务不取消任务。多个任务可并行,但 `image_studio_generation` 使用进程级 semaphore 保证所有套图任务合计最多5个 cmhub 在途 job。线程还在运行时关闭任务只请求协作式停止,模块级引用保留到 `QThread.finished`,不得提前销毁线程对象;下载前后均检查停止信号,停止后的临时文件不入资产库。
提示词管理:
@@ -461,14 +464,14 @@ data/images/<batch_id>/<slug>/<task_id>_<item_id>_new.<ext> # AI 生成的新
| 代理干扰 | 清除 `*_proxy`(requests `trust_env=False`),否则连本地 CDP 超时 |
| WebSocket Origin | `websocket-client` `suppress_origin=True` |
| 关闭连接 vs 关闭 tab | `CDP.close()` 只关闭 WebSocket;需要关闭浏览器页面时必须调用浏览器 target 关闭接口。采集只关闭本轮自动新建的商品页,复用的用户已有 tab 不关闭;③ 更新时程序自动新建的商品页成功/失败都关闭,成功提交且确认跳回商品列表页时关闭前等待 2 秒;③ 复用用户已有商品页时不关闭页面 |
| 前台激活 | ①采集和⑥AI工场只读打开商品页时不主动 `Page.bringToFront`;新建 tab 尝试 `Target.createTarget(background=true)`,不支持时退回普通新建。③更新真实提交每条任务都以前台方式新建或激活商品 tab,并执行 `Page.bringToFront`,保障上传、图片管理器刷新和拖拽排序稳定;后台态封面恢复逻辑仅保留给兼容直接调用,不作为正常③批量路径 |
| 前台激活 | ①采集和⑥商品套图只读打开商品页时不主动 `Page.bringToFront`;新建 tab 尝试 `Target.createTarget(background=true)`,不支持时退回普通新建。③更新真实提交每条任务都以前台方式新建或激活商品 tab,并执行 `Page.bringToFront`,保障上传、图片管理器刷新和拖拽排序稳定;后台态封面恢复逻辑仅保留给兼容直接调用,不作为正常③批量路径 |
| SPA 就绪 | 不用 load 事件;轮询“标题输入框 + 图片 itembox + 上传输入框”三者都在 |
| 商品页错误 toast | Shopee 错误提示使用 `.eds-toasts` / `.eds-toast__content`,可能很快隐藏或 `display:none`。打开商品页/等待 SPA 就绪前应注入 `MutationObserver` 或等价监听,把 toast 文本、`outerHTML`、当前 URL、时间、可见状态保存到页面缓存(如 `window.__cmshopee_toasts`);等待详情页关键元素超时时,再兜底读取当前 DOM 中的 toast。最近错误 toast 应优先成为 `open_product` 失败原因,并写入 DB 运行日志和本地脱敏诊断日志。只有明确商品失效/不存在/无权限类 toast 才驱动①阶段列显示“商品失效”;网络、CDP、未登录、页面超时、风控等其他失败仍显示“失败” |
| 标题输入框 | XPath `//input[@class='eds-input__input' and string-length(@modelvalue)>24]` |
| 写标题 | 原生 setter + 派发 `input`/`change`;`value`==`modelvalue`==新值 |
| 读旧封面 | 第一张 itembox 的 `img.src`(`susercontent` CDN),下载到本地 |
| AI工场原主图读取 | 复用 `open_product(..., bring_to_front=False)` 后台只读打开商品详情页,使用已验证 itembox 顺序读取全部主图 `img.src` 并返回 `{index, src}`;不要求上传 input 之外的新选择器、不下载图片、不改标题/封面、不拖拽、不点击更新;本轮自动新建 tab 按采集规则关闭,复用用户已有 tab 不关闭 |
| 商品套图原主图读取 | 复用 `open_product(..., bring_to_front=False)` 后台只读打开商品详情页,使用已验证 itembox 顺序读取全部主图 `img.src` 并返回 `{index, src}`;不要求上传 input 之外的新选择器、不改标题/封面、不拖拽、不点击更新;URL 读取完成后再由最多2个图片下载 worker 落盘;本轮自动新建 tab 按采集规则关闭,复用用户已有 tab 不关闭 |
| 上传输入框 | `.shopee-image-manager__upload input[type=file]`;上传前先点击 `.shopee-image-manager__upload` 上传块以模拟人工选择图片入口,短暂等待后重新获取 input,再用 `DOM.setFileInputFiles` 传 Windows 路径并派发 `input`/`change` |
| 上传成功 | 上传前先等图片管理器稳定。注意分两种状态:未满 9 张时,上传前要求图片 src 连续稳定、无 loading/blob、上传 input 存在且未禁用;满 9 张时,删除第一张之前只要求现有图片列表稳定,不得要求上传 input 可用,因为 Shopee 可能因满格隐藏/禁用上传入口;删除成功后再要求上传 input 恢复可用。上传后等新图 src 为 `susercontent`。若手动上传成功但自动上传一直转圈,优先检查是否绕过了上传块点击导致 Shopee 前端上传队列未完整初始化;代码应走“点击上传块 → 等待 → 重新取 input → `DOM.setFileInputFiles`”的人工等价路径。T-404 补丁后超时失败会返回 `upload_state`,区分仍在转圈(`UPLOAD_STILL_PROCESSING`)、图片上传错误(`UPLOAD_PAGE_ERROR`)、裁剪弹窗(`UPLOAD_CROP_REQUIRED`)和上传入口未恢复(`UPLOAD_INPUT_NOT_READY`);上传阶段只能把图片管理器内错误或图片/文件/上传相关 toast 归为封面上传错误,物流/备货等页面级校验错误不能阻断封面上传,应留到点击「更新」提交阶段处理;`有1張重複的圖片` / `重複` / `重复` / `duplicate` 属于封面上传错误,必须立即失败并提示新封面与现有商品图片重复 |
| 封面=第一位 | `Input.dispatchMouseEvent` 拖到第一位,落点 `第一张.left - 0.30*宽` |
@@ -516,7 +519,7 @@ cmshopee/
│ └── prompts/
│ ├── title/<名称>.txt # 标题提示词命名模板
│ ├── cover/<名称>.txt # 封面提示词命名模板
│ └── image_studio/<名称>.txt # AI工场完整提示词模板
│ └── image_studio/<名称>.txt # 旧AI工场模板兼容目录,商品套图不依赖
└── prototypes/ # 已验证原型/探查脚本(demo/set_*/get_title/cookies/inspect_images/grab/1.py)
# 逻辑待并入 app/editor.py 后清理;见 prototypes/README.md
```
+2 -2
View File
@@ -4,7 +4,7 @@
## 一句话定位
蝦皮圈優化助手(代号 cmshopee)是一个给**电商运营**使用的 Windows PySide6 桌面自动化工具,用于**管理多个 Shopee 卖家账号、并用 CDP 驱动 Chrome 批量修改商品标题、替换商品封面图**。V0 已验证单账号「改标题 + 换封面」闭环;当前 V1 工作台为 6 Tab:①导入采集 → ②AI生成 → ③点击「开始更新」并确认后批量提交 → 回写结果,④账号管理,⑤设置,⑥AI工场图片候选生成。
蝦皮圈優化助手(代号 cmshopee)是一个给**电商运营**使用的 Windows PySide6 桌面自动化工具,用于**管理多个 Shopee 卖家账号、并用 CDP 驱动 Chrome 批量修改商品标题、替换商品封面图**。V0 已验证单账号「改标题 + 换封面」闭环;当前 V1 工作台为 6 Tab:①导入采集 → ②AI生成 → ③点击「开始更新」并确认后批量提交 → 回写结果,④账号管理,⑤设置,⑥商品套图生成。
## 文档导航
@@ -21,7 +21,7 @@
- [任务看板(历史归档)](06-tasks.md):T-000~T-549 历史任务,已冻结,不再新增。
- [模块 / CLI 合约](api.md):本地模块接口、Chrome 启动参数、账号配置 schema。
- [界面与流程结构](routes.md):GUI 窗口、操作流程、按钮职责(无前端路由,用 GUI 流程替代)。
- [AI工场端到端验收](ai-studio-e2e-checklist.md):⑥ AI工场 cmhub 托管主线的自动化覆盖、人工只读验收和发布检查。
- [旧 AI工场兼容层端到端验收](ai-studio-e2e-checklist.md):T-622 前图片工作区的自动化覆盖;当前⑥商品套图继续复用其中的数据与 cmhub 服务。
- [AI工场托管模型评测与默认档位策略](ai-studio-model-evaluation.md):OpenAI / GPT 托管能力经 cmhub 别名落地前的评测样本、档位策略、上线门槛与运营用法。
- [AI工场 BYOK 启动门禁记录](byok-startup-gate.md):自定义模型 / BYOK 开工前的授权、设备凭证、Provider 白名单、task secret 和接口契约检查记录。
- [当前实现状态](current-state.md):当前代码现实、可运行命令、下一步可做任务。
+2
View File
@@ -1,5 +1,7 @@
# AI工场端到端验收与发布检查
> T-622 后主窗口入口已由「⑥商品套图」替换;本文保留为旧 `ImageStudioTab` 与共享 `image_studio_*` 服务的兼容回归清单,不再代表当前第六 Tab 的界面验收。当前商品套图交互以 [`tasks/T-622.md`](tasks/T-622.md) 和 [`routes.md`](routes.md) 为准。
> T-595 阶段性收口文档。本文只记录 AI工场当前已实现的 cmhub 托管主线,不包含 BYOK、自定义 Provider、自动上传蝦皮或本地图片导入。
## 已实现闭环
+25 -6
View File
@@ -355,9 +355,9 @@ render_prompt(template_text, task) -> str
- `list_cover_templates()` 不会在启动时创建文件;只有保存/新建/另存为才写 `data/prompts/cover/*.txt`。
- 模板名不可为空,不允许路径分隔符、`..` 或 Windows 非法文件名字符;重命名时目标重名会报错。
## AI工场模块(`app/image_studio*.py`,已建)
## 商品套图模块(`app/product_suite.py` + `app/image_studio*.py`,已建)
AI工场是 ⑥ Tab 的本地图片项目工作区,不自动上传蝦皮、不修改线上商品。
商品套图是 ⑥ Tab 的本地图片项目工作区,不自动上传蝦皮、不修改线上商品。底层继续复用旧 AI工场的数据表与生成服务,避免迁移既有项目数据。
```python
# app/image_studio.py
@@ -365,9 +365,13 @@ ImageStudioProject / ImageStudioAsset / ImageStudioJob / ImageStudioSelection
create_or_get_project(account_or_fields, item_id, ...) -> ImageStudioProject
list_projects(path=None) -> list[ImageStudioProject]
update_project_prompt(project_id, draft_prompt, path=None) -> ImageStudioProject
project_suite_settings(project) -> dict
update_project_suite_settings(project_id, settings, path=None) -> ImageStudioProject
sync_original_asset_urls(project_id, image_urls, path=None) -> list[ImageStudioAsset]
list_assets(project_id, kind=None, include_missing=True, path=None) -> list[ImageStudioAsset]
reorder_original_assets(project_id, asset_ids, path=None) -> list[ImageStudioAsset]
create_job(project_id, source_asset_id=None, job_type="main", prompt="", ...) -> ImageStudioJob
list_jobs(project_id, statuses=None, path=None) -> list[ImageStudioJob]
list_resumable_jobs(project_id=None, include_failed_downloads=False, path=None) -> list[ImageStudioJob]
replace_selections(project_id, selection_type, asset_ids, path=None) -> list[ImageStudioSelection]
pull_remote_main_image_urls(account_or_alias, item_id, path=None, config=None) -> dict
@@ -376,6 +380,16 @@ pull_remote_main_image_urls(account_or_alias, item_id, path=None, config=None) -
download_remote_image(url, max_bytes=..., timeout=(connect, read)) -> RemoteImage
load_thumbnail(url, key=None, max_size=220) -> ThumbnailResult
download_original_asset(asset_id, path=None, config=None) -> ImageStudioAsset
import_original_files(project_id, file_paths, path=None, config=None) -> dict
import_original_bytes(project_id, content, filename_hint="clipboard.png", ...) -> ImageStudioAsset
trash_generated_asset(asset_id, path=None, config=None) -> dict
restore_trashed_asset(record, path=None, config=None) -> ImageStudioAsset
# app/product_suite.py
normalize_suite_settings(value=None) -> dict
suite_total_count(settings, image_count) -> int
build_suite_prompt(base_prompt, settings, category, item_id, source_index=1) -> str
build_job_specs(source_assets, base_prompt, settings, item_id) -> list[dict]
# app/image_studio_generation.py
generate_image_jobs(project_id, source_asset_id, prompt, count, job_type="main", aspect_ratio="1:1", ...) -> dict
@@ -387,10 +401,11 @@ export_project_selection(project_id, parent_dir, existing_mode="fail", path=None
要点:
- 项目唯一键为账号别名 + 商品 ID;图片文件默认在 `data/images/pool/<slug>/<item_id>/` 下分 `originals/generated/exports`。
- 项目唯一键为账号别名 + 商品 ID;图片文件默认在 `data/images/pool/<slug>/<item_id>/` 下分 `originals/generated/exports`,删除生成图进入项目内 `.trash` 并可撤销。
- `image_studio_projects.suite_settings_json` 保存平台/国家/语言/比例/逐图主图/分类数量;`draft_prompt` 保存商品卖点。有效原图上限16张,missing 历史不占名额。
- 拉取蝦皮原主图只读:复用 `editor.open_product(..., bring_to_front=False)` 和 `editor.read_product_image_urls()`,不上传、不拖拽、不点击更新。
- 原图下载走 `image_studio_images` 的公网 URL、大小、Content-Type、重定向和 PIL 解码校验;只在用户单击时落盘。
- cmhub 托管生图每张都是独立 job:保存 `task_key/task_id/status/call_id/points_cost/points_balance`;已有 `task_id` 时只 poll/download,不重复 submit。AI工场生成区只展示托管档位、生图别名、扣点和余额,不展示 Provider URL、OpenAI Key 或上游接口路径。
- cmhub 托管生图每张都是独立 job:保存 `task_key/task_id/status/call_id/points_cost/points_balance`;已有 `task_id` 时只 poll/download,不重复 submit。商品套图把平台/国家/语言/比例等上下文写入每个 job prompt,并把比例实参传到 cmhub;界面不展示 Provider URL、OpenAI Key 或上游接口路径。
- `include_failed_downloads=True` 允许 failed 但已有 `task_id`、无输出 asset 的任务继续查询,用于下载失败或本地保存失败恢复。
- 终选顺序由 `replace_selections()` 事务替换,主图/详情图同类别去重、跨类别可复用。
- 导出只写 JPEG 图片文件,透明图铺白底;商品目录已存在时只能覆盖受管命名文件或新建带时间目录,不合并、不递归清空。
@@ -406,7 +421,8 @@ class CollectTab(QWidget) # ① 导入采集:导入 Excel +
class GenerateTab(QWidget) # ② AI生成:提示词管理 + 筛选任务 + 生成封面图片成本开关 + 开始/停止生成 + 新旧封面预览 + AI生成运行日志
class ApplyTab(QWidget) # ③ 更新蝦皮:筛选已生成任务 + 检查本轮更新 + 缺失内容校验 + 确认后分批真实更新 + 运行日志
class SettingsTab(QWidget) # ⑤ 设置:cmhub 网关配置 + 响应式三列布局 + 角色/生成参数/路径端口 + 蝦皮更新安全 + 未保存状态追踪
class ImageStudioTab(QWidget) # ⑥ AI工场:项目、原图、照片池、终选、cmhub托管生成、导出
class ProductSuiteTab(QWidget) # ⑥ 商品套图:多任务、原图、结构配置、AI帮写、cmhub生成、历史结果
class ImageStudioTab(QWidget) # 旧AI工场兼容实现;主窗口不再创建
class CollectWorker(BaseWorker) # ① 后台采集:账号就绪预检 -> editor.collect -> db.set_collected/mark_skipped/mark_failed
class GenerateWorker(BaseWorker) # ② 后台生成:ai.generate_batch -> db.set_generated/set_generated_cover/mark_failed + 进度
class ApplyWorker(BaseWorker) # ③ 后台更新:账号就绪预检 -> 检查或按批调用 editor.apply_task(...) -> db.set_applied/mark_skipped
@@ -417,6 +433,9 @@ class ImageStudioDownloadOriginalWorker(BaseWorker)# ⑥ 后台下载远程原
class ImageStudioGenerateJobsWorker(BaseWorker) # ⑥ 后台提交/查询/下载 cmhub 生图 job
class ImageStudioResumeJobsWorker(BaseWorker) # ⑥ 后台恢复已有 task_id 的生图 job
class ImageStudioExportWorker(BaseWorker) # ⑥ 后台导出终选 JPEG
class ProductSuiteImportImagesWorker(BaseWorker) # ⑥ 后台校验并复制本地/剪贴板商品原图
class ProductSuiteGenerateWorker(BaseWorker) # ⑥ 按套图job规划提交/查询/下载
class ProductSuiteAiWriteWorker(BaseWorker) # ⑥ 后台生成商品卖点与画面要求
class TaskTableModel(QAbstractTableModel) # 任务表格模型:账号/别名/商品ID/阶段;未匹配别名显示“略过”
class GenerateTaskTableModel(QAbstractTableModel) # ② 任务表格模型:店铺/商品ID/旧标题/新标题/状态;generated/未提交/非运行中新标题可本地编辑
class ApplyTaskTableModel(QAbstractTableModel) # ③ 任务表格模型:店铺/商品ID/新标题/新封面/阶段/结果;保持只读,重置更新状态走右键菜单
@@ -428,7 +447,7 @@ TAB_STYLE: str # 顶层 Tab 栏防误点样式:
T-523 后 GUI 已从旧 `app/gui.py` 拆为 `app/gui/` 包:`__init__.py` 负责旧导入路径兼容与 `main()`;`main_window.py` 放 `MainWindow`;`models.py` 放 3 个 TableModel;`widgets.py` 放色板、空状态卡、批次总览和日志 helper;`workers.py` 放具体 GUI worker;`tabs/` 下按 ①~⑥ 拆分各 Tab。对外仍保留 `from app import gui`、`from app.gui import MainWindow/CollectTab/GenerateWorker/...`。
`MainWindow` 已实现六 Tab、① 导入采集任务列表、② AI生成布局/提示词/开始生成/停止/封面对照预览、③ 更新蝦皮筛选列表与检查/确认后分批真实更新、④ 账号管理、⑤ cmhub 设置、⑥ AI工场图片候选生成。缺 PySide6 时 `main()` 返回 1 并输出明确提示。
`MainWindow` 已实现六 Tab、① 导入采集任务列表、② AI生成布局/提示词/开始生成/停止/封面对照预览、③ 更新蝦皮筛选列表与检查/确认后分批真实更新、④ 账号管理、⑤ cmhub 设置、⑥ 商品套图生成。缺 PySide6 时 `main()` 返回 1 并输出明确提示。
主 Tab 栏必须在 `MainWindow` 初始化时应用 `TAB_STYLE`:6 个 Tab 不使用 Qt 默认紧凑宽度,需保证点击区域稳定、间距清晰、当前 Tab 高亮明显。该样式属于全局导航基础,不归后续业务 Tab 任务重复实现。
+28 -34
View File
@@ -1,11 +1,11 @@
# 界面与流程结构
> 桌面工具,无前端路由。当前正式界面用 **5 Tab GUI(PySide6 `QMainWindow` + `QTabWidget`)+ 流水线** 约定界面职责与导航;AI工场代码与数据保留,但主界面入口暂时隐藏。
> 桌面工具,无前端路由。当前正式界面用 **6 Tab GUI(PySide6 `QMainWindow` + `QTabWidget`)+ 流水线** 约定界面职责与导航;⑥商品套图已替换旧 AI工场入口。
## Tab 顺序与职责(工作流优先)
```
① 导入采集 │ ② AI生成 │ ③ 更新蝦皮 │ ④ 账号管理 │ ⑤ 设置
① 导入采集 │ ② AI生成 │ ③ 更新蝦皮 │ ④ 账号管理 │ ⑤ 设置 │ ⑥ 商品套图
```
| Tab | 职责 | 风险 |
@@ -15,18 +15,19 @@
| ③ 更新蝦皮 | 对**已生成**任务点击「开始更新」后弹窗确认;确认后打开编辑页换标题+封面并逐条点「更新」提交;结果回写 Excel | **写线上,高** |
| ④ 账号管理 | Shopee 账号(账号名/别名/数据目录/端口/密码本地明文仅参考/登录状态);启动登录、检测登录、生成快捷方式;启动登录必须复用已打开的同账号 Chrome,避免重复开窗口;检测登录遇到 `accounts.shopee.tw/seller/login` 必须显示未登录 | 中 |
| ⑤ 设置 | cmhub 网关/API Key、生文/生图别名、托管档位提示、生成参数、Chrome 路径、默认端口、蝦皮更新执行参数等 | — |
| ⑥ 商品套图 | 按账号+商品ID管理本地图片项目;拉取/添加商品原图,按套图分类异步生图,查看历史与重试 | 本地生成,中 |
⑥ AI工场原有代码、SQLite项目与图片资产继续保留,但当前不加入主窗口Tab栏;后续重新开放入口需另立任务。隐藏入口不会删除或迁移用户已有AI工场数据。
旧 `ImageStudioTab` 与 `image_studio_*` SQLite/图片资产服务继续保留作内部兼容;主窗口只创建 `ProductSuiteTab`,不会并列暴露两套第六 Tab,也不会删除或迁移用户既有项目数据。
任务的**阶段状态**贯穿各 Tab:`imported → collected → generated → applied`(或 `failed/skipped`)。② 不设逐条人工确认阶段;③ 无常驻提交开关,点击「开始更新」后必须弹窗确认当前筛选范围、任务数量和线上提交风险。各 Tab 聚焦各自阶段的列与按钮,但操作同一批任务(同一 batch)。
## 启动强制升级门禁
创建五个业务Tab之前先请求版本接口。服务端明确要求强制升级时,不创建 `MainWindow`,而是显示「必须升级」模态进度窗口:用户点击「立即升级」后可看到下载、校验、准备新版和重启阶段,以及百分比和字节数;运行中可「取消并退出」,失败后可重试。校验完成后软件启动安装目录外的独立更新器并退出,更新器替换程序后自动重启新版。版本接口完全不可达或非法时仍失败放行;一旦已明确强制,元数据缺失或后续失败都不允许进入旧版主界面。
创建六个业务Tab之前先请求版本接口。服务端明确要求强制升级时,不创建 `MainWindow`,而是显示「必须升级」模态进度窗口:用户点击「立即升级」后可看到下载、校验、准备新版和重启阶段,以及百分比和字节数;运行中可「取消并退出」,失败后可重试。校验完成后软件启动安装目录外的独立更新器并退出,更新器替换程序后自动重启新版。版本接口完全不可达或非法时仍失败放行;一旦已明确强制,元数据缺失或后续失败都不允许进入旧版主界面。
## 全局 Tab 栏可用性
当前 5 个主 Tab 是高频导航入口,不能使用 Qt 默认的紧凑宽度。`MainWindow` 必须为 `QTabWidget/QTabBar` 设置基础样式:
当前 6 个主 Tab 是高频导航入口,不能使用 Qt 默认的紧凑宽度。`MainWindow` 必须为 `QTabWidget/QTabBar` 设置基础样式:
- 每个 Tab 设置稳定最小宽度和足够左右 padding,避免文字贴边或窄到误点。
- Tab 之间保留明显间距,当前 Tab 有清晰背景/边框高亮。
@@ -179,37 +180,30 @@
- ③「更新内容」默认只更新标题,每批最大更新条数默认 1,同时更新蝦皮账号默认 1。
- ③ 点击「开始更新」会先按「更新内容」校验缺失内容,再弹确认框。
## ⑥ AI工场(入口暂时隐藏)
## ⑥ 商品套图
```
┌ AI工场 ──────────────────────────────────────────────────────┐
│ 左轨:账号[▼] 商品ID[____] [打开项目] [拉取主图] [打开项目文件夹] │
│ 项目列表:账号 / 商品ID / 更新时间 │
│ 中区:蝦皮原主图(单击下载进池,双击预览) │
│ 照片池:原图/主图/详情图角标、比例、排队/生成/失败状态 │
│ 右侧:模板[▼] [新建][重命名][保存][删除] │
│ [完整提示词输入框] │
│ 类型[主图▼] 数量[4] 比例[1:1▼] cmhub扣点/余额提示 │
│ [开始生成][停止] 进度条 运行日志 │
│ 底部:主图终选 / 详情图终选(拖入、插入、重排、移出) [导出终选] │
└───────────────────────────────────────────────────────────────┘
┌ 套图任务1 │ 套图任务2 │ + ───────────────────────────────────┐
│ 账号[▼] 商品ID[____________] [拉取蝦皮主图] │
├ 左侧配置(滚动)────────────┬ 右侧生成结果 ─────────────────────┤
│ 商品原图:主图/参考1..5/添加 │ 共N张·成功M张 [历史生成][打开文件夹] │
│ 平台/国家/语言/比例 │ [结果卡][结果卡][失败卡·重试] │
│ 每张上传图分别作为主图生成 │ │
│ 商品卖点与要求 [AI帮写/取消] │ │
│ 白底图/场景图/卖点图/自定义类 │ 进度条 套图X/Y(秒)·失败N │
│ [生成套图(N) ⇄ 停止生成] │ │
└─────────────────────────────┴──────────────────────────────────┘
```
- 项目以 `账号别名 + 商品ID` 唯一;打开项目只创建/切换本地项目,不修改蝦皮。
- 「拉取主图」复用已验证只读 CDP:后台打开商品详情页读取主图 URL,写入 `image_studio_assets(kind=original)`;不下载图片、不改标题/封面、不点击更新。
- 原主图抽屉单击时才下载对应远程原图到项目 `originals/` 并设为源图;双击远程原图会先下载再打开大图预览。
- 照片池只展示本地文件存在且可解码使用的原图、生成主图和生成详情图;单击可用图片设为源图,双击打开大图;右键移除只删除未被任务或终选引用的照片池记录,不删除本地图片文件。照片池附近的「生成任务」区单独展示尚未产出可用图片的等待提交、已提交、生成中、生成失败、任务过期和已停止任务,并显示脱敏后的中文原因摘要、恢复方式和已有计费信息;任务卡不能设为源图或拖入终选。
- 右侧只有一个完整提示词框;模板目录固定为 `data/prompts/image_studio/`,与②标题/封面模板隔离。界面不显示“主提示词 / 每张动作词”。
- 生图固定走 cmhub 托管模型,使用⑤设置里的 cmhub Base URL/API Key/生图别名和图片并发;界面显示当前托管档位(默认/高质量/省点)、生图别名、扣点、余额、进度、失败,不展示自定义 Provider、API Key、生成来源选择或“导入本地图片”入口。
- 「继续查询任务」只恢复当前项目中已保存 `task_id` 且标记为可恢复的 cmhub 生图任务,包括已提交、生成中及下载/保存中断后可继续处理的任务;恢复时只 poll/download 原任务,不再次 submit,不重复扣点。上游已终态失败、任务过期或尚未取得 `task_id` 即停止的任务会明确提示需要重新生成,可能产生新的计费;已有 `task_id` 后被用户停止的任务仍可继续查询。
- 底部终选盘分为主图和详情图两列;照片池中已下载/已生成且本地文件可用的图片可拖入终选,落到已有位置时按插入顺延,同一类别内同一照片只能出现一次,主图和详情图之间允许复用同一照片。
- 终选列表内可拖动重排,Delete 或右键「移出终选」只移出终选,不删除照片池资产或本地文件;拖放/移出失败时刷新回 SQLite 中的持久化顺序。
- 主图推荐 1:1;比例不匹配只用黄色轻提示和 tooltip 提醒,不硬拦。文件缺失或尚未下载的照片不能拖入终选。
- 拉主图、下载原图、生图 submit/poll/download 均通过 worker 执行,主线程只刷新 UI。拉主图、导出等互斥 worker 运行时禁用项目切换和工作区写操作;生图或继续查询任务运行时仍只允许一个耗时 worker,但当前项目的照片池浏览/源图选择、主图和详情图终选拖放、提示词模板编辑以及下一轮类型/数量/比例设置保持可用。这些修改只作用于下一轮,当前已提交任务的参数不变;此时仍禁用项目切换、原图区下载、拉主图、开始生成、继续查询、导出和删除项目,停止为协作式停止。
- 「导出终选」可在主图/详情图未满目标数量时导出当前终选;主图和详情图总数为 0 时阻断。用户选择导出父目录后,程序在其下创建商品 ID 子目录,按终选顺序转码为 `商品ID_主图_1.jpg`、`商品ID_详情图_1.jpg`,透明图铺白底输出真正 JPEG。
- 商品目录已存在时只提供三选:覆盖本软件导出的图片(仅删除匹配当前商品命名规则的旧主图/详情图,保留用户其它文件)、新建带时间目录、取消;不提供合并,也不递归清空用户目录。
- 导出前会预检所有终选源文件和图片解码,先写 staging,转码失败不创建商品目录、不留下半套新图;成功后中文提示实际目录和主图/详情图数量,并提供打开目录。
- 本小节当前覆盖 T-591/T-593:主界面、终选排序和本地导出已接入;不自动上传或修改蝦皮,BYOK/自定义 Provider 仍后置。
- 每个顶部任务标签持有独立账号、商品ID、设置、原图、当前 job 集合和 worker;任务可并行生成。切换任务不停止后台操作;关闭运行中任务先确认并协作式取消,线程引用保留到真正结束,避免 `QThread: Destroyed while thread is still running`。
- 项目仍以 `账号别名 + 商品ID` 唯一,复用 `image_studio_projects/assets/jobs`。`suite_settings_json` 保存平台、国家地区、语言、比例、逐图主图模式和分类数量;卖点文本继续使用 `draft_prompt`。
- 商品原图最多16张。前6个槽位固定显示主图与参考1~5;支持文件选择、外部拖入、剪贴板粘贴和列表内排序。历史失效远程图不占有效名额;第1张是主参考图。
- 「拉取蝦皮主图」复用只读 CDP,读取 URL 后由最多2个下载 worker 后台落盘;不改标题/封面、不拖拽、不点击更新。拉取、下载期间其余界面和其他任务仍可操作。
- 套图只有一个图片类型,不再展示详情图、终选盘或模板 CRUD。默认分类为白底图1、场景图2、卖点图2;自定义分类名称非空、无空格、最多10字且不可重名。逐图主图开启后,白底图只生成一次,其余分类按每张有效原图展开。
- 平台、国家地区、语言和比例都写进每个 job 的完整提示词;比例还透传到 cmhub 生图请求,不是装饰字段。生成仍走 `image_studio_generation.run_jobs()` 的 submit → poll → download 管线。
- 生成按钮按当前总数显示并在运行时切换为停止。结果区显示本轮或历史 job;成功图可预览、复制路径、打开目录、重新生成、移入项目废纸篓并撤销,失败卡显示脱敏中文摘要与重试入口。
- AI帮写和生图按任务独立运行。AI帮写期间若用户改过卖点,返回后必须确认才覆盖;全部用户可见错误隐藏 URL/接口路径和敏感信息。
- ⑥只管理本地图片资产,不自动上传或修改蝦皮;③线上更新边界不受影响。旧 `ImageStudioTab` 留作代码兼容但不再作为主窗口入口。
## 流程导航
@@ -239,12 +233,12 @@
| `ApplyTab(QWidget)` | ③ | 已生成任务筛选 +「更新内容」下拉 + 缺失内容阻断 +「检查本轮更新」+ 分批开始更新确认 + 检查/真实更新运行日志 + 结果回写与结束汇总 |
| `AccountsTab(QWidget)` | ④ | 账号增删改、启动登录、检测登录、生成快捷方式;登录检测把 Shopee accounts 登录页判为未登录 |
| `SettingsTab(QWidget)` | ⑤ | cmhub 网关配置 + 响应式三列设置表单 + 生成参数 + Chrome/端口配置 + 蝦皮更新执行;数据路径字段隐藏但保留配置兼容 |
| `ImageStudioTab(QWidget)` | ⑥ | AI工场项目列表、只读拉蝦皮主图、原图下载进池、照片池、大图预览、完整提示词模板 CRUD、cmhub 托管多图生成控制 |
| `ProductSuiteTab(QWidget)` | ⑥ | 商品套图多任务、账号+商品ID上下文、原图导入/排序、套图分类、AI帮写、cmhub 异步生成、结果历史与删除撤销 |
| `TaskTableModel(QAbstractTableModel)` | ①②③ | 任务表格数据模型,供 `QTableView` 使用 |
| `BaseWorker(QObject)` | 后台 | 定义 `progress/log/row_updated/failed/finished/cancelled` signals |
| `ApplyWorker(BaseWorker)` | ③ | 账号就绪预检、检查本轮更新、按每批最大条数分批、按账号并行或串行调用 `editor.apply_task(...)`、逐条 `set_applied()`,失败继续,写运行日志 |
| `AIModelTestWorker(BaseWorker)` | ⑤ | 后台调用 `appconfig.test_ai_model()` 测试模型连接 |
| `WriteBackWorker(BaseWorker)` | ①③ | ①回写旧字段;③回写新标题/新封面/更新状态 |
| `ImageStudioPullImagesWorker / ImageStudioDownloadOriginalWorker / ImageStudioGenerateJobsWorker` | ⑥ | 后台执行只读拉主图、远程原图下载、cmhub 托管生图 submit/poll/download;不直接操作 QWidget |
| `ImageStudioPullImagesWorker / ImageStudioDownloadOriginalWorker / ProductSuiteImportImagesWorker / ProductSuiteGenerateWorker / ProductSuiteAiWriteWorker` | ⑥ | 后台执行只读拉主图、远程原图下载、本地图片校验复制、cmhub 套图生成与AI帮写;不直接操作 QWidget |
> 采集、生成、更新都是耗时操作,使用 `QObject` worker + `QThread`。Worker 不直接操作 QWidget,只通过 signal 通知主线程刷新 UI。
+12 -5
View File
@@ -3,7 +3,7 @@ id: T-622
title: ⑥用「商品套图」模块替换现有 AI工场(迁移电商图生成器套餐模块)
phase: 7
deps: [T-564]
status: TODO
status: DONE
created: 2026-07-13
---
@@ -42,11 +42,11 @@ UI 已定稿:`docs/ui/tab6-suite-package-v1.svg`(本仓库家风:`#2b3a55`
- 结果网格:每张卡片显示图片/生成中骨架/失败(带「重试」);右键单图可 预览 / 复制路径 / 重新生成。
- 底部一行(与左侧「生成套图」按钮同高):进度条 + `套图 X/Y(秒)· 失败 N` 汇总。
## 待决 / 选型(实现前确认)
## 实现决策
1. **技术栈**:推荐**原生 PySide6 重写 UI**、复用现有 `image_studio_*` 后端;已排除内嵌 WebView(避免 QtWebEngine 打包体积与依赖)。若坚持搬 WebView 需另评估。
2. 平台/国家/语言下拉的选中值如何进入生成上下文(提示词模板变量 or 生成请求参数),需与 `image_studio_generation` 对齐。
3. 效果图当前为「滚动区裁剪」呈现;如需一屏展示全部配置项,属实现期布局细节。
1. **技术栈**:采用原生 PySide6 `ProductSuiteTab`,复用现有 `image_studio_*` 后端,不引入 QtWebEngine;旧 `ImageStudioTab` 仅保留内部兼容。
2. **生成上下文**:平台/国家/语言/比例/分类/商品ID/参考图序号统一由 `product_suite.build_suite_prompt()` 写入每个 job prompt;比例另透传到 cmhub `aspect_ratio` 请求字段。
3. **布局**:配置区可滚动,生成按钮固定;缩略图和卖点框设稳定高度,结构分类用常驻 chip + 单项展开计数,基准窗口首屏可见。
## 验收要点
@@ -102,3 +102,10 @@ UI 已定稿:`docs/ui/tab6-suite-package-v1.svg`(本仓库家风:`#2b3a55`
- 不动 ①~⑤ 既有标签与流程。
- 不引入 `QtWebEngine`(采纳原生重写方案时)。
- 安全红线:不写真实凭证到代码/文档/日志;`config.json`、`cmshopee.db`、`images/` 等不提交;不绕过 Shopee 风控;生成/导出等本地操作不触碰 ③ 的线上提交边界。
## 执行记录
- 2026-07-14:完成第六 Tab 从旧 AI工场入口切换为原生「商品套图」。新增多任务状态、账号+商品ID上下文、16张商品原图导入/拖放/粘贴/排序/后台拉取、结构分类与AI帮写、生成/停止、结果历史、重试、预览、废纸篓删除与撤销;旧 `ImageStudioTab` 保留但主窗口不再创建。
- 2026-07-14:扩展 `image_studio_projects.suite_settings_json` 及原位迁移,新增套图纯逻辑、项目 job 查询/原图排序、本地图片原子导入、生成图废纸篓;cmhub 比例从 UI 真实透传,并以进程级闸门限制多个套图任务合计最多5个在途 job。停止信号在下载前后生效,停止后的临时图片不入资产库。
- 2026-07-14:同步 AI 开发入口、需求、技术栈、架构、API、routes、文档导航与 UI 索引;新增领域、worker、GUI、迁移、图片边界、比例透传和停止清理测试。
- 验证:在只包含 T-622 暂存文件的干净 worktree 中运行 `py -3.10 -m unittest discover -s tests`,459 项全部通过;`python -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check` 全部通过。原工作区另有任务开始前即存在的默认封面提示词改名(删除 `papa1.txt`、新增 `默认.txt`),会使3个仍断言 `papa1` 的旧测试失败,该改动未回退、未纳入本任务提交。
+2 -2
View File
@@ -4,7 +4,7 @@
| 文件 | 内容 |
| --- | --- |
| [overview-pipeline.svg](overview-pipeline.svg) | 主流水线总览(stage:imported→collected→generated→applied;⑥ AI工场为图片候选工作区) |
| [overview-pipeline.svg](overview-pipeline.svg) | 主流水线总览(stage:imported→collected→generated→applied;图中旧⑥AI工场入口已由商品套图替换) |
| [tab1-import-collect.svg](tab1-import-collect.svg) | ① 导入采集:导入 Excel、任务列表、采集旧标题/旧封面、回写、日志 |
| [tab2-ai-generate.svg](tab2-ai-generate.svg) | ② AI生成:提示词、生成、新旧封面对照、重置图片后批量补生成 |
| [tab2-ai-generate-T584.svg](tab2-ai-generate-T584.svg) | ② AI生成(T-584 提案):标题提示词加模板控件行、封面控件压一行、插入类按钮下移、标题输入框增高 |
@@ -14,6 +14,6 @@
| [tab6-image-studio.svg](tab6-image-studio.svg) | ⑥ 图片精修(提案·初版):商品列表、原主图九宫、照片池、生成控制、终选排序 |
| [tab6-image-studio-v2.svg](tab6-image-studio-v2.svg) | ⑥ 图片精修(提案·重排):细图标轨、照片池放大、提示词中心整列、全宽出片盘 |
| [tab6-ai-studio-v3.svg](tab6-ai-studio-v3.svg) | ⑥ AI工场(需求定稿·v3):合并提示词、完整模板 CRUD、异步任务照片池、部分导出与终选排序 |
| [tab6-suite-package-v1.svg](tab6-suite-package-v1.svg) | ⑥ 商品套图(迁移提案·v1):从「虾皮圈电商图生成器」商品套餐模块迁移,替换 AI工场;已去掉「详情图」(与套图合并为单一图片类型)。采用本仓库既有模块风格(#2b3a55 头栏 / #2f6fed 蓝 / 微软雅黑)。多任务标签页、左侧配置面板(商品原图上传、生成设置、AI 帮写、套图结构配置:白底/场景/模特/细节/卖点+自定义分类计数)、右侧生成结果网格、底部进度与生成/停止 |
| [tab6-suite-package-v1.svg](tab6-suite-package-v1.svg) | ⑥ 商品套图(T-622 已实现):从「虾皮圈电商图生成器」商品套餐交互迁移并替换主界面 AI工场入口;单一套图类型,多任务标签,商品原图,平台/国家/语言/比例,AI 帮写,白底/场景/卖点及自定义分类,结果历史、重试、删除撤销与生成进度 |
> 仅为线框效果图,最终样式以实现为准。带 `-T584`/`-v2` 等后缀的为**改版提案**,未定稿。界面职责与流程见 [../routes.md](../routes.md)。