From 2088bc3ea6c6ca8c98b80fa585f76962e4e3f17c Mon Sep 17 00:00:00 2001 From: chengma Date: Wed, 12 Aug 2026 10:32:01 +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=E9=9D=A2=E6=9D=BF=E9=9D=99=E6=80=81=E5=B0=BA=E7=A0=81?= =?UTF-8?q?=E9=87=87=E9=9B=86=20(#177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pdd_collect_service.py | 50 ++++++++++++++++++ client/test/test_pdd_collect_service.py | 68 +++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/client/src/pdd_collect_service.py b/client/src/pdd_collect_service.py index ef15785..68e0acc 100644 --- a/client/src/pdd_collect_service.py +++ b/client/src/pdd_collect_service.py @@ -1858,6 +1858,11 @@ class PddCollectService: "PDD_DATA_SPEC_INCOMPLETE", f"无法建立第二规格续页上下文:{size_name}", ) + static_dimension = self._non_scrollable_second_dimension( + xml_data, initial_size_dimension + ) + if static_dimension is not None: + return static_dimension continuation = self._build_second_dimension_context( xml_data, initial_size_dimension ) @@ -1958,6 +1963,51 @@ class PddCollectService: tuple(DimensionValue(text, available) for text, available in sizes.items()), ) + def _non_scrollable_second_dimension( + self, + xml_data: str | bytes, + dimension: SpecDimension, + ) -> Optional[SpecDimension]: + """强证据确认非滚动面板时,返回当前树中的完整第二规格。""" + + root = _parse_xml(xml_data) + region, _ = self._second_dimension_region(root, dimension) + if region is not None: + return None + + parents = {child: parent for parent in root.iter() for child in parent} + if _find_non_scrollable_spec_panel(root, parents) is None: + return None + + values: dict[str, bool] = {} + for value in dimension.values: + option_text = self._second_dimension_option_text(value.text) + if option_text is None: + continue + values[option_text] = values.get(option_text, False) or value.available + + expected_count = self._dimension_expected_count(dimension.name) + if expected_count is not None and len(values) < expected_count: + raise PddCollectError( + "PDD_DATA_SPEC_INCOMPLETE", + f"{dimension.name}应有 {expected_count} 个选项,实际只采到 {len(values)} 个", + { + "dimension_name": dimension.name, + "expected_count": expected_count, + "collected_count": len(values), + }, + ) + if not values: + return None + return SpecDimension( + "size", + dimension.name, + tuple( + DimensionValue(text, available) + for text, available in values.items() + ), + ) + def _move_second_dimension_to_start( self, device: Any, xml_data: str ) -> tuple[str, bool]: diff --git a/client/test/test_pdd_collect_service.py b/client/test/test_pdd_collect_service.py index 7809cab..15dc060 100644 --- a/client/test/test_pdd_collect_service.py +++ b/client/test/test_pdd_collect_service.py @@ -813,6 +813,74 @@ class PddCollectParserTest(unittest.TestCase): [item.name for item in result.dimensions], ) + 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 = ( + "https://mobile.yangkeduo.com/goods.html?goods_id=123" + ) + device.panel_open = True + service = PddCollectService( + PddDeviceService(lambda _serial: device), + "USB-001", + "client-001", + sleeper=lambda _seconds: None, + ) + + dimension = service._collect_size_dimension(device) + + self.assertIsNotNone(dimension) + self.assertEqual(dimension.name, "尺码") + self.assertEqual( + [value.text for value in dimension.values], + ["M", "L"], + ) + self.assertEqual(device.swipes, []) + + def test_static_sizes_keep_explicit_count_validation(self): + spec_xml = self.submit_hint_spec_xml.replace( + 'text="尺码" bounds=', 'text="尺码(3)" bounds=', 1 + ) + device = FakeCollectDevice(self.home_xml, spec_xml) + device.opened_url = ( + "https://mobile.yangkeduo.com/goods.html?goods_id=123" + ) + device.panel_open = True + service = PddCollectService( + PddDeviceService(lambda _serial: device), + "USB-001", + "client-001", + sleeper=lambda _seconds: None, + ) + + with self.assertRaises(PddCollectError) as raised: + service._collect_size_dimension(device) + + self.assertEqual(raised.exception.code, "PDD_DATA_SPEC_INCOMPLETE") + self.assertEqual(raised.exception.diagnostics["expected_count"], 3) + self.assertEqual(raised.exception.diagnostics["collected_count"], 2) + + def test_static_sizes_require_strong_non_scrollable_panel_evidence(self): + snapshot = parse_spec_panel(self.submit_hint_spec_xml) + dimension = next( + item for item in snapshot.dimensions if item.key == "size" + ) + incomplete_evidence_xml = self.submit_hint_spec_xml.replace( + 'class="android.widget.EditText"', + 'class="android.widget.TextView"', + 1, + ) + service = PddCollectService( + PddDeviceService(lambda _serial: object()), + "USB-001", + "client-001", + ) + + result = service._non_scrollable_second_dimension( + incomplete_evidence_xml, dimension + ) + + self.assertIsNone(result) + def test_isolated_submit_hint_is_not_a_spec_panel(self): xml_data = """