Files
cmautobuy/docs/admin/01-requirements.md
T
chengmaandClaude Opus 5 90379347b4 docs: 建立 Admin 子项目的文档基线
仓库从单子项目变成两个:client(Python + PyQt5 桌面端)和
admin(Go + Gin + HTML 模板 Web 管理端)。两者技术栈完全不同,
规则各自独立,只通过三个 HTTP 接口交互。

新增 admin/AGENTS.md 和 docs/admin/ 共 9 份文档,结构与 docs/client/ 对齐。

关键设计决策(均已与用户确认)
- 四模块:蝦皮数据 / 顺运宝数据 / 采购任务 / 客户端列表,
  统一三段式布局(工具条 / 带勾选的表格 / 状态条)
- 蝦皮数据拆成商品表 + SKU 表:报表本身就是两层结构,
  且 PDD 链接是商品级的,放 SKU 级会重复维护
- pdd_data 存商品级,一个 PDD 商品采一次,所有订单共用
- SKU 映射独立成表且可复用,同一蝦皮 SKU 只需人工匹配一次
- PDD 链接人工填写,采集按钮就放在填链接的编辑弹窗里
- 任务分配给指定客户端;不加心跳,注册在领取时完成,
  在线状态由 last_seen_at 派生
- SQLite 驱动固定 modernc.org/sqlite(纯 Go 免 cgo,go build 直接出 exe)
- 不引入前端框架和 npm 构建,只允许原生 JS 或 htmx

基于样本文件 raw_data/蝦皮数据样本.xlsx 实测得出的约束
- 11287 行 = 5195 商品汇总行 + 6092 SKU 行,导入必须分开处理
- 商品規格ID 零重复,是天然主键
- 规格原文两种格式各占 53.4% / 46.6%,只解析【】会漏掉一半
- 平均每商品仅 1.17 个 SKU,报表不是全量目录,
  因此必须支持手动新增,且货运单外键不能加硬约束

同步更新
- 根 AGENTS.md 开头的指针改为两个子项目对照表
- docs/README.md 重构为双子项目索引
- .gitignore 加 admin/data/、admin.exe、Excel 锁文件

说明:Gitea 尚未配置,本次无对应工单号。
raw_data/ 未提交,含台币销售额等商业数据,待用户决定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 15:49:04 +08:00

