Files
cmshoppe/docs/routes.md
T

36 KiB
Raw Blame History

界面与流程结构

桌面工具,无前端路由。用 6 Tab GUI(PySide6 QMainWindow + QTabWidget)+ 流水线 约定界面职责与导航。

Tab 顺序与职责(工作流优先)

① 导入采集 │ ② AI生成 │ ③ 更新蝦皮 │ ④ 账号管理 │ ⑤ 设置 │ ⑥ AI工场
Tab 职责 风险
① 导入采集 导入多个 Excel;任务列表;采集商品当前的旧标题/旧封面(只读),封面图下载本地;回写 Excel 旧字段 只读,低
② AI生成 左侧标题/封面提示词;右侧按批次/店铺/商品ID/状态筛选任务列表;AI 生成新标题,并按本轮开关可选生成新封面;表格拆分显示「标题状态 / 图片状态」;已生成任务可本地微调新标题;双击看新旧封面 不触线上,中
③ 更新蝦皮 对已生成任务点击「开始更新」后弹窗确认;确认后打开编辑页换标题+封面并逐条点「更新」提交;结果回写 Excel 写线上,高
④ 账号管理 Shopee 账号(账号名/别名/数据目录/端口/密码本地明文仅参考/登录状态);启动登录、检测登录、生成快捷方式;启动登录必须复用已打开的同账号 Chrome,避免重复开窗口;检测登录遇到 accounts.shopee.tw/seller/login 必须显示未登录 中
⑤ 设置 cmhub 网关/API Key、生文/生图别名、托管档位提示、生成参数、Chrome 路径、默认端口、蝦皮更新执行参数等 —
⑥ AI工场 按账号+商品建立图片项目;只读拉取蝦皮原主图 URL;单击原图下载进入照片池;选择源图后用 cmhub 托管模型生成多张主图/详情图候选;管理 AI工场完整提示词模板;终选拖放与导出由后续任务接入 不触线上,中

任务的阶段状态贯穿各 Tab:imported → collected → generated → applied(或 failed/skipped)。② 不设逐条人工确认阶段;③ 无常驻提交开关,点击「开始更新」后必须弹窗确认当前筛选范围、任务数量和线上提交风险。各 Tab 聚焦各自阶段的列与按钮,但操作同一批任务(同一 batch)。

全局 Tab 栏可用性

6 个主 Tab 是高频导航入口,不能使用 Qt 默认的紧凑宽度。MainWindow 必须为 QTabWidget/QTabBar 设置基础样式:

  • 每个 Tab 设置稳定最小宽度和足够左右 padding,避免文字贴边或窄到误点。
  • Tab 之间保留明显间距,当前 Tab 有清晰背景/边框高亮。
  • 样式只影响顶层主 Tab,不改变各业务表格、弹窗和后续 Tab 内部布局。
  • 新增业务 Tab 内容时不得缩小主 Tab 栏点击区域。

全局状态栏反馈

左下角状态栏用于低打扰反馈,T-543 已统一由 MainWindow.show_status(message, level) 控制语义色:

  • 普通/导航/就绪:muted 或系统默认,例如「就绪」「当前:② AI生成」。
  • 信息/进行中:info,例如「正在生成标题」「检测登录中」。
  • 成功:success,例如「设置已保存」「已回写 Excel」。
  • 警告/需用户处理:warning,例如「当前筛选结果没有待生成任务」「请先到①完成旧数据采集」「请先配置 cmhub」。
  • 错误/阻断:danger,例如「更新失败」「账号未登录」「数据目录不可写」「点数不足」。

状态栏只改变文字色,不做大面积背景;它不能替代弹窗、空状态、运行日志、按钮禁用和任务状态列。普通提示必须重置状态栏颜色,避免上一条错误或成功颜色残留。

