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

96 lines
8.8 KiB
Markdown
Raw Normal View History

---
id: T-628
title: 采集当前记录阶段与单条耗时指示器
phase: 2
deps: [T-620]
status: DONE
created: 2026-07-14
---
## 问题 / 背景
①「导入采集」处理商品详情页时,打开页面、等待加载和下载封面都可能持续较长时间。当前页面只有静态的「批次进度」汇总、底部状态栏和运行日志,用户在一条记录长时间未结束时难以判断采集是否仍在执行。
已核实的现状:
- `collectBatchProgressOverview` 是根据 SQLite 任务快照生成的汇总 label,`refresh_tasks()` 时会整体重写文本,本身没有运行计时语义。
- `CollectWorker.progress` 主要在一条记录结束后发出,不能反映当前记录正在执行哪个步骤。
- `row_updated(status=running)` 在单条记录登录状态检查之后才发出;若以它作为计时起点,会漏掉前置检查和本条登录检查耗时。
- `editor.collect()` 已有 `open_product / wait_ready / read_title / read_cover / download_cover` 等步骤回调,可作为结构化阶段来源。
顾客希望在「批次进度」同行右侧增加动态计时,并在每条记录开始时重置,让用户能看到当前采集任务仍处于运行状态。
计时数字持续变化只能证明 GUI 计时器仍在运行,不能证明 CDP 一定在继续推进。因此本任务把需求优化为「**当前阶段 + 当前记录耗时**」指示器:阶段显示最后一次已知进展,秒数显示该记录已经等待多久,不向用户承诺任务一定健康。
## 方案
### 1. 批次进度同行增加独立运行指示器
- 在现有「批次进度」右侧增加独立 label,不把计时字符串拼入 `batch_progress_label`,避免 `refresh_tasks()` 重写批次汇总时覆盖计时内容。
- 两个 label 放入同一水平布局:批次汇总占可伸缩区域,运行指示器靠右并按最长合理文案预留稳定宽度,秒数变化不得造成布局抖动。
- 运行中推荐文案:`正在采集 3/20 · 等待商品页加载 · 本条 00:18`。
- 窗口空间不足时,允许批次汇总自然换行;运行状态、序号和时间必须保持可见,完整商品 ID / 账号可放在中文 tooltip 中,不在紧凑 label 内强行堆叠。
- 不使用纯装饰性转圈或闪烁动画;每秒变化的计时和明确的「正在采集」文字即为运行反馈。
### 2. 使用结构化 Worker 活动信号
- 为 `CollectWorker` 增加专用结构化活动信号,例如 `activity: Signal(dict)`;不得通过解析用户日志文本推断当前任务或步骤。
- 活动 payload 至少支持:
- `preflight_started`:开始检查账号和 Chrome;
- `task_started`:一条 eligible 记录开始处理;
- `task_step`:当前记录进入新步骤;
- `task_finished`:当前记录成功、失败或略过。
- payload 使用稳定字段,如 `state / index / total / task_id / item_id / alias / step / result`,不得把账号密码、Cookie、token 或未脱敏异常放入信号。
- `task_started` 应在进入每条记录处理循环后尽早发出,覆盖本条账号匹配、登录检查和后续采集耗时;不得等到 `db.mark_running()` 后才开始计时。
- `editor.collect()` 的步骤回调继续用于日志,同时把步骤作为结构化 `task_step` 发给 GUI。阶段映射集中维护并全部显示中文,至少覆盖:检查账号、打开商品页、等待商品页加载、读取标题、读取封面、下载封面、保存采集结果。
- 结构化活动信号只增加可观察性,不改变原有 `progress / row_updated / log / failed / finished / cancelled` 的业务语义。
- 本轮正常完成、停止和前置阻断继续以现有 `finished / cancelled` 信号及 summary 中的 `blocked` 字段为唯一终态来源,不在 `activity` 中复制一套终态协议。
### 3. 计时口径与生命周期
- GUI 使用父对象为 `CollectTab` 的 `QTimer`,每 1000ms 刷新一次;真实耗时通过 `time.monotonic()` 计算,不通过简单 `seconds += 1` 累加。
- 点击「采集旧标题/旧封面」并成功创建 Worker 后,记录批次开始时间,显示 `正在检查账号 · 00:00`。
- 收到 `task_started` 时重置当前记录起点并开始显示 `本条 00:00`;收到 `task_step` 只更新阶段文字,不重置本条计时。
- 下一条记录开始时重新从 `00:00` 计时;成功、失败或略过可短暂冻结本条耗时,随后由下一条 `task_started` 覆盖。
- 用户点击「停止」后,指示器立即改为 `正在停止 · 本条 00:18`,在当前不可中断步骤退出前继续显示等待时间;收到 `cancelled` 后再冻结为终态,不能提前伪装成已经停止。
- 正常完成、前置检查阻断、用户停止、Worker 异常及线程释放路径都必须停止 QTimer、清理当前任务引用并冻结最终文字,不能在采集结束后继续走秒。
- 本轮终态文案应区分:
- 正常完成:`采集完成 · 总用时 08:32`;
- 用户停止:`采集已停止 · 总用时 03:10`;
- 前置阻断:`采集未开始 · 检查未通过`;
- 异常结束:`采集已结束 · 请查看运行日志`。
- 计时只用于本次 GUI 显示,不写入 SQLite 或 Excel;现有逐条诊断日志中的 `elapsed_ms` 保持不变。
### 4. 状态语义与视觉边界
- 运行中使用现有信息色/进行中色,并保留「正在采集」文字,不能只靠颜色表达状态。
- 完成、停止、阻断和异常沿用项目既有状态语义色;不增加“卡死”自动判定。页面加载和图片下载可能合法地持续较久,仅凭固定秒数变色会产生误报。
- 用户看到持续增加的本条耗时,应理解为“该记录尚未结束”;阶段长时间不变化时可据此决定查看日志或停止,但 UI 不显示“运行正常”等未经验证的结论。
## 验收要点
- 有批次任务但未运行时,批次汇总显示正常,右侧运行指示器不误报正在采集。
- 点击采集后,在首条记录开始前显示中文前置检查状态和持续计时;GUI 保持可响应。
- 第一条记录开始时显示 `1/N` 并从 `00:00` 计时;步骤变化时阶段文字变化、本条计时不重置。
- 第二条记录开始时显示 `2/N`,本条时间重新从 `00:00` 开始。
- 成功、失败和略过记录都能进入正确的下一条计时;不会因为某条失败而停止整轮指示器。
- 正常完成、前置阻断、用户停止、Worker 异常和线程收尾后计时全部停止,等待数秒后界面秒数不再变化。
- 批次进度汇总刷新不会覆盖运行指示器;运行指示器更新也不会改变批次阶段统计。
- 运行状态 label 宽度稳定,在最小支持窗口及 Windows 100% / 125% / 150% 显示缩放下无重叠和明显布局跳动。
- 单测使用可控 `time.monotonic()` 或注入时间验证,不通过真实 `sleep()` 拉长测试。
- 自动验证:`py -3.10 -m unittest discover -s tests`、`python -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check` 全绿。
## 边界(不改什么)
- 不修改 CDP 选择器、商品页打开/关闭策略、Chrome 前后台切换、登录判定或采集重试策略。
- 不修改任务 `stage/status`、SQLite schema、Excel schema、回写流程或批次统计口径。
- 不把计时器放进 Worker 线程,不让 GUI 每秒查询 SQLite,也不把每秒 tick 写入运行日志。
- 不改变②AI生成、③更新蝦皮及⑥商品套图的计时显示。
## 执行记录
- 2026-07-14:`CollectWorker` 新增独立 `activity` 结构化信号,在前置检查、每条记录开始、采集步骤变化及单条结束时发送稳定 payload;每条记录进入循环即发 `task_started`,登录检查和 `editor.collect()` 步骤只更新阶段,不改变原有进度、日志、状态写库和终态信号。
- 2026-07-14:①「导入采集」在批次进度同行右侧新增固定宽度运行指示器,显示前置检查、`N/总数`、当前中文阶段和本条 `MM:SS`;基于 `time.monotonic()` 每秒刷新,每条开始重置,步骤变化不重置,停止请求期间继续显示实际等待,完成、阻断、停止和异常后冻结中文终态。
- 2026-07-14:新增 Worker 活动事件顺序和 GUI 计时生命周期测试,覆盖三条记录的成功/略过事件、步骤不重置、下一条归零、停止等待、终态冻结、小时格式及中文 tooltip;用户可见终态不展示底层英文异常。
- 验证:当前工作区全套 GUI 测试除任务开始前已存在的默认封面提示词 `papa1.txt` 改名造成的 2 项旧断言外,采集相关测试全部通过;在仅覆盖 T-628 四个文件的隔离 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`(461 项通过)及 `git diff --check`,结果全部通过。