Files
cmbuyer/docs/current-state.md
T

16 KiB
Raw Blame History

当前实现状态

本文是可覆盖的项目级快照,记录代码与任务的现实状态。 执行记录写进各任务文件(docs/tasks/T-<编号>.md)的 ## 执行记录,不在本文重复维护。

职责边界

  • tasks/:任务规格、依赖、状态(frontmatter)和执行记录,一任务一文件。
  • 06-tasks.md:只读路线图,维护阶段划分、里程碑和待办池。
  • current-state.md:当前快照,可覆盖更新。

当前快照

  • 日期:2026-08-04
  • 阶段:Phase 1 · 真机可行性(T-110 已批准受控规格入口,T-103 重新执行)
  • MVP 形态:手工填链接建单 → 批量开始试选 → 定时轮询 → 第一趟试选 → 人工确认 → 第二趟下单 → 待付款
  • 技术栈:已定。采购服务(admin/)使用 Go 1.23+ / gin / SQLite;采购工具(client/) 使用 Python 3.11+ / uiautomator2 / PySide6。 详见 03-tech-stack.md
  • 生产代码:admin/ 已有最小 Go 服务、健康检查、核心领域模型、SQLite 迁移与任务状态机; client/ 已有 Python 包、PySide6 最小入口、运行目录与日志脱敏策略,以及显式 serial 的 ADB 连接边界、本地基线取证 CLI、受限商品链接打开取证 CLI、人工声明规格面板状态的只读取证 CLI, 以及绑定 PKG110 / Android 16 / 拼多多 8.17.0 的规格证据确定性脱敏 CLI;尚无规格选择、价格读取或下单流程
  • 测试:采购服务已覆盖健康检查、核心模型、迁移与状态机等离线包级测试;采购工具 67 项离线单元测试 (全部 mock,不连接真机)
  • 数据:SQLite 核心表与迁移已落成;无业务实例数据
  • 标准启动路径:Windows PowerShell 运行 ./init.ps1,Unix shell 运行 ./init.sh。Windows 入口 优先使用合规的既有 venv;仅在其缺失时才从 Python Launcher 已安装版本中选择最高的 Python 3.11+, 并且不覆盖低版本环境;成功后打印真实启动命令。
  • 标准验证路径:./init.ps1 已实际跑通 admin 的 mod download / test / vet / build、client 的 editable install / 包导入 / unittest / compileall,以及仓库上下文校验。可单独运行两端命令诊断。
  • 当前设备门禁:人工已确认拼多多 8.17.0、goods_id 937122477375 的衣服商品只能通过“快要抢光” 打开规格面板;T-110 已获项目所有者批准,只把该证据/版本绑定的精确唯一入口作为第一趟可逆导航, 数量、确认页、提交订单、付款与通用点击能力仍不可达。T-103 的原始证据本机隔离与确定性脱敏器 已完成离线实现;人工确认面板刚打开时目标颜色和尺码已经自动选中,取证三态已据此修正。下一步 采集“刚打开目标预选 / 两维度改为非目标 / 两维度恢复目标”的 raw 并由人只核对 derived。在派生 证据验收前不写页面判据,Phase 2 仍不能抢跑。

当前目录要点

路径 状态 说明
docs/ 已有 项目规范化文档,本次已完整生成
docs/tasks/ 已有(T-001~T-004、T-005~T-009、T-101~T-110) T-001~T-004、T-101~T-102、T-110 已完成;T-103 重新执行
docs/design/ 已有(6 个原型) web 登录 / 建单 / 工作台 / 详情,desk 采购执行 / 配置;均已人工确认
scripts/ 已有 上下文门禁、Vikunja 单向导出与 MCP 启动包装
admin/ 已初始化 Go 1.23+ / gin / SQLite,含核心模型、迁移与状态机;无真机采购执行
client/ 已初始化 Python 3.11+ 包、依赖源、PySide6 最小入口、显式 serial 的基线/商品打开/规格面板只读取证、确定性证据脱敏、离线测试与 wheel 元数据检查;无规格选择、价格读取或下单流程
init.ps1 / init.sh 已完成 统一安装与离线验证入口;PowerShell 优先复用合规 venv,缺失时自动选择最高的 Python 3.11+,Unix 缺工具链明确失败

