feat: polish Shopee update checks

This commit is contained in:
chengma
2026-07-01 14:30:49 +08:00
parent cc277366ed
commit 84b35f1cdc
12 changed files with 301 additions and 83 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ cmshopee 是一个 Windows 本地桌面自动化工具(PySide6,5 Tab),
- 5 Tab 流水线:① 导入采集 → ② AI生成 → ③ 更新shopee → ④ 账号管理 → ⑤ 设置。
- GUI 固定为 PySide6;后台采集/生成/更新用 `QObject` worker + `QThread` + signal 回传进度。
- 多账号管理;账号以独立 user-data-dir 隔离。③ 更新默认串行,提供「预览本轮更新」按钮;⑤ 可开启按账号并行和设置每批最大更新条数。
- 多账号管理;账号以独立 user-data-dir 隔离。③ 更新默认串行,提供「检查本轮更新」按钮;⑤ 可开启按账号并行和设置每批最大更新条数。
- Excel 导入/回写 + SQLite 实时落库 + 本地图片目录。
- AI 生成标题/封面,不设逐条确认阶段。
- ③ 点击「开始更新」后弹窗确认当前筛选范围和任务数量;用户确认后才批量提交线上。
+4 -4
View File
@@ -45,8 +45,8 @@
| AI 生成(②) | 输入标题/封面提示词,AI 据提示词+旧标题生成新标题、据提示词+旧封面生成新封面 | P0 |
| 提示词管理(②) | 标题提示词「保存」到 `title_prompt.txt` 并启动回显;封面提示词多模板(下拉 + 新建/保存/另存为/重命名/删除)+ 插入 `{新标题}` + 预览(变量替换) | P0 |
| 查看对照(②) | 双击任务弹窗查看新旧封面(纯查看,无逐条审核阶段);可选对单行重生成 | P0 |
| 更新 shopee(③) | 按批次/店铺/状态筛选;可先「预览本轮更新」确认范围;点击「开始更新」后弹窗确认,确认后对当前筛选出的已生成任务按每批最大条数分批更新 Shopee,逐条换标题+封面并点「更新」提交线上;普通正式更新不再受测试商品 ID 限制,可批量更新真实商品;可按状态=失败重试 | P0 |
| 预览本轮更新 / 运行日志 / 多账号并行(③/⑤) | ③ 提供「预览本轮更新」按钮,不打开 Shopee、不提交、不改任务状态,只显示当前筛选范围、店铺分布、预计分批和会更新字段;真实更新写运行日志;可在⑤开启多账号并行,同账号内仍串行 | V2 已接入,预览按钮待改名接入 |
| 更新 shopee(③) | 按批次/店铺/状态筛选;可先「检查本轮更新」确认范围;点击「开始更新」后弹窗确认,确认后对当前筛选出的已生成任务按每批最大条数分批更新 Shopee,逐条换标题+封面并点「更新」提交线上;普通正式更新不再受测试商品 ID 限制,可批量更新真实商品;可按状态=失败重试 | P0 |
| 检查本轮更新 / 运行日志 / 多账号并行(③/⑤) | ③ 提供「检查本轮更新」按钮,不打开 Shopee、不提交、不改任务状态,只显示当前筛选范围、店铺分布、预计分批和会更新字段;真实更新写运行日志;可在⑤开启多账号并行,同账号内仍串行 | V2 已接入,检查按钮已接入 |
| 结果存储与回写 | 各阶段结果实时存 SQLite;该文件全部完成后把旧/新数据+状态批量回写原 Excel | P0 |
| 设置(⑤) | AI 模型管理(下拉+新增/删除/详情/测试连接,至少各一个文本+图像模型);标题/图片大模型角色选择;分辨率(512/1k/2k/4k,返回超时随分辨率自动);并发/重试/jpg质量;图片目录/Chrome 路径/端口;设置页采用居中内容区、适度左右留白和响应式三列表单,长字段跨列显示;点击「保存设置」成功后弹轻量提示框 | P0 |
| 首次引导保护 | 未配账号、对应账号 Chrome 未启动或未登录时,① ③ 执行按钮禁用/执行前拦截并提示去④;③ 不自动打开缺失账号 Chrome,必须中止本轮更新 | P0 |
@@ -107,8 +107,8 @@
- **AI Key 安全**:Key 本地明文存于 `config/ai_models.json`,保存/变更时提示,UI 打码,不写日志,不提交版本库。
- **第三方平台风险**:Shopee 页面结构、class 名、接口随时可能变;限流、风控、封号风险存在,禁止高频批量。
- **自动化边界风险**:③ 确认后会自动改标题、上传图片、拖拽并点「更新」提交线上;点「开始更新」并确认前需自行确保筛选范围、任务来源与 AI 产出可接受。
- **预览边界**:③「预览本轮更新」只预览当前筛选任务并写运行日志,不打开 Shopee、不点击更新、不改任务状态;点击「开始更新」并确认后才可能真实提交。
- **检查边界**:③「检查本轮更新」只检查当前筛选任务并写运行日志,不打开 Shopee、不点击更新、不改任务状态;点击「开始更新」并确认后才可能真实提交。
- **多账号并行风险**:⑤ 开启多账号并行后,不同账号可同时执行更新;同一账号内仍串行。执行前检查本轮账号调试端口,端口冲突时阻断真实更新。
- **合规风险**:仅在自有/授权账号上操作;遵守 Shopee 卖家条款;不绕过任何平台限制。
- **GUI 选型**:V1 固定为 PySide6;后台任务通过 QThread/signal 回传进度,见 [技术栈](03-tech-stack.md) 与 [架构](04-architecture.md)。
- **多账号并行默认关闭**:默认仍串行;只在⑤明确开启后并行,建议先用「预览本轮更新」确认账号、店铺和任务范围。
- **多账号并行默认关闭**:默认仍串行;只在⑤明确开启后并行,建议先用「检查本轮更新」确认账号、店铺和任务范围。
+4 -4
View File
@@ -137,7 +137,7 @@ imported → collected → generated → applied
- `allow_cover_update`:是否允许本次更新包含新封面路径;默认 `false`,未开启时只允许标题更新任务。
- `max_items_per_run`:每批最大更新任务数;默认 `1`,当前筛选结果超过该值时自动分批,不再按总数阻断。
- `close_success_tab`:成功提交后是否关闭本轮程序自动新开的商品编辑页;默认 `false`。只关闭 `open_product()` 本轮新建且已提交成功的 tab,失败任务和用户原本打开的 tab 保留。Shopee 确认成功并跳回商品列表页后,关闭前等待 2 秒,让列表页跳转和页面状态稳定。
- `dry_run`:内部兼容字段;普通用户界面不展示该开关,③「预览本轮更新」按钮触发预览,只写运行日志,不打开 Shopee、不点击「更新」、不改任务状态;默认 `false`。
- `dry_run`:内部兼容字段;普通用户界面不展示该开关,③「检查本轮更新」按钮触发检查模式,只写运行日志,不打开 Shopee、不点击「更新」、不改任务状态;默认 `false`。
- `parallel_accounts`:是否按账号并行执行③真实更新;默认 `false`,即保持串行。
- `max_parallel_accounts`:最多同时执行的账号数;同一账号内仍按任务串行,默认 `2`。
@@ -388,7 +388,7 @@ images/<batch_id>/<slug>/<task_id>_<item_id>_new.<ext> # AI 生成的新封
### 6.3 应用更新(③ Tab)
- ③ 顶部筛选确定本次作用范围;点击「开始更新」后弹窗展示筛选条件、任务数量和“将提交线上”的风险提示。
- ③ 提供「预览本轮更新」按钮:只读取当前筛选结果和写运行日志,不打开 Shopee、不提交、不改任务状态;预览展示总数、店铺分布、每批最大条数、预计批次数、更新字段和略过原因。
- ③ 提供「检查本轮更新」按钮:只读取当前筛选结果和写运行日志,不打开 Shopee、不提交、不改任务状态;检查汇总展示总数、店铺分布、每批最大条数、预计批次数、更新字段和略过原因。
- 弹确认前先读取 `config.json` 的 `shopee_update`:未开启 `allow_real_submit`,或任务含新封面但未开启 `allow_cover_update` 时,直接弹警告阻断,不创建真实更新 `ApplyWorker`。普通正式更新不再检查 `test_item_id`,当前筛选结果可以包含多个真实商品 ID。`max_items_per_run` 作为每批最大任务数,当前筛选总数超过该值时自动分批,不再按总数阻断。
- 用户点「是/确认」才开始批量更新;点「否/取消」不执行、不改库。
- 真实更新前必须做账号就绪预检:按当前筛选结果汇总需要的账号;无账号、账号 Chrome 未启动、CDP 端口不可访问、未登录或本轮账号端口冲突时,整体返回 `blocked` 并由 GUI 弹窗列出账号/原因、引导去④账号管理。预检不通过时不创建商品编辑页、不调用 `editor.apply_task()`、不写失败状态、不自动调用「启动登录」或静默打开 Chrome。
@@ -396,8 +396,8 @@ images/<batch_id>/<slug>/<task_id>_<item_id>_new.<ext> # AI 生成的新封
- `click_update` 点击页面「更新」后必须处理 Shopee 站点侧二次确认框。2026-06-29 真实测试商品实测:页面会出现 `.eds-modal__content` / `.eds-modal__box`,标题为 `確定您要更新商品嗎?`,正文提示建议优化,底部两个按钮分别是 `立即優化` 与主按钮 `更新`。实现时只允许在标题匹配该确认框、且按钮位于可见 modal footer 内时点击 `button.eds-button--primary` / 文案 `更新`;不得点击 `立即優化`。如果弹窗出现但未成功点击主按钮,当前任务必须视为未提交失败,不得写 `committed=1`。
- 更新封面时,`replace_cover()` 必须按“替换第一张”语义执行:无论当前商品图片是 8 张还是 9 张,只要本次有新封面,就先确认该任务已有本地旧封面备份(`old_cover_path` 非空且文件存在),再删除当前线上第一张图、等待图片管理器稳定、上传新图并拖到第一位。缺失备份时不删除线上第一张图,直接返回明确错误,要求先回到①采集旧封面或修复本地备份。
- 默认串行、单条失败继续;⑤ 开启 `parallel_accounts` 后按账号分组并行,不同账号可同时跑,同一账号内仍串行。真实更新前检查本轮账号 `debug_port`,端口冲突直接阻断。
- 「预览本轮更新」只写 `run_logs/run_log_events` 和弹窗/状态栏预览,不调用 `editor.apply_task()`,不做账号登录预检,不写任务状态,不回写 Excel。
- 真实更新按每批最大条数分批执行,每条立即写 SQLite;全部完成回写 Excel(新字段+状态)+ 弹窗汇总。真实更新与预览都写运行日志,日志 payload 走脱敏工具。
- 「检查本轮更新」只写 `run_logs/run_log_events` 和弹窗/状态栏检查汇总,不调用 `editor.apply_task()`,不做账号登录预检,不写任务状态,不回写 Excel。
- 真实更新按每批最大条数分批执行,每条立即写 SQLite;全部完成回写 Excel(新字段+状态)+ 弹窗汇总。真实更新与检查都写运行日志,日志 payload 走脱敏工具。
- 分批更新停止语义为协作式停止:点击停止后设置取消标记;当前正在执行的商品跑到安全边界后写库结束,不再开始新商品,也不进入下一批。未开始任务保持原状态,后续可继续。
- T-404a 已在③提供「重置更新状态」:仅当前选中单条,保留 `new_title/new_cover_path`,本地退回 `stage=generated/status=pending` 以便重复测试上传/提交;若 `committed=1`,必须提示线上已提交过、本地重置不回滚 Shopee、重复更新会再次提交,并保留 committed 历史事实/运行日志。
- 若 `close_success_tab=true`,且商品页是本轮程序自动新建、并已成功提交,则提交后关闭该商品编辑页;若确认后已跳回商品列表页,关闭前等待 2 秒;失败任务和复用的用户已有 tab 不关闭。
+4 -1
View File
@@ -89,8 +89,11 @@
| T-503 | 敏感信息本地明文存储提示与日志脱敏 | T-105, T-501 | 首次保存密码/API Key 时提示“本地明文保存”;UI 打码;日志/导出不含密码/API Key;文档说明 `config.json`/`config/ai_models.json`/DB/user-data-dir/images 必须 gitignore | DONE |
| T-504 | 多账号并行 / dry-run / 运行日志(V2) | T-402 | 端口不冲突;dry-run 只预览;操作留痕 | DONE |
| T-505 | 全流程诊断日志扩展 | T-207, T-504 | 在 T-207 采集日志机制稳定后,扩展到② AI生成、Excel 导入/回写、③ 更新shopee步骤级日志、④ Chrome 启动/登录检测、⑤ AI模型测试连接;数据库日志保存业务可读步骤和错误,本地 log 保存脱敏 traceback;不得记录 Cookie、密码、API Key、token | TODO |
| T-506 | 正式使用批量更新体验:③ 预览本轮更新 + 分批更新全部 + ⑤ 设置页三列布局 | T-501c, T-504 | ③ 增加「预览本轮更新」按钮替代用户可见 dry-run;「开始更新」对当前筛选结果按每批最大更新条数自动分批,确认弹窗显示总数/每批/预计批次,停止为当前商品安全结束后不再开始新任务;⑤ 设置页使用居中内容区,左右留白已缩短为 T-506 初始实现约 40%,模型详情/角色与生成参数/路径与端口/Shopee 更新安全均使用响应式三列布局,长字段跨列;「多账号并行更新」与「最大并行账号数」合并为同一个横向组件,最大并行账号数紧跟其后且不换行 | DONE |
| T-506 | 正式使用批量更新体验:③ 检查本轮更新 + 分批更新全部 + ⑤ 设置页三列布局 | T-501c, T-504 | ③ 增加「检查本轮更新」按钮替代用户可见 dry-run;「开始更新」对当前筛选结果按每批最大更新条数自动分批,确认弹窗显示总数/每批/预计批次,停止为当前商品安全结束后不再开始新任务;⑤ 设置页使用居中内容区,左右留白已缩短为 T-506 初始实现约 40%,模型详情/角色与生成参数/路径与端口/Shopee 更新安全均使用响应式三列布局,长字段跨列;「多账号并行更新」与「最大并行账号数」合并为同一个横向组件,最大并行账号数紧跟其后且不换行 | DONE |
| T-507 | 正式批量更新:移除普通流程测试商品 ID 限制 | T-506 | 普通正式更新不再以 `test_item_id` 阻断当前筛选结果;③ 确认弹窗不再显示测试商品 ID;⑤ 普通设置页隐藏测试商品 ID,仅保留历史/调试兼容字段;仍保留允许真实提交、允许更新封面、分批、二次确认、账号就绪预检和运行日志 | DONE |
| T-508 | ③ 更新shopee生产化操作区 | T-401, T-404a, T-506 | 「开始更新」作为主操作视觉强化;「重置更新状态」从底部按钮移到任务表右键菜单/高级入口,与批量更新物理分离,运行中禁用;更新安全拦截弹窗写明具体开关并提供「前往设置」深链到⑤,不改 Shopee/CDP 更新流程 | DONE |
| T-509 | ② 新标题人工微调 | T-303, T-401 | ②任务表「新标题」列可编辑;仅允许已生成、未提交线上、非运行中任务;编辑写回 `tasks.new_title`,保留 `stage=generated`,清空 `last_error`,状态回到可更新;不触碰 Shopee/CDP/Excel,③ 使用修改后的标题 | DONE |
| T-510 | ③ 检查本轮更新文案统一 | T-506, T-508 | 将用户可见「预览本轮更新」统一改为「检查本轮更新」,按钮、确认框、状态栏、完成弹窗、运行日志和文档一致;保留内部 `dry_run` 字段与执行语义,不改 Shopee/CDP 更新流程 | DONE |
## 里程碑
+16 -15
View File
@@ -92,6 +92,7 @@ mark_failed(task_id, phase, error) -> None # status=failed,stage 不前进
mark_skipped(task_id, reason) -> None # status=skipped,stage 不前进
set_collected(task_id, old_title, old_cover_path) -> None # stage=collected,status=success,collect_attempts+1
set_generated(task_id, new_title, new_cover_path) -> None # stage=generated,status=success,generate_attempts+1
update_generated_title(task_id, new_title, path=None) -> None # T-509 本地人工微调新标题;仅 generated/未提交/非运行中;清空 last_error 并回到 pending,不触碰 Shopee/CDP/Excel/封面
set_applied(task_id, committed, error=None) -> None # 成功 stage=applied;失败 status=failed 且 stage 不前进
# T-404a 已实现:选中记录重置
reset_generated(task_id, delete_file=False) -> dict # 本地清空 AI 结果并退回 collected;默认不删新封面文件
@@ -328,23 +329,23 @@ main() -> int # 创建 QApplication + MainWindow
class MainWindow(QMainWindow) # QTabWidget: ①②③④⑤;支持注入 db_path/config/config_path/ai_models_path 便于测试
class CollectTab(QWidget) # ① 导入采集:导入 Excel + 汇总栏 + QTableView 任务列表 + 未匹配略过标记
class GenerateTab(QWidget) # ② AI生成:提示词管理 + 筛选任务 + 开始/停止生成 + 新旧封面预览 + AI生成运行日志
class ApplyTab(QWidget) # ③ 更新shopee:筛选已生成任务 + 预览本轮更新 + 安全开关拦截 + 确认后分批真实更新 + 运行日志
class ApplyTab(QWidget) # ③ 更新shopee:筛选已生成任务 + 检查本轮更新 + 安全开关拦截 + 确认后分批真实更新 + 运行日志
class SettingsTab(QWidget) # ⑤ 设置:AI 模型管理 + 响应式三列布局 + 角色/生成参数/路径端口 + Shopee 更新安全
class CollectWorker(BaseWorker) # ① 后台采集:账号就绪预检 -> editor.collect -> db.set_collected/mark_skipped/mark_failed
class GenerateWorker(BaseWorker) # ② 后台生成:ai.generate_batch -> db.set_generated/mark_failed + 进度
class ApplyWorker(BaseWorker) # ③ 后台更新:账号就绪预检 -> 预览或按批调用 editor.apply_task(close_success_tab=...) -> db.set_applied/mark_skipped
class ApplyWorker(BaseWorker) # ③ 后台更新:账号就绪预检 -> 检查或按批调用 editor.apply_task(close_success_tab=...) -> db.set_applied/mark_skipped
class WriteBackWorker(BaseWorker) # ①/③ 后台回写:旧字段或更新结果写回原 Excel
class AIModelTestWorker(BaseWorker) # ⑤ 后台测试 AI 模型连接:appconfig.test_ai_model
class TaskTableModel(QAbstractTableModel) # 任务表格模型:账号/别名/商品ID/阶段;未匹配别名显示“略过”
class GenerateTaskTableModel(QAbstractTableModel) # ② 任务表格模型:店铺/商品ID/旧标题/新标题/状态
class ApplyTaskTableModel(QAbstractTableModel) # ③ 任务表格模型:店铺/商品ID/新标题/新封面/阶段/结果
class GenerateTaskTableModel(QAbstractTableModel) # ② 任务表格模型:店铺/商品ID/旧标题/新标题/状态;generated/未提交/非运行中新标题可本地编辑
class ApplyTaskTableModel(QAbstractTableModel) # ③ 任务表格模型:店铺/商品ID/新标题/新封面/阶段/结果;保持只读,重置更新状态走右键菜单
class AccountsTab(QWidget) # ④ 账号管理:表格 + 增删改 + 启动登录 + 检测登录 + 快捷方式
class AccountDialog(QDialog) # 账号编辑弹窗;密码 QLineEdit.Password
TAB_TITLES: list[str] # 固定 Tab 顺序
TAB_STYLE: str # 顶层 Tab 栏防误点样式:最小宽度/padding/间距/当前态
```
`MainWindow` 已实现五 Tab、① 导入采集任务列表、② AI生成布局/提示词/开始生成/停止/封面对照预览、③ 更新shopee筛选列表与预览/确认后分批真实更新、④ 账号管理、⑤ AI 模型管理。缺 PySide6 时 `main()` 返回 1 并输出明确提示。
`MainWindow` 已实现五 Tab、① 导入采集任务列表、② AI生成布局/提示词/开始生成/停止/封面对照预览、③ 更新shopee筛选列表与检查/确认后分批真实更新、④ 账号管理、⑤ AI 模型管理。缺 PySide6 时 `main()` 返回 1 并输出明确提示。
主 Tab 栏必须在 `MainWindow` 初始化时应用 `TAB_STYLE`:5 个 Tab 不使用 Qt 默认紧凑宽度,需保证点击区域稳定、间距清晰、当前 Tab 高亮明显。该样式属于全局导航基础,不归后续业务 Tab 任务重复实现。
@@ -365,7 +366,7 @@ TAB_STYLE: str # 顶层 Tab 栏防误点样式:
- 角色与生成参数读写 `config.json`,并按 3 个组件一组排列:标题大模型(仅 text)、图片大模型(仅 image)、标题/图片并发、失败重试、分辨率、jpg 质量。
- 分辨率下拉固定 `512/1k/2k/4k`;返回超时标签只读展示 `resolution_timeouts[resolution]`。
- 路径与端口读写 `config.json`,并按 3 个组件一组排列:默认调试端口、调试端口范围、CDP 就绪超时等短字段一格;Chrome 路径、账号数据根目录、图片目录、DB 路径等长字段跨整行或跨 2/3 列。保存时校验端口范围和默认端口。
- Shopee 更新安全读写 `config.json` 的 `shopee_update` 段,并按 3 个组件一组排列:允许真实提交、允许更新封面、每批最大更新条数、成功后关闭本次新开编辑页、多账号并行、最大并行账号数;其中「多账号并行更新」与「最大并行账号数」必须合并为同一个横向组件,最大并行账号数紧跟在多账号并行更新后面,不允许被三列表单排到下一行。`dry_run` 字段可保留为内部兼容,但普通用户界面不再展示 dry-run 开关,③ 使用「预览本轮更新」按钮触发预览;测试商品 ID 仅作为历史/调试兼容字段保留,不参与普通正式更新安全检查,普通设置页已隐藏该入口。
- Shopee 更新安全读写 `config.json` 的 `shopee_update` 段,并按 3 个组件一组排列:允许真实提交、允许更新封面、每批最大更新条数、成功后关闭本次新开编辑页、多账号并行、最大并行账号数;其中「多账号并行更新」与「最大并行账号数」必须合并为同一个横向组件,最大并行账号数紧跟在多账号并行更新后面,不允许被三列表单排到下一行。`dry_run` 字段可保留为内部兼容,但普通用户界面不再展示 dry-run 开关,③ 使用「检查本轮更新」按钮触发检查模式;测试商品 ID 仅作为历史/调试兼容字段保留,不参与普通正式更新安全检查,普通设置页已隐藏该入口。
- 真实提交与封面更新默认关闭;用户在 ⑤ 保存开启后,③ 仍需要通过安全开关检查并弹窗确认,才会创建更新 worker。
① 导入采集当前要点(T-202/T-202b):
@@ -389,8 +390,8 @@ TAB_STYLE: str # 顶层 Tab 栏防误点样式:
- 封面提示词模板下拉读取 `prompts/cover/*.txt`;支持新建、保存、另存为、重命名、删除。删除由 GUI 二次确认,删空后下拉显示内存态“默认”,不会自动建文件。
- 「插入标题」在封面提示词光标处插入 `{新标题}`;「预览」使用当前选中任务(无选择则用第一条)调用 `prompts.render_prompt()` 并弹窗展示。
- 筛选栏包含:批次、店铺、商品ID、状态、刷新。批次来自 `db.list_batches()`;店铺来自当前任务别名并优先显示匹配账号名;商品ID输入框按包含匹配 `item_id`,清空表示全部;状态支持全部/待生成/已生成/失败/略过/已更新。
- 任务列表使用 `QTableView + GenerateTaskTableModel`,列为:店铺、商品ID、旧标题、新标题、状态。`stage=collected` 显示“待生成”,`stage=generated` 显示“已生成”,`status=failed/skipped/running` 优先显示对应状态。
- 双击任务行弹窗展示旧封面与新封面路径对应图片;图片不存在时显示空态/路径提示,只做查看,不做审核。
- 任务列表使用 `QTableView + GenerateTaskTableModel`,列为:店铺、商品ID、旧标题、新标题、状态。`stage=collected` 显示“待生成”,`stage=generated` 显示“已生成”,`status=failed/skipped/running` 优先显示对应状态;已生成、未提交线上、非运行中的「新标题」列可双击编辑,调用 `db.update_generated_title()` 写回本地并清空 `last_error`。
- 双击「新标题」列进入本地编辑;双击其他列弹窗展示旧封面与新封面路径对应图片;图片不存在时显示空态/路径提示,只做查看,不做审核。
- 底部「开始生成」只处理当前筛选结果里 `stage=collected` 的任务;批次/店铺/商品ID/状态筛选共同决定当前筛选结果;通过 `GenerateWorker` 调 `ai.generate_batch()`,先并发标题、再并发封面。
- 「停止」调用 worker 的协作式 `cancel()`;未开始的 Future 取消,不记失败;已完成的 `db.set_generated()` 结果保留,可再次开始生成剩余任务。
- 进度标签显示:`标题x/n · 封面x/n · 失败z`;每条封面生成成功后立即 `db.set_generated()`,失败 `db.mark_failed(..., "generate", error)`,GUI 刷新任务表。
@@ -402,18 +403,18 @@ TAB_STYLE: str # 顶层 Tab 栏防误点样式:
- ③ 只列出已具备新标题/新封面、可进入更新阶段的任务:`stage=generated/applied`,或已有新字段且 `status=failed/skipped` 的任务。
- 状态筛选支持:已生成(默认,`stage=generated` 且 `status=success/pending`)、失败、已更新、略过、全部状态。
- 任务列表使用 `QTableView + ApplyTaskTableModel`,列为:店铺、商品ID、新标题、新封面、阶段、结果。
- 「开始更新」只读取当前筛选结果;无任务时只提示,不弹确认、不改库。
- 「预览本轮更新」只读取当前筛选结果并创建预览运行日志,不打开 Shopee、不调用 `editor.apply_task()`、不写任务状态、不回写 Excel;预览内容包含任务总数、店铺分布、每批最大条数、预计批次数、会更新字段和略过原因。
- 点击「开始更新」先读取 `shopee_update`:未允许真实提交,或包含新封面但未允许封面更新时,弹警告并阻断;普通正式更新不再检查 `test_item_id`,当前筛选结果可包含多个真实商品 ID;`max_items_per_run` 作为每批最大更新条数,当前筛选结果超过该值时自动分批。通过后才弹窗展示批次/店铺/商品ID/状态/任务总数、每批最大条数、预计批次数、提交线上风险和当前安全设置。
- 「开始更新」只读取当前筛选结果;无任务时只提示,不弹确认、不改库;该按钮是③的主操作,视觉上强于检查、停止和回写。
- 「检查本轮更新」只读取当前筛选结果并创建检查运行日志,不打开 Shopee、不调用 `editor.apply_task()`、不写任务状态、不回写 Excel;检查内容包含任务总数、店铺分布、每批最大条数、预计批次数、会更新字段和略过原因。
- 点击「开始更新」先读取 `shopee_update`:未允许真实提交,或包含新封面但未允许封面更新时,弹警告并阻断;拦截弹窗写明具体设置项并提供「前往设置」跳到⑤;普通正式更新不再检查 `test_item_id`,当前筛选结果可包含多个真实商品 ID;`max_items_per_run` 作为每批最大更新条数,当前筛选结果超过该值时自动分批。通过后才弹窗展示批次/店铺/商品ID/状态/任务总数、每批最大条数、预计批次数、提交线上风险和当前安全设置。
- 用户点否/取消时不执行、不改库;用户点是后才创建 `ApplyWorker` 做真实提交。
- `ApplyWorker` 只处理当前筛选结果里 `stage=generated` 且已有新标题或新封面、状态为 `success/pending/failed` 的任务;已更新和略过记录仅查看,不会再次提交,除非用户先用 T-404a 的「重置更新状态」把选中记录退回可更新。
- 预览本轮更新:不做账号登录预检,不调用 `editor.apply_task()`,不写任务状态,不回写 Excel;只把每条“将更新/将略过”写入运行日志并弹汇总。
- 检查本轮更新:不做账号登录预检,不调用 `editor.apply_task()`,不写任务状态,不回写 Excel;只把每条“将更新/将略过”写入运行日志并弹汇总。
- 真实更新前先做账号就绪预检:无账号、当前筛选结果匹配账号 Chrome 未启动、CDP 端口不可访问、未登录,或本轮涉及账号调试端口冲突时,返回 `blocked=True`,GUI 弹窗汇总并跳转/引导去④账号管理;预检不通过时不调用 `editor.apply_task()`、不写失败状态、不自动启动 Chrome。
- 预检通过后默认串行;若 `parallel_accounts=true` 且 `max_parallel_accounts>1`,按账号分组并行执行,不同账号可同时跑,同一账号内仍串行。每条执行 `db.mark_running(..., "apply")` → `editor.apply_task(account, task, close_success_tab=设置值)` → `db.set_applied()`;成功推进 `stage=applied/status=success/committed=1`,失败保持原 stage、`status=failed/committed=0/last_error`,单条失败继续下一条。
- 预览和真实更新都会创建 `run_logs`,并把逐条事件写入 `run_log_events`;③ 页面显示最近运行日志。
- 检查和真实更新都会创建 `run_logs`,并把逐条事件写入 `run_log_events`;③ 页面显示最近运行日志。
- 若 `close_success_tab=true`,`editor.apply_task()` 只关闭本轮自动新开且成功提交的商品页;确认后跳回商品列表页时,关闭前等待 2 秒;失败和复用的用户已有 tab 不关闭。
- 别名未匹配账号的任务逐条 `db.mark_skipped()`,原因 `别名未匹配账号`;「停止」调用 worker 协作式 `cancel()`,已开始单条跑到安全边界后结束。
- T-404a 已实现:「重置更新状态」读取当前选中单条;确认后保留 `new_title/new_cover_path`,本地退回 `stage=generated/status=pending` 供重复更新;`committed=1` 时必须提示线上已提交过且不回滚 Shopee,并保留 committed 历史事实/运行日志。
- T-404a/T-508 已实现:「重置更新状态」从底部批处理按钮移到任务表右键菜单,读取当前选中单条,运行中禁用;确认后保留 `new_title/new_cover_path`,本地退回 `stage=generated/status=pending` 供重复更新;`committed=1` 时必须提示线上已提交过且不回滚 Shopee,并保留 committed 历史事实/运行日志。
- ③ 没有常驻提交开关;确认弹窗是提交线上前的边界。
- 更新完成后自动调用 `WriteBackWorker(mode="results")` → `excel.write_back_results()`,把新标题、新封面路径、更新状态写回原 Excel;原文件被占用时提示关闭后点击「回写结果到 Excel」手动重试,SQLite 更新结果不回滚。
- 自动结果回写完成后弹窗汇总成功/失败/略过数量与 Excel 回写文件/行数;若没有可回写批次,也会弹出更新汇总。
@@ -450,7 +451,7 @@ run_worker(worker: BaseWorker, thread_name=None, start=True) -> QThread
- 采集、AI 生成、更新、Excel 回写都通过 worker 执行,用 signal 回传进度。
- 每个 worker/线程按需创建自己的 SQLite connection,不跨线程共享连接。
- ③ 的批量确认弹窗在 GUI 主线程完成;用户确认后才创建 `ApplyWorker`。
- `ApplyWorker` 支持预览、默认串行和按账号并行;真实更新按 `max_items_per_run` 分批调用 `editor.apply_task(..., close_success_tab=...)`,逐条 `set_applied()`,失败继续;账号未就绪或端口冲突时整体阻断并引导④,不进入逐条提交,也不静默启动账号 Chrome。点击停止为协作式停止:当前商品完成后不再开始新商品或下一批。
- `ApplyWorker` 支持检查、默认串行和按账号并行;真实更新按 `max_items_per_run` 分批调用 `editor.apply_task(..., close_success_tab=...)`,逐条 `set_applied()`,失败继续;账号未就绪或端口冲突时整体阻断并引导④,不进入逐条提交,也不静默启动账号 Chrome。点击停止为协作式停止:当前商品完成后不再开始新商品或下一批。
- `WriteBackWorker` 默认 `mode="old"` 回写旧字段;③ 使用 `mode="results"` 回写新标题/新封面/更新状态,支持单批次或多批次列表。
- `execute()` 未捕获异常会发 `failed(-1, error)` 与 `finished({"ok": False, "error": ...})`;普通单行失败由业务 worker 自己发 `failed(task_id, error)` 后继续处理。
+11 -10
View File
File diff suppressed because one or more lines are too long
+14 -13
View File
@@ -11,7 +11,7 @@
| Tab | 职责 | 风险 |
| --- | --- | --- |
| ① 导入采集 | 导入多个 Excel;任务列表;**采集**商品当前的旧标题/旧封面(只读),封面图下载本地;回写 Excel 旧字段 | 只读,低 |
| ② AI生成 | 左侧标题/封面**提示词**;右侧按批次/店铺/商品ID/状态筛选任务列表;AI 生成新标题/新封面;双击看新旧封面 | 不触线上,中 |
| ② AI生成 | 左侧标题/封面**提示词**;右侧按批次/店铺/商品ID/状态筛选任务列表;AI 生成新标题/新封面;已生成任务可本地微调新标题;双击看新旧封面 | 不触线上,中 |
| ③ 更新shopee | 对**已生成**任务点击「开始更新」后弹窗确认;确认后打开编辑页换标题+封面并逐条点「更新」提交;结果回写 Excel | **写线上,高** |
| ④ 账号管理 | Shopee 账号(账号名/别名/数据目录/端口/密码本地明文仅参考/登录状态);启动登录、检测登录、生成快捷方式 | 中 |
| ⑤ 设置 | AI 模型/API Key、本地图片目录、Chrome 路径、默认端口、Shopee 更新安全开关等 | — |
@@ -83,12 +83,12 @@
- **封面提示词**:模板下拉(读 `prompts/cover/*.txt`)+ 图标工具栏(新建/保存/另存为/重命名/删除)+ 多行输入 + 「插入标题」(插 `{新标题}`)/「预览」(变量替换后查看)。
- 变量:`{旧标题}`/`{新标题}`/`{商品id}`/`{店铺}`,生成前按任务替换。
- 右上:按导入批次 / 店铺 / 商品ID / 状态筛选任务;商品ID输入框按包含匹配 `item_id`,清空表示全部。
- 右下:任务列表(店铺名、商品id、旧标题、新标题、状态)+ AI生成运行日志;**双击某条 → 弹窗展示旧封面 | 新封面**(纯查看)。
- 右下:任务列表(店铺名、商品id、旧标题、新标题、状态)+ AI生成运行日志;已生成、未提交线上、非运行中的任务可双击「新标题」列本地微调,写回 `tasks.new_title`,清空 `last_error` 并回到可更新;双击其他列弹窗展示旧封面 | 新封面(纯查看)。
- 底部**单个「开始生成」+「停止」**:开始生成 = **先按 `title_concurrency` 并发生成标题,接着按 `image_concurrency` 并发生成封面**;进度实时显示 标题/封面/失败 计数。
- 生成参数(标题/图片并发数、失败重试、分辨率、jpg 质量、模型/Key)在 **⑤ 设置**,不在本 Tab 重复。
- 每条/每张完成即写库;`run_type=generate` 写 `run_logs/run_log_events`,图片生成失败同时写本地 `logs/cmshopee.log` 脱敏 traceback;「停止」取消未开始项,可再次「开始生成」对剩余继续。
- 「重置生成结果」仅作用当前选中单条,运行中禁用;确认后只改本地 DB,清空 `new_title/new_cover_path/last_error` 并退回 `collected/success` 供重新生成,默认不删除本地新封面文件。若该记录已提交过线上,必须在确认框提示本地重置不回滚 Shopee。
- **无逐条人工审核环节**;新标题直接用 AI 输出(不可编辑);可选对单行 `重生成`。生成完即可进入 ③,③ 开始更新前会做批量确认。
- **无逐条人工审核环节**;新标题默认直接用 AI 输出,但允许对已生成且未提交线上的单行做本地微调;可选对单行 `重生成`。生成完即可进入 ③,③ 开始更新前会做批量确认。
## ③ 更新shopee
@@ -99,27 +99,28 @@
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 店铺 商品ID 新标题 新封面 阶段 结果 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ 运行日志:预览/真实更新逐条留痕 │
│ [预览本轮更新] [▶ 开始更新] [■停止] [重置更新状态] [回写结果到 Excel] │
│ 运行日志:检查/真实更新逐条留痕 │
│ [检查本轮更新] [▶ 开始更新] [■停止] [回写结果到 Excel] │
└───────────────────────────────────────────────────────────────┘
```
- 顶部**按批次 / 店铺 / 商品ID / 状态筛选**(与 ①②一致);商品ID输入框按包含匹配 `item_id`,清空表示全部;「开始更新」作用于**当前筛选结果**,是一道范围控制。
- 店铺筛选:建议**逐店铺更新**(每店铺需先启动其 Chrome 并登录)。
- 状态筛选:`已生成` 只跑未更新的;`失败` 用于**失败重试**;`已更新成功/略过` 仅查看。
- 「预览本轮更新」只读取当前筛选结果并写运行日志,不打开 Shopee、不提交、不改任务状态;弹窗/日志展示总任务数、店铺分布、会更新标题/封面、略过原因、每批最大条数和预计批次数。
- 点击「开始更新」先读取 ⑤ `shopee_update` 安全设置:未允许真实提交,或包含新封面但未允许封面更新时,直接弹警告并阻断;普通正式更新不再以测试商品 ID 限制当前筛选结果,允许当前筛选结果包含多个真实商品 ID;`max_items_per_run` 作为**每批最大更新条数**,当前筛选总数超过该值时不阻断,而是自动分批执行。
- 「检查本轮更新」只读取当前筛选结果并写运行日志,不打开 Shopee、不提交、不改任务状态;弹窗/日志展示总任务数、店铺分布、会更新标题/封面、略过原因、每批最大条数和预计批次数。
- 点击「开始更新」先读取 ⑤ `shopee_update` 安全设置:未允许真实提交,或包含新封面但未允许封面更新时,直接弹警告并阻断;拦截弹窗必须写明具体未开启的设置项,并提供「前往设置」跳到 ⑤;普通正式更新不再以测试商品 ID 限制当前筛选结果,允许当前筛选结果包含多个真实商品 ID;`max_items_per_run` 作为**每批最大更新条数**,当前筛选总数超过该值时不阻断,而是自动分批执行。
- 安全开关通过后,弹窗展示本次筛选条件、任务总数、每批最大条数、预计批次数、安全设置和“将提交线上”的风险提示;用户点「是/确认」才开始,点「否/取消」不执行。
- 真实更新第一条商品前做账号就绪预检:按当前筛选结果汇总需要的账号;无账号、Chrome 未启动、CDP 端口不可访问、未登录或端口冲突时,弹窗列出具体账号/原因并中止本轮,不自动调用「启动登录」或静默打开 Chrome。
- 对确认后的**已生成(generated)任务**执行:打开编辑页换标题+换封面 → 点页面「更新」 → 如 Shopee 弹出“確定您要更新商品嗎?”确认框(`.eds-modal__content` / `.eds-modal__box`),则只点弹窗主按钮「更新」提交,不点「立即優化」。
- 更新封面时统一按替换第一张执行:删除第一张前必须已有该任务的本地旧封面备份(①采集得到的 `old_cover_path` 且文件存在);备份缺失时阻断该条更新并提示先采集/修复备份,不盲删线上图片。
- 默认串行、单条失败继续;⑤ 可开启多账号并行,不同账号同时执行,同一账号内仍串行;真实更新前若本轮账号调试端口冲突则阻断。
- 「预览本轮更新」只写运行日志与预览汇总,不打开 Shopee、不调用 `editor.apply_task()`、不写任务状态、不回写 Excel。
- 真实更新每条立即写回 SQLite(committed/状态/error),失败不阻塞后续任务;预览和真实更新都会写 `run_logs/run_log_events`。
- 「检查本轮更新」只写运行日志与检查汇总,不打开 Shopee、不调用 `editor.apply_task()`、不写任务状态、不回写 Excel。
- 真实更新每条立即写回 SQLite(committed/状态/error),失败不阻塞后续任务;检查和真实更新都会写 `run_logs/run_log_events`。
- 分批更新时「停止」为协作式停止:已开始的当前商品跑到安全边界并写库后停止,不再开始新商品、不进入下一批;未开始任务保持原状态,下次可继续。
- 更新前做账号就绪预检:无账号、对应账号 Chrome 未启动、CDP 端口不可访问或未登录时整体阻断并引导去④账号管理,不进入逐条提交,也不自动打开账号 Chrome。
- 若 ⑤ 开启“成功后关闭本次新开编辑页”,则仅关闭本轮程序自动新开且成功提交的商品页;确认后跳回我的商品列表页时,关闭前等待 2 秒;失败任务和用户原本打开的 tab 不关闭。
- 「重置更新状态」仅作用当前选中单条,运行中禁用;保留 `new_title/new_cover_path`,只把本地状态退回可更新,用于重复测试上传/提交。若 `committed=1`,确认框必须提示线上已提交过、本地重置不回滚 Shopee、重复更新会再次提交;不得静默清除 committed 历史事实。
- 「开始更新」是③的主操作按钮,视觉上必须强于其他批处理按钮。
- 「重置更新状态」从底部批处理按钮移到任务表右键菜单/高级入口,仅作用当前选中单条,运行中禁用;保留 `new_title/new_cover_path`,只把本地状态退回可更新,用于重复测试上传/提交。若 `committed=1`,确认框必须提示线上已提交过、本地重置不回滚 Shopee、重复更新会再次提交;不得静默清除 committed 历史事实。
- 更新完成后自动回写原 Excel:写入新标题、新封面图片路径、更新状态;原文件被锁时提示关闭后点击「回写结果到 Excel」手动重试。
- 自动回写完成后弹窗汇总成功/失败/略过数量与 Excel 回写文件/行数。
@@ -151,7 +152,7 @@
- Shopee 更新安全(T-501c/T-506 已接入):组件组改为 3 个组件一组;允许真实提交、允许更新封面、每批最大更新条数、成功后关闭本次新开编辑页等短字段三列排列;「多账号并行更新」与「最大并行账号数」必须合并为同一个横向组件,最大并行账号数紧跟在多账号并行更新后面,不允许换到下一行;测试商品 ID 仅作为历史/调试兼容字段保留,不参与普通正式更新安全检查,普通设置页已隐藏该入口。
- 默认关闭真实提交和封面更新,每批最大更新条数默认 1。
- ③ 点击「开始更新」会读取这些设置,先拦截不符合条件的更新,再弹确认框。
- 更新执行模式(T-504/T-506 已接入):⑤ 只保留多账号并行更新、最大并行账号数等执行设置;“dry-run”不再作为用户可见开关,改到③成为「预览本轮更新」按钮。
- 更新执行模式(T-504/T-506 已接入):⑤ 只保留多账号并行更新、最大并行账号数等执行设置;“dry-run”不再作为用户可见开关,改到③成为「检查本轮更新」按钮。
- 默认多账号并行关闭;开启多账号并行后同一账号内仍串行。
## 流程导航
@@ -179,12 +180,12 @@
| `MainWindow(QMainWindow)` | 根窗口 | 持有 `QTabWidget`、状态栏、全局消息 |
| `CollectTab(QWidget)` | ① | 导入、任务表、采集、回写 |
| `GenerateTab(QWidget)` | ② | 左提示词管理 + 右筛选/任务列表;双击看新旧封面;开始生成/停止/进度已接入 `GenerateWorker` |
| `ApplyTab(QWidget)` | ③ | 已生成任务筛选 +「预览本轮更新」+ Shopee 更新安全拦截 + 分批开始更新确认 + 预览/真实更新运行日志 + 结果回写与结束汇总 |
| `ApplyTab(QWidget)` | ③ | 已生成任务筛选 +「检查本轮更新」+ Shopee 更新安全拦截 + 分批开始更新确认 + 检查/真实更新运行日志 + 结果回写与结束汇总 |
| `AccountsTab(QWidget)` | ④ | 账号增删改、启动登录、检测登录、生成快捷方式 |
| `SettingsTab(QWidget)` | ⑤ | AI 模型 master-detail 管理 + 响应式三列设置表单 + 角色/生成参数/路径/端口配置 + Shopee 更新安全 |
| `TaskTableModel(QAbstractTableModel)` | ①②③ | 任务表格数据模型,供 `QTableView` 使用 |
| `BaseWorker(QObject)` | 后台 | 定义 `progress/log/row_updated/failed/finished/cancelled` signals |
| `ApplyWorker(BaseWorker)` | ③ | 账号就绪预检、预览本轮更新、按每批最大条数分批、按账号并行或串行调用 `editor.apply_task(..., close_success_tab=...)`、逐条 `set_applied()`,失败继续,写运行日志 |
| `ApplyWorker(BaseWorker)` | ③ | 账号就绪预检、检查本轮更新、按每批最大条数分批、按账号并行或串行调用 `editor.apply_task(..., close_success_tab=...)`、逐条 `set_applied()`,失败继续,写运行日志 |
| `AIModelTestWorker(BaseWorker)` | ⑤ | 后台调用 `appconfig.test_ai_model()` 测试模型连接 |
| `WriteBackWorker(BaseWorker)` | ①③ | ①回写旧字段;③回写新标题/新封面/更新状态 |