docs: 基于源码分析重建 IoT 领域技术文档(8 篇)

删除旧的规划性文档(01/02/03),替换为基于 viewsh-module-iot 模块
419 个 Java 文件全面分析生成的技术现状文档,覆盖:
- 模块架构全景、数据流、存储架构、Redis Key 清单
- 设备接入网关(MQTT/EMQX/HTTP/TCP + 7 种编解码器)
- 产品与设备管理(CRUD/状态机/认证/TDengine 双写)
- 物模型管理(9 种数据类型体系、TSL、DDL 联动)
- 规则引擎(数据转发/场景联动/数据清洗三链路)
- 告警与 OTA 升级(状态机、Quartz 调度)
- 消息总线与集成事件(Local/Redis/RocketMQ 三实现)
- API 契约与枚举汇总(5 Feign 接口、30+ 枚举)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-04-09 23:20:31 +08:00
parent a1c34b4c83
commit c71e8d31cf
12 changed files with 1797 additions and 140 deletions

View File

@@ -18,15 +18,25 @@ IoT 领域不再是停留在 PPT 上的规划,而是当前正在落地的战
- **北向(面向业务)**:必须干净。所有向 Ops 抛出的事件、所有存储到数据库的属性,必须是经过「物模型」标准转换后的统一格式。
- **绝对禁止**:在工单、保洁等业务 Service 里直接引入诸如 `MqttClient`,或者直接去 parse 设备的原始报文。
## 三、核心文档阅读指引
## 三、技术文档(源码分析)
请按顺序阅读以下标准设计
以下文档基于 `viewsh-module-iot` 模块源码419 个 Java 文件)全面分析生成,覆盖所有功能模块的实现细节
1. **[[01-设备接入与控制主链路.md]]**
- 关注网络层和会话层。定义了设备从开机到上线、数据上报和接收指令的全过程Mermaid 时序图)
2. **[[02-物模型标准设计.md]]**
- 关注数据表示层。定义了“属性Property”、“事件Event”和“服务Service”的三要素规范以及设备影子的机制
3. **[[03-规则引擎与联动策略.md]]**
- 关注业务逻辑层。说明了大量传感器数据如何经过防抖、过滤,最终安全地跨域调用 Ops 接口生成维修/应急工单
1. **[[01-IoT模块技术现状全景.md]]**
- 模块架构总览、数据流全景、存储架构、Redis Key 全量清单、定时任务、协议矩阵
2. **[[02-设备接入网关详解.md]]**
- MQTT/EMQX/HTTP/TCP 四协议接入流程、7 种编解码器报文格式、认证机制、JT808 解析
3. **[[03-产品与设备管理详解.md]]**
- 产品 CRUD/发布流程、设备 CRUD/状态机/分组/认证、属性双写TDengine+Redis、消息日志
4. **[[04-物模型管理详解.md]]**
- 属性/事件/服务三要素、9 种数据类型体系、TSL 结构、与 TDengine DDL 联动。
5. **[[05-规则引擎详解.md]]**
- 数据转发9 种 Sink、场景联动SpEL 条件引擎)、数据清洗(保洁业务 4 处理器)。
6. **[[06-告警与OTA详解.md]]**
- 告警配置/记录/自动恢复、OTA 固件/任务/记录状态机、Quartz 独立调度器。
7. **[[07-消息总线与集成事件详解.md]]**
- 三种消息总线实现、统一消息体、RocketMQ 集成事件、保洁/轨迹事件。
8. **[[08-API契约与枚举汇总.md]]**
- 5 个 Feign RPC 接口、9 个 DTO、全量枚举清单、字典常量。
> ⚠️ 注意:本目录下的规范即代表开发契约,开发进行代码结构设计和数据库表设计时,必须与之对齐。
> ⚠️ 注意:本目录下的文档即代表开发契约,开发进行代码结构设计和数据库表设计时,必须与之对齐。

View File

@@ -0,0 +1,232 @@
# 01-IoT 模块技术现状全景
> **基于源码分析** | 模块路径:`viewsh-module-iot` | 419 个 Java 文件 | 4 个子模块
> 分析日期2026-04-09
---
## 一、模块架构总览
```
viewsh-module-iot/
├── viewsh-module-iot-api/ # [契约层] DTO、枚举、Feign RPC 接口
├── viewsh-module-iot-core/ # [核心层] 消息总线、集成事件、通用工具
├── viewsh-module-iot-server/ # [业务层] 设备/产品/物模型/规则/告警/OTA
└── viewsh-module-iot-gateway/ # [接入层] 多协议网关、编解码器
```
### 1.1 模块依赖关系
```
viewsh-module-iot-gateway ──┐
├── viewsh-module-iot-core
viewsh-module-iot-server ───┘ └── viewsh-module-iot-api
└── viewsh-common框架公共
```
- **api** → 纯契约,不含业务实现;被其他微服务(如 Ops通过 Feign 引用
- **core** → 基础库 JARgateway 和 server 共用;定义消息总线、集成事件、设备 API 接口
- **server** → 核心业务微服务(`IoTServerApplication`),对外提供 REST/RPC 接口
- **gateway** → 设备接入网关(`IotGatewayServerApplication`),独立部署,通过消息总线与 server 通信
### 1.2 核心技术栈
| 领域 | 技术选型 |
|------|----------|
| 框架 | Spring Boot + Spring CloudFeign |
| 消息总线 | Local / Redis Stream / RocketMQ三选一配置切换 |
| 时序数据库 | TDengine设备属性 + 消息日志) |
| 关系数据库 | MySQL业务实体 CRUD |
| 缓存 | RedisSpring Cache + 手动 Hash/ZSet/String |
| 协议接入 | Vert.xMQTT Broker / HTTP / TCP Server |
| MQTT 桥接 | EMQX外部 Broker 模式) |
| 定时任务 | XXL-Job常规任务 + Quartz场景规则动态定时 |
| 编解码 | Alink / JT808 / Camera3D11 / HenghuaD5 / PeopleCounter / TCP Binary/JSON |
### 1.3 功能域划分
| 功能域 | 说明 | 详细文档 |
|--------|------|----------|
| 设备接入网关 | MQTT/EMQX/HTTP/TCP 四协议 + 7 种编解码器 | [[02-设备接入网关详解]] |
| 产品与设备管理 | 产品 CRUD/分类、设备 CRUD/分组/状态/认证 | [[03-产品与设备管理详解]] |
| 物模型管理 | 属性/事件/服务定义、数据类型体系、TSL | [[04-物模型管理详解]] |
| 规则引擎 | 数据转发 + 场景联动 + 数据清洗三链路 | [[05-规则引擎详解]] |
| 告警管理 | 告警配置、告警记录、自动触发/恢复 | [[06-告警与OTA详解]] |
| OTA 升级 | 固件管理、升级任务、设备记录追踪 | [[06-告警与OTA详解]] |
| 消息总线与集成事件 | 内部消息传递 + 跨模块 RocketMQ 事件 | [[07-消息总线与集成事件详解]] |
| API 契约与枚举 | Feign RPC 接口、DTO、全量枚举 | [[08-API契约与枚举汇总]] |
---
## 二、数据流全景
### 2.1 上行链路(设备 → 业务)
```
设备 ─→ [Gateway] 协议接收
↓ 编解码器 decode
IotDeviceMessage统一消息体
↓ IotDeviceMessageProducer
消息总线Topic: iot_device_message
┌───────────┼───────────────┬──────────────────┐
↓ ↓ ↓ ↓
[设备消费者] [场景规则] [数据转发] [清洗规则]
状态更新 触发匹配 规则匹配 保洁业务
属性存储 条件判断 Sink 投递 客流/Beacon
消息日志 动作执行 HTTP/MQ/Redis 轨迹检测
↓ ↓ ↓ ↓
TDengine 设备控制 外部系统 RocketMQ→Ops
Redis 告警触发
```
### 2.2 下行链路(业务 → 设备)
```
业务层发送 IotDeviceMessage
↓ Redis 查 serverId设备连接在哪台网关
↓ IotDeviceMessageProducer.sendDeviceMessageToGateway(serverId, msg)
消息总线Topic: iot_device_message_{serverId}
↓ DownstreamSubscriber 消费
[Gateway] 编解码器 encode → 推送至设备
```
### 2.3 消息消费组隔离
同一 Topic`iot_device_message`)下四个独立消费组,互不阻塞:
| 消费组名 | 处理器 | 职责 |
|---------|--------|------|
| `iot_device_message_consumer` | IotDeviceMessageSubscriber | 设备状态/属性/消息日志 |
| `iot_rule_consumer` | IotSceneRuleMessageHandler | 场景联动规则匹配执行 |
| `iot_data_rule_consumer` | IotDataRuleMessageHandler | 数据转发规则匹配投递 |
| `iot_clean_rule_consumer` | IotCleanRuleMessageHandler | 保洁业务清洗规则 |
---
## 三、存储架构
### 3.1 MySQL 表(业务实体)
| 表名 | 说明 | 主要字段 |
|------|------|----------|
| `iot_product_category` | 产品分类 | name, sort, status |
| `iot_product` | 产品 | name, productKey(唯一), categoryId, deviceType, codecType, authType |
| `iot_device` | 设备 | deviceName, productId, productKey, state, deviceSecret, groupIds(JSON) |
| `iot_device_group` | 设备分组 | name, status |
| `iot_thing_model` | 物模型定义 | productId, identifier, type, property/service/event(JSON) |
| `iot_data_rule` | 数据转发规则 | name, status, sourceConfigs(JSON), sinkIds(JSON) |
| `iot_data_sink` | 数据转发目的 | name, type, config(JSON 多态) |
| `iot_scene_rule` | 场景联动规则 | name, status, triggers(JSON), actions(JSON) |
| `iot_alert_config` | 告警配置 | name, level, sceneRuleIds, receiveUserIds, receiveTypes |
| `iot_alert_record` | 告警记录 | configId, sceneRuleId, deviceId, deviceMessage, processStatus |
| `iot_ota_firmware` | OTA 固件 | name, version, productId, fileUrl, fileSize, fileDigestValue |
| `iot_ota_task` | OTA 升级任务 | firmwareId, status, deviceScope, deviceTotalCount |
| `iot_ota_task_record` | OTA 升级记录 | firmwareId, taskId, deviceId, status, progress |
### 3.2 TDengine 超级表(时序数据)
| 超级表 | TAG | 命名规则 | 说明 |
|--------|-----|---------|------|
| `device_message` | device_id | 子表: `device_message_{deviceId}` | 设备消息日志(固定 schema |
| `product_property_{productId}` | device_id | 子表: `device_property_{deviceId}` | 设备属性时序(动态 schema由物模型驱动 |
### 3.3 Redis 缓存全量 Key
| Key 模式 | 类型 | 用途 | TTL |
|---------|------|------|-----|
| `iot:device::{id}` | String | 设备信息缓存Spring Cache | 框架配置 |
| `iot:device::{pk}_{dn}` | String | 设备信息缓存(按 productKey+deviceName | 框架配置 |
| `iot:product::{id}` | String | 产品信息缓存 | 框架配置 |
| `iot:thing_model_list::{productId}` | String | 物模型列表缓存 | 框架配置 |
| `iot:device_property:{deviceId}` | Hash | 设备最新属性值identifier → JSON | 常驻 |
| `iot:device_report_times` | ZSet | 设备上报时间score=时间戳) | 常驻 |
| `iot:device_server_id` | Hash | 设备所在网关 serverId | 常驻 |
| `iot:data_rule_list` | String | 数据转发规则缓存 | 框架配置 |
| `iot:data_sink::{id}` | String | 数据目的缓存 | 框架配置 |
| `iot:scene_rule_list` | String | 场景联动规则缓存 | 框架配置 |
| `iot:jt808_auth_token:{phone}` | String | JT808 鉴权码 | 30 天 |
| `iot:clean:beacon:rssi:window:{did}:{aid}` | String | 保洁 Beacon RSSI 滑动窗口 | 3600s |
| `iot:clean:signal:loss:{did}:{aid}` | Hash | 离岗信号丢失记录 | 3600s |
| `iot:clean:traffic:threshold:{did}:{aid}` | String | 客流阈值计数器 | 86400s |
| `iot:clean:traffic:daily:{did}:{date}` | Hash | 客流当日统计 | 172800s |
| `iot:clean:traffic:lastvalue:{did}` | Hash | 累计值上次值 | 604800s |
| `iot:trajectory:device:enabled:{did}` | String | 轨迹功能开关 | 3600s |
| `iot:trajectory:beacon:registry` | Hash | Beacon 注册表MAC→区域 | 3600s |
| `ops:area:{aid}:type:{type}` | String | 区域设备关联配置 | 86400s |
| `ops:device:index:{did}` | Hash | 设备反向索引 | 3600s |
---
## 四、定时任务汇总
| Job 名 | 调度方式 | 功能 |
|--------|---------|------|
| `deviceOfflineCheckJob` | XXL-Job + @TenantJob | 扫描在线设备超时15 分钟未上报)标记离线 |
| `deviceUpgradeJob` | XXL-Job + @TenantJob | 查询 PENDING 升级记录,向在线设备推送 OTA 指令 |
| `signalLossCheckJob` | XXL-Job | 扫描离岗记录,超时自动完成保洁工单 |
| `iot_scene_rule_timer_{id}` | Quartz动态 | 场景联动定时触发器CRON 表达式驱动) |
---
## 五、对外 RPC 接口Feign
IoT 模块对外暴露 5 个 Feign 接口(服务名 `iot-server`,前缀 `/rpc-api/iot`
| Feign 接口 | 方法数 | 核心能力 |
|-----------|--------|---------|
| `IotDeviceQueryApi` | 3 | 设备查询(单个/批量/列表) |
| `IotDeviceControlApi` | 3 | 设备服务调用(单个/批量)+ 客流计数器重置 |
| `IotDevicePropertyQueryApi` | 4 | 属性查询(最新/历史/批量) |
| `IotDeviceStatusQueryApi` | 3 | 设备状态(在线判断/状态详情/批量) |
| `TrajectoryStateApi` | 1 | 设备实时位置Beacon 定位) |
---
## 六、协议接入矩阵
| 协议 | 部署模式 | 上行 | 下行 | 认证方式 | 编解码器 |
|------|---------|------|------|---------|---------|
| EMQX | 桥接外部 Broker | MQTT Subscribe | MQTT Publish | EMQX HTTP 回调 | Alink |
| MQTT | 内置 Broker | Vert.x MqttServer | endpoint.publish | CONNECT 报文验证 | Alink |
| HTTP | Vert.x HTTP Server | POST 上报 | 不支持 | JWT Token / 免鉴权 | Camera3D11, HenghuaD5, PeopleCounter |
| TCP | Vert.x NetServer | Socket 读取 | Socket 写入 | 首条 auth 消息 / JT808 两步认证 | TCP JSON, TCP Binary, JT808 |
---
## 七、编解码器矩阵
| 编解码器 | 类型标识 | 报文格式 | 支持下行 | 适用设备 |
|---------|---------|---------|---------|---------|
| Alink | `Alink` | JSON阿里云标准 | 是 | 通用 MQTT 设备 |
| Camera3D11 | `CAMERA_3D11` | JSON | 否 | 3D11 单目客流计数器 |
| HenghuaD5 | `HENGHUA_D5` | URL 编码表单 | 否 | 恒华 D5 客流摄像机 |
| PeopleCounter | `PEOPLE_COUNTER` | JSON中文引号兼容 | 否 | 红外客流计数器 |
| JT808 | `JT808` | 二进制帧(交通部标准) | 是 | GPS 定位终端/工牌 |
| TCP JSON | `TCP_JSON` | JSON | 是 | 通用 TCP 设备 |
| TCP Binary | `TCP_BINARY` | 自定义二进制帧0x7E 魔术字) | 是 | 自定义嵌入式设备 |
---
## 八、错误码段分配
| 段 | 范围 | 功能 |
|----|------|------|
| iot-server | `1-050-xxx-xxx` | IoT 业务层 |
| 产品 | 001 | 产品管理 |
| 物模型 | 002 | 物模型管理 |
| 设备 | 003 | 设备管理 |
| 产品分类 | 004 | 产品分类 |
| 设备分组 | 005 | 设备分组 |
| OTA 固件 | 008 | 固件管理 |
| OTA 任务 | 008-100 | 升级任务 |
| OTA 记录 | 008-200 | 升级记录 |
| 数据规则 | 010 | 数据转发 |
| 数据桥接 | 011 | 数据目的 |
| 场景联动 | 012 | 场景规则 |
| 告警配置 | 013 | 告警配置 |
| 告警记录 | 014 | 告警记录 |
| iot-gateway | `1-051-xxx-xxx` | IoT 网关层 |
| 认证失败 | 001-000 | DEVICE_AUTH_FAIL |
| Token 过期 | 001-002 | DEVICE_TOKEN_EXPIRED |
| 设备不存在 | 002-001 | DEVICE_NOT_EXISTS |

