Files
cmautobuy/docs/admin/07-设备登记联调手册.md
T

9.4 KiB
Raw Blame History

07 设备登记联调手册

  • 文档状态:Admin 侧(#12)已实现并通过 curl 验证,等待 Client 侧(#11)接入
  • 读者:Admin 登记接口、Client HttpAdminGateway 开发者
  • 目标:让 Client 设置页安全登记设备,同时不领取任务

接口权威定义:

本文中的登记请求已在真实运行的 Admin 上验证通过(#12 已完成),下面 §5 的每条命令都是实跑结果。


1. 登记、领取和心跳是三件不同的事

1.1 显式登记

设置页点击“保存”后调用:

PUT /api/v1/client/registration

它只新增或更新 Client,不读取、领取或修改任务,也不返回任务。

1.2 领取任务

“获取任务”流程调用:

POST /api/v1/client/tasks/claim

它可能返回 204 No Content,也可能返回 200 + task。返回任务时 Admin 已经改变任务状态,Client 必须可靠写入本地 SQLite,不能丢弃响应。

claim 保留隐式登记作为旧 Client 的兼容兜底,但设置保存入口不得用 claim 代替登记。

1.3 没有心跳接口

本项目仍然不做定时心跳。登记、claim、result 和 failure 都刷新 last_seen_at,Admin 根据最近活动时间计算在线或离线。

客户端执行长任务期间超过在线阈值,Admin 暂时显示离线是已知且接受的取舍。


2. Client 要准备的数据

2.1 设备号:X-Client-Id

对应设置页只读“设备号”。

  • 首次保存时生成一次;
  • 保存到 Client SQLite app_settings,键名 admin.client_id;
  • 重启、升级、修改设置时保持不变;
  • 换电脑或清空本地数据后生成新编号;
  • 只向 Admin 发送生成后的编号,不发送原始硬件参数。

Admin 使用它作为 clients.client_id 主键。设备号变化会产生一条新的 Client 记录。

2.2 设备名:client.name

对应设置页可编辑“设备名”,最多 50 字,可以为空。

  • 显式登记携带非空名称时,Admin 更新名称;
  • 显式登记名称为空时:新 Client 使用设备号兜底,已有 Client 保留原名称;
  • claim 隐式登记已有 Client 时不覆盖名称;
  • Admin 页面仍可人工修改名称。

这意味着用户明确点击“保存”可以同步非空名称,而后台领取不会反复覆盖名称。

2.3 设备和能力

{
  "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]
  }
}
  • supported_types 必须非空,只允许 collect、purchase;
  • device 可选,提供时当前只支持 android;
  • purchase_mode 只允许 dry_run、live;Client 未选择设备或真实采购执行器未就绪时为 dry_run,就绪后自动为 live;
  • schema_versions 只包含正整数,当前使用 [1]。

3. 登记请求

PUT /api/v1/client/registration HTTP/1.1
Host: 127.0.0.1:8080
X-Client-Id: CLIENT-123456
X-Request-Id: 7a5d0000-0000-0000-0000-000000000001
Content-Type: application/json
Authorization: Bearer <token>
{
  "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 是 稳定设备号,Admin Client 唯一键
X-Request-Id 建议 每次请求使用新的 UUID,便于排查
client.name 否 最多 50 字;非空时显式更新名称
supported_types 是 非空任务类型列表
device 否 当前选择的 Android 设备信息
capabilities.purchase_mode 是 MVP 使用 dry_run
capabilities.schema_versions 是 当前使用 [1]
Authorization 当前可选 Admin 当前读取但不校验,正式认证待定

访问令牌不得出现在普通日志、错误提示或测试固件中。


4. 登记响应

新增和更新统一返回 200 OK:

{
  "registered": true,
  "client_id": "CLIENT-123456",
  "registered_at": "2026-08-06T09:00:00Z"
}

Client 必须校验:

  • HTTP 状态为 200;
  • registered 为 true;
  • client_id 与请求的 X-Client-Id 相同;
  • registered_at 是带时区 ISO 8601 时间。

常见错误:

HTTP code retryable 处理
400 MISSING_CLIENT_ID false 检查本地设备号,不自动重试
400 INVALID_BODY false 修正 JSON,不自动重试
422 INVALID_CLIENT_PROFILE false 修正名称或能力字段
500 CLIENT_REGISTER_FAILED true 保留本地设置,允许稍后重试

错误响应使用统一结构:

{
  "error": {
    "code": "INVALID_CLIENT_PROFILE",
    "message": "Client 能力字段无效",
    "retryable": false,
    "request_id": "7a5d...",
    "details": {}
  }
}

5. Admin 实现后的 curl 验证

5.1 启动 Admin

cd D:\chengma\cmautobuy\admin
go run .

5.2 首次登记

curl -i -X PUT http://127.0.0.1:8080/api/v1/client/registration \
  -H 'X-Client-Id: test-device-001' \
  -H 'X-Request-Id: registration-create-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]}
      }'

预期:200 OK,registered: true,Admin /clients 页面出现且只出现一条 test-device-001。

5.3 重复登记

使用同一 X-Client-Id、新的 X-Request-Id 再调用一次。预期仍为 200,客户端列表记录数不增加。

5.4 更新名称

把 client.name 改为“联调测试机-已更新”再次调用。预期记录数不增加,名称更新。

5.5 空名称更新

发送 "client": {"name": ""}。预期已有名称保持不变。

5.6 验证无任务副作用

登记前后检查任务表:任务状态、领取时间和领取历史均不得变化。登记响应不得出现 task 字段。

5.7 错误响应

至少验证缺少 X-Client-Id、名称超过 50 字、空 supported_types 和非法 purchase_mode。

实跑结果(Go 1.23.0,2026-08-06):

验证项 结果
5.2 首次登记 200,registered: true
5.3 重复登记 3 次 200,客户端记录数保持 1
5.4 更新名称 200,名称变为"联调测试机-已更新",记录数仍为 1
5.5 空名称更新 200,名称保持不变
5.6 无任务副作用 插入任务后登记 3 次,任务的 status / claimed_at / updated_at 完全未变;响应不含 task 字段;登记后任务仍能被正常领取
5.7 缺 X-Client-Id 400 MISSING_CLIENT_ID
5.7 名称 51 字 422 INVALID_CLIENT_PROFILE,消息指明"client.name 最多 50 个字,收到 51 个"
5.7 supported_types 为空 422 INVALID_CLIENT_PROFILE
5.7 purchase_mode 非法 422 INVALID_CLIENT_PROFILE
校验不过时不写库 三次非法请求后记录数仍为 1
claim 仍可用 200,正常领到任务

6. claim 兼容登记与领取验证

claim 的请求结构与能力字段保持不变:

POST /api/v1/client/tasks/claim
X-Client-Id: test-device-001
  • 204 No Content:登记/活动时间更新成功,暂时没有任务;
  • 200 OK:领到一个任务,Client 必须立即写入 SQLite;
  • 首次 claim 不保证一定是 204,预先分配过任务时可以直接返回 200;
  • claim 更新已有 Client 时不覆盖名称。

设置页保存按钮不得调用 claim。只有“获取任务”流程可以调用它。


7. Client 保存流程

点击“保存”
  → 从 SQLite 读取 admin.client_id
  → 不存在:生成并保存
  → 已存在:保持不变
  → 保存设备名
  → 页面显示“本地已保存”
  → 后台调用 PUT /registration
      ├─ 成功:显示“已登记到 Admin”
      └─ 失败:显示“本地已保存,Admin 登记失败”

实现要求:

  • SQLite 是设备号权威来源,不能只看输入框是否显示“待生成”;
  • Admin 失败不得回滚本地设置;
  • HTTP 使用 QObject + moveToThread,不得阻塞 Qt 主线程;
  • 保存期间禁用重复点击;
  • 窗口关闭时断开信号,忽略迟到结果;
  • MockAdminGateway 和 HttpAdminGateway 使用相同登记契约测试;
  • 新增 requests 依赖前按项目依赖门禁确认固定版本和许可证。

8. 能力状态

能力 状态
独立登记契约 已确认
Admin 独立登记实现 已完成(#12)
Client 设置页登记实现 等待 #11 和 #12
claim 隐式登记 已有,保留兼容
领取任务 已有
提交结果 / 提交失败 已有
定时心跳 不实现
正式认证 [待定],当前读取但不校验