docs: define idempotent Client registration contract (#12)

This commit is contained in:
chengma
2026-08-06 17:21:36 +08:00
parent 62d46bf10a
commit 02073eab37
8 changed files with 306 additions and 206 deletions
+62 -8
View File
@@ -12,7 +12,8 @@
**核心:Admin 是调度器,Client 是执行器,执行器不参与调度决策。**
- Client 只有三个动作:领一个任务、提交结果、提交失败。**没有任何"去问 Admin 现在怎么想"的调用。**
- Client 的任务流程只有三个动作:领一个任务、提交结果、提交失败。
设置页另有一个幂等 Client 登记动作。**没有任何"去问 Admin 现在怎么想"的调用。**
- Client 拿到任务就做完,中途不管任务是否被取消、是否被重派。
- Admin 负责任务的创建、更新、取消和派发(含重派),这些 Client 一律不感知。
- 结果提交必须幂等;网络重试不能创建重复结果。
@@ -109,6 +110,59 @@ Idempotency-Key: <stable-key>
任务对象里**不含 Admin 侧状态**。Client 不关心 Admin 那边把它标成什么,只管做完提交。
## 4.1 登记或更新 Client
```http
PUT /api/v1/client/registration
X-Client-Id: <stable-client-id>
X-Request-Id: <uuid>
```
请求:
```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]
}
}
```
成功统一返回 `200 OK`:
```json
{
"registered": true,
"client_id": "CLIENT-123456",
"registered_at": "2026-08-06T09:00:00Z"
}
```
规则:
- `[必须]` `X-Client-Id` 是 Client 唯一键;相同编号重复 `PUT` 执行幂等 upsert,不创建重复记录。
- `[必须]` 登记接口不读取、领取或修改任务,也不返回任务。
- `[必须]` `client.name` 可选且最多 50 字。显式登记携带非空名称时允许更新 Admin 名称;空名称更新保留已有名称,新建时用 Client ID 兜底。
- `[必须]` 更新设备、能力、`last_seen_at` 和 `updated_at`。
- `[必须]` `supported_types` 非空且只包含 `collect`、`purchase`;`purchase_mode` 只允许 `dry_run`、`live`;`schema_versions` 只包含正整数。
- `[必须]` Client 必须先把设备号和名称保存到 SQLite,再在后台线程调用本接口;远端失败不得回滚本地保存。
- `[必须]` 设备号生成后稳定复用,不向 Admin 发送生成设备号所用的原始硬件参数。
- `[必须]` 不增加定时心跳。在线状态仍由登记、领取和提交产生的 `last_seen_at` 派生。
错误至少包括 `MISSING_CLIENT_ID`、`INVALID_BODY`、`INVALID_CLIENT_PROFILE` 和 `CLIENT_REGISTER_FAILED`,并使用 §3 的统一结构。
`claim` 继续支持隐式登记作为旧 Client 的兼容兜底:新编号可以创建 Client,已有 Client 只更新设备、能力和最近活动时间,不覆盖名称。
## 5. 领取任务
```http
@@ -145,12 +199,11 @@ POST /api/v1/client/tasks/claim
无可领取任务时返回 `204 No Content`。
`[必须]` **`204` 不是错误。** 新客户端第一次调 `claim` 必然是 204——
它刚被登记,还没有人给它分配任务。Client 要把它当"暂时没活干"处理,
不要报错,也不要因此触发重试风暴。
`[必须]` **`204` 不是错误。** Client 要把它当"暂时没活干"处理,
不要报错,也不要因此触发重试风暴。首次 claim 通常返回 204;如果该编号已经预先分配任务,也可以直接返回 200。
`client.name` 是给人看的显示名,可以不填(不填时 Admin 用 `X-Client-Id` 兜底)。
它**只在首次登记时被采纳**,之后操作员在 Admin 改的名字不会被客户端覆盖。
`client.name` 是给人看的显示名,可以不填(新建时 Admin 用 `X-Client-Id` 兜底)。
在 claim 兼容登记中,它只在首次创建时被采纳,已有 Client 的名称不会被后台领取覆盖;用户通过 §4.1 显式保存非空名称时可以更新。
> 怎么把这条链路跑通、怎么确认设备登记成功,见
> [Admin 侧的设备登记联调手册](../admin/07-设备登记联调手册.md)。
@@ -266,9 +319,10 @@ Idempotency-Key: task-id:attempt-id:failure-v1
## 9. Mock Admin 要求
`MockAdminGateway` 与 HTTP 实现暴露同一应用层接口,只有三个方法:
`MockAdminGateway` 与 HTTP 实现暴露同一应用层接口。登记和三个任务方法都必须使用同一契约:
```text
register_client(client, capabilities) # §4.1
claim_next(client, capabilities) # §5
submit_result(task_id, idempotency_key, result) # §6
submit_failure(task_id, idempotency_key, failure) # §7
@@ -300,7 +354,7 @@ Admin 还没做完,下面这些要和 Admin 一起定。**不许因此停工**
**已定案(不再是待确认项):**
- Admin 只把任务分配给指定 Client,Client 不读全局任务池。见 §5.1。
- **没有租约、没有心跳、没有状态回查。** Client 只有 §5 / §6 / §7 三个调用。见 §1.1。
- **没有租约、没有心跳、没有状态回查。** 任务流程只有 §5 / §6 / §7 三个调用;设置页另有 §4.1 的幂等登记调用。
- Admin 必须无条件接受已派发过的 Client 提交的结果。见 §6.1。
改动这张表里任何一条,都属于会影响业务结果的变更,必须先更新工单并经用户确认。