# 前端接口变更说明

> 最后更新：2026-03-20

---

## 目录

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` 的套餐接口 | 响应新增 `virtual_ratio`、`one_time_commission_amount`、`tier_info`、`calendar_type`、`data_reset_cycle`、`enable_realname_activation` 等字段 |
| `GET /api/admin/iot-cards/standalone` | 响应新增 `virtual_no`（虚拟号）字段 |
| `GET /api/admin/devices` | `device_no` 重命名为 `virtual_no`，新增 `bound_card_count`、`first_commission_paid`、`accumulated_recharge` 字段 |
| 运营商创建/编辑接口 | 新增 `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） |
| 微信支付配置管理（8 个接口） | `GET/POST/PUT/DELETE /api/admin/wechat-configs` 及 `active`、`activate`、`deactivate` 子路由 |
| 代理预充值（4 个接口） | `/api/admin/agent-recharges` 创建/列表/详情/线下确认 |
| 资产管理（11 个接口） | `/api/admin/assets/*` 资产解析、状态刷新、套餐、停复机、钱包概况/流水 |

### 新增（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`。

---

### 套餐响应新增字段（`PackageResponse`）

所有返回 `PackageResponse` 的套餐接口，响应体新增字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `retail_price` | int64 | 零售价（分），代理商可见 |
| `profit_margin` | int64 | 利润空间（分），仅代理用户可见 |
| `current_commission_rate` | string | 当前返佣比例，仅代理用户可见 |
| `one_time_commission_amount` | int64 | 一次性佣金金额（分），代理视角 |
| `tier_info` | object | 梯度返佣信息（含 `current_rate` / `next_threshold` / `next_rate`），仅代理可见 |
| `virtual_ratio` | float64 | 虚流量比例（`real_data_mb/virtual_data_mb`） |
| `calendar_type` | string | 套餐周期类型：`natural_month` / `by_day` |
| `duration_days` | int | 套餐天数（`calendar_type=by_day` 时有值） |
| `data_reset_cycle` | string | 流量重置周期：`daily` / `monthly` / `yearly` / `none` |
| `enable_realname_activation` | bool | 是否启用实名激活 |

---

### IoT 卡列表响应新增 `virtual_no` 字段

接口：

```
GET /api/admin/iot-cards/standalone
```

响应体新增字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `virtual_no` | string | 卡虚拟号（用于客服查找资产） |

---

### 设备列表字段调整

接口：

```
GET /api/admin/devices
```

字段变更如下：

| 变更类型 | 字段 | 类型 | 说明 |
|----------|------|------|------|
| 重命名 | `device_no` → `virtual_no` | string | 设备虚拟号/别名 |
| 新增 | `bound_card_count` | int | 绑定卡数量 |
| 新增 | `first_commission_paid` | bool | 一次性佣金是否已发放 |
| 新增 | `accumulated_recharge` | 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/SN），1~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/SN），1~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 | 已取消 | 无 |

---

### 微信支付配置管理

> 权限：仅超级管理员和平台用户可访问。

#### W1 获取当前生效的支付配置

```
GET /api/admin/wechat-configs/active
```

- **认证**：需要后台 Bearer Token
- **请求参数**：无

