feat(client): add guarded product link capture

This commit is contained in:
QiuSW
2026-08-04 09:26:37 +08:00
parent e7a4be1b9b
commit 7040bb61d8
11 changed files with 808 additions and 13 deletions
+48
View File
@@ -48,6 +48,10 @@ class DuplicatePhysicalDeviceError(DeviceConnectionError):
"""同一物理手机通过多个 ADB 通道同时在线。"""
class IntentLaunchUnconfirmedError(DeviceConnectionError):
"""`am start -W` 没有给出可确认的启动成功结果。"""
@dataclass(frozen=True)
class CommandResult:
"""可注入命令执行器的最小、可离线构造结果。"""
@@ -57,6 +61,14 @@ class CommandResult:
returncode: int = 0
@dataclass(frozen=True)
class IntentLaunchSummary:
"""不含 Activity、页面内容或 ADB 输出的受限启动摘要。"""
status: str
returncode: int
class CommandRunner(Protocol):
"""运行 ADB 子命令的可替换边界。"""
@@ -195,6 +207,42 @@ class AdbClient:
result = self._run_checked(("devices", "-l"))
return parse_adb_devices(result.stdout)
def start_pdd_view_intent(self, serial: str, goods_id: str) -> IntentLaunchSummary:
"""以参数数组启动唯一允许的拼多多 ACTION_VIEW Intent。
这里刻意不提供任意 shell 或任意 package 的执行接口。调用方必须先完成
``inspect`` 和应用版本核验;本方法在本层从纯数字 ``goods_id`` 重建 URL,调用方不能
把另一个 URL 直接交给 ADB。本方法既不点击控件,也不解析 Activity 或页面文本。
"""
selected_serial = _require_serial(serial)
if (
not isinstance(goods_id, str)
or not goods_id
or any(character < "0" or character > "9" for character in goods_id)
):
raise ValueError("goods_id 必须是纯数字")
canonical_url = f"https://mobile.yangkeduo.com/goods.html?goods_id={goods_id}"
result = self._run_checked(
(
"-s",
selected_serial,
"shell",
"am",
"start",
"-W",
"-a",
"android.intent.action.VIEW",
"-d",
canonical_url,
"-p",
"com.xunmeng.pinduoduo",
)
)
if not any(line.strip() == "Status: ok" for line in result.stdout.splitlines()):
raise IntentLaunchUnconfirmedError("商品链接启动结果无法确认,已停止后续取证。")
return IntentLaunchSummary(status="ok", returncode=result.returncode)
def _physical_identity(self, device: AdbDevice) -> frozenset[str]:
serialno = self._getprop(device.serial, "ro.serialno")
boot_serialno = self._getprop(device.serial, "ro.boot.serialno")