From 78ab8716a6dd0ccb90b8198e95f8b471f02ecd82 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Sat, 29 Aug 2026 15:15:25 +0800 Subject: [PATCH] fix(#154): return safely from WeChat to PDD --- android/app/build.gradle.kts | 4 +- .../automation/GoAutoAccessibilityService.kt | 9 ++++ .../automation/PurchaseLiveAutomation.kt | 11 +++- .../automation/PurchaseRehearsalExecutor.kt | 1 + .../agent/PurchaseLiveAutomationTest.kt | 53 ++++++++++++++++++- docs/03-business-rules-and-glossary.md | 6 +-- docs/08-agent-api-contract.md | 6 +-- 7 files changed, 80 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 0cf16cb..393de0b 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "cn.ilapage.goauto.agent" minSdk = 23 targetSdk = 34 - versionCode = 38 - versionName = "0.9.25" + versionCode = 39 + versionName = "0.9.26" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/automation/GoAutoAccessibilityService.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/automation/GoAutoAccessibilityService.kt index 581fc96..e80de06 100644 --- a/android/app/src/main/java/cn/ilapage/goauto/agent/automation/GoAutoAccessibilityService.kt +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/automation/GoAutoAccessibilityService.kt @@ -409,6 +409,15 @@ class GoAutoAccessibilityService : AccessibilityService(), UiDriver, PddCollecto override fun backPurchase(): Boolean = performGlobalAction(GLOBAL_ACTION_BACK) + override fun bringPddToForeground(): Boolean { + val launchIntent = packageManager.getLaunchIntentForPackage(PDD_PACKAGE) ?: return false + launchIntent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK) + return runCatching { + startActivity(launchIntent) + true + }.getOrDefault(false) + } + override fun swipeSpec(direction: SwipeDirection, anchor: SnapshotNode?): Boolean { if (anchor == null) return swipe(SemanticTarget.SPEC_PANEL, direction) val root = rootInActiveWindow ?: return false diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt index de1d68c..9fcee48 100644 --- a/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt @@ -111,16 +111,24 @@ class PurchaseLiveAutomation( val labels = linkedSetOf() var backedOutOfPayment = false var backedOutOfChooser = false + var restoredFromWechat = false repeat(60) { index -> val snapshot = driver.capture() val currentLabels = snapshot.nodes.filter { it.visible }.map { it.label } - currentLabels.forEach(labels::add) if (isKnownAndroidWechatChooser(snapshot, currentLabels)) { if (backedOutOfChooser || !driver.backPurchase()) return null backedOutOfChooser = true pause(500) return@repeat } + if (snapshot.packageName == WECHAT_PACKAGE) { + if (restoredFromWechat || !driver.bringPddToForeground()) return null + restoredFromWechat = true + pause(500) + return@repeat + } + if (snapshot.packageName != PDD_PACKAGE) return null + currentLabels.forEach(labels::add) parseOrderEvidence(labels)?.let { return it } val paymentVisible = isKnownPddPaymentActivity(snapshot) || currentLabels.any { label -> PAYMENT_MARKERS.any(label::contains) } @@ -344,6 +352,7 @@ class PurchaseLiveAutomation( private companion object { const val PDD_PACKAGE = "com.xunmeng.pinduoduo" + const val WECHAT_PACKAGE = "com.tencent.mm" val MASKED_PHONE = Regex("(? snapshot(listOf( @@ -216,6 +253,7 @@ class PurchaseLiveAutomationTest { node("chooser-title", "选择要使用的应用"), node("wechat-1", "微信"), node("wechat-2", "微信分身"), )) "payment" -> UiSnapshot(PDD, "com.xunmeng.pinduoduo.app_pay.core.PayActivity", listOf(node("pay", "立即支付", clickable = true))) + "wechat-login" -> UiSnapshot("com.tencent.mm", "com.tencent.mm.plugin.account.ui.SimpleLoginUI", listOf(node("wechat-login", "微信登录"))) else -> { val nodes = mutableListOf( node("root", "", bounds = NodeBounds(0, 0, 1080, 2200)), @@ -252,7 +290,14 @@ class PurchaseLiveAutomationTest { "138****5678" -> page = "panel" "修改" -> page = "edit" "保存" -> if (!saveStaysInEdit) page = if (savedTransitionWithoutLegacyContext) "saved-transition" else "panel" - "提交订单" -> { submitClicks++; page = if (chooserAfterSubmit) "chooser" else "order" } + "提交订单" -> { + submitClicks++ + page = when { + chooserAfterSubmit -> "chooser" + wechatLoginAfterSubmit -> "wechat-login" + else -> "order" + } + } } return FreshActionResult.SUCCESS } @@ -286,6 +331,12 @@ class PurchaseLiveAutomationTest { return true } + override fun bringPddToForeground(): Boolean { + pddRestoreCount++ + if (!wechatRestoreStuck) page = "payment" + return true + } + private fun snapshot(nodes: List) = UiSnapshot(PDD, ACTIVITY, nodes) private fun node( path: String, diff --git a/docs/03-business-rules-and-glossary.md b/docs/03-business-rules-and-glossary.md index b8ab2aa..53a2aa6 100644 --- a/docs/03-business-rules-and-glossary.md +++ b/docs/03-business-rules-and-glossary.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Business-Rules-and-Glossary wiki_url: https://git.ilapage.cn/OPC/goauto/wiki/Business-Rules-and-Glossary.- -wiki_revision: 71d8707aba33d07f329e1cc86a087383e555738d -synchronized_at: 2026-08-29T03:47:02Z +wiki_revision: d88d5d5aeabdb66bb67a2ec4b32da912fbf0629f +synchronized_at: 2026-08-29T07:11:22Z @@ -149,7 +149,7 @@ synchronized_at: 2026-08-29T01:39:44Z - 地址编辑页可以同时存在收货人、手机号和详细地址等多个输入框;Agent 只选择与“详细地址”标签纵向重叠且位于其右侧的唯一输入框,不能用页面输入框总数或顺序猜测。 - 点击创建订单前,Agent 必须先在本地事务保存 `order_submit_started`、不可逆时间、稳定请求 ID 和不含地址全文的最终确认快照,再用同一请求 ID通知服务端;两侧成功后才允许精确点击唯一创建订单按钮一次。 - 进入不可逆边界后,进程重启、断网、点击结果不明或无法取得唯一订单号/下单时间时只允许只读核单并进入 `order_result_unknown`,禁止再次点击;任务与订单正式关联仍以完整 PDD 订单号为准。 -- 创建订单后出现 Android 多微信应用选择器时,Agent 只有在系统包和 `ChooserActivity` / `ResolverActivity` 白名单、已知选择器标题、微信候选三类证据同时成立时,才允许按一次返回,绝不点击微信候选。回到 PDD 已知支付 Activity 或明确支付动作页面后最多再返回一次,再只读读取订单详情;任一页面不在白名单、一次返回后仍未离开或无法取得唯一订单号与下单时间时保持 `order_result_unknown`,禁止重下单或支付。 +- 创建订单后出现 Android 多微信应用选择器时,Agent 只有在系统包和 `ChooserActivity` / `ResolverActivity` 白名单、已知选择器标题、微信候选三类证据同时成立时,才允许按一次返回,绝不点击微信候选。若已进入真实微信包 `com.tencent.mm`,Agent 不点击、不输入、不登录、不支付、不强制停止微信,只允许用一次无参数 PDD 启动 Intent 把既有 PDD 任务栈拉回前台;第二次仍见微信或拉起失败即保持结果未知。回到 PDD 已知支付 Activity 或明确支付动作页面后最多再返回一次,再只读读取唯一订单号和下单时间;任一页面不在白名单、恢复动作重复、无法到达订单详情或结果不唯一时保持 `order_result_unknown`,禁止重下单或支付。 - Agent 本地 SQLite/Outbox 负责断网和重启恢复,服务端以 `task_id + task_attempt_id` 幂等接收并保存最终事实。 - 人工支付复核只记录 `paid` / `unpaid`;系统不执行或识别支付。快递单号与回填状态属于采购任务,后续物流工单实现。 - 采购任务领取时同时占用设备租约和可选 PDD 账号租约;租约过期后才可释放并重新领取。设备还存在采集任务时不能领取采购任务。 diff --git a/docs/08-agent-api-contract.md b/docs/08-agent-api-contract.md index d72a1b5..02ca2a5 100644 --- a/docs/08-agent-api-contract.md +++ b/docs/08-agent-api-contract.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Android-Agent-API-Contract wiki_url: https://git.ilapage.cn/OPC/goauto/wiki/Android-Agent-API-Contract.- -wiki_revision: 9f04eb9bb7284e525a58309b9c47cdb593a1e09c -synchronized_at: 2026-08-29T03:49:41Z +wiki_revision: 0fbfd430daab3d703649dfab34100be107581b81 +synchronized_at: 2026-08-29T07:11:55Z @@ -532,7 +532,7 @@ Android #42/#36 使用本地 SQLite 保存恢复与重传所需的任务、attem 创建订单前先在本地事务保存 `order_submit_started`、不可逆时间、稳定的 `orderSubmitRequestId` 和脱敏最终确认快照,再调用服务端同名接口;只有两侧标记完成并重新校验商品、规格、数量、单价、地址后缀、PDD 包/Activity 与唯一创建订单按钮后,才点击一次。重启时重放同一标记请求并只读核单;无法取得唯一未付款订单号和 PDD 下单时间时提交 `order_result_unknown`。支付文字仅用于识别未付款/离开支付页,永不点击。服务端数据库仍是最终事实来源;双方均不保存原始控件树、截图、PDD 凭据或完整收货地址。 -创建订单后若出现 Android 多微信应用选择器,只读核单器必须同时确认前台包为 `android` / `com.android.intentresolver`、Activity 为白名单 `ChooserActivity` / `ResolverActivity`、页面出现已知系统选择器标题且至少一个候选以“微信”开头,才允许执行一次系统返回;不得点击任何微信候选。随后若前台为 PDD `com.xunmeng.pinduoduo.app_pay.core.PayActivity` 或当前页面出现支付动作文字,最多再返回一次。返回后只读解析唯一订单号和下单时间;选择器或支付页重复出现、白名单不成立、返回失败、结果不唯一或超时均返回未知结果,禁止再次点击创建订单、取消订单或支付。 +创建订单后若出现 Android 多微信应用选择器,只读核单器必须同时确认前台包为 `android` / `com.android.intentresolver`、Activity 为白名单 `ChooserActivity` / `ResolverActivity`、页面出现已知系统选择器标题且至少一个候选以“微信”开头,才允许执行一次系统返回;不得点击任何微信候选。若前台已经是精确微信包 `com.tencent.mm`,只读核单器不得点击、输入、登录、支付或强制停止微信,只允许执行一次无参数 PDD 启动 Intent 并等待既有 PDD 任务栈回到前台;拉起失败或第二次仍见微信立即返回未知结果。随后若前台为 PDD `com.xunmeng.pinduoduo.app_pay.core.PayActivity` 或当前页面出现支付动作文字,最多再返回一次。返回后只读解析唯一订单号和下单时间;选择器或支付页重复出现、白名单不成立、恢复动作重复、无法到达订单详情、结果不唯一或超时均返回未知结果,禁止再次点击创建订单、取消订单或支付。 | 错误码 | 普通提示 | |---|---|