View File

@@ -1,58 +0,0 @@
# 01-设备接入与控制主链路
在 AIOT 平台中IoT 设备接入由 `viewsh-module-iot-gateway` 统一处理,支持多种协议栈。这是物理世界数据流向业务系统的第一道关口。
## 一、支持的接入协议栈
根据底层网关编解码器Codec的实现平台当前支持以下协议的设备接入
- **MQTT**:主流通讯协议,配合 EMQX 等 Broker 进行上下行流转。
- **JT808**:部标车载终端通信协议。
- **Alink**:阿里标准物联网协议。
- **TCP**:支持自定义的 TCP Binary透传与 TCP JSON 格式。
设备的网络通信类型 (`IotNetTypeEnum`) 抽象为:`WIFI`, `CELLULAR` (蜂窝), `ETHERNET` (以太网), `OTHER`
## 二、MQTT 接入与状态流转
以最典型的 MQTT 接入为例,设备的状态流转受严格的网关路由管理:
```mermaid
sequenceDiagram
participant Device as IoT设备
participant EMQX as EMQX (Broker)
participant Gateway as module-iot-gateway
== 1. 设备鉴权与上线 ==
Device->>EMQX: CONNECT
EMQX->>Gateway: 触发 AuthEventProtocol
Gateway->>Gateway: IotEmqxAuthEventHandler 校验 Token/秘钥
Gateway-->>EMQX: 鉴权结果
EMQX-->>Device: CONNACK
Note over Gateway: 鉴权通过后,状态变更为 ONLINE(1)
== 2. 上行数据 (Upstream) ==
Device->>EMQX: PUBLISH (/up/telemetry)
EMQX->>Gateway: 推送数据
Gateway->>Gateway: IotMqttUpstreamHandler 根据 Codec 解码
Gateway->>Gateway: IotEmqxUpstreamProtocol 解析数据抛出事件
== 3. 下行指令 (Downstream) ==
Gateway->>EMQX: PUBLISH (/down/command) via IotMqttDownstreamHandler
EMQX->>Device: 投递指令
Device-->>EMQX: ACK / 结果返回
== 4. 断开与离线 ==
Device->>EMQX: DISCONNECT 或 心跳超时
EMQX->>Gateway: 触发离线事件
Note over Gateway: 状态变更为 OFFLINE(2)
```
## 三、设备生命周期 (IotDeviceStateEnum)
系统对设备的在线状态维护极其严格,分为三种终态:
1. **`INACTIVE(0)` (未激活)**:设备在平台已建档,但从未与云端建立过通信。
2. **`ONLINE(1)` (在线)**TCP 连接保持中,或心跳周期内正常。
3. **`OFFLINE(2)` (离线)**:网关层面检测到连接断开或心跳超时抛出的事件。
严禁在业务层写死轮询来判断设备在线,必须监听 `IotDeviceStateEnum` 的状态翻转事件。

View File

@@ -1,31 +0,0 @@
# 02-物模型标准设计
底层网关屏蔽了 JT808、MQTT、TCP Binary 等各种异构协议后所有推入平台核心业务流的数据必须服从统一的「物模型Thing Model」规范。
## 一、物模型三要素 (IotThingModelTypeEnum)
根据系统的定义,任何设备的物模型结构被强制约束为三大类型:
### 1. `PROPERTY(1)` (属性)
- 描述:反映设备连续的运行状态数据(如温度、电量、当前档位)。
- 上报:对应网关的方法 `DEVICE_PROPERTY_POST`
- 业务处理:存入实时库或影子库。属性的设定可通过场景规则触发(`DEVICE_PROPERTY_SET`)。
### 2. `SERVICE(2)` (服务)
- 描述:平台主动下发给设备的指令,设备执行后需要响应结果(如开阀、设置重启参数)。
- 调用:对应 `DEVICE_SERVICE_INVOKE`。这更像一种双向 RPC 调用,通常会携带参数 (`IotThingModelParamDirectionEnum`)。
### 3. `EVENT(3)` (事件)
- 描述:设备主动上报的瞬时、非连续通知(如硬件故障、越界告警)。
- 上报:对应 `DEVICE_EVENT_POST`
- 业务处理:通常直接送入规则引擎(`IotSceneRuleTriggerTypeEnum`)作为触发源。
## 二、数据消费去向 (IotDataSinkTypeEnum)
物模型解析后的数据并非只是存在 MySQL 中系统支持将数据分发到各种目标Sink供不同业务消费
- **消息队列**`ROCKETMQ(30)`, `RABBITMQ(31)`, `KAFKA(32)` —— 适合大数据量的数仓抽取。
- **高速缓存**`REDIS(21)` —— 适合大屏实时展示和设备影子。
- **协议转发**`HTTP(1)`, `TCP(2)`, `WEBSOCKET(3)`, `MQTT(10)` —— 适合外部系统对接。
- **持久化**`DATABASE(20)` —— 适合历史轨迹和报表查询。
开发新业务时,请通过配置 Sink 来订阅数据,**不要在核心解析链路里直接写死 DB 插入代码**。

