fix: 修正规格返回后的颜色复核顺序 (#266)
This commit is contained in:
@@ -1303,6 +1303,11 @@ class U2PddPurchaseAdapter(PddPurchaseAdapter):
|
|||||||
"observed_page_kind": current_page_kind,
|
"observed_page_kind": current_page_kind,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
# 完整遍历第二规格后,面板可能停在尺码列表末端,颜色节点已经
|
||||||
|
# 滑出当前控件树。必须先回到颜色区域,再用最新控件树确认原颜色;
|
||||||
|
# 否则“当前看不见”会被误报成“颜色发生变化”。
|
||||||
|
current_xml = self._restore_purchase_panel_color_region(current_xml)
|
||||||
|
root = _parse_xml(current_xml)
|
||||||
if not _selected(root, expected_snapshot.selected_color):
|
if not _selected(root, expected_snapshot.selected_color):
|
||||||
raise PddPurchaseError(
|
raise PddPurchaseError(
|
||||||
"PURCHASE_SPEC_CONTEXT_CHANGED",
|
"PURCHASE_SPEC_CONTEXT_CHANGED",
|
||||||
@@ -1311,7 +1316,6 @@ class U2PddPurchaseAdapter(PddPurchaseAdapter):
|
|||||||
diagnostics={"context_check": "selected_color"},
|
diagnostics={"context_check": "selected_color"},
|
||||||
)
|
)
|
||||||
|
|
||||||
current_xml = self._restore_purchase_panel_color_region(current_xml)
|
|
||||||
try:
|
try:
|
||||||
dimension = self._size_candidate_collector(self._require_device())
|
dimension = self._size_candidate_collector(self._require_device())
|
||||||
except PddCollectError as exc:
|
except PddCollectError as exc:
|
||||||
|
|||||||
@@ -260,10 +260,18 @@ class SoldOutRefreshPurchaseDevice(FakeDevice):
|
|||||||
class VerticallyScrolledColorPanelDevice(FakeDevice):
|
class VerticallyScrolledColorPanelDevice(FakeDevice):
|
||||||
"""模拟 PDD 记住规格面板纵向位置。"""
|
"""模拟 PDD 记住规格面板纵向位置。"""
|
||||||
|
|
||||||
def __init__(self, *, starts_at_top: bool, restores_on_swipe: bool = True):
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
starts_at_top: bool,
|
||||||
|
restores_on_swipe: bool = True,
|
||||||
|
selected_color: str = "",
|
||||||
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.at_color_top = starts_at_top
|
self.at_color_top = starts_at_top
|
||||||
self.restores_on_swipe = restores_on_swipe
|
self.restores_on_swipe = restores_on_swipe
|
||||||
|
self.selected_color = selected_color
|
||||||
|
self.selected_size = ""
|
||||||
self.swipes = []
|
self.swipes = []
|
||||||
|
|
||||||
def dump_hierarchy(self):
|
def dump_hierarchy(self):
|
||||||
@@ -272,9 +280,20 @@ class VerticallyScrolledColorPanelDevice(FakeDevice):
|
|||||||
if self.mode == "home":
|
if self.mode == "home":
|
||||||
return home_xml()
|
return home_xml()
|
||||||
if self.mode == "panel":
|
if self.mode == "panel":
|
||||||
return vertically_scrolled_purchase_panel_xml(
|
xml_data = vertically_scrolled_purchase_panel_xml(
|
||||||
at_color_top=self.at_color_top
|
at_color_top=self.at_color_top
|
||||||
)
|
)
|
||||||
|
if self.at_color_top and self.selected_color == "黑色":
|
||||||
|
xml_data = xml_data.replace(
|
||||||
|
'<node text="黑色" clickable="true"',
|
||||||
|
'<node text="黑色" selected="true" clickable="true"',
|
||||||
|
)
|
||||||
|
if self.at_color_top and self.selected_size == "均码":
|
||||||
|
xml_data = xml_data.replace(
|
||||||
|
'<node text="均码" clickable="true"',
|
||||||
|
'<node text="均码" selected="true" clickable="true"',
|
||||||
|
)
|
||||||
|
return xml_data
|
||||||
return super().dump_hierarchy()
|
return super().dump_hierarchy()
|
||||||
|
|
||||||
def swipe(self, x1, y1, x2, y2, duration=0.0):
|
def swipe(self, x1, y1, x2, y2, duration=0.0):
|
||||||
@@ -1098,6 +1117,87 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
adapter.close()
|
adapter.close()
|
||||||
|
|
||||||
|
def test_apply_resolved_size_restores_hidden_selected_color_before_recheck(self):
|
||||||
|
clock = FakeClock()
|
||||||
|
device = VerticallyScrolledColorPanelDevice(
|
||||||
|
starts_at_top=False,
|
||||||
|
selected_color="黑色",
|
||||||
|
)
|
||||||
|
selected_sizes = []
|
||||||
|
snapshot = PurchaseSpecCandidateSnapshot.build(
|
||||||
|
goods_id="753136429979",
|
||||||
|
selected_color="黑色",
|
||||||
|
target_size="50公斤",
|
||||||
|
dimension_name="尺码",
|
||||||
|
observations=(PurchaseSizeObservation("均码", True),),
|
||||||
|
observed_at="2026-08-18T04:00:00Z",
|
||||||
|
)
|
||||||
|
|
||||||
|
def select_size(_device, _xml, target, **_kwargs):
|
||||||
|
selected_sizes.append(target)
|
||||||
|
device.selected_size = target
|
||||||
|
return True
|
||||||
|
|
||||||
|
adapter = U2PddPurchaseAdapter(
|
||||||
|
"USB-001",
|
||||||
|
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||||
|
sleeper=clock.sleep,
|
||||||
|
monotonic=clock.monotonic,
|
||||||
|
select_size_fn=select_size,
|
||||||
|
size_candidate_collector=lambda _device: SpecDimension(
|
||||||
|
"size", "尺码", (DimensionValue("均码", True),)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
adapter.open_goods(GOODS_URL)
|
||||||
|
device.mode = "panel"
|
||||||
|
|
||||||
|
adapter.apply_resolved_size(snapshot, snapshot.candidates[0])
|
||||||
|
|
||||||
|
self.assertEqual(len(device.swipes), 1)
|
||||||
|
self.assertLess(device.swipes[0][1], device.swipes[0][3])
|
||||||
|
self.assertEqual(selected_sizes, ["均码"])
|
||||||
|
adapter.close()
|
||||||
|
|
||||||
|
def test_apply_resolved_size_stops_when_hidden_color_cannot_be_restored(self):
|
||||||
|
clock = FakeClock()
|
||||||
|
device = VerticallyScrolledColorPanelDevice(
|
||||||
|
starts_at_top=False,
|
||||||
|
restores_on_swipe=False,
|
||||||
|
selected_color="黑色",
|
||||||
|
)
|
||||||
|
selected_sizes = []
|
||||||
|
candidate_scans = []
|
||||||
|
snapshot = PurchaseSpecCandidateSnapshot.build(
|
||||||
|
goods_id="753136429979",
|
||||||
|
selected_color="黑色",
|
||||||
|
target_size="50公斤",
|
||||||
|
dimension_name="尺码",
|
||||||
|
observations=(PurchaseSizeObservation("均码", True),),
|
||||||
|
observed_at="2026-08-18T04:00:00Z",
|
||||||
|
)
|
||||||
|
adapter = U2PddPurchaseAdapter(
|
||||||
|
"USB-001",
|
||||||
|
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||||
|
sleeper=clock.sleep,
|
||||||
|
monotonic=clock.monotonic,
|
||||||
|
select_size_fn=lambda *_args, **_kwargs: selected_sizes.append(True),
|
||||||
|
size_candidate_collector=lambda _device: candidate_scans.append(True),
|
||||||
|
)
|
||||||
|
adapter.open_goods(GOODS_URL)
|
||||||
|
device.mode = "panel"
|
||||||
|
|
||||||
|
with self.assertRaises(PddPurchaseError) as raised:
|
||||||
|
adapter.apply_resolved_size(snapshot, snapshot.candidates[0])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
raised.exception.code,
|
||||||
|
"PURCHASE_PANEL_COLOR_REGION_NOT_FOUND",
|
||||||
|
)
|
||||||
|
self.assertEqual(len(device.swipes), 2)
|
||||||
|
self.assertEqual(candidate_scans, [])
|
||||||
|
self.assertEqual(selected_sizes, [])
|
||||||
|
adapter.close()
|
||||||
|
|
||||||
def test_apply_resolved_size_distinguishes_page_and_color_context_changes(self):
|
def test_apply_resolved_size_distinguishes_page_and_color_context_changes(self):
|
||||||
resolved_size = "均码【80-120斤】"
|
resolved_size = "均码【80-120斤】"
|
||||||
snapshot = PurchaseSpecCandidateSnapshot.build(
|
snapshot = PurchaseSpecCandidateSnapshot.build(
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ Client 不保存或读取 `purchase.live_*` 手工授权设置。`purchase_mode`
|
|||||||
不轮询 Admin。只有 `matched` 响应的哈希、候选编号、原文和 options 都是本次候选的
|
不轮询 Admin。只有 `matched` 响应的哈希、候选编号、原文和 options 都是本次候选的
|
||||||
逐字副本时才继续。响应返回后重新完整遍历真机候选并复算哈希,页面、颜色或候选
|
逐字副本时才继续。响应返回后重新完整遍历真机候选并复算哈希,页面、颜色或候选
|
||||||
有任何变化都在可逆阶段停止。
|
有任何变化都在可逆阶段停止。
|
||||||
|
- 完整遍历第二规格可能把已选颜色滑出当前控件树。Admin 返回解析结果后,Client
|
||||||
|
必须先有界恢复到颜色区域,再读取最新控件树确认原颜色仍明确选中;无法恢复与
|
||||||
|
明确换色分别安全停止,不能把暂时不可见当作颜色已经变化,也不能跳过颜色复核。
|
||||||
- 解析记录不能覆盖任务原始规格。成功结果同时保留原始规格、实际采用规格和
|
- 解析记录不能覆盖任务原始规格。成功结果同时保留原始规格、实际采用规格和
|
||||||
`resolution_id`;解析失败不自动重新采购,已有不可逆标记时永远不调用规格解析。
|
`resolution_id`;解析失败不自动重新采购,已有不可逆标记时永远不调用规格解析。
|
||||||
- 设置采购数量时先读取当前值;数量相同不聚焦输入框,小差值优先使用加减按钮。
|
- 设置采购数量时先读取当前值;数量相同不聚焦输入框,小差值优先使用加减按钮。
|
||||||
|
|||||||
Reference in New Issue
Block a user