Client:执行采集任务,把结果提交给 Admin #32

Closed
opened 2026-08-07 16:53:15 +08:00 by ila · 4 comments
Owner

基本信息

  • 类型:需求(自动化执行,首次真正操作手机)
  • 父级大工单:#1
  • 所属 MVP / 版本:#2 / MVP
  • 前置:#30(领取采集任务)、#31(Admin 侧店铺名与粒度字段)
  • 关联:Admin #16(skus_json 结构)、#24(采集超时 15 分钟)

要解决什么

#30 做完后 Client 能领到采集任务,但领了不会做任何事——
任务会一直停在本地,Admin 那边永远等不到结果。

本工单把这条链路接通:领到任务 → 派给安卓设备 → 打开商品页 →
采集标题、店铺名、所有颜色、所有尺码、各颜色价格 → 提交给 Admin。

这是「获取任务」第一次真正操作手机

client/AGENTS.md:「『获取任务』会真的去操作手机、可能下单」。
在 #30 之前这个按钮只是发 HTTP,本工单之后它会真的驱动设备。

[必须] 本工单只做采集。采集是只读操作——打开商品页、点规格、读文字,
不加购、不下单、不付款。真实下单开关保持关闭。

已经确认的事实(读你的 XML 样本得出,不是猜的)

样本:client/image_xml/737116531267_home.xml 和 _size.xml。

① 价格只有当前选中组合的那一个

整个规格面板只有一处价格(折后¥4.7 和划线价 ¥19.9),
8 个颜色选项旁边没有各自的价格。

→ 必须逐个点击颜色才能拿到各自价格。

② 标题被拆成多个文本节点

【店铺热销】冰丝吊带裙女夏季薄款内搭连
衣裙宽松a字裙中长款打底裙衬裙纯色

→ 必须按位置拼接,取单个节点会得到半截标题。

③ 选项会被屏幕边缘截断

浅蓝色中长款 的 bounds 是 [1044,1561][1080,1669]——宽度只有 36px,
被右边缘切掉。

→ 直接读 text 会拿到不完整的名字,进而算出错的 OptionKey,
规格映射会静默失效或匹配到错的规格。

④ 尺码维度在屏幕外

已选: 黑色中长款 2XL(140-160斤) 证明有尺码维度,
但 8 个颜色已占满屏幕,尺码要滚动才可见。

⑤ 「已选:」行是判断当前状态的唯一可靠锚点

做什么 / 不做什么

做:

  1. 把领到的采集任务派给当前选中的安卓设备
  2. 打开商品页,采集标题、店铺名
  3. 打开规格面板,枚举全部颜色和尺码
  4. 逐个点击颜色读价格(不遍历尺码)
  5. 组装成 Admin 契约的 pdd_data,先写本地库再提交
  6. 界面显示进度和结果

不做(各自独立工单):

  • 不做采购/下单,一行相关代码都不要写
  • 不做循环领取(仍是点一次做一个)
  • 不做断点续采(中途失败就整单重来)
  • 不改 settings_ui*.py / android_device_service.py(另一会话在改)

怎么做

价格按颜色采样,但输出笛卡尔积

已定案:颜色逐个点击读价(约 8 次),尺码只读列表不点击。

但 skus[] 必须输出颜色×尺码的完整组合,不能只输出 8 个颜色。

[必须] 理由:后续「蝦皮 SKU ↔ PDD 规格」匹配是按完整组合做的
(Admin 的 OptionKey() 对整个 options 求值)。只输出颜色的话,
蝦皮那个带尺码的 SKU 永远匹配不上,规格匹配功能直接废掉。

颜色: [黑色中长款, 白色中长款, ...]   ← 逐个点击,各读一次价格
尺码: [M, L, XL, 2XL, ...]           ← 只读文字,不点击

skus[] = 笛卡尔积,每个组合的 price_cent 用它所属颜色的价格

