- 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
442 行
22 KiB
Markdown
442 行
22 KiB
Markdown
# 架构说明
|
||
|
||
## 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 固定侧边栏 + 顶栏
|
||
└── <keep-alive>
|
||
└── <component :is="currentComponent"> 动态视图
|
||
```
|
||
|
||
### 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 与设备完成配对确认。
|