**响应体（`WechatConfigResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | uint | 配置ID |
| `name` | string | 配置名称 |
| `description` | string | 配置描述 |
| `provider_type` | string | 支付渠道类型 (`wechat`:微信直连, `fuiou`:富友) |
| `is_active` | bool | 是否激活 |
| `oa_app_id` | string | 公众号AppID |
| `oa_app_secret` | string | 公众号AppSecret（已脱敏） |
| `oa_token` | string | 公众号Token（已脱敏） |
| `oa_aes_key` | string | 公众号AES加密Key（已脱敏） |
| `oa_oauth_redirect_url` | string | OAuth回调地址 |
| `miniapp_app_id` | string | 小程序AppID |
| `miniapp_app_secret` | string | 小程序AppSecret（已脱敏） |
| `wx_mch_id` | string | 微信商户号 |
| `wx_api_v3_key` | string | 微信APIv3密钥（已脱敏） |
| `wx_api_v2_key` | string | 微信APIv2密钥（已脱敏） |
| `wx_cert_content` | string | 微信支付证书内容（配置状态） |
| `wx_key_content` | string | 微信支付密钥内容（配置状态） |
| `wx_serial_no` | string | 微信证书序列号 |
| `wx_notify_url` | string | 微信支付回调地址 |
| `fy_ins_cd` | string | 富友机构号 |
| `fy_mchnt_cd` | string | 富友商户号 |
| `fy_term_id` | string | 富友终端号 |
| `fy_private_key` | string | 富友私钥（配置状态） |
| `fy_public_key` | string | 富友公钥（配置状态） |
| `fy_api_url` | string | 富友API地址 |
| `fy_notify_url` | string | 富友支付回调地址 |
| `created_at` | string | 创建时间 |
| `updated_at` | string | 更新时间 |

**敏感字段说明**：
- 短密钥字段（如 `oa_app_secret`、`wx_api_v3_key`）返回 `xxxx***xxxx` 脱敏形式。
- 长文本密钥/证书字段（如 `wx_cert_content`、`fy_private_key`）返回 `[已配置]` 或 `[未配置]`。

---

#### W2 支付配置列表

```
GET /api/admin/wechat-configs
```

- **认证**：需要后台 Bearer Token

**Query 参数：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page` | int | 否 | 页码，最小 1 |
| `page_size` | int | 否 | 每页数量，1~100 |
| `provider_type` | string | 否 | 支付渠道类型 (`wechat`/`fuiou`) |
| `is_active` | bool | 否 | 是否激活 (`true`:已激活, `false`:未激活) |

**响应体（`WechatConfigListResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `list` | array | 配置列表（每项字段同 `WechatConfigResponse`） |
| `total` | int64 | 总数 |
| `page` | int | 当前页 |
| `page_size` | int | 每页数量 |

---

#### W3 创建支付配置

```
POST /api/admin/wechat-configs
```

- **认证**：需要后台 Bearer Token

**请求体（`CreateWechatConfigRequest`）：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 配置名称 |
| `description` | string | 否 | 配置描述 |
| `provider_type` | string | 是 | 支付渠道类型 (`wechat`:微信直连, `fuiou`:富友) |
| `oa_app_id` | string | 否 | 公众号AppID |
| `oa_app_secret` | string | 否 | 公众号AppSecret |
| `oa_token` | string | 否 | 公众号Token |
| `oa_aes_key` | string | 否 | 公众号AES加密Key |
| `oa_oauth_redirect_url` | string | 否 | OAuth回调地址 |
| `miniapp_app_id` | string | 否 | 小程序AppID |
| `miniapp_app_secret` | string | 否 | 小程序AppSecret |
| `wx_mch_id` | string | 否 | 微信商户号 |
| `wx_api_v3_key` | string | 否 | 微信APIv3密钥 |
| `wx_api_v2_key` | string | 否 | 微信APIv2密钥 |
| `wx_cert_content` | string | 否 | 微信支付证书内容(PEM格式) |
| `wx_key_content` | string | 否 | 微信支付密钥内容(PEM格式) |
| `wx_serial_no` | string | 否 | 微信证书序列号 |
| `wx_notify_url` | string | 否 | 微信支付回调地址 |
| `fy_ins_cd` | string | 否 | 富友机构号 |
| `fy_mchnt_cd` | string | 否 | 富友商户号 |
| `fy_term_id` | string | 否 | 富友终端号 |
| `fy_private_key` | string | 否 | 富友私钥(PEM格式) |
| `fy_public_key` | string | 否 | 富友公钥(PEM格式) |
| `fy_api_url` | string | 否 | 富友API地址 |
| `fy_notify_url` | string | 否 | 富友支付回调地址 |

**响应体**：`WechatConfigResponse`（字段同 W1，敏感字段脱敏返回）

---

#### W4 支付配置详情

```
GET /api/admin/wechat-configs/:id
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`id`（配置ID）
- **响应体**：`WechatConfigResponse`（字段同 W1，敏感字段脱敏返回）

---

#### W5 更新支付配置

```
PUT /api/admin/wechat-configs/:id
```

- **认证**：需要后台 Bearer Token

