From 0feb900a1d83d306ff16085b59fda8c900b93b0f Mon Sep 17 00:00:00 2001 From: Guoguo Date: Mon, 18 May 2026 08:11:30 -0700 Subject: [PATCH] docs: complete developer documentation (5 guides + index) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 01-快速开始: local setup for all 3 modules, common issues - 02-配置说明: all env vars, WeChat/Pay/DB/COS config details - 03-架构说明: system overview, directory structure, data flows - 04-部署指南: Tencent Cloud SCF/COS deployment, launch checklist - 05-API接口文档: all 42 endpoints with params and response format - README index with audience guide and quick links --- docs/dev/01-快速开始.md | 232 +++++++ docs/dev/02-配置说明.md | 291 ++++++++ docs/dev/03-架构说明.md | 441 ++++++++++++ docs/dev/04-部署指南.md | 335 ++++++++++ docs/dev/05-API接口文档.md | 1297 ++++++++++++++++++++++++++++++++++++ docs/dev/README.md | 34 + 6 files changed, 2630 insertions(+) create mode 100644 docs/dev/01-快速开始.md create mode 100644 docs/dev/02-配置说明.md create mode 100644 docs/dev/03-架构说明.md create mode 100644 docs/dev/04-部署指南.md create mode 100644 docs/dev/05-API接口文档.md create mode 100644 docs/dev/README.md diff --git a/docs/dev/01-快速开始.md b/docs/dev/01-快速开始.md new file mode 100644 index 0000000..2ac7b4a --- /dev/null +++ b/docs/dev/01-快速开始.md @@ -0,0 +1,232 @@ +# 快速开始 + +## 项目概述 + +jw-beauty 是一款光子美容仪配套系统,包含三个子项目: + +- **微信小程序** (`miniprogram/`) — 用户端,蓝牙连接设备、护理流程、订阅管理 +- **后端服务** (`server/`) — Express.js API,部署为腾讯云 SCF HTTP 函数 +- **H5 管理后台** (`admin-console/`) — Vue 3 + uni-app H5,管理设备/用户/订阅/固件 + +## 前置要求 + +| 工具 | 版本要求 | 说明 | +|------|---------|------| +| Node.js | >= 18 | 后端使用 Express 5,管理后台使用 Vite 5 | +| npm | >= 8 | 随 Node.js 一起安装 | +| MySQL | >= 5.7 | 本地开发用,推荐 8.0;也可用 Docker | +| 微信开发者工具 | 最新稳定版 | 小程序调试用 | +| Git | 任意 | 版本管理 | + +## 获取代码 + +```bash +git clone <仓库地址> +cd jw-beauty +``` + +## 后端启动 + +### 1. 安装依赖 + +```bash +cd server +npm install +``` + +### 2. 配置环境变量 + +```bash +cp .env.example .env +``` + +编辑 `.env`,至少填写数据库连接信息: + +```ini +NODE_ENV=development +PORT=3000 + +# 数据库(必填) +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=root +DB_PASSWORD=你的MySQL密码 +DB_NAME=jw_beauty + +# 微信登录(本地开发可暂不填,有 mock 登录) +WECHAT_APPID= +WECHAT_SECRET= + +# JWT 密钥(开发环境用默认值即可,会自动 fallback) +JWT_SECRET=replace-with-a-long-random-secret +ADMIN_JWT_SECRET=replace-with-a-different-long-random-secret + +# 管理员账号(开发环境默认 admin/admin) +ADMIN_USERNAME=admin +ADMIN_PASSWORD=admin + +# 腾讯云(头像上传/COS 功能需要,本地开发可暂不填) +TENCENT_SECRET_ID= +TENCENT_SECRET_KEY= +TENCENT_REGION=ap-guangzhou +COS_BUCKET=jw-bucket-1426323813 +COS_REGION=ap-guangzhou +``` + +### 3. 创建数据库 + +先手动创建数据库: + +```bash +mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS jw_beauty DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;" +``` + +然后运行初始化脚本(建表 + 插入默认设置 + 创建管理员账号): + +```bash +npm run db:init +``` + +### 4. 启动服务 + +```bash +npm start +``` + +看到以下输出说明启动成功: + +``` +Server listening on http://localhost:3000 +``` + +### 5. 验证 + +```bash +curl http://localhost:3000/health +``` + +期望返回: + +```json +{"code":0,"data":{"status":"ok"}} +``` + +## 小程序启动 + +### 1. 打开项目 + +用微信开发者工具打开 `miniprogram/` 目录。 + +项目的 AppID 已在 `project.config.json` 中配置为 `wxc4045074ef298510`。如果你没有该 AppID 的权限,可以用测试号或在开发者工具中选择"测试号"。 + +### 2. 指向本地后端 + +编辑 `miniprogram/config/env.js`,将 `ENV` 改为 `local`: + +```js +var ENV = 'local' // 改为 local,API 请求会发到 http://localhost:3000 + +var API_BASES = { + local: 'http://localhost:3000', + test: 'https://api.vsai.net.cn', + prod: 'https://api.vsai.net.cn' +} +``` + +### 3. 注意事项 + +- **不校验合法域名**:在开发者工具「详情 > 本地设置」中勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」,否则 localhost 请求会被拦截 +- **BLE 蓝牙功能需要真机调试**:模拟器无法使用蓝牙,设备连接/护理流程必须在真机上测试 +- **`__DEV__` 标志**:当 `ENV` 不是 `prod` 时,`__DEV__` 为 `true`,会启用 mock 绑定、mock 购买等开发便捷功能 +- **权限**:小程序使用了蓝牙、位置(蓝牙依赖)、相机(扫码)权限,真机调试时需要授权 + +## 管理后台启动 + +### 1. 安装依赖 + +```bash +cd admin-console +npm install +``` + +### 2. 指向本地后端 + +编辑 `admin-console/src/config/env.js`,将 `ENV` 改为 `local`: + +```js +const ENV = 'local' // 改为 local + +const API_BASES = { + local: 'http://localhost:3000', + test: 'https://api.vsai.net.cn', + prod: 'https://api.vsai.net.cn' +} +``` + +### 3. 启动开发服务器 + +```bash +npm run dev +``` + +启动后访问终端输出的地址,通常是 `http://localhost:5173/admin/`。 + +> 注意路径末尾的 `/admin/`,这是 `vite.config.js` 中配置的 `base` 路径。 + +### 4. 登录 + +使用默认管理员账号登录: + +- 用户名:`admin` +- 密码:`admin` + +(与 `.env` 中 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 一致) + +### 5. 构建生产版本 + +```bash +npm run build:h5 +``` + +构建产物在 `dist/build/h5/` 目录,部署到 COS: + +```bash +npm run deploy:cos +``` + +## 常见问题 + +### 端口 3000 被占用 + +修改 `.env` 中的 `PORT`,同时更新小程序和管理后台的 `env.js` 中 `local` 对应的地址。 + +### 数据库连接失败 + +1. 确认 MySQL 服务已启动 +2. 确认 `.env` 中 `DB_HOST`、`DB_PORT`、`DB_USER`、`DB_PASSWORD` 正确 +3. 确认已创建 `jw_beauty` 数据库 +4. 如果用 Docker 跑 MySQL,注意 host 应为 `127.0.0.1` 而非 `localhost`(避免 socket 连接问题) + +### 微信登录在开发环境怎么测试 + +开发环境(`NODE_ENV=development`)下,服务端提供了 mock 端点: + +- **mock 绑定设备**:不需要真实蓝牙握手 +- **mock 购买订阅**:不需要真实微信支付 + +小程序端 `__DEV__` 为 `true` 时会显示对应的开发快捷入口。 + +如果需要测试真实微信登录,需要在 `.env` 中填写 `WECHAT_APPID` 和 `WECHAT_SECRET`(从微信公众平台获取)。 + +### 管理后台页面空白 + +确认访问地址包含 `/admin/` 路径前缀。直接访问 `http://localhost:5173/` 会 404。 + +### npm install 报错 + +确认 Node.js 版本 >= 18。可以使用 nvm 管理多版本: + +```bash +nvm install 18 +nvm use 18 +``` diff --git a/docs/dev/02-配置说明.md b/docs/dev/02-配置说明.md new file mode 100644 index 0000000..09c4cce --- /dev/null +++ b/docs/dev/02-配置说明.md @@ -0,0 +1,291 @@ +# 配置说明 + +## 环境变量总表 + +后端所有配置通过 `server/.env` 文件管理(通过 dotenv 加载)。下表列出全部变量: + +| 变量名 | 用途 | 示例值 | 必填 | 默认值 | +|--------|------|--------|------|--------| +| `NODE_ENV` | 运行环境 | `development` / `production` | 否 | `development` | +| `PORT` | 监听端口 | `3000` | 否 | `3000` | +| `DB_HOST` | MySQL 主机地址 | `127.0.0.1` | 是 | 无 | +| `DB_PORT` | MySQL 端口 | `3306` | 否 | `3306` | +| `DB_USER` | MySQL 用户名 | `root` | 否 | `root` | +| `DB_PASSWORD` | MySQL 密码 | `mypassword` | 是 | 无 | +| `DB_NAME` | 数据库名 | `jw_beauty` | 否 | `jw_beauty` | +| `WECHAT_APPID` | 微信小程序 AppID | `wxc4045074ef298510` | 生产必填 | 无 | +| `WECHAT_SECRET` | 微信小程序 AppSecret | `abcdef1234567890...` | 生产必填 | 无 | +| `JWT_SECRET` | 用户 JWT 签名密钥 | `a-long-random-string` | 生产必填 | `dev-user-secret` | +| `ADMIN_JWT_SECRET` | 管理员 JWT 签名密钥 | `another-long-random-string` | 生产必填 | `dev-admin-secret` | +| `ADMIN_USERNAME` | 初始管理员用户名 | `admin` | 否 | `admin` | +| `ADMIN_PASSWORD` | 初始管理员密码 | `strongpassword` | 生产必填 | `admin` | +| `TENCENT_SECRET_ID` | 腾讯云 API SecretId | `AKIDxxxx` | COS 功能需要 | 无 | +| `TENCENT_SECRET_KEY` | 腾讯云 API SecretKey | `xxxx` | COS 功能需要 | 无 | +| `TENCENT_REGION` | 腾讯云默认地域 | `ap-guangzhou` | 否 | `ap-guangzhou` | +| `COS_BUCKET` | COS 存储桶名称 | `jw-bucket-1426323813` | 否 | `jw-bucket-1426323813` | +| `COS_REGION` | COS 存储桶地域 | `ap-guangzhou` | 否 | 同 `TENCENT_REGION` | +| `COS_CDN_DOMAIN` | COS CDN 加速域名 | `tx.vsai.net.cn` | 否 | `tx.vsai.net.cn` | + +### 生产环境安全检查 + +当 `NODE_ENV=production` 时,服务启动会强制检查以下条件,不满足则拒绝启动: + +- `JWT_SECRET` 不能是默认值 `dev-user-secret` +- `ADMIN_JWT_SECRET` 不能是默认值 `dev-admin-secret` +- `ADMIN_USERNAME` 和 `ADMIN_PASSWORD` 不能是默认值 `admin` + +## 微信配置 + +### 获取 WECHAT_APPID 和 WECHAT_SECRET + +1. 登录 [微信公众平台](https://mp.weixin.qq.com/) -> 开发管理 -> 开发设置 +2. AppID(小程序ID) 即为 `WECHAT_APPID` +3. AppSecret(小程序密钥) 即为 `WECHAT_SECRET`(需要管理员扫码后才能查看,只显示一次) + +```ini +WECHAT_APPID=wxc4045074ef298510 +WECHAT_SECRET=你的AppSecret +``` + +### 开发环境 + +本地开发时不填这两个值也能运行,但微信登录(`wx.login` -> `code2Session`)会失败。此时可以通过 mock 端点绕过。 + +## 数据库配置 + +### 本地开发 + +```ini +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=root +DB_PASSWORD=你的本地MySQL密码 +DB_NAME=jw_beauty +``` + +使用 Docker 快速启动 MySQL: + +```bash +docker run -d \ + --name jw-mysql \ + -p 3306:3306 \ + -e MYSQL_ROOT_PASSWORD=rootpass \ + -e MYSQL_DATABASE=jw_beauty \ + mysql:8.0 \ + --character-set-server=utf8mb4 \ + --collation-server=utf8mb4_unicode_ci +``` + +然后 `.env` 中填: + +```ini +DB_HOST=127.0.0.1 +DB_PASSWORD=rootpass +``` + +### 腾讯云 TencentDB + +SCF 函数部署时,需要将函数放在与 TencentDB 同一 VPC/子网中: + +```ini +DB_HOST=10.0.0.x # VPC 内网地址 +DB_PORT=3306 +DB_USER=root +DB_PASSWORD=腾讯云数据库密码 +DB_NAME=jw_beauty +``` + +### 数据库表结构 + +共 10 张表(定义在 `server/sql/schema.sql`): + +| 表名 | 说明 | +|------|------| +| `users` | 小程序用户,openid 唯一索引 | +| `devices` | 美容仪设备,device_id 为主键 | +| `bindings` | 用户-设备绑定关系 | +| `subscriptions` | 用户订阅记录(月卡/年卡) | +| `treatment_records` | 护理记录,含时长、模式、光密度等 | +| `device_events` | 设备事件日志(错误、告警等) | +| `device_commands` | 下发给设备的远程指令 | +| `operation_logs` | 操作审计日志 | +| `admin_accounts` | 管理员账号 | +| `system_settings` | 系统设置键值对(定价、功能开关等) | +| `firmware_files` | 固件版本记录 | + +初始化时会自动插入默认系统设置:月卡 99 元、年卡 899 元、试用 7 天等。 + +## COS 配置 + +COS(对象存储)用于存储用户头像和固件文件,通过 CDN 域名对外提供访问。 + +```ini +TENCENT_SECRET_ID=AKIDxxxx # 腾讯云控制台 -> 访问管理 -> API密钥管理 +TENCENT_SECRET_KEY=xxxx +COS_BUCKET=jw-bucket-1426323813 # 存储桶名称(含 APPID 后缀) +COS_REGION=ap-guangzhou # 存储桶所在地域 +COS_CDN_DOMAIN=tx.vsai.net.cn # CDN 加速域名(可选,不填有默认值) +``` + +### 获取密钥 + +1. 登录 [腾讯云控制台](https://console.cloud.tencent.com/) +2. 访问管理 -> API 密钥管理 -> 新建密钥 +3. 复制 SecretId 和 SecretKey + +> 建议创建子用户,只授予 COS 相关权限,不要使用主账号密钥。 + +### 本地开发 + +不配置 COS 相关变量时,头像上传和固件上传功能会报错,但不影响其他功能开发。 + +## 安全配置 + +### JWT_SECRET + +用于签发用户端 JWT token,有效期 7 天。生成方法: + +```bash +node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +``` + +将输出填入 `.env`: + +```ini +JWT_SECRET=生成的64位十六进制字符串 +``` + +### ADMIN_JWT_SECRET + +用于签发管理后台 JWT token,必须与 `JWT_SECRET` 不同。用同样的方法生成: + +```bash +node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +``` + +```ini +ADMIN_JWT_SECRET=另一个64位十六进制字符串 +``` + +### 管理员初始密码 + +`npm run db:init` 会使用 `.env` 中的 `ADMIN_USERNAME` 和 `ADMIN_PASSWORD` 创建管理员账号(bcrypt 哈希存储)。 + +```ini +ADMIN_USERNAME=admin +ADMIN_PASSWORD=一个强密码 +``` + +> 生产环境不能使用默认值 `admin/admin`,服务启动时会报错退出。 + +### CORS + +当前 CORS 设置为 `Access-Control-Allow-Origin: *`(见 `server/src/app.js`),允许所有来源。这是开发阶段的配置,生产环境应收紧为具体域名。 + +允许的自定义请求头: + +``` +Content-Type, Authorization, X-Device-Id, X-App-Version, X-Platform +``` + +## 小程序配置 + +### env.js + +文件路径:`miniprogram/config/env.js` + +```js +var ENV = 'test' // 可选值:local / test / prod + +var API_BASES = { + local: 'http://localhost:3000', + test: 'https://api.vsai.net.cn', + prod: 'https://api.vsai.net.cn' +} + +var __DEV__ = ENV !== 'prod' +``` + +#### ENV 取值说明 + +| 值 | API 地址 | `__DEV__` | 说明 | +|----|---------|-----------|------| +| `local` | `http://localhost:3000` | `true` | 本地开发 | +| `test` | `https://api.vsai.net.cn` | `true` | 连接测试环境 | +| `prod` | `https://api.vsai.net.cn` | `false` | 生产环境 | + +#### `__DEV__` 的作用 + +当 `__DEV__` 为 `true` 时: + +- 启用 mock 绑定设备(无需蓝牙握手) +- 启用 mock 购买订阅(无需微信支付) +- 可能显示额外的调试信息 + +发布正式版时务必将 `ENV` 设为 `prod`。 + +### project.config.json + +文件中的 `appid` 字段需要与实际的小程序 AppID 一致: + +```json +{ + "appid": "wxc4045074ef298510", + "projectname": "hox-beauty" +} +``` + +如果没有该 AppID 权限,在微信开发者工具中可以选择测试号。 + +## 管理后台配置 + +### env.js + +文件路径:`admin-console/src/config/env.js` + +```js +const ENV = 'test' // 可选值:local / test / prod + +const API_BASES = { + local: 'http://localhost:3000', + test: 'https://api.vsai.net.cn', + prod: 'https://api.vsai.net.cn' +} +``` + +与小程序的 `env.js` 功能一致,本地开发时改为 `local`。 + +### Vite base path + +`admin-console/vite.config.js` 配置了 base 路径: + +```js +export default defineConfig({ + base: process.env.PUBLIC_PATH || '/admin/', + plugins: [uni()] +}) +``` + +- 默认 base 为 `/admin/`,意味着所有静态资源路径都会加上 `/admin/` 前缀 +- 部署到 COS 后通过 `https://你的域名/admin/` 访问 +- 本地开发时访问地址为 `http://localhost:5173/admin/` +- 如需改变路径前缀,设置环境变量 `PUBLIC_PATH`: + +```bash +PUBLIC_PATH=/console/ npm run build:h5 +``` + +### 构建与部署 + +```bash +# 开发 +npm run dev + +# 构建 H5 产物 +npm run build:h5 + +# 构建并部署到 COS +npm run deploy:cos +``` + +`deploy:cos` 脚本会将 `dist/build/h5/` 目录的文件上传到 COS 存储桶,需要在 `server/.env`(或管理后台自己的 `.env`)中配置好 `TENCENT_SECRET_ID`、`TENCENT_SECRET_KEY`、`COS_BUCKET` 等变量。 diff --git a/docs/dev/03-架构说明.md b/docs/dev/03-架构说明.md new file mode 100644 index 0000000..9c378b1 --- /dev/null +++ b/docs/dev/03-架构说明.md @@ -0,0 +1,441 @@ +# 架构说明 + +## 1. 系统架构总览 + +``` + +-----------------+ + | 腾讯云 COS | + | (头像/固件存储) | + +--------^--------+ + | ++------------------+ +--------+--------+ +------------------+ +| | | | | | +| 微信小程序 +--->+ SCF 云函数 +<---+ 管理后台 | +| (用户端) | | (Express) | | (uni-app H5) | +| +<---+ +--->+ | ++-------+----------+ +--------+--------+ +------------------+ + | | + | BLE | mysql2 + v v ++-------+----------+ +--------+--------+ +| 光子美容仪 | | MySQL | +| (蓝牙设备) | | (腾讯云) | ++------------------+ +-----------------+ +``` + +三端关系: +- **小程序** 通过 HTTPS 调用后端 API,通过 BLE 连接硬件设备 +- **管理后台** 通过同一套后端 API(`/api/v1/admin/*`)管理数据 +- **后端** 部署为腾讯云 SCF 云函数,连接 MySQL 和 COS + +## 2. 后端架构 + +### 2.1 目录结构 + +``` +server/src/ +├── index.js # SCF 入口,导出 main_handler +├── app.js # Express 应用,中间件 + 路由挂载 +├── config.js # 环境变量配置 +├── middleware/ +│ └── auth.js # JWT 认证中间件 +├── routes/ +│ ├── auth.js # 登录/刷新 token +│ ├── user.js # 用户信息 +│ ├── device.js # 设备绑定/解绑/指令 +│ ├── subscription.js # 订阅计划/购买/试用 +│ ├── treatment.js # 护理记录同步/查询 +│ ├── firmware.js # 固件版本管理 +│ └── admin.js # 管理后台全部接口 +├── dao/ +│ ├── index.js # barrel export +│ ├── user.dao.js # users 表 +│ ├── device.dao.js # devices 表 +│ ├── binding.dao.js # device_bindings 表 +│ ├── subscription.dao.js # subscriptions 表 +│ ├── treatment.dao.js # treatment_records 表 +│ ├── command.dao.js # device_commands 表 +│ ├── device-event.dao.js # device_events 表 +│ ├── admin.dao.js # admin_accounts 表 +│ ├── log.dao.js # operation_logs 表 +│ ├── settings.dao.js # system_settings 表 +│ └── firmware.dao.js # firmware_versions 表 +└── lib/ + ├── serverless.js # SCF event → Express 适配器 + ├── db.js # mysql2 连接池 (query/one/transaction) + ├── auth.js # JWT 签发/密码哈希/Bearer 读取 + ├── response.js # 统一响应 { code, message, data } + ├── settings-cache.js # system_settings 内存缓存 (TTL 60s) + ├── wechat.js # 微信 code2Session / 获取手机号 + ├── cos.js # 腾讯云 COS 客户端 + └── utils.js # 日期格式化工具 +``` + +### 2.2 请求处理流程 + +``` +SCF event + │ + ▼ +serverless.js 将 SCF event 转换为 http.IncomingMessage + ServerResponse + │ + ▼ +Express app(req, res) + │ + ├── express.json() 解析 JSON body + ├── CORS 中间件 设置跨域头,处理 OPTIONS + ├── rate limiter 对 login/upload 路径限流 + ├── authMiddleware 提取 x-forwarded-for → req.ip + │ + ├── /health 健康检查 + ├── /api/v1/auth/* → routes/auth.js + ├── /api/v1/user/* → routes/user.js (requireUser) + ├── /api/v1/device/* → routes/device.js (requireUser) + ├── /api/v1/subscription/* → routes/subscription.js + ├── /api/v1/treatment/* → routes/treatment.js (requireUser) + ├── /api/v1/admin/* → routes/admin.js (requireAdmin) + ├── /api/v1/firmware/* → routes/firmware.js + │ + ├── 404 handler + └── error handler 记录错误日志,返回 3001 +``` + +### 2.3 认证机制 + +JWT 双密钥体系: + +| 角色 | 签发函数 | 密钥 | payload 字段 | +|-------|-------------|---------------------|----------------------------| +| user | signUser() | config.jwt.secret | type, user_id, openid | +| admin | signAdmin() | config.jwt.adminSecret | type, admin_id, username, role | + +中间件层级: +- `authMiddleware` — 全局,仅提取 IP,不验证 token +- `requireUser` — 路由级,验证 user token,查库确认用户存在且 status=1,挂载 `req.user` +- `requireAdmin` — 路由级,验证 admin token,查库确认管理员存在且 status=1,挂载 `req.admin` + +token 刷新:客户端在 token 剩余 <24h 时自动调用 `/auth/refresh`,过期后有 1 天宽限期。 + +### 2.4 DAO 层 + +每个 DAO 文件只操作一张表,通过 `db.js` 提供的 `query/one/transaction` 与数据库交互: + +| DAO 文件 | 负责表 | 核心操作 | +|-----------------------|---------------------|----------------------------------| +| user.dao.js | users | CRUD、openid 查找、分页列表 | +| device.dao.js | devices | 设备列表、按用户绑定关系查询 | +| binding.dao.js | device_bindings | 创建/确认/取消绑定、活跃绑定查询 | +| subscription.dao.js | subscriptions | 试用创建、购买(extend)、查有效订阅 | +| treatment.dao.js | treatment_records | 创建记录、按用户分页、更新设备状态 | +| command.dao.js | device_commands | 创建指令、拉取待执行、标记完成 | +| device-event.dao.js | device_events | 设备异常事件记录 | +| admin.dao.js | admin_accounts | 管理员查找/验证 | +| log.dao.js | operation_logs | 操作日志写入与分页查询 | +| settings.dao.js | system_settings | 全量读取系统配置 | +| firmware.dao.js | firmware_versions | 固件版本列表/最新版查询 | + +### 2.5 工具库 + +| 文件 | 职责 | +|-------------------|-------------------------------------------------------------| +| response.js | `ok(data)` / `fail(code, msg)` 统一响应格式 | +| settings-cache.js | system_settings 内存缓存,TTL 60秒,`invalidateCache()` 手动失效 | +| wechat.js | 微信登录 `code2Session`、获取手机号 `getPhoneNumber` | +| cos.js | 腾讯云 COS 签名 URL 生成 | +| utils.js | `toMysqlDate()` 日期格式化(UTC+8) | +| wxpay.js | (规划中) 微信支付下单、回调签名验证 | + +## 3. 小程序架构 + +### 3.1 目录结构 + +``` +miniprogram/ +├── app.js / app.json / app.wxss # 应用入口 +├── config/ +│ └── env.js # API_BASE 配置 +├── pages/ # 18 个页面 +│ ├── login/ # 微信登录 +│ ├── register/ # 补充信息 +│ ├── index/ # 首页 (tab) +│ ├── scan/ # 扫码绑定 +│ ├── auto-scan/ # 自动扫描蓝牙设备 +│ ├── ble-connect/ # BLE 连接 +│ ├── bind-success/ # 绑定成功 +│ ├── subscribe-prompt/ # 订阅提示 +│ ├── subscribe-plans/ # 选择计划 +│ ├── subscribe-success/ # 订阅成功 +│ ├── wear-check/ # 佩戴检测 +│ ├── treatment-setup/ # 护理参数设置 +│ ├── treating/ # 护理进行中 +│ ├── treatment-done/ # 护理完成 +│ ├── history/ # 护理记录 (tab) +│ ├── profile/ # 我的 (tab) +│ ├── help/ # 帮助 +│ └── contact/ # 联系我们 +├── services/ +│ ├── ble.js # proxy → ble/index.js +│ ├── ble/ # BLE 模块(见 3.3) +│ └── command-sync.js # 远程指令拉取与执行 +└── utils/ + ├── request.js # HTTP 请求封装 + └── api.js # 命名 API 函数 +``` + +### 3.2 页面导航流程 + +完整用户旅程(从登录到护理完成): + +``` +login ──→ register(新用户) ──→ index(首页) + │ + ┌───────┴──────┐ + ▼ ▼ + scan auto-scan + (扫码绑定) (自动扫描BLE) + │ │ + └──────┬───────┘ + ▼ + ble-connect + (蓝牙连接设备) + │ + ▼ + bind-success + (绑定成功) + │ + ┌──────┴──────┐ + ▼ ▼ + subscribe-prompt (已订阅则跳过) + (提示订阅) + │ + ▼ + subscribe-plans + (选择计划) + │ + ▼ + subscribe-success ──→ wear-check + (佩戴检测) + │ + ▼ + treatment-setup + (设置护理参数) + │ + ▼ + treating + (护理中) + │ + ▼ + treatment-done + (护理完成,同步记录) +``` + +底部 Tab 页:首页(index) | 记录(history) | 我的(profile) + +### 3.3 BLE 模块拆分 + +``` +services/ble.js # proxy,直接 re-export ble/index.js + │ +services/ble/ + ├── index.js # barrel,统一导出所有 API + ├── protocol.js # 协议常量 + 帧编解码 + ├── connection.js # 扫描/连接/断开/自动重连 + └── commands.js # 指令发送 + ACK 管理 +``` + +**协议模式**:`PROTOCOL_MODE = 'vendor_33'`,使用自定义 BLE 服务(FFE0/FFE1/FFE2)而非标准 GATT profile。 + +**关键设计决策**: +- connection.js 维护共享状态(`_deviceId`, `_connected`, `_chars`),commands.js 通过 getter 访问 +- 事件系统在 connection.js 中实现(`on/off/emit`),心跳和状态通知作为独立事件分发 +- command-sync.js 负责从服务器拉取待执行指令,逐条执行后上报结果 + +### 3.4 API 调用层 + +``` +页面代码 + │ + │ var api = require('../../utils/api') + │ api.getProfile() + │ + ▼ +api.js 命名函数,封装路径和参数 + │ + │ http.get('/api/v1/user/profile') + │ + ▼ +request.js 统一封装 wx.request + │ + ├── 自动附加 Authorization / X-App-Version / X-Platform + ├── token 过期前 <24h 自动刷新 + ├── code 1001/1002 → 清除 token → reLaunch 到登录页 + └── 统一错误格式 { code, message } +``` + +## 4. 管理后台架构 + +### 4.1 SPA 架构 + +基于 uni-app (Vue 2) 构建的 H5 单页应用。不使用 vue-router,而是通过动态组件实现页面切换: + +``` +AdminLayout 固定侧边栏 + 顶栏 + └── + └── 动态视图 +``` + +### 4.2 VIEW_MAP 与 KEEP_ALIVE_VIEWS + +`pages/admin/index.vue` 中定义了两个核心映射: + +```js +VIEW_MAP = { + dashboard: DashboardView, + device: DeviceListView, + 'device-detail': DeviceDetailView, + user: UserListView, + 'user-detail': UserDetailView, + subscription: SubscriptionView, + record: RecordView, + log: LogView, + settings: SettingsView +} + +KEEP_ALIVE_VIEWS = [ + 'DeviceListView', 'UserListView', + 'SubscriptionView', 'RecordView', 'LogView' +] +``` + +列表页被 keep-alive 缓存,从详情页返回时保留滚动位置和筛选状态。详情页(device-detail, user-detail)通过 `viewKey` 携带 ID,保证每次进入重新挂载。 + +导航通过 `$emit('navigate', viewName, props)` 冒泡到 index.vue,由 `onNavigate` 更新 `currentView` 和 `viewProps`。 + +### 4.3 API 调用模式 + +``` +View 组件 + │ + │ import { get, post } from '../utils/request' + │ get('/api/v1/admin/users') + │ + ▼ +utils/request.js 基于 uni.request 封装 + │ + ├── 自动附加 admin_token (Bearer) + ├── code 1001/1002 → 清除 token → reLaunch 到登录页 + └── 统一 resolve(data) / reject(error) +``` + +状态管理:Pinia store (`store/user.js`) 管理 admin token 和登录信息。 + +### 4.4 构建和部署 + +- 框架:uni-app,编译目标 H5 +- 部署方式:构建产物上传至腾讯云 COS 静态托管,或直接部署到 SCF +- 菜单项:仪表盘、设备管理、用户管理、订阅管理、护理记录、操作日志、系统设置 + +## 5. 数据流 + +### 5.1 支付流程 + +``` +用户点击购买 后端 微信支付 + │ │ │ + │ api.purchase(plan) │ │ + ├─────────────────────────────>│ │ + │ │ (当前: 返回 order_id) │ + │ { order_id, payment_params }│ │ + │<─────────────────────────────│ │ + │ │ │ + │ === 正式支付流程(规划中) ====│ │ + │ wx.requestPayment(params) │ │ + │ ────────────────────────────┼───────────────────────────>│ + │ │ 支付回调 (notify) │ + │ │<───────────────────────────│ + │ │ 验签 → 更新 subscription │ + │ │ subscriptionDao.purchase() │ + │ │ (extend 模式,叠加天数) │ + │ │ │ + │ === 开发环境替代方案 ========│ │ + │ api.mockPurchase(plan) │ │ + ├─────────────────────────────>│ │ + │ │ 直接激活订阅 │ + │ { status: 'active' } │ │ + │<─────────────────────────────│ │ +``` + +订阅模型:`subscriptionDao.purchase()` 采用 extend 模式 —— 如已有有效订阅,在现有到期日基础上叠加天数,而非覆盖。 + +### 5.2 护理记录同步流程 + +``` +treating 页面 BLE 设备 后端 + │ │ │ + │ ble.setParams({...}) │ │ + ├─────────────────────────────>│ │ + │ ACK │ │ + │<─────────────────────────────│ │ + │ ble.startTreatment() │ │ + ├─────────────────────────────>│ │ + │ status notify (周期) │ │ + │<─────────────────────────────│ │ + │ ...护理进行中... │ │ + │ ble.stopTreatment() │ │ + ├─────────────────────────────>│ │ + │ treatment_complete notify │ │ + │<─────────────────────────────│ │ + │ │ │ + │ 跳转 treatment-done 页面 │ + │ │ + │ api.syncTreatment({ │ + │ device_id, session_id, │ + │ start_time, end_time, │ + │ regions, duration_ms, │ + │ mode, wavelength, │ + │ battery, temperature, │ + │ pd_values ... │ + │ }) │ + ├───────────────────────────────────────────────────────>│ + │ treatmentDao.create() │ + │ treatmentDao. │ + │ updateDevice() │ + │ { record_id } │ + │<───────────────────────────────────────────────────────│ +``` + +### 5.3 设备绑定流程 + +``` +用户 小程序 后端 + │ │ │ + │ 扫码/自动扫描获取 device_id│ │ + │─────────────────────────────│ │ + │ │ api.bindDevice(deviceId) │ + │ ├───────────────────────────>│ + │ │ │ 检查:用户未绑定其他设备 + │ │ │ 检查:设备存在于 devices 表 + │ │ │ 取消旧的 pending 绑定 + │ │ │ 创建 pending 绑定 + bind_token + │ │ { device_id, bind_token } │ + │ │<───────────────────────────│ + │ │ │ + │ │ BLE 连接设备 │ + │ │ ble.connect(deviceId) │ + │ │ ble.bindDevice(bind_token) │ + │ │ ──BLE──> 设备 │ + │ │ <──ACK── 设备 │ + │ │ │ + │ │ api.confirmBind( │ + │ │ deviceId, bind_token) │ + │ ├───────────────────────────>│ + │ │ │ bindingDao.confirmBind() + │ │ │ pending → active + │ │ { message: 'success' } │ + │ │<───────────────────────────│ + │ │ │ + │ 跳转 bind-success 页面 │ │ + │<────────────────────────────│ │ +``` + +绑定约束:一个用户同时只能绑定一台设备;绑定前需先通过 BLE 与设备完成配对确认。 diff --git a/docs/dev/04-部署指南.md b/docs/dev/04-部署指南.md new file mode 100644 index 0000000..4065b25 --- /dev/null +++ b/docs/dev/04-部署指南.md @@ -0,0 +1,335 @@ +# 部署指南 + +本文档面向将 jw-beauty 项目部署到腾讯云生产环境的运维人员。 + +--- + +## 1. 部署架构 + +``` +┌─────────────┐ HTTPS ┌──────────────────┐ +│ 微信小程序 │ ───────────────► │ SCF HTTP 函数 │ +└─────────────┘ │ jw-beauty-api │ + │ (Node.js 18) │ +┌─────────────┐ HTTPS │ │ VPC 内网 ┌──────────────┐ +│ 管理后台H5 │ ──► COS/CDN │ scf_bootstrap │ ──────────────────► │ TencentDB │ +│ (UniApp) │ │ → local-server │ │ MySQL 5.7+ │ +└─────────────┘ └──────────────────┘ └──────────────┘ + │ + │ COS SDK + ▼ + ┌──────────────────┐ + │ COS Bucket │ + │ 头像/固件/后台H5 │ + └──────────────────┘ +``` + +| 组件 | 服务 | 说明 | +|------|------|------| +| 后端 API | 腾讯云 SCF (HTTP 函数) | Express 应用,通过 scf_bootstrap 启动 | +| 数据库 | TencentDB for MySQL | 同 VPC 内网连接,端口 3306 | +| 对象存储 | COS | 存放头像、固件包、管理后台静态文件 | +| API 域名 | api.vsai.net.cn | 指向 SCF 函数 URL / API 网关 | +| CDN 域名 | tx.vsai.net.cn | COS 自定义域名,用于头像和固件下载 | +| 小程序 | 微信小程序 | 通过微信开发者工具上传并提审 | + +--- + +## 2. 后端部署(SCF) + +### 2.1 打包 + +部署包以 `server/` 为根目录打包,包含以下文件和目录: + +``` +index.js # SCF 入口 (exports.main_handler) +package.json +package-lock.json +scf_bootstrap # HTTP 函数启动脚本 +scripts/ # local-server.js 等 +src/ # 应用源码 +sql/ # schema.sql +node_modules/ # 依赖 +``` + +**不要** 把 `.env` 文件打进部署包。 + +打包命令: + +```bash +cd server +npm install --production +rm -f /tmp/hox-http-function.zip +zip -qry /tmp/hox-http-function.zip \ + index.js package.json package-lock.json \ + scripts scf_bootstrap src sql node_modules +``` + +### 2.2 scf_bootstrap 配置 + +当前使用 HTTP 函数模式,`scf_bootstrap` 内容: + +```bash +#!/bin/bash +set -e +export PORT=9000 +node scripts/local-server.js +``` + +`scf_bootstrap` 必须有可执行权限。SCF 平台会直接运行此文件启动 HTTP 服务,监听 `PORT` 端口。 + +> 备注:`index.js` 中的 `main_handler` 是事件函数入口,目前未使用。如果切换到事件函数触发模式,SCF 入口改为 `index.main_handler`。 + +### 2.3 环境变量设置 + +在 SCF 控制台的函数配置中添加以下环境变量: + +| 变量名 | 必填 | 说明 | +|--------|------|------| +| `NODE_ENV` | 是 | 必须设为 `production` | +| `PORT` | 否 | 默认 `9000`,scf_bootstrap 已设置 | +| `TENCENT_SECRET_ID` | 是 | 腾讯云 API 密钥 ID(COS 签名用) | +| `TENCENT_SECRET_KEY` | 是 | 腾讯云 API 密钥 Key | +| `TENCENT_REGION` | 否 | 默认 `ap-guangzhou` | +| `DB_HOST` | 是 | 数据库内网地址 | +| `DB_PORT` | 否 | 默认 `3306` | +| `DB_USER` | 是 | 数据库用户名 | +| `DB_PASSWORD` | 是 | 数据库密码 | +| `DB_NAME` | 否 | 默认 `jw_beauty` | +| `COS_BUCKET` | 否 | 默认 `jw-bucket-1426323813` | +| `COS_REGION` | 否 | 默认 `ap-guangzhou` | +| `COS_CDN_DOMAIN` | 否 | 默认 `tx.vsai.net.cn` | +| `WECHAT_APPID` | 是 | 微信小程序 AppID | +| `WECHAT_SECRET` | 是 | 微信小程序 AppSecret | +| `JWT_SECRET` | 是 | 用户 JWT 签名密钥(随机长字符串) | +| `ADMIN_JWT_SECRET` | 是 | 管理员 JWT 签名密钥(随机长字符串) | +| `ADMIN_USERNAME` | 是 | 初始管理员用户名(不要用默认 admin) | +| `ADMIN_PASSWORD` | 是 | 初始管理员密码(不要用默认 admin) | + +> 生产环境安全检查:代码在 `NODE_ENV=production` 时会校验 `JWT_SECRET` 和 `ADMIN_JWT_SECRET` 不是默认值,`ADMIN_USERNAME` / `ADMIN_PASSWORD` 不是 `admin/admin`,否则启动报错。 + +参考模板文件:`server/.env.production.example` + +### 2.4 VPC 网络配置 + +SCF 函数必须配置与 TencentDB 相同的 VPC 和子网,才能通过内网地址访问数据库。 + +操作步骤: +1. 在 SCF 控制台 → 函数管理 → 选择函数 → 函数配置 → 网络配置 +2. 选择与 TencentDB 实例相同的 VPC 和子网 +3. 确认安全组允许 SCF 访问数据库端口(默认 3306) + +### 2.5 注意事项 + +- **冷启动**:SCF 冷启动时需加载 Node.js 运行时和 `node_modules`,首次请求延迟较高(约 1-3 秒)。可配置预置并发实例减少冷启动。 +- **超时设置**:建议函数超时设为 30 秒以上。 +- **内存**:建议 256MB 或以上。 +- **日志**:错误日志通过 `console.error` 输出,可在 SCF 日志服务中查看。 + +--- + +## 3. 数据库初始化 + +### 3.1 连接 TencentDB + +本地连接 TencentDB 需要: +- 数据库已开启公网访问,或通过 VPN/跳板机连接内网 +- 安全组白名单中已添加本机公网 IP +- `DB_PORT` 使用控制台显示的公网端口 + +在 `server/.env` 中配置连接信息后执行: + +```bash +cd server +npm install +npm run db:init +``` + +### 3.2 执行 schema.sql + +`db:init` 脚本会自动执行 `sql/schema.sql`,创建以下业务表: + +| 表名 | 说明 | +|------|------| +| `users` | 用户信息 | +| `devices` | 设备(产品码) | +| `bindings` | 用户-设备绑定关系 | +| `subscriptions` | 订阅记录 | +| `treatment_records` | 护理记录 | +| `device_events` | 设备事件 | +| `device_commands` | 远程指令队列 | +| `operation_logs` | 操作日志 | +| `admin_accounts` | 管理员账户 | +| `system_settings` | 系统设置 | +| `firmware_files` | 固件版本 | + +同时写入默认系统设置(月卡价格、年卡价格、试用天数等)。 + +### 3.3 初始管理员创建 + +`db:init` 会使用环境变量 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 创建初始管理员账户。 + +首次登录后应立即在管理后台修改默认密码。 + +--- + +## 4. 管理后台部署(COS) + +### 4.1 构建 + +确认 `admin-console/src/config/env.js` 中 `ENV` 已设为 `prod`(或 API 地址正确): + +```js +const ENV = 'prod' +``` + +然后构建: + +```bash +cd admin-console +npm install +npm run build:h5 +``` + +构建产物位于 `admin-console/dist/build/h5/`。 + +### 4.2 上传到 COS + +方式一:使用部署脚本(推荐) + +```bash +cd admin-console +npm run deploy:cos +``` + +脚本会读取 `server/.env` 中的 COS 凭证,将构建产物上传到 COS bucket 的 `admin/` 前缀下。 + +可通过环境变量自定义前缀: + +```bash +ADMIN_COS_PREFIX=admin-test/ npm run deploy:cos +``` + +方式二:手动上传 + +通过 COS 控制台或 COSCMD 工具将 `dist/build/h5/` 目录下所有文件上传到 bucket 的 `admin/` 目录。 + +### 4.3 CDN/域名配置 + +管理后台通过 COS 的自定义域名(`tx.vsai.net.cn`)访问: + +- 在 COS 控制台为 bucket 绑定自定义域名 +- 配置 CDN 加速(可选) +- 访问地址:`https://tx.vsai.net.cn/admin/` + +### 4.4 base path 设置 + +管理后台的路由 base path 为 `/admin/`,确保 COS 上传前缀与此一致。 + +如果需要配置单页应用(SPA)的 history 模式回退,可在 COS 静态网站配置中设置错误文档为 `/admin/index.html`。 + +--- + +## 5. 小程序发布 + +### 5.1 环境切换 + +发布前将 `miniprogram/config/env.js` 中的 `ENV` 改为 `prod`: + +```js +var ENV = 'prod' +``` + +### 5.2 微信开发者工具上传 + +1. 打开微信开发者工具 +2. 导入项目目录 `miniprogram/` +3. 确认 AppID 正确 +4. 点击"上传",填写版本号和备注 +5. 在微信公众平台 → 版本管理 → 提交审核 + +### 5.3 审核注意事项 + +- **用户协议** 和 **隐私政策** 页面必须存在且内容完整,否则审核不通过 +- 涉及蓝牙权限的小程序需要在 `app.json` 中声明并说明用途 +- 隐私信息收集弹窗需符合微信规范 +- 建议提交审核前在体验版充分测试 + +--- + +## 6. 域名和证书 + +### 6.1 API 域名 + +| 域名 | 用途 | 指向 | +|------|------|------| +| `api.vsai.net.cn` | 后端 API | SCF 函数 URL / API 网关 | + +需要配置 HTTPS 证书(腾讯云可申请免费 SSL 证书)。 + +### 6.2 COS CDN 域名 + +| 域名 | 用途 | 指向 | +|------|------|------| +| `tx.vsai.net.cn` | 头像/固件下载/管理后台 | COS bucket CDN | + +在 COS 控制台绑定自定义域名并配置 HTTPS。 + +### 6.3 小程序合法域名配置 + +在微信公众平台 → 开发管理 → 开发设置 → 服务器域名中配置: + +| 类型 | 域名 | +|------|------| +| request 合法域名 | `https://api.vsai.net.cn` | +| downloadFile 合法域名 | `https://tx.vsai.net.cn` | +| uploadFile 合法域名 | `https://api.vsai.net.cn` | + +--- + +## 7. 上线检查清单 + +### 环境配置 + +- [ ] `NODE_ENV=production` 已设置 +- [ ] `JWT_SECRET` 和 `ADMIN_JWT_SECRET` 已更换为强随机字符串 +- [ ] `ADMIN_USERNAME` / `ADMIN_PASSWORD` 已更换默认值 +- [ ] 数据库连接使用 VPC 内网地址 +- [ ] 安全组规则正确(SCF → MySQL 3306) +- [ ] COS 凭证配置正确 + +### 数据库 + +- [ ] `schema.sql` 已执行成功,所有表已创建 +- [ ] 初始管理员已创建并更改了默认密码 +- [ ] 默认系统设置已写入 `system_settings` 表 + +### 后端 + +- [ ] `GET /health` 返回 `{"code":0}` +- [ ] `POST /api/v1/admin/login` 可正常登录 +- [ ] `GET /api/v1/admin/dashboard` 返回统计数据 +- [ ] `GET /api/v1/admin/settings` 返回系统设置 + +### 管理后台 + +- [ ] 构建时 `ENV` 已设为 `prod` +- [ ] 构建产物已上传到 COS `admin/` 目录 +- [ ] `https://tx.vsai.net.cn/admin/` 可正常访问 +- [ ] 管理员可登录并查看仪表盘 + +### 小程序 + +- [ ] `miniprogram/config/env.js` 中 `ENV='prod'` +- [ ] 微信公众平台已配置 request/download/upload 合法域名 +- [ ] 小程序登录流程正常 +- [ ] 蓝牙扫码绑定流程正常 +- [ ] 护理记录同步正常 +- [ ] 用户协议和隐私政策页面内容完整 + +### 安全 + +- [ ] `.env` 文件未包含在部署包中 +- [ ] 生产环境 `mock-bind` 和 `mock-purchase` 接口已自动禁用 +- [ ] CORS 策略按需收紧(当前为 `*`) +- [ ] Rate limiting 已生效(登录 15min/10次,管理员 15min/5次) diff --git a/docs/dev/05-API接口文档.md b/docs/dev/05-API接口文档.md new file mode 100644 index 0000000..2c2d6ee --- /dev/null +++ b/docs/dev/05-API接口文档.md @@ -0,0 +1,1297 @@ +# API 接口文档 + +本文档列出 jw-beauty 后端所有 API 接口,供前端开发和调试参考。 + +**Base URL**: `https://api.vsai.net.cn` + +--- + +## 通用约定 + +### 认证方式 + +需要认证的接口在请求头中携带 JWT: + +``` +Authorization: Bearer +``` + +- `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 | 更新固件状态 | diff --git a/docs/dev/README.md b/docs/dev/README.md new file mode 100644 index 0000000..878e97c --- /dev/null +++ b/docs/dev/README.md @@ -0,0 +1,34 @@ +# 开发文档 + +光子美容仪项目开发文档,面向开发者和运维人员。 + +## 目录 + +| 文档 | 说明 | 适合谁读 | +|------|------|---------| +| [01-快速开始](01-快速开始.md) | 本地环境搭建、三端启动步骤、常见问题 | 新加入的开发者 | +| [02-配置说明](02-配置说明.md) | 所有环境变量详解、微信/支付/数据库/COS 配置 | 开发者、运维 | +| [03-架构说明](03-架构说明.md) | 系统架构、目录结构、数据流、模块设计 | 需要改代码的开发者 | +| [04-部署指南](04-部署指南.md) | 腾讯云 SCF/COS 部署、数据库初始化、上线检查清单 | 运维、负责发布的人 | +| [05-API接口文档](05-API接口文档.md) | 全部 42 个 API 接口的方法、参数、响应格式 | 前后端开发者 | + +## 项目结构 + +``` +jw-beauty/ +├── miniprogram/ 小程序(微信原生) +├── server/ 后端(Express + SCF) +├── admin-console/ 管理后台(Vue 3 + uni-app H5) +└── docs/ 文档 + ├── dev/ 开发文档(本目录) + ├── design/ 设计文档 + ├── protocols/ BLE 协议文档 + └── deploy/ 部署笔记 +``` + +## 快速链接 + +- 本地启动后端:`cd server && npm install && npm start` +- 健康检查:`curl http://localhost:3000/health` +- 管理后台开发:`cd admin-console && npm install && npm run dev:h5` +- 数据库初始化:`cd server && npm run db:init`