文档 / 平台设计
顶层设计与技术架构
系统全景、TDengine 数据库、服务器与部署拓扑、命名规范、鉴权体系与 API 一览。
系统全景
┌─────────────┐ HTTPS 事件/心跳 ┌──────────────────────────────┐
│ ESP32 Hub │ ───────────────────▶ │ 服务器 │
│ 磁吸/压力/按钮│ ◀──── BLE 配网 ──── │ Nginx │
└─────────────┘ │ ├─ /fanglian → 后端 API │
▲ │ └─ /fanglian-admin → Web 管理台│
│ 蓝牙下发 Wi-Fi │ │
│ │ 后端 API(PM2 fanglian-backend)│
┌─────────────┐ REST / JWT │ ├─ TDengine(时序事件库) │
│ 微信小程序 │ ───────────────────▶ │ └─ metadata.json(业务元数据) │
└─────────────┘ └─────────────┬────────────────┘
▲ 微信服务通知(订阅消息) │
└────────────────────────────────────────────┘
一次典型事件链路:传感器触发 → Hub 立即 HTTPS 上报 → 后端写入 TDengine 并按订阅偏好推送微信服务通知 → 小程序时间线与 Web 管理台实时可查。Hub 每 60 秒一次心跳,用于判断在线/离线。
四端清单
| 端 | 技术栈 | 代码目录 | 关键说明 |
|---|---|---|---|
| ESP32 Hub | Arduino / PlatformIO | firmware/esp32 |
三种开发板环境,详见固件与烧录 |
| 云端后端 | Next.js App Router API | backend |
设备认证、事件入库、微信登录、订阅与亲友卡 |
| 数据库 | TDengine 3.x | database/01_init.sql |
时序事件存储,REST 接口 6041 |
| 微信小程序 | Taro + React | miniprogram |
用户端全部体验入口 |
| Web 管理台 | Next.js 16 + Tailwind 4 | web |
basePath=/fanglian-admin,页面说明见使用手册 |
数据库:TDengine
时序事件全部存入 TDengine,初始化脚本为 database/01_init.sql(taos -f 执行):
CREATE DATABASE IF NOT EXISTS elder_guard KEEP 365 DURATION 1;
USE elder_guard;
CREATE STABLE IF NOT EXISTS sensor_events (
ts TIMESTAMP, `event_type` VARCHAR(64), `value` DOUBLE
) TAGS (hub_id VARCHAR(64), sensor_id VARCHAR(64), sensor_type VARCHAR(64));
设计要点:
elder_guard库保留 365 天数据,按天切分存储文件,贴合 IoT 时序负载。- 所有事件共用一张超级表
sensor_events,子表以${hub_id}_${sensor_id}命名、首次写入时自动创建,无需预建。 event_type、value是 TDengine 保留字,SQL 中必须用反引号包裹。- 后端通过 REST 接口(默认
http://localhost:6041)读写,事件查询一律ORDER BY ts DESC,最新在前。
业务元数据持久化
设备、用户、绑定、激活码、亲友卡等业务元数据不放在 TDengine,而是持久化在服务器目录 /var/www/fanglian/data/:
| 文件 | 内容 |
|---|---|
fanglian_metadata.json |
用户、家庭组、绑定关系、设备白名单(含配对码)、激活码、亲友卡 |
| last-seen 数据 | 各 Hub 最近上报时间,用于在线状态判断 |
后端运行时有内存缓存:直接改文件必须
pm2 restart fanglian-backend才生效;通过 API 操作会自动刷新缓存并落盘。该目录务必避免被部署流程清空。
服务器与部署拓扑
生产环境单机部署,Nginx 统一入口 https://yeezytb.com:
| 路径 | 指向 | 进程 |
|---|---|---|
/fanglian |
后端 API | PM2 fanglian-backend |
/fanglian-admin |
Web 管理台 | PM2 fanglian-web |
部署纪律(都是踩过的坑):
- Web 端必须在服务器上构建:本地构建会把
NEXT_PUBLIC_API_BASE_URL固化成 localhost,线上请求全部失败。 - rsync 同步必须排除环境文件:Web 端排除
.env.local,后端排除.env、.env.local,否则--delete会删掉生产配置。 - 服务器上构建用
nohup npm run build &后台执行,再pm2 restart,避免 SSH 长连接被断。 - 完整步骤见
dev-logs/web-deploy-guide.md与scripts/deploy-web.sh。
命名与标识规范
| 对象 | 格式 | 示例 |
|---|---|---|
| 家庭组 ID | fl_hh_<6位随机> |
fl_hh_x7k9p2 |
| Hub ID | fl_<类型缩写>_<6位随机> |
fl_reed_a3m8n1、fl_fsr_b2k4p7 |
| 传感器 ID | 固定语义名 | reed_01、fsr_01、button_01 |
| 激活码 | fl_sub_… |
月卡 30 天 / 年卡 365 天 |
| 亲友卡邀请 | fl_gc_… |
微信分享卡片携带 token |
自定义名(用户称呼、家庭组名、设备名)不超过 6 个字符;Web 端优先显示自定义名,系统 ID 以灰色小字随行展示。
鉴权体系
| 调用方 | 凭据 | 说明 |
|---|---|---|
| 设备 → 后端 | 请求头 X-Hub-ID + X-Hub-Token |
token 在设备自激活时签发,存入 NVS |
| 小程序 → 后端 | Authorization: Bearer <JWT> |
wx.login 换 openid 后签发,默认 7 天 |
| Web 管理接口 | admin JWT + X-Admin-Secret |
双重校验;ADMIN_SECRET 未配置时管理接口返回 503 |
后端 .env 设 SUBSCRIPTION_REQUIRED=false 可关闭全部订阅校验(开源自部署模式):事件照常写入,查询接口恒返回会员有效,小程序不显示续费引导。
API 一览
baseURL:https://yeezytb.com/fanglian
设备侧
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/devices/activate |
设备自激活,签发 hub_token 与配对码 |
| POST | /api/v1/events |
写入传感器事件 / 心跳 |
用户侧(小程序)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/auth/wechat |
微信登录,返回 JWT |
| GET | /api/v1/me |
我的设备、家庭组与会员状态 |
| POST | /api/v1/me/activate |
激活码开通/续费会员 |
| POST | /api/v1/me/name · /api/v1/households/name · /api/v1/devices/name |
自定义命名 |
| POST | /api/v1/devices/bind |
配对码绑定(支持 preview);DELETE 解绑 |
| GET | /api/v1/today · /api/v1/timeline |
今日守护状态 / 事件时间线 |
| GET/POST | /api/v1/subscriptions |
按 Hub 查询与设置订阅通知 |
| POST/GET/DELETE | /api/v1/me/cards |
亲友卡创建、列表、撤回 |
| GET/POST | /api/v1/me/cards/invite · /api/v1/me/cards/accept |
亲友卡预览与接受 |
管理侧(Web)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/auth/admin |
管理员登录 |
| GET | /api/v1/admin/devices |
全量 Hub 状态 |
| GET | /api/v1/admin/whitelist |
设备白名单(含配对码) |
| GET | /api/v1/admin/users |
注册用户与绑定关系 |
| POST/GET | /api/v1/admin/subscription-codes |
生成 / 列出激活码 |
| POST | /api/v1/admin/subscription-codes/extend · /revoke |
会员延期 / 撤销 |