docs: 新增设备登记联调手册,并定案 client.name
面向 Client 开发者的实操文档,目标是让一台新设备出现在 Admin 的 客户端列表里。文中所有请求和响应都是在真实运行的 Admin 上跑出来的。 新增 docs/admin/07-设备登记联调手册.md - 先讲清"没有注册接口,登记是 claim 的副作用" - **重点提示 204 不是错误**:新设备第一次 claim 必然 204, 它同时意味着登记成功。这是最容易被 Client 误判成失败的地方 - 设备号必须持久化、永不变——变了 Admin 会当成新机器, 列表里会堆一串僵尸记录 - 设备名只在首次登记时采纳,之后 Admin 侧改名不会被覆盖。 这个行为会让人困惑("我改了 Client 怎么 Admin 没变"),先说清楚 - 四步验证流程 + 可直接复制的 curl + 手工插测试任务的 SQL - 7 条常见问题,含"列表里多出好几台一样的机器"这类实际会碰到的 - 列出 Client 侧待办清单,标明前两条做完就能登记成功 定案 client.name(原 [待定]) Client 设置页本来就有「设备名」输入框(deviceNameInput,50 字上限), 只是没接进 ClientInfo。Admin 仍容忍它缺失,缺了用 X-Client-Id 兜底, 所以 Client 先不发也不影响登记。 同步更新 - docs/client/04-admin-api-contract.md §5:请求示例补 client 字段, 加"204 不是错误"的必须项,并指向本手册 - docs/README.md:两处导航加入口,Client 侧"对接 Admin"那行也指过来 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+3
-1
@@ -34,7 +34,7 @@
|
||||
| 第一次把项目跑起来 | [00 上手指南](client/00-getting-started.md) | — |
|
||||
| 改界面、加页面、调表格 | [05 界面交互规范](client/05-ui-specification.md) | [02 架构](client/02-architecture.md) §5 线程 |
|
||||
| 加字段、改表、写 SQL | [03 数据模型](client/03-data-model.md) | [02 架构](client/02-architecture.md) §7 数据所有权 |
|
||||
| 对接 Admin、写 Gateway | [04 接口契约](client/04-admin-api-contract.md) | [03 数据模型](client/03-data-model.md) §5 Outbox |
|
||||
| 对接 Admin、写 Gateway | [04 接口契约](client/04-admin-api-contract.md) | [07 联调手册](admin/07-设备登记联调手册.md) |
|
||||
| 写自动化、控制手机 | [02 架构](client/02-architecture.md) §9 | [06 质量与安全](client/06-quality-security.md) §4 |
|
||||
| 碰采购、下单相关代码 | [06 质量与安全](client/06-quality-security.md) §3 | [01 需求](client/01-requirements.md) §4.2 |
|
||||
| 写测试 | [06 质量与安全](client/06-quality-security.md) §2 | — |
|
||||
@@ -50,6 +50,7 @@
|
||||
| 加字段、改表、写 SQL | [03 数据模型](admin/03-data-model.md) | [02 架构](admin/02-architecture.md) §2 分层 |
|
||||
| 改 Excel 导入 | [03 数据模型](admin/03-data-model.md) §3.3 | [00 术语表](admin/00-glossary.md) §3 upsert |
|
||||
| 改给 Client 的接口 | [04 Client 接口实现](admin/04-client-api.md) | [Client 侧契约](client/04-admin-api-contract.md) |
|
||||
| 和 Admin 联调、登记新设备 | [07 设备登记联调手册](admin/07-设备登记联调手册.md) | [Client 侧契约](client/04-admin-api-contract.md) §5 |
|
||||
| 写测试 | [06 质量与安全](admin/06-quality-security.md) §2 | — |
|
||||
| 搞不清这功能到底要不要做 | [01 产品需求基线](admin/01-requirements.md) | — |
|
||||
|
||||
@@ -86,6 +87,7 @@
|
||||
| [04 Client 接口实现](admin/04-client-api.md) | 服务端怎么实现那三个接口 |
|
||||
| [05 界面规范](admin/05-ui-specification.md) | 三段式布局、四个页面、弹窗 |
|
||||
| [06 质量、安全与测试](admin/06-quality-security.md) | 测试、Web 安全、发布门禁 |
|
||||
| [07 设备登记联调手册](admin/07-设备登记联调手册.md) | **给 Client 开发者**:怎么让新设备登记成功 |
|
||||
|
||||
## 文档标注说明
|
||||
|
||||
|
||||
@@ -96,8 +96,12 @@ X-Client-Id: client-001 ← 序列号,主键
|
||||
}
|
||||
```
|
||||
|
||||
> `[待定]` `client.name` 是对 Client 契约 §5 的一处小扩展,需在联合评审时确认。
|
||||
> 未确认前 Admin 要容忍它缺失:没有就用 `X-Client-Id` 当显示名。
|
||||
> **已定案:** `client.name` 是对 Client 契约 §5 的一处小扩展。
|
||||
> Client 的设置页本来就有「设备名」输入框(`deviceNameInput`,50 字上限),
|
||||
> 把它带上即可。**Admin 仍然容忍它缺失**——没有就用 `X-Client-Id` 当显示名,
|
||||
> 所以 Client 先不发也不影响登记。
|
||||
>
|
||||
> 登记流程的完整说明见 [07 设备登记联调手册](07-设备登记联调手册.md)。
|
||||
|
||||
处理:
|
||||
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
# 07 设备登记联调手册
|
||||
|
||||
- 文档状态:联调用,随实现更新
|
||||
- 读者:**实现 Client 侧 `HttpAdminGateway` 的开发者**
|
||||
- 目标:让一台新 Client 出现在 Admin 的「客户端列表」里,并确认登记成功
|
||||
|
||||
本文所有请求和响应都是**在真实运行的 Admin 上跑出来的**,不是手写示例。
|
||||
接口的权威定义在 [Client 侧契约](../client/04-admin-api-contract.md),
|
||||
本文只讲怎么把它跑通。
|
||||
|
||||
---
|
||||
|
||||
## 1. 先理解一件事:没有注册接口
|
||||
|
||||
**登记是领取任务的副作用。**
|
||||
|
||||
Client 调 `claim` 的时候,Admin 顺手就把这台机器记下来了。
|
||||
没有 `/register`,也没有心跳接口——这是有意的设计,
|
||||
理由见 [04 Client 接口实现](04-client-api.md) §3。
|
||||
|
||||
所以"登记新设备"这件事,Client 侧要做的就是:**调一次 `claim`**。
|
||||
|
||||
```text
|
||||
Client 启动
|
||||
↓
|
||||
调 POST /api/v1/client/tasks/claim(带上设备号和设备名)
|
||||
↓
|
||||
Admin:没见过这个设备号 → 新增一条记录
|
||||
见过 → 更新设备信息和最近活动时间
|
||||
↓
|
||||
返回 204(没有分给它的任务)或 200(有任务)
|
||||
```
|
||||
|
||||
> **最容易误判的一点:新设备第一次调 `claim` 必然返回 `204`。**
|
||||
>
|
||||
> 因为它刚登记,还没有人给它分配任务。
|
||||
> **`204` 不是失败,它同时意味着登记成功了。**
|
||||
> Client 必须把它当正常情况处理——不要报错、不要弹提示、
|
||||
> 更不要因此触发重试风暴。
|
||||
|
||||
---
|
||||
|
||||
## 2. Client 要准备两样东西
|
||||
|
||||
### 2.1 设备号(`X-Client-Id`)
|
||||
|
||||
对应 Client 设置页那个只读的**「设备号」**输入框(现在显示"待生成")。
|
||||
|
||||
`[必须]` 规则:
|
||||
|
||||
- **首次启动时生成一次**(UUID 或任何全局唯一的串都行);
|
||||
- 生成后存进 `app_settings`,键名 `admin.client_id`;
|
||||
- **之后永远不变**——重启、升级、改配置都不能变。
|
||||
|
||||
**为什么不能变:** Admin 是按设备号认机器的。设备号变了,
|
||||
Admin 会认为这是一台新机器,客户端列表里就会多出一条,
|
||||
原来那条变成永远离线的僵尸记录。
|
||||
|
||||
换电脑、重装系统算新设备,那是对的;但同一台机器重启不能变。
|
||||
|
||||
### 2.2 设备名(`client.name`)
|
||||
|
||||
对应设置页那个可编辑的**「设备名」**输入框(50 字上限)。
|
||||
|
||||
- 给人看的,比如"办公室-01"、"仓库那台";
|
||||
- **可以不填**——不填的话 Admin 用设备号当显示名,不影响功能。
|
||||
|
||||
> **一个会让人困惑的行为,先说清楚:**
|
||||
>
|
||||
> 设备名**只在第一次登记时被采纳**。之后操作员在 Admin 界面上把它
|
||||
> 改成别的名字,Client 再上报也**不会覆盖**。
|
||||
>
|
||||
> 这是故意的——避免操作员精心起的名字被客户端的默认值冲掉。
|
||||
> 所以你在 Client 那边改了设备名,发现 Admin 没变,**不是 bug**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 请求长什么样
|
||||
|
||||
```http
|
||||
POST /api/v1/client/tasks/claim HTTP/1.1
|
||||
Host: 127.0.0.1:8080
|
||||
X-Client-Id: 7f3a1c62-9b04-4e8d-a1f2-5c7e9d0b3a11
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"client": {
|
||||
"name": "办公室-01"
|
||||
},
|
||||
"supported_types": ["collect", "purchase"],
|
||||
"device": {
|
||||
"address": "192.168.0.173:5555",
|
||||
"platform": "android",
|
||||
"pdd_package": "com.xunmeng.pinduoduo"
|
||||
},
|
||||
"capabilities": {
|
||||
"purchase_mode": "dry_run",
|
||||
"schema_versions": [1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `X-Client-Id` 头 | **是** | 设备号。缺了直接 400 |
|
||||
| `client.name` | 否 | 设备名。缺了用设备号当显示名 |
|
||||
| `supported_types` | 是 | 支持的任务类型。只写 `["collect"]` 就不会拿到采购任务 |
|
||||
| `device.*` | 否 | 会存进 Admin 供排查用 |
|
||||
| `capabilities.purchase_mode` | 是 | `dry_run` 或 `live`。演练模式期间固定 `dry_run` |
|
||||
| `Authorization` | 否 | `[待定]` 认证方式未定,当前 Admin **读但不校验** |
|
||||
|
||||
---
|
||||
|
||||
## 4. 响应怎么处理
|
||||
|
||||
### 4.1 `204 No Content` —— 登记成功,暂时没活干
|
||||
|
||||
**没有响应体。** 这是新设备的正常结果,也是引擎空转时的常态。
|
||||
|
||||
Client 该做的:按轮询周期退避后再来,**不要当成错误**。
|
||||
|
||||
### 4.2 `200 OK` —— 领到任务了
|
||||
|
||||
```json
|
||||
{
|
||||
"task": {
|
||||
"id": "PDD-TEST-0001",
|
||||
"type": "purchase",
|
||||
"version": 1,
|
||||
"priority": 0,
|
||||
"payload": {
|
||||
"goods_id": "737116531267",
|
||||
"goods_url": "https://mobile.yangkeduo.com/goods.html?goods_id=737116531267",
|
||||
"options": { "color": "黑色", "size": "M码" },
|
||||
"quantity": 2,
|
||||
"max_price_cent": 4200
|
||||
},
|
||||
"created_at": "2026-08-06T08:00:00Z",
|
||||
"updated_at": "2026-08-06T08:53:20Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意三点:
|
||||
|
||||
- **没有租约字段**,也没有 Admin 侧状态——不用找,本来就没有;
|
||||
- `options` 是**对象**不是字符串;
|
||||
- `max_price_cent` 是**人民币分的整数**(4200 = ¥42.00)。
|
||||
采购任务必然带 `quantity` 和 `max_price_cent`,这是价格保护。
|
||||
|
||||
### 4.3 `400` —— 请求有问题
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "MISSING_CLIENT_ID",
|
||||
"message": "缺少 X-Client-Id 请求头",
|
||||
"retryable": false,
|
||||
"request_id": "",
|
||||
"details": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`retryable: false` 的错误**不要重试**,重试多少次都是一样的结果。
|
||||
|
||||
---
|
||||
|
||||
## 5. 怎么确认登记成功
|
||||
|
||||
### 第一步:起 Admin
|
||||
|
||||
```powershell
|
||||
cd D:\chengma\cmautobuy\admin
|
||||
go run .
|
||||
```
|
||||
|
||||
看到 `Admin 已启动: http://127.0.0.1:8080` 就绪。
|
||||
|
||||
### 第二步:发一次 claim
|
||||
|
||||
不用等 Client 写完,先用 curl 打通链路:
|
||||
|
||||
```bash
|
||||
curl -i -X POST http://127.0.0.1:8080/api/v1/client/tasks/claim \
|
||||
-H 'X-Client-Id: test-device-001' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"client": {"name": "联调测试机"},
|
||||
"supported_types": ["collect", "purchase"],
|
||||
"device": {"address": "192.168.0.173:5555", "platform": "android",
|
||||
"pdd_package": "com.xunmeng.pinduoduo"},
|
||||
"capabilities": {"purchase_mode": "dry_run", "schema_versions": [1]}
|
||||
}'
|
||||
```
|
||||
|
||||
预期:`HTTP/1.1 204 No Content`,无响应体。
|
||||
|
||||
### 第三步:在界面上确认
|
||||
|
||||
浏览器打开 <http://127.0.0.1:8080/clients>,应该看到:
|
||||
|
||||
| 名称 | 序列号 | 状态 | 最近活动 |
|
||||
|---|---|---|---|
|
||||
| 联调测试机 | test-device-001 | 在线 | 2026-08-06T09:03:55Z |
|
||||
|
||||
底部状态条显示 `共 1 台客户端 · 在线 1 · 离线 0`。
|
||||
|
||||
**看到这一行 = 登记打通了。**
|
||||
|
||||
### 第四步:让它领到一个任务(可选)
|
||||
|
||||
想验证 `200` 那条路径,得先有任务。现在还没有创建任务的界面
|
||||
(要等 Excel 导入那条链路做完),手工插一条:
|
||||
|
||||
用 DB Browser 打开 `admin/data/admin.db`,执行:
|
||||
|
||||
```sql
|
||||
INSERT INTO tasks (task_id, task_type, status, assigned_client, order_no,
|
||||
pdd_goods_url, pdd_goods_id, pdd_options,
|
||||
quantity, max_price_cent, created_at, updated_at)
|
||||
VALUES ('PDD-TEST-0001', 'purchase', 'assigned', 'test-device-001',
|
||||
'TEST-ORDER-001',
|
||||
'https://mobile.yangkeduo.com/goods.html?goods_id=737116531267',
|
||||
'737116531267', '{"color":"黑色","size":"M码"}',
|
||||
2, 4200,
|
||||
'2026-08-06T08:00:00Z', '2026-08-06T08:00:00Z');
|
||||
```
|
||||
|
||||
注意 `assigned_client` 要填**你自己的设备号**,任务只发给指定的客户端。
|
||||
|
||||
再调一次 claim,这次会拿到 `200` 和上面 §4.2 那个结构。
|
||||
**再调第三次又是 `204`**——任务已经被领走了,这是对的。
|
||||
|
||||
---
|
||||
|
||||
## 6. 常见问题
|
||||
|
||||
| 现象 | 原因 | 怎么办 |
|
||||
|---|---|---|
|
||||
| 一直 `204`,列表里也没有 | 请求根本没到 Admin | 看 Admin 控制台有没有请求日志;确认端口和地址 |
|
||||
| `400 MISSING_CLIENT_ID` | 没带 `X-Client-Id` 头 | 它是**请求头**不是 body 字段 |
|
||||
| `400 INVALID_BODY` | body 不是合法 JSON | 即使没内容也要发 `{}`,不能发空 |
|
||||
| 列表里多出好几台一样的机器 | **设备号每次启动都在变** | 设备号必须持久化,见 §2.1 |
|
||||
| 改了 Client 的设备名,Admin 没变 | 正常行为,不是 bug | 见 §2.2 |
|
||||
| 状态显示"离线"但客户端在跑 | 超过 10 分钟没调任何接口 | 正常。本项目**有意不做心跳**,执行长任务期间会显示离线 |
|
||||
| 明明有任务却拿到 204 | `assigned_client` 不是你的设备号;或 `supported_types` 不含该任务类型 | 查库确认这两项 |
|
||||
|
||||
---
|
||||
|
||||
## 7. Client 侧要改什么
|
||||
|
||||
按现在的代码(`client/src/admin_gateway.py`),要做这几件事:
|
||||
|
||||
- [ ] `requirements.txt` 加 HTTP 库(`requests`,版本要过工单确认)
|
||||
- [ ] **`ClientInfo` 加 `name` 字段** —— 设置页的「设备名」输入框已经有了,
|
||||
只是没接到网关里
|
||||
- [ ] 设备号生成一次并持久化到 `app_settings`(键 `admin.client_id`),
|
||||
填进设置页那个只读的「设备号」框
|
||||
- [ ] 实现 `HttpAdminGateway`,先只做 `claim_next`
|
||||
- [ ] **把 `204` 当正常情况**返回"暂无任务",不要抛异常
|
||||
- [ ] 契约测试:Mock 和 HTTP 两个实现跑同一套用例
|
||||
|
||||
前两条做完就能登记成功了,后面的可以分批。
|
||||
|
||||
---
|
||||
|
||||
## 8. 还没做完的部分
|
||||
|
||||
| 能力 | 状态 |
|
||||
|---|---|
|
||||
| 登记新设备 | **可用** |
|
||||
| 领取任务 | **可用**(任务需手工插库) |
|
||||
| 提交结果 / 提交失败 | **可用**,含幂等 |
|
||||
| 创建任务的界面 | 未做,等 Excel 导入链路 |
|
||||
| 认证 | `[待定]`,当前读 `Authorization` 头但不校验 |
|
||||
| 改派任务 | 未做,需要时直接改库 |
|
||||
@@ -119,6 +119,9 @@ POST /api/v1/client/tasks/claim
|
||||
|
||||
```json
|
||||
{
|
||||
"client": {
|
||||
"name": "办公室-01"
|
||||
},
|
||||
"supported_types": ["collect", "purchase"],
|
||||
"device": {
|
||||
"address": "192.168.0.173:5555",
|
||||
@@ -142,6 +145,16 @@ POST /api/v1/client/tasks/claim
|
||||
|
||||
无可领取任务时返回 `204 No Content`。
|
||||
|
||||
`[必须]` **`204` 不是错误。** 新客户端第一次调 `claim` 必然是 204——
|
||||
它刚被登记,还没有人给它分配任务。Client 要把它当"暂时没活干"处理,
|
||||
不要报错,也不要因此触发重试风暴。
|
||||
|
||||
`client.name` 是给人看的显示名,可以不填(不填时 Admin 用 `X-Client-Id` 兜底)。
|
||||
它**只在首次登记时被采纳**,之后操作员在 Admin 改的名字不会被客户端覆盖。
|
||||
|
||||
> 怎么把这条链路跑通、怎么确认设备登记成功,见
|
||||
> [Admin 侧的设备登记联调手册](../admin/07-设备登记联调手册.md)。
|
||||
|
||||
规则:
|
||||
|
||||
- `[必须]` 领取必须在 Admin 内部原子完成,一次调用最多返回一个任务。
|
||||
|
||||
Reference in New Issue
Block a user