**路径参数：** `id`（配置ID）

**请求体（`UpdateWechatConfigRequest`，全字段可选）：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 否 | 配置名称 |
| `description` | string | 否 | 配置描述 |
| `provider_type` | string | 否 | 支付渠道类型 (`wechat`:微信直连, `fuiou`:富友) |
| `oa_app_id` | string | 否 | 公众号AppID |
| `oa_app_secret` | string | 否 | 公众号AppSecret |
| `oa_token` | string | 否 | 公众号Token |
| `oa_aes_key` | string | 否 | 公众号AES加密Key |
| `oa_oauth_redirect_url` | string | 否 | OAuth回调地址 |
| `miniapp_app_id` | string | 否 | 小程序AppID |
| `miniapp_app_secret` | string | 否 | 小程序AppSecret |
| `wx_mch_id` | string | 否 | 微信商户号 |
| `wx_api_v3_key` | string | 否 | 微信APIv3密钥 |
| `wx_api_v2_key` | string | 否 | 微信APIv2密钥 |
| `wx_cert_content` | string | 否 | 微信支付证书内容(PEM格式) |
| `wx_key_content` | string | 否 | 微信支付密钥内容(PEM格式) |
| `wx_serial_no` | string | 否 | 微信证书序列号 |
| `wx_notify_url` | string | 否 | 微信支付回调地址 |
| `fy_ins_cd` | string | 否 | 富友机构号 |
| `fy_mchnt_cd` | string | 否 | 富友商户号 |
| `fy_term_id` | string | 否 | 富友终端号 |
| `fy_private_key` | string | 否 | 富友私钥(PEM格式) |
| `fy_public_key` | string | 否 | 富友公钥(PEM格式) |
| `fy_api_url` | string | 否 | 富友API地址 |
| `fy_notify_url` | string | 否 | 富友支付回调地址 |

**响应体**：`WechatConfigResponse`（字段同 W1，敏感字段脱敏返回）

---

#### W6 删除支付配置

```
DELETE /api/admin/wechat-configs/:id
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`id`（配置ID）
- **请求体**：无
- **响应体**：无（仅返回统一成功响应）

---

#### W7 激活支付配置

```
POST /api/admin/wechat-configs/:id/activate
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`id`（配置ID）
- **请求体**：无
- **响应体**：`WechatConfigResponse`（字段同 W1，敏感字段脱敏返回）

---

#### W8 停用支付配置

```
POST /api/admin/wechat-configs/:id/deactivate
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`id`（配置ID）
- **请求体**：无
- **响应体**：`WechatConfigResponse`（字段同 W1，敏感字段脱敏返回）

---

### 代理预充值

> 权限：企业账号无权访问。

#### R1 创建代理充值订单

```
POST /api/admin/agent-recharges
```

- **认证**：需要后台 Bearer Token

**请求体（`CreateAgentRechargeRequest`）：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `shop_id` | uint | 是 | 目标店铺ID，代理只能填自己店铺 |
| `amount` | int64 | 是 | 充值金额（分），范围100元~100万元 |
| `payment_method` | string | 是 | 支付方式 (`wechat`:微信在线支付, `offline`:线下转账仅平台可用) |

**响应体（`AgentRechargeResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | uint | 充值记录ID |
| `recharge_no` | string | 充值单号(ARCH前缀) |
| `shop_id` | uint | 店铺ID |
| `shop_name` | string | 店铺名称 |
| `agent_wallet_id` | uint | 代理钱包ID |
| `amount` | int64 | 充值金额（分） |
| `payment_method` | string | 支付方式 (`wechat`:微信在线支付, `offline`:线下转账) |
| `payment_channel` | string | 实际支付通道 (`wechat_direct`:微信直连, `fuyou`:富友, `offline`:线下转账) |
| `payment_config_id` | uint | 关联支付配置ID，线下充值为 `null` |
| `payment_transaction_id` | string | 第三方支付流水号 |
| `status` | int | 状态 (1:待支付, 2:已完成, 3:已取消) |
| `paid_at` | string | 支付时间 |
| `completed_at` | string | 完成时间 |
| `created_at` | string | 创建时间 |
| `updated_at` | string | 更新时间 |

