fix(agent): 进入订单详情读取结果 (#203)

This commit is contained in:
QiuSW
2026-09-03 12:01:38 +08:00
parent bd4bc5f4f0
commit 73cb94e073
2 changed files with 91 additions and 0 deletions
@@ -121,6 +121,7 @@ class PurchaseLiveAutomation(
var backedOutOfChooser = false
var restoredFromWechat = false
var pddObservedAfterWechatRestore = false
var orderDetailEntryOpened = false
var wechatRestorePendingSamples = 0
var consecutiveEmptySnapshots = 0
repeat(ORDER_RESULT_MAX_SAMPLES) { index ->
@@ -178,6 +179,23 @@ class PurchaseLiveAutomation(
return@repeat
}
if (!orderContextVisible && !unpaidContextVisible) {
val entries = orderDetailEntryTargets(snapshot)
if (entries.size > 1) {
return unknown("PURCHASE_ORDER_DETAIL_ENTRY_AMBIGUOUS", "订单详情入口不唯一,已停止只读核单")
}
if (entries.size == 1) {
if (orderDetailEntryOpened) {
return unknown("PURCHASE_ORDER_DETAIL_ENTRY_TIMEOUT", "进入订单详情后页面未出现可验证证据")
}
when (driver.clickFresh(entries.single())) {
FreshActionResult.SUCCESS -> Unit
FreshActionResult.AMBIGUOUS -> return unknown("PURCHASE_ORDER_DETAIL_ENTRY_AMBIGUOUS", "订单详情入口不唯一,已停止只读核单")
else -> return unknown("PURCHASE_ORDER_DETAIL_ENTRY_FAILED", "订单详情入口点击失败,已停止只读核单")
}
orderDetailEntryOpened = true
pause(500)
return@repeat
}
pause(ORDER_RESULT_SAMPLE_INTERVAL_MS)
return@repeat
}
@@ -375,6 +393,14 @@ class PurchaseLiveAutomation(
snapshot.nodes.filter { node -> node.visible && node.enabled && FINAL_SUBMIT_MARKERS.any { node.label == it || node.label.startsWith(it) } },
)
/** A post-submit navigation target is allowed only when it is one exact, non-payment PDD order-detail entry. */
private fun orderDetailEntryTargets(snapshot: UiSnapshot): List<SnapshotNode> = uniqueClickable(
snapshot,
snapshot.nodes.filter { node ->
node.visible && node.enabled && node.label in ORDER_DETAIL_ENTRY_MARKERS
},
)
private fun pageProblem(snapshot: UiSnapshot) {
val labels = snapshot.nodes.filter { it.visible }.map { it.label }
PddPageClassifier.classify(snapshot.packageName, snapshot.activityName, labels)?.let { fail(it.code, it.message) }
@@ -425,6 +451,7 @@ class PurchaseLiveAutomation(
val FINAL_SUBMIT_MARKERS = listOf("提交订单", "现在买,仅", "确认购买")
val PAYMENT_MARKERS = listOf("立即支付", "确认支付", "输入支付密码")
val UNPAID_MARKERS = listOf("待付款", "待支付", "去支付")
val ORDER_DETAIL_ENTRY_MARKERS = setOf("查看订单", "订单详情")
val ORDER_CONTEXT_MARKERS = listOf("订单编号", "订单号", "下单时间", "创建时间")
val ORDER_NO = Regex("(?:订单编号|订单号)\\s*[::]?\\s*([A-Za-z0-9-]{6,64})")
val ORDER_TIME = Regex("(?:下单时间|创建时间)\\s*[::]?\\s*(20[0-9]{2}[-/.年][0-9]{1,2}[-/.月][0-9]{1,2}日?\\s+[0-9]{1,2}:[0-9]{2}(?::[0-9]{2})?)")
@@ -136,6 +136,52 @@ class PurchaseLiveAutomationTest {
assertFalse(driver.clicked.any { it.contains("支付") })
}
@Test
fun `successful order page enters the unique order detail before reading the result`() {
val driver = LiveDriver(orderDetailEntryAfterSubmit = true)
val automation = PurchaseLiveAutomation(driver, pause = {})
val address = automation.updateShippingAddress("_cg56")
automation.finalConfirmation(input().copy(addressSuffix = "_cg56"), address)
automation.submitOrderOnce()
val order = automation.readOrderResult()
assertEquals("PDD-202608210001", order?.orderNo)
assertEquals(1, driver.orderDetailEntryClicks)
assertEquals("order", driver.currentPage)
assertFalse(driver.clicked.any { it.contains("支付") })
}
@Test
fun `order detail entered from success page scrolls only to reveal folded result evidence`() {
val driver = LiveDriver(orderDetailEntryAfterSubmit = true, orderDetailEvidenceBelowFold = true)
val automation = PurchaseLiveAutomation(driver, pause = {})
val address = automation.updateShippingAddress("_cg58")
automation.finalConfirmation(input().copy(addressSuffix = "_cg58"), address)
automation.submitOrderOnce()
val order = automation.readOrderResult()
assertEquals("PDD-202608210001", order?.orderNo)
assertEquals(1, driver.orderDetailEntryClicks)
assertEquals(1, driver.genericSwipes)
assertFalse(driver.clicked.any { it.contains("支付") })
}
@Test
fun `ambiguous order detail entries stop without navigating`() {
val driver = LiveDriver(orderDetailEntryAfterSubmit = true, duplicateOrderDetailEntry = true)
val automation = PurchaseLiveAutomation(driver, pause = {})
val address = automation.updateShippingAddress("_cg57")
automation.finalConfirmation(input().copy(addressSuffix = "_cg57"), address)
automation.submitOrderOnce()
assertEquals(null, automation.readOrderResult())
assertEquals("PURCHASE_ORDER_DETAIL_ENTRY_AMBIGUOUS", automation.lastOrderReadFailure?.code)
assertEquals(0, driver.orderDetailEntryClicks)
assertFalse(driver.clicked.any { it.contains("支付") })
}
@Test
fun `repeated known payment activity still stops without scrolling or payment clicks`() {
val driver = LiveDriver(postSubmitCaptureSequence = listOf("payment", "payment"))
@@ -342,6 +388,9 @@ class PurchaseLiveAutomationTest {
private val wechatLoginAfterSubmit: Boolean = false,
private val wechatRestoreStuck: Boolean = false,
private val orderEvidenceBelowFold: Boolean = false,
private val orderDetailEntryAfterSubmit: Boolean = false,
private val orderDetailEvidenceBelowFold: Boolean = false,
private val duplicateOrderDetailEntry: Boolean = false,
postSubmitCaptureSequence: List<String> = emptyList(),
) : PurchaseUiDriver {
private var page = "confirmation"
@@ -358,6 +407,7 @@ class PurchaseLiveAutomationTest {
var backCount = 0
var postSubmitBackCount = 0
var pddRestoreCount = 0
var orderDetailEntryClicks = 0
var postSubmitCaptureCount = 0
val currentPage: String get() = page
@@ -393,6 +443,15 @@ class PurchaseLiveAutomationTest {
"order" -> snapshot(listOf(node("status", "待付款"), node("order", "订单号:PDD-202608210001"), node("time", "下单时间:2026-08-21 10:30:00"), node("pay", "立即支付", clickable = true)))
"order-folded" -> snapshot(listOf(node("status", "待付款"), node("pay", "立即支付", clickable = true)))
"order-no-time" -> snapshot(listOf(node("status", "待付款"), node("order", "订单号:PDD-202608210001"), node("pay", "去支付", clickable = true)))
"success" -> snapshot(buildList {
add(node("success-title", "购买成功"))
add(node("detail-parent", "", clickable = true))
add(node("detail", "查看订单", parentPath = "detail-parent"))
if (duplicateOrderDetailEntry) {
add(node("detail-parent-2", "", clickable = true))
add(node("detail-2", "订单详情", parentPath = "detail-parent-2"))
}
})
"chooser" -> UiSnapshot(if (trustedChooser) "android" else "example.untrusted", "com.android.internal.app.ChooserActivity", listOf(
node("chooser-title", "选择要使用的应用"), node("wechat-1", "微信"), node("wechat-2", "微信分身"),
))
@@ -442,9 +501,14 @@ class PurchaseLiveAutomationTest {
page = when {
chooserAfterSubmit -> "chooser"
wechatLoginAfterSubmit -> "wechat-login"
orderDetailEntryAfterSubmit -> "success"
else -> "order"
}
}
"查看订单", "订单详情" -> {
orderDetailEntryClicks++
page = if (orderDetailEvidenceBelowFold) "order-folded" else "order"
}
}
return FreshActionResult.SUCCESS
}