perf: 减少采购重复前台查询 (#120)
This commit is contained in:
@@ -106,6 +106,17 @@ def _labels(root: ET.Element) -> list[str]:
|
|||||||
return [value for node in root.iter("node") if (value := _label(node))]
|
return [value for node in root.iter("node") if (value := _label(node))]
|
||||||
|
|
||||||
|
|
||||||
|
def _package_hint_from_tree(root: ET.Element) -> str:
|
||||||
|
"""可靠控件树可以直接证明 PDD 在前台,避免慢速前台查询。"""
|
||||||
|
|
||||||
|
pdd_node_count = sum(
|
||||||
|
1
|
||||||
|
for node in root.iter("node")
|
||||||
|
if node.get("package") == PDD_PACKAGE_NAME
|
||||||
|
)
|
||||||
|
return PDD_PACKAGE_NAME if pdd_node_count >= 3 else ""
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_diagnostic_xml(xml_data: str) -> str:
|
def _sanitize_diagnostic_xml(xml_data: str) -> str:
|
||||||
"""只保留页面判断所需语义,删除商品、账号和收货相关文字。"""
|
"""只保留页面判断所需语义,删除商品、账号和收货相关文字。"""
|
||||||
|
|
||||||
@@ -400,30 +411,36 @@ class U2PddPurchaseAdapter(PddPurchaseAdapter):
|
|||||||
device = self._require_device()
|
device = self._require_device()
|
||||||
self._check_cancelled("purchase_page_check")
|
self._check_cancelled("purchase_page_check")
|
||||||
try:
|
try:
|
||||||
current = device.app_current()
|
trace = current_performance_trace()
|
||||||
xml_data = self._dump_hierarchy()
|
stage = trace.stage("purchase_read_state") if trace else nullcontext()
|
||||||
root = _parse_xml(xml_data)
|
with stage:
|
||||||
kind = _page_kind(root, str(current.get("package") or ""))
|
xml_data = self._dump_hierarchy()
|
||||||
labels = _labels(root)
|
root = _parse_xml(xml_data)
|
||||||
submit_targets = _final_submit_targets(root)
|
current_package = _package_hint_from_tree(root)
|
||||||
selected = {
|
if not current_package:
|
||||||
key: value
|
current = device.app_current()
|
||||||
for key, value in self._requested_options.items()
|
current_package = str(current.get("package") or "")
|
||||||
if _selected(root, value)
|
kind = _page_kind(root, current_package)
|
||||||
}
|
labels = _labels(root)
|
||||||
return PurchasePageState(
|
submit_targets = _final_submit_targets(root)
|
||||||
page_kind=kind,
|
selected = {
|
||||||
goods_id=self._goods_id,
|
key: value
|
||||||
selected_options=selected,
|
for key, value in self._requested_options.items()
|
||||||
quantity=_quantity(root),
|
if _selected(root, value)
|
||||||
price_cent=_price_cent(root),
|
}
|
||||||
candidate_count=1 if kind != "unknown" else 0,
|
return PurchasePageState(
|
||||||
in_stock=not any(
|
page_kind=kind,
|
||||||
marker in " ".join(labels)
|
goods_id=self._goods_id,
|
||||||
for marker in _OUT_OF_STOCK_MARKERS
|
selected_options=selected,
|
||||||
),
|
quantity=_quantity(root),
|
||||||
submit_candidate_count=len(submit_targets),
|
price_cent=_price_cent(root),
|
||||||
)
|
candidate_count=1 if kind != "unknown" else 0,
|
||||||
|
in_stock=not any(
|
||||||
|
marker in " ".join(labels)
|
||||||
|
for marker in _OUT_OF_STOCK_MARKERS
|
||||||
|
),
|
||||||
|
submit_candidate_count=len(submit_targets),
|
||||||
|
)
|
||||||
except PddPurchaseError:
|
except PddPurchaseError:
|
||||||
raise
|
raise
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -458,20 +475,35 @@ class U2PddPurchaseAdapter(PddPurchaseAdapter):
|
|||||||
panel_xml = self._wait_for_confirmation_panel()
|
panel_xml = self._wait_for_confirmation_panel()
|
||||||
|
|
||||||
color = checked.get("color")
|
color = checked.get("color")
|
||||||
if color and not self._select_color(
|
if color:
|
||||||
device, panel_xml, color, action_delay=0.2
|
trace = current_performance_trace()
|
||||||
):
|
stage = (
|
||||||
raise PddPurchaseError(
|
trace.stage("purchase_select_color")
|
||||||
"PURCHASE_OPTIONS_MISMATCH",
|
if trace else nullcontext()
|
||||||
f"没有精确选中颜色:{color}",
|
|
||||||
step="purchase_select_options",
|
|
||||||
)
|
)
|
||||||
|
with stage:
|
||||||
|
color_selected = self._select_color(
|
||||||
|
device, panel_xml, color, action_delay=0.2
|
||||||
|
)
|
||||||
|
if not color_selected:
|
||||||
|
raise PddPurchaseError(
|
||||||
|
"PURCHASE_OPTIONS_MISMATCH",
|
||||||
|
f"没有精确选中颜色:{color}",
|
||||||
|
step="purchase_select_options",
|
||||||
|
)
|
||||||
size = checked.get("size")
|
size = checked.get("size")
|
||||||
if size:
|
if size:
|
||||||
latest_xml = self._dump_hierarchy()
|
latest_xml = self._dump_hierarchy()
|
||||||
if not self._select_size(
|
trace = current_performance_trace()
|
||||||
device, latest_xml, size, action_delay=0.2
|
stage = (
|
||||||
):
|
trace.stage("purchase_select_size")
|
||||||
|
if trace else nullcontext()
|
||||||
|
)
|
||||||
|
with stage:
|
||||||
|
size_selected = self._select_size(
|
||||||
|
device, latest_xml, size, action_delay=0.2
|
||||||
|
)
|
||||||
|
if not size_selected:
|
||||||
raise PddPurchaseError(
|
raise PddPurchaseError(
|
||||||
"PURCHASE_OPTIONS_MISMATCH",
|
"PURCHASE_OPTIONS_MISMATCH",
|
||||||
f"没有精确选中尺码:{size}",
|
f"没有精确选中尺码:{size}",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import unittest
|
|||||||
|
|
||||||
from src.pdd_device_service import PddDeviceService
|
from src.pdd_device_service import PddDeviceService
|
||||||
from src.pdd_purchase_adapter import PddPurchaseError
|
from src.pdd_purchase_adapter import PddPurchaseError
|
||||||
|
from src.performance_timing import TaskPerformanceTrace
|
||||||
from src.pdd_u2_purchase_adapter import U2PddPurchaseAdapter
|
from src.pdd_u2_purchase_adapter import U2PddPurchaseAdapter
|
||||||
from src.pdd_u2_purchase_adapter import U2PddLivePurchaseAdapter
|
from src.pdd_u2_purchase_adapter import U2PddLivePurchaseAdapter
|
||||||
|
|
||||||
@@ -251,6 +252,54 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
|||||||
self.assertEqual(device.editor_values, [])
|
self.assertEqual(device.editor_values, [])
|
||||||
self.assertEqual(device.app_wait_calls, 0)
|
self.assertEqual(device.app_wait_calls, 0)
|
||||||
|
|
||||||
|
def test_reliable_pdd_tree_skips_repeated_app_current(self):
|
||||||
|
device = FakeDevice()
|
||||||
|
adapter = self._adapter(device, [])
|
||||||
|
adapter.open_goods(GOODS_URL)
|
||||||
|
calls_after_open = device.app_current_calls
|
||||||
|
|
||||||
|
state = adapter.read_state()
|
||||||
|
|
||||||
|
self.assertEqual(state.page_kind, "goods")
|
||||||
|
self.assertEqual(device.app_current_calls, calls_after_open)
|
||||||
|
adapter.close()
|
||||||
|
|
||||||
|
def test_ambiguous_tree_falls_back_to_app_current(self):
|
||||||
|
device = FakeDevice()
|
||||||
|
adapter = self._adapter(device, [])
|
||||||
|
adapter.open_goods(GOODS_URL)
|
||||||
|
calls_after_open = device.app_current_calls
|
||||||
|
device.mode = "special"
|
||||||
|
device.special_xml = (
|
||||||
|
'<hierarchy><node package="com.xunmeng.pinduoduo" '
|
||||||
|
'text="占位"/></hierarchy>'
|
||||||
|
)
|
||||||
|
|
||||||
|
adapter.read_state()
|
||||||
|
|
||||||
|
self.assertEqual(device.app_current_calls, calls_after_open + 1)
|
||||||
|
adapter.close()
|
||||||
|
|
||||||
|
def test_purchase_state_and_option_stages_are_recorded(self):
|
||||||
|
device = FakeDevice()
|
||||||
|
adapter = self._adapter(device, [])
|
||||||
|
records = []
|
||||||
|
trace = TaskPerformanceTrace(sink=records.append)
|
||||||
|
trace.bind_task("PUR-PERFORMANCE")
|
||||||
|
|
||||||
|
with trace.activate():
|
||||||
|
adapter.open_goods(GOODS_URL)
|
||||||
|
adapter.read_state()
|
||||||
|
adapter.select_options(
|
||||||
|
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||||
|
)
|
||||||
|
|
||||||
|
operations = [record["operation"] for record in records]
|
||||||
|
self.assertIn("purchase_read_state", operations)
|
||||||
|
self.assertIn("purchase_select_color", operations)
|
||||||
|
self.assertIn("purchase_select_size", operations)
|
||||||
|
adapter.close()
|
||||||
|
|
||||||
def test_same_quantity_does_not_focus_editor(self):
|
def test_same_quantity_does_not_focus_editor(self):
|
||||||
device = FakeDevice()
|
device = FakeDevice()
|
||||||
adapter = self._adapter(device, [])
|
adapter = self._adapter(device, [])
|
||||||
@@ -517,11 +566,13 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
|||||||
adapter.set_quantity(2)
|
adapter.set_quantity(2)
|
||||||
|
|
||||||
state = adapter.read_state()
|
state = adapter.read_state()
|
||||||
|
calls_before_submit = device.app_current_calls
|
||||||
adapter.submit_order_once()
|
adapter.submit_order_once()
|
||||||
with self.assertRaises(PddPurchaseError) as raised:
|
with self.assertRaises(PddPurchaseError) as raised:
|
||||||
adapter.submit_order_once()
|
adapter.submit_order_once()
|
||||||
|
|
||||||
self.assertEqual(state.submit_candidate_count, 1)
|
self.assertEqual(state.submit_candidate_count, 1)
|
||||||
|
self.assertEqual(device.app_current_calls, calls_before_submit + 1)
|
||||||
self.assertEqual(raised.exception.code, "PURCHASE_SUBMIT_ALREADY_ATTEMPTED")
|
self.assertEqual(raised.exception.code, "PURCHASE_SUBMIT_ALREADY_ATTEMPTED")
|
||||||
# 依次点击采购入口、数量加号和最终提交。
|
# 依次点击采购入口、数量加号和最终提交。
|
||||||
self.assertEqual(len(device.clicks), 3)
|
self.assertEqual(len(device.clicks), 3)
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ Client 不保存或读取 `purchase.live_*` 手工授权设置。`purchase_mode`
|
|||||||
|
|
||||||
- 点击前必须确认包名、页面类型、目标语义和坐标边界。
|
- 点击前必须确认包名、页面类型、目标语义和坐标边界。
|
||||||
- 高风险点击应在最新控件树上重新定位,不使用长时间缓存的坐标。
|
- 高风险点击应在最新控件树上重新定位,不使用长时间缓存的坐标。
|
||||||
|
- 采购普通状态读取先使用最新控件树确认 PDD 页面;控件树归属不明确时才查询
|
||||||
|
当前前台应用,避免在部分真机上重复等待十秒以上。最终下单点击前仍必须单独
|
||||||
|
查询前台应用。性能日志分别记录状态读取、颜色选择和尺码选择耗时。
|
||||||
- 规格选择后读取最新控件树并验证选中状态。
|
- 规格选择后读取最新控件树并验证选中状态。
|
||||||
- 设置采购数量时先读取当前值;数量相同不聚焦输入框,小差值优先使用加减按钮。
|
- 设置采购数量时先读取当前值;数量相同不聚焦输入框,小差值优先使用加减按钮。
|
||||||
只有输入框兜底路径确认输入法已经显示时才允许按一次返回键;输入法关闭后必须
|
只有输入框兜底路径确认输入法已经显示时才允许按一次返回键;输入法关闭后必须
|
||||||
|
|||||||
Reference in New Issue
Block a user