首次使用引导保护

  • ① 导入采集 与 ③ 更新蝦皮 都依赖账号已配置且已登录(在 ④ 账号管理)。
  • ① 点击「采集旧标题/旧封面」后,会为本轮匹配到账号的店铺自动确保 Chrome 就绪:已打开则复用,未打开才启动;随后逐账号检测蝦皮登录态。明确进入登录页的账号任务整组略过并在结束汇总中提示去④人工登录;NO_SESSION_COOKIE 或 CDP 短暂读取异常只视为登录状态暂不确定,重试后仍不确定也继续尝试采集,避免误判批量略过。
  • ③ 点击「开始更新」后必须检查当前筛选结果涉及的账号;只要有账号 Chrome 未启动、CDP 端口不可访问或 Shopee 未登录,就弹窗列出账号并中止本轮更新,不创建真实更新 worker,不提交任何商品。
  • 可以提供「打开账号管理」或「启动登录」入口辅助用户处理当前账号;③ 不要无提示批量启动所有账号 Chrome,避免在提交线上前开错账号或启动过多浏览器进程。用户主动点击④「启动登录」时也必须先检查该账号 CDP 端口,已打开则复用现有 Chrome 并打开/激活卖家中心 tab,不重复 Popen 新窗口。
  • 老用户账号已就绪则无感。

① 导入采集

┌ 导入采集 ─────────────────────────────────────────────────────┐
│ [导入 Excel…] [移除] [清空]                                    │
│ ▸ 3 文件 · 128 行 · 有效125/无效3 · 匹配123 · 未匹配5⚠         │ ← 导入汇总栏
│   匹配明细:女装店60 · my主店40 · 饰品店23                      │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 账号名  别名     商品ID       阶段        旧标题  旧封面    │ │
│ │ 主店A  女装店  51100639510  待采集       —      —          │ │
│ └───────────────────────────────────────────────────────────┘ │
│ [▶ 采集旧标题/旧封面]  [■停止]      [回写旧数据到 Excel]       │
│ 日志:逐条 文件→匹配账号、采集步骤、失败原因                     │
└───────────────────────────────────────────────────────────────┘
  • 导入:openpyxl 解析输入列(账号名/别名/商品id)入 SQLite。

  • 导入汇总栏(导入后即时刷新,跑采集前的校验关口):显示 文件数、解析行数(原始数据量)、有效/无效行、匹配账号行数(按账号细分)、未匹配行数。未匹配/无效数字标红可点,点击在列表筛出便于定位纠错。

  • 采集:点击后先为本轮匹配账号确保 Chrome 就绪(已开复用、未开启动),再检测登录;明确未登录账号的任务整组略过并汇总提示。NO_SESSION_COOKIE、登录检测超时或 CDP 短暂异常会重试,连续不确定时不批量略过,继续打开商品页由真实页面结果决定成功/失败。登录账号用对应 Chrome 只读打开商品页,读旧标题、下载旧封面到 data/images/<batch_id>/<slug>/<task_id>_<item_id>_old.jpg,写 old_title/old_cover_path,stage=collected。采集不主动把商品页切到前台;程序自动新建商品页 tab 时尽量后台创建,采集结束后自动关闭;若复用用户原本打开的 tab,则不关闭。采集结束不关闭账号 Chrome,用户可自行关闭。

  • 若商品 ID 已失效、无权限或店铺不匹配,Shopee 可能只弹出短暂错误 toast;采集失败时界面日志应显示捕获到的 toast 文案,并把 toast HTML/URL 写入本地诊断日志,避免用户手动抢复制。只有明确捕获商品失效/商品不存在/无权限类 toast 时,①列表“阶段”列显示“商品失效”;其他商品页打开失败仍显示“失败”。如果失败发生在 open_product() 内部,本轮自动新建的商品 tab 必须关闭,复用用户已有 tab 不关闭。

  • 回写:采集完成后自动把旧标题/旧封面路径批量回写原 Excel;保留「回写旧数据到 Excel」作为手动重试入口(原文件被锁→提示关闭后重试/另存)。

  • 别名未匹配账号 → 该行 skipped 并记原因,不为未匹配别名启动 Chrome。账号预检或采集中途明确进入登录页 → 该账号剩余任务整组 skipped 并记原因,结束汇总列出需补登录的账号;NO_SESSION_COOKIE 等不确定检测结果只写日志和诊断信息,不触发整组 skipped。T-207 接入后,① 采集会像③更新一样写 run_logs/run_log_events,并把完整脱敏 traceback 写入本地 data/logs/,用于定位失败卡在哪个步骤。

  • 「删除批次」位于①批次筛选旁,只能对当前选中的具体批次执行,不能在“全部批次”下执行;运行中禁用。删除是软删除:写本地批次删除标记,不物理删除 DB 记录,不删除原 Excel,不回滚蝦皮。删除后该批次不再出现在①/②/③任何批次下拉、任务列表、筛选、采集、生成、更新、回写入口中。确认框必须显示任务数、已上线任务数,并提示软删除只隐藏本地批次、不会回滚线上修改。

