diff --git a/docs/09-delivery-issues.md b/docs/09-delivery-issues.md index a1e8e79..73ba55c 100644 --- a/docs/09-delivery-issues.md +++ b/docs/09-delivery-issues.md @@ -26,6 +26,7 @@ | T16 | [#18](https://git.ilapage.cn/OPC/goauto/issues/18) | 任务重置、失败删除与并发约束 | T02、T06、T15 | | T17 | [#19](https://git.ilapage.cn/OPC/goauto/issues/19) | 一加最小闭环验收 | T05、T06、T09~T16 | | T18 | [#20](https://git.ilapage.cn/OPC/goauto/issues/20) | 本地 config.yaml 数据库启动配置 | T01 | +| T19 | [#21](https://git.ilapage.cn/OPC/goauto/issues/21) | PDD 商品详情颜色价格与尺码采集规则 | T11、T12、T13、T17 | ## 延期 diff --git a/docs/11-pdd-detail-rule-migration-analysis.md b/docs/11-pdd-detail-rule-migration-analysis.md new file mode 100644 index 0000000..b25e501 --- /dev/null +++ b/docs/11-pdd-detail-rule-migration-analysis.md @@ -0,0 +1,97 @@ +# PDD 商品详情采集规则迁移分析 + +## 结论 + +从 `D:\chengma\cmautobuy\client` 迁移这套能力是合理的,但不能把其中的文字、resource-id 或坐标直接拼成 GoAuto `schemaVersion: 1` 规则。源项目的可靠性来自一套有状态采集算法,而 GoAuto v1 目前只支持唯一节点上的单次 `wait`、`click`、`input`、`back` 和 `extract`。 + +推荐新增 `schemaVersion: 2` 的受限高层采集动作:服务端规则管理页面证据、规格别名、超时和遍历上限;Android Agent 固化遍历、选中确认、稳定采价和禁止下单/支付的算法。规则草案见 [pdd-product-detail-v2.proposed.json](rules/pdd-product-detail-v2.proposed.json)。该文件目前是设计输入,v2 执行器完成前不能导入管理端。 + +## 源项目实际做法 + +正式采集链位于 `client/src/pdd_collect_service.py`,可复用的是以下行为: + +1. 校验 URL 域名和 `goods_id` 一致性。 +2. 打开链接后持续分类页面,区分商品页、首页、登录、验证码、网络错误、风控和支付页。 +3. 读取商品摘要;有限纵向滚动补采店铺和评价。 +4. 用多项强证据确认规格面板,避免把推荐商品或全屏散落文字误判为规格。 +5. 颜色列表先归左,再按行蛇形遍历;每次点击后丢弃旧树并重新读取节点。 +6. 只有确认颜色已选中,且价格连续两次一致,才建立颜色与价格的关联。 +7. 颜色不可选或价格缺失时保留缺失信息,继续采集其它颜色和尺码。 +8. 尺码只读不点击;横向或纵向滑动到连续两次视口稳定,并校验“尺码(N)”中的数量。 +9. 按颜色价格展开颜色与尺码组合,并限制最大 SKU 数量。 + +源项目正式采集使用 `device.open_url()` 发起深链,不是先控制浏览器。`tools/test_pdd_home_deeplink.py` 只是用固定通用 URL 做只读诊断。因此浏览器“打开拼多多APP”和系统“打开”的步骤应继续采用 GoAuto 已通过一加真机验证的导航链,不能声称它来自源项目正式采集规则。 + +## 不能直接迁移的内容 + +| 源项目能力 | GoAuto v1 现状 | 直接迁移结果 | +|---|---|---| +| 同一容器提取多个规格值 | 每步必须唯一匹配 | 多个颜色或尺码会报 `RULE_AMBIGUOUS` | +| 横向、纵向滚动及边界判断 | 没有滚动动作 | 只能读取当前视口 | +| 点击后重新获取树 | 线性步骤不表达节点失效 | 容易点击旧节点或错绑价格 | +| 选中状态、可用状态和 bounds | `UiNodeRef` 只有文本 | 不能确认点击是否生效 | +| 价格连续两次稳定 | 只读取一次 | 可能记录颜色切换前的旧价格 | +| 颜色与价格成对累计 | `extract` 只产生独立字符串列表 | 两个数组可能错位 | +| 规格面板容器和相对位置 | selector 只支持节点自身属性 | 推荐列表可能被误判为规格 | +| 完整性计数、总时长和 SKU 上限 | 仅有单步超时 | 无法证明遍历完整,也缺少资源上限 | + +因此,给 v1 增加一批 `extractAll` 步骤仍不能解决关联、滚动和状态确认问题。 + +## 推荐架构 + +```text +任务规则快照(服务端) + ├─ navigation:浏览器和系统确认层的精确允许动作 + ├─ pageEvidence:PDD 包名、Activity 和商品页证据 + └─ collector:pddProductDetailV1 + 别名、超时、次数和 SKU 上限 + │ + ▼ +Android 受限状态机 + 页面分类 → 规格入口 → 面板强校验 → 逐颜色确认并采价 → 只读尺码 → 组装结果 + │ + ▼ +结构化结果(不含原始控件树和截图) +``` + +### 为什么不做通用脚本 DSL + +把循环、任意正则、坐标点击和条件跳转都开放给服务端,会使规则成为远程执行脚本:难以静态验证、难以保证不会触发下单,并且每条规则都要重复实现边界和超时。高层采集器只暴露有限配置,既保留集中管理,又能在 Android 内统一测试安全状态机。 + +### 服务端可配置内容 + +- 浏览器包名和精确的“打开拼多多APP”/系统“打开”步骤。 +- PDD 商品详情精确包名、Activity 和根节点证据。 +- 颜色、尺码标题的精确别名列表。 +- 页面、选中、价格稳定超时。 +- 最大商品页纵向滑动、规格横向/纵向滑动和最大 SKU 数量。 + +### Android 固定内容 + +- 规格入口只能使用 `safeBottomSpecEntryV1` 安全策略,禁止规则提供任意坐标。 +- 价格只能使用固定人民币解析器,金额输出为整数分。 +- 每次颜色点击后必须重新获取内存控件树;不能复用旧节点。 +- 若页面暴露 `selected`/`checked` 或“已选”摘要,必须据此确认颜色已选中。 +- 价格必须连续两次读取一致才算该颜色的价格。 +- 尺码只读,不点击;禁止任何提交订单和支付动作。 +- 每个循环都有次数和总时长上限。 + +## 相对源项目的优化 + +1. **不复制 XML 解析实现。** GoAuto 直接把 `AccessibilityNodeInfo` 投影为仅存在内存的不可变树模型,避免序列化、落盘和再次解析 XML。 +2. **保留浏览器链。** 源项目直接深链,GoAuto 则继续验证浏览器提示、系统确认和最终 PDD Activity,符合当前设备部署方式。 +3. **能力协商。** 设备注册/心跳应上报 `collector.pdd.product-detail.v1`;服务端不能把 v2 任务发给旧 APK。只比较 `agentVersion` 不够可靠。 +4. **第三维不静默合并。** 若出现颜色、尺码之外的维度,保存已确认的数据并提交 `completed_partial`,在 `missing` 标记不支持维度;不伪造完整 SKU。源项目当前直接失败,和 GoAuto“数据不齐仍提交”的规则不完全一致。 +5. **缺价不伪造 SKU。** 颜色仍进入维度,缺少稳定价格时记录 `price:<颜色>`;不为该颜色生成带虚构价格的 SKU。 +6. **规则能力版本独立。** `schemaVersion` 管契约形状,`collectorId` 管 Android 算法能力,便于以后修复页面适配而不破坏已有任务快照。 + +## 建议实施顺序 + +1. 定义 v2 规则契约、设备能力协商和服务端校验。 +2. 建立纯 Kotlin 内存节点树与可单测的 PDD 采集状态机。 +3. 接入无障碍服务的滚动、节点刷新、选中与价格稳定读取。 +4. 在管理端提供内置规则模板,避免管理员手写大段 JSON。 +5. 用一加真机验证浏览器到商品页、多个颜色价格、全部尺码和部分结果。 + +## 当前状态 + +本分析和规则草案属于 T19 的设计交付。GoAuto 当前服务端仍只接受 `schemaVersion: 1`,Android 也没有 v2 采集器;在 T19 实现完成并通过测试前,草案不能创建为生产可用规则。 diff --git a/docs/rules/pdd-product-detail-v2.proposed.json b/docs/rules/pdd-product-detail-v2.proposed.json new file mode 100644 index 0000000..3c279d9 --- /dev/null +++ b/docs/rules/pdd-product-detail-v2.proposed.json @@ -0,0 +1,73 @@ +{ + "schemaVersion": 2, + "ruleType": "pddProductDetail", + "navigation": { + "steps": [ + { + "id": "browser-open-pdd", + "page": "browser", + "packageName": "com.heytap.browser", + "action": "click", + "selector": { + "text": "打开拼多多APP" + }, + "timeoutMs": 15000, + "optional": true + }, + { + "id": "system-open", + "page": "system-confirm", + "packageName": "android", + "action": "click", + "selector": { + "text": "打开" + }, + "timeoutMs": 8000, + "optional": true + } + ] + }, + "pageEvidence": { + "packageName": "com.xunmeng.pinduoduo", + "activityName": "com.xunmeng.pinduoduo.activity.NewPageActivity", + "selector": { + "resourceId": "android:id/content", + "className": "android.widget.FrameLayout" + } + }, + "collector": { + "collectorId": "pddProductDetailV1", + "specEntryStrategy": "safeBottomSpecEntryV1", + "priceParser": "pddRmbPriceV1", + "priceGranularity": "color", + "dimensionAliases": { + "color": [ + "颜色分类", + "颜色", + "款式", + "颜色款式", + "花色" + ], + "size": [ + "尺码", + "尺寸", + "套餐" + ] + }, + "timeoutsMs": { + "page": 30000, + "specPanel": 10000, + "selection": 2000, + "price": 2000, + "overall": 180000 + }, + "limits": { + "goodsPageVerticalSwipes": 3, + "specHorizontalSwipes": 12, + "specVerticalSwipes": 12, + "stableEdgeReads": 2, + "stablePriceReads": 2, + "maxSkuCount": 500 + } + } +}