任务状态

任务状态以 docs/tasks/ 各任务文件 frontmatter 的 status 为准。本节只写项目级摘要:

  • 已完成:T-005(采购服务交互原型)、T-006(采购工具交互原型)、T-007(产品名称与 源码目录契约)、T-008(Vikunja 任务权威与单向导出)、T-009(MVP 关键路径与并行波次), 以及 T-001(采购服务 Go 骨架)。
  • 已完成:T-002(采购工具 Python 骨架)、T-003(双端统一初始化与验证入口)、 T-004(核心数据模型)、T-101(真机环境盘点与 USB/WiFi 双通道人工验收)、T-102(canonical 链接打开与目标商品/隐私人工验收)。
  • 已完成 T-110(第一趟受控规格入口与隐私脱敏边界)。T-103 是当前最高优先级和 MVP 生死线, 已补充 T-110 依赖并恢复 DOING;脱敏器已完成,三态派生证据和新真机验收完成前不开发依赖真机可读字段的 Phase 2 生产页面。
  • 已确认原型继续只作信息架构依据;原型假数据不调用真实接口、不驱动真机。真机结论改变 可读字段时必须先回修原型与交互清单。

当前可运行内容

Windows 统一安装、离线验证与启动命令提示已可运行:

.\init.ps1

它会在成功后输出以下真实启动命令,而不自动启动或连接设备:

采购服务:cd admin; go run ./cmd/server
采购工具:cd client; .\.venv\Scripts\python.exe -m cmbuyer_client

采购工具当前可运行的离线验证与 wheel 元数据检查:

cd client
.\.venv\Scripts\python.exe -m unittest discover -s tests -t .
.\.venv\Scripts\python.exe -m compileall -q src tests scripts
$wheelDir = Join-Path $env:TEMP ('cmbuyer-client-wheel-' + [guid]::NewGuid())
New-Item -ItemType Directory -Path $wheelDir | Out-Null
.\.venv\Scripts\python.exe -m pip wheel --no-deps . --wheel-dir $wheelDir
.\.venv\Scripts\python.exe scripts/verify_wheel_metadata.py (Get-ChildItem $wheelDir -Filter '*.whl').FullName

采购工具规范安装与本地开发入口:

# 在仓库根执行统一初始化;成功后再进入 client/
.\init.ps1
cd client
.\.venv\Scripts\python.exe -m cmbuyer_client

client/requirements.txt 是唯一依赖来源,client/pyproject.toml 动态读取它生成 wheel 的 Requires-Dist。Python 3.12 与 3.14 均已验证离线测试、编译与 wheel 元数据;完整运行时依赖安装 (pip install -e .)已通过。init.ps1 优先使用合规既有 venv,缺失时自动选择最高的 Python 3.11+; 本机 Python 3.12 与 3.14 均已验证,主工作区当前选择 Python 3.14。桌面 GUI 与真机流程未作为 T-003 验收执行。

T-101 的人工真机验收命令(先把手机切到不含收货地址、手机号、支付信息或其他无关隐私的安全页面; 必须从 adb devices -l 手工复制在线 serial,不能留空或自动选择):

# 仓库根目录;USB 和 WiFi 分开执行,每次只保留一个通道在线
D:\Portable\adb\adb.exe devices -l
.\client\.venv\Scripts\python.exe client\scripts\capture_device_baseline.py --serial <USB_SERIAL> --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-101\usb-baseline" --timeout 10 --adb D:\Portable\adb\adb.exe
# WiFi 必须由人先建立通道、断开 USB 后再手工粘贴在线 WiFi serial;脚本不自动 connect/reconnect
.\client\.venv\Scripts\python.exe client\scripts\capture_device_baseline.py --serial <WIFI_SERIAL> --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-101\wifi-baseline" --timeout 10 --adb D:\Portable\adb\adb.exe

