266 lines
6.7 KiB
Markdown
266 lines
6.7 KiB
Markdown
# 设备直接绑定接口联调文档
|
||
|
||
## 1. 接口说明
|
||
|
||
该接口用于设备无法正常获取绑定验证码时,由白名单来源的业务页面发起直接绑定。
|
||
|
||
接口完成的业务效果与验证码绑定一致:
|
||
|
||
- 创建当前程序用户与设备的有效绑定关系;
|
||
- 将设备绑定状态更新为 `bound`;
|
||
- 作废该设备尚未使用的绑定验证码;
|
||
- 清理绑定验证码缓存;
|
||
- 写入绑定审计记录。
|
||
|
||
接口不会自动创建不存在的 ICCID。调用前,设备必须已经登记在后端 `sim_card` 表中,并且设备状态为 `active`。
|
||
|
||
## 2. 接口地址
|
||
|
||
```http
|
||
POST /api/v1/program/bindings/by-iccid
|
||
```
|
||
|
||
生产环境示例:
|
||
|
||
```text
|
||
https://<traffic-api-domain>/api/v1/program/bindings/by-iccid
|
||
```
|
||
|
||
将 `<traffic-api-domain>` 替换为实际 4G 管理后端域名。
|
||
|
||
## 3. 调用前提
|
||
|
||
### 3.1 程序用户登录
|
||
|
||
调用方必须先通过程序用户登录接口获取 `PROGRAM_USER` 的 `accessToken`:
|
||
|
||
```http
|
||
POST /api/v1/program/auth/login
|
||
```
|
||
|
||
后续请求携带:
|
||
|
||
```http
|
||
Authorization: Bearer <accessToken>
|
||
```
|
||
|
||
不能使用 `SUPER_ADMIN`、`FACTORY_PROVISIONER` 或设备 Token 调用此接口。
|
||
|
||
### 3.2 来源白名单
|
||
|
||
当前默认允许以下 Origin:
|
||
|
||
```text
|
||
https://banban.api.talkingq.com
|
||
http://192.168.101.151
|
||
```
|
||
|
||
请求必须携带与页面实际来源完全一致的 `Origin` 请求头:
|
||
|
||
```http
|
||
Origin: https://banban.api.talkingq.com
|
||
```
|
||
|
||
或:
|
||
|
||
```http
|
||
Origin: http://192.168.101.151
|
||
```
|
||
|
||
白名单由环境变量配置:
|
||
|
||
```bash
|
||
BINDING_ALLOWED_ORIGINS=https://banban.api.talkingq.com,http://192.168.101.151
|
||
```
|
||
|
||
多个来源使用英文逗号分隔。来源匹配包含协议、主机和端口,不能只填写裸域名或裸 IP。
|
||
|
||
例如,下面这些值不是同一个 Origin:
|
||
|
||
```text
|
||
http://192.168.101.151
|
||
https://192.168.101.151
|
||
http://192.168.101.151:8080
|
||
```
|
||
|
||
如果页面实际通过 HTTPS 或非默认端口访问,需要把实际 Origin 加入 `BINDING_ALLOWED_ORIGINS` 后重启后端。
|
||
|
||
## 4. 请求格式
|
||
|
||
### 4.1 请求头
|
||
|
||
```http
|
||
Origin: https://banban.api.talkingq.com
|
||
Authorization: Bearer <PROGRAM_USER_ACCESS_TOKEN>
|
||
Content-Type: application/json
|
||
X-Request-Id: direct-bind-20260920-0001
|
||
```
|
||
|
||
`Origin` 是来源白名单校验字段;`Authorization` 是业务身份认证字段;`X-Request-Id` 用于日志追踪,建议每次请求使用唯一值。
|
||
|
||
### 4.2 请求体
|
||
|
||
```json
|
||
{
|
||
"iccid": "898604E91123D1334901",
|
||
"requestId": "direct-bind-20260920-0001"
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `iccid` | string | 是 | 设备 ICCID,18 至 22 位字母或数字,后端会统一转为大写 |
|
||
| `requestId` | string | 是 | 幂等请求号,最长 64 个字符;同一用户重试必须使用相同值 |
|
||
|
||
## 5. curl 联调示例
|
||
|
||
请将 Token 和 ICCID 替换为真实值,不要把 Token 提交到代码仓库或群聊。
|
||
|
||
```bash
|
||
curl -i -X POST \
|
||
"https://<traffic-api-domain>/api/v1/program/bindings/by-iccid" \
|
||
-H "Origin: https://banban.api.talkingq.com" \
|
||
-H "Authorization: Bearer <PROGRAM_USER_ACCESS_TOKEN>" \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-Request-Id: direct-bind-20260920-0001" \
|
||
--data '{
|
||
"iccid": "898604E91123D1334901",
|
||
"requestId": "direct-bind-20260920-0001"
|
||
}'
|
||
```
|
||
|
||
使用 IP 来源联调时,将 `Origin` 改为:
|
||
|
||
```http
|
||
Origin: http://192.168.101.151
|
||
```
|
||
|
||
## 6. 成功响应
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "success",
|
||
"data": {
|
||
"iccid": "898604E91123D1334901",
|
||
"bindingStatus": "active",
|
||
"idempotent": false,
|
||
"boundAt": "2026-09-20T15:30:00"
|
||
},
|
||
"requestId": "http-request-id",
|
||
"timestamp": "2026-09-20T07:30:00Z"
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `data.iccid` | 实际完成绑定的 ICCID |
|
||
| `data.bindingStatus` | 成功时为 `active`,表示用户绑定关系有效 |
|
||
| `data.idempotent` | `false` 表示本次新建绑定,`true` 表示重复请求已返回原绑定结果 |
|
||
| `data.boundAt` | 绑定时间 |
|
||
|
||
## 7. 幂等重试规则
|
||
|
||
请求超时或网络断开时,使用同一个 `requestId` 重试,不要生成新的请求号:
|
||
|
||
```json
|
||
{
|
||
"iccid": "898604E91123D1334901",
|
||
"requestId": "direct-bind-20260920-0001"
|
||
}
|
||
```
|
||
|
||
同一程序用户、同一 ICCID、同一 `requestId` 重试时,接口返回 `idempotent=true`。
|
||
|
||
同一 `requestId` 改用其他 ICCID 时,接口返回 `IDEMPOTENCY_CONFLICT`。
|
||
|
||
## 8. 常见失败响应
|
||
|
||
### 8.1 来源不在白名单
|
||
|
||
```http
|
||
HTTP/1.1 403 Forbidden
|
||
```
|
||
|
||
可能原因:
|
||
|
||
- 未携带 `Origin`;
|
||
- Origin 协议不一致,例如配置了 HTTPS 但请求使用 HTTP;
|
||
- IP 使用了非默认端口;
|
||
- 后端环境变量未更新或服务未重启。
|
||
|
||
### 8.2 未登录或 Token 无效
|
||
|
||
```json
|
||
{
|
||
"code": "UNAUTHORIZED"
|
||
}
|
||
```
|
||
|
||
请确认使用的是程序用户登录返回的 `accessToken`,并且 Token 尚未过期或撤销。
|
||
|
||
### 8.3 设备不存在
|
||
|
||
```json
|
||
{
|
||
"code": "DEVICE_NOT_FOUND"
|
||
}
|
||
```
|
||
|
||
表示 ICCID 尚未登记到后端。该接口不会自动创建设备档案,应先通过设备接入或预置流程登记设备。
|
||
|
||
### 8.4 设备已被其他用户绑定
|
||
|
||
```json
|
||
{
|
||
"code": "DEVICE_ALREADY_BOUND"
|
||
}
|
||
```
|
||
|
||
不能通过该接口覆盖其他用户的绑定关系,需要先由当前绑定用户解绑或由管理员执行解绑流程。
|
||
|
||
### 8.5 设备已禁用
|
||
|
||
```json
|
||
{
|
||
"code": "DEVICE_DISABLED"
|
||
}
|
||
```
|
||
|
||
需要先恢复设备状态为 `active`。
|
||
|
||
### 8.6 参数错误
|
||
|
||
```json
|
||
{
|
||
"code": "INVALID_REQUEST"
|
||
}
|
||
```
|
||
|
||
请检查 `iccid` 是否为 18 至 22 位字母数字,以及 `requestId` 是否为空或超过 64 个字符。
|
||
|
||
## 9. 浏览器联调注意事项
|
||
|
||
- 前端请求必须使用 `Authorization` 请求头携带 Token,不能只依赖 Cookie;
|
||
- 页面来源必须与白名单完全一致;
|
||
- 浏览器会先发送 CORS 预检请求,后端已经允许 `POST`、`OPTIONS` 以及 `Authorization`、`Content-Type`、`X-Request-Id` 请求头;
|
||
- 如果通过后端服务调用,HTTP 客户端通常不会自动添加 `Origin`,需要按接口要求主动设置;
|
||
- `Origin` 不是加密签名,不能替代登录 Token;
|
||
- 联调日志中可以使用响应里的 `requestId` 联系后端排查。
|
||
|
||
## 10. 验收检查
|
||
|
||
建议至少验证以下场景:
|
||
|
||
1. 使用 `https://banban.api.talkingq.com` 来源和有效程序用户 Token,绑定一个未绑定的 active ICCID,返回 `idempotent=false`。
|
||
2. 使用相同用户、相同 ICCID、相同 `requestId` 再次调用,返回 `idempotent=true`。
|
||
3. 使用其他 Origin 调用,返回 HTTP 403。
|
||
4. 不携带 `Authorization`,返回 `UNAUTHORIZED`。
|
||
5. 使用已绑定到其他用户的 ICCID,返回 `DEVICE_ALREADY_BOUND`。
|
||
6. 使用不存在的 ICCID,返回 `DEVICE_NOT_FOUND`。
|
||
7. 确认数据库中 `sim_card.binding_status` 为 `bound`,并且 `user_device_binding.status` 为 `active`。
|