diff --git a/db/mysql/module/maku-module-iot.sql b/db/mysql/module/maku-module-iot.sql
new file mode 100644
index 0000000000000000000000000000000000000000..cd3eb8ca207af487663a1a6113f8811288b80386
--- /dev/null
+++ b/db/mysql/module/maku-module-iot.sql
@@ -0,0 +1,141 @@
+-- 表结构
+CREATE TABLE `iot_device` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
+ `code` varchar(255) NOT NULL COMMENT '编码',
+ `name` varchar(255) NOT NULL COMMENT '名称',
+ `type` int NOT NULL COMMENT '设备类型,1.手持设备,2.柜体,3传感设备',
+ `uid` varchar(255) NOT NULL COMMENT '唯一标识码',
+ `secret` varchar(255) DEFAULT NULL COMMENT '设备密钥',
+ `app_version` varchar(255) DEFAULT NULL COMMENT 'App版本号',
+ `battery_percent` varchar(10) DEFAULT NULL COMMENT '电池电量百分比',
+ `temperature` varchar(10) DEFAULT NULL COMMENT '温度',
+ `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态,0禁用,1启用',
+ `running_status` int NOT NULL DEFAULT '0' COMMENT '运行状态,0.离线状态 1.在线状态 2.正常待机 3.用户使用中 4.OTA升级中',
+ `up_time` datetime DEFAULT NULL COMMENT '上线时间',
+ `down_time` datetime DEFAULT NULL COMMENT '下线时间',
+ `tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
+ `creator` bigint DEFAULT NULL COMMENT '创建者',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `updater` bigint DEFAULT NULL COMMENT '更新者',
+ `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+ `version` int DEFAULT NULL COMMENT '版本号',
+ `deleted` tinyint DEFAULT NULL COMMENT '删除标识 0:正常 1:已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备表';
+
+CREATE TABLE `iot_device_event_log` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `device_id` bigint NOT NULL COMMENT '设备id',
+ `event_type` tinyint NOT NULL COMMENT '事件类型',
+ `event_uid` varchar(50) DEFAULT NULL COMMENT '事件标识id',
+ `event_payload` varchar(1000) DEFAULT NULL COMMENT '事件数据',
+ `event_time` datetime DEFAULT NULL COMMENT '事件时间',
+ `tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
+ `version` int DEFAULT NULL COMMENT '版本号',
+ `deleted` tinyint DEFAULT NULL COMMENT '删除标识 0:正常 1:已删除',
+ `creator` bigint DEFAULT NULL COMMENT '创建者',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `updater` bigint DEFAULT NULL COMMENT '更新者',
+ `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备事件日志';
+
+CREATE TABLE `iot_device_service_log` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `device_id` bigint NOT NULL COMMENT '设备id',
+ `service_type` tinyint NOT NULL COMMENT '服务类型',
+ `service_uid` varchar(50) DEFAULT NULL COMMENT '服务标识id',
+ `service_payload` varchar(1000) DEFAULT NULL COMMENT '服务数据',
+ `service_time` datetime DEFAULT NULL COMMENT '服务时间',
+ `tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
+ `version` int DEFAULT NULL COMMENT '版本号',
+ `deleted` tinyint DEFAULT NULL COMMENT '删除标识 0:正常 1:已删除',
+ `creator` bigint DEFAULT NULL COMMENT '创建者',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `updater` bigint DEFAULT NULL COMMENT '更新者',
+ `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备服务日志';
+
+
+-- 菜单,权限
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ( NULL, '物联网平台', NULL, NULL, 0, 0, 'icon-printer-fill', 6, 0, 0, 10000,now(), 10000, now());
+
+SET @pid = @@identity;
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES ((SELECT @pid) , '设备列表', 'iot/device/index', NULL, 0, 0, 'icon-menu', 0, 0, 0, 10000, now(), 10000, now());
+
+SET @menuId = @@identity;
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '查看', '', 'iot:device:page', 1, 0, '', 0, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '新增', '', 'iot:device:save', 1, 0, '', 1, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '修改', '', 'iot:device:update,iot:device:info', 1, 0, '', 2, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '删除', '', 'iot:device:delete', 1, 0, '', 3, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '下发指令', '', 'iot:device:send', 1, 0, '', 4, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '上报数据', '', 'iot:device:report', 1, 0, '', 5, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '设备事件日志', '', 'iot:device_event_log:page', 1, 0, '', 5, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time)
+VALUES (@menuId, '设备服务日志', '', 'iot:device_service_log:page', 1, 0, '', 5, 0, 0, 10000, now(), 10000, now());
+
+-- 字典数据
+INSERT INTO sys_dict_type (dict_type,dict_name,remark,sort,tenant_id,version,deleted,creator,create_time,updater,update_time )VALUE( 'device_type', '设备类型', '设备类型', 0, 10000, 0, 0, 10000, now(), 10000, now() );
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '手持设备', '1', 'primary', '', 0, 10000, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '柜体', '2', 'primary', '', 1, 10000, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '传感设备', '3', 'primary', '', 2, 10000, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_dict_type (dict_type,dict_name,remark,sort,tenant_id,version,deleted,creator,create_time,updater,update_time )VALUES( 'device_running_status', '设备运行状态', '设备运行状态:离线|在线|待机|使用中|OTA升级中', 0, 10000, 0, 0, 10000, now(), 10000, now() );
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '离线状态', '0', 'danger', NULL, 0, 10000, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '在线状态', '1', 'success', NULL, 1, 10000, 0, 0, 10000, now(), 10000, now());
+
+
+INSERT INTO sys_dict_type (dict_type,dict_name,remark,sort,tenant_id,version,deleted,creator,create_time,updater,update_time )VALUES( 'device_command', '设备指令', '设备服务具备的功能', 0, 10000, 0, 0, 10000, now(), 10000, now() );
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '远程锁定', 'LOCK', NULL, NULL, 0, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '远程解锁', 'UNLOCK', NULL, NULL, 1, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '登录', 'SIGN_ON', NULL, NULL, 2, NULL, 0, 1, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '登出', 'SIGN_OFF', NULL, NULL, 3, NULL, 0, 1, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), 'OTA升级', 'OTA_UPGRADE', NULL, NULL, 4, NULL, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_dict_type (dict_type,dict_name,remark,sort,tenant_id,version,deleted,creator,create_time,updater,update_time )VALUES( 'device_property', '设备属性', '设备通用属性:运行状态|APP版本|电池电量百分比|温度', 0, 10000, 0, 0, 10000, now(), 10000, now() );
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '运行状态', 'RUNNING_STATUS', NULL, NULL, 0, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), 'APP版本', 'APP_VERSION', NULL, NULL, 1, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '电池电量百分比', 'BATTERY_PERCENT', NULL, NULL, 2, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '温度', 'TEMPERATURE', NULL, NULL, 3, NULL, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_dict_type (dict_type,dict_name,remark,sort,tenant_id,version,deleted,creator,create_time,updater,update_time )VALUES( 'device_event_type', '事件类型', '事件日志类型', 0, 10000, 0, 0, 10000, now(), 10000, now() );
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '下线', 'OFFLINE', 'danger', NULL, 1, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '上线', 'ONLINE', 'primary', NULL, 2, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '登录', 'SIGN_ON', 'primary', NULL, 3, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '退出登录', 'SIGN_OFF', 'danger', NULL, 4, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), 'OTA升级', 'OTA_UPGRADE', 'primary', NULL, 5, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '设备远程锁定', 'LOCK', 'primary', NULL, 6, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '设备远程解锁', 'UNLOCK', 'primary', NULL,7, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), 'APP版本信息', 'APP_VERSION_REPORT', 'primary', NULL, 8, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '电池电量', 'BATTERY_PERCENT_REPORT', 'primary', NULL, 9, NULL, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, label_class, remark, sort, tenant_id, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '温度', 'TEMPERATURE_REPORT', 'primary', NULL, 0, NULL, 10, 0, 10000, now(), 10000, now());
+
+
+
+
+
+
+
diff --git a/deploy/docker-compose-emqx.yml b/deploy/docker-compose-emqx.yml
new file mode 100644
index 0000000000000000000000000000000000000000..af84a448f852558291f46a9feffe920c980925ac
--- /dev/null
+++ b/deploy/docker-compose-emqx.yml
@@ -0,0 +1,51 @@
+version: '3.9'
+# 通过 Docker Compose 构建 EMQX 集群
+services:
+ maku-emqx1:
+ image: emqx:5.7.1
+ container_name: maku-emqx1
+ environment:
+ - "EMQX_NODE_NAME=emqx@node1.emqx.io"
+ - "EMQX_CLUSTER__DISCOVERY_STRATEGY=static"
+ - "EMQX_CLUSTER__STATIC__SEEDS=[emqx@node1.emqx.io,emqx@node2.emqx.io]"
+ healthcheck:
+ test: ["CMD", "/opt/emqx/bin/emqx", "ctl", "status"]
+ interval: 5s
+ timeout: 25s
+ retries: 5
+ networks:
+ emqx-bridge:
+ aliases:
+ - node1.emqx.io
+ ports:
+ - 1883:1883
+ - 8083:8083
+ - 8084:8084
+ - 8883:8883
+ - 18083:18083
+ # 如果需要持久 Docker 容器 ,请将以下目录挂载到容器外部,这样即使容器被删除数据也不会丢失
+ volumes:
+ - /work/www/maku-boot/emqx/data:/opt/emqx/data
+ - /work/www/maku-boot/emqx/log:/opt/emqx/log
+# maku-emqx2:
+# image: emqx:5.7.1
+# container_name: maku-emqx2
+# environment:
+# - "EMQX_NODE_NAME=emqx@node2.emqx.io"
+# - "EMQX_CLUSTER__DISCOVERY_STRATEGY=static"
+# - "EMQX_CLUSTER__STATIC__SEEDS=[emqx@node1.emqx.io,emqx@node2.emqx.io]"
+# healthcheck:
+# test: ["CMD", "/opt/emqx/bin/emqx", "ctl", "status"]
+# interval: 5s
+# timeout: 25s
+# retries: 5
+# networks:
+# emqx-bridge:
+# aliases:
+# - node2.emqx.io
+# volumes:
+# - $PWD/emqx2_data:/opt/emqx/data
+
+networks:
+ emqx-bridge:
+ driver: bridge
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/pom.xml b/maku-boot-module/maku-module-iot/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d38864a0b6c611e6fc6b3e2a1d7e8e12951980a9
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/pom.xml
@@ -0,0 +1,54 @@
+
+
+ net.maku
+ maku-boot-module
+ ${revision}
+
+ 4.0.0
+ maku-module-iot
+ jar
+
+
+
+ net.maku
+ maku-framework
+ ${revision}
+
+
+ org.springframework.integration
+ spring-integration-mqtt
+
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+
+
+ org.mockito
+ mockito-core
+ 4.0.0
+ test
+
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 4.0.0
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceController.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceController.java
new file mode 100644
index 0000000000000000000000000000000000000000..1433db12670848a4ebe5aa9559df844010e3ad38
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceController.java
@@ -0,0 +1,110 @@
+package net.maku.iot.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.utils.PageResult;
+import net.maku.framework.common.utils.Result;
+import net.maku.iot.convert.IotDeviceConvert;
+import net.maku.iot.entity.IotDeviceEntity;
+import net.maku.iot.query.IotDeviceQuery;
+import net.maku.iot.service.IotDeviceService;
+import net.maku.iot.vo.DeviceCommandVO;
+import net.maku.iot.vo.DeviceReportAttributeDataVO;
+import net.maku.iot.vo.IotDeviceVO;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 设备表
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@RestController
+@RequestMapping("iot/device")
+@Tag(name = "设备表")
+@AllArgsConstructor
+public class IotDeviceController {
+ private final IotDeviceService iotDeviceService;
+
+ @GetMapping("/page")
+ @Operation(summary = "分页")
+ @PreAuthorize("hasAuthority('iot:device:page')")
+ public Result> page(@ParameterObject @Valid IotDeviceQuery query) {
+ PageResult page = iotDeviceService.page(query);
+
+ return Result.ok(page);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "信息")
+ @PreAuthorize("hasAuthority('iot:device:info')")
+ public Result get(@PathVariable("id") Long id) {
+ IotDeviceEntity entity = iotDeviceService.getById(id);
+
+ return Result.ok(IotDeviceConvert.INSTANCE.convert(entity));
+ }
+
+ @PostMapping
+ @Operation(summary = "保存")
+ @PreAuthorize("hasAuthority('iot:device:save')")
+ public Result save(@RequestBody IotDeviceVO vo) {
+ iotDeviceService.save(vo);
+
+ return Result.ok();
+ }
+
+ @PutMapping
+ @Operation(summary = "修改")
+ @PreAuthorize("hasAuthority('iot:device:update')")
+ public Result update(@RequestBody @Valid IotDeviceVO vo) {
+ iotDeviceService.update(vo);
+
+ return Result.ok();
+ }
+
+ @DeleteMapping
+ @Operation(summary = "删除")
+ @PreAuthorize("hasAuthority('iot:device:delete')")
+ public Result delete(@RequestBody List idList) {
+ iotDeviceService.delete(idList);
+
+ return Result.ok();
+ }
+
+ @PostMapping("/asyncSendCommand")
+ @Operation(summary = "下发指令-不等待设备回复")
+ @PreAuthorize("hasAuthority('iot:device:send')")
+ public Result asyncSendCommand(@RequestBody DeviceCommandVO vo) {
+ iotDeviceService.asyncSendCommand(vo);
+ return Result.ok();
+ }
+
+ @PostMapping("/syncSendCommand")
+ @Operation(summary = "下发指令-等待设备回复")
+ @PreAuthorize("hasAuthority('iot:device:send')")
+ public Result syncSendCommand(@RequestBody DeviceCommandVO vo) {
+ iotDeviceService.syncSendCommand(vo);
+ return Result.ok();
+ }
+
+ @PostMapping("/syncSendCommand/debug")
+ @Operation(summary = "下发指令-等待设备回复-调试")
+ @PreAuthorize("hasAuthority('iot:device:send')")
+ public Result syncSendCommandDebug(@RequestBody DeviceCommandVO vo) {
+ return Result.ok(iotDeviceService.syncSendCommandDebug(vo).getResponsePayload());
+ }
+
+ @PostMapping("/simulateDeviceReportAttributeData")
+ @Operation(summary = "模拟设备属性数据上报")
+ @PreAuthorize("hasAuthority('iot:device:report')")
+ public Result simulateDeviceReportAttributeData(@RequestBody DeviceReportAttributeDataVO vo) {
+ iotDeviceService.simulateDeviceReportAttributeData(vo);
+ return Result.ok();
+ }
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceEventLogController.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceEventLogController.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfb04f4ac4f3067461893df2649a12465eaa4ae1
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceEventLogController.java
@@ -0,0 +1,76 @@
+package net.maku.iot.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.utils.PageResult;
+import net.maku.framework.common.utils.Result;
+import net.maku.iot.convert.IotDeviceEventLogConvert;
+import net.maku.iot.entity.IotDeviceEventLogEntity;
+import net.maku.iot.query.IotDeviceEventLogQuery;
+import net.maku.iot.service.IotDeviceEventLogService;
+import net.maku.iot.vo.IotDeviceEventLogVO;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 设备事件日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@RestController
+@RequestMapping("iot/device_event_log")
+@Tag(name = "设备事件日志")
+@AllArgsConstructor
+public class IotDeviceEventLogController {
+ private final IotDeviceEventLogService iotDeviceEventLogService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @PreAuthorize("hasAuthority('iot:device_event_log:page')")
+ public Result> page(@ParameterObject @Valid IotDeviceEventLogQuery query) {
+ PageResult page = iotDeviceEventLogService.page(query);
+
+ return Result.ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "信息")
+ @PreAuthorize("hasAuthority('iot:device_event_log:info')")
+ public Result get(@PathVariable("id") Long id) {
+ IotDeviceEventLogEntity entity = iotDeviceEventLogService.getById(id);
+
+ return Result.ok(IotDeviceEventLogConvert.INSTANCE.convert(entity));
+ }
+
+ @PostMapping
+ @Operation(summary = "保存")
+ @PreAuthorize("hasAuthority('iot:device_event_log:save')")
+ public Result save(@RequestBody IotDeviceEventLogVO vo) {
+ iotDeviceEventLogService.save(vo);
+
+ return Result.ok();
+ }
+
+ @PutMapping
+ @Operation(summary = "修改")
+ @PreAuthorize("hasAuthority('iot:device_event_log:update')")
+ public Result update(@RequestBody @Valid IotDeviceEventLogVO vo) {
+ iotDeviceEventLogService.update(vo);
+
+ return Result.ok();
+ }
+
+ @DeleteMapping
+ @Operation(summary = "删除")
+ @PreAuthorize("hasAuthority('iot:device_event_log:delete')")
+ public Result delete(@RequestBody List idList) {
+ iotDeviceEventLogService.delete(idList);
+
+ return Result.ok();
+ }
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceServiceLogController.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceServiceLogController.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c76075277b0c7818fa3f30131e687b3571cb11d
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/controller/IotDeviceServiceLogController.java
@@ -0,0 +1,76 @@
+package net.maku.iot.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.utils.PageResult;
+import net.maku.framework.common.utils.Result;
+import net.maku.iot.convert.IotDeviceServiceLogConvert;
+import net.maku.iot.entity.IotDeviceServiceLogEntity;
+import net.maku.iot.query.IotDeviceServiceLogQuery;
+import net.maku.iot.service.IotDeviceServiceLogService;
+import net.maku.iot.vo.IotDeviceServiceLogVO;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 设备服务日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@RestController
+@RequestMapping("iot/device_service_log")
+@Tag(name = "设备服务日志")
+@AllArgsConstructor
+public class IotDeviceServiceLogController {
+ private final IotDeviceServiceLogService iotDeviceServiceLogService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @PreAuthorize("hasAuthority('iot:device_service_log:page')")
+ public Result> page(@ParameterObject @Valid IotDeviceServiceLogQuery query) {
+ PageResult page = iotDeviceServiceLogService.page(query);
+
+ return Result.ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "信息")
+ @PreAuthorize("hasAuthority('iot:device_service_log:info')")
+ public Result get(@PathVariable("id") Long id) {
+ IotDeviceServiceLogEntity entity = iotDeviceServiceLogService.getById(id);
+
+ return Result.ok(IotDeviceServiceLogConvert.INSTANCE.convert(entity));
+ }
+
+ @PostMapping
+ @Operation(summary = "保存")
+ @PreAuthorize("hasAuthority('iot:device_service_log:save')")
+ public Result save(@RequestBody IotDeviceServiceLogVO vo) {
+ iotDeviceServiceLogService.save(vo);
+
+ return Result.ok();
+ }
+
+ @PutMapping
+ @Operation(summary = "修改")
+ @PreAuthorize("hasAuthority('iot:device_service_log:update')")
+ public Result update(@RequestBody @Valid IotDeviceServiceLogVO vo) {
+ iotDeviceServiceLogService.update(vo);
+
+ return Result.ok();
+ }
+
+ @DeleteMapping
+ @Operation(summary = "删除")
+ @PreAuthorize("hasAuthority('iot:device_service_log:delete')")
+ public Result delete(@RequestBody List idList) {
+ iotDeviceServiceLogService.delete(idList);
+
+ return Result.ok();
+ }
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceConvert.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..c332ae6e79be51c63260a90f6a593b424b3c3ce8
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceConvert.java
@@ -0,0 +1,25 @@
+package net.maku.iot.convert;
+
+import net.maku.iot.entity.IotDeviceEntity;
+import net.maku.iot.vo.IotDeviceVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 设备表
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Mapper
+public interface IotDeviceConvert {
+ IotDeviceConvert INSTANCE = Mappers.getMapper(IotDeviceConvert.class);
+
+ IotDeviceEntity convert(IotDeviceVO vo);
+
+ IotDeviceVO convert(IotDeviceEntity entity);
+
+ List convertList(List list);
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceEventLogConvert.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceEventLogConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..68a1c9d638fca2207c7c4584cddcaa0927aef8f1
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceEventLogConvert.java
@@ -0,0 +1,25 @@
+package net.maku.iot.convert;
+
+import net.maku.iot.entity.IotDeviceEventLogEntity;
+import net.maku.iot.vo.IotDeviceEventLogVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 设备事件日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Mapper
+public interface IotDeviceEventLogConvert {
+ IotDeviceEventLogConvert INSTANCE = Mappers.getMapper(IotDeviceEventLogConvert.class);
+
+ IotDeviceEventLogEntity convert(IotDeviceEventLogVO vo);
+
+ IotDeviceEventLogVO convert(IotDeviceEventLogEntity entity);
+
+ List convertList(List list);
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceServiceLogConvert.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceServiceLogConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b11ea711d8d6e13b08966c665468f18cda43cb8
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/convert/IotDeviceServiceLogConvert.java
@@ -0,0 +1,25 @@
+package net.maku.iot.convert;
+
+import net.maku.iot.entity.IotDeviceServiceLogEntity;
+import net.maku.iot.vo.IotDeviceServiceLogVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 设备服务日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Mapper
+public interface IotDeviceServiceLogConvert {
+ IotDeviceServiceLogConvert INSTANCE = Mappers.getMapper(IotDeviceServiceLogConvert.class);
+
+ IotDeviceServiceLogEntity convert(IotDeviceServiceLogVO vo);
+
+ IotDeviceServiceLogVO convert(IotDeviceServiceLogEntity entity);
+
+ List convertList(List list);
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceDao.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..be01471304787470bf9f6271f5ad1ba3ad952b24
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceDao.java
@@ -0,0 +1,15 @@
+package net.maku.iot.dao;
+
+import net.maku.framework.mybatis.dao.BaseDao;
+import net.maku.iot.entity.IotDeviceEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 设备表
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Mapper
+public interface IotDeviceDao extends BaseDao {
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceEventLogDao.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceEventLogDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..4adc63b98ef4f6b59a1fd11ac94da81174828410
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceEventLogDao.java
@@ -0,0 +1,15 @@
+package net.maku.iot.dao;
+
+import net.maku.framework.mybatis.dao.BaseDao;
+import net.maku.iot.entity.IotDeviceEventLogEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 设备事件日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Mapper
+public interface IotDeviceEventLogDao extends BaseDao {
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceServiceLogDao.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceServiceLogDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..509be3849e9e2da1c775d3e9c990ccd487f40847
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dao/IotDeviceServiceLogDao.java
@@ -0,0 +1,15 @@
+package net.maku.iot.dao;
+
+import net.maku.framework.mybatis.dao.BaseDao;
+import net.maku.iot.entity.IotDeviceServiceLogEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 设备服务日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Mapper
+public interface IotDeviceServiceLogDao extends BaseDao {
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dto/DeviceClientDTO.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dto/DeviceClientDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..667949e5b557d134cc42d2fec132436dbde7e99e
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/dto/DeviceClientDTO.java
@@ -0,0 +1,88 @@
+package net.maku.iot.dto;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
+import lombok.Builder;
+import lombok.Data;
+import net.maku.iot.entity.IotDeviceEntity;
+import net.maku.iot.enums.DeviceTypeEnum;
+
+import java.util.List;
+
+/**
+ * 设备客户端信息
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Data
+@Builder
+public class DeviceClientDTO {
+ /**
+ * 格式 : deviceType_tenantId_deviceId_uid
+ */
+ private static final String CLIENT_ID_TEMPLATE = "{}_{}_{}_{}";
+ private static final String INVALID_CLIENT_ID = "无效的设备clientId:{}";
+ /**
+ * 租户id
+ */
+ private Long tenantId;
+
+ /**
+ * 设备id
+ */
+ private Long deviceId;
+
+ /**
+ * 设备类型
+ */
+ private String deviceType;
+
+ /**
+ * 设备唯一标识
+ */
+ private String uid;
+
+ /**
+ * 生成clientId
+ *
+ * @return
+ */
+ public String buildClientId() {
+ if (StrUtil.isBlank(uid)) {
+ return null;
+ }
+ return StrUtil.format(CLIENT_ID_TEMPLATE, deviceType, tenantId, deviceId, uid);
+ }
+
+ /**
+ * 从clientId解析设备client信息
+ *
+ * @param clientId
+ * @return
+ */
+ public static DeviceClientDTO parse(String clientId) {
+ List clientIdParts = StrUtil.split(clientId, "_");
+ Assert.isTrue(clientIdParts.size() > 3, INVALID_CLIENT_ID, clientId);
+ return DeviceClientDTO.builder()
+ .deviceType(clientIdParts.get(0))
+ .tenantId(Long.valueOf(clientIdParts.get(1)))
+ .deviceId(Long.valueOf(clientIdParts.get(2)))
+ .uid(clientIdParts.get(3))
+ .build();
+ }
+
+ /**
+ * 从Device创建deviceClient
+ *
+ * @param device
+ * @return
+ */
+ public static DeviceClientDTO from(IotDeviceEntity device) {
+ return DeviceClientDTO.builder()
+ .deviceType(DeviceTypeEnum.parse(device.getType().toString()).name().toLowerCase())
+ .tenantId(device.getTenantId() == null ? 0 : device.getTenantId())
+ .deviceId(device.getId())
+ .uid(device.getUid())
+ .build();
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceEntity.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f0340991eb42d6d3725302017c3e5cc91775290
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceEntity.java
@@ -0,0 +1,86 @@
+package net.maku.iot.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import net.maku.framework.mybatis.entity.BaseEntity;
+
+import java.time.LocalDateTime;
+
+/**
+ * 设备表
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@EqualsAndHashCode(callSuper = false)
+@Data
+@TableName("iot_device")
+public class IotDeviceEntity extends BaseEntity {
+
+ /**
+ * 编码
+ */
+ private String code;
+
+ /**
+ * 名称
+ */
+ private String name;
+
+ /**
+ * 设备类型,1.手持设备,2.柜体,3传感设备
+ */
+ private Integer type;
+
+ /**
+ * 唯一标识码
+ */
+ private String uid;
+
+ /**
+ * 设备密钥
+ */
+ private String secret;
+
+ /**
+ * App版本号
+ */
+ private String appVersion;
+
+ /**
+ * 电池电量百分比
+ */
+ private String batteryPercent;
+
+ /**
+ * 温度
+ */
+ private String temperature;
+
+ /**
+ * 状态,0禁用,1正常
+ */
+ private Integer status;
+
+ /**
+ * 运行状态,0.离线状态 1.在线状态 2.正常待机 3.用户使用中 4.OTA升级中
+ */
+ private Integer runningStatus;
+
+ /**
+ * 上线时间
+ */
+ private LocalDateTime upTime;
+
+ /**
+ * 下线时间
+ */
+ private LocalDateTime downTime;
+
+ /**
+ * 租户ID
+ */
+ private Long tenantId;
+
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceEventLogEntity.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceEventLogEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b37b4908b949f99c582e80cbe10471a76574e8a
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceEventLogEntity.java
@@ -0,0 +1,51 @@
+package net.maku.iot.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import net.maku.framework.mybatis.entity.BaseEntity;
+
+import java.time.LocalDateTime;
+
+/**
+ * 设备事件日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@EqualsAndHashCode(callSuper = false)
+@Data
+@TableName("iot_device_event_log")
+public class IotDeviceEventLogEntity extends BaseEntity {
+
+ /**
+ * 设备id
+ */
+ private Long deviceId;
+
+ /**
+ * 事件类型
+ */
+ private Integer eventType;
+
+ /**
+ * 事件标识id
+ */
+ private String eventUid;
+
+ /**
+ * 事件数据
+ */
+ private String eventPayload;
+
+ /**
+ * 事件时间
+ */
+ private LocalDateTime eventTime;
+
+ /**
+ * 租户ID
+ */
+ private Long tenantId;
+
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceServiceLogEntity.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceServiceLogEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b20796890b5965e6e0e5ebabb2f1324d32bde5a
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/entity/IotDeviceServiceLogEntity.java
@@ -0,0 +1,51 @@
+package net.maku.iot.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import net.maku.framework.mybatis.entity.BaseEntity;
+
+import java.time.LocalDateTime;
+
+/**
+ * 设备服务日志
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@EqualsAndHashCode(callSuper = false)
+@Data
+@TableName("iot_device_service_log")
+public class IotDeviceServiceLogEntity extends BaseEntity {
+
+ /**
+ * 设备id
+ */
+ private Long deviceId;
+
+ /**
+ * 服务类型
+ */
+ private Integer serviceType;
+
+ /**
+ * 服务标识id
+ */
+ private String serviceUid;
+
+ /**
+ * 服务数据
+ */
+ private String servicePayload;
+
+ /**
+ * 服务时间
+ */
+ private LocalDateTime serviceTime;
+
+ /**
+ * 租户ID
+ */
+ private Long tenantId;
+
+
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceCommandEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceCommandEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f726806e89ed6f02d6ce87e0a1356f5d2c5213d
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceCommandEnum.java
@@ -0,0 +1,78 @@
+package net.maku.iot.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 服务端下发的指令枚举类
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DeviceCommandEnum {
+
+ /**
+ * 远程锁定
+ */
+ LOCK(1, "远程锁定", DeviceEventTypeEnum.LOCK),
+
+ /**
+ * 远程解锁
+ */
+ UNLOCK(2, "远程解锁", DeviceEventTypeEnum.UNLOCK),
+
+ /**
+ * OTA升级
+ */
+ OTA_UPGRADE(3, "OTA升级", DeviceEventTypeEnum.OTA_UPGRADE);
+
+ /**
+ * 类型值
+ */
+ private final Integer value;
+
+ /**
+ * 显示名称
+ */
+ private final String title;
+
+
+ /**
+ * 对应的设备事件日志类型
+ */
+ private final DeviceEventTypeEnum eventType;
+
+ /**
+ * 解析指定的valueName
+ *
+ * @param valueName
+ * @return DeviceCommandEnum
+ * @throws IllegalArgumentException
+ */
+ public static DeviceCommandEnum parse(String valueName) {
+ return Arrays.stream(DeviceCommandEnum.values())
+ .filter(d -> d.name().equalsIgnoreCase(valueName)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(
+ StrUtil.format("无效的DeviceCommandEnum值:{}", valueName)));
+ }
+
+ /**
+ * 根据 value值 获取对应枚举
+ *
+ * @param value
+ * @return DeviceCommandEnum
+ */
+ public static DeviceCommandEnum getEnum(Integer value) {
+ for (DeviceCommandEnum commandEnum : DeviceCommandEnum.values()) {
+ if (commandEnum.getValue().equals(value)) {
+ return commandEnum;
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceEventTypeEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceEventTypeEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3578e7c441c5f4e7469b2bd46ce1a4df8cc1987
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceEventTypeEnum.java
@@ -0,0 +1,105 @@
+package net.maku.iot.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 设备事件类型枚举
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DeviceEventTypeEnum {
+ /**
+ * 下线
+ */
+ OFFLINE(0, "下线事件"),
+
+ /**
+ * 上线
+ */
+ ONLINE(1, "上线事件"),
+
+ /**
+ * 登录
+ */
+ SIGN_ON(2, "登录事件"),
+
+ /**
+ * 退出登录
+ */
+ SIGN_OFF(3, "退出登录事件"),
+ /**
+ * OTA升级
+ */
+ OTA_UPGRADE(4, "OTA升级事件"),
+
+ /**
+ * 设备远程锁定
+ */
+ LOCK(5, "设备远程锁定"),
+
+ /**
+ * 设备远程解锁
+ */
+ UNLOCK(6, "设备远程解锁"),
+
+ /**
+ * APP版本信息上报
+ */
+ APP_VERSION_REPORT(7, "APP版本信息上报"),
+
+ /**
+ * 电池电量上报
+ */
+ BATTERY_PERCENT_REPORT(8, "电池电量上报"),
+
+ /**
+ * 温度上报
+ */
+ TEMPERATURE_REPORT(9, "温度上报");
+
+ /**
+ * 类型值
+ */
+ private final Integer value;
+
+ /**
+ * 类型名称
+ */
+ private final String title;
+
+
+ /**
+ * 解析指定的valueName
+ *
+ * @param valueName
+ * @return DeviceEventTypeEnum
+ * @throws IllegalArgumentException
+ */
+ public static DeviceEventTypeEnum parse(String valueName) {
+ return Arrays.stream(DeviceEventTypeEnum.values())
+ .filter(d -> d.name().equalsIgnoreCase(valueName)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(
+ StrUtil.format("无效的DeviceEventTypeEnum值:{}", valueName)));
+ }
+
+ /**
+ * 根据 value值 获取对应枚举
+ *
+ * @param value
+ * @return DeviceCommandEnum
+ */
+ public static DeviceEventTypeEnum getEnum(Integer value) {
+ for (DeviceEventTypeEnum commandEnum : DeviceEventTypeEnum.values()) {
+ if (commandEnum.getValue().equals(value)) {
+ return commandEnum;
+ }
+ }
+ return null;
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DevicePropertyEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DevicePropertyEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..74d000f1b5bc361b6c3084d687021d96a0256ec8
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DevicePropertyEnum.java
@@ -0,0 +1,40 @@
+package net.maku.iot.enums;
+
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 设备属性枚举
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DevicePropertyEnum {
+ /**
+ * 运行状态
+ */
+ RUNNING_STATUS(1),
+
+ /**
+ * APP版本
+ */
+ APP_VERSION(2),
+
+ /**
+ * 电池电量百分比
+ */
+ BATTERY_PERCENT(3),
+
+ /**
+ * 温度
+ */
+ TEMPERATURE(4);
+
+ /**
+ * 类型值
+ */
+ private final Integer value;
+
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceRunningStatusEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceRunningStatusEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3c2a68bb9afbb3caab7cb934013cee75977ebb9
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceRunningStatusEnum.java
@@ -0,0 +1,58 @@
+package net.maku.iot.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 设备运行状态枚举
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DeviceRunningStatusEnum {
+ /**
+ * 离线状态
+ */
+ OFFLINE(0, "离线", DeviceEventTypeEnum.OFFLINE),
+
+ /**
+ * 在线状态
+ */
+ ONLINE(1, "在线", DeviceEventTypeEnum.ONLINE);
+
+ /**
+ * 状态值
+ */
+ private final Integer value;
+
+ /**
+ * 状态显示名称
+ */
+ private final String title;
+
+ /**
+ * 设备事件类型
+ */
+ private final DeviceEventTypeEnum eventType;
+
+
+ /**
+ * 解析指定的valueName
+ *
+ * @param valueName
+ * @return DeviceRunningStatusEnum
+ * @throws IllegalArgumentException
+ */
+ public static DeviceRunningStatusEnum parse(String valueName) {
+ return Arrays.stream(DeviceRunningStatusEnum.values())
+ .filter(d -> d.getValue().toString().equals(valueName)
+ || d.name().equalsIgnoreCase(valueName)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(
+ StrUtil.format("无效的DeviceRunningStatusEnum值:{}", valueName)));
+ }
+
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceServiceEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceServiceEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..46c91337f0ffb9028bd4700830e316bd3ab6566f
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceServiceEnum.java
@@ -0,0 +1,18 @@
+package net.maku.iot.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum DeviceServiceEnum {
+
+ COMMAND_ID("命令ID");
+
+ /**
+ * 类型值
+ */
+ private final String value;
+
+
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceTopicEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceTopicEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c1fbcfe2fcca830acd1c28d5b412bb0f85e78cc
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceTopicEnum.java
@@ -0,0 +1,141 @@
+package net.maku.iot.enums;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.maku.iot.dto.DeviceClientDTO;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * 设备消息主题类型枚举
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DeviceTopicEnum {
+ /**
+ * 设备指令下发主题
+ */
+ COMMAND("command"),
+
+ /**
+ * 设备信息上报主题(指令响应反馈)
+ */
+ COMMAND_RESPONSE("command_response"),
+
+ /**
+ * 设备信息上报主题(属性相关)
+ */
+ PROPERTY("property");
+
+ /**
+ * 设备信息上报主题前缀
+ */
+ public static final String TOPIC_PREFIX = "/maku/device";
+
+
+ /**
+ * MQTT 主题模板,参数:前缀/clientid(deviceType_tenantId_deviceId_uid)/设备主题
+ */
+ private static final String TOPIC_TEMPLATE = "{}/{}/{}";
+
+ /**
+ * 设备主题通配符模板,参数:前缀/+/设备主题
+ */
+ private static final String TOPIC_WILDCARD_TEMPLATE = "{}/+/{}";
+
+ /**
+ * MQTT 主题
+ */
+ private final String topic;
+
+ /**
+ * 获取设备主题通配符
+ *
+ * @return 主题通配符
+ */
+ public String getWildcard() {
+ return StrUtil.format(TOPIC_WILDCARD_TEMPLATE, TOPIC_PREFIX, getTopic());
+ }
+
+ /**
+ * 构建完整路径的设备主题
+ *
+ * @param deviceClient 设备客户端信息
+ * @return 完整路径的设备主题
+ */
+ public String buildTopic(DeviceClientDTO deviceClient) {
+ return StrUtil.format(TOPIC_TEMPLATE, TOPIC_PREFIX, deviceClient.buildClientId(), getTopic());
+ }
+
+ /**
+ * 解析指定的主题字符串为 DeviceTopicEnum 枚举,若无效则抛出 IllegalArgumentException 异常
+ *
+ * @param topic 主题字符串
+ * @return 对应的 DeviceTopicEnum 枚举
+ * @throws IllegalArgumentException 当主题字符串无效时抛出
+ */
+ public static DeviceTopicEnum parse(String topic) {
+ String topicSuffix = StrUtil.startWith(topic, TOPIC_PREFIX) ? StrUtil.subAfter(topic, "/", true) : topic;
+ return Arrays.stream(DeviceTopicEnum.values())
+ .filter(d -> d.name().equalsIgnoreCase(topicSuffix)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(StrUtil.format("无效的 DeviceTopicEnum 值:{}", topic)));
+ }
+
+ /**
+ * 判断指定的主题是否以给定前缀开头
+ *
+ * @param topic 主题字符串
+ * @param prefix 前缀字符串
+ * @return 若主题以给定前缀开头则返回 true,否则返回 false
+ */
+ public static boolean startsWith(String topic, String prefix) {
+ try {
+ DeviceTopicEnum deviceTopic = parse(topic);
+ return deviceTopic.getTopic().startsWith(prefix);
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ /**
+ * 解析主题并获取其完整信息(设备信息及主题)
+ *
+ * @param topic 主题字符串
+ * @return 包含设备信息及主题的 DeviceTopicContext 对象
+ */
+ public static DeviceTopicContext parseContext(String topic) {
+ String topicSuffix = StrUtil.subAfter(topic, TOPIC_PREFIX, false);
+ List parts = StrUtil.split(topicSuffix, "/", true, true);
+ Assert.isTrue(parts.size() == 2, "无效的设备主题:{}", topic);
+
+ DeviceClientDTO deviceClient = DeviceClientDTO.parse(parts.get(0));
+ DeviceTopicEnum deviceTopic = DeviceTopicEnum.parse(parts.get(1));
+
+ return DeviceTopicContext.builder()
+ .client(deviceClient)
+ .topic(deviceTopic)
+ .build();
+ }
+
+ @Data
+ @Builder
+ public static class DeviceTopicContext {
+ /**
+ * 设备客户端信息
+ */
+ private DeviceClientDTO client;
+
+ /**
+ * 设备主题
+ */
+ private DeviceTopicEnum topic;
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceTypeEnum.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceTypeEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..17cfef6f88818d01cfe27f100248c0c21804fb07
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/enums/DeviceTypeEnum.java
@@ -0,0 +1,52 @@
+package net.maku.iot.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 设备类型枚举,新增的设备类型枚举命名不允许使用"_"
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Getter
+@RequiredArgsConstructor
+public enum DeviceTypeEnum {
+ /**
+ * 手持设备
+ */
+ HANDSET(1),
+
+ /**
+ * 柜体
+ */
+ CABINET(2),
+
+ /**
+ * 传感设备
+ */
+ SENSOR(3);
+
+ /**
+ * 类型值
+ */
+ private final Integer value;
+
+ /**
+ * 解析指定的valueName
+ *
+ * @param valueName
+ * @return DeviceTypeEnum
+ * @throws IllegalArgumentException
+ */
+ public static DeviceTypeEnum parse(String valueName) {
+ return Arrays.stream(DeviceTypeEnum.values())
+ .filter(d -> d.getValue().toString().equals(valueName)
+ || d.name().equalsIgnoreCase(valueName)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(
+ StrUtil.format("无效的DeviceTypeEnum值:{}", valueName)));
+ }
+
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/MqttGateway.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/MqttGateway.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3114a1c4a62ae86938e06bdcc0192353200bfd8
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/MqttGateway.java
@@ -0,0 +1,42 @@
+package net.maku.iot.mqtt;
+
+import net.maku.iot.mqtt.config.MqttConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.mqtt.support.MqttHeaders;
+import org.springframework.integration.support.MessageBuilder;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.stereotype.Component;
+
+/**
+ * MQTT网关
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Component
+@MessagingGateway(defaultRequestChannel = MqttConfig.OUTBOUND_CHANNEL)
+public class MqttGateway {
+
+ @Autowired
+ private MqttConfig mqttConfig;
+
+
+ public void sendToMqtt(String payload) {
+ mqttConfig.mqttOutboundHandler().handleMessage(MessageBuilder.withPayload(payload).build());
+ }
+
+
+ public void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload) {
+ mqttConfig.mqttOutboundHandler().handleMessage(MessageBuilder.withPayload(payload).setHeader(MqttHeaders.TOPIC, topic).build());
+ }
+
+
+ public void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload) {
+ mqttConfig.mqttOutboundHandler().handleMessage(MessageBuilder.withPayload(payload).setHeader(MqttHeaders.TOPIC, topic).setHeader(MqttHeaders.QOS, qos).build());
+ }
+
+
+ public void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.RETAINED) boolean retained, String payload) {
+ mqttConfig.mqttOutboundHandler().handleMessage(MessageBuilder.withPayload(payload).setHeader(MqttHeaders.TOPIC, topic).setHeader(MqttHeaders.RETAINED, retained).build());
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/config/MqttConfig.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/config/MqttConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ff261b647956d89dcab14c9d9be9a15969886eb
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/config/MqttConfig.java
@@ -0,0 +1,164 @@
+package net.maku.iot.mqtt.config;
+
+import jakarta.annotation.PostConstruct;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.maku.iot.enums.DeviceTopicEnum;
+import net.maku.iot.mqtt.factory.MqttMessageHandlerFactory;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
+import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
+import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
+import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
+import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.stereotype.Component;
+
+/**
+ * MQTT 配置类,用于设置和管理 MQTT 连接和消息处理。
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Component
+@ConfigurationProperties(prefix = "spring.mqtt")
+@Slf4j
+@Configuration
+@IntegrationComponentScan
+@RequiredArgsConstructor
+@Data
+public class MqttConfig {
+
+ public static final String OUTBOUND_CHANNEL = "mqttOutboundChannel";
+ public static final String INPUT_CHANNEL = "mqttInputChannel";
+
+ // MQTT 用户名
+ private String username;
+
+ // MQTT 密码
+ private String password;
+
+ // MQTT 服务器 URL
+ private String hostUrl;
+
+ // 客户端 ID
+ private String clientId;
+
+ // 默认主题
+ private String defaultTopic;
+
+ // 处理 MQTT 消息的工厂
+ private final MqttMessageHandlerFactory mqttMessageHandlerFactory;
+
+ @PostConstruct
+ public void init() {
+ log.info("MQTT 主机: {} 客户端ID: {} 默认主题:{}", this.hostUrl, this.clientId, this.defaultTopic);
+ }
+
+ /**
+ * 配置并返回一个 MqttPahoClientFactory 实例,用于创建 MQTT 客户端连接。
+ *
+ * @return MqttPahoClientFactory
+ */
+ @Bean
+ public MqttPahoClientFactory mqttClientFactory() {
+ // 设置连接选项,包括服务器 URI、用户名和密码。
+ final MqttConnectOptions options = new MqttConnectOptions();
+ options.setServerURIs(new String[]{hostUrl});
+ options.setUserName(username);
+ options.setPassword(password.toCharArray());
+ final DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
+ factory.setConnectionOptions(options);
+ return factory;
+ }
+
+ /**
+ * 创建一个用于发送 MQTT 消息的 MessageChannel。
+ *
+ * @return MessageChannel
+ */
+ @Bean(OUTBOUND_CHANNEL)
+ public MessageChannel mqttOutboundChannel() {
+ return new DirectChannel();
+ }
+
+ /**
+ * 配置用于发送 MQTT 消息的 MessageHandler。
+ *
+ * @return MessageHandler
+ */
+ @Bean
+ @ServiceActivator(inputChannel = OUTBOUND_CHANNEL)
+ public MessageHandler mqttOutboundHandler() {
+ // 使用 MqttPahoMessageHandler 创建一个新的 MQTT 客户端连接,用于发布消息。
+ final MqttPahoMessageHandler handler = new MqttPahoMessageHandler(clientId + "_pub", mqttClientFactory());
+ handler.setDefaultQos(1);
+ handler.setDefaultRetained(false);
+ handler.setDefaultTopic(defaultTopic);
+ handler.setAsync(true);
+ return handler;
+ }
+
+ /**
+ * 创建用于接收 MQTT 消息的 MessageChannel。
+ *
+ * @return MessageChannel
+ */
+ @Bean
+ public MessageChannel mqttInputChannel() {
+ return new DirectChannel();
+ }
+
+ /**
+ * 配置 客户端,订阅的主题,
+ * PROPERTY:设备属性上报主题,
+ * COMMAND_RESPONSE:下发指令执行结果主题
+ *
+ * @return MqttPahoMessageDrivenChannelAdapter
+ */
+ @Bean
+ @ConditionalOnProperty(name = "spring.mqtt.mqttEnabled", havingValue = "true")
+ public MqttPahoMessageDrivenChannelAdapter mqttInboundAdapter() {
+ final MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
+ clientId + "_sub",
+ mqttClientFactory(), DeviceTopicEnum.PROPERTY.getWildcard(),
+ DeviceTopicEnum.COMMAND_RESPONSE.getWildcard()
+ );
+ adapter.setCompletionTimeout(15000);
+ adapter.setConverter(new DefaultPahoMessageConverter());
+ adapter.setQos(1);
+ adapter.setOutputChannel(mqttInputChannel());
+ return adapter;
+ }
+
+ /**
+ * 通过通道获取数据并处理消息。
+ *
+ * @return MessageHandler
+ */
+ @Bean
+ @ServiceActivator(inputChannel = INPUT_CHANNEL)
+ public MessageHandler mqttMessageHandler() {
+ return message -> {
+ String topic = (String) message.getHeaders().get("mqtt_receivedTopic");
+ if (topic != null) {
+ mqttMessageHandlerFactory.getHandlersForTopic(topic).forEach(handler -> {
+ if (log.isDebugEnabled()) {
+ log.debug("主题: {}, 消息内容: {}", topic, message.getPayload());
+ }
+ handler.handle(topic, message.getPayload().toString());
+ });
+ } else {
+ log.warn("接收到主题为null的消息。");
+ }
+ };
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DeviceCommandDTO.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DeviceCommandDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..e304a294e9b88ec0dd0fb8b64009a6ba7bc4ae6f
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DeviceCommandDTO.java
@@ -0,0 +1,32 @@
+package net.maku.iot.mqtt.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import net.maku.iot.enums.DeviceCommandEnum;
+
+/**
+ * 设备命令对象
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Data
+@Schema(description = "设备命令对象")
+public class DeviceCommandDTO {
+ /**
+ * 命令类型
+ */
+ @Schema(description = "命令类型", required = true)
+ private DeviceCommandEnum command;
+
+ /**
+ * 命令id
+ */
+ @Schema(description = "命令id", required = true)
+ private String id;
+
+ /**
+ * 命令内容
+ */
+ @Schema(description = "命令内容")
+ private String payload;
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DeviceCommandResponseDTO.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DeviceCommandResponseDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..48e0f5261b3e471390de44412c20b5953fdc5e4c
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DeviceCommandResponseDTO.java
@@ -0,0 +1,45 @@
+package net.maku.iot.mqtt.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import net.maku.iot.enums.DeviceCommandEnum;
+
+/**
+ * 设备命令响应DTO
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Data
+@Schema(description = "设备命令响应DTO")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DeviceCommandResponseDTO {
+ /**
+ * 命令类型
+ */
+ @Schema(description = "命令类型", required = true)
+ private DeviceCommandEnum command;
+
+ /**
+ * 命令ID
+ */
+ @Schema(description = "命令ID", required = true)
+ private String commandId;
+
+ /**
+ * 命令是否完成(默认true:命令已完成;false:命令未完成,后续命令完成将再次发送响应消息,服务端将继续等待该命令完成的响应)
+ */
+ @Schema(description = "命令是否完成(默认true:命令已完成;false:命令未完成,后续命令完成将再次发送响应消息,服务端将继续等待该命令完成的响应)")
+ private boolean isCompleted = true;
+
+ /**
+ * 响应状态码,0成功,其它数值异常,根据业务需要自定义
+ */
+ @Schema(description = "响应状态码,0成功,其它数值异常,根据业务需要自定义")
+ private Integer statusCode = 0;
+ /**
+ * 命令响应结果
+ */
+ @Schema(description = "命令响应结果")
+ private String responsePayload;
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DevicePropertyDTO.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DevicePropertyDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d75b32904441da81c03e12fbfdca11dd2d41f800
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/dto/DevicePropertyDTO.java
@@ -0,0 +1,26 @@
+package net.maku.iot.mqtt.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import net.maku.iot.enums.DevicePropertyEnum;
+
+/**
+ * 设备属性对象
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Data
+@Schema(description = "设备属性对象")
+public class DevicePropertyDTO {
+ /**
+ * 设备属性类型
+ */
+ @Schema(description = "设备属性类型")
+ private DevicePropertyEnum propertyType;
+
+ /**
+ * 属性数据
+ */
+ @Schema(description = "状态数据,不同状态类型需传入相应的状态数据", required = true)
+ private String payload;
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/DeviceCommandResponseHandlerFactory.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/DeviceCommandResponseHandlerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..742584d9bddb64345d03caa2f448d84d8946c8dd
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/DeviceCommandResponseHandlerFactory.java
@@ -0,0 +1,41 @@
+package net.maku.iot.mqtt.factory;
+
+import lombok.RequiredArgsConstructor;
+import net.maku.iot.mqtt.handler.DeviceCommandResponseHandler;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 设备命令响应处理器工厂,自动获取所有实现的handler实例
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Component
+@RequiredArgsConstructor
+public class DeviceCommandResponseHandlerFactory {
+ private final ApplicationContext applicationContext;
+
+ /**
+ * 所有设备命令响应handlers
+ */
+ private List handlers;
+
+ /**
+ * 获取设备命令响应handlers
+ *
+ * @return
+ */
+ public List getHandlers() {
+ if (handlers != null) {
+ return handlers;
+ }
+ handlers = Collections.unmodifiableList(
+ new ArrayList<>(applicationContext.getBeansOfType(
+ DeviceCommandResponseHandler.class).values()));
+ return handlers;
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/DevicePropertyChangeHandlerFactory.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/DevicePropertyChangeHandlerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0d6bb427e1bd149cd78bab73f8540a6bdcbdef5
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/DevicePropertyChangeHandlerFactory.java
@@ -0,0 +1,41 @@
+package net.maku.iot.mqtt.factory;
+
+import lombok.RequiredArgsConstructor;
+import net.maku.iot.mqtt.handler.DevicePropertyChangeHandler;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 设备运行状态变化处理器工厂,自动获取所有实现的handler实例
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Component
+@RequiredArgsConstructor
+public class DevicePropertyChangeHandlerFactory {
+ private final ApplicationContext applicationContext;
+
+ /**
+ * 所有设备运行属性变化handlers
+ */
+ private List handlers;
+
+ /**
+ * 获取设备运行状态变化handlers
+ *
+ * @return
+ */
+ public List getHandlers() {
+ if (handlers != null) {
+ return handlers;
+ }
+ handlers = Collections.unmodifiableList(
+ new ArrayList<>(applicationContext.getBeansOfType(
+ DevicePropertyChangeHandler.class).values()));
+ return handlers;
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/MqttMessageHandlerFactory.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/MqttMessageHandlerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..368cf546cc33001cdef7aab5943dcee9a11546d6
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/factory/MqttMessageHandlerFactory.java
@@ -0,0 +1,47 @@
+package net.maku.iot.mqtt.factory;
+
+import lombok.RequiredArgsConstructor;
+import net.maku.iot.mqtt.handler.MqttMessageHandler;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * MQTT消息处理器工厂,自动获取所有实现的处理器实例
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Component
+@RequiredArgsConstructor
+public class MqttMessageHandlerFactory {
+ private final ApplicationContext applicationContext;
+
+ /**
+ * 所有消息处理器
+ */
+ private List messageHandlers;
+
+ private List loadHandlers() {
+ if (messageHandlers != null) {
+ return messageHandlers;
+ }
+ messageHandlers = new ArrayList<>(applicationContext.getBeansOfType(MqttMessageHandler.class).values());
+ return messageHandlers;
+ }
+
+ /**
+ * 获取与主题对应的处理器
+ *
+ * @param topic 主题
+ * @return 处理器列表
+ */
+ public List getHandlersForTopic(String topic) {
+ return Collections.unmodifiableList(loadHandlers().stream()
+ .filter(handler -> handler.supports(topic))
+ .collect(Collectors.toList()));
+ }
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DeviceCommandResponseHandler.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DeviceCommandResponseHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c04a66da5c544ba6983adb932ae87d85834686b
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DeviceCommandResponseHandler.java
@@ -0,0 +1,19 @@
+package net.maku.iot.mqtt.handler;
+
+
+import net.maku.iot.mqtt.dto.DeviceCommandResponseDTO;
+
+/**
+ * 设备命令响应处理器
+ *
+ * @author LSF maku_lsf@163.com
+ */
+public interface DeviceCommandResponseHandler {
+ /**
+ * 设备命令响应处理
+ *
+ * @param topic
+ * @param commandResponse
+ */
+ void handle(String topic, DeviceCommandResponseDTO commandResponse);
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DeviceCommandResponseMqttMessageHandler.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DeviceCommandResponseMqttMessageHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..775f84a03e3d9a4b8a8a236b77aeab807c6413bb
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DeviceCommandResponseMqttMessageHandler.java
@@ -0,0 +1,73 @@
+package net.maku.iot.mqtt.handler;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.maku.framework.common.utils.JsonUtils;
+import net.maku.iot.enums.DeviceTopicEnum;
+import net.maku.iot.mqtt.dto.DeviceCommandResponseDTO;
+import net.maku.iot.mqtt.factory.DeviceCommandResponseHandlerFactory;
+import net.maku.iot.mqtt.service.DeviceMqttService;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+/**
+ * 设备命令响应处理器
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DeviceCommandResponseMqttMessageHandler implements MqttMessageHandler {
+ private final DeviceCommandResponseHandlerFactory deviceCommandResponseHandlerFactory;
+
+
+ private final DeviceMqttService deviceMqttService;
+
+ @Override
+ public boolean supports(String topic) {
+ return DeviceTopicEnum.startsWith(topic, DeviceTopicEnum.COMMAND_RESPONSE.getTopic());
+ }
+
+ @Override
+ public void handle(String topic, String message) {
+ DeviceCommandResponseDTO commanddResponseDTO = parseCommandReplyMessage(topic, message);
+ Optional.ofNullable(commanddResponseDTO.getCommand())
+ .orElseThrow(() -> new IllegalArgumentException(StrUtil.format("缺失指令类型! 主题:'{}',消息:{}", topic, message)));
+ Optional.ofNullable(commanddResponseDTO.getCommandId())
+ .orElseThrow(() -> new IllegalArgumentException(StrUtil.format("缺失指令ID! 主题:'{}',消息:{}", topic, message)));
+
+ Optional.ofNullable(commanddResponseDTO)
+ .ifPresent(responseDTO -> {
+ // 调用设备命令执行器的命令响应处理逻辑
+ try {
+ deviceMqttService.commandReplied(topic, responseDTO);
+ } catch (Exception e) {
+ log.error(StrUtil.format("调用设备命令执行器响应处理方法出错,topic:{}, message:{}", topic, message), e);
+ }
+ // 调用自定义命令响应处理器
+ try {
+ deviceCommandResponseHandlerFactory.getHandlers().forEach(h -> h.handle(topic, responseDTO));
+ } catch (Exception e) {
+ log.error(StrUtil.format("调用设备命令响应响应处理器出错,topic:{}, message:{}", topic, message), e);
+ }
+ });
+ }
+
+ private DeviceCommandResponseDTO parseCommandReplyMessage(String topic, String message) {
+ try {
+ DeviceCommandResponseDTO commandResponse = JsonUtils.parseObject(message, DeviceCommandResponseDTO.class);
+ if (StrUtil.isBlank(commandResponse.getCommandId())) {
+ log.error(StrUtil.format("主题'{}'的消息,缺失指令ID", topic));
+ return null;
+ }
+ return commandResponse;
+
+ } catch (Exception e) {
+ log.error(StrUtil.format("将主题'{}'的消息解析为设备命令响应对象失败", topic), e);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DevicePropertyChangeHandler.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DevicePropertyChangeHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..739f266b447e05154a41d4db26eddb5c13c66375
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DevicePropertyChangeHandler.java
@@ -0,0 +1,19 @@
+package net.maku.iot.mqtt.handler;
+
+
+import net.maku.iot.mqtt.dto.DevicePropertyDTO;
+
+/**
+ * 设备属性变化处理器
+ *
+ * @author LSF maku_lsf@163.com
+ */
+public interface DevicePropertyChangeHandler {
+ /**
+ * 设备属性状态变化处理
+ *
+ * @param topic
+ * @param deviceStatus
+ */
+ void handle(String topic, DevicePropertyDTO deviceStatus);
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DevicePropertyMqttMessageHandler.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DevicePropertyMqttMessageHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..f47e205aea71f856b7777f4410df79f03b633e69
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/DevicePropertyMqttMessageHandler.java
@@ -0,0 +1,49 @@
+package net.maku.iot.mqtt.handler;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.maku.framework.common.utils.JsonUtils;
+import net.maku.iot.enums.DeviceTopicEnum;
+import net.maku.iot.mqtt.dto.DevicePropertyDTO;
+import net.maku.iot.mqtt.factory.DevicePropertyChangeHandlerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+/**
+ * 设备属性上报消息处理器
+ *
+ * @author LSF maku_lsf@163.com
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DevicePropertyMqttMessageHandler implements MqttMessageHandler {
+
+ private final DevicePropertyChangeHandlerFactory statusChangeHandlerFactory;
+
+ @Override
+ public boolean supports(String topic) {
+ return DeviceTopicEnum.startsWith(topic, DeviceTopicEnum.PROPERTY.getTopic());
+ }
+
+ @Override
+ public void handle(String topic, String message) {
+ DevicePropertyDTO devicePropertyDTO = parseStatusMessage(topic, message);
+ Optional.ofNullable(devicePropertyDTO)
+ .ifPresent(deviceProperty -> statusChangeHandlerFactory.getHandlers()
+ .forEach(h -> h.handle(topic, deviceProperty)));
+ }
+
+ private DevicePropertyDTO parseStatusMessage(String topic, String message) {
+ try {
+ return JsonUtils.parseObject(message, DevicePropertyDTO.class);
+ } catch (Exception e) {
+ log.error(StrUtil.format("将主题'{}'的消息解析为设备运行状态对象失败", topic), e);
+ return null;
+ }
+ }
+
+
+}
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/MqttMessageHandler.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/MqttMessageHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..33307ea332eba25cfe0160619bd14b22d3d83a86
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/handler/MqttMessageHandler.java
@@ -0,0 +1,24 @@
+package net.maku.iot.mqtt.handler;
+
+/**
+ * MQTT订阅消息处理接口
+ *
+ * @author LSF maku_lsf@163.com
+ */
+public interface MqttMessageHandler {
+ /**
+ * 是否支持处理指定的topic
+ *
+ * @param topic
+ * @return
+ */
+ boolean supports(String topic);
+
+ /**
+ * mqtt消息处理接口
+ *
+ * @param topic
+ * @param message
+ */
+ void handle(String topic, String message);
+}
\ No newline at end of file
diff --git a/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/service/DeviceMqttService.java b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/service/DeviceMqttService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6f1973cc05ecf64f7a8fd6cd0f902d50bf4e9ef
--- /dev/null
+++ b/maku-boot-module/maku-module-iot/src/main/java/net/maku/iot/mqtt/service/DeviceMqttService.java
@@ -0,0 +1,227 @@
+package net.maku.iot.mqtt.service;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.maku.framework.common.exception.ServerException;
+import net.maku.iot.dto.DeviceClientDTO;
+import net.maku.iot.entity.IotDeviceEntity;
+import net.maku.iot.enums.DeviceCommandEnum;
+import net.maku.iot.enums.DeviceServiceEnum;
+import net.maku.iot.enums.DeviceTopicEnum;
+import net.maku.iot.mqtt.MqttGateway;
+import net.maku.iot.mqtt.dto.DeviceCommandDTO;
+import net.maku.iot.mqtt.dto.DeviceCommandResponseDTO;
+import net.maku.iot.service.IotDeviceServiceLogService;
+import net.maku.iot.utils.MqttUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.UUID;
+import java.util.concurrent.*;
+
+/**
+ * 设备命令发送服务
+ **/
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DeviceMqttService {
+ private final MqttUtils mqttUtils;
+ private final MqttGateway mqttGateway;
+ private final IotDeviceServiceLogService iotDeviceEventLogService;
+
+ /**
+ * 命令等待exchanger缓存,key: command id
+ */
+ private ConcurrentMap> commandExchangers = new ConcurrentHashMap<>();
+
+ /**
+ * 异步发送命令,返回命令id
+ *
+ * @param device
+ * @param command
+ * @param payload
+ * @return
+ */
+ public String asyncSendCommand(IotDeviceEntity device, DeviceCommandEnum command, String payload) {
+ return asyncSendCommand(device, command, payload, Boolean.FALSE);
+ }
+
+
+ /**
+ * 异步发送命令,返回命令id
+ *
+ * @param device
+ * @param command
+ * @param payload
+ * @param retained
+ * @return
+ */
+ public String asyncSendCommand(IotDeviceEntity device, DeviceCommandEnum command, String payload, boolean retained) {
+ // 构建命令对象
+ String commandId = StrUtil.replaceChars(UUID.randomUUID().toString(), "-", "");
+ DeviceCommandDTO commandDTO = new DeviceCommandDTO();
+ commandDTO.setCommand(command);
+ commandDTO.setId(commandId);
+ commandDTO.setPayload(payload);
+ String commandTopic = DeviceTopicEnum.COMMAND.buildTopic(DeviceClientDTO.from(device));
+
+ // 发送命令到设备命令主题
+ try {
+ mqttGateway.sendToMqtt(commandTopic, retained, JSONUtil.toJsonStr(commandDTO));
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ throw new ServerException(StrUtil.format("发送'{}'命令:{} 到设备:{}-{}, Topic:{} 失败",
+ command.getTitle(), commandId, device.getCode(), device.getName(), commandTopic));
+ }
+ log.info("发送'{}'命令:{} 到设备:{}-{}, Topic:{} 成功", command.getTitle(), commandId, device.getCode(), device.getName(), commandTopic);
+ iotDeviceEventLogService.createAndSaveDeviceServiceLog(device.getId(), device.getTenantId(), command, commandId, payload);
+ return commandId;
+ }
+
+
+ /**
+ * 同步发送命令并返回响应结果
+ *
+ * @param device
+ * @param command
+ * @param payload
+ * @return
+ */
+ public DeviceCommandResponseDTO syncSendCommand(IotDeviceEntity device, DeviceCommandEnum command, String payload) {
+ return syncSendCommand(device, command, payload, Boolean.FALSE);
+ }
+
+ /**
+ * 发送命令并返回响应结果
+ *
+ * @param device
+ * @param command
+ * @param payload
+ * @param retained
+ * @return
+ */
+ public DeviceCommandResponseDTO syncSendCommand(IotDeviceEntity device, DeviceCommandEnum command, String payload, boolean retained) {
+ // 构建并发送命令
+ String commandId = asyncSendCommand(device, command, payload, retained);
+ // 等待返回结果
+ return waitCommandResponse(command, commandId);
+ }
+
+
+ /**
+ * 发送命令并返回响应结果,模拟设备响应
+ *
+ * @param device
+ * @param command
+ * @param payload
+ * @return
+ */
+ public DeviceCommandResponseDTO syncSendCommandDebug(IotDeviceEntity device, DeviceCommandEnum command, String payload) {
+ // 构建并发送命令
+ String commandId = asyncSendCommand(device, command, payload);
+
+ // 2秒后模拟设备响应
+ new Thread(() -> {
+ try {
+ //模拟设备正常响应
+ Thread.sleep(2000);
+ //模拟设备超时响应
+ //Thread.sleep(15000);
+ DeviceCommandResponseDTO simulateResponseDto = new DeviceCommandResponseDTO();
+ simulateResponseDto.setCommandId(commandId);
+ simulateResponseDto.setResponsePayload(command.getTitle() + ",设备执行成功!");
+ simulateResponseDto.setCommand(command);
+ simulateDeviceCommandResponseAttributeData(device, JSONUtil.toJsonStr(simulateResponseDto));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ log.error("模拟设备响应线程被中断", e);
+ }
+ }).start();
+
+ // 等待设备响应
+ return waitCommandResponse(command, commandId);
+ }
+
+
+ /**
+ * 订阅设备命令响应主题并等待获取返回结果
+ *
+ * @param command
+ * @param commandId
+ * @return
+ */
+ private DeviceCommandResponseDTO waitCommandResponse(DeviceCommandEnum command, String commandId) {
+ // 创建命令响应等待exchanger
+ Exchanger