---

#### R2 代理充值订单列表

```
GET /api/admin/agent-recharges
```

- **认证**：需要后台 Bearer Token

**Query 参数（`AgentRechargeListRequest`）：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page` | int | 否 | 页码，默认1 |
| `page_size` | int | 否 | 每页条数，默认20，最大100 |
| `shop_id` | uint | 否 | 按店铺ID过滤 |
| `status` | int | 否 | 按状态过滤 (1:待支付, 2:已完成, 3:已取消) |
| `start_date` | string | 否 | 创建时间起始日期(YYYY-MM-DD) |
| `end_date` | string | 否 | 创建时间截止日期(YYYY-MM-DD) |

**响应体（`AgentRechargeListResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `total` | int64 | 总记录数 |
| `page` | int | 当前页码 |
| `page_size` | int | 每页条数 |
| `list` | array | 充值记录列表（每项字段同 `AgentRechargeResponse`） |

---

#### R3 代理充值订单详情

```
GET /api/admin/agent-recharges/:id
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`id`（充值记录ID）
- **响应体**：`AgentRechargeResponse`（字段同 R1）

---

#### R4 确认线下充值

```
POST /api/admin/agent-recharges/:id/offline-pay
```

- **认证**：需要后台 Bearer Token

**路径参数：** `id`（充值记录ID）

**请求体（`AgentOfflinePayRequest`）：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `operation_password` | string | 是 | 操作密码 |

**响应体**：`AgentRechargeResponse`（字段同 R1）

---

### 资产管理

#### Z1 通过标识符解析资产

```
GET /api/admin/assets/resolve/:identifier
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`identifier`（资产标识符，支持虚拟号/ICCID/IMEI/SN/MSISDN）

**响应体（`AssetResolveResponse`）：**

**基础字段**

| 字段 | 类型 | 说明 |
|------|------|------|
| `asset_type` | string | 资产类型：`card` 或 `device` |
| `asset_id` | uint | 资产数据库ID |
| `virtual_no` | string | 虚拟号 |
| `status` | int | 资产状态 |
| `batch_no` | string | 批次号 |
| `shop_id` | uint | 所属店铺ID |
| `shop_name` | string | 所属店铺名称 |
| `series_id` | uint | 套餐系列ID |
| `series_name` | string | 套餐系列名称 |
| `first_commission_paid` | bool | 一次性佣金是否已发放 |
| `accumulated_recharge` | int64 | 累计充值金额(分) |
| `activated_at` | string | 激活时间 |
| `created_at` | string | 创建时间 |
| `updated_at` | string | 更新时间 |

**状态聚合字段**

| 字段 | 类型 | 说明 |
|------|------|------|
| `real_name_status` | int | 实名状态：0未实名 1实名中 2已实名 |
| `current_package` | string | 当前套餐名称（无套餐时为空） |
| `package_total_mb` | int64 | 当前套餐总虚流量(MB)，已按 `virtual_ratio` 换算 |
| `package_used_mb` | float64 | 当前已用虚流量(MB)，已按 `virtual_ratio` 换算 |
| `package_remain_mb` | float64 | 当前套餐剩余虚流量(MB)，已按 `virtual_ratio` 换算 |
| `device_protect_status` | string | 设备保护期状态：`none`/`stop`/`start`（仅 `asset_type=device`） |

**绑定关系字段**

| 字段 | 类型 | 说明 |
|------|------|------|
| `iccid` | string | 卡ICCID（`asset_type=card`） |
| `bound_device_id` | uint | 绑定设备ID（`asset_type=card`） |
| `bound_device_no` | string | 绑定设备虚拟号（`asset_type=card`） |
| `bound_device_name` | string | 绑定设备名称（`asset_type=card`） |
| `bound_card_count` | int | 绑定卡数量（`asset_type=device`） |
| `cards` | array | 绑定卡列表（`asset_type=device`，每项字段见下） |

**`cards` 子项（`BoundCardInfo`）**

| 字段 | 类型 | 说明 |
|------|------|------|
| `card_id` | uint | 卡ID |
| `iccid` | string | ICCID |
| `msisdn` | string | 手机号 |
| `network_status` | int | 网络状态：0停机 1开机 |
| `real_name_status` | int | 实名状态 |
| `slot_position` | int | 插槽位置 |

