init
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m3s

This commit is contained in:
Break
2026-06-16 15:15:50 +08:00
parent 84ab3bad99
commit 1f634eb465
21 changed files with 2776 additions and 41 deletions

View File

@@ -204,7 +204,68 @@ components:
description: 当前生效套餐已用流量(MB)
type: integer
card_no:
description: 卡标识
description: 卡标识(原始传入值)
type: string
device_no:
description: 设备虚拟号(当入参被识别为设备维度时返回,表示该卡/标识关联的设备)
type: string
pending_packages:
description: 待生效套餐列表
items:
$ref: '#/components/schemas/DtoAgentOpenAPIPackageTrafficItem'
nullable: true
type: array
type: object
DtoAgentOpenAPIDeviceOperationRequest:
properties:
device_no:
description: 设备标识支持虚拟号、IMEI
maxLength: 100
minLength: 1
type: string
required:
- device_no
type: object
DtoAgentOpenAPIDeviceSwitchCardRequest:
properties:
device_no:
description: 设备标识支持虚拟号、IMEI
maxLength: 100
minLength: 1
type: string
iccid:
description: 目标卡 ICCID
maxLength: 100
minLength: 1
type: string
required:
- device_no
- iccid
type: object
DtoAgentOpenAPIDeviceTrafficResponse:
properties:
active_expires_at:
description: 当前生效套餐最晚过期时间
format: date-time
nullable: true
type: string
active_packages:
description: 当前生效套餐列表
items:
$ref: '#/components/schemas/DtoAgentOpenAPIPackageTrafficItem'
nullable: true
type: array
active_remaining_flow_mb:
description: 当前生效套餐剩余流量(MB)
type: integer
active_total_flow_mb:
description: 当前生效套餐总流量(MB)
type: integer
active_used_flow_mb:
description: 当前生效套餐已用流量(MB)
type: integer
device_no:
description: 设备标识
type: string
pending_packages:
description: 待生效套餐列表
@@ -1269,7 +1330,7 @@ components:
minimum: 0
type: integer
paid_amount:
description: 购买实付金额(分),线下支付或无订单分配时为空
description: 购买成本价(分),仅平台账号可见,非平台账号不返回此字段
nullable: true
type: integer
priority:
@@ -1292,6 +1353,10 @@ components:
refund_no:
description: 退款单号快照(未退款时为空)
type: string
retail_amount:
description: 购买零售价(分),无订单关联时为空
nullable: true
type: integer
status:
description: 状态0待生效 1生效中 2已用完 3已过期 4已失效
type: integer
@@ -1679,7 +1744,7 @@ components:
nullable: true
type: string
reference_type:
description: 关联业务类型rechargeorder可空
description: 关联业务类型rechargeorder 或 exchange(可空)
nullable: true
type: string
remark:
@@ -1687,10 +1752,10 @@ components:
nullable: true
type: string
transaction_type:
description: 交易类型recharge/deduct/refund
description: 交易类型recharge/deduct/refund/exchange
type: string
transaction_type_text:
description: 交易类型文本:充值/扣款/退款
description: 交易类型文本:充值/扣款/退款/换货迁移
type: string
type: object
DtoAssetWalletTransactionListResponse:
@@ -2468,6 +2533,9 @@ components:
exchange_reason:
description: 换货原因
type: string
flow_type:
description: 换货流程类型 (shipping:物流换货, direct:直接换货)
type: string
id:
description: 换货单ID
minimum: 0
@@ -2475,6 +2543,9 @@ components:
status:
description: 换货状态 (1:待填写信息, 2:待发货, 3:已发货待确认, 4:已完成, 5:已取消)
type: integer
status_name:
description: 换货状态名称(中文)
type: string
status_text:
description: 换货状态文本
type: string
@@ -3148,6 +3219,21 @@ components:
maxLength: 100
minLength: 1
type: string
flow_type:
description: 换货流程类型 (shipping:物流换货, direct:直接换货)
enum:
- shipping
- direct
type: string
migrate_data:
description: 是否执行全量迁移direct 流程未传按 false 处理
nullable: true
type: boolean
new_identifier:
description: 新资产标识符direct 流程必填(ICCID/虚拟号/IMEI/SN)
maxLength: 100
minLength: 1
type: string
old_asset_type:
description: 旧资产类型 (iot_card:物联网卡, device:设备)
type: string
@@ -4895,6 +4981,11 @@ components:
type: object
DtoExchangeOrderResponse:
properties:
completed_at:
description: 换货完成时间
format: date-time
nullable: true
type: string
created_at:
description: 创建时间
format: date-time
@@ -4920,6 +5011,12 @@ components:
express_no:
description: 快递单号
type: string
flow_type:
description: 换货流程类型 (shipping:物流换货, direct:直接换货)
type: string
flow_type_name:
description: 换货流程类型名称
type: string
id:
description: 换货单ID
minimum: 0
@@ -4967,6 +5064,11 @@ components:
description: 备注
nullable: true
type: string
shipped_at:
description: 发货时间
format: date-time
nullable: true
type: string
shop_id:
description: 所属店铺ID
minimum: 0
@@ -4975,6 +5077,9 @@ components:
status:
description: 换货状态 (1:待填写信息, 2:待发货, 3:已发货待确认, 4:已完成, 5:已取消)
type: integer
status_name:
description: 换货状态名称(中文)
type: string
status_text:
description: 换货状态文本
type: string
@@ -8118,6 +8223,24 @@ components:
nullable: true
type: string
type: object
DtoUpdateAssetPackageExpiresAtRequest:
properties:
expires_at:
description: 新的套餐过期时间格式YYYY-MM-DD HH:MM:SS 或 RFC3339
type: string
required:
- expires_at
type: object
DtoUpdateAssetPackageUsageRequest:
properties:
data_usage_mb:
description: 新的套餐真实已用量(MB)
minimum: 0
nullable: true
type: integer
required:
- data_usage_mb
type: object
DtoUpdateAssetPollingStatusRequest:
properties:
enable_polling:
@@ -10975,6 +11098,166 @@ paths:
summary: 资产套餐列表
tags:
- 资产管理
/api/admin/assets/{identifier}/packages/{package_usage_id}/expires-at:
patch:
description: 修改指定资产下某条套餐使用记录的过期时间。仅账号ID为2的账号可调用。
parameters:
- description: 资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN
in: path
name: identifier
required: true
schema:
description: 资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN
type: string
- description: 套餐使用记录ID
in: path
name: package_usage_id
required: true
schema:
description: 套餐使用记录ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoUpdateAssetPackageExpiresAtRequest'
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoAssetPackageResponse'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- BearerAuth: []
summary: 修改资产套餐过期时间
tags:
- 资产管理
/api/admin/assets/{identifier}/packages/{package_usage_id}/used-data:
patch:
description: 修改指定资产下某条套餐使用记录的真实已用量(MB)。仅账号ID为2的账号可调用。
parameters:
- description: 资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN
in: path
name: identifier
required: true
schema:
description: 资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN
type: string
- description: 套餐使用记录ID
in: path
name: package_usage_id
required: true
schema:
description: 套餐使用记录ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoUpdateAssetPackageUsageRequest'
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoAssetPackageResponse'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- BearerAuth: []
summary: 修改资产套餐已用量
tags:
- 资产管理
/api/admin/assets/{identifier}/polling-status:
patch:
description: 启用或禁用指定资产IoT卡或设备的定期状态轮询。
@@ -11490,11 +11773,11 @@ paths:
schema:
description: 每页数量默认20最大100
type: integer
- description: 交易类型过滤recharge/deduct/refund
- description: 交易类型过滤recharge/deduct/refund/exchange
in: query
name: transaction_type
schema:
description: 交易类型过滤recharge/deduct/refund
description: 交易类型过滤recharge/deduct/refund/exchange
nullable: true
type: string
- description: 开始时间RFC3339
@@ -13399,6 +13682,13 @@ paths:
description: 是否有生效中的套餐true:有生效中主套餐, false:无生效中主套餐)
nullable: true
type: boolean
- description: 关键字搜索匹配虚拟号或IMEI(模糊查询与virtual_no/imei独立)
in: query
name: keyword
schema:
description: 关键字搜索匹配虚拟号或IMEI(模糊查询与virtual_no/imei独立)
maxLength: 100
type: string
responses:
"200":
content:
@@ -15499,6 +15789,15 @@ paths:
minimum: 1
nullable: true
type: integer
- description: 换货流程类型 (shipping:物流换货, direct:直接换货)
in: query
name: flow_type
schema:
description: 换货流程类型 (shipping:物流换货, direct:直接换货)
enum:
- shipping
- direct
type: string
- description: 资产标识符搜索(旧资产/新资产标识符模糊匹配)
in: query
name: identifier
@@ -16772,6 +17071,13 @@ paths:
description: 运营商名称(模糊查询)
maxLength: 100
type: string
- description: 关键字搜索匹配ICCID或卡虚拟号(模糊查询与iccid/virtual_no独立)
in: query
name: keyword
schema:
description: 关键字搜索匹配ICCID或卡虚拟号(模糊查询与iccid/virtual_no独立)
maxLength: 100
type: string
responses:
"200":
content:
@@ -26812,7 +27118,7 @@ paths:
}
```
按 card_no 查询单卡流量、当前套餐和待生效套餐。card_no 支持 ICCID虚拟号MSISDN。
按 card_no 查询流量、当前套餐和待生效套餐。card_no 支持三种解析路径1) ICCID/虚拟号/MSISDN 对应独立卡查卡维度流量2) 已绑定设备的卡,自动反查绑定设备,查设备维度流量(响应含 device_no3) IMEI 或设备虚拟号,直接查设备维度流量(响应含 device_no
parameters:
- description: 卡标识(支持 ICCID、虚拟号、MSISDN
in: query
@@ -26880,7 +27186,462 @@ paths:
AgentOpenAPIPassword: []
AgentOpenAPISign: []
AgentOpenAPITimestamp: []
summary: 查询单卡流量
summary: 查询单卡或设备流量
tags:
- 代理开放接口
/api/open/v1/devices/reboot:
post:
description: |-
认证方式:
1. 每个请求必须携带 HeaderX-Agent-Account、X-Agent-Password、X-Agent-Timestamp、X-Agent-Nonce、X-Agent-Sign。
2. Header 字段说明:
- X-Agent-Account代理账号标识填写代理登录平台使用的用户名或手机号例如 agent001该值由平台开通代理账号时提供必须与签名原文 account 行完全一致。
- X-Agent-Password代理账号当前登录密码由代理账号持有人提供该值同时作为 HMAC-SHA256 签名密钥,密码变更后必须使用新密码签名。
- X-Agent-Timestamp请求发起时间由调用方生成支持 Unix 秒、Unix 毫秒或 RFC3339 时间,例如 1715400000、1715400000000 或 2024-05-11T10:00:00+08:00必须与签名原文 timestamp 行完全一致。
- X-Agent-Nonce请求随机串由调用方每次请求自行生成例如 UUID、雪花 ID 或“时间戳+随机数”;不需要平台提前分配,同一账号在 5 分钟内不可重复。
- X-Agent-Sign请求签名由调用方按下方规则实时计算得到不是平台分配的固定值只要 method、path、query、body、timestamp、nonce、account 或 password 任意一个变化,签名都会变化。
3. X-Agent-Timestamp 超出允许时间窗口会认证失败;客户端服务器时间应保持同步。
4. X-Agent-Nonce 重复会被判定为重放请求。
5. 签名原文按 7 行拼接,行尾使用 \n最后一行后不追加换行
METHOD
PATH
canonical_query
body_sha256
timestamp
nonce
account
6. METHOD 使用大写 HTTP 方法PATH 只取请求路径,例如 /api/open/v1/cards/status不包含域名和 query。
7. canonical_query 为 query 参数规范化结果:排除 sign 字段参数名升序同名多值按值升序key 和 value 使用 URL QueryEscape 编码后用 key=value 拼接,多个参数用 & 连接;无 query 时为空字符串。
8. body_sha256 为原始请求 body 字节的 SHA256 小写十六进制值GET 或空 body 使用空字符串的 SHA256POST JSON 请求必须用最终发送的 body 字符串计算,签名和请求发送的 body 必须完全一致。
9. X-Agent-Sign = hex(HMAC-SHA256(password, sign_payload))password 为 X-Agent-Password 的原始值,结果使用小写十六进制。
Node.js 签名示例:
```javascript
const crypto = require("crypto");
function queryEscape(value) {
return new URLSearchParams({ v: String(value) }).toString().slice(2);
}
function canonicalQuery(params) {
return Object.entries(params || {})
.filter(([key]) => key.toLowerCase() !== "sign")
.flatMap(([key, value]) => {
const values = Array.isArray(value) ? value : [value];
return values.sort().map((item) => [key, item]);
})
.sort(([aKey, aValue], [bKey, bValue]) => {
if (aKey === bKey) return String(aValue).localeCompare(String(bValue));
return aKey.localeCompare(bKey);
})
.map(([key, value]) => queryEscape(key) + "=" + queryEscape(value))
.join("&");
}
function buildAgentSign({ method, path, query, body, timestamp, nonce, account, password }) {
const bodyText = body || "";
const bodyHash = crypto.createHash("sha256").update(bodyText).digest("hex");
const signPayload = [
method.toUpperCase(),
path,
canonicalQuery(query),
bodyHash,
timestamp,
nonce,
account,
].join("\n");
return crypto.createHmac("sha256", password).update(signPayload).digest("hex");
}
```
按 device_no 远程重启设备。
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoAgentOpenAPIDeviceOperationRequest'
responses:
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- AgentOpenAPIAccount: []
AgentOpenAPINonce: []
AgentOpenAPIPassword: []
AgentOpenAPISign: []
AgentOpenAPITimestamp: []
summary: 重启设备
tags:
- 代理开放接口
/api/open/v1/devices/reset:
post:
description: |-
认证方式:
1. 每个请求必须携带 HeaderX-Agent-Account、X-Agent-Password、X-Agent-Timestamp、X-Agent-Nonce、X-Agent-Sign。
2. Header 字段说明:
- X-Agent-Account代理账号标识填写代理登录平台使用的用户名或手机号例如 agent001该值由平台开通代理账号时提供必须与签名原文 account 行完全一致。
- X-Agent-Password代理账号当前登录密码由代理账号持有人提供该值同时作为 HMAC-SHA256 签名密钥,密码变更后必须使用新密码签名。
- X-Agent-Timestamp请求发起时间由调用方生成支持 Unix 秒、Unix 毫秒或 RFC3339 时间,例如 1715400000、1715400000000 或 2024-05-11T10:00:00+08:00必须与签名原文 timestamp 行完全一致。
- X-Agent-Nonce请求随机串由调用方每次请求自行生成例如 UUID、雪花 ID 或“时间戳+随机数”;不需要平台提前分配,同一账号在 5 分钟内不可重复。
- X-Agent-Sign请求签名由调用方按下方规则实时计算得到不是平台分配的固定值只要 method、path、query、body、timestamp、nonce、account 或 password 任意一个变化,签名都会变化。
3. X-Agent-Timestamp 超出允许时间窗口会认证失败;客户端服务器时间应保持同步。
4. X-Agent-Nonce 重复会被判定为重放请求。
5. 签名原文按 7 行拼接,行尾使用 \n最后一行后不追加换行
METHOD
PATH
canonical_query
body_sha256
timestamp
nonce
account
6. METHOD 使用大写 HTTP 方法PATH 只取请求路径,例如 /api/open/v1/cards/status不包含域名和 query。
7. canonical_query 为 query 参数规范化结果:排除 sign 字段参数名升序同名多值按值升序key 和 value 使用 URL QueryEscape 编码后用 key=value 拼接,多个参数用 & 连接;无 query 时为空字符串。
8. body_sha256 为原始请求 body 字节的 SHA256 小写十六进制值GET 或空 body 使用空字符串的 SHA256POST JSON 请求必须用最终发送的 body 字符串计算,签名和请求发送的 body 必须完全一致。
9. X-Agent-Sign = hex(HMAC-SHA256(password, sign_payload))password 为 X-Agent-Password 的原始值,结果使用小写十六进制。
Node.js 签名示例:
```javascript
const crypto = require("crypto");
function queryEscape(value) {
return new URLSearchParams({ v: String(value) }).toString().slice(2);
}
function canonicalQuery(params) {
return Object.entries(params || {})
.filter(([key]) => key.toLowerCase() !== "sign")
.flatMap(([key, value]) => {
const values = Array.isArray(value) ? value : [value];
return values.sort().map((item) => [key, item]);
})
.sort(([aKey, aValue], [bKey, bValue]) => {
if (aKey === bKey) return String(aValue).localeCompare(String(bValue));
return aKey.localeCompare(bKey);
})
.map(([key, value]) => queryEscape(key) + "=" + queryEscape(value))
.join("&");
}
function buildAgentSign({ method, path, query, body, timestamp, nonce, account, password }) {
const bodyText = body || "";
const bodyHash = crypto.createHash("sha256").update(bodyText).digest("hex");
const signPayload = [
method.toUpperCase(),
path,
canonicalQuery(query),
bodyHash,
timestamp,
nonce,
account,
].join("\n");
return crypto.createHmac("sha256", password).update(signPayload).digest("hex");
}
```
按 device_no 远程恢复设备出厂设置。
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoAgentOpenAPIDeviceOperationRequest'
responses:
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- AgentOpenAPIAccount: []
AgentOpenAPINonce: []
AgentOpenAPIPassword: []
AgentOpenAPISign: []
AgentOpenAPITimestamp: []
summary: 恢复出厂设置
tags:
- 代理开放接口
/api/open/v1/devices/switch-card:
post:
description: |-
认证方式:
1. 每个请求必须携带 HeaderX-Agent-Account、X-Agent-Password、X-Agent-Timestamp、X-Agent-Nonce、X-Agent-Sign。
2. Header 字段说明:
- X-Agent-Account代理账号标识填写代理登录平台使用的用户名或手机号例如 agent001该值由平台开通代理账号时提供必须与签名原文 account 行完全一致。
- X-Agent-Password代理账号当前登录密码由代理账号持有人提供该值同时作为 HMAC-SHA256 签名密钥,密码变更后必须使用新密码签名。
- X-Agent-Timestamp请求发起时间由调用方生成支持 Unix 秒、Unix 毫秒或 RFC3339 时间,例如 1715400000、1715400000000 或 2024-05-11T10:00:00+08:00必须与签名原文 timestamp 行完全一致。
- X-Agent-Nonce请求随机串由调用方每次请求自行生成例如 UUID、雪花 ID 或“时间戳+随机数”;不需要平台提前分配,同一账号在 5 分钟内不可重复。
- X-Agent-Sign请求签名由调用方按下方规则实时计算得到不是平台分配的固定值只要 method、path、query、body、timestamp、nonce、account 或 password 任意一个变化,签名都会变化。
3. X-Agent-Timestamp 超出允许时间窗口会认证失败;客户端服务器时间应保持同步。
4. X-Agent-Nonce 重复会被判定为重放请求。
5. 签名原文按 7 行拼接,行尾使用 \n最后一行后不追加换行
METHOD
PATH
canonical_query
body_sha256
timestamp
nonce
account
6. METHOD 使用大写 HTTP 方法PATH 只取请求路径,例如 /api/open/v1/cards/status不包含域名和 query。
7. canonical_query 为 query 参数规范化结果:排除 sign 字段参数名升序同名多值按值升序key 和 value 使用 URL QueryEscape 编码后用 key=value 拼接,多个参数用 & 连接;无 query 时为空字符串。
8. body_sha256 为原始请求 body 字节的 SHA256 小写十六进制值GET 或空 body 使用空字符串的 SHA256POST JSON 请求必须用最终发送的 body 字符串计算,签名和请求发送的 body 必须完全一致。
9. X-Agent-Sign = hex(HMAC-SHA256(password, sign_payload))password 为 X-Agent-Password 的原始值,结果使用小写十六进制。
Node.js 签名示例:
```javascript
const crypto = require("crypto");
function queryEscape(value) {
return new URLSearchParams({ v: String(value) }).toString().slice(2);
}
function canonicalQuery(params) {
return Object.entries(params || {})
.filter(([key]) => key.toLowerCase() !== "sign")
.flatMap(([key, value]) => {
const values = Array.isArray(value) ? value : [value];
return values.sort().map((item) => [key, item]);
})
.sort(([aKey, aValue], [bKey, bValue]) => {
if (aKey === bKey) return String(aValue).localeCompare(String(bValue));
return aKey.localeCompare(bKey);
})
.map(([key, value]) => queryEscape(key) + "=" + queryEscape(value))
.join("&");
}
function buildAgentSign({ method, path, query, body, timestamp, nonce, account, password }) {
const bodyText = body || "";
const bodyHash = crypto.createHash("sha256").update(bodyText).digest("hex");
const signPayload = [
method.toUpperCase(),
path,
canonicalQuery(query),
bodyHash,
timestamp,
nonce,
account,
].join("\n");
return crypto.createHmac("sha256", password).update(signPayload).digest("hex");
}
```
按 device_no 将多卡设备切换到指定 ICCID 的卡。
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoAgentOpenAPIDeviceSwitchCardRequest'
responses:
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- AgentOpenAPIAccount: []
AgentOpenAPINonce: []
AgentOpenAPIPassword: []
AgentOpenAPISign: []
AgentOpenAPITimestamp: []
summary: 切网(多卡设备切换 ICCID
tags:
- 代理开放接口
/api/open/v1/devices/traffic:
get:
description: |-
认证方式:
1. 每个请求必须携带 HeaderX-Agent-Account、X-Agent-Password、X-Agent-Timestamp、X-Agent-Nonce、X-Agent-Sign。
2. Header 字段说明:
- X-Agent-Account代理账号标识填写代理登录平台使用的用户名或手机号例如 agent001该值由平台开通代理账号时提供必须与签名原文 account 行完全一致。
- X-Agent-Password代理账号当前登录密码由代理账号持有人提供该值同时作为 HMAC-SHA256 签名密钥,密码变更后必须使用新密码签名。
- X-Agent-Timestamp请求发起时间由调用方生成支持 Unix 秒、Unix 毫秒或 RFC3339 时间,例如 1715400000、1715400000000 或 2024-05-11T10:00:00+08:00必须与签名原文 timestamp 行完全一致。
- X-Agent-Nonce请求随机串由调用方每次请求自行生成例如 UUID、雪花 ID 或“时间戳+随机数”;不需要平台提前分配,同一账号在 5 分钟内不可重复。
- X-Agent-Sign请求签名由调用方按下方规则实时计算得到不是平台分配的固定值只要 method、path、query、body、timestamp、nonce、account 或 password 任意一个变化,签名都会变化。
3. X-Agent-Timestamp 超出允许时间窗口会认证失败;客户端服务器时间应保持同步。
4. X-Agent-Nonce 重复会被判定为重放请求。
5. 签名原文按 7 行拼接,行尾使用 \n最后一行后不追加换行
METHOD
PATH
canonical_query
body_sha256
timestamp
nonce
account
6. METHOD 使用大写 HTTP 方法PATH 只取请求路径,例如 /api/open/v1/cards/status不包含域名和 query。
7. canonical_query 为 query 参数规范化结果:排除 sign 字段参数名升序同名多值按值升序key 和 value 使用 URL QueryEscape 编码后用 key=value 拼接,多个参数用 & 连接;无 query 时为空字符串。
8. body_sha256 为原始请求 body 字节的 SHA256 小写十六进制值GET 或空 body 使用空字符串的 SHA256POST JSON 请求必须用最终发送的 body 字符串计算,签名和请求发送的 body 必须完全一致。
9. X-Agent-Sign = hex(HMAC-SHA256(password, sign_payload))password 为 X-Agent-Password 的原始值,结果使用小写十六进制。
Node.js 签名示例:
```javascript
const crypto = require("crypto");
function queryEscape(value) {
return new URLSearchParams({ v: String(value) }).toString().slice(2);
}
function canonicalQuery(params) {
return Object.entries(params || {})
.filter(([key]) => key.toLowerCase() !== "sign")
.flatMap(([key, value]) => {
const values = Array.isArray(value) ? value : [value];
return values.sort().map((item) => [key, item]);
})
.sort(([aKey, aValue], [bKey, bValue]) => {
if (aKey === bKey) return String(aValue).localeCompare(String(bValue));
return aKey.localeCompare(bKey);
})
.map(([key, value]) => queryEscape(key) + "=" + queryEscape(value))
.join("&");
}
function buildAgentSign({ method, path, query, body, timestamp, nonce, account, password }) {
const bodyText = body || "";
const bodyHash = crypto.createHash("sha256").update(bodyText).digest("hex");
const signPayload = [
method.toUpperCase(),
path,
canonicalQuery(query),
bodyHash,
timestamp,
nonce,
account,
].join("\n");
return crypto.createHmac("sha256", password).update(signPayload).digest("hex");
}
```
按 device_no 查询设备套餐内流量、当前套餐和待生效套餐。device_no 支持虚拟号、IMEI。
parameters:
- description: 设备标识支持虚拟号、IMEI
in: query
name: device_no
required: true
schema:
description: 设备标识支持虚拟号、IMEI
maxLength: 100
minLength: 1
type: string
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoAgentOpenAPIDeviceTrafficResponse'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- AgentOpenAPIAccount: []
AgentOpenAPINonce: []
AgentOpenAPIPassword: []
AgentOpenAPISign: []
AgentOpenAPITimestamp: []
summary: 查询设备套餐内流量
tags:
- 代理开放接口
/api/open/v1/packages: