1
T-251
ila edited this page 2026-08-07 16:36:59 +08:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

同步来源: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 不匹配、身份缺失,以及非终轮/终轮策略。

验收要点

  • 候选卡延迟出现时不消耗滚动或搜索轮次,并可继续采集。
  • 结果页 10 秒无候选卡时返回 CANDIDATE_RESULTS_NOT_READY。
  • SKU 不匹配和身份缺失分别返回稳定细分失败码。
  • 第一、第二轮失败只记录事件;第三轮耗尽追加唯一 FAIL outbox。
  • 升级前已经耗尽的 execution 可幂等补写终态。
  • 终态上传后复用既有同步 STOP 路径清理任务并停止 heartbeat。
  • T-248/T-250 的累计候选、第三轮保底、预算、随机停顿和支付边界不变。
  • 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 状态收敛。