1
T-256
ila edited this page 2026-08-07 16:37:02 +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-256.md · commit afc651f75a3a


id: T-256 title: 允许少于目标数量的有效候选进入人工确认 phase: 2 deps:

  • T-254 status: DOING created: 2026-07-30 context_ref: c488e50 work_branch: null write_paths:
  • docs/tasks/T-256.md
  • docs/current-state.md
  • android-buyer/app/src/main/**
  • android-buyer/app/src/test/**

问题 / 背景

T-254 的真机计数分布显示,第一轮已经采集到一个完全合格的候选:

轮次 resolved target skuMismatch identityFail reason
1 1 2 0 0 CANDIDATE_TARGET_NOT_FOUND
2 0 1 0 0 CANDIDATE_ALL_EXCLUDED
3 0 2 0 0 CANDIDATE_ALL_EXCLUDED

该候选颜色尺码硬约束匹配、商品身份解析成功、双证据齐全,但因为 PROCUREMENT_CANDIDATE_TARGET = 2 而未达标,整条任务判 FAILED,候选没有提交给 Admin,人工连看都看不到。

这与需求直接冲突。docs/02-requirements.md:

  • 范围决策:「以 SKU 颜色/尺码硬约束过滤后回传 0..5 个」
  • F-005 验收:「匹配项按评估结果排序且最多 5 个,不足时不得凑数」
  • 业务规则 20:「回传可以少于 5 个,禁止用弱匹配凑数」

需求要求的是「不足就少回传」,当前实现做成了「不足就整体失败」。这是本轮采购任务 失败的直接可修复原因,且不依赖 T-255 对卡片识别率的根治。

关联需求与交互

  • 功能:F-005、F-006、F-007
  • 用户故事:US-004、US-005
  • 交互:沿用 Admin 任务详情候选确认区,不新增页面
  • 架构/API:候选回传与人工确认接口契约不变,后端零改动

行为契约

一、候选采集完成判定

candidateCollectionReady 改为:存在至少一个 hasResolvedIdentity 为真的候选 即视为可提交,不再要求达到 PROCUREMENT_CANDIDATE_TARGET。

PROCUREMENT_CANDIDATE_TARGET = 2 保留原值和原语义,继续用于计算每轮 roundCandidateTarget(即「本轮还想再找几个」),但不再作为成败门槛。

二、三轮预算继续走满

有效候选数未达 PROCUREMENT_CANDIDATE_TARGET 时,剩余轮次仍然要执行,尽量补足。 不得因为已经拿到 1 个就提前终止搜索——目标仍是尽量凑够 2 个,只是凑不够不算失败。

三轮跑完后:

  • 累计有效候选 ≥ 1 → 提交候选并进入 WAITING_CONFIRMATION,不写 FAIL 终态
  • 累计有效候选 = 0 → 维持 T-251 现有的 CANDIDATE_SEARCH_EXHAUSTED 可重试 FAIL

三、不得凑数

只有 hasResolvedIdentity 为真、且经过既有颜色/尺码硬约束校验的候选才能计入。 identityFailureCode 非空的观测候选不计入这个「至少一个」的判定,其既有的 观测保存行为不变。

四、诊断可见性

提交时的 search run 必须如实记录本次实际提交的候选数与 PROCUREMENT_CANDIDATE_TARGET 的差距,让 Admin 知道这是「不足目标但可用」而不是「完整 Top N」。沿用 T-254 的 诊断 message 机制,不新增字段格式。

方案

  1. MainActivity.candidateCollectionReady:判定改为「至少一个 EXACT 或 FALLBACK 的 已解析候选」。
  2. 三轮流程收尾处:区分「有候选 → 提交并进人工确认」与「零候选 → 沿用 T-251 终态」 两条路径。
  3. roundCandidateTarget 的计算方式不变,继续按缺口驱动后续轮次。
  4. 单测覆盖:1 个候选跑完三轮后提交且不写 FAIL、2 个候选行为与现状一致、0 个候选仍 写 CANDIDATE_SEARCH_EXHAUSTED、仅有未解析身份的观测候选不满足「至少一个」。

验收要点

  • 三轮累计恰好 1 个有效候选时,任务进入 WAITING_CONFIRMATION 且不写 FAIL 终态。
  • 三轮累计 0 个有效候选时,仍写 T-251 的 CANDIDATE_SEARCH_EXHAUSTED 可重试终态。
  • 累计 ≥ 2 个时行为与本任务前完全一致,有回归测试证明。
  • 只有身份未解析的观测候选时不满足提交条件。
  • 已拿到 1 个候选后,剩余轮次仍然执行,不提前终止。
  • PROCUREMENT_CANDIDATE_TARGET 常量值未改变。
  • Android Debug/Release 单测、lintDebug、assembleDebug、assembleRelease 全部通过。
  • 真机复跑 T-254 那条任务:第一轮那个候选出现在 Admin 候选确认区,可查看双证据。

边界

  • 不改 PROCUREMENT_CANDIDATE_TARGET 的值,也不改 MAX_CANDIDATES_PER_PROBE。
  • 不改 PinduoduoImageCandidateRoundPolicy 的三轮预算、timeout、attempts、scrolls。
  • 不改卡片识别、签名算法或跨轮去重——那是 T-255 的范围。
  • 不改 T-254 的诊断计数与失败码语义。
  • 不降低颜色、尺码、身份解析、双证据任何一项校验,不允许用弱匹配或未解析身份的 观测候选充数。
  • 不改后端代码、数据库、API 契约或 Admin 模板。
  • 不新增第四轮搜索。
  • 不启动真实下单,不进入订单提交或支付。

执行记录

开工后记录修改、命令、结果、环境、决策和 blocker。

2026-07-30

  • 状态:开工时由 TODO 改为 DOING;按用户要求未改为 DONE。
  • 修改:
    • MainActivity.candidateCollectionReady 现在只判断累计候选中是否存在 hasResolvedIdentity 为真的候选;身份未解析的诊断观测不会满足提交条件。
    • 新增 CandidateCollectionPolicy,将“已有有效候选”“已达到原目标”和“达到第三轮 后允许以不足目标数量收尾”分开判定;PROCUREMENT_CANDIDATE_TARGET = 2、三轮门禁、 roundCandidateTarget、MAX_CANDIDATES_PER_PROBE 和图片候选预算未修改。
    • 首轮只有一个有效候选时继续执行第二、第三轮;第三轮结束后将累计有效候选排入 WAITING_CONFIRMATION,不写 CANDIDATE_SEARCH_EXHAUSTED。零个有效候选仍沿用 CANDIDATE_SEARCH_EXHAUSTED 终态。
    • 三轮耗尽后的进程恢复也先检查持久化候选;已有有效候选回到正常提交路径,只有零个 有效候选才执行既有耗尽失败恢复。
    • 成功收尾和恢复收尾的 recordCandidateSearchAttempt 均复用 T-254 诊断 message, 并把本次实际累计提交候选数写入 resolved、目标写入 target,以显示不足目标但可用。
    • 新增 CandidateCollectionPolicyTest,覆盖 1 个候选三轮收尾、2 个候选原目标行为、 0 个候选失败路径和仅身份未解析观测不满足提交条件。
    • 未修改 backend-api、API 契约、数据库、候选识别/去重、SKU 硬约束、三轮预算、下单 提交或支付逻辑;未运行 init.ps1,未执行 git commit 或 push。
  • 命令与结果(工作目录:D:\chengma\cmroubao\android-buyer,PowerShell):
    • .\gradlew.bat testDebugUnitTest --no-daemon:BUILD SUCCESSFUL。
    • .\gradlew.bat testReleaseUnitTest --no-daemon:BUILD SUCCESSFUL。
    • .\gradlew.bat lintDebug --no-daemon:BUILD SUCCESSFUL。
    • .\gradlew.bat assembleDebug --no-daemon:BUILD SUCCESSFUL。
    • .\gradlew.bat assembleRelease --no-daemon:BUILD SUCCESSFUL。
    • 五项命令均退出码 0。
  • 环境:Windows PowerShell;JDK 17.0.13;Android SDK 34;Gradle Wrapper 8.2。构建输出 的 SDK XML 版本提示为既有环境警告,不影响五项目标成功。
  • 决策:保留既有“达到 2 个 exact 或 2 个 fallback 可提前收尾”的行为;只有累计有效候选 少于目标时才要求继续到第三轮。第三轮结束时只要有至少一个已解析身份候选即可提交, 以满足不足时少回传而非整体失败的需求。
  • 未验证 / blocker:无法连接并操作测试真机,也无法在 Admin 任务详情确认候选双证据, 因此 T-256 的最终 on-device verification 未完成;需要在测试设备上复跑 T-254 任务并 确认一个有效候选进入人工确认区。该 blocker 不影响本次五项 Android Gradle 验证。
  • 最终复核:补充进程恢复门禁后,以上五条 Gradle 命令均重新执行并再次以退出码 0 BUILD SUCCESSFUL 结束。