"""PDD 采购演练适配层的稳定边界。 应用服务只依赖本文件中的小接口,不直接认识 uiautomator2。接口故意不提供 “提交订单”或“付款”方法,因此演练代码没有可误调用的真实下单入口。 """ from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass, field from typing import Mapping @dataclass(frozen=True) class PurchasePageState: """一次重新读取页面后得到的采购相关事实。""" page_kind: str goods_id: str selected_options: Mapping[str, str] = field(default_factory=dict) quantity: int = 0 price_cent: int = 0 candidate_count: int = 1 class PddPurchaseError(RuntimeError): """适配层失败,包含稳定代码、步骤和是否可安全重试。""" def __init__( self, code: str, message: str, *, step: str, retryable: bool = False, diagnostics: Mapping[str, object] | None = None, ) -> None: super().__init__(message) self.code = str(code or "PURCHASE_ADAPTER_ERROR") self.message = str(message or "PDD 采购演练失败") self.step = str(step or "purchase_prepare") self.retryable = bool(retryable) self.diagnostics = dict(diagnostics or {}) class PddPurchaseAdapter(ABC): """一台设备的一次采购演练会话;只能由创建它的工作线程使用。""" @abstractmethod def open_goods(self, goods_url: str) -> None: """通过商品链接打开 PDD 商品页。""" @abstractmethod def read_state(self) -> PurchasePageState: """重新读取当前页面,不返回缓存状态。""" @abstractmethod def select_options(self, options: Mapping[str, str]) -> None: """按完整动态规格对象精确选择,不做相似匹配。""" @abstractmethod def set_quantity(self, quantity: int) -> None: """设置采购数量。""" @abstractmethod def enter_confirmation(self) -> None: """进入最终提交前确认页,但不得提交订单。""" @abstractmethod def stop_before_submit(self) -> None: """在提交订单按钮之前停止并保持页面可供人工核对。""" @abstractmethod def close(self) -> None: """释放设备会话;不得在此方法中产生页面点击。"""