# API 接口文档

本文档列出 jw-beauty 后端所有 API 接口，供前端开发和调试参考。

**Base URL**: `https://api.vsai.net.cn`

---

## 通用约定

### 认证方式

需要认证的接口在请求头中携带 JWT：

```
Authorization: Bearer <token>
```

- `requireUser`：需要用户 Token（通过 `/auth/login` 获取）
- `requireAdmin`：需要管理员 Token（通过 `/admin/login` 获取）

### 响应格式

所有接口返回统一 JSON 结构：

```json
{
  "code": 0,
  "message": "success",
  "data": {}
}
```

| code | 含义 |
|------|------|
| 0 | 成功 |
| 1001 | Token 无效或过期 |
| 1002 | 管理员未授权 |
| 1004 | 用户不存在 |
| 1005 | 设备/记录不存在 |
| 1006 | 设备未绑定 |
| 2001 | 参数错误或业务限制 |
| 3001 | 服务端错误 |

### 公共请求头

| Header | 说明 |
|--------|------|
| `Authorization` | Bearer Token |
| `Content-Type` | `application/json`（除文件上传外） |
| `X-Device-Id` | 设备标识（可选） |
| `X-App-Version` | 应用版本号（可选） |
| `X-Platform` | 平台标识（可选） |

### 分页参数

支持分页的列表接口统一使用：

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `page` | number | 1 | 页码（从 1 开始） |
| `page_size` | number | 20 | 每页条数（最大 100） |

### Rate Limiting

| 路径 | 限制 |
|------|------|
| `POST /api/v1/auth/login` | 15 分钟内最多 10 次 |
| `POST /api/v1/admin/login` | 15 分钟内最多 5 次 |
| `POST /api/v1/user/avatar` | 15 分钟内最多 20 次 |
| `POST /api/v1/user/phone` | 15 分钟内最多 20 次 |

---

## 健康检查

### `GET /health`

| 项目 | 说明 |
|------|------|
| Auth | 无 |
| 说明 | 健康检查，用于部署验证和监控 |

**响应示例**：

```json
{ "code": 0, "message": "success", "data": { "status": "ok" } }
```

---

## 1. 认证模块

### 1.1 `POST /api/v1/auth/login`

| 项目 | 说明 |
|------|------|
| Auth | 无 |
| 说明 | 微信小程序登录。维护模式下拒绝登录。新用户自动注册。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `code` | string | 是 | `wx.login()` 返回的临时登录凭证 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `token` | string | JWT Token |
| `user_id` | string | 用户 ID |
| `is_new_user` | boolean | 是否新注册用户 |
| `user_info` | object | 用户基本信息 |
| `user_info.nickname` | string | 昵称 |
| `user_info.avatar` | string | 头像 URL |
| `user_info.phone` | string | 手机号 |
| `user_info.gender` | number | 性别（0=未知） |
| `expires_in` | number | Token 有效期（秒），固定 604800（7天） |

**响应示例**：

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "token": "eyJhbGci...",
    "user_id": "1",
    "is_new_user": false,
    "user_info": {
      "user_id": "1",
      "nickname": "用户1",
      "avatar": "",
      "phone": "",
      "gender": 0
    },
    "expires_in": 604800
  }
}
```

---

### 1.2 `POST /api/v1/auth/refresh`

| 项目 | 说明 |
|------|------|
| Auth | Bearer Token（可过期不超过 1 天） |
| 说明 | 刷新用户 Token。Token 未过期且剩余有效期超过 7 天时原样返回。 |

**请求 Body**：无

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `token` | string | 新 Token（或原 Token） |
| `expires_in` | number | 有效期（秒） |

---

## 2. 用户模块

### 2.1 `GET /api/v1/user/profile`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 获取当前用户资料 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `user_id` | string | 用户 ID |
| `nickname` | string | 昵称 |
| `avatar` | string | 头像 URL |
| `phone` | string | 手机号 |
| `gender` | number | 性别 |
| `bind_time` | null | 保留字段 |
| `device_count` | number | 已绑定设备数量 |

---

### 2.2 `PUT /api/v1/user/profile`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 更新用户资料 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nickname` | string | 否 | 昵称 |
| `avatar` | string | 否 | 头像 URL（也接受 `avatar_url`） |
| `gender` | number | 否 | 性别 |

**响应 `data`**：

```json
{ "message": "success" }
```

---

### 2.3 `POST /api/v1/user/avatar`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| Content-Type | `multipart/form-data` |
| 说明 | 上传头像图片到 COS。限制 2MB，仅支持 jpg/png/gif/webp。 |

**请求**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | file | 是 | 图片文件 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `avatar` | string | COS CDN 头像 URL |

---

### 2.4 `POST /api/v1/user/phone`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 微信手机号授权绑定 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `code` | string | 是 | 微信 `getPhoneNumber` 返回的 code |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `phone` | string | 完整手机号（带区号） |
| `pure_phone_number` | string | 不带区号的手机号 |
| `country_code` | string | 国家区号 |

---

## 3. 设备模块

### 3.1 `POST /api/v1/device/bind`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 申请绑定设备。每个用户同时只能绑定一台设备。设备绑定功能可通过系统设置关闭。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 是 | 设备 ID（产品码） |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `device_id` | string | 设备 ID |
| `bind_token` | string | 绑定令牌（16 字符 hex），用于确认绑定 |
| `subscription` | object | 当前订阅状态 |
| `subscription.plan` | string | 订阅计划（none/trial/monthly/yearly） |
| `subscription.remaining_days` | number | 剩余天数 |

---

### 3.2 `POST /api/v1/device/bind/confirm`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 确认绑定设备（蓝牙握手成功后调用） |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 是 | 设备 ID |
| `bind_token` | string | 是 | 绑定令牌 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `message` | string | "success" |
| `subscription` | object | 当前订阅状态 |

---

### 3.3 `POST /api/v1/device/mock-bind`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 环境 | 仅非 production 环境可用 |
| 说明 | 模拟绑定设备，跳过蓝牙确认流程。用于开发调试。 |

**请求 Body**：

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

**响应 `data`**：

```json
{ "message": "success", "device_id": "DEV001" }
```

---

### 3.4 `POST /api/v1/device/unbind`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 解绑设备 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 否 | 指定设备 ID，不传则解绑当前设备 |

**响应 `data`**：

```json
{ "message": "success" }
```

---

### 3.5 `GET /api/v1/device/list`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 获取当前用户的设备列表 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `devices` | array | 设备列表 |
| `total` | number | 设备数量 |

---

### 3.6 `GET /api/v1/device/:device_id`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 获取已绑定设备详情。只能查看自己绑定的设备。 |

**路径参数**：

| 参数 | 说明 |
|------|------|
| `device_id` | 设备 ID |

**响应 `data`**：设备详情对象（含 device_id, device_name, firmware_version, battery, temperature 等）

---

### 3.7 `GET /api/v1/device/command/pending`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 拉取设备待执行的远程指令。拉取后指令标记为已推送。 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 是 | 设备 ID |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `commands` | array | 指令列表 |
| `commands[].seq` | number | 指令序号（command_id） |
| `commands[].opcode` | number | 操作码 |
| `commands[].payload` | object | 指令参数 |

---

### 3.8 `POST /api/v1/device/command/result`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 上报指令执行结果 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `command_id` 或 `seq` | number | 是 | 指令 ID |
| `success` | boolean | 否 | 是否成功，默认 true |

**响应 `data`**：

```json
{ "message": "success" }
```

---

### 3.9 `POST /api/v1/device/event`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 上报设备事件（错误、温度异常等） |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 是 | 设备 ID |
| `event_type` | string | 否 | 事件类型，默认 `device_error` |
| `error_code` | number | 否 | 错误码 |
| `temperature` | number | 否 | 温度 |

**响应 `data`**：

```json
{ "message": "ok" }
```

---

## 4. 订阅模块

### 4.1 `GET /api/v1/subscription/plans`

| 项目 | 说明 |
|------|------|
| Auth | 无 |
| 说明 | 获取可用订阅计划列表。价格和试用天数从系统设置读取。 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `plans` | array | 计划列表 |
| `plans[].key` | string | 计划标识（monthly/yearly/trial） |
| `plans[].name` | string | 名称（月卡/年卡/试用） |
| `plans[].price` | number | 价格（分） |
| `plans[].days` | number | 有效天数 |

**响应示例**：

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "plans": [
      { "key": "monthly", "name": "月卡", "price": 99, "days": 30 },
      { "key": "yearly", "name": "年卡", "price": 899, "days": 365 },
      { "key": "trial", "name": "试用", "price": 0, "days": 7 }
    ]
  }
}
```

---

### 4.2 `GET /api/v1/subscription`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 获取当前用户的订阅状态 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | string | `active` / `expired` / `inactive` |
| `plan` | string | 计划（none/trial/monthly/yearly） |
| `start_time` | string | 开始时间（无订阅时无此字段） |
| `expire_time` | string | 到期时间（无订阅时无此字段） |
| `remaining_days` | number | 剩余天数 |
| `trial_used` | boolean | 是否已使用过试用 |

---

### 4.3 `POST /api/v1/subscription/purchase`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 创建购买订单。当前返回订单号和占位支付参数，支付集成待完成。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `plan` 或 `plan_type` | string | 是 | 计划标识（trial/monthly/yearly） |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `order_id` | string | 订单号 |
| `payment_params` | object | 支付参数（当前为空对象） |
| `plan` | string | 计划标识 |
| `amount` | number | 金额 |

---

### 4.4 `POST /api/v1/subscription/mock-purchase`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 环境 | 仅非 production 环境可用 |
| 说明 | 模拟购买订阅，直接激活。不支持 trial 计划（trial 请用 `/subscription/trial`）。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `plan` 或 `plan_type` | string | 是 | `monthly` 或 `yearly` |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | string | `active` |
| `plan` | string | 计划标识 |
| `remaining_days` | number | 剩余天数 |

---

### 4.5 `POST /api/v1/subscription/trial`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 激活试用订阅。每个用户仅可使用一次。已有有效订阅时不可激活。 |

**请求 Body**：无

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | string | `active` |
| `plan` | string | `trial` |
| `remaining_days` | number | 试用天数（默认 7） |

---

### 4.6 `POST /api/v1/subscription/verify`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 管理员手动激活订阅（临时方案，支付集成前使用） |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `user_id` | number | 是 | 用户 ID |
| `plan` 或 `plan_type` | string | 否 | 计划，默认 `monthly` |
| `order_id` | string | 否 | 订单号，不传则自动生成 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | string | `active` |
| `plan` | string | 计划标识 |
| `remaining_days` | number | 剩余天数 |

---

## 5. 护理模块

### 5.1 `GET /api/v1/treatment/history`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 获取当前用户的护理记录列表，支持分页 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page` | number | 否 | 页码，默认 1 |
| `page_size` | number | 否 | 每页条数，默认 20 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `total` | number | 总记录数 |
| `page` | number | 当前页码 |
| `page_size` | number | 每页条数 |
| `records` | array | 护理记录列表 |

---

### 5.2 `POST /api/v1/treatment/sync`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 同步护理记录。设备必须已绑定。同时更新设备的电量和温度。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 是 | 设备 ID |
| `session_id` | string | 否 | 会话 ID，不传自动生成 |
| `start_time` | string | 否 | 开始时间 |
| `end_time` | string | 否 | 结束时间 |
| `regions` | string/array | 否 | 护理区域（数组会用逗号拼接） |
| `total_duration_ms` | number | 否 | 总时长（毫秒） |
| `mode` | number | 否 | 护理模式 |
| `avg_pd` | number | 否 | 平均光密度 |
| `battery` | number | 否 | 电量 |
| `temperature` | number | 否 | 温度 |
| `wavelength` | number | 否 | 波长 |
| `brightness` | number | 否 | 亮度 |
| `pd_values` | object | 否 | PD 传感器数据 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `record_id` | string | 会话 ID（session_id） |

---

### 5.3 `GET /api/v1/treatment/:record_id`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 获取单条护理记录详情。只能查看自己的记录。 |

**路径参数**：

| 参数 | 说明 |
|------|------|
| `record_id` | 会话 ID（session_id） |

**响应 `data`**：护理记录完整对象

---

## 6. 固件模块

### 6.1 `GET /api/v1/firmware/latest`

| 项目 | 说明 |
|------|------|
| Auth | requireUser |
| 说明 | 检查固件更新。返回最新固件信息和有时效的 COS 签名下载 URL。 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `current_version` | string | 否 | 当前固件版本号 |

**响应 `data`**（有更新）：

| 字段 | 类型 | 说明 |
|------|------|------|
| `has_update` | boolean | `true` |
| `version` | string | 最新版本号 |
| `size_bytes` | number | 文件大小（字节） |
| `sha256` | string | 文件 SHA256 校验值 |
| `download_url` | string | 签名下载 URL（有效期 600 秒） |

**响应 `data`**（无更新）：

```json
{ "has_update": false }
```

---

## 7. 管理后台模块

所有管理后台接口路径前缀为 `/api/v1/admin`，需要 `requireAdmin` 认证。

### 7.1 认证

