From 5bc8a4e487be9d63c27c63901975579d8f46fa0b Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 21 Jan 2026 21:37:39 +0800 Subject: [PATCH] =?UTF-8?q?perf=EF=BC=9A=E3=80=90iot=E3=80=91=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20IotDeviceMessageUtils.notContainsIdentifier=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotDevicePropertyPostTriggerMatcher.java | 3 +- .../data/action/tcp/IotTcpClientTest.java | 151 ++++++++++++++++++ .../iot/core/util/IotDeviceMessageUtils.java | 11 ++ .../core/util/IotDeviceMessageUtilsTest.java | 72 ++++++++- 4 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java index d653c9c42e..1f019b5761 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java @@ -38,8 +38,7 @@ public class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerM // 1.3 检查消息中是否包含触发器指定的属性标识符 // 注意:属性上报可能同时上报多个属性,所以需要判断 trigger.getIdentifier() 是否在 message 的 params 中 - // TODO @puhui999:可以考虑 notXXX 方法,简化代码(尽量取反) - if (!IotDeviceMessageUtils.containsIdentifier(message, trigger.getIdentifier())) { + if (IotDeviceMessageUtils.notContainsIdentifier(message, trigger.getIdentifier())) { IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中不包含属性: " + trigger.getIdentifier()); return false; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java new file mode 100644 index 0000000000..cd28f8f54e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java @@ -0,0 +1,151 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp; + +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotTcpClient} 的单元测试 + *