311 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 01 Admin 产品需求基线
- 文档状态:基线草案,待需求评审
- 适用范围:`admin/`
- 产品类型:本地运行的 Web 管理端(Go + Gin + HTML 模板)
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。
没有标注的默认是 `[必须]`。看不懂的词查 [术语表](00-glossary.md)。
## 1. 背景与目标
我们在**蝦皮**(台湾)卖货,在**拼多多**(大陆)进货,中间由**顺运宝**提供货运单。
Admin 的职责是把这三方的数据串起来,最终产出 Client 能执行的采购任务。
产品目标:
1. 导入并维护蝦皮商品档案,为每个商品登记对应的拼多多链接。
2. 同步顺运宝货运单,知道"这单该买什么"。
3. 把蝦皮的颜色尺码,对应到拼多多的颜色尺码,**且这个对应关系可复用**。
4. 生成采购任务并分配给指定客户端,跟踪执行结果。
5. 管理客户端清单,知道谁在干活。
## 2. 用户与外部系统
- **操作人员:**导入报表、填 PDD 链接、发起采集、做规格匹配、创建并分配任务、处理异常。
- **Client:**领取任务、在安卓设备上执行、提交结果。契约见 [Client 侧文档](../client/04-admin-api-contract.md)。
- **蝦皮:**只提供 Excel 报表导出,**没有接口对接**。
- **顺运宝:**提供货运单,`[待定]` 同步方式待确认,MVP 先做占位按钮。
- **拼多多:**由 Client 操作,Admin 不直接接触。
## 3. 完整业务链路
这条链路是理解全部四个模块的关键,先看懂它:
```text
蝦皮报表 ──导入──→ 商品表 + SKU 表
│
编辑弹窗:人工填 PDD 链接
│ 点【采集】
↓
创建采集任务 → 分配 Client → 被领取
↓
Client 采回 PDD 的颜色尺码和价格
↓
存入 商品表.pdd_data
│
顺运宝货运单 ─同步─→ 货运单表
│ 双击行
↓
匹配弹窗:蝦皮规格 ←→ PDD 规格
(匹配过的自动带出,只有新规格要手工做)
↓
创建采购任务 → 分配 Client → 被领取
↓
Client 下单 → 提交结果 → 人工付款
```
两个要点:
- **采集是商品级的**,一个 PDD 商品采一次,所有相关订单共用结果。
- **匹配是 SKU 级的且可复用**,同一个蝦皮 SKU 只需人工匹配一次。
## 4. 产品范围
顶级导航固定四个模块,顺序不变:
| # | 模块 | 职责 |
|---|---|---|
| 1 | 蝦皮数据 | 商品档案、PDD 链接、发起采集 |
| 2 | 顺运宝数据 | 货运单、规格匹配、生成采购任务 |
| 3 | 采购任务 | 执行进度跟踪 |
| 4 | 客户端列表 | 客户端注册与状态 |
四个模块**统一使用三段式页面布局**:顶部工具条 / 中间带勾选的表格 / 底部状态条。
详见 [05 界面规范](05-ui-specification.md)。
### 4.1 蝦皮数据模块
**顶部工具条:** 导入按钮、商品 ID 搜索框、搜索按钮、删除按钮、批量采集按钮。
**中间表格**(按 SKU 展开显示,数据来自商品表和 SKU 表联查):
| 列 | 说明 |
|---|---|
| 勾选 | 支持批量删除、批量采集 |
| 商品 ID | 蝦皮商品编号 |
| 商品名称 | |
| 颜色 / 尺码 / 建议 | 从规格原文解析,解析不出来留空并标记 |
| PDD 链接 | 人工填写,空的要显眼 |
| 采集状态 | 见 §6 |
| 更新时间 | |
**双击行打开编辑弹窗**,这是 PDD 链接唯一的录入口:
- 可编辑:PDD 链接、颜色、尺码、建议;
- 弹窗内提供【采集】按钮,紧挨 PDD 链接输入框;
- `[必须]` 采集按钮**只出现在弹窗里**,不要放在每个表格行——
一个商品有 N 个 SKU 行,放行上就是 N 个按钮干同一件事,还会建出 N 个重复任务。
**其他要求:**
- `[必须]` 支持**手动新增**一条 SKU。报表只含有销售成绩的 SKU(样本里平均每商品仅 1.17 个),
订单来了查无此 SKU 是常态,必须能补。
- `[必须]` 导入是 **upsert**,绝不允许先清空再导入,否则人工填的 PDD 链接会被洗掉。
- `[必须]` 规格原文永远保留,解析失败留空,不要猜。
**底部状态条:** 最近一次导入的时间、条数、失败行数。
### 4.2 顺运宝数据模块
**顶部工具条:** 同步按钮、订单号搜索框、搜索按钮、创建采购任务按钮、删除按钮。
- `[待定]` 同步按钮 MVP 阶段只做占位:点击后提示"同步功能待接入",不发请求。
**中间表格:**
| 列 | 说明 |
|---|---|
| 勾选 | 支持批量删除、批量建任务 |
| 货运单 ID | |
| 订单号 | |
| 商品标题 | |
| 蝦皮商品 ID | 关联蝦皮数据模块 |
| 规格 SKU | 蝦皮的规格编号 |
| 数量 | |
| 价格 | 蝦皮售价,**台币分**,见 §7 |
| 图片 | 存 URL,表格里显示缩略图 |
| 匹配状态 | 已匹配 / 待匹配 |
| 更新时间 | |
原始的完整货运单 JSON 存 `syb_data` 字段,**不作为表格列显示**,在详情里看。
**双击行打开匹配弹窗:**
- 左边显示蝦皮的颜色尺码,右边显示该商品 `pdd_data` 里的 PDD 颜色尺码;
- 操作员选好对应关系,点保存;
- `[必须]` 保存的是**可复用的 SKU 映射**,不是这一张订单的临时数据。
下次遇到同一个蝦皮 SKU 自动带出,操作员只需确认。
- `[必须]` 该商品尚未采集(`pdd_data` 为空)时,弹窗要明确提示"请先到蝦皮数据模块采集",
而不是显示一个空列表让人困惑。
**创建采购任务:** 勾选若干行 → 点按钮 → 校验通过后生成任务。校验规则见 §5。
**底部状态条:** 最近同步时间、待匹配条数。
### 4.3 采购任务模块
**顶部工具条:** 订单号搜索框、搜索按钮、删除按钮。
**中间表格:**
| 列 | 说明 |
|---|---|
| 勾选 | 支持批量删除 |
| 订单号 | |
| 商品标题 | |
| 颜色 / 尺码 | **PDD 侧**的规格,不是蝦皮的 |
| 数量 | |
| 价格上限 | 人民币分,见 §7 |
| 蝦皮 ID | |
| 分配客户端 | 可改派 |
| 状态 | 见 §6 |
| 更新时间 | |
**底部状态条:** 各状态的任务条数统计。
### 4.4 客户端列表模块
**顶部工具条:** 客户端名称搜索框、搜索按钮、删除按钮。
**中间表格:** 勾选、名称、序列号、状态、最近活动时间、更新时间。
**注册方式:**
- `[必须]` 客户端**调用领取接口时自动注册**:新序列号就新增,已有就更新。
**不设单独的注册或心跳接口**,理由见 [04 Client 接口实现](04-client-api.md) §3。
- `[必须]` `状态` 是**派生字段,不存库**:
最近活动时间在 N 分钟内算"在线",否则"离线"。`[建议]` N 默认 10 分钟。
- 名称由客户端上报,操作员可以在 Admin 这边改成好记的名字。
**底部状态条:** 在线 / 离线数量统计。
## 5. 创建采购任务的校验
`[必须]` 下面任何一条不满足就不允许创建,并明确告诉操作员缺什么:
1. 该蝦皮商品已填 PDD 链接;
2. 该蝦皮商品已采集成功(`pdd_data` 非空);
3. 该蝦皮 SKU 已有 PDD 规格映射;
4. 数量大于 0;
5. **价格上限已填且大于 0** —— 默认从 `pdd_data` 里该 SKU 的价格带出,操作员可改,但不允许为空;
6. 已选择分配的客户端。
第 5 条是硬要求:Client 契约规定采购任务必须带明确的价格保护
(见 [Client 契约](../client/04-admin-api-contract.md) §4),没有它 Client 会拒绝执行。
## 6. 状态定义
### 6.1 采集状态(蝦皮商品)
| 值 | 中文 | 含义 |
|---|---|---|
| `no_link` | 未填链接 | 还没填 PDD 链接 |
| `pending` | 未采集 | 已填链接,还没发起采集 |
| `collecting` | 采集中 | 采集任务已创建,尚未回结果 |
| `collected` | 已采集 | `pdd_data` 已就绪 |
| `failed` | 采集失败 | Client 报告失败,可重新采集 |
`[必须]` `collecting` 状态的商品不允许再建采集任务,按钮置灰并提示"已在采集中"。
### 6.2 任务状态(采集任务和采购任务通用)
这是 **Admin 侧的状态**,和 Client 本地的 8 个状态是两套,不要混
(Client 侧见 [03 数据模型](../client/03-data-model.md) §7)。
| 值 | 中文 | 什么时候 |
|---|---|---|
| `pending` | 待分配 | 刚创建,还没指定客户端 |
| `assigned` | 待领取 | 已分配给某个客户端 |
| `claimed` | 已领取 | 客户端领走了,正在执行 |
| `succeeded` | 成功 | 收到结果 |
| `manual_review` | 需人工 | 客户端报告需要人处理 |
| `failed` | 失败 | 客户端报告失败 |
| `cancelled` | 已取消 | 人工取消 |
`[必须]` **Admin 看不到客户端执行到哪一步**(没有心跳,是有意的)。
`claimed` 之后就只能等结果。想知道细节看 Client 那边的界面。
客户端提交失败时带的 `status`,按下表落地:
| 客户端报告 | Admin 置为 |
|---|---|
| `retry_wait` | `assigned`(等它再来领) |
| `manual_review` | `manual_review` |
| `failed` | `failed` |
| `cancelled` | `cancelled` |
## 7. 金额与币种
`[必须]` **一律用整数存,禁止浮点。** 字段名带单位后缀。
| 场景 | 币种 | 字段示例 |
|---|---|---|
| 蝦皮售价 | 台币 | `price_twd_cent` |
| PDD 采购价、价格上限 | 人民币 | `max_price_cent` |
`[必须]` **两种币种不得混用,不得互相换算后覆盖原值。**
采购任务里的价格上限是**人民币**,来源是采集回来的 PDD 价格,
和蝦皮的台币售价没有换算关系。
`[待定]` 是否需要展示汇率或利润,待业务确认。MVP 不做。
## 8. MVP 范围
MVP 包含:
- 四模块页面框架和统一的三段式布局;
- SQLite 建库与迁移;
- 蝦皮 Excel 导入(upsert)、搜索、批量删除、手动新增、编辑弹窗;
- PDD 链接录入与发起采集;
- 顺运宝数据的**手工录入或造数**(同步按钮占位);
- 规格匹配弹窗与可复用映射;
- 创建采购任务并分配客户端;
- 给 Client 的三个接口:领取、提交结果、提交失败;
- 客户端自动注册与在线状态。
MVP 之后:
- 接入顺运宝真实同步;
- 打包成 exe;
- 权限与多用户;
- 统计报表。
## 9. 非目标
- 不做面向外部的公网服务,只在内网/本机运行。
- 不直接对接蝦皮和拼多多的接口。
- 不自动决定买哪个商品——PDD 链接和规格匹配都由人确认。
- 不自动付款。
- MVP 不做用户登录和权限体系。
## 10. 非功能需求
- 单机运行,操作人员规模是个位数,不追求高并发。
- 页面在 1366×768 上可正常使用。
- 导入 1 万行 Excel 应在可接受时间内完成,并显示进度或结果统计。
- 所有写操作有 CSRF 防护,所有 SQL 参数化。
- 日志、页面、导出不含 token、密码、Cookie。
- 数据放程序旁边的 `data/`,便携模式,见 [02 架构](02-architecture.md) §6。
## 11. 待确认事项
不许因此停工。先按临时默认值做,定了再按工单改。
| # | 待确认什么 | 临时默认值 |
|---|---|---|
| 1 | 顺运宝同步方式(接口?导出文件?) | 按钮占位,数据靠手工录入或造数 |
| 2 | 蝦皮有无全量规格导出 | 没有。靠反复导入累积 + 手动新增补齐 |
| 3 | Admin 是否需要登录 | MVP 不做,仅本机/内网访问 |
| 4 | 是否需要利润和汇率展示 | 不做 |
**已定案:**
- PDD 链接**人工填写**,入口在蝦皮数据模块的编辑弹窗,采集按钮也在那里。
- 任务**分配给指定客户端**,Client 只领分给自己的。
- **不加心跳接口**,注册在领取时完成,在线状态由最近活动时间派生。
- SKU 映射**独立成表且可复用**,同一蝦皮 SKU 只人工匹配一次。
- 货运单的"规格 SKU"**直接对应蝦皮 `商品規格ID`**,不需要转换。
但**不加外键**——本地查不到该 SKU 是常态,见 [03 数据模型](03-data-model.md) §4。
- Go 版本固定 **1.23.0**。