#### `POST /api/v1/admin/login`

| 项目 | 说明 |
|------|------|
| Auth | 无 |
| 说明 | 管理员登录。支持 bcrypt 和旧版 salt 哈希两种密码格式（旧格式自动升级）。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `username` | string | 是 | 用户名 |
| `password` | string | 是 | 密码 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `token` | string | 管理员 JWT Token |
| `admin_id` | string | 管理员 ID |
| `username` | string | 用户名 |
| `real_name` | string | 真实姓名 |
| `role` | string | 角色 |

---

#### `POST /api/v1/admin/password`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 修改管理员密码。新密码最少 6 位。 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `old_password` | string | 是 | 原密码 |
| `new_password` | string | 是 | 新密码（>=6 位） |

**响应 `data`**：

```json
{ "message": "success" }
```

---

### 7.2 仪表盘

#### `GET /api/v1/admin/dashboard`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 获取后台首页统计数据 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `device_count` | number | 设备总数 |
| `user_count` | number | 用户总数 |
| `treatment_count` | number | 护理记录总数 |
| `subscription_count` | number | 订阅总数 |
| `sub_stats` | object | 订阅统计详情 |

---

### 7.3 设备管理

#### `GET /api/v1/admin/devices`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 设备列表，支持关键词搜索和分页 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `keyword` | string | 否 | 搜索关键词（设备 ID/名称） |
| `page` | number | 否 | 页码 |
| `page_size` | number | 否 | 每页条数 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 设备列表 |
| `total` | number | 总数 |

---

#### `POST /api/v1/admin/devices`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 预生成单个产品码（设备） |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 是 | 设备 ID |
| `product_id` | string | 否 | 产品型号，默认 `HOX_LIGHT_MASK` |
| `device_secret` | string | 否 | 设备密钥 |
| `device_name` | string | 否 | 设备名称，默认"光子美容仪" |
| `firmware_version` | string | 否 | 固件版本，默认 `1.0.0` |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `device_id` | string | 已创建的设备 ID |

---

#### `POST /api/v1/admin/devices/batch`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 批量预生成产品码，单次最多 500 个 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_ids` | string[] | 是 | 设备 ID 数组（1-500 个） |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `created` | number | 成功创建数量 |
| `failed` | number | 失败数量 |

---

#### `GET /api/v1/admin/devices/:device_id`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 获取设备详情，含绑定历史和最近护理记录 |

**响应 `data`**：设备信息 + `binding_history`（绑定历史数组）+ `recent_treatments`（最近护理数组）

---

#### `POST /api/v1/admin/devices/:device_id/unbind`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 后台强制解绑设备 |

**响应 `data`**：

```json
{ "message": "success" }
```

---

#### `POST /api/v1/admin/devices/:device_id/command`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 向设备推送远程指令 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `opcode` | number | 是 | 操作码 |
| 其他 | any | 否 | 指令参数（整个 body 存入 payload） |

**响应 `data`**：

```json
{ "message": "queued", "command": { ... } }
```

---

#### `GET /api/v1/admin/devices/:device_id/commands`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 获取设备的指令历史，支持分页 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 指令列表 |
| `total` | number | 总数 |

---

### 7.4 用户管理

#### `GET /api/v1/admin/users`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 用户列表，支持关键词搜索和分页 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `keyword` | string | 否 | 搜索关键词 |
| `page` | number | 否 | 页码 |
| `page_size` | number | 否 | 每页条数 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 用户列表 |
| `total` | number | 总数 |

---

#### `GET /api/v1/admin/users/:user_id`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 获取用户详情（含订阅、设备等关联信息） |

**响应 `data`**：用户详情对象

---

#### `POST /api/v1/admin/users/:user_id/unbind`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 后台解绑用户的设备 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device_id` | string | 否 | 指定设备 ID，不传则解绑该用户所有设备 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `message` | string | "success" |
| `affected_rows` | number | 受影响行数 |

---

#### `POST /api/v1/admin/users/:user_id/deactivate`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 注销用户。同时解绑该用户的所有设备。 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `message` | string | "success" |
| `unbound_rows` | number | 解绑的设备数 |

---

### 7.5 订阅管理

#### `GET /api/v1/admin/subscriptions`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 订阅列表，支持按状态筛选和分页 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `tab` | string | 否 | 筛选标签（如 active/expired） |
| `page` | number | 否 | 页码 |
| `page_size` | number | 否 | 每页条数 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 订阅列表 |
| `total` | number | 总数 |
| `stats` | object | 订阅统计 |