View File

@@ -0,0 +1,298 @@
# 02-设备接入网关详解
> 模块路径:`viewsh-module-iot-gateway` | 启动类:`IotGatewayServerApplication`
---
## 一、网关架构
### 1.1 双向通信模型
```
设备 ──→ UpstreamProtocol接收上行 ──→ 消息总线 ──→ iot-server
设备 ←── DownstreamSubscriber发送下行 ←── 消息总线 ←── iot-server
```
每个网关实例启动时生成唯一 `serverId``{IP}_{port}`IP 中 `.` 替换为 `_`),用于下行消息路由,确保消息只路由到设备实际连接的网关。
### 1.2 配置入口
配置前缀:`viewsh.iot.gateway`
```yaml
viewsh:
iot:
gateway:
rpc:
url: http://localhost:48080 # iot-server RPC 地址
connectTimeout: 5000
readTimeout: 10000
token:
secret: xxx # HTTP JWT 签名密钥
expiration: 3600 # JWT 过期秒数
protocol:
http:
enabled: true
serverPort: 8090
emqx:
enabled: false
mqttHost: 127.0.0.1
mqttPort: 1883
mqtt:
enabled: false
port: 1883
tcp:
enabled: true
port: 8091
```
---
## 二、EMQX 协议(桥接外部 Broker
### 2.1 架构
网关以 **MQTT 客户端** 身份连接 EMQX Broker同时在 `httpPort` 启动 HTTP Server 接收 EMQX 认证/事件回调。
### 2.2 组件
| 组件 | 职责 |
|------|------|
| `IotEmqxAuthEventProtocol` | HTTP Server提供 `/mqtt/auth`(认证回调)和 `/mqtt/event`(事件回调) |
| `IotEmqxUpstreamProtocol` | MQTT Client订阅设备上行 Topic含断线自动重连 |
| `IotEmqxUpstreamHandler` | 解析 Topic 提取 productKey/deviceName解码后发消息总线 |
| `IotEmqxDownstreamSubscriber` | 订阅消息总线下行 Topic |
| `IotEmqxDownstreamHandler` | 编码消息后通过 EMQX 发布 |
### 2.3 认证回调
- **请求**EMQX HTTP 认证插件 `POST /mqtt/auth`Body 含 `clientid/username/password`
- **处理**:调用 `IotDeviceCommonApi.authDevice()`
- **响应**`{"result":"allow","is_superuser":false}``{"result":"deny"}`
### 2.4 事件回调
- `client.connected` → 发送设备上线消息
- `client.disconnected` → 发送设备离线消息
-`username`(格式 `deviceName&productKey`)解析设备身份
### 2.5 配置项
| 字段 | 说明 | 默认值 |
|------|------|--------|
| `httpPort` | 认证回调端口 | 8090 |
| `mqttHost/mqttPort` | EMQX 地址 | — / 1883 |
| `mqttTopics` | 订阅主题列表 | — |
| `mqttQos` | QoS | 1 |
| `reconnectDelayMs` | 重连延迟 | 5000 |
| `will.enabled/topic/payload` | 遗嘱消息 | — |
---
## 三、MQTT 协议(内置 Broker
### 3.1 架构
网关自身充当 MQTT Broker 服务端Vert.x `MqttServer`),设备直连。
### 3.2 组件
| 组件 | 职责 |
|------|------|
| `IotMqttUpstreamProtocol` | 启动 MQTT Server每连接创建 Handler |
| `IotMqttUpstreamHandler` | 处理 CONNECT 认证、PUBLISH 上行、CLOSE 离线 |
| `IotMqttConnectionManager` | 连接管理器(双 Mapendpoint→info、deviceId→endpoint |
| `IotMqttDownstreamHandler` | 编码消息后通过 `endpoint.publish()` 下发 |
### 3.3 认证流程
1. 设备发送 CONNECT 报文clientId + username + password
2. 调用 `IotDeviceCommonApi.authDevice()` 校验
3. 失败:`endpoint.reject(BAD_USER_NAME_OR_PASSWORD)`
4. 成功:注册连接、发上线消息
### 3.4 重复连接处理
`registerConnection()` 检测到同一设备 ID 的旧连接,自动断开旧连接再注册新连接。
### 3.5 配置项
| 字段 | 默认值 |
|------|--------|
| `port` | 1883 |
| `maxMessageSize` | 8192 |
| `keepAliveTimeoutSeconds` | 300 |
---
## 四、HTTP 协议
### 4.1 路由表
| 路径 | 处理器 | 说明 |
|------|--------|------|
| `POST /auth` | IotHttpAuthHandler | 设备认证,换取 JWT Token |
| `POST /topic/sys/:pk/:dn/*` | IotHttpUpstreamHandler | 通用上行数据 |
| `POST /api/camera/heartBeat` | IotCameraUpstreamHandler | 3D11 心跳 |
| `POST /api/camera/dataUpload` | IotCameraUpstreamHandler | 3D11 数据上报 |
### 4.2 认证机制
- **JWT Token**`POST /auth` 验证凭据后返回 JWT后续请求在 `Authorization` Header 携带
- **免鉴权**:设备 `authType=NONE` 时无需 Token仅校验 productKey/deviceName 存在
- Token payload`{productKey, deviceName, exp}`
### 4.3 特殊响应格式
不同编解码器返回不同响应:
- `PEOPLE_COUNTER``PeopleCounterUploadResp`(含营业时间、存储周期等)
- `HENGHUA_D5``{"code":0,"msg":"success"}`
- 其他 → `{"messageId":"..."}`
### 4.4 下行
HTTP 为短连接,**不支持下行推送**`IotHttpDownstreamSubscriber.onMessage()` 仅记录日志。
---
## 五、TCP 协议
### 5.1 自动协议检测
`IotTcpUpstreamHandler.getMessageCodecType()` 按以下优先级自动检测报文格式:
1. **缓存**:已识别过的连接直接复用
2. **JT808**:首尾均为 `0x7E`
3. **Binary**:首字节为 `0x7E`
4. **JSON**:字符串以 `{``[` 开头
### 5.2 协议处理器插件
通过 `ProtocolHandler` 接口实现插件化:
| 实现类 | 支持格式 | 认证方式 |
|--------|---------|---------|
| `StandardProtocolHandler` | TCP_JSON, TCP_BINARY | 首条 `auth` 消息,含 clientId/username/password |
| `Jt808ProtocolHandler` | JT808 | 两步认证0x0100 注册 → 0x0102 鉴权 |
### 5.3 JT808 两步认证
1. **终端注册**0x0100从报文提取手机号 → 查设备 → 生成随机鉴权码 → 存 Redis30 天 TTL → 返回注册应答 0x8100
2. **终端鉴权**0x0102从 Redis 取鉴权码比对 → 返回通用应答 0x8001 → 标记连接已认证
### 5.4 JT808 下行指令
| 服务标识 | JT808 指令 | 说明 |
|---------|-----------|------|
| `TTS` | 0x8300 文本下发flag=0x08 | 语音播报 |
| `instruction_config` | 0x8300 文本下发flag=0x01 | 静默配置 |
| `locationQuery` | 0x8201待实现 | 位置查询 |
### 5.5 配置项
| 字段 | 默认值 |
|------|--------|
| `port` | 8091 |
| `keepAliveTimeoutMs` | 30000 |
| `maxConnections` | 1000 |
---
## 六、MQTT Topic 规则
所有 MQTT 系列协议EMQX/MQTT共用 `IotMqttTopicUtils.buildTopicByMethod()`
```
/sys/{productKey}/{deviceName}/{method路径}[_reply]
```
- 方法名中 `.``/``thing.property.post``/sys/pk/dn/thing/property/post`
- 回复消息追加 `_reply``/sys/pk/dn/thing/property/post_reply`
---
## 七、编解码器详解
### 7.1 Alink`Alink`
阿里云标准 JSON 协议:
```json
{"id":"msg001","version":"1.0","method":"thing.property.post","params":{"temperature":26.5},"code":200}
```
### 7.2 Camera3D11`CAMERA_3D11`
```json
{"sn":"SN001","startTime":1700000000,"endTime":1700003600,"time":1700003600,"in":50,"out":45}
```
→ 属性映射:`people_in`, `people_out`, `stat_start_time`, `stat_end_time`
→ 校验:`time` 距当前超 24 小时丢弃
### 7.3 HenghuaD5`HENGHUA_D5`
URL 编码表单:`status=1&type=1&data=%7B...%7D`
- `status=0` → 心跳,忽略
- `type=1` → 拌线统计:`InNum`/`OutNum``people_in`/`people_out`
### 7.4 PeopleCounter`PEOPLE_COUNTER`
JSON含中文引号自动修复
```json
{"uuid":"xxx","data":[{"time":"20231015103000","in":5,"out":3,"rxBat":85,"txBat":90}]}
```
→ 取 data 列表中最新一条,映射:`people_in`, `people_out`, `battery_rx`, `battery_tx`
### 7.5 JT808`JT808`
交通部标准二进制协议,帧结构:`0x7E [消息头12字节] [消息体] [校验码] 0x7E`
**支持的上行消息**
| 消息 ID | 方法 | 说明 |
|---------|------|------|
| 0x0001 | `jt808.terminal.commonResp` | 终端通用应答 |
| 0x0002 | `jt808.terminal.heartbeat` | 心跳 |
| 0x0100 | `jt808.terminal.register` | 终端注册 |
| 0x0102 | `jt808.terminal.auth` | 终端鉴权 |
| 0x0200 | `thing.property.post` | 位置信息上报 |
| 0x0006 | `thing.event.post` | 按键事件 |
| 0x0704 | `thing.property.post` | 批量位置上报 |
**位置信息字段**latitude, longitude, elevation, speed, direction, time, warningFlag, status
**扩展字段解析器**
| 扩展 ID | 解析内容 |
|---------|---------|
| 0x01 | 里程0.1km 精度) |
| 0x30 | 信号强度 |
| 0xF3 | 蓝牙信标MAC + RSSI |
| 0xF4 | 附近蓝牙MAC + RSSI |
| 0xFE | 电池信息/固件版本/ICCID |
### 7.6 TCP Binary`TCP_BINARY`
自定义二进制帧:
```
[0x7E魔术字][0x01版本][类型1字节][总长度4字节][消息ID][方法名][消息体]
```
### 7.7 TCP JSON`TCP_JSON`
```json
{"id":"msg001","method":"thing.property.post","params":{"key":"value"}}
```
---
## 八、设备信息缓存
`IotDeviceServiceImpl` 使用 Guava `LoadingCache` 双缓存:
-`id` 缓存1 分钟 TTL
-`productKey + deviceName` 缓存1 分钟 TTL
- 两缓存查询成功后互相填充
设备远程 API`IotDeviceApiImpl`)通过 `RestTemplate` 调用 iot-server 的 RPC 接口:
- `POST /rpc-api/iot/device/auth` → 设备认证
- `POST /rpc-api/iot/device/get` → 查询设备信息