+ * 测试 dataFormat 默认值行为 + * Property 1: TCP 客户端 dataFormat 默认值行为 + * Validates: Requirements 1.1, 1.2 + * + * @author HUIHUI + */ +class IotTcpClientTest { + + @Test + public void testConstructor_dataFormatNull() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, null); + + // 断言:dataFormat 为 null 时应使用默认值 + assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT, + ReflectUtil.getFieldValue(client, "dataFormat")); + } + + @Test + public void testConstructor_dataFormatEmpty() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, ""); + + // 断言:dataFormat 为空字符串时应使用默认值 + assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT, + ReflectUtil.getFieldValue(client, "dataFormat")); + } + + @Test + public void testConstructor_dataFormatBlank() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, " "); + + // 断言:dataFormat 为纯空白字符串时应使用默认值 + assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT, + ReflectUtil.getFieldValue(client, "dataFormat")); + } + + @Test + public void testConstructor_dataFormatValid() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + String dataFormat = "BINARY"; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, dataFormat); + + // 断言:dataFormat 为有效值时应保持原值 + assertEquals(dataFormat, ReflectUtil.getFieldValue(client, "dataFormat")); + } + + @Test + public void testConstructor_defaultValues() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, null); + + // 断言:验证所有默认值 + assertEquals(host, ReflectUtil.getFieldValue(client, "host")); + assertEquals(port, ReflectUtil.getFieldValue(client, "port")); + assertEquals(IotDataSinkTcpConfig.DEFAULT_CONNECT_TIMEOUT_MS, + ReflectUtil.getFieldValue(client, "connectTimeoutMs")); + assertEquals(IotDataSinkTcpConfig.DEFAULT_READ_TIMEOUT_MS, + ReflectUtil.getFieldValue(client, "readTimeoutMs")); + assertEquals(IotDataSinkTcpConfig.DEFAULT_SSL, + ReflectUtil.getFieldValue(client, "ssl")); + assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT, + ReflectUtil.getFieldValue(client, "dataFormat")); + } + + @Test + public void testConstructor_customValues() { + // 准备参数 + String host = "192.168.1.100"; + Integer port = 9090; + Integer connectTimeoutMs = 3000; + Integer readTimeoutMs = 8000; + Boolean ssl = true; + String dataFormat = "BINARY"; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, connectTimeoutMs, readTimeoutMs, ssl, dataFormat); + + // 断言:验证自定义值 + assertEquals(host, ReflectUtil.getFieldValue(client, "host")); + assertEquals(port, ReflectUtil.getFieldValue(client, "port")); + assertEquals(connectTimeoutMs, ReflectUtil.getFieldValue(client, "connectTimeoutMs")); + assertEquals(readTimeoutMs, ReflectUtil.getFieldValue(client, "readTimeoutMs")); + assertEquals(ssl, ReflectUtil.getFieldValue(client, "ssl")); + assertEquals(dataFormat, ReflectUtil.getFieldValue(client, "dataFormat")); + } + + @Test + public void testIsConnected_initialState() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, null); + + // 断言:初始状态应为未连接 + assertFalse(client.isConnected()); + } + + @Test + public void testToString() { + // 准备参数 + String host = "localhost"; + Integer port = 8080; + + // 调用 + IotTcpClient client = new IotTcpClient(host, port, null, null, null, null); + String result = client.toString(); + + // 断言 + assertNotNull(result); + assertTrue(result.contains("host='localhost'")); + assertTrue(result.contains("port=8080")); + assertTrue(result.contains("dataFormat='JSON'")); + assertTrue(result.contains("connected=false")); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java index 5c1ac26005..b02a9b4c3a 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java @@ -99,6 +99,17 @@ public class IotDeviceMessageUtils { return false; } + /** + * 判断消息中是否不包含指定的标识符 + * + * @param message 消息 + * @param identifier 要检查的标识符 + * @return 是否不包含 + */ + public static boolean notContainsIdentifier(IotDeviceMessage message, String identifier) { + return !containsIdentifier(message, identifier); + } + /** * 将 params 解析为 Map * 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 index a6d669d170..b0d39be519 100644 --- 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 @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.iot.core.util; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 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; +import static org.junit.jupiter.api.Assertions.*; /** * {@link IotDeviceMessageUtils} 的单元测试 @@ -138,4 +138,72 @@ public class IotDeviceMessageUtilsTest { Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature"); assertEquals(25.5, result); // 应该返回直接标识符的值 } + + // ========== notContainsIdentifier 测试 ========== + + /** + * 测试 notContainsIdentifier 与 containsIdentifier 的互补性 + * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性** + * **Validates: Requirements 4.1** + */ + @Test + public void testNotContainsIdentifier_complementary_whenContains() { + // 准备参数:消息包含指定标识符 + IotDeviceMessage message = new IotDeviceMessage(); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); + Map params = new HashMap<>(); + params.put("temperature", 25); + message.setParams(params); + String identifier = "temperature"; + + // 调用 & 断言:验证互补性 + boolean containsResult = IotDeviceMessageUtils.containsIdentifier(message, identifier); + boolean notContainsResult = IotDeviceMessageUtils.notContainsIdentifier(message, identifier); + assertTrue(containsResult); + assertFalse(notContainsResult); + assertEquals(!containsResult, notContainsResult); + } + + /** + * 测试 notContainsIdentifier 与 containsIdentifier 的互补性 + * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性** + * **Validates: Requirements 4.1** + */ + @Test + public void testNotContainsIdentifier_complementary_whenNotContains() { + // 准备参数:消息不包含指定标识符 + IotDeviceMessage message = new IotDeviceMessage(); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); + Map params = new HashMap<>(); + params.put("temperature", 25); + message.setParams(params); + String identifier = "humidity"; + + // 调用 & 断言:验证互补性 + boolean containsResult = IotDeviceMessageUtils.containsIdentifier(message, identifier); + boolean notContainsResult = IotDeviceMessageUtils.notContainsIdentifier(message, identifier); + assertFalse(containsResult); + assertTrue(notContainsResult); + assertEquals(!containsResult, notContainsResult); + } + + /** + * 测试 notContainsIdentifier 与 containsIdentifier 的互补性 - 空参数场景 + * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性** + * **Validates: Requirements 4.1** + */ + @Test + public void testNotContainsIdentifier_complementary_nullParams() { + // 准备参数:params 为 null + IotDeviceMessage message = new IotDeviceMessage(); + message.setParams(null); + String identifier = "temperature"; + + // 调用 & 断言:验证互补性 + boolean containsResult = IotDeviceMessageUtils.containsIdentifier(message, identifier); + boolean notContainsResult = IotDeviceMessageUtils.notContainsIdentifier(message, identifier); + assertFalse(containsResult); + assertTrue(notContainsResult); + assertEquals(!containsResult, notContainsResult); + } }