fix: 等待微信返回后再只读核单 (#124)

This commit is contained in:
chengma
2026-08-10 19:58:57 +08:00
parent 0f5d38b08f
commit 4999aa70e4
4 changed files with 274 additions and 27 deletions
+85 -23
View File
@@ -9,6 +9,7 @@ from __future__ import annotations
import re
import time
import xml.etree.ElementTree as ET
from collections import Counter
from datetime import datetime, timedelta, timezone
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from typing import Any, Callable, Optional
@@ -96,6 +97,26 @@ def _package_hint_from_tree(root: ET.Element) -> str:
return PDD_PACKAGE_NAME if pdd_node_count >= 3 else ""
def _foreground_package_from_tree(root: ET.Element) -> str:
"""控件树只有一个明确业务包时返回它;系统界面节点不参与判断。"""
ignored_packages = {"", "com.android.systemui"}
counts = Counter(
node.get("package", "")
for node in root.iter("node")
if node.get("package", "") not in ignored_packages
)
if not counts:
return ""
highest = max(counts.values())
packages = [
package
for package, count in counts.items()
if count == highest and count >= 3
]
return packages[0] if len(packages) == 1 else ""
def _label_count(root: ET.Element, targets: tuple[str, ...]) -> int:
return sum(
1
@@ -329,7 +350,10 @@ class U2PddPurchaseReconcileAdapter(PddPurchaseReconcileAdapter):
device_service: Optional[PddDeviceService] = None,
cancelled: Callable[[], bool] = lambda: False,
settle_seconds: float = 0.4,
transition_timeout_seconds: float = 30.0,
max_pages: int = 5,
monotonic: Callable[[], float] = time.monotonic,
sleep: Callable[[float], None] = time.sleep,
) -> None:
self._device_address = str(device_address or "").strip()
self._device_service = (
@@ -339,7 +363,12 @@ class U2PddPurchaseReconcileAdapter(PddPurchaseReconcileAdapter):
)
self._cancelled = cancelled
self._settle_seconds = max(0.0, float(settle_seconds))
self._transition_timeout_seconds = max(
0.0, float(transition_timeout_seconds)
)
self._max_pages = max(1, int(max_pages))
self._monotonic = monotonic
self._sleep = sleep
self._session = None
self._device = None
@@ -431,23 +460,39 @@ class U2PddPurchaseReconcileAdapter(PddPurchaseReconcileAdapter):
return self._device
def _open_unpaid_orders(self, device: Any) -> None:
root = _parse_xml(device.dump_hierarchy(compressed=False))
if not _package_hint_from_tree(root):
current = device.app_current()
if str(current.get("package") or "") != PDD_PACKAGE_NAME:
device.app_start(PDD_PACKAGE_NAME)
self._settle()
deadline = self._monotonic() + self._transition_timeout_seconds
opened_order_area = False
for _ in range(6):
checked_foreground = False
started_pdd = False
pressed_back = False
clicked_navigation: set[str] = set()
last_reason = "等待 PDD 待付款页稳定"
while self._monotonic() <= deadline:
self._check_cancelled()
root = _parse_xml(device.dump_hierarchy(compressed=False))
if self._monotonic() > deadline:
last_reason = "读取手机控件树超时"
break
if not _package_hint_from_tree(root):
current = device.app_current()
if str(current.get("package") or "") != PDD_PACKAGE_NAME:
raise PddPurchaseReconcileError(
"RECONCILE_WRONG_APP", "核单时 PDD 不在前台"
)
if not checked_foreground:
current_package = _foreground_package_from_tree(root)
if not current_package:
current = device.app_current()
current_package = str(current.get("package") or "")
if self._monotonic() > deadline:
last_reason = "查询手机前台应用超时"
break
checked_foreground = True
if current_package != PDD_PACKAGE_NAME:
device.app_start(PDD_PACKAGE_NAME)
started_pdd = True
last_reason = "已返回 PDD,等待待付款页加载"
elif started_pdd:
last_reason = "PDD 正在从微信中间页恢复"
else:
last_reason = "PDD 控件树正在加载"
self._settle()
continue
self._raise_for_special_page(root)
combined = _subtree_text(root)
if _is_single_order_detail(root) or (
@@ -460,27 +505,44 @@ class U2PddPurchaseReconcileAdapter(PddPurchaseReconcileAdapter):
):
return
if any(marker in combined for marker in _PAYMENT_ACTION_MARKERS):
device.press("back")
if not pressed_back:
device.press("back")
pressed_back = True
last_reason = "已只读返回,等待待付款页稳定"
self._settle()
continue
if "我的订单" in combined:
if self._click_unique_label(device, root, "待付款"):
if (
"待付款" not in clicked_navigation
and self._click_unique_label(device, root, "待付款")
):
clicked_navigation.add("待付款")
opened_order_area = True
last_reason = "已打开待付款,等待订单列表加载"
self._settle()
continue
if self._click_unique_label(device, root, "我的订单"):
if (
"我的订单" not in clicked_navigation
and self._click_unique_label(device, root, "我的订单")
):
clicked_navigation.add("我的订单")
opened_order_area = True
last_reason = "已打开我的订单,等待待付款入口加载"
self._settle()
continue
if self._click_unique_label(device, root, "个人中心"):
if (
"个人中心" not in clicked_navigation
and self._click_unique_label(device, root, "个人中心")
):
clicked_navigation.add("个人中心")
last_reason = "已打开个人中心,等待我的订单入口加载"
self._settle()
continue
raise PddPurchaseReconcileError(
"RECONCILE_ORDER_ENTRY_NOT_FOUND",
"没有找到唯一的“个人中心/我的订单/待付款”只读入口",
)
last_reason = "PDD 页面仍在过渡,尚未出现唯一只读订单入口"
self._settle()
raise PddPurchaseReconcileError(
"RECONCILE_ORDER_PAGE_TIMEOUT", "打开待付款订单列表超时"
"RECONCILE_ORDER_PAGE_TIMEOUT",
f"等待 PDD 待付款页稳定超时:{last_reason}",
)
@staticmethod
@@ -534,7 +596,7 @@ class U2PddPurchaseReconcileAdapter(PddPurchaseReconcileAdapter):
def _settle(self) -> None:
if self._settle_seconds:
time.sleep(self._settle_seconds)
self._sleep(self._settle_seconds)
def create_u2_purchase_reconcile_adapter(