docs(project): align product names and source directories

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