View File

@@ -0,0 +1,246 @@
# 03-产品与设备管理详解
> 模块路径:`viewsh-module-iot-server` | Controller 前缀:`/iot/product`、`/iot/device`
---
## 一、产品管理
### 1.1 产品生命周期
```
创建UNPUBLISHED=0开发中
↓ 配置物模型
发布PUBLISHED=1→ 自动创建 TDengine 超级表
已发布状态不可修改/删除
```
### 1.2 API 接口
| 路径 | 方法 | 权限 | 说明 |
|------|------|------|------|
| `/iot/product/create` | POST | `iot:product:create` | 创建产品(自动生成 productSecret |
| `/iot/product/update` | PUT | `iot:product:update` | 更新产品(不可改 productKey已发布不可改 |
| `/iot/product/update-status` | PUT | `iot:product:update` | 发布/取消发布 |
| `/iot/product/delete` | DELETE | `iot:product:delete` | 删除(已发布或有设备不可删) |
| `/iot/product/get` | GET | `iot:product:query` | 获取详情(含 categoryName |
| `/iot/product/get-by-key` | GET | `iot:product:query` | 按 productKey 查询 |
| `/iot/product/page` | GET | `iot:product:query` | 分页name, productKey |
| `/iot/product/simple-list` | GET | 无 | 下拉列表id, name, status, deviceType |
| `/iot/product/export-excel` | GET | `iot:product:export` | Excel 导出 |
### 1.3 产品表(`iot_product`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `name` | String | 产品名称 |
| `product_key` | String | 产品标识(全局唯一,不区分大小写) |
| `category_id` | Long | 分类 ID |
| `device_type` | Integer | 0=直连, 1=网关子设备, 2=网关 |
| `net_type` | Integer | 0=WiFi, 1=Cellular, 2=Ethernet, 3=其他 |
| `codec_type` | String | 编解码器类型 |
| `auth_type` | String | 认证类型SECRET/PRODUCT_SECRET/NONE |
| `product_secret` | String | 产品密钥 |
| `status` | Integer | 0=开发中, 1=已发布 |
### 1.4 产品分类
| 路径 | 方法 | 说明 |
|------|------|------|
| `/iot/product-category/create` | POST | 创建分类 |
| `/iot/product-category/update` | PUT | 更新分类 |
| `/iot/product-category/delete` | DELETE | 删除分类 |
| `/iot/product-category/page` | GET | 分页查询 |
| `/iot/product-category/simple-list` | GET | 下拉列表(仅启用) |
---
## 二、设备管理
### 2.1 设备状态机
```
INACTIVE(0) ──首次上线──→ ONLINE(1) ──断连──→ OFFLINE(2)
↑ │
└────重新上线────────┘
```
状态更新时:
- 首次上线更新 `activeTime`
- 上线更新 `onlineTime`,离线更新 `offlineTime`
- 发布 `DeviceStatusChangedEvent` 到 RocketMQ跨模块集成事件
- 清除设备 Redis 缓存
### 2.2 API 接口
| 路径 | 方法 | 权限 | 说明 |
|------|------|------|------|
| `/iot/device/create` | POST | `iot:device:create` | 创建设备 |
| `/iot/device/update` | PUT | `iot:device:update` | 更新(不可改 deviceName/productId |
| `/iot/device/update-group` | PUT | `iot:device:update` | 批量更新设备分组 |
| `/iot/device/delete` | DELETE | `iot:device:delete` | 删除单个 |
| `/iot/device/delete-list` | DELETE | `iot:device:delete` | 批量删除(检查子设备) |
| `/iot/device/get` | GET | `iot:device:query` | 获取详情 |
| `/iot/device/page` | GET | `iot:device:query` | 分页name, nickname, productId, deviceType, status, groupId |
| `/iot/device/count` | GET | `iot:device:query` | 按产品统计设备数 |
| `/iot/device/simple-list` | GET | 无 | 精简列表 |
| `/iot/device/import` | POST | `iot:device:import` | Excel 导入 |
| `/iot/device/get-auth-info` | GET | `iot:device:auth-info` | MQTT 认证三元组 |
| `/iot/device/export-excel` | GET | `iot:device:export` | Excel 导出 |
### 2.3 设备创建校验
1. 产品存在性检查
2. deviceName 在同产品内唯一productKey + deviceName 联合索引)
3. 网关子设备校验 gatewayId 对应网关类型设备
4. 分组 ID 全部存在
5. 序列号全局唯一
6. 自动生成 deviceSecretUUID、设置状态 INACTIVE
### 2.4 设备表(`iot_device`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `device_name` | String | 设备名称(产品内唯一) |
| `nickname` | String | 备注名称 |
| `serial_number` | String | 序列号(全局唯一) |
| `product_id` | Long | 产品 ID |
| `product_key` | String | 产品标识(冗余) |
| `device_type` | Integer | 设备类型(冗余) |
| `gateway_id` | Long | 网关设备 ID子设备专用 |
| `group_ids` | Set\<Long\> | 分组 ID 集合JSON 数组LongSetTypeHandler |
| `state` | Integer | 0=未激活, 1=在线, 2=离线 |
| `online_time` | LocalDateTime | 最后上线时间 |
| `offline_time` | LocalDateTime | 最后离线时间 |
| `active_time` | LocalDateTime | 激活时间 |
| `device_secret` | String | 设备密钥 |
| `auth_type` | String | 认证类型(为空继承产品) |
| `config` | String | 设备配置 JSON下发给设备 |
| `firmware_id` | Long | 当前固件 ID |
### 2.5 设备认证
认证三元组生成规则(`IotDeviceAuthUtils`
- `clientId` = `{productKey}.{deviceName}`
- `username` = `{deviceName}&{productKey}`
- `password` = HmacSHA256(secret, 按字母序拼接 key+value)
认证类型优先级:设备 `authType` > 产品 `authType`
---
## 三、设备分组
### 3.1 存储方式
设备分组 ID 存储在 `iot_device.group_ids` 字段JSON 数组),使用 `LongSetTypeHandler` 处理。
按分组查询设备使用 `FIND_IN_SET(groupId, group_ids)` MySQL 语法。
### 3.2 API 接口
| 路径 | 方法 | 说明 |
|------|------|------|
| `/iot/device-group/create` | POST | 创建分组 |
| `/iot/device-group/update` | PUT | 更新分组 |
| `/iot/device-group/delete` | DELETE | 删除分组 |
| `/iot/device-group/page` | GET | 分页查询(含 deviceCount |
| `/iot/device-group/simple-list` | GET | 下拉列表(仅启用) |
---
## 四、设备属性管理
### 4.1 双写架构
```
属性上报 → 写入 TDengine 子表(历史) + 更新 Redis Hash最新值
```
### 4.2 TDengine 属性超级表
- **命名**`product_property_{productId}`
- **子表**`device_property_{deviceId}` USING ... TAGS(deviceId)
- **字段**`ts TIMESTAMP`, `report_time TIMESTAMP`, 每个物模型属性一列
- **DDL 管理**:物模型变更时自动 ALTERADD/DROP/MODIFY COLUMN
### 4.3 物模型类型 → TDengine 类型映射
| 物模型 | TDengine |
|--------|----------|
| int | INT |
| float | FLOAT |
| double | DOUBLE |
| enum | TINYINT |
| bool | TINYINT |
| text | VARCHAR(n) |
| date | TIMESTAMP |
| struct | VARCHAR(1024) |
| array | VARCHAR(1024) |
### 4.4 Redis 最新属性缓存
- Key`iot:device_property:{deviceId}`Hash
- Hash Key属性 identifier
- Hash ValueJSON 序列化的 `IotDevicePropertyDO`value + updateTime
- 无 TTL常驻
### 4.5 API 接口
| 路径 | 方法 | 说明 |
|------|------|------|
| `/iot/device/property/get-latest` | GET | 获取设备全部最新属性(从 Redis拼接物模型元信息 |
| `/iot/device/property/history-list` | GET | 属性历史查询(从 TDengine支持时间范围 |
---
## 五、设备消息日志
### 5.1 TDengine 消息超级表
超级表 `device_message`,子表 `device_message_{deviceId}`
| 列 | 类型 | 说明 |
|----|------|------|
| ts | TIMESTAMP | 写入时间 |
| id | NCHAR(50) | 消息 UUID |
| method | NCHAR(100) | 消息方法 |
| params | NCHAR(2048) | 请求参数 JSON |
| data | NCHAR(2048) | 响应数据 JSON |
| upstream | BOOL | 是否上行 |
| reply | BOOL | 是否回复 |
| identifier | NCHAR(100) | 标识符 |
| request_id | NCHAR(50) | 请求编号 |
| code | INT | 响应码 |
### 5.2 API 接口
| 路径 | 方法 | 说明 |
|------|------|------|
| `/iot/device/message/page` | GET | 消息分页(按 method/upstream/reply/identifier/时间范围过滤) |
| `/iot/device/message/pair-page` | GET | 请求-响应配对查询 |
| `/iot/device/message/send` | POST | 发送消息(设备模拟) |
---
## 六、统计功能
### 6.1 全局汇总(`/iot/statistics/get-summary`
返回:产品/品类/设备总数、今日新增数、在线/离线/未激活设备数、各品类设备数分布。
### 6.2 消息按日期统计(`/iot/statistics/get-device-message-summary-by-date`
按 interval天/周/月)聚合,返回时间轴上的上行/下行消息数。底层使用 TDengine `TIMETRUNCATE(ts, 1h)` 按小时查出后再合并。
---
## 七、设备离线检测
`IotDeviceOfflineCheckJob`XXL-Job
1. 查询所有在线设备
2. 从 Redis ZSet`iot:device_report_times`)获取超时设备 ID超时阈值 = keepAliveTime × keepAliveFactor = 15 分钟)
3. 对超时设备发送离线消息,走完整消息处理链路

View File

@@ -1,42 +0,0 @@
# 03-规则引擎与告警联动
规则引擎Scene Rule是打通设备感知IoT与人工干预Ops的桥梁。系统通过灵活的触发器和执行器组合实现自动化的业务闭环。
## 一、规则触发与执行架构
`IotSceneRule` 体系下,一个完整的规则包含**触发器 (Trigger)** 和 **执行器 (Action)**
### 1. 触发类型 (`IotSceneRuleTriggerTypeEnum`)
系统支持 5 种维度的触发方式:
- **`DEVICE_STATE_UPDATE(1)`**:设备上下线翻转触发。
- **`DEVICE_PROPERTY_POST(2)`**:属性上报触发(如温湿度数值变化)。由于属性可能批量上报,引擎会做拆解匹配。
- **`DEVICE_EVENT_POST(3)`**:事件上报触发(如故障事件)。
- **`DEVICE_SERVICE_INVOKE(4)`**:服务调用触发。
- **`TIMER(100)`**:定时触发(如每天早上 8 点自动执行)。
### 2. 动作类型 (`IotSceneRuleActionTypeEnum`)
触发条件满足后,系统可执行如下动作组合:
- **联动反控**
- `DEVICE_PROPERTY_SET(1)`:修改另一台设备的属性。
- `DEVICE_SERVICE_INVOKE(2)`:调用另一台设备的服务(如温度高触发开风扇)。
- **告警流转**
- `ALERT_TRIGGER(100)`:触发告警(生成 `IotAlertRecordDO`)。
- `ALERT_RECOVER(101)`:解除告警。
## 二、告警系统与 Ops 的联动
告警 (`ALERT`) 是连接 Ops 派单引擎的关键介质。
### 1. 告警产生
当规则引擎执行了 `ALERT_TRIGGER` 动作,系统会在 `IotAlertRecord` 表中写入一条告警记录。该记录包含触发的规则 ID、设备 ID 和严重等级。
### 2. 联动派单 (`EventDomainEnum.RULE`)
告警生成后,将向外抛出事件(领域标识为 `RULE`)。
此时,`module-ops` 监听到告警事件,并根据内部逻辑:
- 如果属于紧急安保事件 -> 派发 `SECURITY` 类型的抢单任务给就近保安。
- 如果属于设备故障 -> 派发 `REPAIR` 类型的工单给工程组。
### 3. 告警处理闭环 (`IotAlertRecordProcessReqVO`)
当维修工或保安在现场处理完毕通过移动端App / 智能工牌)提交完工(状态变为 `COMPLETED`Ops 系统必须调用 IoT 领域的处理接口。
- 调用 `IotAlertRecordController.processAlert()` 进行状态核销。
- 或者由设备恢复正常状态后,触发规则引擎的 `ALERT_RECOVER(101)` 动作实现自动核销。

View File

@@ -0,0 +1,178 @@
# 04-物模型管理详解
> Controller 前缀:`/iot/thing-model` | 表:`iot_thing_model`
---
## 一、物模型三要素
| 类型 | type 值 | 说明 |
|------|---------|------|
| 属性Property | 1 | 设备可读写的状态量(温度、湿度、电量等) |
| 服务Service | 2 | 设备可执行的操作(语音播报、重启等),支持入参/出参 |
| 事件Event | 3 | 设备主动上报的通知(告警、按键等),分 info/alert/error 三级 |
---
## 二、API 接口
| 路径 | 方法 | 权限 | 说明 |
|------|------|------|------|
| `/iot/thing-model/create` | POST | `iot:thing-model:create` | 创建物模型 |
| `/iot/thing-model/update` | PUT | `iot:thing-model:update` | 更新物模型 |
| `/iot/thing-model/delete` | DELETE | `iot:thing-model:delete` | 删除物模型 |
| `/iot/thing-model/get` | GET | `iot:thing-model:query` | 获取单条 |
| `/iot/thing-model/get-tsl` | GET | `iot:thing-model:query` | 获取产品完整 TSL全部属性/服务/事件) |
| `/iot/thing-model/list` | GET | `iot:thing-model:query` | 按产品+条件列表查询 |
| `/iot/thing-model/page` | GET | `iot:thing-model:query` | 分页查询 |
---
## 三、创建校验规则
1. 标识符在同产品下唯一
2. **保留字禁止**`set`, `get`, `post`, `property`, `event`, `time`, `value`
3. 名称在同产品下唯一
4. 产品状态必须为"开发中"(已发布产品不可修改物模型)
5. 按 type 只存储对应类型的字段MapStruct 转换时清空不相关字段)
---
## 四、数据类型体系
### 4.1 基础类型枚举(`IotDataSpecsDataTypeEnum`
| 值 | 说明 | DataSpecs 子类 |
|----|------|---------------|
| `int` | 整型 | ThingModelNumericDataSpec |
| `float` | 单精度浮点 | ThingModelNumericDataSpec |
| `double` | 双精度浮点 | ThingModelNumericDataSpec |
| `text` | 文本 | ThingModelDateOrTextDataSpecs |
| `date` | 日期 | ThingModelDateOrTextDataSpecs |
| `bool` | 布尔 | ThingModelBoolOrEnumDataSpecs |
| `enum` | 枚举 | ThingModelBoolOrEnumDataSpecs |
| `array` | 数组 | ThingModelArrayDataSpecs |
| `struct` | 结构体 | ThingModelStructDataSpecs |
### 4.2 数值型int/float/double
| 字段 | 说明 |
|------|------|
| `max` | 最大值(字符串格式) |
| `min` | 最小值 |
| `step` | 步长 |
| `precise` | 精度float/double 可用) |
| `defaultValue` | 默认值 |
| `unit` | 单位符号 |
| `unitName` | 单位名称 |
### 4.3 布尔/枚举型bool/enum
| 字段 | 说明 |
|------|------|
| `name` | 枚举项名称(中英文/数字/下划线/短划线≤20字符 |
| `value` | 枚举值Integer |
使用 `dataSpecsList` 存储,每项一个 name+value 对。
### 4.4 文本/日期型text/date
| 字段 | 说明 |
|------|------|
| `length` | 文本最大长度≤2048text 类型必填) |
| `defaultValue` | 默认值 |
### 4.5 数组型array
| 字段 | 说明 |
|------|------|
| `size` | 元素个数 |
| `childDataType` | 元素类型struct/int/float/double/text |
| `dataSpecsList` | 当 childDataType=struct 时,结构体成员规范 |
### 4.6 结构体型struct
| 字段 | 说明 |
|------|------|
| `identifier` | 属性标识符 |
| `name` | 属性名称 |
| `accessMode` | 操作类型r/rw |
| `childDataType` | 子数据类型int/float/double/text/date/enum/bool |
| `dataSpecs/dataSpecsList` | 子规范 |
---
## 五、属性定义ThingModelProperty
| 字段 | 类型 | 说明 |
|------|------|------|
| `identifier` | String | 标识符字母开头字母数字下划线≤32 |
| `name` | String | 属性名称 |
| `accessMode` | String | `r`=只读, `rw`=读写 |
| `required` | Boolean | 是否必选 |
| `dataType` | String | 数据类型 |
| `dataSpecs` | Object | 非列表型数据规范 |
| `dataSpecsList` | List | 列表型数据规范 |
---
## 六、服务定义ThingModelService
| 字段 | 类型 | 说明 |
|------|------|------|
| `identifier` | String | 服务标识符 |
| `name` | String | 服务名称 |
| `callType` | String | `async`=异步, `sync`=同步 |
| `inputParams` | List\<ThingModelParam\> | 输入参数列表 |
| `outputParams` | List\<ThingModelParam\> | 输出参数列表 |
| `method` | String | 设备执行的操作标识 |
---
## 七、事件定义ThingModelEvent
| 字段 | 类型 | 说明 |
|------|------|------|
| `identifier` | String | 事件标识符 |
| `name` | String | 事件名称 |
| `type` | String | `info`=信息, `alert`=告警, `error`=故障 |
| `outputParams` | List\<ThingModelParam\> | 输出参数列表 |
| `method` | String | 操作标识 |
---
## 八、参数定义ThingModelParam
用于服务的 inputParams/outputParams 和事件的 outputParams
| 字段 | 类型 | 说明 |
|------|------|------|
| `identifier` | String | 参数标识符 |
| `name` | String | 参数名称 |
| `direction` | String | `input`=入参, `output`=出参 |
| `paraOrder` | Integer | 参数序号(从 0 开始) |
| `dataType` | String | 数据类型 |
| `dataSpecs/dataSpecsList` | Object/List | 数据规范 |
---
## 九、缓存策略
- 缓存 Key`iot:thing_model_list::{productId}`Spring Cache
- 读取:`@Cacheable` + `@TenantIgnore`(跨租户缓存命中)
- 驱逐:创建/更新/删除物模型时,通过 `getSelf()` 代理调用触发 `@CacheEvict`
- 驱逐 `getSelf()` 设计原因Spring AOP 代理无法拦截内部方法调用,需通过代理对象调用
---
## 十、物模型与 TDengine 联动
产品发布时(`updateProductStatus` → PUBLISHED
1. 调用 `defineDevicePropertyData(productId)` 创建 TDengine 超级表
2. 超级表字段由物模型属性列表动态生成
物模型变更时(`alterProductPropertySTable`
- 新增属性 → `ALTER STABLE ADD COLUMN`
- 删除属性 → `ALTER STABLE DROP COLUMN`
- 文本变长 → `ALTER STABLE MODIFY COLUMN`
- 类型改变/缩短 → DROP + ADD

View File

@@ -0,0 +1,235 @@
# 05-规则引擎详解
> 三条独立处理链路:数据转发 + 场景联动 + 数据清洗
---
## 一、规则引擎架构
```
设备消息IotDeviceMessage
├─→ [数据转发] IotDataRuleService.executeDataRule()
│ 匹配 DataRule → Sink → Action
├─→ [场景联动] IotSceneRuleService.executeSceneRuleByDevice()
│ Trigger 匹配 → Condition 判断 → Action 执行
└─→ [数据清洗] CleanRuleProcessorManager.processMessage()
保洁业务专用:客流/Beacon/按键/轨迹
```
三者通过独立消费组并行处理,互不阻塞。
---
## 二、数据转发规则
### 2.1 核心概念
- **DataRule数据流转规则**定义触发条件SourceConfig 列表和目标sinkIds 列表)
- **DataSink数据目的**:定义投递到哪里,独立存储可复用
### 2.2 SourceConfig 匹配条件
| 字段 | 说明 |
|------|------|
| `method` | 消息方法(如 thing.property.post |
| `productId` | 产品 ID |
| `deviceId` | 设备 IDDEVICE_ID_ALL=全部) |
| `identifier` | 物模型标识符(空=全部) |
### 2.3 DataSink 类型与实现状态
| 类型 | type | 实现类 | 连接管理 | 状态 |
|------|------|--------|---------|------|
| HTTP | 1 | IotHttpDataSinkAction | RestTemplate无池 | 已实现 |
| TCP | 2 | IotTcpDataRuleAction | Guava Cache30min TTL | 已实现 |
| Redis | 21 | IotRedisRuleAction | Redisson Cache30min TTL | 已实现 |
| RocketMQ | 30 | IotRocketMQDataRuleAction | DefaultMQProducer Cache | 已实现 |
| RabbitMQ | 31 | IotRabbitMQDataRuleAction | Channel Cache | 已实现 |
| Kafka | 32 | IotKafkaDataRuleAction | KafkaProducer Cache | 已实现 |
| WebSocket | 3 | — | — | 待实现 |
| MQTT | 10 | — | — | 待实现 |
| Database | 20 | — | — | 待实现 |
### 2.4 Redis Sink 支持的数据结构
| 结构 | 操作 |
|------|------|
| STREAM | `XADD` |
| HASH | `HSET` |
| LIST | `RPUSH` |
| SET | `SADD` |
| ZSET | `ZADD`score 字段或时间戳) |
| STRING | `SET` |
### 2.5 执行流程
```
消息到达 → 提取 deviceId/method/identifier
→ 缓存查规则key = deviceId_method_identifier
→ 遍历匹配规则 → 遍历 sinkIds
→ 获取 DataSink → 路由到 Action → 发送
```
### 2.6 API 接口
| 路径 | 说明 |
|------|------|
| `/iot/data-rule/create\|update\|delete\|get\|page` | 数据转发规则 CRUD |
| `/iot/data-sink/create\|update\|delete\|get\|page\|simple-list` | 数据目的 CRUD |
---
## 三、场景联动规则
### 3.1 数据结构
```
IotSceneRuleDO
├── triggers: List<Trigger> # 触发器列表
│ ├── type # 触发类型
│ ├── productId / deviceId # 设备范围
│ ├── identifier # 物模型标识符
│ ├── operator / value # 比较条件
│ ├── cronExpression # CRON定时触发专用
│ └── conditionGroups # 条件分组(外层 OR内层 AND
│ └── List<TriggerCondition>
└── actions: List<Action> # 执行动作列表
├── type # 动作类型
├── productId / deviceId # 目标设备
├── identifier # 物模型标识符
├── params # 参数 JSON
└── alertConfigId # 告警配置 ID
```
### 3.2 触发器类型
| 枚举值 | type | 触发时机 | 匹配器 |
|--------|------|---------|--------|
| DEVICE_STATE_UPDATE | 1 | 设备上下线 | IotDeviceStateUpdateTriggerMatcher |
| DEVICE_PROPERTY_POST | 2 | 属性上报 | IotDevicePropertyPostTriggerMatcher |
| DEVICE_EVENT_POST | 3 | 事件上报 | IotDeviceEventPostTriggerMatcher |
| DEVICE_SERVICE_INVOKE | 4 | 服务调用 | IotDeviceServiceInvokeTriggerMatcher |
| TIMER | 100 | 定时 | IotTimerTriggerMatcherQuartz Job 驱动) |
### 3.3 条件操作符SpEL 表达式引擎)
| 操作符 | 表达式 |
|--------|--------|
| `=` | `#source == #value` |
| `!=` | `!(#source == #value)` |
| `>` / `>=` / `<` / `<=` | 数值比较(自动转 Double |
| `in` / `not in` | `#values.contains(#source)` |
| `between` / `not between` | 区间判断 |
| `like` | `#source.contains(#value)` |
| `not null` | 非空判断 |
| `time_>` / `time_<` / `time_between` | 当日时间比较HH:mm:ss |
| `date_time_>` / `date_time_<` / `date_time_between` | 日期时间比较(时间戳) |
### 3.4 执行动作类型
| 枚举值 | type | 实现类 | 说明 |
|--------|------|--------|------|
| DEVICE_PROPERTY_SET | 1 | IotDeviceControlSceneRuleAction | 设备属性设置(支持全设备广播) |
| DEVICE_SERVICE_INVOKE | 2 | IotDeviceServiceInvokeSceneRuleAction | 设备服务调用 |
| ALERT_TRIGGER | 100 | IotAlertTriggerSceneRuleAction | 触发告警记录 |
| ALERT_RECOVER | 101 | IotAlertRecoverSceneRuleAction | 自动恢复告警 |
### 3.5 条件分组逻辑
```
conditionGroups = [[c1, c2], [c3, c4]]
匹配 = (c1 AND c2) OR (c3 AND c4)
```
外层 List 为 **OR** 关系,内层 List 为 **AND** 关系。
### 3.6 定时触发流程
```
创建/启用 SceneRule → 注册 Quartz Jobiot_scene_rule_timer_{id}
Quartz 触发 → IotSceneRuleJob → executeSceneRuleByTimer(id) → 执行 Actions
禁用 → 暂停 Job | 删除 → 删除 Job
```
### 3.7 API 接口
| 路径 | 说明 |
|------|------|
| `/iot/scene-rule/create\|update\|delete\|get\|page\|simple-list` | 场景规则 CRUD |
| `/iot/scene-rule/update-status` | 启用/禁用(同时管理 Quartz Job |
---
## 四、数据清洗(保洁业务专用)
### 4.1 处理器路由
```
PROPERTY_POST:
people_in / people_out → TrafficThresholdRuleProcessor客流阈值
bluetoothDevices → BeaconDetectionRuleProcessor蓝牙到岗/离岗)
→ TrajectoryDetectionProcessor轨迹检测
EVENT_POST:
button_event → ButtonEventRuleProcessor按键确认
```
### 4.2 客流阈值处理器TrafficThresholdRuleProcessor
1. 解析 `people_in`/`people_out` 原始值
2. 判断上报模式INCREMENTAL直接用/ CUMULATIVERedis 差值计算)
3. 累加到当日统计Redis Hash
4.`people_in` 参与工单触发
5. 原子累加阈值计数器,达阈值后分布式防重复锁
6. 发布 `CleanOrderCreateEvent` → RocketMQ → Ops 模块
### 4.3 蓝牙信标检测器BeaconDetectionRuleProcessor
**强进弱出算法**`RssiSlidingWindowDetector`
- **进入检测**OUT_AREA → IN_AREA最近 N 个采样中 RSSI ≥ 强阈值的次数达标 → `ARRIVE_CONFIRMED`
- **离开检测**IN_AREA → OUT_AREA最近 N 个采样中 RSSI < 弱阈值的次数达标 `LEAVE_CONFIRMED`
信号提取优先级MAC 地址精确匹配 > iBeacon 三元组匹配 > -999缺失值
事件发布:
- 到岗 → `CleanOrderArriveEvent`
- 离岗 → 记录 SignalLoss不立即完成由定时 Job 超时判断)
### 4.4 信号丢失超时检测SignalLossRuleProcessor
XXL-Job `signalLossCheckJob` 定时扫描:
1. 扫描 `iot:clean:signal:loss:*` 所有离岗记录
2. 超过 `exit.lossTimeoutMinutes` 自动发布 `CleanOrderCompleteEvent`
3. 清理 Redis 数据
### 4.5 按键事件处理器ButtonEventRuleProcessor
从设备 config JSON 解析按键配置,根据当前工单状态分派:
- 无工单 → 查询事件
- DISPATCHED → 确认事件(防重复 10s
- 其他 → 查询事件
### 4.6 轨迹检测处理器TrajectoryDetectionProcessor
1. 检查设备是否开启轨迹功能config.trajectoryTracking.enabled
2. 获取全量 Beacon 注册表
3. 从蓝牙列表提取各区域最强 RSSI
4. 更新各区域滑动窗口
5. 检测进入/离开/切换区域
6. 发布 `TrajectoryEnterEvent` / `TrajectoryLeaveEvent`
### 4.7 集成配置结构
```
CleanOrderIntegrationConfig
├── trafficThreshold:
│ reportMode / threshold / timeWindowSeconds / autoCreateOrder / orderPriority
└── beaconPresence:
beaconMac / beaconUuid+major+minor
enter: rssiThreshold / windowSize / hitCount / autoArrival
exit: weakRssiThreshold / windowSize / hitCount / lossTimeoutMinutes / minValidWorkMinutes
```

View File

@@ -0,0 +1,188 @@
# 06-告警与 OTA 升级详解
---
## 一、告警管理
### 1.1 告警配置(`iot_alert_config`
告警配置关联场景联动规则(通过 `sceneRuleIds` JSON 数组),当场景规则中的 `ALERT_TRIGGER` 动作执行时,查找关联的启用告警配置来创建告警记录。
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `name` | String | 配置名称 |
| `description` | String | 描述 |
| `level` | Integer | 告警级别(字典 `iot_alert_level` |
| `status` | Integer | 0=禁用, 1=启用 |
| `sceneRuleIds` | List\<Long\> | 关联场景规则 ID 列表FIND_IN_SET 查询) |
| `receiveUserIds` | List\<Long\> | 接收用户 ID 列表 |
| `receiveTypes` | List\<Integer\> | 接收方式1=短信, 2=邮件, 3=站内信 |
### 1.2 告警记录(`iot_alert_record`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `configId` | Long | 告警配置 ID冗余 |
| `configName` | String | 告警名称(冗余) |
| `configLevel` | Integer | 告警级别(冗余) |
| `sceneRuleId` | Long | 触发的场景规则 ID |
| `productId` | Long | 产品 ID |
| `deviceId` | Long | 设备 ID |
| `deviceMessage` | IotDeviceMessage | 触发的设备消息JSON 存储) |
| `processStatus` | Boolean | 是否已处理 |
| `processRemark` | String | 处理备注 |
### 1.3 告警触发链路
```
设备消息 → 场景规则匹配 → ALERT_TRIGGER 动作
→ 查询 sceneRuleId 关联的启用告警配置
→ 创建告警记录(冗余配置信息 + 设备消息快照)
→ TODO: 发送通知(短信/邮件/站内信)
```
### 1.4 告警自动恢复
```
设备消息 → 场景规则匹配 → ALERT_RECOVER 动作
→ 查询该场景 + 设备下未处理的告警记录
→ 批量标记为已处理,备注:"告警自动回复,基于【规则名称】场景联动规则"
```
### 1.5 API 接口
| 路径 | 方法 | 说明 |
|------|------|------|
| `/iot/alert-config/create\|update\|delete\|get\|page` | — | 告警配置 CRUD |
| `/iot/alert-config/simple-list` | GET | 启用配置下拉 |
| `/iot/alert-record/get\|page` | GET | 告警记录查询 |
| `/iot/alert-record/process` | PUT | 处理告警记录 |
---
## 二、OTA 升级
### 2.1 三层结构
```
OTA 固件Firmware→ 升级任务Task→ 升级记录TaskRecord
```
### 2.2 OTA 固件(`iot_ota_firmware`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `name` | String | 固件名称 |
| `version` | String | 版本号(同产品下唯一) |
| `productId` | Long | 产品 ID |
| `fileUrl` | String | 固件文件 URL |
| `fileSize` | Long | 文件大小(字节,创建时自动下载计算) |
| `fileDigestAlgorithm` | String | 签名算法(固定 MD5 |
| `fileDigestValue` | String | MD5 签名值 |
创建时自动下载固件文件计算 MD5 和文件大小。
### 2.3 OTA 升级任务(`iot_ota_task`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `name` | String | 任务名称 |
| `firmwareId` | Long | 固件 ID |
| `status` | Integer | 10=进行中, 20=已结束, 30=已取消 |
| `deviceScope` | Integer | 1=全部设备, 2=指定设备 |
| `deviceTotalCount` | Integer | 设备总数 |
| `deviceSuccessCount` | Integer | 成功数 |
### 2.4 OTA 升级记录(`iot_ota_task_record`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 主键 |
| `firmwareId` | Long | 目标固件 ID |
| `taskId` | Long | 任务 ID |
| `deviceId` | Long | 设备 ID |
| `fromFirmwareId` | Long | 来源固件 ID |
| `status` | Integer | 0=待推送, 10=已推送, 20=升级中, 30=成功, 40=失败, 50=已取消 |
| `progress` | Integer | 进度0-100% |
| `description` | String | 进度描述 |
### 2.5 OTA 升级流程
```
1. 创建固件(上传 URL → 自动下载计算 MD5
2. 创建升级任务(选范围 → 校验设备 → 批量创建记录 PENDING
3. 定时 JobdeviceUpgradeJob扫描 PENDING 记录
→ 跳过离线设备
→ 构建 OTA 升级消息version/fileUrl/fileSize/digest
→ 通过消息总线下发
→ 更新状态为 PUSHED
4. 设备收到固件 → 开始升级 → 上报进度thing.ota.progress
→ 解析 version/status/progress → 更新记录状态
5. 升级成功 → 更新设备 firmwareId
6. 所有记录完成 → 任务自动置为 END
```
### 2.6 状态统计优先级
同一设备可能有多条升级记录(多次任务),统计时按优先级去重:
`SUCCESS > PENDING > PUSHED > UPGRADING > FAILURE > CANCELED`
### 2.7 乐观锁保护
所有状态更新均使用 `UPDATE ... WHERE id=? AND status IN (?)` 条件更新,防止并发覆盖。
### 2.8 API 接口
| 路径 | 方法 | 说明 |
|------|------|------|
| `/iot/ota/firmware/create\|update\|get\|page` | — | 固件 CRUD |
| `/iot/ota/task/create` | POST | 创建升级任务 |
| `/iot/ota/task/cancel` | POST | 取消任务(批量取消记录) |
| `/iot/ota/task/get\|page` | GET | 任务查询 |
| `/iot/ota/task/record/get\|page` | GET | 记录查询 |
| `/iot/ota/task/record/get-status-statistics` | GET | 状态统计 |
| `/iot/ota/task/record/cancel` | PUT | 取消单条记录 |
---
## 三、框架层关键设计
### 3.1 TDengine 启动初始化
`TDengineTableInitRunner``ApplicationRunner`
- 启动时创建 `device_message` 超级表
- **失败则 `System.exit(1)` 强制退出**TDengine 是系统前提)
### 3.2 Quartz 独立调度器
IoT 使用独立 Quartz 实例(`iotScheduler`),非全局 XXL-Job
- 原因XXL-Job 无法动态添加/删除 Job场景规则定时触发需要
- 配置集群模式25 线程,数据库持久化
- 方法:`addOrUpdateJob` / `deleteJob` / `pauseJob` / `resumeJob`
### 3.3 安全放行
RPC 接口(`/rpc-api/iot/**`)全部放行(`permitAll`Feign 调用无需认证。
### 3.4 Feign 客户端
```java
@EnableFeignClients(clients = {
AdminUserApi.class, // 用户管理
SmsSendApi.class, // 短信发送
MailSendApi.class, // 邮件发送
NotifyMessageSendApi.class, // 站内信
AreaDeviceApi.class // Ops 区域设备
})
```
### 3.5 设备心跳超时
配置:`ViewshIotProperties`
- `keepAliveTime` = 10 分钟
- `keepAliveFactor` = 1.5
- 实际超时 = **15 分钟**

View File

@@ -0,0 +1,186 @@
# 07-消息总线与集成事件详解
> 模块路径:`viewsh-module-iot-core`
---
## 一、消息总线MessageBus
### 1.1 职责
IoT 模块内部gateway ↔ server的设备消息传输通道三种实现可配置切换。
### 1.2 核心接口
```java
// 消息总线
interface IotMessageBus {
void post(String topic, Object message);
void register(IotMessageSubscriber<?> subscriber);
}
// 消息订阅者
interface IotMessageSubscriber<T> {
String getTopic();
String getGroup();
void onMessage(T message);
}
```
### 1.3 Topic 定义
| 常量 | 值 | 方向 |
|------|----|------|
| `MESSAGE_BUS_DEVICE_MESSAGE_TOPIC` | `iot_device_message` | gateway → server |
| `MESSAGE_BUS_GATEWAY_DEVICE_MESSAGE_TOPIC` | `iot_device_message_%s` | server → gateway%s=serverId |
### 1.4 三种实现对比
| 实现 | 激活条件 | 适用场景 | 序列化 | 特点 |
|------|---------|---------|--------|------|
| **Local** | `type=local`(默认) | 单机/开发 | 无 | Spring Event 同步分发 |
| **Redis** | `type=redis` | 轻量集群 | JSON + Redis Stream | 手动 ACK含 Pending 重发 Job |
| **RocketMQ** | `type=rocketmq` | 生产集群 | JSON + MQ | 并发消费,失败自动延迟重试 |
### 1.5 配置
```yaml
viewsh:
iot:
message-bus:
type: local # local / redis / rocketmq
```
### 1.6 Local 实现
- `post()``ApplicationContext.publishEvent(IotLocalMessage)`
- `@EventListener` → 按 topic 路由给订阅者
- 同步串行,无跨进程能力
### 1.7 Redis 实现
- 使用 Redis Stream`XADD`/`XREADGROUP`
- `StreamMessageListenerContainer`batchSize=10
- 手动 ACK`autoAcknowledge=false`
- 附加 Job`RedisPendingMessageResendJob`(重发未 ACK 消息)+ `RedisStreamMessageCleanupJob`(清理已消费消息)
### 1.8 RocketMQ 实现
- `post()``rocketMQTemplate.syncSend(topic, json)`
- `register()` → 创建 `DefaultMQPushConsumer``MessageListenerConcurrently` 消费
- 失败返回 `RECONSUME_LATER`(延迟重试)
- `@PreDestroy` 优雅关闭所有 consumer
---
## 二、统一消息体IotDeviceMessage
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String | 消息 UUID |
| `reportTime` | LocalDateTime | 上报时间 |
| `deviceId` | Long | 设备 ID |
| `tenantId` | Long | 租户 ID |
| `serverId` | String | 网关服务 ID |
| `requestId` | String | 设备请求 ID |
| `method` | String | 消息方法(枚举) |
| `params` | Object | 请求参数 |
| `data` | Object | 响应数据 |
| `code` | Integer | 响应码 |
| `skipReply` | Boolean | 跳过业务层回复 |
| `msg` | String | 响应信息 |
### 消息方法枚举
| 方法 | 名称 | 方向 | 禁用回复 |
|------|------|------|---------|
| `thing.state.update` | 状态更新 | 上行 | 是 |
| `thing.property.post` | 属性上报 | 上行 | 否 |
| `thing.property.set` | 属性设置 | 下行 | 否 |
| `thing.event.post` | 事件上报 | 上行 | 否 |
| `thing.service.invoke` | 服务调用 | 下行 | 否 |
| `thing.config.push` | 配置推送 | 下行 | 否 |
| `thing.ota.upgrade` | OTA 推送 | 下行 | 否 |
| `thing.ota.progress` | OTA 进度 | 上行 | 是 |
---
## 三、集成事件Integration Events
### 3.1 职责
IoT 模块向外部业务模块Ops 等)发布跨服务事件,通过 RocketMQ 传递。独立于消息总线。
### 3.2 设备核心事件
| 事件类 | Topic | 状态 |
|--------|-------|------|
| `DeviceStatusChangedEvent` | `integration-device-status` | 已启用 |
| `DevicePropertyChangedEvent` | `integration-device-property` | 已定义未启用 |
| `DeviceEventOccurredEvent` | `integration-device-event` | 已定义未启用 |
所有事件继承 `BaseDeviceEvent`eventId, deviceId, deviceName, nickname, productId, productKey, tenantId, eventTime
DeviceStatusChangedEvent 扩展字段:`oldStatus`, `newStatus`, `reason`
### 3.3 保洁业务事件
| 事件类 | Topic | 触发场景 |
|--------|-------|---------|
| `CleanOrderCreateEvent` | `ops-order-create` | 客流阈值超限 |
| `CleanOrderArriveEvent` | `ops-order-arrive` | 蓝牙信标到岗 |
| `CleanOrderCompleteEvent` | `ops-order-complete` | 蓝牙信号丢失超时 |
| `CleanOrderAuditEvent` | `ops-order-audit` | 审计节点记录 |
### 3.4 轨迹事件
| 事件类 | Topic | 触发场景 |
|--------|-------|---------|
| `TrajectoryEnterEvent` | `trajectory-enter` | 进入区域 |
| `TrajectoryLeaveEvent` | `trajectory-leave` | 离开区域 |
### 3.5 发布机制
`RocketMQIntegrationEventPublisher`
- destination = `{topic}:{productKey}`productKey 作为 RocketMQ Tag
- 超时 3000ms同步发送
- 异常吞掉(不影响业务主流程)
### 3.6 配置
```yaml
viewsh:
integration:
mq:
enabled: true # 默认启用
producerGroup: integration-event-producer
sendTimeoutMs: 3000
maxMessageSize: 4194304 # 4MB
```
---
## 四、工具类
### 4.1 IotDeviceAuthUtils
- 认证三元组生成clientId + username + passwordHmacSHA256
- 参考阿里云 IoT MQTT 签名规范
- `parseUsername(username)` → 从 `deviceName&productKey` 解析设备身份
### 4.2 IotDeviceMessageUtils
- `generateMessageId()` → UUID
- `isUpstreamMessage(msg)` → 判断上行/下行(支持标准物模型 + JT808
- `extractPropertyValue(msg, identifier)` → 六级降级策略提取属性值
- `getIdentifier(msg)` → 提取消息标识符
- `buildMessageBusGatewayDeviceMessageTopic(serverId)` → 下行 Topic
### 4.3 属性值提取六级降级策略
1. params 非 Map → 直接返回
2. `paramsMap.get(identifier)` → 直接匹配
3. `paramsMap.get("properties")` 中的 identifier → 标准属性格式
4. `paramsMap.get("data")` 中的 identifier → data 嵌套格式
5. `paramsMap.get("value")` → 单值消息
6. Map 只有两个 key 且含 `"identifier"` → 取另一个 key

View File

@@ -0,0 +1,215 @@
# 08-API 契约与枚举汇总
> 模块路径:`viewsh-module-iot-api` | 服务名:`iot-server` | RPC 前缀:`/rpc-api/iot`
---
## 一、Feign RPC 接口
### 1.1 IotDeviceQueryApi设备查询
| 方法 | HTTP | URL | 参数 | 返回 |
|------|------|-----|------|------|
| `getDeviceSimpleList` | GET | `/device/simple-list` | deviceType?, productId? | `List<IotDeviceSimpleRespDTO>` |
| `getDevice` | GET | `/device/get` | id | `IotDeviceSimpleRespDTO` |
| `batchGetDevices` | GET | `/device/batch-get` | ids | `List<IotDeviceSimpleRespDTO>` |
### 1.2 IotDeviceControlApi设备控制
| 方法 | HTTP | URL | 请求体 | 返回 |
|------|------|-----|--------|------|
| `invokeService` | POST | `/device/control/invoke-service` | `IotDeviceServiceInvokeReqDTO` | `IotDeviceServiceInvokeRespDTO` |
| `invokeServiceBatch` | POST | `/device/control/invoke-service-batch` | `List<...ReqDTO>` | `List<...RespDTO>` |
| `resetTrafficCounter` | POST | `/device/control/reset-traffic-counter` | `ResetTrafficCounterReqDTO` | Boolean |
### 1.3 IotDevicePropertyQueryApi属性查询
| 方法 | HTTP | URL | 参数 | 返回 |
|------|------|-----|------|------|
| `getProperty` | GET | `/device/property/get` | deviceId, identifier | `DevicePropertyRespDTO` |
| `getLatestProperties` | GET | `/device/property/get-latest` | deviceId | `Map<String, Object>` |
| `batchGetProperties` | POST | `/device/property/batch-get` | `DevicePropertyBatchQueryReqDTO` | `Map<Long, Map<String, Object>>` |
| `getPropertyHistory` | POST | `/device/property/history` | `DevicePropertyHistoryQueryReqDTO` | `List<DevicePropertyHistoryRespDTO>` |
### 1.4 IotDeviceStatusQueryApi状态查询
| 方法 | HTTP | URL | 参数 | 返回 |
|------|------|-----|------|------|
| `isDeviceOnline` | GET | `/device/status/is-online` | deviceId | Boolean |
| `getDeviceStatus` | GET | `/device/status/get-status` | deviceId | `DeviceStatusRespDTO` |
| `batchGetDeviceStatus` | GET | `/device/status/batch-get-status` | deviceIds | `List<DeviceStatusRespDTO>` |
### 1.5 TrajectoryStateApi轨迹状态
| 方法 | HTTP | URL | 参数 | 返回 |
|------|------|-----|------|------|
| `getCurrentLocation` | GET | `/trajectory/current-location` | deviceId | `DeviceLocationDTO` |
---
## 二、DTO 汇总
### 2.1 IotDeviceSimpleRespDTO
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 设备 ID |
| `deviceName` | String | 设备名称 |
| `productId` | Long | 产品 ID |
| `productKey` | String | 产品标识 |
| `productName` | String | 产品名称 |
| `nickname` | String | 备注名 |
| `serialNumber` | String | 序列号 |
| `state` | Integer | 0=未激活, 1=在线, 2=离线 |
| `deviceType` | Integer | 设备类型 |
### 2.2 IotDeviceServiceInvokeReqDTO
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `deviceId` | Long | 是 | 目标设备 |
| `identifier` | String | 是 | 服务标识 |
| `params` | Map\<String, Object\> | 否 | 服务参数 |
| `timeoutSeconds` | Integer | 否 | 超时秒数(默认 30 |
### 2.3 IotDeviceServiceInvokeRespDTO
| 字段 | 类型 | 说明 |
|------|------|------|
| `messageId` | String | 消息追踪 ID |
| `success` | Boolean | 是否成功 |
| `data` | Object | 响应数据 |
| `code` | Integer | 错误码 |
| `errorMsg` | String | 错误信息 |
| `responseTime` | LocalDateTime | 响应时间 |
### 2.4 DevicePropertyRespDTO
| 字段 | 类型 | 说明 |
|------|------|------|
| `identifier` | String | 属性标识 |
| `value` | Object | 当前值(动态类型) |
| `updateTime` | Long | 更新时间戳(ms) |
### 2.5 DevicePropertyBatchQueryReqDTO
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `deviceIds` | List\<Long\> | 是 | 设备 ID 列表 |
| `identifiers` | List\<String\> | 否 | 属性标识列表(空=全部) |
### 2.6 DevicePropertyHistoryQueryReqDTO
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `deviceId` | Long | 是 | 设备 ID |
| `identifier` | String | 是 | 属性标识 |
| `startTime` | LocalDateTime | 否 | 起始时间 |
| `endTime` | LocalDateTime | 否 | 截止时间 |
| `limit` | Integer | 否 | 最大条数1-1000默认 100 |
### 2.7 DeviceStatusRespDTO
| 字段 | 类型 | 说明 |
|------|------|------|
| `deviceId` | Long | 设备 ID |
| `deviceCode` | String | 设备编码 |
| `status` | Integer | 0/1/2 |
| `statusChangeTime` | LocalDateTime | 状态变更时间 |
### 2.8 DeviceLocationDTO
| 字段 | 类型 | 说明 |
|------|------|------|
| `deviceId` | Long | 设备 ID |
| `areaId` | Long | 区域 ID |
| `enterTime` | Long | 进入时间戳(ms) |
| `beaconMac` | String | Beacon MAC |
| `inArea` | Boolean | 是否在区域内 |
### 2.9 ResetTrafficCounterReqDTO
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `deviceId` | Long | 是 | 设备 ID |
| `newBaseValue` | Long | 否 | @Deprecated 已废弃 |
| `orderId` | Long | 否 | 关联工单 ID |
| `remark` | String | 否 | 操作说明 |
---
## 三、全量枚举汇总
### 3.1 产品枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotProductStatusEnum** | UNPUBLISHED(0), PUBLISHED(1) | 产品状态 |
| **IotProductDeviceTypeEnum** | DIRECT(0), GATEWAY_SUB(1), GATEWAY(2) | 设备拓扑类型 |
| **IotNetTypeEnum** | WIFI(0), CELLULAR(1), ETHERNET(2), OTHER(3) | 联网方式 |
| **IotLocationTypeEnum** | IP(1), DEVICE(2), MANUAL(3) | 定位方式 |
### 3.2 设备枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotDeviceStateEnum** | INACTIVE(0), ONLINE(1), OFFLINE(2) | 设备状态 |
### 3.3 物模型枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotThingModelTypeEnum** | PROPERTY(1), SERVICE(2), EVENT(3) | 功能类型 |
| **IotDataSpecsDataTypeEnum** | int, float, double, enum, bool, text, date, struct, array | 数据类型 |
| **IotThingModelAccessModeEnum** | READ_ONLY("r"), READ_WRITE("rw") | 访问模式 |
| **IotThingModelServiceCallTypeEnum** | ASYNC("async"), SYNC("sync") | 服务调用类型 |
| **IotThingModelServiceEventTypeEnum** | INFO("info"), ALERT("alert"), ERROR("error") | 事件级别 |
| **IotThingModelParamDirectionEnum** | INPUT("input"), OUTPUT("output") | 参数方向 |
### 3.4 OTA 枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotOtaTaskStatusEnum** | IN_PROGRESS(10), END(20), CANCELED(30) | 任务状态 |
| **IotOtaTaskRecordStatusEnum** | PENDING(0), PUSHED(10), UPGRADING(20), SUCCESS(30), FAILURE(40), CANCELED(50) | 记录状态 |
| **IotOtaTaskDeviceScopeEnum** | ALL(1), SELECT(2) | 设备范围 |
### 3.5 告警枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotAlertReceiveTypeEnum** | SMS(1), MAIL(2), NOTIFY(3) | 通知方式 |
### 3.6 规则引擎枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotDataSinkTypeEnum** | HTTP(1), TCP(2), WS(3), MQTT(10), DB(20), Redis(21), RocketMQ(30), RabbitMQ(31), Kafka(32) | 数据目的类型 |
| **IotRedisDataStructureEnum** | STREAM(1), HASH(2), LIST(3), SET(4), ZSET(5), STRING(6) | Redis 结构 |
| **IotSceneRuleTriggerTypeEnum** | STATE(1), PROPERTY(2), EVENT(3), SERVICE(4), TIMER(100) | 触发类型 |
| **IotSceneRuleConditionTypeEnum** | DEVICE_STATE(1), DEVICE_PROPERTY(2), CURRENT_TIME(100) | 条件类型 |
| **IotSceneRuleConditionOperatorEnum** | =, !=, >, >=, <, <=, in, not in, between, like, not null, time_>, time_<, ... | 操作符 |
| **IotSceneRuleActionTypeEnum** | PROPERTY_SET(1), SERVICE_INVOKE(2), ALERT_TRIGGER(100), ALERT_RECOVER(101) | 动作类型 |
### 3.7 Core 层枚举
| 枚举 | 值 | 说明 |
|------|----|------|
| **IotAuthTypeEnum** | SECRET, PRODUCT_SECRET, DYNAMIC, NONE | 认证类型 |
| **IotDeviceMessageMethodEnum** | thing.state.update, thing.property.post/set, thing.event.post, thing.service.invoke, thing.config.push, thing.ota.upgrade/progress | 消息方法 |
| **IotDeviceMessageTypeEnum** | state, event, service, config, ota, register, topology | 消息类型 |
### 3.8 字典类型常量
| 常量 | 字典值 | 说明 |
|------|--------|------|
| NET_TYPE | `iot_net_type` | 联网方式 |
| LOCATION_TYPE | `iot_location_type` | 定位方式 |
| CODEC_TYPE | `iot_codec_type` | 编解码类型 |
| PRODUCT_STATUS | `iot_product_status` | 产品状态 |
| PRODUCT_DEVICE_TYPE | `iot_product_device_type` | 产品设备类型 |
| DEVICE_STATE | `iot_device_state` | 设备状态 |
| ALERT_LEVEL | `iot_alert_level` | 告警级别 |
| OTA_TASK_DEVICE_SCOPE | `iot_ota_task_device_scope` | OTA 设备范围 |
| OTA_TASK_STATUS | `iot_ota_task_status` | OTA 任务状态 |
| OTA_TASK_RECORD_STATUS | `iot_ota_task_record_status` | OTA 记录状态 |