diff --git a/AGENTS.md b/AGENTS.md index 38229f8..7360bd8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,8 +5,8 @@ ## 项目定位 -cmbuyer 是一个自动化采购系统:**网页端**(Go)负责建单与人工决策,**桌面端**(Python) -驱动 Android 手机在拼多多完成找货和下单。 +cmbuyer 是一个自动化采购系统:**采购服务**(网页端,`admin/`,Go)负责建单与人工决策, +**采购工具**(Windows 桌面端,`client/`,Python)驱动 Android 手机在拼多多完成找货和下单。 **系统只创建待付款订单,任何情况下都不自动付款。** @@ -66,7 +66,7 @@ cmbuyer 是一个自动化采购系统:**网页端**(Go)负责建单与人 - 默认**单任务、单责任 agent、单写入者**:一个任务只有一个负责人,同时只有一个 agent 修改该任务的 `write_paths`。 -- 多 agent 并行只拆到写路径互不重叠的任务。web 端与 desk 端天然可并行。 +- 多 agent 并行只拆到写路径互不重叠的任务。`admin/` 采购服务与 `client/` 采购工具天然可并行。 - 复杂任务先规划再编码。方案、不可变约束、写路径和验收门禁必须写入任务文件, 不能只停留在对话里。 - 任务内委派不是默认流程。委派后仍保持唯一写入者,执行者必须继承任务文件中的不可变 @@ -79,11 +79,11 @@ cmbuyer 是一个自动化采购系统:**网页端**(Go)负责建单与人 按 [`docs/03-tech-stack.md`](docs/03-tech-stack.md) 第六节的验证矩阵判断层级: ```bash -# web 端 +# 采购服务(admin/) go test ./... go vet ./... -# desk 端 +# 采购工具(client/) python -m unittest discover -s tests -t . python -m compileall -q src tests diff --git a/README.md b/README.md index 66f7f56..63f287b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # cmbuyer -自动化采购系统。**网页端**负责建单与人工决策,**桌面端**驱动 Android 手机在拼多多完成 -选规格和下单。 +自动化采购系统。**采购服务**(网页端,`admin/`)负责建单与人工决策,**采购工具** +(Windows 桌面端,`client/`)驱动 Android 手机在拼多多完成选规格和下单。 > **系统只创建待付款订单,任何情况下都不自动付款。** @@ -14,10 +14,10 @@ cmbuyer 把这个过程自动化,人只在两个点介入:**机器选对了 手工填链接(MVP) / Excel · ERP(V2) │ v - web 端(Go) 建单 · 试选确认 · 下单授权 · 审计 + 采购服务(admin/,Go) 建单 · 试选确认 · 下单授权 · 审计 │ HTTP v - desk 端(Python) 领任务 · 跑流程 · 回传 +采购工具(client/,Python) 领任务 · 跑流程 · 回传 │ ADB(USB / WiFi) v Android 手机(拼多多 App) diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md index 4ef32ef..013e553 100644 --- a/docs/00-ai-start-here.md +++ b/docs/00-ai-start-here.md @@ -5,8 +5,9 @@ ## 一句话定位 -cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决策,**桌面端**驱动 Android -手机在拼多多完成找货和下单,**付款始终由人完成**。 +cmbuyer 是一个自动化采购系统:**采购服务**(网页端,`admin/`)负责建单与人工决策, +**采购工具**(桌面端,`client/`)驱动 Android 手机在拼多多完成找货和下单, +**付款始终由人完成**。 第一版 MVP 只做**任务自带商品链接**的情形,**分两趟跑**: @@ -90,7 +91,7 @@ cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决 1. Phase 0:网页端 / 桌面端 MVP 原型人工确认、两端骨架与数据模型。 2. **Phase 1:真机取证(最高风险,生死线)。** -3. Phase 2:web 端核心(含**授权超时**,不得推后)。 +3. Phase 2:采购服务核心(含**授权超时**,不得推后)。 4. Phase 3:双端打通与**第一趟试选**端到端。 5. Phase 4:**第二趟下单**与收尾。 6. V2 及以后:图搜、Excel、ERP、订单自动核对、AI 辅助。 @@ -146,7 +147,7 @@ cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决 项目事实只信: - 本目录下的文档(架构、API、需求、交互) -- 数据库迁移文件与 `web/internal/domain/` 中的实体定义 +- 数据库迁移文件与 `admin/internal/domain/` 中的实体定义 - 真机取证产物(截图、页面 XML)及其记录的 App 版本 - 当前仓库代码 @@ -161,8 +162,8 @@ cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决 | 做什么 | 读什么 | | --- | --- | -| web 页面 | `02-requirements.md` 验收 → `07-user-stories.md` → `08-interaction-checklist.md` → `routes.md` → `04-architecture.md` | -| desk 界面 | 同上,`routes.md` 看第三节桌面端结构 | +| 采购服务页面 | `02-requirements.md` 验收 → `07-user-stories.md` → `08-interaction-checklist.md` → `routes.md` → `04-architecture.md` | +| 采购工具界面 | 同上,`routes.md` 看第三节桌面端结构 | | 设备侧 API | `api.md` → `04-architecture.md` 数据模型与鉴权边界 | | 真机自动化 | `04-architecture.md` 第三节两趟执行与三道闸门 → 第四节边界 → `api.md` 第三节模块合约 → **先真机取证** | | 数据模型 | `04-architecture.md` 第五节;schema 变化必须同步 `api.md` 和 `current-state.md` | @@ -174,11 +175,11 @@ cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决 > [`03-tech-stack.md`](03-tech-stack.md) 和 [`current-state.md`](current-state.md)。 ```bash -# web 端(改了 Go 代码后) +# 采购服务 admin/(改了 Go 代码后) go test ./... go vet ./... -# desk 端(改了 Python 代码后) +# 采购工具 client/(改了 Python 代码后) python -m unittest discover -s tests -t . python -m compileall -q src tests diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 910fa4c..8a0c5f4 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -3,9 +3,10 @@ > 「用什么」的统一速查表。选型与理由在此集中维护;「怎么把它们搭起来」见 > [架构设计](04-architecture.md)。未定项必须标为待定,不要让 agent 在代码里自行决定。 -本项目是**双端**结构,两端技术栈独立,通过 HTTP 契约耦合。 +本项目是**双产品**结构:采购服务位于 `admin/`,采购工具位于 `client/`;两套技术栈独立, +通过 HTTP 契约耦合。 -## 一、web 端(Go) +## 一、采购服务(网页端,`admin/`,Go) | 维度 | 选型 | 状态 | 理由 / 说明 | | --- | --- | --- | --- | @@ -21,7 +22,7 @@ | 测试 | `go test` | 已定 | 标准库足够 | | 部署 | 单二进制 + 数据目录 | 已定 | 运营电脑本机运行 | -## 二、desk 端(Python) +## 二、采购工具(Windows 桌面端,`client/`,Python) | 维度 | 选型 | 状态 | 理由 / 说明 | | --- | --- | --- | --- | @@ -31,7 +32,7 @@ | 桌面 GUI | `PySide6` | 已定 | 前序项目已验证;执行员需要看设备状态和批次进度 | | 截图处理 | `Pillow` | 已定 | 判断页面是否渲染完成,避免保存白屏壳层 | | HTTP 客户端 | 标准库 `urllib` 或 `httpx` | **待定** | 先用标准库;确有重试/连接池需求再评估 | -| Excel | 不引入 | 已定 | Excel 解析移到 web 端;desk 端不再直接读表 | +| Excel | 不引入 | 已定 | Excel 解析移到采购服务;采购工具不再直接读表 | | 测试 | `unittest`(标准库) | 已定 | 前序项目 171 项测试均用标准库,无需 pytest | | 打包 | `pyinstaller` | 已定 | 交付给运营电脑;开发期依赖 | @@ -39,9 +40,9 @@ | 维度 | 选型 | 状态 | 说明 | | --- | --- | --- | --- | -| 调用位置 | desk 端 | 已定 | PC 有算力;改 prompt 不需要重新打包 | +| 调用位置 | 采购工具 | 已定 | PC 有算力;改 prompt 不需要重新打包 | | provider | 待定 | **待定** | 需先确认预算与合规;不得由 agent 自行选定 | -| 凭据存储 | desk 端本机配置文件,不入库、不上传 | 已定 | web 端不保存、不代理、不下发任何模型凭据 | +| 凭据存储 | 采购工具本机配置文件,不入库、不上传 | 已定 | 采购服务不保存、不代理、不下发任何模型凭据 | | 输入 | 完整节点树 XML + 页面截图 | 已定 | `dump_hierarchy(compressed=False)` 不丢节点 | ## 四、决策记录与演进 @@ -50,10 +51,10 @@ 实时视图再评估,届时以整页替换为单位迁移,不做半 SPA。 - **SQLite 而不是 Postgres。** MVP 单机、单写入者。出现多实例或跨机访问需求时再迁移; 数据访问层不得写死 SQLite 方言。 -- **Excel 解析放 web 端而不是 desk 端。** 建单入口集中在一处才能统一审计。代价是要在 +- **Excel 解析放采购服务而不是采购工具。** 建单入口集中在一处才能统一审计。代价是要在 Go 侧重写表头校验和行级报错,不能直接复用前序项目的 Python 实现。 -- **desk 端不持有业务权威。** 金额上限、授权有效性、任务状态流转的判定权在 web 端; - desk 端本地校验只作为第二道防线,两边不一致时一律转人工。 +- **采购工具不持有业务权威。** 金额上限、授权有效性、任务状态流转的判定权在采购服务; + 采购工具本地校验只作为第二道防线,两边不一致时一律转人工。 - **不引入 pytest / 不引入 ORM。** 同一职责不并存两套方案。 ## 五、构建与运行命令 @@ -61,7 +62,7 @@ > **占位符。** 代码尚未初始化,以下命令分别在 T-001 / T-002 落地后由对应任务替换为真实可运行命令, > 并同步到 [`00-ai-start-here.md`](00-ai-start-here.md) 和 [`current-state.md`](current-state.md)。 -| 用途 | web 端 | desk 端 | +| 用途 | 采购服务(`admin/`) | 采购工具(`client/`) | | --- | --- | --- | | 安装依赖 | `go mod download` | `python -m venv .venv` + `pip install -r requirements.txt` | | 本地开发 | 【T-001 填写】 | 【T-002 填写】 | @@ -80,14 +81,14 @@ $env:GOTOOLCHAIN = "local" | 层级 | 触发条件 | 命令 / 操作 | 通过证据 | | --- | --- | --- | --- | -| 任务相关验证 | 每个任务必跑 | 改 web 端跑 `go test ./...` + `go vet ./...`;改 desk 端跑 `python -m unittest discover -s tests -t .` + `python -m compileall -q src tests` | 退出码 0、测试数 | +| 任务相关验证 | 每个任务必跑 | 改 `admin/` 跑 `go test ./...` + `go vet ./...`;改 `client/` 跑 `python -m unittest discover -s tests -t .` + `python -m compileall -q src tests` | 退出码 0、测试数 | | 完整门禁 | 发布前;修改 HTTP 契约、数据库 schema、依赖或构建配置时;跨端改动时 | 两端全部测试 + 静态检查 + 两端构建 | 退出码 0、测试数、产物路径 | | 人工 / 设备验收 | 任何涉及真机页面判据、下单动作或付款路径的任务 | 连接真机执行,记录设备型号、Android 版本、拼多多版本、goods_id、截图与页面 XML 路径 | 人工结论 + 证据文件路径 | - 任务相关验证不能省略。跨端契约改动必跑完整门禁——两端会同时坏。 - **真机验收只能由人完成。** agent 不得据自身判断把需要真机的任务标为 `DONE`。 - 真机验收必须记录**拼多多 App 版本**。页面判据与版本绑定,换版本即失效。 -- 交付 desk 端安装包时记录产物路径与 SHA-256;**版本号不能单独证明部署的是本次构建**。 +- 交付采购工具安装包时记录产物路径与 SHA-256;**版本号不能单独证明部署的是本次构建**。 ## 七、依赖纪律 diff --git a/docs/04-architecture.md b/docs/04-architecture.md index b5f27d8..3bb7794 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -11,7 +11,7 @@ └────────────┬───────────┴────────────────────┘ v ┌─────────────────────────────────────────┐ - │ web 端(Go 单二进制) │ + │ 采购服务(admin/,Go 单二进制) │ │ · 建单、批量开始试选与任务生命周期 │ │ · 候选确认与下单授权(唯一决策权威) │ │ · 证据存储与审计 │ @@ -21,7 +21,7 @@ │ Bearer Token + 设备绑定 v ┌─────────────────────────────────────────┐ - │ desk 端(Python + PySide6) │ + │ 采购工具(client/,Python + PySide6) │ │ · 领任务、跑流程、回传结果 │ │ · 本地执行轨迹与证据落盘 │ │ · AI 辅助调用(P1) │ @@ -35,15 +35,16 @@ 组件落位: -- web 端:Go + gin,入口 `web/cmd/server/main.go`,模板 `web/internal/transport/webui/templates/` -- desk 端:Python,入口 `desk/src/main.py`,真机流程 `desk/src/android/pdd_flow.py` +- 采购服务:Go + gin,入口 `admin/cmd/server/main.go`,模板 + `admin/internal/transport/webui/templates/` +- 采购工具:Python,入口 `client/src/main.py`,真机流程 `client/src/android/pdd_flow.py` - 数据库:SQLite,迁移由 goose 管理 -- 证据存储:web 端本地文件系统,SHA-256 寻址 -- 外部服务:顺运宝 ERP(只读)、AI provider(P1,仅 desk 端调用) +- 证据存储:采购服务本地文件系统,SHA-256 寻址 +- 外部服务:顺运宝 ERP(只读)、AI provider(P1,仅采购工具调用) ## 二、职责划分 -### web 端 +### 采购服务(网页端,`admin/`) **独占**: @@ -60,7 +61,7 @@ - 不保存、代理或下发任何 AI provider 凭据 - 不解析拼多多页面 -### desk 端 +### 采购工具(桌面端,`client/`) **独占**: @@ -72,7 +73,7 @@ **不做**: -- **不自行决定买哪个候选**——必须等 web 端的授权 +- **不自行决定买哪个候选**——必须等采购服务的授权 - **不自行放宽金额上限**——本地校验只能更严,不能更松 - 不直接读 Excel 或访问 ERP - 不在没有授权的情况下执行任何创建订单的动作 @@ -87,14 +88,14 @@ 这是本项目最核心的结构决策。**MVP 只做 A 路径(任务自带商品链接),分两趟跑完。** ```text - ┌──────────── web 端开始第一趟 ────────────┐ + ┌────────── 采购服务开始第一趟 ───────────┐ │ 新任务先保存为 DRAFT │ │ 管理员在任务表格勾选一条或多条 │ │ 原子转为 PENDING,只进入试选队列 │ └────────────────────┬─────────────────────┘ v ┌──────────────── 第一趟:试选 ────────────────┐ - │ desk 端轮询领取 PENDING 任务 │ + │ 采购工具轮询领取 PENDING 任务 │ │ 1. open_product(url) │ │ 2. 打开规格面板 │ │ 3. 按维度精确勾选颜色分类、尺码 │ @@ -107,14 +108,14 @@ 任务转 WAITING_CONFIRMATION │ ┌────────────────────┴─────────────────────────┐ - │ 人在 web 端确认:机器选对了吗 │ + │ 人在采购服务确认:机器选对了吗 │ │ 看:需求 vs 选中规格、单价、合计、截图 │ │ 点「确认下单(不付款)」→ 签发授权,锁定授权价 │ │ 或「退回,不买」→ 任务终止 │ └────────────────────┬─────────────────────────┘ v ┌──────────────── 第二趟:下单 ────────────────┐ - │ desk 端轮询拿到授权 │ + │ 采购工具轮询拿到授权 │ │ 1. 重新 open_product(url) │ │ 2. 重新按维度精确勾选同一规格 │ │ 3. 【闸门二】重读单价,必须与授权价一致 │ @@ -179,15 +180,15 @@ V2 实现时仍遵守:**图搜的唯一产出是 goods_id**,不在搜索结 | 安全校验 | 检测到验证码、风控、人脸、短信校验立即停止,不尝试绕过 | 封号 / 违规 | | 敏感信息 | 只读非敏感摘要,不提取收货地址原文、手机号、支付凭据 | 隐私泄露 | | 授权一次性 | 一笔授权只能产生一笔订单,重复提交幂等 | 重复采购 | -| 服务端提交围栏 | 真机点击前必须由 web 端原子冻结授权并创建唯一提交记录;失败或响应不明不得点击 | 并发 / 断网导致重复下单 | +| 服务端提交围栏 | 真机点击前必须由采购服务原子冻结授权并创建唯一提交记录;失败或响应不明不得点击 | 并发 / 断网导致重复下单 | | App 版本失配即停 | 运行版本与本项目已取证版本不一致时停止领取真机任务,先重新取证 | 旧判据误点新页面 | ### 提交订单的四个前置条件 这是本项目唯一会创建真实待付款订单的动作。**四者同时满足才允许点击,且只点一次:** -1. **授权存在且未消费,并已建立服务端提交围栏**——web 端已签发、desk 端已 ack; - 真机点击前,web 端在一个原子事务中把授权从可执行态冻结为本次唯一 +1. **授权存在且未消费,并已建立服务端提交围栏**——采购服务已签发、采购工具已 ack; + 真机点击前,采购服务在一个原子事务中把授权从可执行态冻结为本次唯一 `order_submission`。围栏接口失败或响应不明时不得点击。 2. **闸门二通过**——第二趟重读的单价与授权时锁定的价格一致。 3. **闸门三通过**——订单确认页「实付款」不超过授权总额上限。 @@ -211,9 +212,9 @@ V2 实现时仍遵守:**图搜的唯一产出是 goods_id**,不在搜索结 1. **dry-run(只读演练)**:进入订单确认页,读取规格、数量和「实付款」,确认提交控件 唯一,上传证据后退出。该阶段绝不点击「提交订单」,也不消费授权。 -2. **提交围栏**:真实第二趟再次读取并通过三道闸门后,desk 端向 web 端申请围栏。web 端 +2. **提交围栏**:真实第二趟再次读取并通过三道闸门后,采购工具向采购服务申请围栏。采购服务 原子校验任务版本、命令、未消费授权和唯一性,创建 `order_submissions` 记录并冻结授权。 - 只有明确收到成功响应,desk 端才可点击一次。 + 只有明确收到成功响应,采购工具才可点击一次。 3. **结果调和**:点击后只上报观察结果。明确创建则转 `WAITING_PAYMENT`;超时、外部支付、 安全校验或断连均转 `RECONCILIATION_REQUIRED`,保留额度并由人核查。**不得释放围栏、 重新签发授权或自动重试点击。** @@ -307,7 +308,7 @@ MVP 的授权没有「选择理由 / 拒绝理由」——那是从多个候选 ### 5.2 状态机 -任务状态(web 端权威)。创建与开始试选分离;两趟执行对应两次 `CLAIMED → RUNNING`: +任务状态(采购服务权威)。创建与开始试选分离;两趟执行对应两次 `CLAIMED → RUNNING`: ```text DRAFT ─start trial→ PENDING ─┐ @@ -331,7 +332,7 @@ PENDING_RETRIAL ─────────────┴─claim→ CLAIMED | `RUNNING(TRIAL)` | 第一趟试选中:正在勾选规格、读价、截图 | | `WAITING_CONFIRMATION` | 试选已回传,**等人确认机器选对了没** | | `PENDING_RETRIAL` | 旧授权已过期或在围栏前被放弃,必须重新跑第一趟取得新价格 | -| `AUTHORIZED` | 已签发授权,等 desk 端下一轮轮询领走 | +| `AUTHORIZED` | 已签发授权,等采购工具下一轮轮询领走 | | `ORDERING` | 第二趟下单中:重新选规格、过闸门二三、提交订单 | | `WAITING_PAYMENT` | 订单已创建,等人在拼多多付款。**这不是成功** | | `RECONCILIATION_REQUIRED` | 已建立提交围栏,但点击结果不明确;可能已创建订单,只能核查,不能重试 | @@ -355,7 +356,7 @@ PENDING_RETRIAL ─────────────┴─claim→ CLAIMED - 每笔授权带 `expires_at`。**仅在尚未建立提交围栏时**,超时自动转 `EXPIRED`。 - 授权 `EXPIRED` 后任务转 `PENDING_RETRIAL`,先重新跑第一趟取得新价格,再回到人工确认; 不允许在旧 `spec_trials` 上直接重新确认。 -- web 端在围栏建立前提供「放弃当前授权」入口;围栏建立后改为「进入人工核查」,不得 +- 采购服务在围栏建立前提供「放弃当前授权」入口;围栏建立后改为「进入人工核查」,不得 作废或释放授权。 - **任何时候都不允许出现「任务停在某状态且界面上没有任何可用动作」的组合。** 这是验收项,不是实现细节。 @@ -367,12 +368,12 @@ PENDING_RETRIAL ─────────────┴─claim→ CLAIMED | 数据 | 位置 | 理由 | | --- | --- | --- | -| 候选商品页 / 规格页截图 | 上传 web 端 | 管理员做授权决策必须看 | -| 订单确认页截图 | 上传 web 端 | 授权后核对与审计必须留 | -| 订单核对截图 | 上传 web 端 | 资金核对证据 | -| 完整节点树 XML | **仅 desk 端本地** | 体积大、含页面全文、只用于排障 | -| AI 调用记录(P1) | **仅 desk 端本地** | 含 prompt / 响应全文,脱敏成本高 | -| 失败现场快照 | 仅 desk 端本地,可按需手工导出 | 同上 | +| 候选商品页 / 规格页截图 | 上传采购服务 | 管理员做授权决策必须看 | +| 订单确认页截图 | 上传采购服务 | 授权后核对与审计必须留 | +| 订单核对截图 | 上传采购服务 | 资金核对证据 | +| 完整节点树 XML | **仅采购工具本地** | 体积大、含页面全文、只用于排障 | +| AI 调用记录(P1) | **仅采购工具本地** | 含 prompt / 响应全文,脱敏成本高 | +| 失败现场快照 | 仅采购工具本地,可按需手工导出 | 同上 | 上传前必须脱敏:**不上传含收货地址、手机号、支付凭据的截图区域或文本。** @@ -397,9 +398,9 @@ PENDING_RETRIAL ─────────────┴─claim→ CLAIMED 2. **Phase 1 真机取证**:WiFi ADB 连通;打开商品 → 打开规格面板 → 按维度精确勾选颜色 分类和尺码 → **读到该 SKU 单价** → 设数量 → 进订单确认页 → 读「实付款」。 **结论写入文档,判据带拼多多 App 版本。** -3. **Phase 2 web 端核心**:数据模型与状态机、手工建单、任务查询、试选结果接收、 +3. **Phase 2 采购服务核心**:数据模型与状态机、手工建单、任务查询、试选结果接收、 确认页与授权签发、**授权超时与放弃**。 -4. **Phase 3 双端打通**:设备侧 API、desk 端 `HttpTaskSource`/`HttpResultSink`、 +4. **Phase 3 双端打通**:设备侧 API、采购工具 `HttpTaskSource`/`HttpResultSink`、 定时轮询、第一趟试选端到端。 5. **Phase 4 闭环收尾**:第二趟下单(含三道闸门与提交)、失败分类、完整验收、打包。 6. **V2 及以后**:图片搜索路径、候选对照台、Excel 导入、ERP 建单、订单自动核对、AI 辅助。 @@ -412,7 +413,7 @@ PENDING_RETRIAL ─────────────┴─claim→ CLAIMED ```text cmbuyer/ ├── docs/ -├── web/ # Go +├── admin/ # 采购服务(Go) │ ├── cmd/server/ │ ├── internal/ │ │ ├── domain/ # 实体与状态机,无外部依赖 @@ -422,7 +423,7 @@ cmbuyer/ │ │ │ └── webui/ # 管理页面 + 模板 + 静态资源 │ │ └── storage/ # SQLite 与证据资产 │ └── migrations/ -├── desk/ # Python +├── client/ # 采购工具(Python) │ ├── src/ │ │ ├── android/ # adb / device / pdd_flow │ │ ├── core/ # models / task_runner / sources 抽象 @@ -432,7 +433,7 @@ cmbuyer/ └── scripts/ ``` -`desk/src/core/sources.py` 必须保留 `TaskSource` / `ResultSink` 抽象,执行器只依赖抽象。 +`client/src/core/sources.py` 必须保留 `TaskSource` / `ResultSink` 抽象,执行器只依赖抽象。 这样离线 Excel 模式可作为降级路径存在,且执行器不因来源变化而改动。 ## 九、架构纪律 diff --git a/docs/05-coding-rules.md b/docs/05-coding-rules.md index 7560b18..4479cba 100644 --- a/docs/05-coding-rules.md +++ b/docs/05-coding-rules.md @@ -73,7 +73,7 @@ ## 3. 事实来源纪律 -- 只相信本目录文档、数据库迁移、`web/internal/domain/`、真机取证产物和当前代码。 +- 只相信本目录文档、数据库迁移、`admin/internal/domain/`、真机取证产物和当前代码。 - **前序项目 `cmroubao` / `cmpdd` 是设计依据,不是事实来源。** 引用其结论必须重新验证。 - 不从备份、草稿、旧导出文件里推断当前事实。 - 不虚构字段、接口、状态码、配置项。 @@ -91,10 +91,10 @@ ## 5. 架构纪律 - 技术栈以 `03-tech-stack.md` 为准;新增依赖前先说明用途、替代方案和维护成本。 -- 双端职责以 `04-architecture.md` 第二节为准:**desk 端不持有业务权威**,不自行决定 +- 双端职责以 `04-architecture.md` 第二节为准:**采购工具不持有业务权威**,不自行决定 买哪个、不自行放宽金额上限。 - HTTP 契约以 `api.md` 为准,**这是双端之间的唯一权威**;契约改动必跑完整门禁。 -- desk 端执行器只依赖 `TaskSource` / `ResultSink` 抽象,不认识来源。 +- `client/` 中的采购工具执行器只依赖 `TaskSource` / `ResultSink` 抽象,不认识来源。 - 两端校验结果不一致时**转人工**,不取任一方结论。 ## 6. 代码规范 @@ -136,11 +136,11 @@ - [ ] 回复里如实说明跑了什么命令、结果如何。 ```bash -# web 端 +# 采购服务(admin/) go test ./... go vet ./... -# desk 端 +# 采购工具(client/) python -m unittest discover -s tests -t . python -m compileall -q src tests ``` diff --git a/docs/06-tasks.md b/docs/06-tasks.md index d32feb5..7511bb4 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -21,8 +21,8 @@ | ID | 任务 | 依赖 | 验收要点 | | --- | --- | --- | --- | -| T-001 | 初始化 web 端 Go 骨架 | - | `go test ./...`、`go vet ./...` 可运行;健康检查端点可访问;用真实命令替换 `03-tech-stack.md`、`00-ai-start-here.md`、`current-state.md` 中的占位命令 | -| T-002 | 初始化 desk 端 Python 骨架 | - | 虚拟环境、`requirements.txt`、`unittest` 可运行;`python -m compileall` 通过;日志与产物目录策略明确且不记录敏感信息 | +| T-001 | 初始化采购服务 `admin/` Go 骨架 | - | 在 `admin/` 创建 Go 模块;`go test ./...`、`go vet ./...` 可运行;健康检查端点可访问;用真实命令替换 `03-tech-stack.md`、`00-ai-start-here.md`、`current-state.md` 中的占位命令 | +| T-002 | 初始化采购工具 `client/` Python 骨架 | - | 在 `client/` 创建虚拟环境、`requirements.txt` 与应用骨架;`unittest`、`python -m compileall` 可运行;日志与产物目录策略明确且不记录敏感信息 | | T-003 | 建立 `init.ps1` 统一入口 | T-001, T-002 | 一条命令完成两端安装与基础验证并打印启动命令;未配置时主动失败而不是静默跳过 | | T-004 | 建立核心数据模型与状态机 | T-001 | `tasks`、`order_authorizations`、`order_submissions` 表与 `04-architecture.md` 一致;状态流转有单元测试;金额用十进制字符串 | | T-005 | 网页端 MVP 交互原型 | - | “采购服务”的 `docs/design/web-*.html` 单文件假数据原型覆盖登录、表格工作台、创建弹窗、批量开始试选、路由化详情抽屉与完整详情;键盘、窄屏、空态 / 错误 / 加载、围栏后调和状态可演示;经人工确认前保持 `DOING` | @@ -47,7 +47,7 @@ > **T-106 与 T-107 只做只读演练,不得点击提交订单。** 本路线图中首次允许创建真实订单 > 的任务是 T-401;执行前必须取得明确授权,并在任务文件记录订单是否产生、如何处置。 -### Phase 2 · web 端核心 +### Phase 2 · 采购服务核心 | ID | 任务 | 依赖 | 验收要点 | | --- | --- | --- | --- | @@ -66,7 +66,7 @@ | --- | --- | --- | --- | | T-301 | 设备凭据与身份隔离(F-013) | T-201 | 设备 Bearer 不能建单或授权;管理会话不能调设备接口;凭据可撤销 | | T-302 | 原子领取与租约(F-005) | T-301, T-004 | 并发领取只有一个成功;重复领取重放同一结果;**同时支持领取待试选与已授权两类**;`claim_token` 与 `claim_generation` 校验生效 | -| T-303 | desk 端 `HttpTaskSource` / `HttpResultSink` | T-302, T-002 | 执行器只依赖抽象;测试假数据与断连 JSONL 暂存不扩大 Excel MVP 范围;补传使用幂等键 | +| T-303 | 采购工具 `HttpTaskSource` / `HttpResultSink` | T-302, T-002 | 执行器只依赖抽象;测试假数据与断连 JSONL 暂存不扩大 Excel MVP 范围;补传使用幂等键 | | T-304 | 定时轮询与会话边界 | T-303, T-006 | 只在执行员启动的会话内轮询,关窗口即停;连续失败达阈值自动停止并提示原因;间隔可配置 | | T-306 | 证据上传与分层 | T-304, T-103 | 规格面板截图与订单截图上传服务端并脱敏;节点树与失败现场留本地;上传前校验不含地址与手机号 | | T-305 | **第一趟试选端到端** | T-306, T-104 | 从领取跑到试选回传:开商品、勾选规格、读单价、截图、退出释放手机;任务转「等你确认」;全程有事件与证据 | @@ -79,7 +79,7 @@ | T-402 | 待付款收口与手工完成 | T-401 | 详情页展示订单截图与授权信息供核对;人付款后手工标记完成;**待付款不等于成功** | | T-403 | 失败分类与证据归档(F-011) | T-401 | 覆盖需求列出的全部失败原因;终态一次原子回写,不落中间态 | | T-404 | 完整验收 MVP | T-402, T-403 | `02-requirements.md` 的 P0 验收全部通过;真机记录写入任务文件与 `current-state.md` | -| T-405 | desk 端打包与运行文档 | T-404 | 运营电脑可按文档运行;记录产物 SHA-256 | +| T-405 | 采购工具打包与运行文档 | T-404 | 运营电脑可按文档运行;记录产物 SHA-256 | ### V2 及以后(不在 MVP,编号预留) @@ -115,6 +115,6 @@ - 完整 RBAC(F-105) - 多平台比价(F-106) - 证据保留期与自动清理策略 -- web 端从 SQLite 迁移到 Postgres 的评估 +- 采购服务从 SQLite 迁移到 Postgres 的评估 - 设备凭据轮换机制 - 定时轮询间隔的风控友好节奏(需真机观察后确定) diff --git a/docs/README.md b/docs/README.md index 228717a..a6c2e45 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,9 +2,10 @@ ## 一句话定位 -cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决策,**桌面端**驱动 Android -手机在拼多多完成选规格和下单,**付款始终由人完成**。第一版先跑通「手工建单 → 定时领取 -→ 第一趟试选 → 人工确认 → 第二趟下单 → 待付款」闭环。 +cmbuyer 是一个自动化采购系统:**采购服务**(网页端,`admin/`)负责建单与人工决策, +**采购工具**(Windows 桌面端,`client/`)驱动 Android 手机在拼多多完成选规格和下单, +**付款始终由人完成**。第一版先跑通「手工建单 → 定时领取 → 第一趟试选 → 人工确认 → +第二趟下单 → 待付款」闭环。 ## 文档导航 @@ -21,7 +22,7 @@ cmbuyer 是一个自动化采购系统:**网页端**负责建单与人工决 - [任务路线图](06-tasks.md):阶段划分、里程碑、建议拆分清单。 - [任务文件](tasks/README.md):一任务一文件约定与真机验收要求。 - [API 合约](api.md):**双端之间的唯一权威**,含设备侧接口与本地模块合约。 -- [路由与页面结构](routes.md):web 页面路由与 desk 端界面结构。 +- [路由与页面结构](routes.md):采购服务页面路由与采购工具界面结构。 - [交互清单](08-interaction-checklist.md):IX 编号、状态与异常清单、无障碍要求。 - [设计原型输入约定](design/README.md):单文件 HTML 低保真原型的形态与边界。 - [当前实现状态](current-state.md):当前快照、可运行命令、下一步任务、已知风险。 diff --git a/docs/api.md b/docs/api.md index d3ef805..bd3cd3a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,6 @@ # API 合约 -> 本文定义 web 端对外的 HTTP 接口,以及 desk 端本地模块的合约。 +> 本文定义采购服务(`admin/`)对外的 HTTP 接口,以及采购工具(`client/`)本地模块的合约。 > **这是双端之间的唯一权威。** 实现前可细化,但不得在代码里另起一套不兼容接口。 > > 本合约的设备心跳、任务领取、事件、证据、授权命令与 ack 结构参考了前序项目 @@ -20,7 +20,7 @@ | 客户端 | 方式 | 说明 | | --- | --- | --- | | 管理 Web | Session Cookie + CSRF Token | 表单提交必须带 CSRF | -| desk 端 | `Authorization: Bearer ` | 凭据绑定设备标识,可单独撤销 | +| 采购工具 | `Authorization: Bearer ` | 凭据绑定设备标识,可单独撤销 | | ERP 对接 | `Authorization: Bearer ` | 只能调用货运同步接口 | 三种身份互不通用。设备凭据**不能**创建任务或签发授权;管理会话**不能**调用设备接口。 @@ -79,7 +79,7 @@ } ``` -- 只接受当前状态为 `DRAFT` 的任务;该动作含义是允许 desk 端领取第一趟试选,**不签发下单 +- 只接受当前状态为 `DRAFT` 的任务;该动作含义是允许采购工具领取第一趟试选,**不签发下单 授权、不建立提交围栏、不创建订单、不付款**。 - 服务端在一个事务内校验全部任务存在、属于当前管理范围、状态仍为 `DRAFT` 且版本匹配, 然后统一转 `PENDING` 并递增版本。任一项失败返回 `409 conflict`,整批不产生部分成功。 @@ -109,7 +109,7 @@ 转人工核查,不能重新开放为可执行。 - MVP 没有「选择理由 / 拒绝理由」——那是多候选择一时的留档需求。这里只有可选 `note`。 -## 二、设备侧接口(desk 端调用) +## 二、设备侧接口(采购工具调用) 全部要求有效设备 Bearer;凭据中的设备标识是权威身份,请求体里的设备字段仅作核对。 @@ -192,7 +192,7 @@ ### dry-run 与真实提交协议 -`POST /api/v1/tasks/{id}/order-dry-runs/start` 创建或重放一次演练记录。desk 端随后只允许 +`POST /api/v1/tasks/{id}/order-dry-runs/start` 创建或重放一次演练记录。采购工具随后只允许 进入订单确认页、读取非敏感摘要和验证提交控件唯一,不允许点击。完成后调用 `POST /api/v1/order-dry-runs/{rid}/ready`: @@ -210,7 +210,7 @@ - dry-run 只证明当次页面达到 `READY`,不冻结授权,也不能作为稍后真实点击时的页面事实。 - `has_address` 只报布尔值,**不得回传地址原文或手机号**。 -真实第二趟重新通过三道闸门后,desk 端在点击前调用 +真实第二趟重新通过三道闸门后,采购工具在点击前调用 `POST /api/v1/tasks/{id}/order-submissions/start`: ```json @@ -227,7 +227,7 @@ - 服务端在一个事务中校验命令、任务版本、授权未消费、闸门值与唯一性,创建或重放唯一 `order_submission` 并把授权置为 `FENCED`。同一授权或命令不得产生第二条提交记录。 -- 只有明确收到 `201/200` 且响应中的 `click_permitted: true`,desk 端才允许点击一次。 +- 只有明确收到 `201/200` 且响应中的 `click_permitted: true`,采购工具才允许点击一次。 超时、网络错误、冲突或响应无法解析时**不得点击**,转人工查询该幂等键。 - `dry_run_id` 只证明曾完成安全演练;服务端仍以本次真实提交请求携带的闸门读数复核。 @@ -255,7 +255,7 @@ - 拒绝含 `authorization:`、`api_key`、`bearer ` 的内容,防止凭据误入审计日志。 - **超长必须由客户端截断后再发,服务端拒绝而不是静默截断。** -## 三、desk 端本地模块合约 +## 三、采购工具本地模块合约 ### `TaskSource` / `ResultSink` @@ -275,14 +275,14 @@ class ResultSink(ABC): | 实现 | 用途 | | --- | --- | -| `HttpTaskSource` | 从 web 端领取任务(默认) | -| `HttpResultSink` | 回传结果到 web 端(默认) | +| `HttpTaskSource` | 从采购服务领取任务(默认) | +| `HttpResultSink` | 回传结果到采购服务(默认) | | `FixtureTaskSource` | 仅测试 / 演示:读取仓库内假数据,不接触真实订单 | | `JsonlResultSink` | 仅测试 / 断连暂存:本地追加写入,恢复连接后按幂等键补传 | ### 真机流程模块 -`desk/src/android/pdd_flow.py` 的公开入口,每个都不得越界: +`client/src/android/pdd_flow.py` 的公开入口,每个都不得越界: | 函数 | 输入 | 输出 | 副作用边界 | | --- | --- | --- | --- | diff --git a/docs/current-state.md b/docs/current-state.md index d05c200..0511206 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -14,13 +14,15 @@ - 日期:2026-08-03 - 阶段:**Phase 0 · 地基(先确认原型,尚未开始生产编码)** - MVP 形态:手工填链接建单 → 批量开始试选 → 定时轮询 → **第一趟试选** → 人工确认 → **第二趟下单** → 待付款 -- 技术栈:已定。web 端 Go 1.23+ / gin / SQLite;desk 端 Python 3.11+ / uiautomator2 / PySide6。 +- 技术栈:已定。采购服务(`admin/`)使用 Go 1.23+ / gin / SQLite;采购工具(`client/`) + 使用 Python 3.11+ / uiautomator2 / PySide6。 详见 [`03-tech-stack.md`](03-tech-stack.md) - 生产代码:**无**。仓库目前只有文档 - 测试:**无** - 数据:**无** -- 标准启动路径:`./init.ps1`(Windows)/ `./init.sh`。已存在但会因两端目录未初始化而以 - 退出码 3 结束并列出待办任务——这是预期行为 +- 标准启动路径:`./init.ps1`(Windows)/ `./init.sh`。已存在,以 `admin/go.mod` 和 + `client/requirements.txt` 判断两端是否初始化;当前会以退出码 3 结束并列出 T-001 / T-002 + ——这是预期行为 - 标准验证路径:目前只有 `python scripts/validate_agent_context.py` 可跑通 - 当前 blocker:T-005“采购服务”Web 表格工作台和 T-006 第三轮“采购工具”桌面工作区均已通过 自动检查;两者经人工确认前不开始对应生产 UI。 @@ -33,15 +35,15 @@ | `docs/tasks/` | 已有(2 个活动任务) | T-005 / T-006 均为 `DOING`,含 `README.md` 与 `_template.md` | | `docs/design/` | 已有(6 个原型) | web 登录 / 建单 / 工作台 / 详情,desk 采购执行 / 配置;待人工确认 | | `scripts/` | 已有 | `validate_agent_context.py` | -| `web/` | **待建** | Go 后端与管理页面(T-001) | -| `desk/` | **待建** | Python 桌面端(T-002) | +| `admin/` | **待建** | “采购服务”Go 后端与管理页面(T-001) | +| `client/` | **待建** | “采购工具”Python 桌面端(T-002) | | `init.ps1` / `init.sh` | 已有(骨架) | 统一入口。两端目录建好后由 T-003 补全并验证 | ## 任务状态 任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要: -- 已完成:无。 +- 已完成:T-007(项目 / 产品名称与 `admin/`、`client/` 源码目录契约)。 - 正在进行:T-005(“采购服务”网页端 MVP 交互原型)、T-006(桌面端 MVP 交互原型)。 T-005 已将三泳道改为传统表格并统一网页端品牌名称;T-006 第三轮已把执行记录详情从模态框 改为左侧内联双视图,支持连续选行、可见返回按钮、`Esc` 层级、异常读取状态和紧凑宽度 @@ -64,8 +66,8 @@ python scripts/validate_agent_context.py T-001 / T-002 完成后,本节替换为真实命令: ```bash -# web 端(T-001 后填写) -# desk 端(T-002 后填写) +# 采购服务 admin/(T-001 后填写) +# 采购工具 client/(T-002 后填写) # 统一入口(T-003 后填写) ``` diff --git a/docs/design/desk-execution.html b/docs/design/desk-execution.html index a6b415a..4aa7b54 100644 --- a/docs/design/desk-execution.html +++ b/docs/design/desk-execution.html @@ -644,7 +644,7 @@ fencing: { tone:'caution', title:'正在申请服务端提交围栏', text:'等待明确的 click_permitted。超时、冲突、网络失败或响应不明都不会点击。', web:'ok', device:'ok', version:'ok', leg:'ORDER · 申请围栏', legTone:'caution', action:'等待围栏响应', enabled:false, reason:'申请中禁止取消、关闭或重复申请。', session:'运行中', step:'申请唯一提交围栏', kind:'order', active:3, countdown:'暂停', fail:'0 / 3', lease:'02:31' }, fence_failed: { tone:'critical', title:'未获得点击许可', text:'围栏申请失败或响应不明:没有点击提交订单。请按幂等键核查服务端记录,不能直接重试。', web:'bad', device:'ok', version:'ok', leg:'ORDER · 未获许可', legTone:'critical', action:'打开围栏核查说明', enabled:true, safeAction:'review', reason:'只核查围栏状态,不会触发提交。', session:'已停止', step:'核查幂等键', kind:'order', active:3, countdown:'—', fail:'1 / 3', submission:'围栏状态未知 · 幂等键 DEMO-FENCE-042' }, fenced: { tone:'caution', title:'围栏已建立,只核对同一提交', text:'submission_id 已固定。内部流程至多点击一次;用户界面不提供提交、重领、放弃或重试入口。', web:'ok', device:'ok', version:'ok', leg:'ORDER · 已围栏', legTone:'caution', action:'打开同一提交核查说明', enabled:true, safeAction:'review', reason:'只能查看与调和同一提交记录。', session:'受控执行', step:'调和订单结果', kind:'order', active:5, countdown:'暂停', fail:'0 / 3', lease:'01:49', submission:'SUB-DEMO-0042 · FENCED' }, - waiting_payment: { tone:'success', title:'已明确创建待付款订单', text:'自动化已停止。系统不会付款;请人在拼多多核对后自行付款,再到 web 端标记。', web:'ok', device:'ok', version:'ok', leg:'WAITING_PAYMENT', legTone:'success', action:'查看待付款说明', enabled:true, safeAction:'paymentInfo', reason:'仅显示人工收口说明,不执行支付。', session:'已停止', step:'等待人工付款', kind:'order', active:6, countdown:'—', fail:'0 / 3', count:5, submission:'SUB-DEMO-0042 · SUBMITTED' }, + waiting_payment: { tone:'success', title:'已明确创建待付款订单', text:'自动化已停止。系统不会付款;请人在拼多多核对后自行付款,再到采购服务标记。', web:'ok', device:'ok', version:'ok', leg:'WAITING_PAYMENT', legTone:'success', action:'查看待付款说明', enabled:true, safeAction:'paymentInfo', reason:'仅显示人工收口说明,不执行支付。', session:'已停止', step:'等待人工付款', kind:'order', active:6, countdown:'—', fail:'0 / 3', count:5, submission:'SUB-DEMO-0042 · SUBMITTED' }, uncertain: { tone:'critical', title:'订单可能已创建,结果不明确', text:'保持金额预留和永久围栏,只核查 SUB-DEMO-0042;禁止重新领取、放弃授权或再次点击。', web:'ok', device:'ok', version:'ok', leg:'RECONCILIATION_REQUIRED', legTone:'critical', action:'打开人工核查说明', enabled:true, safeAction:'review', reason:'核查不会发起新的下单。可另行导出假证据摘要。', session:'已停止', step:'人工核查同一提交', kind:'order', active:5, countdown:'—', fail:'0 / 3', submission:'SUB-DEMO-0042 · 结果不明确' }, security: { tone:'critical', title:'检测到安全校验,已停止', text:'保留现场并转人工;不尝试绕过验证码、风控、人脸或短信校验。若已围栏,只调和同一提交。', web:'ok', device:'ok', version:'ok', leg:'待人工 · 安全校验', legTone:'critical', action:'打开人工核查说明', enabled:true, safeAction:'review', reason:'只显示核查说明,不提供绕过或继续入口。', session:'已停止', step:'等待人工处理', kind:'order', active:5, countdown:'—', fail:'1 / 3', submission:'SUB-DEMO-0042 · RECONCILIATION_REQUIRED' }, payment_handoff: { tone:'critical', title:'进入外部支付交接,已停止', text:'订单可能已创建、支付未完成。不会读取或输入支付凭据,也没有“继续付款”按钮。', web:'ok', device:'ok', version:'ok', leg:'待人工 · 外部支付', legTone:'critical', action:'打开人工核查说明', enabled:true, safeAction:'paymentInfo', reason:'只显示人工核查与付款边界。', session:'已停止', step:'人工核查同一提交', kind:'order', active:5, countdown:'—', fail:'0 / 3', submission:'SUB-DEMO-0042 · HANDED_OFF' }, diff --git a/docs/routes.md b/docs/routes.md index 2bdff19..cea05e4 100644 --- a/docs/routes.md +++ b/docs/routes.md @@ -1,9 +1,10 @@ # 路由与页面结构 -> 本文约定 web 端页面路由、页面职责和组件归属,以及 desk 端的界面结构。 +> 本文约定采购服务(网页端,`admin/`)页面路由、页面职责和组件归属,以及采购工具 +> (桌面端,`client/`)的界面结构。 > 具体交互行为以[交互清单](08-interaction-checklist.md)为准,接口形状以 [API 合约](api.md) 为准。 -## 一、web 端页面路由 +## 一、采购服务页面路由 网页端用户可见产品名统一为“采购服务”。`cmbuyer` 只作为仓库和系统内部标识,不出现在 网页标题、页头品牌或无障碍名称中;桌面端仍使用独立名称“采购工具”。 @@ -20,7 +21,7 @@ 登录后默认进入 `/tasks`。未登录访问受保护页面时跳转 `/login` 并携带站内返回路径; **只接受 `/tasks` 及其子路径**,拒绝绝对 URL、`//` 和反斜杠。 -## 二、web 端页面职责 +## 二、采购服务页面职责 ### 采购任务 `/tasks` @@ -86,7 +87,7 @@ 尺码、数量、价格上限。链接无法解析出 `goods_id` 时明确报错并保留已填内容;保存成功后 任务为 `DRAFT`,尚未进入设备领取队列。 -## 三、desk 端界面结构 +## 三、采购工具界面结构 桌面端产品名为“采购工具”,不是网页;使用顶部固定两页签,不做多级导航,默认打开采购执行: @@ -121,11 +122,11 @@ ## 四、导航规则 -- web 端从路由化详情抽屉返回工作台时不重新加载列表;完整详情页返回时保留原筛选条件。 -- desk 端启动时若服务端有本设备的活跃任务,优先恢复该任务,不允许直接领取下一条。 +- 采购服务从路由化详情抽屉返回工作台时不重新加载列表;完整详情页返回时保留原筛选条件。 +- 采购工具启动时若服务端有本设备的活跃任务,优先恢复该任务,不允许直接领取下一条。 - 第一趟试选完成后**必须退出商品页**再进入下一轮轮询,不停在规格面板等人。 -- desk 端在真机步骤执行期间禁用硬取消和关闭窗口,避免留下无法判定的中间态。 -- 任何进入外部支付页的情形,desk 端立即停止并跳回待人工,**不提供「继续」按钮**。 +- 采购工具在真机步骤执行期间禁用硬取消和关闭窗口,避免留下无法判定的中间态。 +- 任何进入外部支付页的情形,采购工具立即停止并跳回待人工,**不提供「继续」按钮**。 - dry-run 与真实下单必须使用不同的醒目标识;dry-run 不得出现可触发真实提交的控件。 - 已建立提交围栏后,不提供重新领取、重新提交或放弃授权,只能恢复同一提交记录并调和。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 36b9d10..979b0d3 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -16,7 +16,7 @@ | --- | --- | | T-0xx | Phase 0 地基 | | T-1xx | Phase 1 真机取证 | -| T-2xx | Phase 2 web 端核心 | +| T-2xx | Phase 2 采购服务核心 | | T-3xx | Phase 3 双端打通与第一趟试选 | | T-4xx | Phase 4 第二趟下单与收尾 | | T-5xx | V2 及以后(图搜、Excel、ERP、订单核对、AI 辅助) | @@ -46,8 +46,8 @@ needs_device: true # 是否需要真机验收 needs_human_review: false # 是否需要人审原型 / 文案 / 决策后才能 DONE write_paths: - docs/tasks/T-101.md - - desk/src/android/** - - desk/tests/** + - client/src/android/** + - client/tests/** --- ``` diff --git a/docs/tasks/T-007.md b/docs/tasks/T-007.md index 59d41ae..f3c30c5 100644 --- a/docs/tasks/T-007.md +++ b/docs/tasks/T-007.md @@ -3,7 +3,7 @@ id: T-007 title: 统一产品名称与源码目录契约 phase: 0 deps: [] -status: DOING +status: DONE created: 2026-08-03 context_ref: a7465d2 work_branch: task/t-007-project-naming-layout @@ -74,6 +74,19 @@ write_paths: ## 执行记录 -### 2026-08-03 · 待实现 +### 2026-08-03 · 命名与目录契约已统一 -- 已确认命名与目录映射;任务保持 `DOING`,先更新规范与入口脚本,再运行静态门禁。 +- 项目入口、技术栈、架构、API、路由、路线图和当前状态已统一为:整体项目 `cmbuyer`; + 采购服务是网页端,源码目录 `admin/`;采购工具是 Windows 桌面端,源码目录 `client/`。 +- 当前规范性源码路径已切换到 `admin/cmd/server/main.go`、`admin/internal/...`、 + `client/src/main.py`、`client/src/android/pdd_flow.py` 与 `client/tests/`。原型文件前缀、 + HTTP 路由和内部 `webui` 包保持不变。 +- `init.ps1` 与 `init.sh` 使用 `admin/go.mod`、`client/requirements.txt` 作为初始化哨兵, + 不再仅凭空目录判断。验证时发现工作区已有空 `admin/` / `client/`,旧判据误建了 + `client/.venv`;该虚拟环境创建时间与本轮验证一致,已移入回收站,两个目标目录均未删除。 +- `& .\init.ps1`:上下文清单通过,并按 Phase 0 约定以退出码 3 精确列出 + “T-001 初始化采购服务 admin/ Go 骨架”和“T-002 初始化采购工具 client/ Python 骨架”。 +- PowerShell `ScriptBlock` 解析与 `bash -n init.sh` 通过;路径审计未在当前入口 / 架构中发现 + `web/cmd`、`web/internal`、`desk/src`、`desk/tests`、`cd web` 或 `cd desk`。 +- `python scripts/validate_agent_context.py` 与 `git diff --check` 通过。没有创建或修改生产代码, + 没有触及 API 语义、页面判据或资金安全边界。 diff --git a/init.ps1 b/init.ps1 index ff9aeb9..bf008bc 100644 --- a/init.ps1 +++ b/init.ps1 @@ -4,8 +4,9 @@ # - Windows 原生 PowerShell:./init.ps1 # - WSL / Git Bash:./init.sh # -# 本项目是双端结构:web/(Go)+ desk/(Python)。脚本按目录是否存在分别处理, -# 未初始化的一端会跳过并提示对应任务,不会静默通过。 +# 本项目是双产品结构:admin/(采购服务,Go)+ client/(采购工具,Python)。脚本按 +# admin/go.mod 与 client/requirements.txt 两个初始化哨兵分别处理;未初始化的一端会跳过并 +# 提示对应任务,不会把空目录误判为可运行项目,也不会静默通过。 # # T-001 / T-002 / T-003 落地后,把下面的命令补全,并同步: # docs/03-tech-stack.md、docs/00-ai-start-here.md、docs/current-state.md @@ -16,49 +17,49 @@ Set-Location -Path $PSScriptRoot # Go 工具链固定用本机版本,避免自动下载 $env:GOTOOLCHAIN = "local" -$WebDir = Join-Path $PSScriptRoot "web" -$DeskDir = Join-Path $PSScriptRoot "desk" +$AdminDir = Join-Path $PSScriptRoot "admin" +$ClientDir = Join-Path $PSScriptRoot "client" $Pending = @() Write-Host "==> 当前目录: $($PWD.Path)" -# ---------------------------------------------------------------- web 端(Go) -if (Test-Path $WebDir) { - Write-Host "==> web 端:同步依赖" - Push-Location $WebDir +# ------------------------------------------------------- 采购服务(admin/,Go) +if (Test-Path (Join-Path $AdminDir "go.mod") -PathType Leaf) { + Write-Host "==> 采购服务:admin/ 同步依赖" + Push-Location $AdminDir try { go mod download - Write-Host "==> web 端:静态检查" + Write-Host "==> 采购服务:admin/ 静态检查" go vet ./... - Write-Host "==> web 端:测试" + Write-Host "==> 采购服务:admin/ 测试" go test ./... } finally { Pop-Location } } else { - Write-Host "==> web 端:目录 web/ 不存在,跳过。先做 T-001(初始化 web 端 Go 骨架)。" - $Pending += "T-001 初始化 web 端 Go 骨架" + Write-Host "==> 采购服务:admin/go.mod 不存在,跳过。先做 T-001(初始化 admin/ Go 骨架)。" + $Pending += "T-001 初始化采购服务 admin/ Go 骨架" } -# ------------------------------------------------------------ desk 端(Python) -if (Test-Path $DeskDir) { - Write-Host "==> desk 端:同步依赖" - Push-Location $DeskDir +# ---------------------------------------------------- 采购工具(client/,Python) +if (Test-Path (Join-Path $ClientDir "requirements.txt") -PathType Leaf) { + Write-Host "==> 采购工具:client/ 同步依赖" + Push-Location $ClientDir try { if (-not (Test-Path ".venv")) { python -m venv .venv } & ".venv\Scripts\python.exe" -m pip install -q -r requirements.txt - Write-Host "==> desk 端:语法检查" + Write-Host "==> 采购工具:client/ 语法检查" & ".venv\Scripts\python.exe" -m compileall -q src tests - Write-Host "==> desk 端:测试" + Write-Host "==> 采购工具:client/ 测试" & ".venv\Scripts\python.exe" -m unittest discover -s tests -t . } finally { Pop-Location } } else { - Write-Host "==> desk 端:目录 desk/ 不存在,跳过。先做 T-002(初始化 desk 端 Python 骨架)。" - $Pending += "T-002 初始化 desk 端 Python 骨架" + Write-Host "==> 采购工具:client/requirements.txt 不存在,跳过。先做 T-002(初始化 client/ Python 骨架)。" + $Pending += "T-002 初始化采购工具 client/ Python 骨架" } # ------------------------------------------------------------------ 文档自检 @@ -76,8 +77,8 @@ if ($Pending.Count -gt 0) { } Write-Host "==> 启动命令" -Write-Host " web 端: cd web ; go run ./cmd/server" -Write-Host " desk 端: cd desk ; .venv\Scripts\python.exe src/main.py" +Write-Host " 采购服务:cd admin ; go run ./cmd/server" +Write-Host " 采购工具:cd client ; .venv\Scripts\python.exe src/main.py" Write-Host "" Write-Host "基础验证失败时先修基线,不要在坏的起点上继续叠新功能。" Write-Host "真机连接与设备验收只能由人工完成,agent 不得据此把任务标为 DONE。" diff --git a/init.sh b/init.sh index ca31857..1dadb89 100644 --- a/init.sh +++ b/init.sh @@ -4,6 +4,8 @@ # 注意:本项目的构建与验证工具链只在 Windows 侧(Go SDK、Python 环境、Android SDK)。 # 在 WSL 中运行本脚本通常会因缺少工具链而失败——这是预期行为,不是 bug。 # 正式验证请在 Windows PowerShell 运行 ./init.ps1。 +# 双产品目录为 admin/(采购服务,Go)与 client/(采购工具,Python);分别以 go.mod 与 +# requirements.txt 作为初始化哨兵,不能只按空目录存在判断。 # # T-001 / T-002 / T-003 落地后补全命令,并同步: # docs/03-tech-stack.md、docs/00-ai-start-here.md、docs/current-state.md @@ -17,32 +19,32 @@ pending=() echo "==> 当前目录: $(pwd)" -# ---------------------------------------------------------------- web 端(Go) -if [ -d web ]; then - echo "==> web 端:同步依赖" - ( cd web && go mod download ) - echo "==> web 端:静态检查" - ( cd web && go vet ./... ) - echo "==> web 端:测试" - ( cd web && go test ./... ) +# ------------------------------------------------------- 采购服务(admin/,Go) +if [ -f admin/go.mod ]; then + echo "==> 采购服务:admin/ 同步依赖" + ( cd admin && go mod download ) + echo "==> 采购服务:admin/ 静态检查" + ( cd admin && go vet ./... ) + echo "==> 采购服务:admin/ 测试" + ( cd admin && go test ./... ) else - echo "==> web 端:目录 web/ 不存在,跳过。先做 T-001(初始化 web 端 Go 骨架)。" - pending+=("T-001 初始化 web 端 Go 骨架") + echo "==> 采购服务:admin/go.mod 不存在,跳过。先做 T-001(初始化 admin/ Go 骨架)。" + pending+=("T-001 初始化采购服务 admin/ Go 骨架") fi -# ------------------------------------------------------------ desk 端(Python) -if [ -d desk ]; then - echo "==> desk 端:同步依赖" - ( cd desk \ +# ---------------------------------------------------- 采购工具(client/,Python) +if [ -f client/requirements.txt ]; then + echo "==> 采购工具:client/ 同步依赖" + ( cd client \ && { [ -d .venv ] || python3 -m venv .venv; } \ && .venv/bin/python -m pip install -q -r requirements.txt ) - echo "==> desk 端:语法检查" - ( cd desk && .venv/bin/python -m compileall -q src tests ) - echo "==> desk 端:测试" - ( cd desk && .venv/bin/python -m unittest discover -s tests -t . ) + echo "==> 采购工具:client/ 语法检查" + ( cd client && .venv/bin/python -m compileall -q src tests ) + echo "==> 采购工具:client/ 测试" + ( cd client && .venv/bin/python -m unittest discover -s tests -t . ) else - echo "==> desk 端:目录 desk/ 不存在,跳过。先做 T-002(初始化 desk 端 Python 骨架)。" - pending+=("T-002 初始化 desk 端 Python 骨架") + echo "==> 采购工具:client/requirements.txt 不存在,跳过。先做 T-002(初始化 client/ Python 骨架)。" + pending+=("T-002 初始化采购工具 client/ Python 骨架") fi # ------------------------------------------------------------------ 文档自检 @@ -60,8 +62,8 @@ if [ ${#pending[@]} -gt 0 ]; then fi echo "==> 启动命令" -echo " web 端: cd web && go run ./cmd/server" -echo " desk 端: cd desk && .venv/bin/python src/main.py" +echo " 采购服务:cd admin && go run ./cmd/server" +echo " 采购工具:cd client && .venv/bin/python src/main.py" echo echo "基础验证失败时先修基线,不要在坏的起点上继续叠新功能。" echo "真机连接与设备验收只能由人工完成,agent 不得据此把任务标为 DONE。"