开发行动路线
本文档定义 YunStack-IoT 从当前状态到 v0.2 完成的开发步骤。每个阶段独立可测,按顺序执行,全程使用设备模拟器验证,无需真实硬件。
当前状态
| 已完成 | 说明 |
|---|---|
| NestJS 框架 | 模块化架构、全局中间件、异常过滤器 |
| JWT 认证 + RBAC | Passport + 角色权限守卫 |
| 用户管理 | CRUD + 状态管理 + 密码重置 |
| 角色管理 | CRUD + 权限值绑定 |
| 文件上传 | 本地存储 + OSS |
| 系统管理 | 菜单/部门/字典/配置/通知/日志 |
| Web 后台 | 系统管理全套页面 |
| 小程序 | 登录/注册/个人中心/设置 |
| MQTT 客户端 | mqtt.js 基础连接 + 通配符订阅 |
| 待补全 | 说明 |
|---|---|
| 数据库 Schema | 现有仅 User/Role 两表,需扩展至 12 表 |
| IoT 业务模块 | 产品/设备/固件/OTA/组织管理均未实现 |
初期 MQTT 简化策略:后端仅作为 MQTT 客户端订阅 Topic 接收消息。EMQX PostgreSQL 认证和在线状态(属性上报驱动 + Redis TTL)在后续阶段补全。
阶段 1:数据库标准化
目标:将 Prisma Schema 对齐文档设计,为所有后续模块打好数据基础。
1.1 新增数据库表
在现有 schema.prisma 基础上,新建以下 Prisma Model:
| 表 | 对应文档 | 说明 |
|---|---|---|
Permission | 04.数据模型概览.md | 权限定义(code/type/menu/api) |
UserRole | 同上 | 用户↔角色多对多关联表 |
RolePermission | 同上 | 角色↔权限多对多关联表 |
Product | 同上 | 产品定义 + 物模型 JSONB |
Device | 同上 | 设备实例(双 ID + 影子 + 在线状态) |
DeviceLog | 同上 | 设备事件日志 |
参考文档:Docs/03.软件部分/01.后端/03.数据库设计.md
1.2 规范化现有表
- [ ] User 表:去掉
isDeleted/isDisabled,统一为status(1=启用,0=禁用) - [ ] Role 表:去掉
value字段,改为code+sort,去掉isDeleted/isDisabled - [ ] 所有 ID 从
cuid()改为uuid()(Device 表用 UUID v4) - [ ] 时间字段命名统一:
createdTime/updatedTime→createdAt/updatedAt
1.3 生成并迁移
- [ ]
pnpm db:generate生成 Prisma Client - [ ]
pnpm db:migrate创建迁移文件 - [ ] 更新 seed 脚本:插入 admin 角色 + 基础权限 + 示例产品
- [ ] 验证:
pnpm prisma db seed无错误
1.4 类型规范
- [ ]
tsconfig.json已设为"strict": true - [ ] DTO 文件统一使用
class-validator装饰器 - [ ] ESLint
no-explicit-any: error
阶段验收:
- 所有表创建成功,
db:generate无类型错误 - seed 数据可插入
- 现有用户/角色 API 正常(需适配字段变更)
阶段 2:产品管理 ProductModule
目标:实现产品 CRUD + 物模型 JSON 校验。
2.1 Product CRUD
- [ ] 创建
src/modules/product/product.module.ts - [ ] 创建
ProductService:create / findAll / findById / update / delete - [ ] 创建
ProductController:REST 端点(见 API 规范)
| 端点 | 方法 | 说明 |
|---|---|---|
/api/v1/products | GET | 产品列表(分页) |
/api/v1/products | POST | 创建产品 |
/api/v1/products/:id | GET | 产品详情 |
/api/v1/products/:id | PUT | 更新产品 |
/api/v1/products/:id | DELETE | 删除产品 |
/api/v1/products/:id/thing-model | GET | 获取物模型 JSON |
2.2 物模型校验
- [ ] 创建
ThingModelValidator:校验 JSON 结构(version/deviceType/properties/events/services 必填字段) - [ ] 属性校验:id/name/type 必填,type 必须在允许的枚举值内
- [ ] 校验失败返回业务错误码
A0201
2.3 测试
- [ ]
product.service.spec.ts:CRUD 全部方法 + 物模型校验 - [ ] 使用 Postman/MQTTX 手动测试所有端点
阶段验收:
- 创建产品 "四路继电器"(p_relay_4ch)和 "环境传感器"(p_env_sensor),物模型 JSON 通过校验
- Service 测试覆盖率 ≥ 80%
阶段 3:MQTT 基础通信
目标:后端连接 EMQX,订阅设备 Topic,收到消息后打印日志。初期不做 EMQX 认证配置(PostgreSQL 认证在阶段 11 补全)。
3.1 MQTT 客户端连接
- [ ] 创建
src/gateways/mqtt/mqtt.module.ts— 动态模块,onModuleInit连接 EMQX - [ ] 连接参数从环境变量读取(
MQTT_BROKER_URL、MQTT_USERNAME、MQTT_PASSWORD) - [ ] 断线重连:指数退避(1s→2s→4s→最大30s)
- [ ] 连接成功/断开日志
3.2 Topic 订阅
- [ ] 后端订阅以下通配符 Topic(使用共享订阅前缀
$share/group1/):
$share/group1/sys/+/+/thing/event/property/post ← 属性上报
$share/group1/sys/+/+/thing/service/+/set_reply ← 控制回复- [ ] 消息到达 → 解析 Topic → 提取 productId + deviceId → 打印日志(内容 + 时间戳)
3.3 简化测试
- [ ] 启动 EMQX(docker compose up emqx)
- [ ] 启动设备模拟器(
pnpm start) - [ ] 后端日志能打印出模拟器上报的属性数据
- [ ] 使用 MQTTX 手动发布消息,后端日志正确输出
阶段验收:
- 后端启动后成功连接 EMQX,日志显示"MQTT 已连接"
- 模拟器上报数据时,后端日志打印完整的 Topic + Payload
- 后端重启后自动重连并重新订阅
阶段 4:设备管理 DeviceModule
目标:实现设备 CRUD、手动注册、在线状态、影子存储。
4.1 Device CRUD
- [ ] 创建
src/modules/device/device.module.ts - [ ] 创建
DeviceService:create / findAll / findById / update / delete - [ ] 创建
DeviceController:REST 端点
| 端点 | 方法 | 说明 |
|---|---|---|
/api/v1/devices | GET | 设备列表(分页,搜索,按产品/状态筛选) |
/api/v1/devices | POST | 手动注册设备 |
/api/v1/devices/:id | GET | 设备详情(含影子) |
/api/v1/devices/:id | PUT | 更新设备信息 |
/api/v1/devices/:id | DELETE | 删除设备 |
/api/v1/devices/:id/name | PATCH | 修改设备名称 |
4.2 MQTT → 设备状态联动
- [ ] MQTT 属性上报消息到达 →
DeviceService.handlePropertyPost(productId, deviceId, params) - [ ] 更新设备
lastOnlineAt+ 标记在线(isOnline: true) - [ ] 写入设备影子:Redis Hash 热缓存 + PostgreSQL JSONB 权威数据
- [ ] 不使用 Webhook 判定离线——简化为:超过 120 秒无上报自动标记离线(定时任务或在读取时判断)
4.3 手动注册
- [ ]
POST /api/v1/devices— 输入 productId + name + physicalId - [ ] 后端分配逻辑 ID(UUID v4),生成 DeviceSecret(无需 EMQX 认证,预留)
- [ ] 返回设备完整信息
4.4 测试
- [ ]
device.service.spec.ts:CRUD 全方法 + 影子更新逻辑 - [ ] 启动模拟器 → 设备列表 API 能查询到模拟器数据
阶段验收:
- 手动创建设备 → 模拟器用对应 physicalId 上报 → 设备详情 API 返回最新影子数据
- 设备列表 API 分页/搜索/筛选正常
阶段 5:设备控制 + WebSocket 推送
目标:用户通过 API 下发控制指令,设备响应后前端实时收到反馈。
5.1 设备控制
- [ ]
POST /api/v1/devices/:id/control— 接收{ params }→ - [ ]
MqttService.publish()发布到sys/{pid}/{did}/thing/service/property/set - [ ] 记录 msgId → 等待 set_reply(30 秒超时)
- [ ] 收到 set_reply → 更新指令状态 → 返回成功
- [ ] 超时 → 返回错误码 A0104
5.2 WebSocket 实时推送
- [ ] 创建
src/gateways/websocket/— 基于@nestjs/websockets(Socket.IO) - [ ] JWT 认证(连接时验证 Token)
- [ ] 房间机制:
socket.join(deviceId)订阅设备,io.to(deviceId).emit()推送 - [ ] 推送事件:
device.status— 设备上下线device.shadow— 设备属性变更
5.3 MQTT → WebSocket 桥接
- [ ] 属性上报 → 更新影子 → 查路由表获取订阅用户 → WebSocket 推送
- [ ] 控制回复 → 更新指令状态 → WebSocket 推送执行结果
5.4 测试
- [ ]
device.service.spec.ts:控制指令下发 + 超时处理 - [ ] 浏览器连接 WebSocket,模拟器上报数据后页面实时更新
- [ ] Postman 调用 control 接口,模拟器收到指令并回复
阶段验收:
- Postman 控制继电器 → 模拟器收到指令并回复 → API 返回成功
- 模拟器上报数据 → 浏览器 WebSocket 收到 device.shadow 推送
- 指令超时 30s 返回 A0104
阶段 6:InfluxDB 时序数据
目标:设备属性写入 InfluxDB,API 支持历史查询。
6.1 InfluxDB 集成
- [ ] 创建
InfluxModule— 初始化 InfluxDB Client(环境变量INFLUX_URL、INFLUX_TOKEN) - [ ] 属性上报到达 → 异步写入 InfluxDB(不阻塞影子更新和推送路径)
- [ ] Measurement:
device_properties,Tag: product_id/device_id/property,Field: value
6.2 历史查询 API
- [ ]
GET /api/v1/devices/:id/telemetry?start=&end=&keys= - [ ] InfluxDB Flux 查询 → 返回时序数组
[{time, prop1, prop2}, ...] - [ ] 支持多属性叠加查询
6.3 降采样 Task
- [ ] 配置 InfluxDB Task:原始数据 90 天,1 分钟聚合 1 年,1 小时聚合永久
6.4 测试
- [ ] 启动模拟器跑 10 分钟 → 查询历史 API 返回数据
- [ ] 多属性查询返回正确结构
- [ ] 无数据时返回空数组
阶段验收:
- 历史查询 API 返回时序数据,前端可直接绑定 ECharts
- 降采样 Task 在 InfluxDB Dashboard 可见
阶段 7:OTA 固件升级
目标:固件上传、版本管理、升级任务创建和进度追踪。
7.1 数据库迁移
- [ ] 新增 Firmware、OtaTask 表(Prisma Schema + 迁移)
7.2 FirmwareModule
- [ ] 固件上传:校验 .bin 文件 → 上传 OSS → 记录版本信息
- [ ] 版本列表:按产品筛选 + 分页
- [ ] 预签名 URL 生成(设备下载用,24h TTL)
7.3 OtaModule
- [ ] 创建升级任务:选择固件 + 目标设备 → 批量创建 OtaTask
- [ ] 逐批下发 MQTT 通知(
thing/ota/upgrade,每批 100 台,间隔 30s) - [ ] 进度追踪:监听
thing/ota/progress→ 更新 OtaTask → WebSocket 推送 - [ ] 离线补发:设备上线时检查未完成任务
7.4 测试
- [ ] 上传固件 → 创建任务 → 模拟器收到 OTA 通知
- [ ] 模拟器上报进度 → 升级任务详情进度条更新
阶段验收:
- 固件上传/列表/删除正常
- 升级任务从创建到完成全流程可追踪
阶段 8:组织管理 OrgModule
目标:组织/房间管理,设备归属分组。
8.1 数据库迁移
- [ ] 新增 Organization、Room 表,Device 表新增 roomId 字段
8.2 OrgModule
- [ ] Organization CRUD:创建/列表/编辑/删除
- [ ] Room CRUD:在组织下创建/列表/编辑/删除房间
- [ ] 设备归属:修改设备 roomId,支持按组织/房间查询设备列表
- [ ] 设备列表 API 新增
organizationId和roomId筛选参数
8.3 测试
- [ ] 创建组织 → 创建房间 → 设备分配到房间 → 按房间查询返回正确
阶段验收:
- 组织和房间 API 完整
- 设备可按组织/房间筛选
阶段 9:Web 后台 IoT 页面
目标:补全 Web 后台的 IoT 业务页面。
按照 界面与交互设计.md 和 技术实现.md 分页开发:
| 页面 | 路由 | 关键功能 |
|---|---|---|
| Dashboard IoT 概览 | /dashboard | 设备总数/在线率/告警数卡片 + 趋势图 |
| 产品列表 | /products | CRUD + 物模型编辑器 |
| 设备列表 | /devices | 分页/搜索/筛选 + WebSocket 在线状态 |
| 设备详情 | /devices/:id | 影子/控制面板/日志三 Tab |
| 数据看板 | /devices/:id/telemetry | ECharts 时序图 + 导出 CSV |
| 固件管理 | /firmware | 上传/版本列表 |
| 升级任务 | /firmware/upgrade | 创建/进度追踪 |
每页开发步骤
- 路由配置 + 页面骨架
- API 对接 + 数据加载
- 状态处理(loading/空/错误)
- WebSocket 实时更新
- 交互完善(弹窗/确认/表单校验)
- 权限适配(admin vs user)
阶段验收:
- 启动后端 + 模拟器,所有页面数据实时展示
- 设备控制按钮下发指令后状态正常更新
- 数据看板图表响应时间范围切换
阶段 10:小程序 IoT 页面
目标:补全小程序的设备列表、控制、配网、家庭管理页面。
| 页面 | 路径 | 关键功能 |
|---|---|---|
| 设备列表 | /pages/devices/index | 卡片列表 + WebSocket 在线状态 |
| 设备详情 | /pages/devices/detail | 控制组件(开关/滑块/数值)+ 实时更新 |
| 设备配网 | /pages/devices/config | BLE 配网五步骤 |
| 数据历史 | /pages/devices/telemetry | 时序图 + 时间切换 |
| 设备设置 | /pages/devices/settings | 命名/信息/解绑 |
| 家庭管理 | /pages/mine/family | 组织/房间创建和切换 |
每页开发步骤
- 页面路由注册 + 导航配置
- API 对接 + 数据渲染
- WebSocket 连接 + 实时更新
- 状态处理(loading/空/错误)
- 交互完善 + 动画
阶段验收:
- 小程序可查看模拟器上报的设备数据
- 开关/滑块控制后状态实时反馈
- BLE 配网流程完整可走通
阶段 11:EMQX PostgreSQL 认证配置(后置)
目标:补全初期跳过的安全机制——配置 EMQX 直连 PostgreSQL 完成设备认证。
11.1 EMQX PostgreSQL 认证
- [ ] 在 EMQX Dashboard 中配置 PostgreSQL 认证插件,连接业务数据库
- [ ] 配置认证查询 SQL:根据 ClientId + Username + Password 查询
device表验证凭证 - [ ] 配置密码加密方式(如 bcrypt/sha256),确保与数据库存储格式一致
- [ ] 验证:设备使用无效凭证连接时被 EMQX 拒绝
11.2 EMQX 内置 ACL
- [ ] 在 EMQX Dashboard 中配置内置 ACL 规则
- [ ] 设备只能 Pub/Sub 自身 Topic(
sys/{pid}/{did}/#),拒绝跨设备访问 - [ ] 后端管理账号拥有全部 Topic 的 Pub/Sub 权限
- [ ] 验证:设备 A 尝试订阅设备 B 的 Topic 被 ACL 拒绝
11.3 端到端验证
- [ ] 启动完整环境(EMQX + PostgreSQL + 后端 + 模拟器)
- [ ] 设备使用正确凭证连接 → EMQX 认证通过 → 连接成功
- [ ] 设备使用错误凭证连接 → EMQX 拒绝连接
- [ ] 设备属性上报 → 后端收到并更新在线状态 + 设备影子
阶段验收:
- 设备认证完全由 EMQX PostgreSQL 插件完成,后端无需 HTTP Auth 回调
- ACL 规则生效,设备权限隔离正确
- 设备上下线通过属性上报驱动,Redis TTL 120s 自动判定离线
并行策略
- 阶段 1~3 必须顺序(数据库 → 产品 → MQTT 基础)
- 阶段 4~5 可部分重叠(设备管理 ← → WebSocket)
- 阶段 6 可与 5 并行(InfluxDB 不阻塞控制流)
- 阶段 7 可独立开发(OTA 是独立模块)
- 阶段 8 可独立开发(组织管理与核心链路耦合低)
- 阶段 9~10 依赖 4~6 完成(前端需要后端 API)
- 阶段 11 最后补全(不影响功能性开发)
每个阶段的提交规范
- 一个阶段一个分支(如
phase-2-product) - Service + Controller + DTO + spec 测试 同步提交
- 通过
pnpm test全部测试后合并 - 提交信息格式:
feat: <阶段描述>
