docs: define idempotent Client registration contract (#12)
This commit is contained in:
+39
-10
@@ -8,9 +8,10 @@
|
||||
|
||||
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。
|
||||
|
||||
## 1. 一共只有三个接口
|
||||
## 1. 一共四个接口
|
||||
|
||||
```http
|
||||
PUT /api/v1/client/registration 登记或更新 Client
|
||||
POST /api/v1/client/tasks/claim 领一个任务
|
||||
POST /api/v1/client/tasks/{task_id}/result 提交成功结果
|
||||
POST /api/v1/client/tasks/{task_id}/failure 提交失败/需人工
|
||||
@@ -20,6 +21,31 @@ POST /api/v1/client/tasks/{task_id}/failure 提交失败/需人工
|
||||
理由见 Client 契约 §1.1:本项目人工付款,重复下单只产生重复的**未付款**订单,
|
||||
不值得为它引入一整套中断逻辑。
|
||||
|
||||
### 1.1 独立登记接口
|
||||
|
||||
设置页保存当前设备时调用:
|
||||
|
||||
```http
|
||||
PUT /api/v1/client/registration
|
||||
X-Client-Id: <stable-client-id>
|
||||
```
|
||||
|
||||
请求体与 claim 的 Client、设备和能力部分相同。相同 `X-Client-Id` 重复调用执行 upsert,统一返回 `200 OK`:
|
||||
|
||||
```json
|
||||
{
|
||||
"registered": true,
|
||||
"client_id": "CLIENT-123456",
|
||||
"registered_at": "2026-08-06T09:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
`[必须]` 本接口只登记 Client,不查询、领取或修改任务,也不返回任务。
|
||||
|
||||
`[必须]` 显式登记携带非空名称时允许更新名称;空名称更新保留现有名称,新建时使用 Client ID 兜底。设备、能力、`last_seen_at` 和 `updated_at` 正常更新。
|
||||
|
||||
`[必须]` 校验 `client.name` 最多 50 字、任务类型、执行模式和结构版本。错误使用统一 JSON 结构,至少覆盖 `MISSING_CLIENT_ID`、`INVALID_BODY`、`INVALID_CLIENT_PROFILE` 和 `CLIENT_REGISTER_FAILED`。
|
||||
|
||||
## 2. 领取任务
|
||||
|
||||
```http
|
||||
@@ -76,11 +102,11 @@ WHERE task_id = ? AND status = 'assigned';
|
||||
这些在建任务时就该校验住(见 [01 需求](01-requirements.md) §5),
|
||||
不要等到这里才发现发不出去。
|
||||
|
||||
## 3. 客户端注册就在领取里做
|
||||
## 3. claim 保留隐式登记作为兼容兜底
|
||||
|
||||
`[必须]` **不设单独的注册接口,也不设心跳接口。**
|
||||
`[必须]` **有独立登记接口,但不设心跳接口。**
|
||||
|
||||
`claim` 请求里已经带齐了初始化需要的东西:
|
||||
旧 Client 可能直接调用 `claim`,其请求里已经带齐初始化需要的信息:
|
||||
|
||||
```http
|
||||
X-Client-Id: client-001 ← 序列号,主键
|
||||
@@ -96,14 +122,14 @@ X-Client-Id: client-001 ← 序列号,主键
|
||||
}
|
||||
```
|
||||
|
||||
> **已定案:** `client.name` 是对 Client 契约 §5 的一处小扩展。
|
||||
> **已定案:** `client.name` 同时用于显式登记和 claim 兼容登记。
|
||||
> Client 的设置页本来就有「设备名」输入框(`deviceNameInput`,50 字上限),
|
||||
> 把它带上即可。**Admin 仍然容忍它缺失**——没有就用 `X-Client-Id` 当显示名,
|
||||
> 所以 Client 先不发也不影响登记。
|
||||
> 显式登记会更新非空名称;claim 对已有 Client 不覆盖名称。
|
||||
> Admin 容忍名称缺失,新建时用 `X-Client-Id` 当显示名。
|
||||
>
|
||||
> 登记流程的完整说明见 [07 设备登记联调手册](07-设备登记联调手册.md)。
|
||||
|
||||
处理:
|
||||
claim 的兼容登记处理:
|
||||
|
||||
```text
|
||||
upsert clients:
|
||||
@@ -122,8 +148,7 @@ upsert clients:
|
||||
`[必须]` `result` 和 `failure` 接口**也要刷新 `last_seen_at`**。
|
||||
否则客户端执行长任务期间不调 claim,会被误判成离线。
|
||||
|
||||
**新客户端第一次来必然拿不到任务**(还没人给它分配),返回 `204` 是正常的。
|
||||
它这时已经登记进列表了,操作员就能给它分配任务。
|
||||
新客户端直接调用 claim 时,`204` 是正常的无任务结果,Client 必须正常处理;如果已经预先分配任务,也可能首次调用就返回 `200`。
|
||||
|
||||
## 4. 提交结果
|
||||
|
||||
@@ -231,6 +256,10 @@ MVP 阶段只在内网/本机运行。
|
||||
|
||||
Client 契约里散落的 Admin 侧硬要求,汇总在这里,**可以直接当验收标准用**:
|
||||
|
||||
- [ ] `PUT /registration` 相同 Client ID 幂等 upsert,不创建重复记录
|
||||
- [ ] 登记接口不读取、领取或修改任务
|
||||
- [ ] 显式登记更新非空名称,空名称更新保留已有名称
|
||||
- [ ] 登记接口校验名称、任务类型、执行模式和结构版本
|
||||
- [ ] `claim` 一次只返回一个任务,且只返回分配给该客户端的
|
||||
- [ ] `claim` 原子完成,并发下不会把同一任务发给两个客户端
|
||||
- [ ] `claim` 无任务时返回 `204`,不是 `200` 加空对象
|
||||
|
||||
Reference in New Issue
Block a user