数据模型概览
YunStack-IoT 的数据模型围绕 产品 → 设备 → 遥测数据 构建,并包含用户、角色、权限、固件等支撑实体。
1. 核心实体关系
2. 核心实体说明
Product(产品)
| 字段 | 类型 | 说明 |
|---|---|---|
id | string (PK) | 产品标识,如 p_relay_4ch |
name | string | 产品名称 |
description | string | 产品描述 |
image | string? | 产品图片 URL,通过文件服务上传 |
thingModel | jsonb | 物模型 JSON(属性/事件/服务定义,见 05.物模型设计规范) |
createdAt | timestamp | 创建时间 |
Device(设备)
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid (PK) | 逻辑 ID,平台分配 |
physicalId | string | 物理 ID(WiFi MAC),设备认证标识 |
productId | string (FK) | 所属产品 |
name | string | 设备名称 |
secret | string | 设备密钥(加密存储) |
shadow | jsonb | 设备影子权威数据(desired + reported + version + updatedAt) |
roomId | int? (FK) | 所属房间(可选,为空时属于组织根目录) |
firmwareVersion | string | 当前固件版本号 |
isOnline | boolean | 在线状态(从 Redis 同步,非实时字段) |
createdAt | timestamp | 注册时间 |
lastOnlineAt | timestamp | 最近上线时间 |
影子存储说明: PostgreSQL 中的
shadow字段为权威数据,用于 API 查询和审计。运行时另有一份 Redis Hash 热缓存用于 WebSocket 实时推送。两级存储的设计原因和同步策略详见 12.缓存策略 第 3 节。
User(用户)
| 字段 | 类型 | 说明 |
|---|---|---|
id | int (PK) | 用户 ID |
username | string | 用户名 |
password | string | bcrypt 加密 |
nickname | string | 昵称 |
status | int | 状态:1 启用,0 禁用 |
createdAt | timestamp | 注册时间 |
Role(角色)
| 字段 | 类型 | 说明 |
|---|---|---|
id | int (PK) | 角色 ID |
name | string | 角色名称 |
code | string | 角色编码(如 admin、user) |
sort | int | 排序号 |
RBAC 权限模型
User ←(多对多)→ Role ←(多对多)→ Permission。权限分为两类:
| 类型 | 说明 | 示例 |
|---|---|---|
| 菜单权限 | 控制前端路由可见性 | system:user:list、device:detail |
| 接口权限 | 控制后端 API 访问 | NestJS Guard 根据角色 code 判断 |
Firmware(固件版本)
| 字段 | 类型 | 说明 |
|---|---|---|
id | string (PK) | 固件版本 ID |
productId | string (FK) | 适用产品 |
version | string | 语义化版本号(如 1.1.0) |
url | string | OSS 下载 URL |
size | int | 文件大小(字节) |
md5 | string | MD5 校验值 |
changelog | text | 更新日志 |
uploadedAt | timestamp | 上传时间 |
DeviceLog(设备日志)
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid (PK) | 日志 ID |
deviceId | uuid (FK) | 关联设备 |
type | enum | 事件类型:online、offline、report、control、ota |
content | jsonb | 日志内容 |
保留 180 天,用于审计追溯。
OtaTask(升级任务)
| 字段 | 类型 | 说明 |
|---|---|---|
id | string (PK) | 任务 ID |
firmwareId | string (FK) | 目标固件版本 |
deviceId | uuid (FK) | 目标设备 |
status | enum | 状态:pending、notified、downloading、installing、success、failed |
progress | int | 下载进度 0-100 |
createdAt | timestamp | 创建时间 |
completedAt | timestamp | 完成时间 |
Organization(组织)
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid (PK) | 组织 ID |
name | string | 组织名称(如"家"、"工厂A") |
description | string? | 组织描述 |
ownerId | string (FK) | 创建者 |
createdAt | timestamp | 创建时间 |
一个用户可创建多个组织,一个组织下包含多个房间。
命名说明: 实体命名为 Organization 而非 Family,使其同时适用于家居(组织="家")和工业(组织="工厂")场景。前端页面名称可面向不同用户群体调整,但后端数据模型保持通用。
Room(房间)
| 字段 | 类型 | 说明 |
|---|---|---|
id | int (PK) | 房间 ID |
name | string | 房间名称(如"客厅"、"生产线1") |
organizationId | uuid (FK) | 所属组织 |
sort | int | 排序号 |
createdAt | timestamp | 创建时间 |
设备通过 roomId 归属到某个房间,未分配房间的设备属于组织根目录。
3. 双 ID 体系
每个设备同时拥有两个标识:
| ID | 来源 | 用途 |
|---|---|---|
| physicalId(物理ID) | WiFi MAC 地址 | MQTT 通信认证、固件标识 |
| id(逻辑ID) | 平台分配 UUID | 业务层引用、API 操作 |
物理 ID 在设备注册时上报,平台返回逻辑 ID。后续所有 MQTT 通信和 API 调用均使用逻辑 ID,避免暴露设备物理地址。
4. 存储分层
| 数据类型 | 存储 | 说明 |
|---|---|---|
| 业务数据 | PostgreSQL | 产品、设备、用户、角色、固件版本等关系型数据 |
| 设备影子(权威) | PostgreSQL (JSONB) | 设备期望状态与上报状态的完整 JSON,用于 API 查询和审计 |
| 设备影子(缓存) | Redis (Hash) | 最近一次属性值的实时缓存,用于 WebSocket 推送,30 分钟 TTL |
| 在线状态 | Redis (String) | 实时在线/离线标记,120 秒 TTL |
| 指令状态 | Redis (String) | 待响应指令的追踪,30 秒 TTL |
| 遥测数据 | InfluxDB | 设备上报的时序数据(温度、湿度、开关状态等) |
| 设备日志 | PostgreSQL | 上下线、控制、OTA 事件日志,保留 180 天 |
影子两级存储的设计原因和同步策略详见 12.缓存策略。
5. 数据保留策略
| 数据类型 | 保留时长 | 存储 |
|---|---|---|
| 遥测原始数据 | 90 天 | InfluxDB |
| 遥测 1 分钟聚合 | 1 年 | InfluxDB(降采样) |
| 遥测 1 小时聚合 | 永久 | InfluxDB(降采样) |
| 设备日志 | 180 天 | PostgreSQL |
| 设备影子历史 | 30 天 | PostgreSQL |
| 操作审计日志 | 永久 | PostgreSQL |
完整的 DDL 和 Prisma Schema 详见 数据库设计。
