diff --git a/开发者文档/03-IoT领域/00-IoT领域总览.md b/开发者文档/03-IoT领域/00-IoT领域总览.md index 141f1d7..c872ce8 100644 --- a/开发者文档/03-IoT领域/00-IoT领域总览.md +++ b/开发者文档/03-IoT领域/00-IoT领域总览.md @@ -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、全量枚举清单、字典常量。 -> ⚠️ 注意:本目录下的规范即代表开发契约,开发进行代码结构设计和数据库表设计时,必须与之对齐。 \ No newline at end of file +> ⚠️ 注意:本目录下的文档即代表开发契约,开发进行代码结构设计和数据库表设计时,必须与之对齐。 \ No newline at end of file diff --git a/开发者文档/03-IoT领域/01-IoT模块技术现状全景.md b/开发者文档/03-IoT领域/01-IoT模块技术现状全景.md new file mode 100644 index 0000000..2de72ec --- /dev/null +++ b/开发者文档/03-IoT领域/01-IoT模块技术现状全景.md @@ -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** → 基础库 JAR,gateway 和 server 共用;定义消息总线、集成事件、设备 API 接口 +- **server** → 核心业务微服务(`IoTServerApplication`),对外提供 REST/RPC 接口 +- **gateway** → 设备接入网关(`IotGatewayServerApplication`),独立部署,通过消息总线与 server 通信 + +### 1.2 核心技术栈 + +| 领域 | 技术选型 | +|------|----------| +| 框架 | Spring Boot + Spring Cloud(Feign) | +| 消息总线 | Local / Redis Stream / RocketMQ(三选一,配置切换) | +| 时序数据库 | TDengine(设备属性 + 消息日志) | +| 关系数据库 | MySQL(业务实体 CRUD) | +| 缓存 | Redis(Spring Cache + 手动 Hash/ZSet/String) | +| 协议接入 | Vert.x(MQTT 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 | diff --git a/开发者文档/03-IoT领域/01-设备接入与控制主链路.md b/开发者文档/03-IoT领域/01-设备接入与控制主链路.md deleted file mode 100644 index c5e8902..0000000 --- a/开发者文档/03-IoT领域/01-设备接入与控制主链路.md +++ /dev/null @@ -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` 的状态翻转事件。 \ No newline at end of file diff --git a/开发者文档/03-IoT领域/02-物模型标准设计.md b/开发者文档/03-IoT领域/02-物模型标准设计.md deleted file mode 100644 index 5158244..0000000 --- a/开发者文档/03-IoT领域/02-物模型标准设计.md +++ /dev/null @@ -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 插入代码**。 \ No newline at end of file diff --git a/开发者文档/03-IoT领域/02-设备接入网关详解.md b/开发者文档/03-IoT领域/02-设备接入网关详解.md new file mode 100644 index 0000000..ca3c66f --- /dev/null +++ b/开发者文档/03-IoT领域/02-设备接入网关详解.md @@ -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` | 连接管理器(双 Map:endpoint→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):从报文提取手机号 → 查设备 → 生成随机鉴权码 → 存 Redis(30 天 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` → 查询设备信息 diff --git a/开发者文档/03-IoT领域/03-产品与设备管理详解.md b/开发者文档/03-IoT领域/03-产品与设备管理详解.md new file mode 100644 index 0000000..76c949b --- /dev/null +++ b/开发者文档/03-IoT领域/03-产品与设备管理详解.md @@ -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. 自动生成 deviceSecret(UUID)、设置状态 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\ | 分组 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 管理**:物模型变更时自动 ALTER(ADD/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 Value:JSON 序列化的 `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. 对超时设备发送离线消息,走完整消息处理链路 diff --git a/开发者文档/03-IoT领域/03-规则引擎与联动策略.md b/开发者文档/03-IoT领域/03-规则引擎与联动策略.md deleted file mode 100644 index 5d6d05e..0000000 --- a/开发者文档/03-IoT领域/03-规则引擎与联动策略.md +++ /dev/null @@ -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)` 动作实现自动核销。 \ No newline at end of file diff --git a/开发者文档/03-IoT领域/04-物模型管理详解.md b/开发者文档/03-IoT领域/04-物模型管理详解.md new file mode 100644 index 0000000..20c4506 --- /dev/null +++ b/开发者文档/03-IoT领域/04-物模型管理详解.md @@ -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` | 文本最大长度(≤2048,text 类型必填) | +| `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\ | 输入参数列表 | +| `outputParams` | List\ | 输出参数列表 | +| `method` | String | 设备执行的操作标识 | + +--- + +## 七、事件定义(ThingModelEvent) + +| 字段 | 类型 | 说明 | +|------|------|------| +| `identifier` | String | 事件标识符 | +| `name` | String | 事件名称 | +| `type` | String | `info`=信息, `alert`=告警, `error`=故障 | +| `outputParams` | List\ | 输出参数列表 | +| `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 diff --git a/开发者文档/03-IoT领域/05-规则引擎详解.md b/开发者文档/03-IoT领域/05-规则引擎详解.md new file mode 100644 index 0000000..2e26ca5 --- /dev/null +++ b/开发者文档/03-IoT领域/05-规则引擎详解.md @@ -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` | 设备 ID(DEVICE_ID_ALL=全部) | +| `identifier` | 物模型标识符(空=全部) | + +### 2.3 DataSink 类型与实现状态 + +| 类型 | type | 实现类 | 连接管理 | 状态 | +|------|------|--------|---------|------| +| HTTP | 1 | IotHttpDataSinkAction | RestTemplate(无池) | 已实现 | +| TCP | 2 | IotTcpDataRuleAction | Guava Cache(30min TTL) | 已实现 | +| Redis | 21 | IotRedisRuleAction | Redisson Cache(30min 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 # 触发器列表 +│ ├── type # 触发类型 +│ ├── productId / deviceId # 设备范围 +│ ├── identifier # 物模型标识符 +│ ├── operator / value # 比较条件 +│ ├── cronExpression # CRON(定时触发专用) +│ └── conditionGroups # 条件分组(外层 OR,内层 AND) +│ └── List +└── actions: List # 执行动作列表 + ├── 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 | 定时 | IotTimerTriggerMatcher(Quartz 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 Job(iot_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(直接用)/ CUMULATIVE(Redis 差值计算) +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 +``` diff --git a/开发者文档/03-IoT领域/06-告警与OTA详解.md b/开发者文档/03-IoT领域/06-告警与OTA详解.md new file mode 100644 index 0000000..164b3bc --- /dev/null +++ b/开发者文档/03-IoT领域/06-告警与OTA详解.md @@ -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\ | 关联场景规则 ID 列表(FIND_IN_SET 查询) | +| `receiveUserIds` | List\ | 接收用户 ID 列表 | +| `receiveTypes` | List\ | 接收方式: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. 定时 Job(deviceUpgradeJob)扫描 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 分钟** diff --git a/开发者文档/03-IoT领域/07-消息总线与集成事件详解.md b/开发者文档/03-IoT领域/07-消息总线与集成事件详解.md new file mode 100644 index 0000000..e9a142e --- /dev/null +++ b/开发者文档/03-IoT领域/07-消息总线与集成事件详解.md @@ -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 { + 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 + password(HmacSHA256) +- 参考阿里云 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 diff --git a/开发者文档/03-IoT领域/08-API契约与枚举汇总.md b/开发者文档/03-IoT领域/08-API契约与枚举汇总.md new file mode 100644 index 0000000..10aeb6c --- /dev/null +++ b/开发者文档/03-IoT领域/08-API契约与枚举汇总.md @@ -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` | +| `getDevice` | GET | `/device/get` | id | `IotDeviceSimpleRespDTO` | +| `batchGetDevices` | GET | `/device/batch-get` | ids | `List` | + +### 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` | +| `batchGetProperties` | POST | `/device/property/batch-get` | `DevicePropertyBatchQueryReqDTO` | `Map>` | +| `getPropertyHistory` | POST | `/device/property/history` | `DevicePropertyHistoryQueryReqDTO` | `List` | + +### 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` | + +### 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\ | 否 | 服务参数 | +| `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\ | 是 | 设备 ID 列表 | +| `identifiers` | List\ | 否 | 属性标识列表(空=全部) | + +### 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 记录状态 |