[必须] 每条 SKU 带 price_observed_at,记录读到这个价格时实际选中的组合。
同颜色下只有一条是实测的,其余是推断的。

[必须] 顶层 price_granularity: "color"。

契约详见 #31。

选项名被截断怎么办

[必须] 不得直接用被截断节点的 text。

按下面顺序取,取到即止:

  1. 节点的 content-desc(无障碍文本,通常是完整的)
  2. 横向滚动容器后重新 dump,取完整的 text
  3. 都拿不到 → 整单失败,error.code = SKU_NAME_TRUNCATED

[必须] 第 3 条是失败,不是"尽力而为"。名字不全会算出错的 OptionKey,
规格映射静默失效——或者更糟,撞上另一个规格,将来按它下单就是买错东西。
宁可这次采不到,也不能存一份看起来正常、实际是错的数据。

标题拼接

[必须] 按 bounds 的 y 坐标排序后拼接同一区域的多个文本节点。

[必须] 拼完要有长度下限校验(比如 < 6 个字视为没采到,整单失败)。
半截标题存进去,操作员核对「是不是我要的商品」时会被误导。

店铺名:采不到必须给证据

已定案:并入本工单,采不到留空。

[必须] 但采不到时必须在报告里给出证据——说明在哪一屏找过、
用什么定位方式找的,并导出一份该屏的 XML 到 client/image_xml/。

理由:「尽力而为」没法客观验收,事后分不清是真的拿不到还是没写好。
有 XML 就能判断。

[建议] 已知样本里主页没有店铺名,只有底部导航的「店铺」按钮
(bounds [54,2258][132,2311])。可能需要向下滚动。

不得在主线程做任何这些事

[必须] 根 AGENTS.md 红线。整个采集流程放 QObject + moveToThread 的
Worker,模板见 02 架构 §5.1。
不得用 QThread 子类、QRunnable、threading。

[必须] 一个设备同一时间只由一个工作线程控制,不跨线程共享 uiautomator2 Device。

[必须] 窗口关闭时按 §5.2 断开连接。

[必须] 必须处理:启动、运行、安全停止、成功、失败、取消、超时、
设备断开、迟到结果(client/AGENTS.md 明确列了这 9 种)。

先写本地库,再提交(Outbox)

[必须] 顺序:采集完成 → 写本地 SQLite → 通过 Outbox 提交 Admin。
不得采完直接发 HTTP。

[必须] 提交带 Idempotency-Key,重试只重发、不重采。
重采会在拼多多上多点几十次,而且两次结果可能不一致。

超时要卡在 15 分钟以内

[必须] 整单采集超时上限设为 10 分钟,超了主动失败并提交
submit_failure。

理由:Admin 侧 #24 规定 collecting 超过 15 分钟就允许别人重新建任务。
Client 这边如果拖到 15 分钟以上,会出现「Admin 已经放行、又建了一个新任务,
而这台还在采」的重叠局面。留 5 分钟余量。

失败要能定位

[必须] 失败时提交 submit_failure,error.code 用可枚举的常量
(如 SKU_NAME_TRUNCATED / SKU_PANEL_NOT_FOUND / TITLE_TOO_SHORT /
DEVICE_DISCONNECTED / COLLECT_TIMEOUT),不要只发一句自由文本。

[必须] diagnostics 里带本地 XML/截图的路径引用,
按已定案的 Artifact 策略:只报本地引用、不上传文件
(docs/client/04 §10)。

预计修改文件