脚本只读取非敏感设备元数据、拼多多版本、截图和完整 XML;不会打开商品或写页面判据。它对同机双 通道、身份读取失败、offline、unauthorized、超时或 serial 不存在均 fail closed。成功目录的 manifest.json 只记录元数据、产物路径和 SHA-256,不记录 XML 页面正文或原始 serial。T-101 已由人 完成两次验收并确认原始证据不含敏感信息;--timeout 约束 ADB 命令、ADB socket 以及截图/节点树的 公开 JSON-RPC 调用,uiautomator2 初始化仍有上游固定启动上限。截图/XML 只保留在本地,执行记录只写 路径和 SHA-256,原始证据不得提交 Git。

T-102 的人工真机验收命令(只接受唯一 canonical 链接;USB 或 WiFi 每次只保留一个通道在线):

# 仓库根目录;从输出中手工复制本次在线 serial
D:\Portable\adb\adb.exe devices -l
.\client\.venv\Scripts\python.exe client\scripts\capture_product_open.py --serial <SERIAL> --url "https://mobile.yangkeduo.com/goods.html?goods_id=<GOODS_ID>" --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-102\product-open-<GOODS_ID>" --timeout 10 --adb D:\Portable\adb\adb.exe

脚本只允许 Android VIEW intent,并把 package 固定为 com.xunmeng.pinduoduo;intent 后会在 --timeout 的有限窗口内只读轮询前台 package,解决 App 异步切换造成的一次性误判,超时仍会停止。 它不点击、滑动、输入或判断商品页节点,也不打开规格、读取价格、进入下单或支付。运行拼多多版本必须精确为 8.17.0,否则在 intent 前停止。成功后由人本地查看截图/XML,确认页面确为该 goods_id 对应商品并 检查无地址、手机号、支付信息或其他无关隐私;只回报 manifest 路径及截图/XML SHA-256,原始证据 不得提交 Git。T-102 已由人确认 goods_id 958756616606 的目标商品及截图/XML 隐私,并完成验收。

T-103 已确认当前衣服商品只能从精确文案“快要抢光”进入规格面板;T-110 只批准该证据/版本绑定入口, 不授权“免拼购买 / 单独购买 / 直接拼成”等其他文案。现有只读取证脚本本身仍不打开面板、不点击或选择 规格,也不读取价格;--state 只是人工声明,不能作为自动判据。面板刚打开时目标颜色 “黑色CHA(纯棉)”与尺码“M(建议100-115)”已经自动选中,不强行取消。人依次准备刚打开目标预选、 颜色和尺码均为非目标值、再恢复目标值三个状态,并分别采集到全新 raw 目录:

# 仓库根目录;三次采集使用三个全新的 output-dir
.\client\.venv\Scripts\python.exe client\scripts\capture_sku_panel_spike.py --serial <SERIAL> --url "https://mobile.yangkeduo.com/goods.html?goods_id=<GOODS_ID>" --state panel-opened-target-preselected --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-opened-target-<GOODS_ID>-v2\raw" --timeout 10 --adb D:\Portable\adb\adb.exe
.\client\.venv\Scripts\python.exe client\scripts\capture_sku_panel_spike.py --serial <SERIAL> --url "https://mobile.yangkeduo.com/goods.html?goods_id=<GOODS_ID>" --state alternate-all-dimensions-selected --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-alternate-<GOODS_ID>-v2\raw" --timeout 10 --adb D:\Portable\adb\adb.exe
.\client\.venv\Scripts\python.exe client\scripts\capture_sku_panel_spike.py --serial <SERIAL> --url "https://mobile.yangkeduo.com/goods.html?goods_id=<GOODS_ID>" --state target-selection-restored --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-target-restored-<GOODS_ID>-v2\raw" --timeout 10 --adb D:\Portable\adb\adb.exe

每组 raw 采集成功后分别运行离线脱敏器;derived 必须尚不存在:

.\client\.venv\Scripts\python.exe client\scripts\sanitize_sku_panel_evidence.py --raw-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-opened-target-<GOODS_ID>-v2\raw" --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-opened-target-<GOODS_ID>-v2\derived"
.\client\.venv\Scripts\python.exe client\scripts\sanitize_sku_panel_evidence.py --raw-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-alternate-<GOODS_ID>-v2\raw" --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-alternate-<GOODS_ID>-v2\derived"
.\client\.venv\Scripts\python.exe client\scripts\sanitize_sku_panel_evidence.py --raw-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-target-restored-<GOODS_ID>-v2\raw" --output-dir "$env:LOCALAPPDATA\cmbuyer\artifacts\T-103\sku-panel-target-restored-<GOODS_ID>-v2\derived"

原始 screenshot/XML 可能包含 PDD 固定展示的地址和掩码手机号,只能留在本机 raw 目录,不能由 agent、fixture、业务或上传端消费。脱敏器会校验源哈希、设备/App/商品/分辨率与隐私结构,在 sibling derived 目录原子发布 screenshot/XML 与 manifest;任何不匹配、手机号残留或已有目标均拒绝发布。 自动复检通过且人确认三态对应性后,agent 才能读取派生物并提取最小 fixture、编写判据。 人工确认中还必须记录中间态实际选择的非目标颜色和尺码,不能只写“已切换”。

关键背景

本项目是 cmroubao(Go 后端 + Android AccessibilityService)与 cmpdd (Python + uiautomator2)两个前序项目的合并重启。

  • 取 cmroubao 的后端任务生命周期、设备侧 API 形状、下单授权状态机、ERP 对接、管理 Web。
  • 取 cmpdd 的 uiautomator2 真机自动化、按维度精确选规格、订单确认页读取、付款闸门。
  • 丢弃自研 Android APK 与 AccessibilityService 感知层。

前序项目是设计依据,不是事实来源。 其中的结论(尤其是拼多多页面判据)必须在本项目 用真机重新验证,且记录取证时的拼多多 App 版本。

已知风险(开工前须知)

  1. M2 是生死线:真机能按链接打开商品、精确勾选颜色分类和尺码、读到该 SKU 单价 (T-103)。Phase 1 不通过之前不要写生产页面;Phase 0 原型只确认流程和信息架构,真机 结论改变字段时必须回修。
  2. 拼多多页面结构随版本变化,已观察到详情页无独立规格入口、价格节点被拆分等情况。
  3. 授权卡死:前序项目出现过 EXECUTING 授权永不推进导致任务锁死。本项目在 T-207 实现围栏前超时 / 放弃,在 T-208 实现围栏后调和;围栏后不得释放或重试。
  4. 规格面板单价位置未取证:闸门一依赖它,T-103 必须一并取证。若读不可靠, 确认页设计要改。
  5. MVP 已收窄:只做手工填链接、批量开始第一趟试选和两趟执行。Excel、ERP、图搜、 批量顺序编排 / 暂停接管、订单自动核对、AI 辅助全部推到 V2(见 06-tasks.md 的 T-501~T-508)。
  6. App 版本必须 fail closed:运行时拼多多版本与本项目已取证版本不一致就停止领取, 不允许用旧判据继续跑。

开始编码前检查

  1. 读 ../AGENTS.md。
  2. 读 00-ai-start-here.md,特别是「本项目的四条特有纪律」。
  3. 读 05-coding-rules.md,特别是第 1 节红线。
  4. 在 docs/tasks/ 找 status: TODO 且依赖均 DONE 的任务文件;暂无时先按 06-tasks.md 落成任务文件。
  5. 在独立分支 / worktree 把任务改为 DOING 后再改生产代码。

维护规则

实际代码状态变化时同步更新本文:新增或移动入口文件、初始化框架、新增可运行命令、 发现文档与代码不一致、阶段或 blocker 变化。

任务长期状态改在对应任务文件的 frontmatter;每轮执行记录、验证命令、阻塞点和关键决策 写进该任务文件的 ## 执行记录。本文只保留当前快照,不保留完整历史。