采购流程缺少售罄识别:误报为规格不匹配,且替换入口无法触发 #158

Closed
opened 2026-08-29 16:40:16 +08:00 by ila · 2 comments
Owner

所属与来源

  • 关联工单:#130 Agent 从失败采集/采购任务发起替代商品采集(其可替换错误码集合依赖本工单产出的错误码)、#129~#132 PDD 商品替换流程、#127 采购规则落库。
  • 来源:用户于 2026-08-29 询问「采购时进入 PDD 商品页面显示商品已售罄是怎样处理的」。经复核,采购路径完全没有售罄判定,属功能缺口。
  • 类型:Android Agent / 采购执行的售罄识别与有界恢复。
  • 设计证据:不新增页面、组件、导航与显示文案;补齐既有采购流程缺失的页面状态识别,属恢复预期行为的缺陷修复,不需要原型。
  • 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。

当前事实(提交 cc326eb 复核)

  1. 采购执行器无任何售罄处理:PurchaseRehearsalExecutor.kt 全文无 售罄 / SOLD_OUT 相关代码。

  2. verifyProduct(PurchaseRehearsalExecutor.kt:200-208)的通过条件仅为「包名是 PDD 且存在可见节点」:

    if (snapshot.packageName == PDD_PACKAGE && snapshot.nodes.any { it.visible }) return null
    
  3. pageProblem 依赖的 PddPageClassifier.classify(PddNavigation.kt:10-22)只识别四类问题:登录失效、验证码/人机验证、风控、链接无效或已下架,不含售罄。

  4. 采集侧的售罄判定位于 ParsedPddScreen.isTransientSoldOut(PddProductDetailCollector.kt:120-139),并配有有界下拉恢复(:604-621),采购路径从未调用。

  5. 规格值级别的可选性已有实现:PddProductDetailCollector.kt:1423 依据标签是否含「售罄/缺货/不可选」标记 VisibleSpecValue.available。

  6. selectSpecs 在无可用规格时返回 PURCHASE_SPEC_NOT_MATCHED「没有下发可用的商品规格」(:240)。

问题

售罄商品会正常通过 verifyProduct,随后在规格入口或规格选择环节失败,典型错误为 PURCHASE_SPEC_NOT_MATCHED「没有下发可用的商品规格」。

该错误信息具有误导性:采购员据此会判断为规格映射配置错误,前往 Admin 反复调整映射,而真实原因是商品已售罄,任何映射调整都无效。

连带缺口:替换流程在采购场景下无法触发

#130 已将 PDD_GOODS_SOLD_OUT 纳入可发起替换的错误码集合,依据是该错误码产生于有界恢复失败之后、代表确认失效。

但采购路径不会产生该错误码,因此采购因售罄失败时:

  • Agent 上的「采集替代商品」入口不会出现;
  • 采购员无法走替换流程。

用户当初提出替换需求时明确包含「链接已失效或已售罄」两种情况,而「采购时才发现售罄」正是其中最典型的一种——目前这条路是断的。

目标

  1. 采购进入商品页后能识别售罄,并在恢复失败时以 PDD_GOODS_SOLD_OUT 明确失败。
  2. 复用采集侧既有的判定与恢复实现,不重写第二套。
  3. 使 #129~#132 的替换流程在采购场景下真正可用。

非目标

  • 不修改采集侧的售罄判定与恢复行为。
  • 不修改 PddPageClassifier 已识别的四类问题。
  • 不改变规格映射、价格护栏、数量与创建订单逻辑。
  • 不实现支付;不新增下单或地址目标。
  • 不引入 OCR/VLM,不保存控件树与截图。

实施方案

一、在采购流程中加入售罄判定

  1. 在 verifyProduct 通过之后、进入规格环节之前,增加一次售罄判定。
  2. 复用采集侧实现:通过既有的 currentScreen(input) 取得 ParsedPddScreen,调用 isTransientSoldOut(...),不重写判定逻辑。
  3. 判定所需的文案(精确售罄文案、兜底顶部文案、主商品证据别名)应来自规则下发,与采集侧保持同一来源;采购规则未提供时使用与采集侧一致的默认值。实施时确认取值路径并在工单说明。