**设备专属字段**

| 字段 | 类型 | 说明 |
|------|------|------|
| `device_name` | string | 设备名称 |
| `imei` | string | 设备IMEI |
| `sn` | string | 设备序列号 |
| `device_model` | string | 设备型号 |
| `device_type` | string | 设备类型 |
| `max_sim_slots` | int | 最大插槽数 |
| `manufacturer` | string | 制造商 |

**卡专属字段**

| 字段 | 类型 | 说明 |
|------|------|------|
| `carrier_id` | uint | 运营商ID |
| `carrier_type` | string | 运营商类型 |
| `carrier_name` | string | 运营商名称 |
| `msisdn` | string | 手机号 |
| `imsi` | string | IMSI |
| `card_category` | string | 卡业务类型 |
| `supplier` | string | 供应商 |
| `activation_status` | int | 激活状态 |
| `enable_polling` | bool | 是否参与轮询 |
| `network_status` | int | 网络状态：0停机 1开机（仅 `asset_type=card`） |

---

#### Z2 资产实时状态

```
GET /api/admin/assets/:asset_type/:id/realtime-status
```

- **认证**：需要后台 Bearer Token

**路径参数：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `asset_type` | string | 是 | 资产类型：`card` 或 `device` |
| `id` | uint | 是 | 资产ID |

**响应体（`AssetRealtimeStatusResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `asset_type` | string | 资产类型：`card` 或 `device` |
| `asset_id` | uint | 资产ID |
| `network_status` | int | 网络状态（`asset_type=card`）：0停机 1开机 |
| `real_name_status` | int | 实名状态（`asset_type=card`） |
| `current_month_usage_mb` | float64 | 本月已用流量MB（`asset_type=card`） |
| `last_sync_time` | string | 最后同步时间（`asset_type=card`） |
| `device_protect_status` | string | 保护期状态（`asset_type=device`）：`none`/`stop`/`start` |
| `cards` | array | 绑定卡状态列表（`asset_type=device`，每项字段同 `BoundCardInfo`） |

---

#### Z3 刷新资产状态

```
POST /api/admin/assets/:asset_type/:id/refresh
```

- **认证**：需要后台 Bearer Token
- **路径参数**：同 Z2
- **请求体**：无
- **响应体**：`AssetRealtimeStatusResponse`（字段同 Z2）

---

#### Z4 资产套餐列表

```
GET /api/admin/assets/:asset_type/:id/packages
```

- **认证**：需要后台 Bearer Token
- **路径参数**：同 Z2

**响应体（数组，每项为 `AssetPackageResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `package_usage_id` | uint | 套餐使用记录ID |
| `package_id` | uint | 套餐ID |
| `package_name` | string | 套餐名称 |
| `package_type` | string | 套餐类型：`formal`/`addon` |
| `usage_type` | string | 使用类型：`single_card`/`device` |
| `status` | int | 状态：0待生效 1生效中 2已用完 3已过期 4已失效 |
| `status_name` | string | 状态名称 |
| `data_limit_mb` | int64 | 套餐真流量总量(MB) |
| `virtual_limit_mb` | int64 | 套餐虚流量总量(MB)，按 `virtual_ratio` 换算 |
| `data_usage_mb` | int64 | 已用真流量(MB) |
| `virtual_used_mb` | float64 | 已用虚流量(MB)，按 `virtual_ratio` 换算 |
| `virtual_remain_mb` | float64 | 剩余虚流量(MB)，按 `virtual_ratio` 换算 |
| `virtual_ratio` | float64 | 虚流量比例(real/virtual) |
| `activated_at` | string | 激活时间（待生效套餐为空） |
| `expires_at` | string | 到期时间（待生效套餐为空） |
| `master_usage_id` | uint | 主套餐ID（加油包时有值） |
| `priority` | int | 优先级 |
| `created_at` | string | 创建时间 |

---

#### Z5 当前生效套餐

```
GET /api/admin/assets/:asset_type/:id/current-package
```

