perf:【iot】添加 IotDeviceMessageUtils.notContainsIdentifier 方法

This commit is contained in:
puhui999
2026-01-21 21:37:39 +08:00
parent f320569f2c
commit 5bc8a4e487
4 changed files with 233 additions and 4 deletions

View File

@@ -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;

View File

@@ -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} 的单元测试
* <p>
* 测试 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"));
}
}

View File

@@ -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
*

View File

@@ -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<String, Object> 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<String, Object> 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);
}
}