Files
junhong_cmp_fiber/docs/前端接口变更说明.md
huang b9733c4913
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s
fix: 修正零售价架构错误 + 清理旧微信配置 + 归档提案 + 前端接口文档
1. 修正 retail_price 架构:
   - 删除 batch-pricing 接口的 pricing_target 字段和 retail_price 分支
     (上级只能改下级成本价,不能改零售价)
   - 新增 PATCH /api/admin/packages/:id/retail-price 接口
     (代理自己改自己的零售价,校验 retail_price >= cost_price)

2. 清理旧微信 YAML 配置(已全部迁移到数据库 tb_wechat_config):
   - 删除 config.yaml 中 wechat.official_account 配置节
   - 删除 NewOfficialAccountApp() 旧工厂函数
   - 清理 personal_customer service 中的死代码(旧登录/绑定微信方法)
   - 清理 docker-compose.prod.yml 中旧微信环境变量和证书挂载注释

3. 归档四个已完成提案到 openspec/changes/archive/

4. 新增前端接口变更说明文档(docs/前端接口变更说明.md)

5. 修正归档提案和 specs 中关于 pricing_target 的错误描述
2026-03-19 17:39:43 +08:00

1206 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端接口变更说明
> 最后更新2026-03-19
---
## 目录
1. [接口变更概览](#一接口变更概览)
2. [删除的接口](#二删除的接口)
3. [修改的后台接口](#三修改的后台接口)
4. [新增的后台接口](#四新增的后台接口)
5. [新增的 C 端接口](#五新增的-c-端接口)
6. [认证方式说明](#六认证方式说明)
7. [统一响应格式](#七统一响应格式)
---
## 一、接口变更概览
**C 端接口统一前缀**`/api/c/v1/`
**后台接口统一前缀**`/api/admin/`
### 删除
| 接口 | 说明 |
|------|------|
| `POST /api/personal/login` | 旧个人客户登录,已下线 |
| `POST /api/personal/send-code` | 旧验证码发送,已下线 |
| `POST /api/personal/wechat-oauth-login` | 旧微信登录,已下线 |
| `POST /api/personal/bind-wechat` | 旧微信绑定,已下线 |
| `GET /api/personal/profile` | 旧个人信息,已下线 |
| `/api/h5/*` 下所有路由 | 旧 H5 接口全部下线 |
### 修改(后台)
| 接口 | 变更内容 |
|------|----------|
| `POST /api/admin/shop-package-batch-pricing/batch-update` | 仅支持批量调整成本价(移除 `pricing_target` |
| 所有返回 `PackageResponse` 的套餐列表接口 | 响应新增 `retail_price` 字段 |
| 运营商创建/编辑接口 | 新增 `realname_link_type``realname_link_template` 字段 |
### 新增(后台)
| 接口 | 说明 |
|------|------|
| `PATCH /api/admin/packages/:id/retail-price` | 代理修改自己的套餐零售价 |
| `PATCH /api/admin/iot-cards/:id/deactivate` | 手动停用 IoT 卡 |
| `PATCH /api/admin/devices/:id/deactivate` | 手动停用设备 |
| `POST /api/admin/exchanges` | 发起换货单 |
| `GET /api/admin/exchanges` | 换货单列表 |
| `GET /api/admin/exchanges/:id` | 换货单详情 |
| `POST /api/admin/exchanges/:id/ship` | 发货 |
| `POST /api/admin/exchanges/:id/complete` | 确认完成 |
| `POST /api/admin/exchanges/:id/cancel` | 取消换货 |
| `POST /api/admin/exchanges/:id/renew` | 旧资产转新generation+1 |
### 新增C 端)
| 接口 | 说明 |
|------|------|
| `POST /api/c/v1/auth/verify-asset` | 资产验证 |
| `POST /api/c/v1/auth/wechat-login` | 公众号登录 |
| `POST /api/c/v1/auth/miniapp-login` | 小程序登录 |
| `POST /api/c/v1/auth/send-code` | 发送验证码 |
| `POST /api/c/v1/auth/bind-phone` | 绑定手机号 |
| `POST /api/c/v1/auth/change-phone` | 换绑手机号 |
| `POST /api/c/v1/auth/logout` | 退出登录 |
| `GET /api/c/v1/asset/info` | 资产基本信息 |
| `GET /api/c/v1/asset/packages` | 可购套餐列表 |
| `GET /api/c/v1/asset/package-history` | 历史套餐列表 |
| `POST /api/c/v1/asset/refresh` | 手动刷新资产状态 |
| `GET /api/c/v1/wallet/detail` | 钱包详情 |
| `GET /api/c/v1/wallet/transactions` | 钱包流水列表 |
| `GET /api/c/v1/wallet/recharge-check` | 充值预检 |
| `POST /api/c/v1/wallet/recharge` | 创建充值订单 |
| `GET /api/c/v1/wallet/recharges` | 充值记录列表 |
| `POST /api/c/v1/orders/create` | 创建套餐购买订单 |
| `GET /api/c/v1/orders` | 订单列表 |
| `GET /api/c/v1/orders/:id` | 订单详情 |
| `GET /api/c/v1/realname/link` | 获取实名跳转链接 |
| `GET /api/c/v1/device/cards` | 设备卡列表 |
| `POST /api/c/v1/device/reboot` | 设备重启 |
| `POST /api/c/v1/device/factory-reset` | 恢复出厂设置 |
| `POST /api/c/v1/device/wifi` | WiFi 配置 |
| `POST /api/c/v1/device/switch-card` | 切卡 |
| `GET /api/c/v1/exchange/pending` | 查询进行中的换货通知 |
| `POST /api/c/v1/exchange/:id/shipping-info` | 填写收货信息 |
---
## 二、删除的接口
以下接口已全部下线,请停止调用:
**旧 H5 接口(`/api/h5/` 下所有路由)**
**旧个人客户接口(`/api/personal/` 下):**
- `POST /api/personal/login`
- `POST /api/personal/send-code`
- `POST /api/personal/wechat-oauth-login`
- `POST /api/personal/bind-wechat`
- `GET /api/personal/profile`
以上接口由新 C 端认证接口(`/api/c/v1/auth/`)替代。
---
## 三、修改的后台接口
### 批量调价接口移除 `pricing_target` 字段
```
POST /api/admin/shop-package-batch-pricing/batch-update
```
**移除请求字段:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `pricing_target` | string | 否 | 已移除,不再支持通过该接口调整零售价 |
**变更说明**:该接口现仅支持批量调整成本价,零售价调整改为独立接口 `PATCH /api/admin/packages/:id/retail-price`
---
### 套餐列表响应新增 `retail_price` 字段
所有返回 `PackageResponse` 的套餐列表接口,响应体新增字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `retail_price` | int64 | 零售价(单位:分),代理商可见 |
---
### 运营商管理 DTO 新增实名相关字段
运营商创建/编辑接口新增以下字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `realname_link_type` | string | 实名链接类型:`none`(无需实名)/ `template`(模板实名)/ `gateway`(网关实名) |
| `realname_link_template` | string | 实名链接模板(`realname_link_type``template` 时使用) |
---
## 四、新增的后台接口
### 资产停用
#### 手动停用 IoT 卡
```
PATCH /api/admin/iot-cards/:id/deactivate
```
- **认证**:需要后台 Bearer Token
- **路径参数**`id`IoT 卡 ID
- **说明**:将卡的 `asset_status` 设为 4已停用
---
#### 手动停用设备
```
PATCH /api/admin/devices/:id/deactivate
```
- **认证**:需要后台 Bearer Token
- **路径参数**`id`(设备 ID
- **说明**:将设备的 `asset_status` 设为 4已停用
---
### 换货管理
#### H1 发起换货单
```
POST /api/admin/exchanges
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `old_asset_type` | string | 是 | 旧资产类型:`iot_card`(物联网卡)/ `device`(设备) |
| `old_identifier` | string | 是 | 旧资产标识符ICCID/虚拟号/IMEI/SN1~100 字符 |
| `exchange_reason` | string | 是 | 换货原因1~100 字符 |
| `remark` | string | 否 | 备注,最多 500 字符 |
---
#### H2 换货单列表
```
GET /api/admin/exchanges
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page` | int | 否 | 页码,最小 1 |
| `page_size` | int | 否 | 每页数量1~100 |
| `status` | int | 否 | 换货状态1待填写信息/ 2待发货/ 3已发货待确认/ 4已完成/ 5已取消 |
| `identifier` | string | 否 | 资产标识符模糊搜索(旧资产/新资产均可) |
| `created_at_start` | string | 否 | 创建时间起始 |
| `created_at_end` | string | 否 | 创建时间结束 |
**响应体list 中每项)**:见下方 `ExchangeOrderResponse` 字段说明。
---
#### H3 换货单详情
```
GET /api/admin/exchanges/:id
```
**路径参数**`id`(换货单 ID
**响应体(`ExchangeOrderResponse`**
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | uint | 换货单 ID |
| `exchange_no` | string | 换货单号 |
| `old_asset_type` | string | 旧资产类型 |
| `old_asset_id` | uint | 旧资产 ID |
| `old_asset_identifier` | string | 旧资产标识符 |
| `new_asset_type` | string | 新资产类型 |
| `new_asset_id` | uint | 新资产 ID可为空 |
| `new_asset_identifier` | string | 新资产标识符 |
| `recipient_name` | string | 收件人姓名 |
| `recipient_phone` | string | 收件人电话 |
| `recipient_address` | string | 收货地址 |
| `express_company` | string | 快递公司 |
| `express_no` | string | 快递单号 |
| `migrate_data` | bool | 是否执行全量迁移 |
| `migration_completed` | bool | 迁移是否已完成 |
| `migration_balance` | int64 | 迁移转移金额(分) |
| `exchange_reason` | string | 换货原因 |
| `remark` | string | 备注(可为空) |
| `status` | int | 换货状态1~5 |
| `status_text` | string | 换货状态文本 |
| `shop_id` | uint | 所属店铺 ID可为空 |
| `created_at` | string | 创建时间 |
| `updated_at` | string | 更新时间 |
| `creator` | uint | 创建人 ID |
| `updater` | uint | 更新人 ID |
---
#### H4 发货
```
POST /api/admin/exchanges/:id/ship
```
**路径参数**`id`(换货单 ID
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `express_company` | string | 是 | 快递公司1~100 字符 |
| `express_no` | string | 是 | 快递单号1~100 字符 |
| `new_identifier` | string | 是 | 新资产标识符ICCID/虚拟号/IMEI/SN1~100 字符 |
| `migrate_data` | bool | 是 | 是否执行全量迁移true执行false不执行 |
---
#### H5 确认完成
```
POST /api/admin/exchanges/:id/complete
```
**路径参数**`id`(换货单 ID
**请求体**:无
**说明**:若发货时 `migrate_data=true`,确认完成时会执行全量数据迁移(钱包余额、套餐记录等从旧资产迁移到新资产)。
---
#### H6 取消换货
```
POST /api/admin/exchanges/:id/cancel
```
**路径参数**`id`(换货单 ID
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `remark` | string | 否 | 取消备注,最多 500 字符 |
**限制**:仅 `status=1`(待填写信息)或 `status=2`(待发货)时可取消。
---
#### H7 旧资产转新generation+1
```
POST /api/admin/exchanges/:id/renew
```
**路径参数**`id`(换货单 ID
**请求体**:无
**说明**:将旧资产的 `generation` 字段加 1使其可重新销售。
---
### 换货业务流程
```mermaid
flowchart TD
A["后台发起换货\nH1 POST /admin/exchanges"] --> B[换货单创建\nstatus=1 待填写信息]
B --> C["客户端轮询通知\nG1 GET /exchange/pending"]
C --> D["客户端填写收货信息\nG2 POST /exchange/:id/shipping-info"]
D --> E[status=2 待发货]
E --> F["后台发货\nH4 POST /exchanges/:id/ship\n填写快递信息和新资产标识符"]
F --> G[status=3 已发货待确认]
G --> H["后台确认完成\nH5 POST /exchanges/:id/complete"]
H --> I{migrate_data=true?}
I -->|是| J[执行全量数据迁移\n钱包余额、套餐记录迁移到新资产]
I -->|否| K[直接完成]
J --> L[status=4 已完成]
K --> L
L --> M{需要回收旧资产?}
M -->|是| N["后台转新\nH7 POST /exchanges/:id/renew\n旧资产 generation+1 可重新销售"]
M -->|否| O[流程结束]
N --> O
```
**取消流程:**
```mermaid
flowchart TD
A[status=1 待填写信息] -->|H6 取消| C[status=5 已取消]
B[status=2 待发货] -->|H6 取消| C
```
### 换货状态机
| 状态值 | 状态名 | 可执行操作 |
|--------|--------|-----------|
| 1 | 待填写信息 | 客户端填写收货信息G2、后台取消H6 |
| 2 | 待发货 | 后台发货H4、后台取消H6 |
| 3 | 已发货待确认 | 后台确认完成H5 |
| 4 | 已完成 | 后台转新H7可选 |
| 5 | 已取消 | 无 |
---
## 五、新增的 C 端接口
所有接口位于 `/api/c/v1/` 下,**认证接口(`/auth/`)无需登录,其余全部需要 JWT 认证**`Authorization: Bearer <token>`)。
---
### 认证(/api/c/v1/auth/
#### A1 资产验证(无需认证)
```
POST /api/c/v1/auth/verify-asset
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符SN/IMEI/虚拟号/ICCID/MSISDN1~50 字符 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `asset_token` | string | 资产令牌5 分钟有效,用于后续登录接口 |
| `expires_in` | int | 过期时间(秒),固定 300 |
---
#### A2 公众号登录(无需认证)
```
POST /api/c/v1/auth/wechat-login
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `code` | string | 是 | 微信 OAuth 授权码 |
| `asset_token` | string | 是 | A1 返回的资产令牌 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `token` | string | 登录 JWT 令牌 |
| `need_bind_phone` | bool | 是否需要绑定手机号true 时引导用户完成 A4+A5 |
| `is_new_user` | bool | 是否新注册用户 |
---
#### A3 小程序登录(无需认证)
```
POST /api/c/v1/auth/miniapp-login
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `code` | string | 是 | 小程序登录凭证 |
| `asset_token` | string | 是 | A1 返回的资产令牌 |
| `nickname` | string | 否 | 用户昵称(前端授权后传入) |
| `avatar_url` | string | 否 | 用户头像 URL前端授权后传入 |
**响应体**:同 A2`token` / `need_bind_phone` / `is_new_user`
---
#### A4 发送验证码(无需认证)
```
POST /api/c/v1/auth/send-code
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `phone` | string | 是 | 手机号,固定 11 位 |
| `scene` | string | 是 | 业务场景:`bind_phone`(绑定手机)/ `change_phone_old`(换绑旧手机验证)/ `change_phone_new`(换绑新手机验证) |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `cooldown_seconds` | int | 冷却秒数,期间不可重复发送 |
---
#### A5 绑定手机号(需 JWT 认证)
```
POST /api/c/v1/auth/bind-phone
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `phone` | string | 是 | 手机号,固定 11 位 |
| `code` | string | 是 | 验证码,固定 6 位 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `phone` | string | 已绑定手机号 |
| `bound_at` | string | 绑定时间 |
---
#### A6 换绑手机号(需 JWT 认证)
```
POST /api/c/v1/auth/change-phone
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `old_phone` | string | 是 | 旧手机号,固定 11 位 |
| `old_code` | string | 是 | 旧手机号验证码,固定 6 位 |
| `new_phone` | string | 是 | 新手机号,固定 11 位 |
| `new_code` | string | 是 | 新手机号验证码,固定 6 位 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `phone` | string | 换绑后手机号 |
| `changed_at` | string | 换绑时间 |
---
#### A7 退出登录(需 JWT 认证)
```
POST /api/c/v1/auth/logout
```
**请求体**:无
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `success` | bool | 是否成功 |
---
### 认证登录完整流程
```mermaid
flowchart TD
A[用户打开客户端] --> B[输入资产标识符\nSN/IMEI/ICCID/虚拟号]
B --> C["A1 POST /auth/verify-asset\n获得 asset_token5分钟有效"]
C --> D{选择登录方式}
D -->|公众号| E["A2 POST /auth/wechat-login\n传入 code + asset_token"]
D -->|小程序| F["A3 POST /auth/miniapp-login\n传入 code + asset_token"]
E --> G{need_bind_phone?}
F --> G
G -->|true 需要绑定| H["A4 POST /auth/send-code\nscene=bind_phone"]
H --> I["A5 POST /auth/bind-phone\n传入 phone + code"]
I --> J[进入主页面]
G -->|false 已绑定| J
```
**换绑手机号流程:**
```mermaid
flowchart TD
A[用户申请换绑] --> B["A4 POST /auth/send-code\nscene=change_phone_old\n发送旧手机验证码"]
B --> C["A4 POST /auth/send-code\nscene=change_phone_new\n发送新手机验证码"]
C --> D["A6 POST /auth/change-phone\n传入 old_phone+old_code+new_phone+new_code"]
D --> E[换绑成功]
```
---
### 资产(/api/c/v1/asset/
#### B1 资产基本信息
```
GET /api/c/v1/asset/info?identifier=xxx
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `asset_type` | string | 资产类型:`card`(卡)/ `device`(设备) |
| `asset_id` | uint | 资产 ID |
| `identifier` | string | 资产标识符 |
| `virtual_no` | string | 虚拟号 |
| `status` | int | 状态0禁用/ 1启用 |
| `real_name_status` | int | 实名状态0未实名/ 1已实名 |
| `carrier_name` | string | 运营商名称 |
| `generation` | string | 制式 |
| `wallet_balance` | int64 | 钱包余额(分) |
---
#### B2 可购套餐列表
```
GET /api/c/v1/asset/packages?identifier=xxx
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
**响应体:**
```json
{
"packages": [
{
"package_id": 1,
"package_name": "月套餐30G",
"package_type": "formal",
"retail_price": 2900,
"cost_price": 2000,
"validity_days": 30,
"is_addon": false,
"data_allowance": 30720,
"data_unit": "MB",
"description": "每月30G流量"
}
]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `package_id` | uint | 套餐 ID |
| `package_name` | string | 套餐名称 |
| `package_type` | string | 套餐类型:`formal`(正式套餐)/ `addon`(加油包) |
| `retail_price` | int64 | 零售价(分) |
| `cost_price` | int64 | 成本价(分) |
| `validity_days` | int | 有效天数 |
| `is_addon` | bool | 是否加油包 |
| `data_allowance` | int64 | 流量额度 |
| `data_unit` | string | 流量单位 |
| `description` | string | 套餐说明 |
---
#### B3 历史套餐列表
```
GET /api/c/v1/asset/package-history?identifier=xxx&page=1&page_size=20
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符 |
| `page` | int | 是 | 页码,最小 1 |
| `page_size` | int | 是 | 每页数量1~100 |
**响应体**:分页列表,包含 `list` / `total` / `page` / `page_size`
---
#### B4 手动刷新资产状态
```
POST /api/c/v1/asset/refresh
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `refresh_type` | string | 刷新类型:`card`(卡)/ `device`(设备) |
| `accepted` | bool | 是否已受理 |
| `cooldown_seconds` | int | 冷却秒数(期间不可重复刷新) |
---
### 钱包(/api/c/v1/wallet/
#### C1 钱包详情
```
GET /api/c/v1/wallet/detail?identifier=xxx
```
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `wallet_id` | uint | 钱包 ID |
| `resource_type` | string | 资源类型:`iot_card`(物联网卡)/ `device`(设备) |
| `resource_id` | uint | 资源 ID |
| `balance` | int64 | 可用余额(分) |
| `frozen_balance` | int64 | 冻结余额(分) |
| `updated_at` | string | 更新时间 |
---
#### C2 钱包流水列表
```
GET /api/c/v1/wallet/transactions?identifier=xxx&page=1&page_size=20
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符 |
| `transaction_type` | string | 否 | 流水类型筛选 |
| `start_time` | string | 否 | 开始时间 |
| `end_time` | string | 否 | 结束时间 |
| `page` | int | 是 | 页码 |
| `page_size` | int | 是 | 每页数量1~100 |
**响应体list 中每项):**
| 字段 | 类型 | 说明 |
|------|------|------|
| `transaction_id` | uint | 流水 ID |
| `type` | string | 流水类型 |
| `amount` | int64 | 变动金额(分) |
| `balance_after` | int64 | 变动后余额(分) |
| `created_at` | string | 创建时间 |
| `remark` | string | 备注 |
---
#### C3 充值预检
```
GET /api/c/v1/wallet/recharge-check?identifier=xxx
```
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `need_force_recharge` | bool | 是否需要强制充值 |
| `force_recharge_amount` | int64 | 强制充值金额(分) |
| `trigger_type` | string | 触发类型 |
| `min_amount` | int64 | 最小充值金额(分) |
| `max_amount` | int64 | 最大充值金额(分) |
| `message` | string | 提示信息 |
---
#### C4 创建充值订单
```
POST /api/c/v1/wallet/recharge
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
| `amount` | int64 | 是 | 充值金额100~10000000 |
| `payment_method` | string | 是 | 支付方式,目前仅支持 `wechat` |
| `app_type` | string | 是 | 应用类型:`official_account`(公众号)/ `miniapp`(小程序) |
**响应体:**
```json
{
"recharge": {
"recharge_id": 1,
"recharge_no": "RC20260319001",
"amount": 10000,
"status": 0
},
"pay_config": {
"app_id": "wx...",
"timestamp": "1710000000",
"nonce_str": "abc123",
"package": "prepay_id=wx...",
"sign_type": "RSA",
"pay_sign": "..."
}
}
```
`pay_config` 字段直接传给微信 JSAPI 调起支付。
---
#### C5 充值记录列表
```
GET /api/c/v1/wallet/recharges?identifier=xxx&page=1&page_size=20
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符 |
| `status` | int | 否 | 充值状态0待支付/ 1已支付/ 2已关闭 |
| `page` | int | 是 | 页码 |
| `page_size` | int | 是 | 每页数量1~100 |
**响应体list 中每项):**
| 字段 | 类型 | 说明 |
|------|------|------|
| `recharge_id` | uint | 充值 ID |
| `recharge_no` | string | 充值单号 |
| `amount` | int64 | 充值金额(分) |
| `status` | int | 状态0待支付/ 1已支付/ 2已关闭 |
| `payment_method` | string | 支付方式 |
| `created_at` | string | 创建时间 |
| `auto_purchase_status` | string | 自动购包状态 |
---
### 钱包充值流程
```mermaid
flowchart TD
A[用户进入充值页] --> B["C3 GET /wallet/recharge-check\n预检是否需要强充"]
B --> C{need_force_recharge}
C -->|true 需要强充| D[展示强制充值金额\n用户确认]
C -->|false 自由充值| E[用户输入充值金额]
D --> F["C4 POST /wallet/recharge\n创建充值订单"]
E --> F
F --> G[调起微信支付\n使用 pay_config]
G --> H[支付成功]
H --> I[后端回调处理\n余额增加]
I --> J[充值完成]
```
---
### 订单(/api/c/v1/orders/
#### D1 创建套餐购买订单(核心接口)
```
POST /api/c/v1/orders/create
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
| `package_ids` | []uint | 是 | 套餐 ID 列表,至少 1 个 |
| `app_type` | string | 是 | 应用类型:`official_account`(公众号)/ `miniapp`(小程序) |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `order_type` | string | 订单类型:`package`(套餐订单)/ `recharge`(充值订单) |
| `order` | object | 套餐订单信息(`order_type=package` 时有值) |
| `recharge` | object | 充值订单信息(`order_type=recharge` 时有值) |
| `pay_config` | object | 微信支付配置,直接传给 JSAPI |
| `linked_package_info` | object | 关联套餐信息(强充场景下有值) |
`order` 字段结构:
| 字段 | 类型 | 说明 |
|------|------|------|
| `order_id` | uint | 订单 ID |
| `order_no` | string | 订单号 |
| `total_amount` | int64 | 订单总金额(分) |
| `payment_status` | int | 支付状态0待支付/ 1已支付/ 2已取消 |
| `created_at` | string | 创建时间 |
`recharge` 字段结构:
| 字段 | 类型 | 说明 |
|------|------|------|
| `recharge_id` | uint | 充值 ID |
| `recharge_no` | string | 充值单号 |
| `amount` | int64 | 充值金额(分) |
| `status` | int | 状态0待支付/ 1已支付/ 2已关闭 |
| `auto_purchase_status` | string | 自动购包状态 |
`linked_package_info` 字段结构(强充场景):
| 字段 | 类型 | 说明 |
|------|------|------|
| `package_names` | []string | 套餐名称列表 |
| `total_package_amount` | int64 | 套餐总金额(分) |
| `force_recharge_amount` | int64 | 强制充值金额(分) |
| `wallet_credit` | int64 | 钱包抵扣金额(分) |
> **注意**:当余额不足时,后端会自动创建充值订单(`order_type=recharge`),用户支付充值后系统自动购买套餐,前端无需二次调用购买接口。
---
#### D2 订单列表
```
GET /api/c/v1/orders?identifier=xxx&page=1&page_size=20
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符 |
| `payment_status` | int | 否 | 支付状态0待支付/ 1已支付/ 2已取消 |
| `page` | int | 是 | 页码 |
| `page_size` | int | 是 | 每页数量1~100 |
**响应体list 中每项):**
| 字段 | 类型 | 说明 |
|------|------|------|
| `order_id` | uint | 订单 ID |
| `order_no` | string | 订单号 |
| `total_amount` | int64 | 订单总金额(分) |
| `payment_status` | int | 支付状态 |
| `created_at` | string | 创建时间 |
| `package_names` | []string | 套餐名称列表 |
---
#### D3 订单详情
```
GET /api/c/v1/orders/:id
```
**路径参数**`id`(订单 ID
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `order_id` | uint | 订单 ID |
| `order_no` | string | 订单号 |
| `total_amount` | int64 | 订单总金额(分) |
| `payment_status` | int | 支付状态0待支付/ 1已支付/ 2已取消 |
| `payment_method` | string | 支付方式 |
| `created_at` | string | 创建时间 |
| `paid_at` | string | 支付时间(可为空) |
| `completed_at` | string | 完成时间(可为空) |
| `packages` | array | 订单套餐列表 |
`packages` 中每项:
| 字段 | 类型 | 说明 |
|------|------|------|
| `package_id` | uint | 套餐 ID |
| `package_name` | string | 套餐名称 |
| `package_type` | string | 套餐类型:`formal`(正式套餐)/ `addon`(加油包) |
| `price` | int64 | 单价(分) |
| `quantity` | int | 数量 |
---
### 套餐购买流程
```mermaid
flowchart TD
A[用户选择套餐] --> B["D1 POST /orders/create\n传入 identifier + package_ids + app_type"]
B --> C{响应 order_type}
C -->|package 套餐订单| D[调起微信支付\n使用 pay_config]
C -->|recharge 充值订单| E[提示用户需要充值\n显示 linked_package_info 中的金额说明]
E --> F[调起微信支付\n使用 pay_config]
D --> G[支付成功]
F --> G
G --> H[后端支付回调处理]
H -->|套餐订单| I[直接激活套餐]
H -->|充值订单| J[余额到账后自动购买套餐]
I --> K[套餐激活完成]
J --> K
```
> **强充两阶段说明**:当用户钱包余额不足时,后端自动创建充值订单(`order_type=recharge`)。用户支付充值金额后,系统自动扣款购买套餐,前端无需再次调用购买接口。`linked_package_info` 字段包含套餐名称和金额明细,可用于向用户展示说明。
---
### 实名(/api/c/v1/realname/
#### E1 获取实名跳转链接
```
GET /api/c/v1/realname/link?identifier=xxx&iccid=xxx
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
| `iccid` | string | 否 | 物联网卡 ICCID设备场景下指定具体卡 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `realname_mode` | string | 实名模式:`none`(无需实名)/ `template`(模板实名)/ `gateway`(网关实名) |
| `realname_url` | string | 实名跳转链接 |
| `card_info` | object | 卡片简要信息 |
| `expire_at` | string | 链接过期时间(可为空) |
`card_info` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `iccid` | string | 物联网卡 ICCID |
| `msisdn` | string | 手机号 |
| `virtual_no` | string | 虚拟号 |
---
### 设备(/api/c/v1/device/
#### F1 设备卡列表
```
GET /api/c/v1/device/cards?identifier=xxx
```
**响应体:**
```json
{
"cards": [
{
"card_id": 1,
"iccid": "898600...",
"msisdn": "1380000...",
"carrier_name": "中国移动",
"network_status": "online",
"real_name_status": 1,
"slot_position": 1,
"is_active": true
}
]
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `card_id` | uint | 卡 ID |
| `iccid` | string | 物联网卡 ICCID |
| `msisdn` | string | 手机号 |
| `carrier_name` | string | 运营商名称 |
| `network_status` | string | 网络状态 |
| `real_name_status` | int | 实名状态0未实名/ 1已实名 |
| `slot_position` | int | 插槽位置 |
| `is_active` | bool | 是否当前激活卡 |
---
#### F2 设备重启
```
POST /api/c/v1/device/reboot
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `accepted` | bool | 是否已受理 |
| `request_id` | string | 请求 ID |
---
#### F3 恢复出厂设置
```
POST /api/c/v1/device/factory-reset
```
**请求体**:同 F2`identifier` 字段)
**响应体**:同 F2`accepted` / `request_id`
---
#### F4 WiFi 配置
```
POST /api/c/v1/device/wifi
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
| `ssid` | string | 是 | WiFi 名称1~32 字符 |
| `password` | string | 是 | WiFi 密码1~64 字符 |
| `enabled` | bool | 是 | 是否启用 WiFi |
**响应体**:同 F2`accepted` / `request_id`
---
#### F5 切卡
```
POST /api/c/v1/device/switch-card
```
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~50 字符 |
| `target_iccid` | string | 是 | 目标 ICCID1~30 字符 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `accepted` | bool | 是否已受理 |
| `target_iccid` | string | 目标 ICCID |
---
### 换货(/api/c/v1/exchange/
#### G1 查询进行中的换货通知
```
GET /api/c/v1/exchange/pending?identifier=xxx
```
**Query 参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `identifier` | string | 是 | 资产标识符1~100 字符 |
**响应体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | uint | 换货单 ID |
| `exchange_no` | string | 换货单号 |
| `status` | int | 换货状态1~5 |
| `status_text` | string | 换货状态文本 |
| `exchange_reason` | string | 换货原因 |
| `created_at` | string | 创建时间 |
> 若无进行中的换货单,返回空数据(非报错)。
---
#### G2 填写收货信息
```
POST /api/c/v1/exchange/:id/shipping-info
```
**路径参数**`id`(换货单 ID
**请求体:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `recipient_name` | string | 是 | 收件人姓名1~50 字符 |
| `recipient_phone` | string | 是 | 收件人电话1~20 字符 |
| `recipient_address` | string | 是 | 收货地址1~500 字符 |
---
## 六、认证方式说明
| 端 | 认证方式 | Header 格式 |
|----|----------|-------------|
| 后台(`/api/admin/` | Bearer TokenRedis 存储) | `Authorization: Bearer <token>` |
| C 端(`/api/c/v1/` | JWT | `Authorization: Bearer <jwt>` |
**C 端 JWT 获取方式**:通过 A2 公众号登录或 A3 小程序登录接口获取,有效期请参考接口返回。
---
## 七、统一响应格式
所有接口均返回以下格式:
```json
{
"code": 0,
"msg": "success",
"data": { ... },
"timestamp": 1710000000
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | int | 业务状态码0 表示成功 |
| `msg` | string | 提示信息 |
| `data` | object | 业务数据 |
| `timestamp` | int64 | 服务器时间戳(秒) |
**常见错误码:**
| 错误码 | 说明 |
|--------|------|
| 1001 | 缺失认证令牌 |
| 1002 | 无效或过期令牌 |
| 1003 | 权限不足 |
| 1200 | 换货单不存在 |
| 1201 | 换货单状态不允许此操作 |
| 1202 | 旧资产不存在或已停用 |
| 1203 | 新资产标识符无效 |
| 1204 | 换货单已取消,无法操作 |
| 1205 | 数据迁移失败 |
| 1206 | 收货信息填写超时 |
| 4000 | 参数错误 |
| 5000 | 服务器内部错误 |