- **认证**：需要后台 Bearer Token
- **路径参数**：同 Z2
- **响应体**：`AssetPackageResponse`（字段同 Z4）

---

#### Z6 设备停机

```
POST /api/admin/assets/device/:device_id/stop
```

- **认证**：需要后台 Bearer Token

**路径参数：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | uint | 是 | 设备ID |

**响应体（`DeviceSuspendResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `success_count` | int | 成功停机卡数 |
| `fail_count` | int | 失败卡数 |
| `skip_count` | int | 跳过卡数（未实名或已停机） |
| `failed_items` | array | 失败详情（`iccid` / `reason`） |

---

#### Z7 设备复机

```
POST /api/admin/assets/device/:device_id/start
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`device_id`（设备ID）
- **请求体**：无
- **响应体**：无（仅返回统一成功响应）

---

#### Z8 单卡停机

```
POST /api/admin/assets/card/:iccid/stop
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`iccid`（卡ICCID）
- **请求体**：无
- **响应体**：无（仅返回统一成功响应）

---

#### Z9 单卡复机

```
POST /api/admin/assets/card/:iccid/start
```

- **认证**：需要后台 Bearer Token
- **路径参数**：`iccid`（卡ICCID）
- **请求体**：无
- **响应体**：无（仅返回统一成功响应）

---

#### Z10 资产钱包概况

```
GET /api/admin/assets/:asset_type/:id/wallet
```

- **认证**：需要后台 Bearer Token
- **路径参数**：同 Z2

**响应体（`AssetWalletResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `wallet_id` | uint | 钱包数据库ID |
| `resource_type` | string | 资源类型：`iot_card` 或 `device` |
| `resource_id` | uint | 对应卡或设备的数据库ID |
| `balance` | int64 | 总余额（分） |
| `frozen_balance` | int64 | 冻结余额（分） |
| `available_balance` | int64 | 可用余额 = `balance - frozen_balance`（分） |
| `currency` | string | 币种，目前固定 CNY |
| `status` | int | 钱包状态：1-正常 2-冻结 3-关闭 |
| `status_text` | string | 状态文本 |
| `created_at` | string | 创建时间（RFC3339） |
| `updated_at` | string | 更新时间（RFC3339） |

---

#### Z11 资产钱包流水列表

```
GET /api/admin/assets/:asset_type/:id/wallet/transactions
```

- **认证**：需要后台 Bearer Token

**路径参数：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `asset_type` | string | 是 | 资产类型：`card` 或 `device` |
| `id` | uint | 是 | 资产ID |

**Query 参数：**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page` | int | 否 | 页码，默认1 |
| `page_size` | int | 否 | 每页数量，默认20，最大100 |
| `transaction_type` | string | 否 | 交易类型过滤：`recharge`/`deduct`/`refund` |
| `start_time` | string | 否 | 开始时间（RFC3339） |
| `end_time` | string | 否 | 结束时间（RFC3339） |

**响应体（`AssetWalletTransactionListResponse`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `list` | array | 流水列表（每项字段见下） |
| `total` | int64 | 总记录数 |
| `page` | int | 当前页码 |
| `page_size` | int | 每页数量 |
| `total_pages` | int | 总页数 |

**`list` 子项（`AssetWalletTransactionItem`）：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | uint | 流水记录ID |
| `transaction_type` | string | 交易类型：`recharge`/`deduct`/`refund` |
| `transaction_type_text` | string | 交易类型文本：充值/扣款/退款 |
| `amount` | int64 | 变动金额（分），充值为正数，扣款/退款为负数 |
| `balance_before` | int64 | 变动前余额（分） |
| `balance_after` | int64 | 变动后余额（分） |
| `reference_type` | string | 关联业务类型：`recharge` 或 `order`（可空） |
| `reference_no` | string | 关联业务编号：充值单号（CRCH…）或订单号（ORD…）（可空） |
| `remark` | string | 备注（可空） |
| `created_at` | string | 流水创建时间（RFC3339） |

---

## 五、新增的 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/MSISDN），1~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_token（5分钟有效）"]
    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 | 是 | 目标 ICCID，1~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 Token（Redis 存储） | `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 | 服务器内部错误 |