二、补充「规格全不可选」这一采购特有情形

  1. 采集侧的 isTransientSoldOut 要求「没有主商品证据」(规格面板未打开、无规格入口)。而采购流程走到该步时规格入口通常存在,仅是其中所有规格值不可选,因此该条件在采购场景下可能不成立。
  2. 需补充一条采购特有判定:规格面板打开后,若全部规格值的 available 均为 false,同样判为售罄。可直接复用 VisibleSpecValue.available(事实 5),不新增解析逻辑。

三、有界恢复

  1. 判定成立后执行有界下拉恢复,做法与采集侧一致:按配置的次数下拉、间隔等待、稳定后重新解析并复判。
  2. 恢复次数、间隔与等待时长由规则配置;采购规则中如无对应配置,实施时确认是复用采集规则的配置还是在采购规则中新增字段,并在工单说明选择理由。
  3. 三种结束情形:
    • 下拉手势失败 → PDD_GOODS_SOLD_OUT,提示恢复失败;
    • 恢复后仍判定售罄 → PDD_GOODS_SOLD_OUT,提示确认售罄;
    • 恢复后页面证据失效 → 沿用既有的页面证据失效错误,不误报为售罄。
  4. 恢复成功则继续既有采购流程,不改变后续任何行为。

四、与替换流程衔接

  1. 本工单产出的 PDD_GOODS_SOLD_OUT 需被 #130 的可替换错误码集合覆盖(该集合由服务端集中定义)。实施时确认采购侧的该错误码已在集合内,使替换入口可正常出现。

安全边界

  • 售罄判定仅为只读识别,不触发任何点击。
  • 下拉恢复为有界操作,次数与时长受配置约束,不得无限重试。
  • 不点击相似目标、不猜测规格。
  • 不涉及创建订单、地址与支付。
  • 不保存控件树、截图与页面文本原文。

