文件
jw-beauty/docs/dev/03-架构说明.md
T
Guoguo 0feb900a1d docs: complete developer documentation (5 guides + index)
- 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
2026-05-18 08:11:30 -07:00

22 KiB
原始文件 Blame 文件历史

架构说明

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 中定义了两个核心映射:

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 更新 currentViewviewProps

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 与设备完成配对确认。