文件 改什么
client/src/pdd_collect_service.py 新建:采集流程编排
client/src/pdd_page_parser.py 新建:页面解析(标题拼接、选项枚举、价格读取、截断处理)
client/src/pdd_ui_event.py 领到任务后启动采集 Worker,显示进度
client/src/http_admin_gateway.py 实现 submit_result / submit_failure
client/src/task_repository.py 采集结果落本地库、Outbox 状态
client/test/test_pdd_page_parser.py 新建:用 client/image_xml/ 的真实 XML 做固件
client/test/test_pdd_collect_service.py 新建:流程、失败分支、超时
docs/client/* 契约、界面、质量文档同步

验收标准

解析(用真实 XML 固件,不许自己编)

  • 737116531267_home.xml 能拼出完整标题,不是半截
  • 737116531267_size.xml 能枚举出全部 8 个颜色
  • 被截断的 浅蓝色中长款 能取到完整名字,取不到则整单失败
  • 能读出实付价 4.7 和划线价 19.9,price_cent = 470
  • 标题过短时整单失败,不提交半截标题

采集流程

  • 领到任务后自动开始采集,界面有进度提示
  • skus[] 是颜色×尺码的完整组合,不是只有颜色
  • 每条 SKU 有 price_observed_at,同颜色下只有一条与之相同
  • 顶层 price_granularity = "color"
  • 店铺名采到则提交;采不到留空并在报告里附 XML 证据
  • 超过 10 分钟主动失败并提交 submit_failure

红线

  • 没有任何加购/下单/付款相关代码
  • 主线程内无 uiautomator2 / ADB / HTTP 调用
  • 用 QObject + moveToThread
  • 窗口关闭后迟到结果不访问已销毁控件
  • 9 种状态(启动/运行/停止/成功/失败/取消/超时/断开/迟到)都有处理

提交

  • 先写本地库再提交
  • 带 Idempotency-Key,重试只重发不重采
  • 失败用可枚举 error.code,不是自由文本
  • diagnostics 只带本地引用,不上传文件

联调

  • Admin 的 PDD 页该商品从「采集中」变「已采集」,规格数正确
  • 弹窗里能看到颜色×尺码的完整规格表和价格
  • Admin 采集采购页该任务变「成功」

怎么验证

1. 单元测试(不需要设备)

cd D:\chengma\cmautobuy\client
C:/Python310/python.exe -m pytest test/ -q

[必须] 解析相关的测试必须用 client/image_xml/ 里的真实 XML。
自己编一份字段恰好对得上的固件,等于没测——本工单已经证明真实页面
有截断、有拆分节点这些编不出来的情况。

2. 界面离屏冒烟

$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'); app.quit()"
Remove-Item Env:QT_QPA_PLATFORM

3. 真机联调

[必须] 这一步必须单独标记为真机测试,并说明没有执行任何下单操作。

起 Admin → PDD 页建商品建采集任务 → Client 连设备 → 点「获取任务」。

期望并贴出证据:

  • Admin /tasks 该任务:待分配 → 已领取 → 成功
  • Admin /pdd 该商品:采集中 → 已采集,规格数 = 颜色数 × 尺码数
  • 弹窗里规格表完整,价格正确,有粒度提示

风险和回退

风险 应对
选项名截断 → 算出错的 OptionKey → 将来买错东西 取不到完整名字就整单失败,已列为验收项
半截标题误导操作员 长度下限校验,整单失败
采集超过 15 分钟与 Admin 超时重叠 本地 10 分钟主动失败,留 5 分钟余量
重试导致重复采集 Outbox + Idempotency-Key,只重发不重采
顺手写了下单代码 已列为红线验收项;评审时重点看
大码加价导致推断价偏低 #31 的 price_granularity 和 price_observed_at 标注,下单时价格保护据此判断

回退:git revert。采集是只读操作,最坏情况是 Admin 上几条任务失败,
重新建一个即可。不会产生订单。

## 基本信息 - 类型:需求(自动化执行,**首次真正操作手机**) - 父级大工单:#1 - 所属 MVP / 版本:#2 / MVP - 前置:**#30**(领取采集任务)、**#31**(Admin 侧店铺名与粒度字段) - 关联:Admin #16(`skus_json` 结构)、#24(采集超时 15 分钟) ## 要解决什么 #30 做完后 Client 能领到采集任务,但领了**不会做任何事**—— 任务会一直停在本地,Admin 那边永远等不到结果。 本工单把这条链路接通:领到任务 → 派给安卓设备 → 打开商品页 → 采集标题、店铺名、所有颜色、所有尺码、各颜色价格 → 提交给 Admin。 ### 这是「获取任务」第一次真正操作手机 `client/AGENTS.md`:「『获取任务』会真的去操作手机、可能下单」。 在 #30 之前这个按钮只是发 HTTP,**本工单之后它会真的驱动设备**。 `[必须]` 本工单**只做采集**。采集是只读操作——打开商品页、点规格、读文字, 不加购、不下单、不付款。真实下单开关保持关闭。 ## 已经确认的事实(读你的 XML 样本得出,不是猜的) 样本:`client/image_xml/737116531267_home.xml` 和 `_size.xml`。 ### ① 价格只有当前选中组合的那一个 整个规格面板只有一处价格(`折后¥4.7` 和划线价 `¥19.9`), 8 个颜色选项**旁边没有各自的价格**。 → 必须逐个点击颜色才能拿到各自价格。 ### ② 标题被拆成多个文本节点 ``` 【店铺热销】冰丝吊带裙女夏季薄款内搭连 衣裙宽松a字裙中长款打底裙衬裙纯色 ``` → 必须按位置拼接,取单个节点会得到半截标题。 ### ③ 选项会被屏幕边缘截断 `浅蓝色中长款` 的 bounds 是 `[1044,1561][1080,1669]`——**宽度只有 36px**, 被右边缘切掉。 → 直接读 `text` 会拿到不完整的名字,进而算出错的 `OptionKey`, **规格映射会静默失效或匹配到错的规格**。 ### ④ 尺码维度在屏幕外 `已选: 黑色中长款 2XL(140-160斤)` 证明有尺码维度, 但 8 个颜色已占满屏幕,尺码要滚动才可见。 ### ⑤ 「已选:」行是判断当前状态的唯一可靠锚点 ## 做什么 / 不做什么 做: 1. 把领到的采集任务派给当前选中的安卓设备 2. 打开商品页,采集标题、店铺名 3. 打开规格面板,枚举全部颜色和尺码 4. **逐个点击颜色读价格**(不遍历尺码) 5. 组装成 Admin 契约的 `pdd_data`,先写本地库再提交 6. 界面显示进度和结果 不做(各自独立工单): - **不做采购/下单**,一行相关代码都不要写 - 不做循环领取(仍是点一次做一个) - 不做断点续采(中途失败就整单重来) - 不改 `settings_ui*.py` / `android_device_service.py`(另一会话在改) ## 怎么做 ### 价格按颜色采样,但输出笛卡尔积 **已定案:颜色逐个点击读价(约 8 次),尺码只读列表不点击。** 但 `skus[]` **必须输出颜色×尺码的完整组合**,不能只输出 8 个颜色。 `[必须]` 理由:后续「蝦皮 SKU ↔ PDD 规格」匹配是按**完整组合**做的 (Admin 的 `OptionKey()` 对整个 `options` 求值)。只输出颜色的话, 蝦皮那个带尺码的 SKU **永远匹配不上**,规格匹配功能直接废掉。 ```text 颜色: [黑色中长款, 白色中长款, ...] ← 逐个点击,各读一次价格 尺码: [M, L, XL, 2XL, ...] ← 只读文字,不点击 skus[] = 笛卡尔积,每个组合的 price_cent 用它所属颜色的价格 ``` `[必须]` 每条 SKU 带 `price_observed_at`,记录读到这个价格时**实际选中的组合**。 同颜色下只有一条是实测的,其余是推断的。 `[必须]` 顶层 `price_granularity: "color"`。 契约详见 #31。 ### 选项名被截断怎么办 `[必须]` **不得直接用被截断节点的 `text`**。 按下面顺序取,取到即止: 1. 节点的 `content-desc`(无障碍文本,通常是完整的) 2. 横向滚动容器后重新 dump,取完整的 `text` 3. 都拿不到 → **整单失败**,`error.code = SKU_NAME_TRUNCATED` `[必须]` 第 3 条是**失败,不是"尽力而为"**。名字不全会算出错的 `OptionKey`, 规格映射静默失效——或者更糟,撞上另一个规格,**将来按它下单就是买错东西**。 宁可这次采不到,也不能存一份看起来正常、实际是错的数据。 ### 标题拼接 `[必须]` 按 bounds 的 y 坐标排序后拼接同一区域的多个文本节点。 `[必须]` 拼完要有长度下限校验(比如 < 6 个字视为没采到,整单失败)。 半截标题存进去,操作员核对「是不是我要的商品」时会被误导。 ### 店铺名:采不到必须给证据 已定案:并入本工单,采不到留空。 `[必须]` 但**采不到时必须在报告里给出证据**——说明在哪一屏找过、 用什么定位方式找的,并**导出一份该屏的 XML 到 `client/image_xml/`**。 理由:「尽力而为」没法客观验收,事后分不清是真的拿不到还是没写好。 有 XML 就能判断。 `[建议]` 已知样本里主页没有店铺名,只有底部导航的「店铺」按钮 (bounds `[54,2258][132,2311]`)。可能需要向下滚动。 ### 不得在主线程做任何这些事 `[必须]` 根 `AGENTS.md` 红线。整个采集流程放 `QObject` + `moveToThread` 的 Worker,模板见 [02 架构 §5.1](../docs/client/02-architecture.md)。 **不得**用 `QThread` 子类、`QRunnable`、`threading`。 `[必须]` 一个设备同一时间只由一个工作线程控制,不跨线程共享 uiautomator2 Device。 `[必须]` 窗口关闭时按 §5.2 断开连接。 `[必须]` 必须处理:启动、运行、安全停止、成功、失败、取消、超时、 设备断开、迟到结果(`client/AGENTS.md` 明确列了这 9 种)。 ### 先写本地库,再提交(Outbox) `[必须]` 顺序:采集完成 → 写本地 SQLite → 通过 Outbox 提交 Admin。 **不得**采完直接发 HTTP。 `[必须]` 提交带 `Idempotency-Key`,重试**只重发、不重采**。 重采会在拼多多上多点几十次,而且两次结果可能不一致。 ### 超时要卡在 15 分钟以内 `[必须]` 整单采集超时上限设为 **10 分钟**,超了主动失败并提交 `submit_failure`。 理由:Admin 侧 #24 规定 `collecting` 超过 **15 分钟**就允许别人重新建任务。 Client 这边如果拖到 15 分钟以上,会出现「Admin 已经放行、又建了一个新任务, 而这台还在采」的重叠局面。留 5 分钟余量。 ### 失败要能定位 `[必须]` 失败时提交 `submit_failure`,`error.code` 用可枚举的常量 (如 `SKU_NAME_TRUNCATED` / `SKU_PANEL_NOT_FOUND` / `TITLE_TOO_SHORT` / `DEVICE_DISCONNECTED` / `COLLECT_TIMEOUT`),不要只发一句自由文本。 `[必须]` `diagnostics` 里带**本地** XML/截图的路径引用, 按已定案的 Artifact 策略:**只报本地引用、不上传文件** (`docs/client/04` §10)。 ## 预计修改文件 | 文件 | 改什么 | |---|---| | `client/src/pdd_collect_service.py` | 新建:采集流程编排 | | `client/src/pdd_page_parser.py` | 新建:页面解析(标题拼接、选项枚举、价格读取、截断处理) | | `client/src/pdd_ui_event.py` | 领到任务后启动采集 Worker,显示进度 | | `client/src/http_admin_gateway.py` | 实现 `submit_result` / `submit_failure` | | `client/src/task_repository.py` | 采集结果落本地库、Outbox 状态 | | `client/test/test_pdd_page_parser.py` | 新建:**用 `client/image_xml/` 的真实 XML 做固件** | | `client/test/test_pdd_collect_service.py` | 新建:流程、失败分支、超时 | | `docs/client/*` | 契约、界面、质量文档同步 | ## 验收标准 **解析(用真实 XML 固件,不许自己编)** - [ ] `737116531267_home.xml` 能拼出完整标题,不是半截 - [ ] `737116531267_size.xml` 能枚举出全部 8 个颜色 - [ ] **被截断的 `浅蓝色中长款` 能取到完整名字**,取不到则整单失败 - [ ] 能读出实付价 `4.7` 和划线价 `19.9`,`price_cent = 470` - [ ] 标题过短时整单失败,不提交半截标题 **采集流程** - [ ] 领到任务后自动开始采集,界面有进度提示 - [ ] `skus[]` 是颜色×尺码的**完整组合**,不是只有颜色 - [ ] 每条 SKU 有 `price_observed_at`,同颜色下只有一条与之相同 - [ ] 顶层 `price_granularity = "color"` - [ ] 店铺名采到则提交;采不到留空**并在报告里附 XML 证据** - [ ] 超过 10 分钟主动失败并提交 `submit_failure` **红线** - [ ] **没有任何加购/下单/付款相关代码** - [ ] 主线程内无 uiautomator2 / ADB / HTTP 调用 - [ ] 用 `QObject` + `moveToThread` - [ ] 窗口关闭后迟到结果不访问已销毁控件 - [ ] 9 种状态(启动/运行/停止/成功/失败/取消/超时/断开/迟到)都有处理 **提交** - [ ] 先写本地库再提交 - [ ] 带 `Idempotency-Key`,重试只重发不重采 - [ ] 失败用可枚举 `error.code`,不是自由文本 - [ ] `diagnostics` 只带本地引用,不上传文件 **联调** - [ ] Admin 的 PDD 页该商品从「采集中」变「已采集」,规格数正确 - [ ] 弹窗里能看到颜色×尺码的完整规格表和价格 - [ ] Admin 采集采购页该任务变「成功」 ## 怎么验证 **1. 单元测试(不需要设备)** ```powershell cd D:\chengma\cmautobuy\client C:/Python310/python.exe -m pytest test/ -q ``` `[必须]` 解析相关的测试**必须用 `client/image_xml/` 里的真实 XML**。 自己编一份字段恰好对得上的固件,等于没测——本工单已经证明真实页面 有截断、有拆分节点这些编不出来的情况。 **2. 界面离屏冒烟** ```powershell $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'); app.quit()" Remove-Item Env:QT_QPA_PLATFORM ``` **3. 真机联调** `[必须]` 这一步**必须单独标记为真机测试**,并说明**没有执行任何下单操作**。 起 Admin → PDD 页建商品建采集任务 → Client 连设备 → 点「获取任务」。 期望并贴出证据: - Admin `/tasks` 该任务:待分配 → 已领取 → **成功** - Admin `/pdd` 该商品:采集中 → **已采集**,规格数 = 颜色数 × 尺码数 - 弹窗里规格表完整,价格正确,有粒度提示 ## 风险和回退 | 风险 | 应对 | |---|---| | 选项名截断 → 算出错的 OptionKey → **将来买错东西** | 取不到完整名字就整单失败,已列为验收项 | | 半截标题误导操作员 | 长度下限校验,整单失败 | | 采集超过 15 分钟与 Admin 超时重叠 | 本地 10 分钟主动失败,留 5 分钟余量 | | 重试导致重复采集 | Outbox + Idempotency-Key,只重发不重采 | | 顺手写了下单代码 | 已列为红线验收项;评审时重点看 | | 大码加价导致推断价偏低 | #31 的 `price_granularity` 和 `price_observed_at` 标注,下单时价格保护据此判断 | 回退:`git revert`。采集是只读操作,最坏情况是 Admin 上几条任务失败, 重新建一个即可。**不会产生订单。**
Author
Owner

补充执行与恢复要求(合并重复工单 #33 的有效内容):

  • 点击“获取任务”时,必须先查本地最早的 claimed 采集任务;存在则直接执行,不再向 Admin 领取。这样当前已经领取的任务不会被遗留。
  • 只有本地没有待执行任务时,才向 Admin 最多领取一条;领取成功必须先写 SQLite,再操作手机。
  • 采集成功后,pdd_data、执行记录和 Outbox 事件必须在同一个 SQLite 事务中落库。
  • 网络提交失败时保留 result_pending 和 Outbox,不得重新采集;恢复后只重发同一事件并复用持久化的 Idempotency-Key。
  • 程序启动时将遗留的 Outbox sending 恢复为 pending,并允许继续处理 claimed / result_pending。
  • Admin 接受结果后,Outbox 标记为已发送,本地任务改为 succeeded。
  • 可恢复网络错误进入等待重试;登录失效、验证码、规格不完整等进入人工处理或按契约提交结构化失败。
  • #32 的“不断点续采”仍然成立:采集过程本身中断后可以整单重来;已经完整采集并落库的结果不得重采。

以上作为 #32 的验收补充,不扩大到采购、下单或付款。

补充执行与恢复要求(合并重复工单 #33 的有效内容): - 点击“获取任务”时,必须先查本地最早的 `claimed` 采集任务;存在则直接执行,不再向 Admin 领取。这样当前已经领取的任务不会被遗留。 - 只有本地没有待执行任务时,才向 Admin 最多领取一条;领取成功必须先写 SQLite,再操作手机。 - 采集成功后,`pdd_data`、执行记录和 Outbox 事件必须在同一个 SQLite 事务中落库。 - 网络提交失败时保留 `result_pending` 和 Outbox,不得重新采集;恢复后只重发同一事件并复用持久化的 `Idempotency-Key`。 - 程序启动时将遗留的 Outbox `sending` 恢复为 `pending`,并允许继续处理 `claimed` / `result_pending`。 - Admin 接受结果后,Outbox 标记为已发送,本地任务改为 `succeeded`。 - 可恢复网络错误进入等待重试;登录失效、验证码、规格不完整等进入人工处理或按契约提交结构化失败。 - #32 的“不断点续采”仍然成立:采集过程本身中断后可以整单重来;已经完整采集并落库的结果不得重采。 以上作为 #32 的验收补充,不扩大到采购、下单或付款。
Author
Owner

实现进度(2026-08-07):已完成单条采集主链代码与自动化测试。当前包含:本地 claimed/retry_wait 优先、Outbox 优先补交、QObject 工作线程、HTTP result/failure 幂等提交、采集结果与 Outbox 同事务、Admin 超时只重发不重采、10 分钟总超时、真实 XML 的标题拼接/8 色/实付价与划线价测试、颜色逐个采价且尺码只读、截断规格名保护。尚在执行全量回归与真机只读联调;没有加入任何加购、下单或付款代码。

实现进度(2026-08-07):已完成单条采集主链代码与自动化测试。当前包含:本地 claimed/retry_wait 优先、Outbox 优先补交、QObject 工作线程、HTTP result/failure 幂等提交、采集结果与 Outbox 同事务、Admin 超时只重发不重采、10 分钟总超时、真实 XML 的标题拼接/8 色/实付价与划线价测试、颜色逐个采价且尺码只读、截断规格名保护。尚在执行全量回归与真机只读联调;没有加入任何加购、下单或付款代码。
Author
Owner

实现已提交,等待验收(工单保持 open)。\n\n- 功能提交:2d1fdbe eat: 执行并提交 PDD 采集任务 (#32)\n- 归档提交:51c2292 docs: 归档任务 #32\n- Client:143 项测试通过,离屏 UI 冒烟通过\n- Admin:go test ./... 通过\n- 真实 XML:完整标题、8 个颜色、470 分实付价、1990 分划线价均通过\n- 真机只读验证:USB 设备在进入规格面板前从 ADB 消失,触发 device not found;没有执行任何加购、下单或付款。已补充 DEVICE_DISCONNECTED 稳定分类。\n\n未完成的验收项:稳定真机上的全部颜色/尺码扫描,以及真实 Admin 接收后页面状态与规格表核对。详细记录见 docs/task/32-client-执行采集任务并提交-admin.md。

实现已提交,等待验收(工单保持 open)。\n\n- 功能提交:2d1fdbe eat: 执行并提交 PDD 采集任务 (#32)\n- 归档提交:51c2292 docs: 归档任务 #32\n- Client:143 项测试通过,离屏 UI 冒烟通过\n- Admin:go test ./... 通过\n- 真实 XML:完整标题、8 个颜色、470 分实付价、1990 分划线价均通过\n- 真机只读验证:USB 设备在进入规格面板前从 ADB 消失,触发 device not found;没有执行任何加购、下单或付款。已补充 DEVICE_DISCONNECTED 稳定分类。\n\n未完成的验收项:稳定真机上的全部颜色/尺码扫描,以及真实 Admin 接收后页面状态与规格表核对。详细记录见 docs/task/32-client-执行采集任务并提交-admin.md。
Author
Owner

验收通过,关闭

  • 实现提交: 2d1fdbe feat: 执行并提交 PDD 采集任务 (#32)
  • 归档: docs/task/32-client-执行采集任务并提交-admin.md(状态已更新,4022fee)

架构角色复核要点:

skus 是颜色×尺码的笛卡尔积,每条带 price_observed_at。这是本工单最要紧的一条——只输出颜色的话,后续「蝦皮 SKU ↔ PDD 规格」匹配(按完整组合求 OptionKey)会直接废掉。实现独立得出了与工单一致的结论。

字段名(price_observed_at / list_price_cent / shop_name)与 Admin 侧 #31 完全对得上。

后续修复已另立工单

本工单交付后的真机问题分别由 #35(页面误判 + 失败回执)、#36(进入规格面板)、#37(蛇形逐色采价)、#40(滚动采店铺名和评价数)、#42(允许颜色缺价)、#44(等待时间)处理,均仍在进行中。

遗留

  • 需在稳定的真机环境上完成最终联调验收
  • 底部状态未细分「第几个颜色」的逐步进度,不影响数据链路
## 验收通过,关闭 - **实现提交:** `2d1fdbe` feat: 执行并提交 PDD 采集任务 (#32) - **归档:** `docs/task/32-client-执行采集任务并提交-admin.md`(状态已更新,`4022fee`) 架构角色复核要点: **`skus` 是颜色×尺码的笛卡尔积**,每条带 `price_observed_at`。这是本工单最要紧的一条——只输出颜色的话,后续「蝦皮 SKU ↔ PDD 规格」匹配(按完整组合求 `OptionKey`)会直接废掉。实现独立得出了与工单一致的结论。 字段名(`price_observed_at` / `list_price_cent` / `shop_name`)与 Admin 侧 #31 完全对得上。 ### 后续修复已另立工单 本工单交付后的真机问题分别由 #35(页面误判 + 失败回执)、#36(进入规格面板)、#37(蛇形逐色采价)、#40(滚动采店铺名和评价数)、#42(允许颜色缺价)、#44(等待时间)处理,均仍在进行中。 ### 遗留 - 需在稳定的真机环境上完成最终联调验收 - 底部状态未细分「第几个颜色」的逐步进度,不影响数据链路
ila closed this issue 2026-08-09 10:19:13 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#32