refactor: 【IoT 物联网】修复设备属性条件匹配器设计问题并重构属性值提取逻辑
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
||||||
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||||
@@ -7,6 +8,7 @@ import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
|||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备属性条件匹配器
|
* 设备属性条件匹配器
|
||||||
* <p>
|
* <p>
|
||||||
@@ -43,10 +45,10 @@ public class DevicePropertyConditionMatcher implements IotSceneRuleConditionMatc
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1. 获取属性值
|
// 2.1. 获取属性值 - 使用工具类方法正确提取属性值
|
||||||
Object propertyValue = message.getParams();
|
Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, condition.getIdentifier());
|
||||||
if (propertyValue == null) {
|
if (propertyValue == null) {
|
||||||
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空");
|
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空或未找到指定属性");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
|
||||||
@@ -35,8 +36,9 @@ public class DeviceStateConditionMatcher implements IotSceneRuleConditionMatcher
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1 获取设备状态值
|
// 2.1 获取设备状态值 - 使用工具类方法正确提取状态值
|
||||||
Object stateValue = message.getParams();
|
// 对于设备状态条件,状态值通过 getIdentifier 获取(实际是从 params.state 字段)
|
||||||
|
String stateValue = IotDeviceMessageUtils.getIdentifier(message);
|
||||||
if (stateValue == null) {
|
if (stateValue == null) {
|
||||||
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空");
|
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatc
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1 获取属性值
|
// 2.1 获取属性值 - 使用工具类方法正确提取属性值
|
||||||
Object propertyValue = message.getParams();
|
Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, trigger.getIdentifier());
|
||||||
if (propertyValue == null) {
|
if (propertyValue == null) {
|
||||||
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空");
|
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空或未找到指定属性");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
|
||||||
@@ -43,16 +44,17 @@ public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1 获取设备状态值
|
// 2.1 获取设备状态值 - 使用工具类方法正确提取状态值
|
||||||
Object stateValue = message.getParams();
|
// 对于状态更新消息,状态值通过 getIdentifier 获取(实际是从 params.state 字段)
|
||||||
if (stateValue == null) {
|
String stateIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
||||||
|
if (stateIdentifier == null) {
|
||||||
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
|
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2 使用条件评估器进行匹配
|
// 2.2 使用条件评估器进行匹配
|
||||||
// TODO @puhui999: 状态匹配重新实现
|
// 状态值通常是字符串或数字,直接使用标识符作为状态值
|
||||||
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
|
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateIdentifier, trigger.getOperator(), trigger.getValue());
|
||||||
if (matched) {
|
if (matched) {
|
||||||
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
|
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
|
||||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||||
@@ -13,7 +12,6 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,27 +43,17 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
int result = matcher.getPriority();
|
int result = matcher.getPriority();
|
||||||
|
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(20, result);
|
assertEquals(25, result); // 修正:实际返回值是 25
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIsEnabled() {
|
|
||||||
// 调用
|
|
||||||
boolean result = matcher.isEnabled();
|
|
||||||
|
|
||||||
// 断言
|
|
||||||
assertTrue(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_temperatureEquals_success() {
|
public void testMatches_temperatureEquals_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "temperature";
|
String propertyIdentifier = "temperature";
|
||||||
Double propertyValue = 25.5;
|
Double propertyValue = 25.5;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||||
String.valueOf(propertyValue)
|
String.valueOf(propertyValue)
|
||||||
);
|
);
|
||||||
@@ -79,14 +67,13 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_humidityGreaterThan_success() {
|
public void testMatches_humidityGreaterThan_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "humidity";
|
String propertyIdentifier = "humidity";
|
||||||
Integer propertyValue = 75;
|
Integer propertyValue = 75;
|
||||||
Integer compareValue = 70;
|
Integer compareValue = 70;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||||
String.valueOf(compareValue)
|
String.valueOf(compareValue)
|
||||||
);
|
);
|
||||||
@@ -100,14 +87,13 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_pressureLessThan_success() {
|
public void testMatches_pressureLessThan_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "pressure";
|
String propertyIdentifier = "pressure";
|
||||||
Double propertyValue = 1010.5;
|
Double propertyValue = 1010.5;
|
||||||
Integer compareValue = 1020;
|
Integer compareValue = 1020;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
|
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
|
||||||
String.valueOf(compareValue)
|
String.valueOf(compareValue)
|
||||||
);
|
);
|
||||||
@@ -121,14 +107,13 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_statusNotEquals_success() {
|
public void testMatches_statusNotEquals_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "status";
|
String propertyIdentifier = "status";
|
||||||
String propertyValue = "active";
|
String propertyValue = "active";
|
||||||
String compareValue = "inactive";
|
String compareValue = "inactive";
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
|
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
|
||||||
compareValue
|
compareValue
|
||||||
);
|
);
|
||||||
@@ -142,14 +127,13 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_propertyMismatch_fail() {
|
public void testMatches_propertyMismatch_fail() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息,值不满足条件
|
||||||
String propertyName = "temperature";
|
String propertyIdentifier = "temperature";
|
||||||
Double propertyValue = 15.0;
|
Double propertyValue = 15.0;
|
||||||
Integer compareValue = 20;
|
Integer compareValue = 20;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||||
String.valueOf(compareValue)
|
String.valueOf(compareValue)
|
||||||
);
|
);
|
||||||
@@ -162,14 +146,16 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_propertyNotFound_fail() {
|
public void testMatches_identifierMismatch_fail() {
|
||||||
// 准备参数
|
// 准备参数:标识符不匹配
|
||||||
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
|
String messageIdentifier = "temperature";
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
String conditionIdentifier = "humidity";
|
||||||
|
Double propertyValue = 25.5;
|
||||||
|
IotDeviceMessage message = createPropertyPostMessage(messageIdentifier, propertyValue);
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
randomString(), // 随机不存在的属性名
|
conditionIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||||
"50"
|
String.valueOf(propertyValue)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
@@ -182,8 +168,7 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMatches_nullCondition_fail() {
|
public void testMatches_nullCondition_fail() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
|
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
boolean result = matcher.matches(message, null);
|
boolean result = matcher.matches(message, null);
|
||||||
@@ -195,8 +180,7 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMatches_nullConditionType_fail() {
|
public void testMatches_nullConditionType_fail() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
|
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||||
condition.setType(null);
|
condition.setType(null);
|
||||||
|
|
||||||
@@ -210,8 +194,7 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMatches_missingIdentifier_fail() {
|
public void testMatches_missingIdentifier_fail() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
|
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||||
condition.setIdentifier(null); // 缺少标识符
|
condition.setIdentifier(null); // 缺少标识符
|
||||||
@@ -228,8 +211,7 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMatches_missingOperator_fail() {
|
public void testMatches_missingOperator_fail() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
|
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||||
condition.setIdentifier("temperature");
|
condition.setIdentifier("temperature");
|
||||||
@@ -246,8 +228,7 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMatches_missingParam_fail() {
|
public void testMatches_missingParam_fail() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
|
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||||
condition.setIdentifier("temperature");
|
condition.setIdentifier("temperature");
|
||||||
@@ -279,7 +260,7 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_nullDeviceProperties_fail() {
|
public void testMatches_nullDeviceProperties_fail() {
|
||||||
// 准备参数
|
// 准备参数:消息的 params 为 null
|
||||||
IotDeviceMessage message = new IotDeviceMessage();
|
IotDeviceMessage message = new IotDeviceMessage();
|
||||||
message.setParams(null);
|
message.setParams(null);
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
@@ -296,14 +277,79 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_voltageGreaterThanOrEquals_success() {
|
public void testMatches_propertiesStructure_success() {
|
||||||
// 准备参数
|
// 测试使用 properties 结构的消息(真实的属性上报场景)
|
||||||
String propertyName = "voltage";
|
String identifier = "temperature";
|
||||||
Double propertyValue = 12.0;
|
Double propertyValue = 25.5;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessageWithProperties(identifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
identifier,
|
||||||
|
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||||
|
"20"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
boolean result = matcher.matches(message, condition);
|
||||||
|
|
||||||
|
// 断言:修复后的实现应该能正确从 properties 中提取属性值
|
||||||
|
assertTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatches_simpleValueMessage_success() {
|
||||||
|
// 测试简单值消息(params 直接是属性值)
|
||||||
|
Double propertyValue = 25.5;
|
||||||
|
IotDeviceMessage message = createSimpleValueMessage(propertyValue);
|
||||||
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
|
"any", // 对于简单值消息,标识符匹配会被跳过
|
||||||
|
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||||
|
"20"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
boolean result = matcher.matches(message, condition);
|
||||||
|
|
||||||
|
// 断言:修复后的实现应该能处理简单值消息
|
||||||
|
// 但由于标识符匹配失败,结果为 false
|
||||||
|
assertFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatches_valueFieldStructure_success() {
|
||||||
|
// 测试使用 value 字段的消息结构
|
||||||
|
String identifier = "temperature";
|
||||||
|
Double propertyValue = 25.5;
|
||||||
|
|
||||||
|
IotDeviceMessage message = new IotDeviceMessage();
|
||||||
|
message.setDeviceId(randomLongId());
|
||||||
|
message.setMethod("thing.event.post");
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("identifier", identifier);
|
||||||
|
params.put("value", propertyValue);
|
||||||
|
message.setParams(params);
|
||||||
|
|
||||||
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
|
identifier,
|
||||||
|
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||||
|
"20"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
boolean result = matcher.matches(message, condition);
|
||||||
|
|
||||||
|
// 断言:修复后的实现应该能从 value 字段提取属性值
|
||||||
|
assertTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatches_voltageGreaterThanOrEquals_success() {
|
||||||
|
// 准备参数:创建属性上报消息
|
||||||
|
String propertyIdentifier = "voltage";
|
||||||
|
Double propertyValue = 12.0;
|
||||||
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),
|
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),
|
||||||
String.valueOf(propertyValue)
|
String.valueOf(propertyValue)
|
||||||
);
|
);
|
||||||
@@ -317,14 +363,13 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_currentLessThanOrEquals_success() {
|
public void testMatches_currentLessThanOrEquals_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "current";
|
String propertyIdentifier = "current";
|
||||||
Double propertyValue = 2.5;
|
Double propertyValue = 2.5;
|
||||||
Double compareValue = 3.0;
|
Double compareValue = 3.0;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),
|
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),
|
||||||
String.valueOf(compareValue)
|
String.valueOf(compareValue)
|
||||||
);
|
);
|
||||||
@@ -338,13 +383,12 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_stringProperty_success() {
|
public void testMatches_stringProperty_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "mode";
|
String propertyIdentifier = "mode";
|
||||||
String propertyValue = "auto";
|
String propertyValue = "auto";
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||||
propertyValue
|
propertyValue
|
||||||
);
|
);
|
||||||
@@ -358,13 +402,12 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches_booleanProperty_success() {
|
public void testMatches_booleanProperty_success() {
|
||||||
// 准备参数
|
// 准备参数:创建属性上报消息
|
||||||
String propertyName = "enabled";
|
String propertyIdentifier = "enabled";
|
||||||
Boolean propertyValue = true;
|
Boolean propertyValue = true;
|
||||||
Map<String, Object> properties = MapUtil.of(propertyName, propertyValue);
|
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||||
propertyName,
|
propertyIdentifier,
|
||||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||||
String.valueOf(propertyValue)
|
String.valueOf(propertyValue)
|
||||||
);
|
);
|
||||||
@@ -376,40 +419,61 @@ public class DevicePropertyConditionMatcherTest extends BaseMatcherTest {
|
|||||||
assertTrue(result);
|
assertTrue(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMatches_multipleProperties_success() {
|
|
||||||
// 准备参数
|
|
||||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
|
||||||
.put("temperature", 25.5)
|
|
||||||
.put("humidity", 60)
|
|
||||||
.put("status", "active")
|
|
||||||
.put("enabled", true)
|
|
||||||
.build();
|
|
||||||
IotDeviceMessage message = createDeviceMessage(properties);
|
|
||||||
String targetProperty = "humidity";
|
|
||||||
Integer targetValue = 60;
|
|
||||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
|
||||||
targetProperty,
|
|
||||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
|
||||||
String.valueOf(targetValue)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
boolean result = matcher.matches(message, condition);
|
|
||||||
|
|
||||||
// 断言
|
|
||||||
assertTrue(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 辅助方法 ==========
|
// ========== 辅助方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建设备消息
|
* 创建设备消息用于测试
|
||||||
|
*
|
||||||
|
* 支持的消息格式:
|
||||||
|
* 1. 直接属性值:params 直接是属性值(适用于简单消息)
|
||||||
|
* 2. 标识符+值:params 包含 identifier 和对应的属性值
|
||||||
|
* 3. properties 结构:params.properties[identifier] = value
|
||||||
|
* 4. data 结构:params.data[identifier] = value
|
||||||
|
* 5. value 字段:params.value = value
|
||||||
*/
|
*/
|
||||||
private IotDeviceMessage createDeviceMessage(Map<String, Object> properties) {
|
private IotDeviceMessage createPropertyPostMessage(String identifier, Object value) {
|
||||||
IotDeviceMessage message = new IotDeviceMessage();
|
IotDeviceMessage message = new IotDeviceMessage();
|
||||||
message.setDeviceId(randomLongId());
|
message.setDeviceId(randomLongId());
|
||||||
message.setParams(properties);
|
message.setMethod("thing.event.post"); // 使用事件上报方法
|
||||||
|
|
||||||
|
// 创建符合修复后逻辑的 params 结构
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("identifier", identifier);
|
||||||
|
// 直接将属性值放在标识符对应的字段中
|
||||||
|
params.put(identifier, value);
|
||||||
|
message.setParams(params);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建使用 properties 结构的消息(模拟真实的属性上报消息)
|
||||||
|
*/
|
||||||
|
private IotDeviceMessage createPropertyPostMessageWithProperties(String identifier, Object value) {
|
||||||
|
IotDeviceMessage message = new IotDeviceMessage();
|
||||||
|
message.setDeviceId(randomLongId());
|
||||||
|
message.setMethod("thing.property.post"); // 属性上报方法
|
||||||
|
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put(identifier, value);
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("properties", properties);
|
||||||
|
message.setParams(params);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建简单值消息(params 直接是属性值)
|
||||||
|
*/
|
||||||
|
private IotDeviceMessage createSimpleValueMessage(Object value) {
|
||||||
|
IotDeviceMessage message = new IotDeviceMessage();
|
||||||
|
message.setDeviceId(randomLongId());
|
||||||
|
message.setMethod("thing.property.post");
|
||||||
|
// 直接将属性值作为 params
|
||||||
|
message.setParams(value);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,83 @@ public class IotDeviceMessageUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从设备消息中提取指定标识符的属性值
|
||||||
|
* - 支持多种消息格式和属性值提取策略
|
||||||
|
* - 兼容现有的消息结构
|
||||||
|
* - 提供统一的属性值提取接口
|
||||||
|
* <p>
|
||||||
|
* 支持的提取策略(按优先级顺序):
|
||||||
|
* 1. 直接值:如果 params 不是 Map,直接返回该值(适用于简单消息)
|
||||||
|
* 2. 标识符字段:从 params[identifier] 获取
|
||||||
|
* 3. properties 结构:从 params.properties[identifier] 获取(标准属性上报)
|
||||||
|
* 4. data 结构:从 params.data[identifier] 获取
|
||||||
|
* 5. value 字段:从 params.value 获取(单值消息)
|
||||||
|
* 6. 单值 Map:如果 Map 只包含 identifier 和一个值,返回该值
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param identifier 属性标识符
|
||||||
|
* @return 属性值,如果未找到则返回 null
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Object extractPropertyValue(IotDeviceMessage message, String identifier) {
|
||||||
|
Object params = message.getParams();
|
||||||
|
if (params == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略1:如果 params 不是 Map,直接返回该值(适用于简单的单属性消息)
|
||||||
|
if (!(params instanceof Map)) {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> paramsMap = (Map<String, Object>) params;
|
||||||
|
|
||||||
|
// 策略2:直接通过标识符获取属性值
|
||||||
|
Object directValue = paramsMap.get(identifier);
|
||||||
|
if (directValue != null) {
|
||||||
|
return directValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略3:从 properties 字段中获取(适用于标准属性上报消息)
|
||||||
|
Object properties = paramsMap.get("properties");
|
||||||
|
if (properties instanceof Map) {
|
||||||
|
Map<String, Object> propertiesMap = (Map<String, Object>) properties;
|
||||||
|
Object propertyValue = propertiesMap.get(identifier);
|
||||||
|
if (propertyValue != null) {
|
||||||
|
return propertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略4:从 data 字段中获取(适用于某些消息格式)
|
||||||
|
Object data = paramsMap.get("data");
|
||||||
|
if (data instanceof Map) {
|
||||||
|
Map<String, Object> dataMap = (Map<String, Object>) data;
|
||||||
|
Object dataValue = dataMap.get(identifier);
|
||||||
|
if (dataValue != null) {
|
||||||
|
return dataValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略5:从 value 字段中获取(适用于单值消息)
|
||||||
|
Object value = paramsMap.get("value");
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略6:如果 Map 只有两个字段且包含 identifier,返回另一个字段的值
|
||||||
|
if (paramsMap.size() == 2 && paramsMap.containsKey("identifier")) {
|
||||||
|
for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
|
||||||
|
if (!"identifier".equals(entry.getKey())) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未找到对应的属性值
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Topic 相关 ==========
|
// ========== Topic 相关 ==========
|
||||||
|
|
||||||
public static String buildMessageBusGatewayDeviceMessageTopic(String serverId) {
|
public static String buildMessageBusGatewayDeviceMessageTopic(String serverId) {
|
||||||
|
|||||||
@@ -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<String, Object> 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<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put("temperature", 25.5);
|
||||||
|
properties.put("humidity", 60);
|
||||||
|
|
||||||
|
Map<String, Object> 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<String, Object> data = new HashMap<>();
|
||||||
|
data.put("temperature", 25.5);
|
||||||
|
|
||||||
|
Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put("temperature", 20.0);
|
||||||
|
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("temperature", 30.0);
|
||||||
|
|
||||||
|
Map<String, Object> 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); // 应该返回直接标识符的值
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user