② AI生成

左右布局:左侧约 1/4 放提示词,右侧放筛选 + 任务列表。

┌ AI生成 ───────────────────────────────────────────────────────┐
│ 左侧提示词区:                                                     │
│   标题提示词                                                       │
│   模板[▼] [新建] [保存模板] [模板操作▼]                            │
│   [标题提示词输入框,较旧版增高]                                    │
│   [插入旧标题] [保存标题提示词]                                     │
│   封面提示词                                                       │
│   模板[▼] [新建] [保存模板] [模板操作▼]                            │
│   [封面提示词输入框] [插入标题] [预览]                              │
│ 右侧任务区:批次/店铺/商品ID/状态筛选 + 任务表 + AI生成运行日志       │
│  生成内容[只生成标题▼]  标题30/30 ━━━━━ 生标题用时 18 秒 [▶ 开始生成][■停止][重置生成结果] │
│                              图片0/0  ━━━━━ 生图用时 0 秒                              │
└───────────────────────────────────────────────────────────────┘
  • 左侧(提示词管理,上下两块):
    • 标题提示词:模板下拉(读 data/prompts/title/*.txt)+「新建 / 保存模板 / 模板操作(另存为、重命名、删除)」+ 多行输入 + 下方工具条「插入旧标题」(插 {旧标题})/「保存标题提示词」(写 data/title_prompt.txt)。启动时自动加载 title_prompt.txt 回显,不自动用模板覆盖工作文本;用户选择模板时才把模板内容载入输入框。若标题提示词包含 {旧标题},生成前替换为当前任务旧标题且不重复追加旧标题块;不包含时保持旧行为自动追加旧标题。
    • 封面提示词:模板控件压缩到一行(读 data/prompts/cover/*.txt,新建/保存模板/另存为/重命名/删除)+ 多行输入 + 下方工具条「插入标题」(插 {新标题})/「预览」(变量替换后查看)。
    • 变量:标题提示词本阶段只支持 {旧标题};封面提示词支持 {旧标题}/{新标题}/{商品id}/{店铺},生成前按任务替换。
  • 右上:按导入批次 / 店铺 / 商品ID / 状态筛选任务;商品ID输入框按包含匹配 item_id,清空表示全部。
  • 筛选行提供「打开图片文件夹」按钮,用于只读打开本地图片目录:选中某行时打开该商品所在账号图片文件夹(优先打开已有新/旧封面文件的真实父目录,缺失时回退到规范账号目录);未选行且选择具体批次时打开该批次图片文件夹;未选行且为全部批次时打开图片根目录。目录不存在只中文提示,不自动创建目录,不修改任务状态。
  • 右下:任务列表(店铺名、商品id、旧标题、新标题、标题状态、图片状态)+ AI生成运行日志;标题/图片状态由 new_title、new_cover_path、stage/status 和失败步骤推导,帮助用户区分“标题未生成 / 图片未生成 / 标题成功但图片失败”。商品ID列按原等分宽度约 50% 显示;标题状态和图片状态列在 T-554 基础上再缩到约 33%,缩出的宽度平均给旧标题和新标题。已生成、未提交线上、非运行中的任务可双击「新标题」列本地微调,写回 tasks.new_title,清空 last_error 并回到可更新;双击其他列弹窗展示旧封面、新封面和历史候选图。
  • 底部单个「开始生成」+「停止」,并增加「生成内容」下拉:默认只生成标题,可选只生成封面或生成标题和封面;只生成封面要求任务已有新标题。标题/图片两条进度条右侧分别显示同宽用时标签(生标题用时 N 秒 / 生图用时 N 秒),运行中每秒递增,完成/停止后冻结;原图片进度条右侧的失败数和 cmhub 余额不再占用该位置。cmhub 模式会把用户设置的图片并发内部限制到最大 5,并用同样最大 5 的独立下载线程池拉取 image_url,不新增用户可见下载并发配置;运行日志显示用户设置并发和实际并发。下拉状态持久化到 config.json 的 ai.generate_mode,并继续写回旧兼容 ai.generate_cover。
  • 生成参数(标题/图片并发数、失败重试、分辨率、jpg 质量、cmhub 网关/Key/别名)在 ⑤ 设置;②只暴露本轮生成标题/封面/图文的内容模式。⑤ 不新增“下载并发”控件;cmhub 图片下载并发由程序按实际生图并发自动计算,最大 5。
  • 只生成标题时标题成功即写库并进入 generated,new_cover_path 留空;只生成封面时不调用标题生成、不覆盖已有标题;生成标题和封面时按缺失组件增量补齐。三种模式都写 run_type=generate 的 run_logs/run_log_events 和用户可读滚动日志;日志开头明确显示本轮生成内容。点击「开始生成」时先清空②界面可见日志并写入本轮开始摘要,运行中只追加本轮日志;不删除历史 run_logs/run_log_events 或本地 data/logs/。进入页面默认可显示“本轮日志会在开始运行后显示”,历史日志不自动混入当前运行界面。「停止」取消未开始项,可再次「开始生成」对剩余继续。
  • 「重置生成结果」支持选中任务或当前筛选结果,运行中禁用;确认框提供「重置标题 / 重置封面 / 重置全部」,只改本地 DB,默认不删除本地新封面文件。若范围内包含已提交线上记录,必须提示本地重置不回滚蝦皮,重生成后再更新会再次提交线上。
  • 无逐条人工审核环节;新标题默认使用 AI 输出,但允许对已生成且未提交线上的单行做本地微调。封面画廊内「重置图片」只清当前任务 new_cover_path 并归档旧图,不启动单条生图;用户翻看完后用状态筛选「待生成」并点击「开始生成」批量补封面。生成完即可进入 ③,③ 开始更新前会做批量确认。

③ 更新蝦皮

┌ 更新蝦皮 ───────────────────────────────────────────────────┐
│ 批次[本次▼] 店铺[全部▼] 商品ID[____] 状态[已生成▼] [筛选]      │
│ ⚠ 点击「开始更新」后先校验更新内容,再确认【当前筛选结果】并提交 │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 店铺  商品ID       新标题      新封面   阶段    结果       │ │
│ └───────────────────────────────────────────────────────────┘ │
│ 运行日志:检查/真实更新逐条留痕                                  │
│ 更新内容[只更新标题▼] [检查本轮更新] [▶ 开始更新] [■停止] [回写结果到 Excel] │
└───────────────────────────────────────────────────────────────┘
  • 顶部按批次 / 店铺 / 商品ID / 状态筛选(与 ①②一致);商品ID输入框按包含匹配 item_id,清空表示全部;「开始更新」作用于当前筛选结果,是一道范围控制。

    • 店铺筛选:建议逐店铺更新(每店铺需先启动其 Chrome 并登录)。
    • 状态筛选:已生成 只跑未更新的;失败 用于失败重试;已更新成功/略过 仅查看。
  • 「更新内容」下拉支持只更新标题、只更新封面、更新标题和封面;点击「检查本轮更新」或「开始更新」前先按当前模式检查 new_title / new_cover_path,缺失时中文弹窗阻断,不打开 Chrome、不改任务状态。

  • 「检查本轮更新」只读取当前筛选结果并写运行日志,不打开 Shopee、不提交、不改任务状态;弹窗/日志展示总任务数、店铺分布、当前更新内容、会更新标题/封面、略过原因、每批最大条数和预计批次数。

  • 缺失内容校验通过后,点击「开始更新」读取 ⑤ shopee_update 执行设置:普通正式更新不再以测试商品 ID 或旧真实提交开关限制当前筛选结果,允许当前筛选结果包含多个真实商品 ID;max_items_per_run 作为每批最大更新条数,当前筛选总数超过该值时不阻断,而是自动分批执行。

  • 弹窗展示本次筛选条件、更新内容、任务总数、每批最大条数、预计批次数、执行设置和“将提交线上”的风险提示;用户点「是/确认」才开始,点「否/取消」不执行。

  • 真实更新第一条商品前做账号就绪预检:按当前筛选结果汇总需要的账号;无账号、Chrome 未启动、CDP 端口不可访问、未登录或端口冲突时,弹窗列出具体账号/原因并中止本轮,不自动调用「启动登录」或静默打开 Chrome。

  • 对确认后的已生成(generated)任务按当前更新内容执行:打开编辑页换标题和/或换封面 → 点页面「更新」 → 如 Shopee 弹出“確定您要更新商品嗎?”确认框(.eds-modal__content / .eds-modal__box),则只点弹窗主按钮「更新」提交,不点「立即優化」。

  • ③每条更新任务都会把对应 Chrome 的当前商品 tab 切到前台;新建 tab 不使用后台创建,复用 tab 时也显式激活。上传封面、等待图片管理器刷新、拖到第一位和提交线上均以稳定性优先,因此批量更新期间 Chrome 会随任务切换到前台。后台态封面安全恢复逻辑仅为兼容直接调用保留,正常③批量路径不依赖它。

  • 打开编辑页失败时,如果 Shopee 弹出错误 toast(如商品 ID 不正确、商品不存在、无权限),③运行日志和任务失败原因必须显示该 toast 文案;同时把 toast HTML/URL/时间写入本地诊断日志。用户不需要手动复制瞬时 toast 的 HTML。

  • 更新封面时统一按替换第一张执行:删除第一张前必须已有该任务的本地旧封面备份(①采集得到的 old_cover_path 且文件存在);备份缺失时阻断该条更新并提示先采集/修复备份,不盲删线上图片。

  • 默认串行、单条失败继续;⑤「同时更新蝦皮账号」设为 1 时逐个账号执行,设为 2..5 时不同账号同时执行,同一账号内仍串行;真实更新前若本轮账号调试端口冲突则阻断。

  • 「检查本轮更新」只写运行日志与检查汇总,不打开 Shopee、不调用 editor.apply_task()、不写任务状态、不回写 Excel。

  • 真实更新每条立即写回 SQLite(committed/状态/error),失败不阻塞后续任务;检查和真实更新都会写 run_logs/run_log_events。点击「检查本轮更新」或「开始更新」时先清空③界面可见日志并写入本轮检查/更新开始摘要,运行中只追加本轮日志;不删除历史 run_logs/run_log_events 或本地 data/logs/,历史日志不自动混入当前运行界面。

  • 分批更新时「停止」为协作式停止:已开始的当前商品跑到安全边界并写库后停止,不再开始新商品、不进入下一批;未开始任务保持原状态,下次可继续。

  • 更新前做账号就绪预检:无账号、对应账号 Chrome 未启动、CDP 端口不可访问或未登录时整体阻断并引导去④账号管理,不进入逐条提交,也不自动打开账号 Chrome。

  • 若 ⑤ 开启“成功后关闭本次新开编辑页”,则仅关闭本轮程序自动新开且成功提交的商品页;确认后跳回我的商品列表页时,关闭前等待 2 秒;已进入编辑页后的失败任务和用户原本打开的 tab 不关闭。但如果失败发生在 open_product() 内部,程序自动新开的无效商品页 tab 要关闭,避免 Chrome 残留空错误页。

  • 「开始更新」是③的主操作按钮,视觉上必须强于其他批处理按钮。

  • 「重置更新状态」从底部批处理按钮移到任务表右键菜单/高级入口,仅作用当前选中单条,运行中禁用;保留 new_title/new_cover_path,只把本地状态退回可更新,用于重复测试上传/提交。若 committed=1,确认框必须提示线上已提交过、本地重置不回滚蝦皮、重复更新会再次提交;不得静默清除 committed 历史事实。

  • 更新完成后自动回写原 Excel:写入新标题、新封面图片路径、更新状态;原文件被锁时提示关闭后点击「回写结果到 Excel」手动重试。

  • 自动回写完成后弹窗汇总成功/失败/略过数量与 Excel 回写文件/行数。

④ 账号管理

┌ 账号管理 ─────────────────────────────────────────────────────┐
│ 账号名  别名     地区              端口  登录状态  备注        │
│ 主店A  女装店  seller.shopee.tw  9222  ●已登录                │
│ [+新增][✎编辑][🗑删除]  [▶启动并登录][🔄检测登录][⧉快捷方式] │
└───────────────────────────────────────────────────────────────┘

账号弹窗字段:账号名、别名(唯一,Excel 用它匹配)、地区域名、调试端口、密码(本地明文仅参考,保存/变更时提示,UI 打码,不自动登录)、备注;配置目录按别名生成 slug 只读显示。

⑤ 设置

  • 设置页整体布局:内容区居中,左右留白已从 T-506 初始实现缩短到约 40%;实现上用最大内容宽度 + 自适应 margin,而不是写死窗口像素。所有设置组默认响应式 3 列表单:短字段占 1 格,URL/API Key/路径等长字段跨 2 格或 3 格;窄窗口自动降为 2 列/1 列。点击「保存设置」成功后,状态栏显示“设置已保存”,并弹出轻量提示框。T-531 已完成:保存成功会清除未保存标记;保存失败时保留未保存标记并阻止离开。
  • cmhub 网关配置(T-529 后普通用户唯一 AI 入口):⑤设置页不再显示「AI 后端」label 或 direct/cmhub 下拉,直接展示 cmhub 网关 Base URL、API Key、生文别名、生图别名、连接超时、刷新别名、测试连接/查余额;Base URL 输入框旁提示“只填网关根,如 https://host”,保存/刷新前会规整掉 /api、/api/v1 或其它路径。刷新别名/测试连接使用当前输入框内容发起请求,但不自动保存 URL/API Key,成功文案需提醒“记得点保存设置持久化”。
    • T-532 需求:测试连接/查余额成功时,如果 cmhub /balance 返回账号名、用户名或邮箱等可识别信息,成功提示应显示 cmhub 账号「<账号名>」连接成功:...,让用户确认当前 API Key 属于哪个 cmhub 账号;当前接口结构兼容 { "user": "cmhub_user", "points_balance": 88, "account": { "username": "cmhub_user", "display_name": "主账号" } },优先显示 account.display_name,没有时再用 account.username / user 等兜底;邮箱需脱敏,没有账号信息时保留 cmhub 连接成功:... 兜底文案。
    • API Key 存在 data/config/cmhub.json,本地明文保存;保存/变更时提示;UI 使用密码框打码显示,不进入日志/导出。
    • 生文/生图别名来自 GET /api/v1/models 动态下拉,过滤未定价别名并展示“默认档 / 高质量档 / 省点档”、展示名、单价/需原图提示;网关临时不可达时保留已存别名。
    • 保存设置固定写 ai.backend=cmhub。允许先保存不完整 cmhub 配置,②真正生成时如果缺 Base URL/API Key/别名,会提示去⑤补配置,不静默回退 direct。
    • T-531 已完成:⑤设置页任意可编辑控件变更都进入未保存状态,保存按钮旁显示“● 未保存更改”;切换到其它 Tab 或关闭窗口时弹出保存/放弃/取消。放弃会重新从本地配置文件回填控件,避免未保存的 URL/API Key 留在界面上;程序化回填、保存后重载和刷新别名填充下拉不会误触发未保存状态。
    • direct 模型清单和 data/config/ai_models.json 代码路径保留为内部兼容/手工回滚,不在普通 UI 暴露。
  • AI 生成参数:标题并发、图片并发、失败重试、分辨率、返回超时等短字段按三列排列;标题/图片并发可选 1..5,失败重试可选 0..10,旧配置超限值会自动夹紧;图片保存质量保留内部默认 90,不在普通 UI 展示。
    • 分辨率为 512 / 1k / 2k / 4k,在 cmhub 默认模式下只控制生成图片尺寸;⑤「返回超时」只读展示当前实际等待口径:标题 600 秒、图片 900 秒,不再随分辨率切换显示 180/240/360/600,避免用户误解生图等待时间。
    • 保存写入 config.json 的 ai 段,供 ② AI生成复用;标题/图片模型角色下拉随 direct UI 一起隐藏。
  • 路径与端口(T-501b/T-506/T-539/T-580 已接入):组件组改为 3 个组件一组;普通设置页只显示 Chrome 路径、默认端口、端口起止、Chrome 就绪超时。T-538 后账号数据根目录、图片目录、DB 路径固定解析到 data/ 下,普通 UI 不再提供输入框,避免用户误改后数据分裂;config.json 中 user_data_root / image_dir / db_path 字段继续作为内部兼容字段保留,手工配置值仍会被读取和保存。
  • 蝦皮更新执行(T-580 已接入):组件组改为 3 个组件一组;普通设置页只保留「每批最大更新条数」和「同时更新蝦皮账号(1..5)」两个执行参数。1 表示逐个账号串行,2..5 表示按账号分组并行;“dry-run”不作为用户可见开关,改到③成为「检查本轮更新」按钮;测试商品 ID 和旧封面开关仅作为历史/调试兼容字段读取,普通设置页无入口,保存后不再写回。
    • ③「更新内容」默认只更新标题,每批最大更新条数默认 1,同时更新蝦皮账号默认 1。
    • ③ 点击「开始更新」会先按「更新内容」校验缺失内容,再弹确认框。

⑥ AI工场

┌ AI工场 ──────────────────────────────────────────────────────┐
│ 左轨:账号[▼] 商品ID[____] [打开项目] [拉取主图] [打开项目文件夹] │
│      项目列表:账号 / 商品ID / 更新时间                         │
│ 中区:蝦皮原主图(单击下载进池,双击预览)                         │
│      照片池:原图/主图/详情图角标、比例、排队/生成/失败状态           │
│ 右侧:模板[▼] [新建][重命名][保存][删除]                           │
│      [完整提示词输入框]                                            │
│      类型[主图▼] 数量[4] 比例[1:1▼] cmhub扣点/余额提示              │
│      [开始生成][停止] 进度条 运行日志                               │
│ 底部:主图终选 / 详情图终选(拖入、插入、重排、移出) [导出终选]       │
└───────────────────────────────────────────────────────────────┘
  • 项目以 账号别名 + 商品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,不重复扣点。照片池中的任务行显示排队/生成/失败/过期/停止状态,并附带扣点、余额和 call_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 仍后置。

流程导航

④ 账号管理:配账号 + 启动登录(首次必做;重复点击应复用已打开 Chrome)
        │
① 导入采集:导入 Excel → 采集旧标题/旧封面 → 自动回写旧字段(失败可手动重试)
        │
② AI生成:提示词 → 选择生成标题/封面/图文(无逐条审核)
        │
⑤ 设置:配置每批最大更新条数和蝦皮更新执行模式
        │
③ 更新蝦皮:选择更新标题/封面/图文 → 对已生成任务点击开始更新 → 缺失内容校验 → 弹窗确认 → 账号就绪预检(未启动/未登录则中止) → 按模式换标题/封面 → 点「更新」提交 → 回写结果 → 弹窗汇总
  • 未配账号 / 未登录:① 会自动确保匹配账号 Chrome 就绪但不自动登录,未登录账号略过并提示去④;③ 更新前仍只检测账号 Chrome/CDP/登录态,未启动或未登录则中止,不静默启动缺失账号 Chrome。
  • 已生成的任务即可进 ③;③ 经用户确认批量弹窗后提交线上,无常驻提交开关。
  • 任意步骤失败:记入该任务、日志标明,不影响其他任务。

组件建议(PySide6)

组件 归属 说明
MainWindow(QMainWindow) 根窗口 持有 QTabWidget、状态栏、全局消息
CollectTab(QWidget) ① 导入、任务表、采集、回写
GenerateTab(QWidget) ② 左提示词管理 + 右筛选/任务列表;双击看新旧封面;开始生成/停止/进度;本轮「生成内容」下拉接入 GenerateWorker
ApplyTab(QWidget) ③ 已生成任务筛选 +「更新内容」下拉 + 缺失内容阻断 +「检查本轮更新」+ 分批开始更新确认 + 检查/真实更新运行日志 + 结果回写与结束汇总
AccountsTab(QWidget) ④ 账号增删改、启动登录、检测登录、生成快捷方式;登录检测把 Shopee accounts 登录页判为未登录
SettingsTab(QWidget) ⑤ cmhub 网关配置 + 响应式三列设置表单 + 生成参数 + Chrome/端口配置 + 蝦皮更新执行;数据路径字段隐藏但保留配置兼容
ImageStudioTab(QWidget) ⑥ AI工场项目列表、只读拉蝦皮主图、原图下载进池、照片池、大图预览、完整提示词模板 CRUD、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

采集、生成、更新都是耗时操作,使用 QObject worker + QThread。Worker 不直接操作 QWidget,只通过 signal 通知主线程刷新 UI。