feat: 完成T-504更新执行增强

- 新增 dry-run、多账号并行和最大并行账号数设置

- 增加 run_logs/run_log_events 运行日志表及读写接口

- ApplyWorker 支持 dry-run 预览、按账号并行、端口冲突阻断和日志展示

- 补充 DB/GUI 单元测试并同步任务、架构、API、路由和当前状态文档

验证: python -m compileall app main.py tests; python -m unittest discover -s tests
This commit is contained in:
chengma
2026-06-29 10:25:09 +08:00
parent 01e319cad8
commit 0e0ed193b6
17 changed files with 1071 additions and 149 deletions
+43 -4
View File
@@ -114,7 +114,10 @@ imported → collected → generated → applied
"allow_real_submit": false,
"allow_cover_update": false,
"max_items_per_run": 1,
"close_success_tab": false
"close_success_tab": false,
"dry_run": false,
"parallel_accounts": false,
"max_parallel_accounts": 2
}
}
```
@@ -134,8 +137,11 @@ imported → collected → generated → applied
- `allow_cover_update`:是否允许本次更新包含新封面路径;默认 `false`,未开启时只允许标题更新任务。
- `max_items_per_run`:单次允许更新的最大任务数;默认 `1`,当前筛选结果超过即阻断。
- `close_success_tab`:成功提交后是否关闭本轮程序自动新开的商品编辑页;默认 `false`。只关闭 `open_product()` 本轮新建且已提交成功的 tab,失败任务和用户原本打开的 tab 保留。
- `dry_run`:只预览当前筛选任务,写运行日志,不打开 Shopee、不点击「更新」、不改任务状态;默认 `false`。
- `parallel_accounts`:是否按账号并行执行③真实更新;默认 `false`,即保持串行。
- `max_parallel_accounts`:最多同时执行的账号数;同一账号内仍按任务串行,默认 `2`。
该段不是 V2 dry-run,也不是替代 ③ 确认弹窗的常驻授权;③ 仍必须先通过安全开关检查,再弹窗确认,用户点是后才真实提交。
该段不是替代 ③ 确认弹窗的常驻授权;③ 仍必须弹窗确认,用户点是后才执行。`dry_run=true` 时不会真实提交;`dry_run=false` 时仍必须先通过真实更新安全开关检查。
### 5.1b AI 模型清单 `config/ai_models.json`
@@ -239,6 +245,37 @@ CREATE INDEX idx_tasks_alias ON tasks(alias);
CREATE INDEX idx_tasks_item ON tasks(item_id);
```
运行日志(T-504):
```sql
CREATE TABLE run_logs (
id INTEGER PRIMARY KEY,
run_type TEXT NOT NULL, -- apply 等
dry_run INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'running', -- running/done/blocked/cancelled
total INTEGER NOT NULL DEFAULT 0,
done INTEGER NOT NULL DEFAULT 0,
success_count INTEGER NOT NULL DEFAULT 0,
skipped_count INTEGER NOT NULL DEFAULT 0,
failed_count INTEGER NOT NULL DEFAULT 0,
options_json TEXT,
summary_json TEXT,
started_at TEXT NOT NULL,
finished_at TEXT
);
CREATE TABLE run_log_events (
id INTEGER PRIMARY KEY,
run_id INTEGER NOT NULL REFERENCES run_logs(id) ON DELETE CASCADE,
task_id INTEGER,
alias TEXT,
item_id TEXT,
level TEXT NOT NULL DEFAULT 'info',
message TEXT NOT NULL,
created_at TEXT NOT NULL
);
```
关键事实:
- `alias` 是账号↔任务**唯一关联键**;找不到账号 → stage=skipped,error=别名未匹配,最后弹窗汇总。
@@ -333,11 +370,13 @@ images/<slug>/<item_id>_new.<ext> # AI 生成的新封面
### 6.3 应用更新(③ Tab)
- ③ 顶部筛选确定本次作用范围;点击「开始更新」后弹窗展示筛选条件、任务数量和“将提交线上”的风险提示。
- 弹确认前先读取 `config.json` 的 `shopee_update`:未开启 `allow_real_submit`、任务数超过 `max_items_per_run`、包含非 `test_item_id` 商品、或任务含新封面但未开启 `allow_cover_update` 时,直接弹警告阻断,不创建 `ApplyWorker`。
- 弹确认前先读取 `config.json` 的 `shopee_update`:`dry_run=false` 且未开启 `allow_real_submit`、任务数超过 `max_items_per_run`、包含非 `test_item_id` 商品、或任务含新封面但未开启 `allow_cover_update` 时,直接弹警告阻断,不创建 `ApplyWorker`。`dry_run=true` 时只预览,不受真实提交开关限制。
- 用户点「是/确认」才开始批量更新;点「否/取消」不执行、不改库。
- 对确认后的**已生成(generated)任务**:`open_product` → `change_title(new_title)`(如有)→ `replace_cover(new_cover_path)`(如有)→ `click_update` 提交。
- 满 9 张封面时,T-502 的删除流程必须先确认该任务已有本地旧封面备份:`old_cover_path` 非空且文件存在。缺失备份时不删除线上第一张图,直接返回明确错误,要求先回到①采集旧封面或修复本地备份。
- 串行、单条失败继续;每条立即写 SQLite;全部完成回写 Excel(新字段+状态)+ 弹窗汇总。
- 默认串行、单条失败继续;⑤ 开启 `parallel_accounts` 后按账号分组并行,不同账号可同时跑,同一账号内仍串行。真实更新前检查本轮账号 `debug_port`,端口冲突直接阻断。
- `dry_run=true` 时只写 `run_logs/run_log_events` 和弹窗/状态栏预览,不调用 `editor.apply_task()`,不做账号登录预检,不写任务状态,不回写 Excel。
- 真实更新每条立即写 SQLite;全部完成回写 Excel(新字段+状态)+ 弹窗汇总。真实更新与 dry-run 都写运行日志,日志 payload 走脱敏工具。
- 若 `close_success_tab=true`,且商品页是本轮程序自动新建、并已成功提交,则提交后关闭该商品编辑页;失败任务和复用的用户已有 tab 不关闭。
### 6.4 登录检测