验收标准

  • 采购进入已售罄商品页时,最终以 PDD_GOODS_SOLD_OUT 失败,不再误报为 PURCHASE_SPEC_NOT_MATCHED。
  • 判定复用采集侧 isTransientSoldOut,未出现第二套售罄判定实现(代码检查佐证)。
  • 规格面板已打开但全部规格值不可选时,同样判为售罄。
  • 页面渲染延迟导致的假售罄可通过有界下拉恢复,恢复后采购流程正常继续。
  • 下拉手势失败、恢复后仍售罄、恢复后页面证据失效三种情形分别返回正确错误码。
  • 恢复为有界操作,不出现无限重试或忙循环。
  • 采购因售罄失败后,Agent 上出现「采集替代商品」入口(与 #130 联调验证)。
  • 正常在售商品的采购流程行为与本工单实施前完全一致。
  • 采集侧售罄处理未受影响。

验证方式

  • cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebug
  • 纯函数单元测试:售罄页面、假售罄(有主商品证据)、规格全不可选、恢复成功、恢复失败五类快照输入。
  • 真机 PKG110:对一个确认已售罄的商品发起采购,验证错误码与替换入口;对正常商品验证流程无回归。真实下单前须取得人工授权;永久禁止支付。
  • 真机、多设备与异常路径未覆盖部分如实回写。

依赖、并行与风险

  • 无强前置依赖;与 #130 构成闭环,建议一并验收。
  • 与 #157(采购就地重试)、#132 均涉及采购路径,但文件不同,可并行开发。
  • 风险:采购与采集的页面形态不同,直接套用采集侧判定可能漏判或误判。缓解:第二节补充采购特有情形,并以五类快照输入覆盖测试。
  • 风险:售罄判定引入后,原本能勉强跑通的边缘场景可能提前失败。缓解:判定要求页面证据成立且缺少可用规格,条件保守;验收含正常商品的回归。
  • 回退:还原本工单提交即可恢复现有行为,无数据影响。

文档影响

  • Wiki Business-Rules-and-Glossary:采购流程的售罄识别与有界恢复口径,以及 PDD_GOODS_SOLD_OUT 在采购场景下的产生条件。
  • Wiki Android-Agent-API-Contract:如售罄恢复配置需在采购规则中新增字段,需同步说明。
  • 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 sync 与一轮 sync --check,把页面与 revision 写回本工单。

状态

待实施。

## 所属与来源 - 关联工单:#130 Agent 从失败采集/采购任务发起替代商品采集(其可替换错误码集合依赖本工单产出的错误码)、#129~#132 PDD 商品替换流程、#127 采购规则落库。 - 来源:用户于 2026-08-29 询问「采购时进入 PDD 商品页面显示商品已售罄是怎样处理的」。经复核,采购路径**完全没有售罄判定**,属功能缺口。 - 类型:Android Agent / 采购执行的售罄识别与有界恢复。 - 设计证据:不新增页面、组件、导航与显示文案;补齐既有采购流程缺失的页面状态识别,属恢复预期行为的缺陷修复,不需要原型。 - 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 `AGENTS.md`「Gitea 交互与工单最小读取」记录回退原因。 ## 当前事实(提交 cc326eb 复核) 1. **采购执行器无任何售罄处理**:`PurchaseRehearsalExecutor.kt` 全文无 `售罄` / `SOLD_OUT` 相关代码。 2. `verifyProduct`(`PurchaseRehearsalExecutor.kt:200-208`)的通过条件仅为「包名是 PDD 且存在可见节点」: ```kotlin if (snapshot.packageName == PDD_PACKAGE && snapshot.nodes.any { it.visible }) return null ``` 3. `pageProblem` 依赖的 `PddPageClassifier.classify`(`PddNavigation.kt:10-22`)只识别四类问题:登录失效、验证码/人机验证、风控、链接无效或已下架,**不含售罄**。 4. 采集侧的售罄判定位于 `ParsedPddScreen.isTransientSoldOut`(`PddProductDetailCollector.kt:120-139`),并配有有界下拉恢复(`:604-621`),采购路径从未调用。 5. 规格值级别的可选性已有实现:`PddProductDetailCollector.kt:1423` 依据标签是否含「售罄/缺货/不可选」标记 `VisibleSpecValue.available`。 6. `selectSpecs` 在无可用规格时返回 `PURCHASE_SPEC_NOT_MATCHED`「没有下发可用的商品规格」(`:240`)。 ## 问题 售罄商品会正常通过 `verifyProduct`,随后在规格入口或规格选择环节失败,典型错误为 `PURCHASE_SPEC_NOT_MATCHED`「没有下发可用的商品规格」。 **该错误信息具有误导性**:采购员据此会判断为规格映射配置错误,前往 Admin 反复调整映射,而真实原因是商品已售罄,任何映射调整都无效。 ### 连带缺口:替换流程在采购场景下无法触发 #130 已将 `PDD_GOODS_SOLD_OUT` 纳入可发起替换的错误码集合,依据是该错误码产生于有界恢复失败之后、代表确认失效。 但采购路径**不会产生该错误码**,因此采购因售罄失败时: - Agent 上的「采集替代商品」入口不会出现; - 采购员无法走替换流程。 用户当初提出替换需求时明确包含「链接已失效**或已售罄**」两种情况,而「采购时才发现售罄」正是其中最典型的一种——目前这条路是断的。 ## 目标 1. 采购进入商品页后能识别售罄,并在恢复失败时以 `PDD_GOODS_SOLD_OUT` 明确失败。 2. 复用采集侧既有的判定与恢复实现,不重写第二套。 3. 使 #129~#132 的替换流程在采购场景下真正可用。 ## 非目标 - 不修改采集侧的售罄判定与恢复行为。 - 不修改 `PddPageClassifier` 已识别的四类问题。 - 不改变规格映射、价格护栏、数量与创建订单逻辑。 - 不实现支付;不新增下单或地址目标。 - 不引入 OCR/VLM,不保存控件树与截图。 ## 实施方案 ### 一、在采购流程中加入售罄判定 1. 在 `verifyProduct` 通过之后、进入规格环节之前,增加一次售罄判定。 2. **复用采集侧实现**:通过既有的 `currentScreen(input)` 取得 `ParsedPddScreen`,调用 `isTransientSoldOut(...)`,不重写判定逻辑。 3. 判定所需的文案(精确售罄文案、兜底顶部文案、主商品证据别名)应来自规则下发,与采集侧保持同一来源;采购规则未提供时使用与采集侧一致的默认值。实施时确认取值路径并在工单说明。 ### 二、补充「规格全不可选」这一采购特有情形 4. 采集侧的 `isTransientSoldOut` 要求「**没有主商品证据**」(规格面板未打开、无规格入口)。而采购流程走到该步时规格入口通常存在,仅是其中所有规格值不可选,因此该条件在采购场景下可能不成立。 5. 需补充一条采购特有判定:**规格面板打开后,若全部规格值的 `available` 均为 false,同样判为售罄**。可直接复用 `VisibleSpecValue.available`(事实 5),不新增解析逻辑。 ### 三、有界恢复 6. 判定成立后执行有界下拉恢复,做法与采集侧一致:按配置的次数下拉、间隔等待、稳定后重新解析并复判。 7. 恢复次数、间隔与等待时长由规则配置;采购规则中如无对应配置,实施时确认是复用采集规则的配置还是在采购规则中新增字段,并在工单说明选择理由。 8. 三种结束情形: - 下拉手势失败 → `PDD_GOODS_SOLD_OUT`,提示恢复失败; - 恢复后仍判定售罄 → `PDD_GOODS_SOLD_OUT`,提示确认售罄; - 恢复后页面证据失效 → 沿用既有的页面证据失效错误,不误报为售罄。 9. 恢复成功则继续既有采购流程,不改变后续任何行为。 ### 四、与替换流程衔接 10. 本工单产出的 `PDD_GOODS_SOLD_OUT` 需被 #130 的可替换错误码集合覆盖(该集合由服务端集中定义)。实施时确认采购侧的该错误码已在集合内,使替换入口可正常出现。 ## 安全边界 - 售罄判定仅为只读识别,不触发任何点击。 - 下拉恢复为有界操作,次数与时长受配置约束,不得无限重试。 - 不点击相似目标、不猜测规格。 - 不涉及创建订单、地址与支付。 - 不保存控件树、截图与页面文本原文。 ## 验收标准 - [ ] 采购进入已售罄商品页时,最终以 `PDD_GOODS_SOLD_OUT` 失败,**不再误报为 `PURCHASE_SPEC_NOT_MATCHED`**。 - [ ] 判定复用采集侧 `isTransientSoldOut`,未出现第二套售罄判定实现(代码检查佐证)。 - [ ] 规格面板已打开但全部规格值不可选时,同样判为售罄。 - [ ] 页面渲染延迟导致的假售罄可通过有界下拉恢复,恢复后采购流程正常继续。 - [ ] 下拉手势失败、恢复后仍售罄、恢复后页面证据失效三种情形分别返回正确错误码。 - [ ] 恢复为有界操作,不出现无限重试或忙循环。 - [ ] 采购因售罄失败后,Agent 上出现「采集替代商品」入口(与 #130 联调验证)。 - [ ] 正常在售商品的采购流程行为与本工单实施前完全一致。 - [ ] 采集侧售罄处理未受影响。 ## 验证方式 - `cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebug` - 纯函数单元测试:售罄页面、假售罄(有主商品证据)、规格全不可选、恢复成功、恢复失败五类快照输入。 - 真机 PKG110:对一个确认已售罄的商品发起采购,验证错误码与替换入口;对正常商品验证流程无回归。**真实下单前须取得人工授权;永久禁止支付。** - 真机、多设备与异常路径未覆盖部分如实回写。 ## 依赖、并行与风险 - 无强前置依赖;与 #130 构成闭环,建议一并验收。 - 与 #157(采购就地重试)、#132 均涉及采购路径,但文件不同,可并行开发。 - 风险:采购与采集的页面形态不同,直接套用采集侧判定可能漏判或误判。缓解:第二节补充采购特有情形,并以五类快照输入覆盖测试。 - 风险:售罄判定引入后,原本能勉强跑通的边缘场景可能提前失败。缓解:判定要求页面证据成立且缺少可用规格,条件保守;验收含正常商品的回归。 - 回退:还原本工单提交即可恢复现有行为,无数据影响。 ## 文档影响 - Wiki `Business-Rules-and-Glossary`:采购流程的售罄识别与有界恢复口径,以及 `PDD_GOODS_SOLD_OUT` 在采购场景下的产生条件。 - Wiki `Android-Agent-API-Contract`:如售罄恢复配置需在采购规则中新增字段,需同步说明。 - 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 `sync` 与一轮 `sync --check`,把页面与 revision 写回本工单。 ## 状态 待实施。
Author
Owner

实施完成,待验收

已按 #158 范围完成 Android 采购售罄识别与有界恢复,提交:77a2b25(已推送 main)。

实现

  • 商品页验证后、规格动作前复用采集侧 ParsedPddScreen.isTransientSoldOut,没有新增第二套页面售罄文案分类器。
  • 增加采购补充判定:规格面板已确认打开、已解析规格值非空且全部 available=false 时按售罄处理。
  • 两种售罄均执行同一套有界恢复:规格面板场景先返回商品页,再下拉 2 次(间隔 1000ms,等待 2000ms);恢复后重新打开规格面板。
  • 手势失败或恢复后仍售罄返回 PDD_GOODS_SOLD_OUT;恢复后商品页证据丢失返回既有 RULE_NOT_MATCHED,不会继续选择规格、修改地址或创建订单。
  • 采购规则 JSON/API 未增加字段,使用与采集默认值一致的 Android 共享常量,避免与待办 #127 的规则契约调整冲突。
  • 服务端既有替换资格已确认包含 PDD_GOODS_SOLD_OUT,本单无需服务端改动。

验证

  • PurchaseRehearsalExecutorTest:通过。新增覆盖临时售罄恢复成功、恢复后仍售罄、手势失败、恢复后商品证据丢失、规格值全部不可选恢复并重开面板;原有正常采购回归保持通过。
  • .\scripts\verify.ps1 -Component android:通过(Debug/Release 单测及 Debug APK 构建)。
  • 修改共享默认常量后的复核:gradlew :app:testDebugUnitTest --tests cn.ilapage.goauto.agent.PurchaseRehearsalExecutorTest :app:assembleDebug:通过。
  • python dev_scripts/harness.py check --strict:通过。
  • python dev_scripts/harness.py sync 与 sync --check:通过。

文档与边界

  • 已更新 Wiki Business-Rules-and-Glossary,在线回读 revision:2e474dd5f804c3e754ecc8e8722c9469e424acf0,本地镜像已同步。
  • GITEA_TOKEN 当前失效,Wiki 写入按仓库规则回退到 Git 已配置凭据调用 Gitea API;未输出或落盘凭据。
  • 未更新 API Contract:本单没有规则/API 字段变化。
  • 未执行真机正式采购:该操作可能创建订单,需要独立人工授权;永久禁止支付。建议验收时选一个明确售罄商品和一个正常商品,分别验证失败码/替代入口及正常规格选择不回归。
## 实施完成,待验收 已按 #158 范围完成 Android 采购售罄识别与有界恢复,提交:`77a2b25`(已推送 `main`)。 ### 实现 - 商品页验证后、规格动作前复用采集侧 `ParsedPddScreen.isTransientSoldOut`,没有新增第二套页面售罄文案分类器。 - 增加采购补充判定:规格面板已确认打开、已解析规格值非空且全部 `available=false` 时按售罄处理。 - 两种售罄均执行同一套有界恢复:规格面板场景先返回商品页,再下拉 2 次(间隔 1000ms,等待 2000ms);恢复后重新打开规格面板。 - 手势失败或恢复后仍售罄返回 `PDD_GOODS_SOLD_OUT`;恢复后商品页证据丢失返回既有 `RULE_NOT_MATCHED`,不会继续选择规格、修改地址或创建订单。 - 采购规则 JSON/API 未增加字段,使用与采集默认值一致的 Android 共享常量,避免与待办 #127 的规则契约调整冲突。 - 服务端既有替换资格已确认包含 `PDD_GOODS_SOLD_OUT`,本单无需服务端改动。 ### 验证 - `PurchaseRehearsalExecutorTest`:通过。新增覆盖临时售罄恢复成功、恢复后仍售罄、手势失败、恢复后商品证据丢失、规格值全部不可选恢复并重开面板;原有正常采购回归保持通过。 - `.\scripts\verify.ps1 -Component android`:通过(Debug/Release 单测及 Debug APK 构建)。 - 修改共享默认常量后的复核:`gradlew :app:testDebugUnitTest --tests cn.ilapage.goauto.agent.PurchaseRehearsalExecutorTest :app:assembleDebug`:通过。 - `python dev_scripts/harness.py check --strict`:通过。 - `python dev_scripts/harness.py sync` 与 `sync --check`:通过。 ### 文档与边界 - 已更新 Wiki `Business-Rules-and-Glossary`,在线回读 revision:`2e474dd5f804c3e754ecc8e8722c9469e424acf0`,本地镜像已同步。 - `GITEA_TOKEN` 当前失效,Wiki 写入按仓库规则回退到 Git 已配置凭据调用 Gitea API;未输出或落盘凭据。 - 未更新 API Contract:本单没有规则/API 字段变化。 - 未执行真机正式采购:该操作可能创建订单,需要独立人工授权;永久禁止支付。建议验收时选一个明确售罄商品和一个正常商品,分别验证失败码/替代入口及正常规格选择不回归。
Author
Owner

用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。

用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。
ila closed this issue 2026-08-29 20:44:42 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#158