Files
cmautobuy/docs/task/32-client-执行采集任务并提交-admin.md
T
chengmaandClaude Opus 5 4022feee43 docs: 记录 #31 #32 验收通过
两份归档都由实现方写好,本次只更新状态。

#31 遵守了 #20 立的「迁移只追加」规则:v4 用 ALTER TABLE 加 shop_name,
v1/v2 原文未动,CheckSchema 也覆盖了新列。

#32 的 skus 是颜色×尺码的笛卡尔积并带 price_observed_at,
与 #31 的 price_granularity 对得上——两侧独立得出同一结论。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 10:19:11 +08:00

5.1 KiB
Raw Blame History

32 Client 执行采集任务并提交 Admin

  • 类型:需求(自动化执行、跨模块联调)
  • 父级大工单:#1
  • 所属 MVP / 版本:#2 / MVP
  • 状态:验收通过
  • 日期:2026-08-07
  • Gitea 工单:#32

背景与目标

#30 已能领取采集任务,但任务只停留在本地 claimed。本任务接通单条只读采集闭环:本地任务优先、操作当前 Android 设备采集商品、结果先写 SQLite 和 Outbox、最后幂等提交 Admin。

本任务只浏览商品页、展开规格和选择颜色,不包含加购、下单或付款。

最终方案

  • 新增 CollectTaskService,一次点击只处理一条工作:先补交 Outbox,再执行最早的本地 claimed/retry_wait,最后才领取新任务。
  • 采集仍复用并补全现有 PddCollectService,没有再拆一个重复的页面解析模块。
  • 任务执行继续使用 QObject + moveToThread;Worker 不访问 QWidget,结果通过信号回到主线程。
  • 启动时把采集任务残留的 running 恢复为 retry_wait,把 Outbox 的 sending 恢复为 pending。
  • 采集成功时在同一事务保存 pdd_data、运行记录和完整 Outbox 请求;只有 Admin 返回 accepted: true 才改为 succeeded。
  • Admin 暂时不可用时保留 result_pending 和原幂等键,下次只重发已保存请求,不重新操作手机。
  • HTTP Gateway 实现 /result 和 /failure,携带 X-Client-Id、X-Request-Id 和 Idempotency-Key。
  • pdd_data 使用 Admin 已确认的顶层结构,price_granularity 固定为 color。
  • 每个可用颜色只点击一次读取实付价和划线价;尺码只读取、不点击,再展开颜色×尺码笛卡尔积。
  • 每条 SKU 保存 price_observed_at,说明该价格实际在哪个已选组合下读取。
  • 标题按真实控件 bounds 拼接;规格名优先使用 content-desc,仍截断时整单失败。
  • 店铺名允许为空;运行时把相关无障碍 XML 写到 data/artifacts/,结果只保存本地路径和 SHA-256,不上传文件。
  • 整单采集上限为 10 分钟;登录、验证码、截断数据、设备断开和超时都有稳定错误代码。

改了哪些

  • client/src/collect_task_service.py:单任务领取、采集、Outbox 和提交编排。
  • client/src/pdd_collect_service.py:真实标题、规格、颜色价格、划线价、总超时、诊断文件和断线处理。
  • client/src/task_repository.py / task_models.py:运行记录、结果与失败事务、Outbox 状态和启动恢复。
  • client/src/http_admin_gateway.py:实现结果与失败 HTTP 提交。
  • client/src/pdd_ui_event.py:把“获取任务”接到后台采集闭环并反馈状态。
  • client/image_xml/737116531267_*.xml:真机导出的回归固件。
  • client/test/:增加采集编排、Outbox、HTTP 提交和真实 XML 测试。
  • docs/client/02、04、05:同步当前架构、接口和单任务界面状态。

验收结果

验收标准 结果
真实 XML 拼出完整标题 通过
真实规格 XML 枚举 8 个完整颜色名 通过
实付价 470 分、划线价 1990 分 通过
颜色逐个采价,尺码只读,输出完整笛卡尔积 通过(自动测试)
每条 SKU 有 price_observed_at 通过
price_granularity = color 通过
结果与 Outbox 同事务,成功确认后才完成 通过
Admin 超时只重发、不重采 通过
HTTP 提交带 Client ID 和幂等键 通过
10 分钟总超时及稳定失败代码 通过
工作线程不访问 QWidget 通过
不含加购、下单、付款代码 通过
真机完整采集并提交真实 Admin 未通过:见测试说明

测试

执行命令:

# client 目录
C:/Python310/python.exe -m unittest discover -s test -p 'test_*.py' -q

# 界面离屏冒烟
$env:QT_QPA_PLATFORM='offscreen'
C:/Python310/python.exe -c "from src.ui_main import MainWindow; from PyQt5.QtWidgets import QApplication; app=QApplication([]); w=MainWindow(); print('OK'); w.close(); app.quit()"

# admin 目录
go test ./...
  • Client 全量 143 项测试通过。
  • Admin 全量 Go 测试通过。
  • 界面离屏启动和关闭通过,输出 OK。
  • 真机只读验证使用 USB 设备 R28M30T2JWE,在进入规格面板前设备从 ADB 列表消失,uiautomator2 返回 device not found。验证过程中没有执行加购、下单或付款;代码随后补充并测试了 DEVICE_DISCONNECTED 分类。
  • 没验证到的部分:尚未完成真机的全部颜色横向扫描、屏外尺码纵向扫描以及向真实 Admin 提交后的页面状态和规格表验收;原因是设备断开且本机 Admin 当时未运行。Windows 高对比度和 200% 缩放也未手工验证。

遗留问题

  • 重新连接稳定的 Android 设备并启动 Admin 后,需要按工单的真机联调步骤完成最终验收。
  • 当前底部状态显示整条任务的启动、完成或错误,尚未细分显示“第几个颜色”等逐步进度;不影响数据链路。

相关提交

  • 2d1fdbe feat: 执行并提交 PDD 采集任务 (#32)