---

#### `POST /api/v1/admin/subscriptions`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 为指定用户创建订阅 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `user_id` | number | 是 | 用户 ID |
| `plan` | string | 否 | 计划，默认 `monthly` |
| `amount` | number | 否 | 金额，默认 0 |
| `order_id` | string | 否 | 订单号，自动生成 |
| `days` | number | 否 | 有效天数，默认 30 |

**响应 `data`**：

```json
{ "message": "success" }
```

---

#### `POST /api/v1/admin/subscriptions/cancel`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 取消指定订阅 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `subscription_id` | number | 是 | 订阅 ID |

**响应 `data`**：

```json
{ "message": "success" }
```

---

### 7.6 护理记录

#### `GET /api/v1/admin/records`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 护理记录列表，支持多条件筛选和分页 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `keyword` | string | 否 | 搜索关键词 |
| `user_id` | number | 否 | 按用户筛选 |
| `date_from` | string | 否 | 起始日期 |
| `date_to` | string | 否 | 截止日期 |
| `page` | number | 否 | 页码 |
| `page_size` | number | 否 | 每页条数 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 护理记录列表 |
| `total` | number | 总数 |

---

### 7.7 操作日志

#### `GET /api/v1/admin/logs`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 操作日志列表，支持按类型和设备筛选 |

**Query 参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `type` | string | 否 | 日志类型（action 字段） |
| `device_id` | string | 否 | 按设备筛选 |
| `page` | number | 否 | 页码 |
| `page_size` | number | 否 | 每页条数 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 日志列表 |
| `total` | number | 总数 |

---

### 7.8 系统设置

#### `GET /api/v1/admin/settings`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 获取所有系统设置 |

**响应 `data`**：KV 对象，包含以下设置项：

| Key | 类型 | 默认值 | 说明 |
|-----|------|--------|------|
| `system_name` | string | "光子美容仪后台" | 系统名称 |
| `admin_email` | string | - | 管理员邮箱 |
| `timezone` | string | - | 时区 |
| `monthly_price` | number | 99 | 月卡价格 |
| `yearly_price` | number | 899 | 年卡价格 |
| `trial_days` | number | 7 | 试用天数 |
| `enable_register` | boolean | true | 是否允许注册 |
| `enable_binding` | boolean | true | 是否允许设备绑定 |
| `enable_free_mode` | boolean | true | 是否启用自由模式 |
| `enable_smart_mode` | boolean | - | 是否启用智能模式 |
| `maintenance_mode` | boolean | false | 维护模式（开启后拒绝用户登录） |

---

#### `POST /api/v1/admin/settings`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 更新系统设置。仅接受白名单内的 key。更新后自动清除设置缓存。 |

**请求 Body**：KV 对象，key 为上表中允许的设置项名。

**请求示例**：

```json
{
  "monthly_price": 129,
  "trial_days": 14,
  "maintenance_mode": false
}
```

**响应 `data`**：

```json
{ "message": "success" }
```

---

### 7.9 固件管理

#### `GET /api/v1/admin/firmware`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 获取所有固件版本列表 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `records` | array | 固件列表 |
| `total` | number | 总数 |

---

#### `POST /api/v1/admin/firmware`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 登记新固件版本（固件文件需先上传到 COS） |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `version` | string | 是 | 版本号 |
| `cos_key` | string | 是 | COS 对象 Key |
| `device_type` | string | 否 | 设备类型 |
| `size_bytes` | number | 否 | 文件大小 |
| `sha256` | string | 否 | SHA256 校验值 |
| `status` | number | 否 | 状态（0=禁用, 1=启用），默认 1 |

**响应 `data`**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `firmware_id` | number | 固件记录 ID |

---

#### `POST /api/v1/admin/firmware/:firmware_id/status`

| 项目 | 说明 |
|------|------|
| Auth | requireAdmin |
| 说明 | 更新固件启用/禁用状态 |

**请求 Body**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `status` | number | 是 | 0=禁用, 1=启用 |

**响应 `data`**：

```json
{ "message": "success" }
```

---

## 接口速查索引

