From 186bb144c9644edfff912170630bf1feae5cc361 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 2 Oct 2025 17:57:40 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT=20?= =?UTF-8?q?=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/IotDeviceMessageMethodEnum.java | 86 +++++++++++ .../core/util/IotDeviceMessageUtilsTest.java | 141 ++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java new file mode 100644 index 000000000..e62b78e24 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.iot.core.enums; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Set; + +/** + * IoT 设备消息的方法枚举 + * + * @author haohao + */ +@Getter +@AllArgsConstructor +public enum IotDeviceMessageMethodEnum implements ArrayValuable { + + // ========== 设备状态 ========== + + STATE_UPDATE("thing.state.update", "设备状态更新", true), + + // TODO 芋艿:要不要加个 ping 消息; + + // ========== 设备属性 ========== + // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services + + PROPERTY_POST("thing.property.post", "属性上报", true), + PROPERTY_SET("thing.property.set", "属性设置", false), + + // ========== 设备事件 ========== + // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services + + EVENT_POST("thing.event.post", "事件上报", true), + + // ========== 设备服务调用 ========== + // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services + + SERVICE_INVOKE("thing.service.invoke", "服务调用", false), + + // ========== 设备配置 ========== + // 可参考:https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 + + CONFIG_PUSH("thing.config.push", "配置推送", false), + + // ========== OTA 固件 ========== + // 可参考:https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates + + OTA_UPGRADE("thing.ota.upgrade", "OTA 固定信息推送", false), + OTA_PROGRESS("thing.ota.progress", "OTA 升级进度上报", true), + ; + + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod) + .toArray(String[]::new); + + /** + * 不进行 reply 回复的方法集合 + */ + public static final Set REPLY_DISABLED = SetUtils.asSet( + STATE_UPDATE.getMethod(), + OTA_PROGRESS.getMethod() // 参考阿里云,OTA 升级进度上报,不进行回复 + ); + + private final String method; + + private final String name; + + private final Boolean upstream; + + @Override + public String[] array() { + return ARRAYS; + } + + public static IotDeviceMessageMethodEnum of(String method) { + return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), + IotDeviceMessageMethodEnum.values()); + } + + public static boolean isReplyDisabled(String method) { + return REPLY_DISABLED.contains(method); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java b/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java new file mode 100644 index 000000000..a6d669d17 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java @@ -0,0 +1,141 @@ +package cn.iocoder.yudao.module.iot.core.util; + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link IotDeviceMessageUtils} 的单元测试 + * + * @author HUIHUI + */ +public class IotDeviceMessageUtilsTest { + + @Test + public void testExtractPropertyValue_directValue() { + // 测试直接值(非 Map) + IotDeviceMessage message = new IotDeviceMessage(); + message.setParams(25.5); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); + } + + @Test + public void testExtractPropertyValue_directIdentifier() { + // 测试直接通过标识符获取 + IotDeviceMessage message = new IotDeviceMessage(); + Map params = new HashMap<>(); + params.put("temperature", 25.5); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); + } + + @Test + public void testExtractPropertyValue_propertiesStructure() { + // 测试 properties 结构 + IotDeviceMessage message = new IotDeviceMessage(); + Map properties = new HashMap<>(); + properties.put("temperature", 25.5); + properties.put("humidity", 60); + + Map params = new HashMap<>(); + params.put("properties", properties); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); + } + + @Test + public void testExtractPropertyValue_dataStructure() { + // 测试 data 结构 + IotDeviceMessage message = new IotDeviceMessage(); + Map data = new HashMap<>(); + data.put("temperature", 25.5); + + Map params = new HashMap<>(); + params.put("data", data); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); + } + + @Test + public void testExtractPropertyValue_valueField() { + // 测试 value 字段 + IotDeviceMessage message = new IotDeviceMessage(); + Map params = new HashMap<>(); + params.put("identifier", "temperature"); + params.put("value", 25.5); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); + } + + @Test + public void testExtractPropertyValue_singleValueMap() { + // 测试单值 Map(包含 identifier 和一个值) + IotDeviceMessage message = new IotDeviceMessage(); + Map params = new HashMap<>(); + params.put("identifier", "temperature"); + params.put("actualValue", 25.5); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); + } + + @Test + public void testExtractPropertyValue_notFound() { + // 测试未找到属性值 + IotDeviceMessage message = new IotDeviceMessage(); + Map params = new HashMap<>(); + params.put("humidity", 60); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertNull(result); + } + + @Test + public void testExtractPropertyValue_nullParams() { + // 测试 params 为 null + IotDeviceMessage message = new IotDeviceMessage(); + message.setParams(null); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertNull(result); + } + + @Test + public void testExtractPropertyValue_priorityOrder() { + // 测试优先级顺序:直接标识符 > properties > data > value + IotDeviceMessage message = new IotDeviceMessage(); + + Map properties = new HashMap<>(); + properties.put("temperature", 20.0); + + Map data = new HashMap<>(); + data.put("temperature", 30.0); + + Map params = new HashMap<>(); + params.put("temperature", 25.5); // 最高优先级 + params.put("properties", properties); + params.put("data", data); + params.put("value", 40.0); + message.setParams(params); + + Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); + assertEquals(25.5, result); // 应该返回直接标识符的值 + } +}