Skip to content

开发行动路线

本文档定义 YunStack-IoT 从当前状态到 v0.2 完成的开发步骤。每个阶段独立可测,按顺序执行,全程使用设备模拟器验证,无需真实硬件。


当前状态

已完成说明
NestJS 框架模块化架构、全局中间件、异常过滤器
JWT 认证 + RBACPassport + 角色权限守卫
用户管理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:

对应文档说明
Permission04.数据模型概览.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/updatedTimecreatedAt/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
  • [ ] 创建 ProductServicecreate / findAll / findById / update / delete
  • [ ] 创建 ProductController:REST 端点(见 API 规范)
端点方法说明
/api/v1/productsGET产品列表(分页)
/api/v1/productsPOST创建产品
/api/v1/products/:idGET产品详情
/api/v1/products/:idPUT更新产品
/api/v1/products/:idDELETE删除产品
/api/v1/products/:id/thing-modelGET获取物模型 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_URLMQTT_USERNAMEMQTT_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
  • [ ] 创建 DeviceServicecreate / findAll / findById / update / delete
  • [ ] 创建 DeviceController:REST 端点
端点方法说明
/api/v1/devicesGET设备列表(分页,搜索,按产品/状态筛选)
/api/v1/devicesPOST手动注册设备
/api/v1/devices/:idGET设备详情(含影子)
/api/v1/devices/:idPUT更新设备信息
/api/v1/devices/:idDELETE删除设备
/api/v1/devices/:id/namePATCH修改设备名称

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_URLINFLUX_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 新增 organizationIdroomId 筛选参数

8.3 测试

  • [ ] 创建组织 → 创建房间 → 设备分配到房间 → 按房间查询返回正确

阶段验收:

  • 组织和房间 API 完整
  • 设备可按组织/房间筛选

阶段 9:Web 后台 IoT 页面

目标:补全 Web 后台的 IoT 业务页面。

按照 界面与交互设计.md技术实现.md 分页开发:

页面路由关键功能
Dashboard IoT 概览/dashboard设备总数/在线率/告警数卡片 + 趋势图
产品列表/productsCRUD + 物模型编辑器
设备列表/devices分页/搜索/筛选 + WebSocket 在线状态
设备详情/devices/:id影子/控制面板/日志三 Tab
数据看板/devices/:id/telemetryECharts 时序图 + 导出 CSV
固件管理/firmware上传/版本列表
升级任务/firmware/upgrade创建/进度追踪

每页开发步骤

  1. 路由配置 + 页面骨架
  2. API 对接 + 数据加载
  3. 状态处理(loading/空/错误)
  4. WebSocket 实时更新
  5. 交互完善(弹窗/确认/表单校验)
  6. 权限适配(admin vs user)

阶段验收:

  • 启动后端 + 模拟器,所有页面数据实时展示
  • 设备控制按钮下发指令后状态正常更新
  • 数据看板图表响应时间范围切换

阶段 10:小程序 IoT 页面

目标:补全小程序的设备列表、控制、配网、家庭管理页面。

页面路径关键功能
设备列表/pages/devices/index卡片列表 + WebSocket 在线状态
设备详情/pages/devices/detail控制组件(开关/滑块/数值)+ 实时更新
设备配网/pages/devices/configBLE 配网五步骤
数据历史/pages/devices/telemetry时序图 + 时间切换
设备设置/pages/devices/settings命名/信息/解绑
家庭管理/pages/mine/family组织/房间创建和切换

每页开发步骤

  1. 页面路由注册 + 导航配置
  2. API 对接 + 数据渲染
  3. WebSocket 连接 + 实时更新
  4. 状态处理(loading/空/错误)
  5. 交互完善 + 动画

阶段验收:

  • 小程序可查看模拟器上报的设备数据
  • 开关/滑块控制后状态实时反馈
  • 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: <阶段描述>