diff --git a/T-251.-.md b/T-251.-.md new file mode 100644 index 0000000..6d7c6ec --- /dev/null +++ b/T-251.-.md @@ -0,0 +1,126 @@ + +> 同步来源:[`docs/tasks/T-251.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-251.md) · commit `afc651f75a3a` + +--- +id: T-251 +title: 修复候选结果页早判与三轮耗尽后执行不终止 +phase: 2 +deps: + - T-248 + - T-250 +status: DONE +created: 2026-07-30 +context_ref: 9e399e7 +work_branch: null +write_paths: + - docs/tasks/T-251.md + - docs/current-state.md + - android-buyer/app/src/main/** + - android-buyer/app/src/test/** +--- + +## 问题 / 背景 + +真机任务在 Admin 显示“执行中”,手机停在拼多多图片搜索结果页。现场状态表明三轮候选 +搜索均在候选浏览步骤返回 `TARGET_NOT_READY`,本地 attempt gate 已记录三次失败并禁止 +再次启动,但 execution 仍继续每 30 秒 heartbeat,服务端因此一直保持 `RUNNING`。 + +拼多多结果页的标题和排序栏会早于商品卡片稳定出现。现有代码在页面已被识别为结果页后 +立即读取候选卡;首次读取为空时会直接消耗滚动和本轮预算,不能区分“卡片仍在加载”、 +“规格不匹配”和“商品身份无法解析”。第三轮耗尽后只记录搜索事件,没有写入既有终态 +outbox,是服务端状态不能收敛的直接原因。 + +## 关联需求与交互 + +- 功能:F-004、F-005、F-006、F-007 +- 用户故事:US-003、US-004、US-006 +- 交互:沿用候选搜索进度、安全停止和任务详情页面,不新增采购操作 +- 架构/API:复用 T-207 的事件/终态 outbox 和 execution `FAIL` 上报,不修改后端契约 + +## 行为契约 + +### 候选卡就绪 + +- 结果页外壳识别成功后,候选浏览必须在当前步骤 timeout 内对候选卡做最长 10 秒的 + 有界轮询;卡片出现后才允许开始检查商品或消耗结果滚动次数。 +- 轮询期间页面变为验证码、登录、风险控制、订单确认或支付页时,继续按既有安全策略 + 立即停止,不把阻塞页面当作加载中。 +- 10 秒内没有任何可安全读取的候选卡时,本轮返回 + `CANDIDATE_RESULTS_NOT_READY`;不得以滚动成功伪装成已取得候选。 +- 就绪等待包含在 T-250 的候选浏览 100/45 秒 timeout 内,不增加三轮总预算。 + +### 失败分类 + +- 页面已出现候选卡,但规格弹层内颜色或尺码不满足任务硬约束时,使用 + `CANDIDATE_SKU_NOT_MATCHED`。 +- 候选可打开但无法取得稳定 `goods_id`、规范链接或身份指纹时,使用 + `CANDIDATE_IDENTITY_UNRESOLVED`。 +- 候选卡存在但在本轮检查/滚动上限内没有可保存候选,且不属于以上单一主因时,使用 + `CANDIDATE_TARGET_NOT_FOUND`。 +- 页面启动阶段原有 `TARGET_NOT_READY` 保留,不再作为候选采集的笼统结果。 + +### 三轮耗尽终态 + +- 每轮仍先通过既有 API 记录搜索尝试、轮次、原因和 `exhausted`。 +- 第三轮失败且 execution-scoped attempt gate 已耗尽时,App 必须追加一个 + `CANDIDATE_SEARCH_EXHAUSTED` 的 `FAIL` 终态 outbox;`step` 固定为 + `CANDIDATE_SEARCH`,`order_submitted=false`,并标为可安全重试。 +- 终态 outbox 必须复用当前 execution/generation 和幂等保护;后台同步上传成功后清理 + 当前任务并停止 execution heartbeat,使 Admin 从 `RUNNING` 收敛到 `FAILED`。 +- 升级前已经耗尽三轮的本地状态必须被识别;若还没有终态 outbox,App 自动补写一次, + 若终态已经在 outbox 中则只补齐本地 `terminalQueued` 标记,不重复生成终态。 +- 三轮尚未耗尽时不得写终态;已增量回传的候选继续保留,现有重新执行和下单信号阻断 + 规则不变。 + +## 方案 + +1. 在 `PinduoduoCandidateAutomation` 增加有界候选卡就绪轮询,并记录本轮是否见到卡片、 + 规格不匹配和身份解析失败,用确定性优先级返回细分失败码。 +2. 为三轮搜索结果增加纯 Kotlin 终态策略,只在 gate 确认耗尽时生成固定的失败载荷; + `MainActivity` 先记录轮次事件,再调用 `ProcurementRepository.failExecution`。 +3. 复用 `ProcurementExecutionService` 的 outbox flush 和 STOP 路径,不另建心跳开关,也 + 不从拼多多页面执行任何下单、返回、支付或全局导航动作。 +4. 增加自动化单测覆盖延迟卡片、无卡片、SKU 不匹配、身份缺失,以及非终轮/终轮策略。 + +## 验收要点 + +- [x] 候选卡延迟出现时不消耗滚动或搜索轮次,并可继续采集。 +- [x] 结果页 10 秒无候选卡时返回 `CANDIDATE_RESULTS_NOT_READY`。 +- [x] SKU 不匹配和身份缺失分别返回稳定细分失败码。 +- [x] 第一、第二轮失败只记录事件;第三轮耗尽追加唯一 `FAIL` outbox。 +- [x] 升级前已经耗尽的 execution 可幂等补写终态。 +- [x] 终态上传后复用既有同步 STOP 路径清理任务并停止 heartbeat。 +- [x] T-248/T-250 的累计候选、第三轮保底、预算、随机停顿和支付边界不变。 +- [x] Android 单测、lint、Debug 和 Release 构建通过。 + +## 边界 + +- 不改变后端 execution 状态机、候选 API、Admin 选择授权或安全重试规则。 +- 不降低候选卡、商品身份、颜色、尺码、数量、价格和订单确认页校验。 +- 不把空白/加载页视作默认候选,不在本任务增加第四轮搜索。 +- 不启动真实拼多多采购,不进入订单提交或支付。 + +## 执行记录 + +- 2026-07-30:真机执行记录显示三轮均在 `pdd_browse_candidates` 失败并耗尽, + `execution_report` 没有终态,服务仍持续 heartbeat;拼多多前台已显示完整图片搜索 + 结果,确认同时存在结果卡片早判竞态和耗尽后未上报终态两个问题。 +- 2026-07-30:候选自动化增加生产默认 50 次、每次 200 毫秒的卡片就绪轮询;就绪前 + 不滚动。候选终态按无卡片、SKU 不匹配、身份未解析和未找到目标四类稳定返回,启动 + 阶段的 `TARGET_NOT_READY` 含义不变。 +- 2026-07-30:Main 统一通过 `recordAutomaticSearchFailure` 先记录轮次事件,再在第三轮 + 耗尽时追加 `CANDIDATE_SEARCH_EXHAUSTED` 的可重试 `FAIL`。attempt gate 持久化 + `terminalQueued`,自动轮询还能恢复升级前已经三轮耗尽但没有终态的 execution; + repository 既有 terminal outbox、同步后清理和 STOP 行为保持不变。 +- 2026-07-30:新增延迟卡片、空结果、SKU 不匹配、身份解析失败、三轮门禁恢复和终态 + 策略测试。`testDebugUnitTest`、`lintDebug assembleDebug assembleRelease` 与根 + `init.ps1` 全部通过;根入口同时通过 Android Debug/Release 单测和 Go 后端全量测试。 +- 2026-07-30:本次未覆盖安装 APK、未启动 Roubao 或拼多多,也未触发真实采购;现场 + execution 从 `RUNNING` 收敛到 `FAILED` 需在安装新 APK 并启动 Roubao 后复验。 +- 2026-07-30:随后通过 USB 将最新 Debug APK 覆盖安装到 PKG110,`adb install -r` + 成功;手机 `base.apk` 与本地产物 SHA-256 均为 + `19ac6295e033f02693fa539128d2881a7c91d8924c73c7bb1a3f7e96cd2aadac`,版本仍为 + `1.4.16 (21)`。覆盖安装后 Roubao 采购无障碍仍在启用列表中。 +- 2026-07-30:为触发升级前耗尽状态恢复,仅启动 Roubao 主界面;没有启动拼多多或 + 下单。随后 ADB 设备连接消失,且本机后端 `8080` TLS 握手超时,因此尚未确认 + `terminalQueued`、终态上传或 Admin 状态收敛。