| # | Method | Path | Auth | 说明 |
|---|--------|------|------|------|
| - | GET | `/health` | 无 | 健康检查 |
| 1.1 | POST | `/api/v1/auth/login` | 无 | 微信登录 |
| 1.2 | POST | `/api/v1/auth/refresh` | Bearer | Token 刷新 |
| 2.1 | GET | `/api/v1/user/profile` | User | 获取资料 |
| 2.2 | PUT | `/api/v1/user/profile` | User | 更新资料 |
| 2.3 | POST | `/api/v1/user/avatar` | User | 上传头像 |
| 2.4 | POST | `/api/v1/user/phone` | User | 绑定手机号 |
| 3.1 | POST | `/api/v1/device/bind` | User | 申请绑定设备 |
| 3.2 | POST | `/api/v1/device/bind/confirm` | User | 确认绑定 |
| 3.3 | POST | `/api/v1/device/mock-bind` | User | 模拟绑定（非生产） |
| 3.4 | POST | `/api/v1/device/unbind` | User | 解绑设备 |
| 3.5 | GET | `/api/v1/device/list` | User | 设备列表 |
| 3.6 | GET | `/api/v1/device/:device_id` | User | 设备详情 |
| 3.7 | GET | `/api/v1/device/command/pending` | User | 拉取待执行指令 |
| 3.8 | POST | `/api/v1/device/command/result` | User | 上报指令结果 |
| 3.9 | POST | `/api/v1/device/event` | User | 上报设备事件 |
| 4.1 | GET | `/api/v1/subscription/plans` | 无 | 订阅计划列表 |
| 4.2 | GET | `/api/v1/subscription` | User | 订阅状态 |
| 4.3 | POST | `/api/v1/subscription/purchase` | User | 创建购买订单 |
| 4.4 | POST | `/api/v1/subscription/mock-purchase` | User | 模拟购买（非生产） |
| 4.5 | POST | `/api/v1/subscription/trial` | User | 激活试用 |
| 4.6 | POST | `/api/v1/subscription/verify` | Admin | 手动激活订阅 |
| 5.1 | GET | `/api/v1/treatment/history` | User | 护理记录列表 |
| 5.2 | POST | `/api/v1/treatment/sync` | User | 同步护理记录 |
| 5.3 | GET | `/api/v1/treatment/:record_id` | User | 护理记录详情 |
| 6.1 | GET | `/api/v1/firmware/latest` | User | 检查固件更新 |
| 7.1 | POST | `/api/v1/admin/login` | 无 | 管理员登录 |
| 7.1 | POST | `/api/v1/admin/password` | Admin | 修改密码 |
| 7.2 | GET | `/api/v1/admin/dashboard` | Admin | 仪表盘统计 |
| 7.3 | GET | `/api/v1/admin/devices` | Admin | 设备列表 |
| 7.3 | POST | `/api/v1/admin/devices` | Admin | 创建设备 |
| 7.3 | POST | `/api/v1/admin/devices/batch` | Admin | 批量创建设备 |
| 7.3 | GET | `/api/v1/admin/devices/:id` | Admin | 设备详情 |
| 7.3 | POST | `/api/v1/admin/devices/:id/unbind` | Admin | 强制解绑 |
| 7.3 | POST | `/api/v1/admin/devices/:id/command` | Admin | 推送指令 |
| 7.3 | GET | `/api/v1/admin/devices/:id/commands` | Admin | 指令历史 |
| 7.4 | GET | `/api/v1/admin/users` | Admin | 用户列表 |
| 7.4 | GET | `/api/v1/admin/users/:id` | Admin | 用户详情 |
| 7.4 | POST | `/api/v1/admin/users/:id/unbind` | Admin | 解绑用户设备 |
| 7.4 | POST | `/api/v1/admin/users/:id/deactivate` | Admin | 注销用户 |
| 7.5 | GET | `/api/v1/admin/subscriptions` | Admin | 订阅列表 |
| 7.5 | POST | `/api/v1/admin/subscriptions` | Admin | 创建订阅 |
| 7.5 | POST | `/api/v1/admin/subscriptions/cancel` | Admin | 取消订阅 |
| 7.6 | GET | `/api/v1/admin/records` | Admin | 护理记录列表 |
| 7.7 | GET | `/api/v1/admin/logs` | Admin | 操作日志 |
| 7.8 | GET | `/api/v1/admin/settings` | Admin | 获取设置 |
| 7.8 | POST | `/api/v1/admin/settings` | Admin | 更新设置 |
| 7.9 | GET | `/api/v1/admin/firmware` | Admin | 固件列表 |
| 7.9 | POST | `/api/v1/admin/firmware` | Admin | 登记固件 |
| 7.9 | POST | `/api/v1/admin/firmware/:id/status` | Admin | 更新固件状态 |
