From 7b6e59c58daf9b7e762ec9bff74de279ad233bf1 Mon Sep 17 00:00:00 2001 From: chengma Date: Tue, 18 Aug 2026 17:31:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=BC=E5=AE=B9=E9=9D=9E=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E8=A7=84=E6=A0=BC=E7=A1=AE=E8=AE=A4=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=20(#275)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pdd_collect_service.py | 47 ++++++++++++++------- client/test/test_pdd_collect_service.py | 54 +++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/client/src/pdd_collect_service.py b/client/src/pdd_collect_service.py index e0c1076..89cd4f4 100644 --- a/client/src/pdd_collect_service.py +++ b/client/src/pdd_collect_service.py @@ -512,6 +512,9 @@ def _find_non_scrollable_spec_panel( node.get("package") == PDD_PACKAGE_NAME for node in root.iter("node") ) + is_order_confirmation = ( + classify_pdd_page(root, "").kind == PAGE_ORDER_CONFIRMATION + ) headings: list[ET.Element] = [] summaries: list[ET.Element] = [] @@ -546,9 +549,12 @@ def _find_non_scrollable_spec_panel( and screen_bottom and (bounds[1] + bounds[3]) // 2 >= screen_bottom * 0.6 and "提交订单" in descendant_label - and any( - word in descendant_label - for word in ("选择", "颜色", "尺码", "规格") + and ( + is_order_confirmation + or any( + word in descendant_label + for word in ("选择", "颜色", "尺码", "规格") + ) ) ): submit_hints.append(node) @@ -563,17 +569,23 @@ def _find_non_scrollable_spec_panel( if compact == "增加数量" and node.get("clickable") == "true": increases.append(node) - if not headings or not summaries or not panel_cues: - return None - if confirms: - action_nodes = [confirms[0]] - elif ( - pdd_node_count >= 3 + strong_confirmation_evidence = ( + is_order_confirmation + and pdd_node_count >= 3 and len(submit_hints) == 1 and len(quantity_editors) == 1 and len(decreases) == 1 and len(increases) == 1 - ): + ) + if not headings or not summaries: + return None + # 部分新版确认页会把“确认款式/关闭”滚出当前视口。只有页面分类和 + # 下面的数量、提交区域证据同时成立时,才允许缺少顶部提示。 + if not panel_cues and not strong_confirmation_evidence: + return None + if confirms: + action_nodes = [confirms[0]] + elif strong_confirmation_evidence: action_nodes = [ submit_hints[0], quantity_editors[0], @@ -583,7 +595,9 @@ def _find_non_scrollable_spec_panel( else: return None - required = [*headings, summaries[0], panel_cues[0], *action_nodes] + required = [*headings, summaries[0], *action_nodes] + if panel_cues: + required.append(panel_cues[0]) common = set([required[0], *_ancestors(required[0], parents)]) for node in required[1:]: common.intersection_update([node, *_ancestors(node, parents)]) @@ -595,17 +609,22 @@ def _find_non_scrollable_spec_panel( ) candidates: list[tuple[int, ET.Element]] = [] + full_screen_candidates: list[tuple[int, ET.Element]] = [] for node in common: bounds = _parse_bounds(node.get("bounds", "")) if bounds is None or node.get("visible-to-user", "true") != "true": continue area = (bounds[2] - bounds[0]) * (bounds[3] - bounds[1]) if screen_area and area >= screen_area * 0.95: + if strong_confirmation_evidence: + full_screen_candidates.append((area, node)) continue candidates.append((area, node)) - if not candidates: - return None - return min(candidates, key=lambda item: item[0])[1] + if candidates: + return min(candidates, key=lambda item: item[0])[1] + if full_screen_candidates: + return min(full_screen_candidates, key=lambda item: item[0])[1] + return None def _is_spec_panel_open(xml_data: str | bytes) -> bool: diff --git a/client/test/test_pdd_collect_service.py b/client/test/test_pdd_collect_service.py index bd5fb68..d6d9e93 100644 --- a/client/test/test_pdd_collect_service.py +++ b/client/test/test_pdd_collect_service.py @@ -46,6 +46,42 @@ def style_package_spec_xml() -> str: """ +def root_level_non_scrollable_confirmation_xml() -> str: + """模拟顶部提示不可见、证据节点都挂在全屏根容器的确认页。""" + + return """ + + + + + + + + + + + + + + + """ + + @dataclass(frozen=True) class FakeTask: goods_url: str @@ -863,6 +899,24 @@ class PddCollectParserTest(unittest.TestCase): [item.name for item in result.dimensions], ) + def test_parse_root_level_confirmation_with_size_before_color(self): + xml_data = root_level_non_scrollable_confirmation_xml() + + result = parse_spec_panel(xml_data) + + self.assertTrue(_is_spec_panel_open(xml_data)) + self.assertEqual( + [(item.key, item.name) for item in result.dimensions], + [("size", "尺码"), ("color", "颜色")], + ) + self.assertEqual( + [value.text for value in result.dimensions[0].values], + [ + "女小码【建议-体重75-100斤】", + "女大码【建议-体重101-120斤】", + ], + ) + def test_collects_static_sizes_from_non_scrollable_submit_hint_panel(self): device = FakeCollectDevice(self.home_xml, self.submit_hint_spec_xml) device.opened_url = (