feat:【IoT 物联网】新增场景规则触发器匹配子条件策略接口和实现
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.enums.rule;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IoT 场景规则条件层级枚举
|
||||||
|
* <p>
|
||||||
|
* 用于区分主条件(触发器级别)和子条件(条件分组级别)
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum IotSceneRuleConditionLevelEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主条件 - 触发器级别的条件
|
||||||
|
* 用于判断触发器本身是否匹配(如消息类型、设备标识等)
|
||||||
|
*/
|
||||||
|
PRIMARY(1, "主条件"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子条件 - 条件分组级别的条件
|
||||||
|
* 用于判断具体的业务条件(如设备状态、属性值、时间条件等)
|
||||||
|
*/
|
||||||
|
SECONDARY(2, "子条件");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件层级
|
||||||
|
*/
|
||||||
|
private final Integer level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件层级名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据层级值获取枚举
|
||||||
|
*
|
||||||
|
* @param level 层级值
|
||||||
|
* @return 条件层级枚举
|
||||||
|
*/
|
||||||
|
public static IotSceneRuleConditionLevelEnum levelOf(Integer level) {
|
||||||
|
if (level == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (IotSceneRuleConditionLevelEnum levelEnum : values()) {
|
||||||
|
if (levelEnum.getLevel().equals(level)) {
|
||||||
|
return levelEnum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为主条件
|
||||||
|
*
|
||||||
|
* @return 是否为主条件
|
||||||
|
*/
|
||||||
|
public boolean isPrimary() {
|
||||||
|
return this == PRIMARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为子条件
|
||||||
|
*
|
||||||
|
* @return 是否为子条件
|
||||||
|
*/
|
||||||
|
public boolean isSecondary() {
|
||||||
|
return this == SECONDARY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,19 +19,17 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
|||||||
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
|
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
|
||||||
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
|
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
|
||||||
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.device.IotDeviceDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||||
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.dal.mysql.rule.IotSceneRuleMapper;
|
import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
|
import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
|
||||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
|
||||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleTriggerMatcherManager;
|
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -67,7 +65,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
|
|||||||
@Resource
|
@Resource
|
||||||
private IotDeviceService deviceService;
|
private IotDeviceService deviceService;
|
||||||
@Resource
|
@Resource
|
||||||
private IotSceneRuleTriggerMatcherManager triggerMatcherManager;
|
private IotSceneRuleMatcherManager matcherManager;
|
||||||
@Resource
|
@Resource
|
||||||
private List<IotSceneRuleAction> sceneRuleActions;
|
private List<IotSceneRuleAction> sceneRuleActions;
|
||||||
|
|
||||||
@@ -285,7 +283,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
|
|||||||
private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {
|
private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {
|
||||||
try {
|
try {
|
||||||
// 2. 检查触发器的条件分组
|
// 2. 检查触发器的条件分组
|
||||||
return triggerMatcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);
|
return matcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}",
|
log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}",
|
||||||
sceneRule.getId(), trigger.getType(), message, e);
|
sceneRule.getId(), trigger.getType(), message, e);
|
||||||
@@ -333,7 +331,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于消息,判断触发器的条件是否匹配
|
* 基于消息,判断触发器的子条件是否匹配
|
||||||
*
|
*
|
||||||
* @param message 设备消息
|
* @param message 设备消息
|
||||||
* @param condition 触发条件
|
* @param condition 触发条件
|
||||||
@@ -344,21 +342,8 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
|
|||||||
private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
|
private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
|
||||||
IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
|
IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
|
||||||
try {
|
try {
|
||||||
// 1. 根据条件类型进行匹配
|
// 使用重构后的条件匹配管理器进行匹配
|
||||||
if (IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType().equals(condition.getType())) {
|
return matcherManager.isConditionMatched(message, condition);
|
||||||
// 设备状态条件匹配
|
|
||||||
return matchDeviceStateCondition(message, condition);
|
|
||||||
} else if (IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType().equals(condition.getType())) {
|
|
||||||
// 设备属性条件匹配
|
|
||||||
return matchDevicePropertyCondition(message, condition);
|
|
||||||
} else if (IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType().equals(condition.getType())) {
|
|
||||||
// 当前时间条件匹配
|
|
||||||
return matchCurrentTimeCondition(condition);
|
|
||||||
} else {
|
|
||||||
log.warn("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 存在未知的条件类型({})]",
|
|
||||||
sceneRule.getId(), trigger, condition.getType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]",
|
log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]",
|
||||||
sceneRule.getId(), trigger, e);
|
sceneRule.getId(), trigger, e);
|
||||||
@@ -366,105 +351,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 匹配设备状态条件
|
|
||||||
*
|
|
||||||
* @param message 设备消息
|
|
||||||
* @param condition 触发条件
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
private boolean matchDeviceStateCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
|
||||||
// TODO @芋艿:需要根据设备状态进行匹配
|
|
||||||
// 这里需要检查消息中的设备状态是否符合条件中定义的状态
|
|
||||||
log.debug("[matchDeviceStateCondition][设备状态条件匹配逻辑待实现] condition: {}", condition);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 匹配设备属性条件
|
|
||||||
*
|
|
||||||
* @param message 设备消息
|
|
||||||
* @param condition 触发条件
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
private boolean matchDevicePropertyCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
|
||||||
// 1. 检查标识符是否匹配
|
|
||||||
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
|
||||||
if (StrUtil.isBlank(condition.getIdentifier()) || !condition.getIdentifier().equals(messageIdentifier)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 获取消息中的属性值
|
|
||||||
Object messageValue = message.getData();
|
|
||||||
if (messageValue == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 根据操作符进行匹配
|
|
||||||
return evaluateCondition(messageValue, condition.getOperator(), condition.getParam());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 匹配当前时间条件
|
|
||||||
*
|
|
||||||
* @param condition 触发条件
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
private boolean matchCurrentTimeCondition(IotSceneRuleDO.TriggerCondition condition) {
|
|
||||||
// TODO @芋艿:需要根据当前时间进行匹配
|
|
||||||
// 这里需要检查当前时间是否符合条件中定义的时间范围
|
|
||||||
log.debug("[matchCurrentTimeCondition][当前时间条件匹配逻辑待实现] condition: {}", condition);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 评估条件是否匹配
|
|
||||||
*
|
|
||||||
* @param sourceValue 源值(来自消息)
|
|
||||||
* @param operator 操作符
|
|
||||||
* @param paramValue 参数值(来自条件配置)
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
private boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
|
|
||||||
try {
|
|
||||||
// 1. 校验操作符是否合法
|
|
||||||
IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
|
|
||||||
if (operatorEnum == null) {
|
|
||||||
log.warn("[evaluateCondition][存在错误的操作符({})]", operator);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.1 构建 Spring 表达式的变量
|
|
||||||
Map<String, Object> springExpressionVariables = MapUtil.<String, Object>builder()
|
|
||||||
.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue)
|
|
||||||
.build();
|
|
||||||
// 2.2 根据操作符类型处理参数值
|
|
||||||
if (StrUtil.isNotBlank(paramValue)) {
|
|
||||||
// TODO @puhui999:这里是不是在 IotSceneRuleConditionOperatorEnum 加个属性;
|
|
||||||
if (operatorEnum == IotSceneRuleConditionOperatorEnum.IN
|
|
||||||
|| operatorEnum == IotSceneRuleConditionOperatorEnum.NOT_IN
|
|
||||||
|| operatorEnum == IotSceneRuleConditionOperatorEnum.BETWEEN
|
|
||||||
|| operatorEnum == IotSceneRuleConditionOperatorEnum.NOT_BETWEEN) {
|
|
||||||
// 处理多值情况
|
|
||||||
List<String> paramValues = StrUtil.split(paramValue, CharPool.COMMA);
|
|
||||||
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
|
|
||||||
convertList(paramValues, NumberUtil::parseDouble));
|
|
||||||
} else {
|
|
||||||
// 处理单值情况
|
|
||||||
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
|
|
||||||
NumberUtil.parseDouble(paramValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 计算 Spring 表达式
|
|
||||||
return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}",
|
|
||||||
sourceValue, operator, paramValue, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。
|
// TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ import java.util.Map;
|
|||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IoT 场景规则触发器匹配器抽象基类
|
* IoT 场景规则匹配器抽象基类
|
||||||
* <p>
|
* <p>
|
||||||
* 提供通用的条件评估逻辑和工具方法
|
* 提供通用的条件评估逻辑和工具方法,支持触发器和条件两种匹配类型
|
||||||
*
|
*
|
||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRuleTriggerMatcher {
|
public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评估条件是否匹配
|
* 评估条件是否匹配
|
||||||
@@ -68,6 +68,8 @@ public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRule
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 触发器相关工具方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查基础触发器参数是否有效
|
* 检查基础触发器参数是否有效
|
||||||
*
|
*
|
||||||
@@ -79,15 +81,81 @@ public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRule
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查操作符和值是否有效
|
* 检查触发器操作符和值是否有效
|
||||||
*
|
*
|
||||||
* @param trigger 触发器配置
|
* @param trigger 触发器配置
|
||||||
* @return 是否有效
|
* @return 是否有效
|
||||||
*/
|
*/
|
||||||
protected boolean isOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
|
protected boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
|
||||||
return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());
|
return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录触发器匹配成功日志
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param trigger 触发器配置
|
||||||
|
*/
|
||||||
|
protected void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
|
log.debug("[{}][消息({}) 匹配触发器({}) 成功]", getMatcherName(), message.getRequestId(), trigger.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录触发器匹配失败日志
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param trigger 触发器配置
|
||||||
|
* @param reason 失败原因
|
||||||
|
*/
|
||||||
|
protected void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
|
||||||
|
log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", getMatcherName(), message.getRequestId(), trigger.getType(), reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 条件相关工具方法 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查基础条件参数是否有效
|
||||||
|
*
|
||||||
|
* @param condition 触发条件
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
protected boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
return condition != null && condition.getType() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查条件操作符和参数是否有效
|
||||||
|
*
|
||||||
|
* @param condition 触发条件
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
protected boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录条件匹配成功日志
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param condition 触发条件
|
||||||
|
*/
|
||||||
|
protected void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
log.debug("[{}][消息({}) 匹配条件({}) 成功]", getMatcherName(), message.getRequestId(), condition.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录条件匹配失败日志
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param condition 触发条件
|
||||||
|
* @param reason 失败原因
|
||||||
|
*/
|
||||||
|
protected void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
|
||||||
|
log.debug("[{}][消息({}) 匹配条件({}) 失败: {}]", getMatcherName(), message.getRequestId(), condition.getType(), reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 通用工具方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查标识符是否匹配
|
* 检查标识符是否匹配
|
||||||
*
|
*
|
||||||
@@ -99,25 +167,4 @@ public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRule
|
|||||||
return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
|
return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录匹配成功日志
|
|
||||||
*
|
|
||||||
* @param message 设备消息
|
|
||||||
* @param trigger 触发器配置
|
|
||||||
*/
|
|
||||||
protected void logMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
|
||||||
log.debug("[{}][消息({}) 匹配触发器({}) 成功]", getMatcherName(), message.getRequestId(), trigger.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录匹配失败日志
|
|
||||||
*
|
|
||||||
* @param message 设备消息
|
|
||||||
* @param trigger 触发器配置
|
|
||||||
* @param reason 失败原因
|
|
||||||
*/
|
|
||||||
protected void logMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
|
|
||||||
log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", getMatcherName(), message.getRequestId(), trigger.getType(), reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
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.enums.rule.IotSceneRuleConditionLevelEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前时间条件匹配器
|
||||||
|
* <p>
|
||||||
|
* 处理时间相关的子条件匹配逻辑
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间格式化器 - HH:mm:ss
|
||||||
|
*/
|
||||||
|
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间格式化器 - HH:mm
|
||||||
|
*/
|
||||||
|
private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern("HH:mm");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.CONDITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
|
||||||
|
return IotSceneRuleConditionTypeEnum.CURRENT_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
|
||||||
|
return IotSceneRuleConditionLevelEnum.SECONDARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
// 1. 基础参数校验
|
||||||
|
if (!isBasicConditionValid(condition)) {
|
||||||
|
logConditionMatchFailure(message, condition, "条件基础参数无效");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查操作符和参数是否有效
|
||||||
|
if (!isConditionOperatorAndParamValid(condition)) {
|
||||||
|
logConditionMatchFailure(message, condition, "操作符或参数无效");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取当前时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
// 4. 根据操作符类型进行不同的时间匹配
|
||||||
|
String operator = condition.getOperator();
|
||||||
|
String param = condition.getParam();
|
||||||
|
|
||||||
|
boolean matched = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (operator.startsWith("date_time_")) {
|
||||||
|
// 日期时间匹配(时间戳)
|
||||||
|
matched = matchDateTime(now, operator, param);
|
||||||
|
} else if (operator.startsWith("time_")) {
|
||||||
|
// 当日时间匹配(HH:mm:ss)
|
||||||
|
matched = matchTime(now.toLocalTime(), operator, param);
|
||||||
|
} else {
|
||||||
|
// 其他操作符,使用通用条件评估器
|
||||||
|
matched = evaluateCondition(now.toEpochSecond(java.time.ZoneOffset.of("+8")), operator, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
logConditionMatchSuccess(message, condition);
|
||||||
|
} else {
|
||||||
|
logConditionMatchFailure(message, condition, "时间条件不匹配");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[CurrentTimeConditionMatcher][时间条件匹配异常] operator: {}, param: {}", operator, param, e);
|
||||||
|
logConditionMatchFailure(message, condition, "时间条件匹配异常: " + e.getMessage());
|
||||||
|
matched = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配日期时间(时间戳)
|
||||||
|
*/
|
||||||
|
private boolean matchDateTime(LocalDateTime now, String operator, String param) {
|
||||||
|
long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8"));
|
||||||
|
return evaluateCondition(currentTimestamp, operator.substring("date_time_".length()), param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配当日时间(HH:mm:ss)
|
||||||
|
*/
|
||||||
|
private boolean matchTime(LocalTime currentTime, String operator, String param) {
|
||||||
|
try {
|
||||||
|
String actualOperator = operator.substring("time_".length());
|
||||||
|
|
||||||
|
if ("between".equals(actualOperator)) {
|
||||||
|
// 时间区间匹配
|
||||||
|
String[] timeRange = param.split(",");
|
||||||
|
if (timeRange.length != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalTime startTime = parseTime(timeRange[0].trim());
|
||||||
|
LocalTime endTime = parseTime(timeRange[1].trim());
|
||||||
|
|
||||||
|
return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime);
|
||||||
|
} else {
|
||||||
|
// 单个时间比较
|
||||||
|
LocalTime targetTime = parseTime(param);
|
||||||
|
|
||||||
|
switch (actualOperator) {
|
||||||
|
case ">":
|
||||||
|
return currentTime.isAfter(targetTime);
|
||||||
|
case "<":
|
||||||
|
return currentTime.isBefore(targetTime);
|
||||||
|
case ">=":
|
||||||
|
return !currentTime.isBefore(targetTime);
|
||||||
|
case "<=":
|
||||||
|
return !currentTime.isAfter(targetTime);
|
||||||
|
case "=":
|
||||||
|
return currentTime.equals(targetTime);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[CurrentTimeConditionMatcher][时间解析异常] param: {}", param, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析时间字符串
|
||||||
|
*/
|
||||||
|
private LocalTime parseTime(String timeStr) {
|
||||||
|
if (StrUtil.isBlank(timeStr)) {
|
||||||
|
throw new IllegalArgumentException("时间字符串不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试不同的时间格式
|
||||||
|
try {
|
||||||
|
if (timeStr.length() == 5) { // HH:mm
|
||||||
|
return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT);
|
||||||
|
} else { // HH:mm:ss
|
||||||
|
return LocalTime.parse(timeStr, TIME_FORMATTER);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("时间格式无效: " + timeStr, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 40; // 较低优先级
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,13 +16,18 @@ import org.springframework.stereotype.Component;
|
|||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备事件上报消息方法
|
* 设备事件上报消息方法
|
||||||
*/
|
*/
|
||||||
private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod();
|
private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.TRIGGER;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
||||||
return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
|
return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
|
||||||
@@ -32,20 +37,20 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
|
|||||||
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
// 1. 基础参数校验
|
// 1. 基础参数校验
|
||||||
if (!isBasicTriggerValid(trigger)) {
|
if (!isBasicTriggerValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "触发器基础参数无效");
|
logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查消息方法是否匹配
|
// 2. 检查消息方法是否匹配
|
||||||
if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) {
|
if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) {
|
||||||
logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod());
|
logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查标识符是否匹配
|
// 3. 检查标识符是否匹配
|
||||||
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
||||||
if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
|
if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
|
||||||
logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
|
logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,18 +59,18 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
|
|||||||
if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
|
if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
|
||||||
Object eventData = message.getData();
|
Object eventData = message.getData();
|
||||||
if (eventData == null) {
|
if (eventData == null) {
|
||||||
logMatchFailure(message, trigger, "消息中事件数据为空");
|
logTriggerMatchFailure(message, trigger, "消息中事件数据为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
|
boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
logMatchFailure(message, trigger, "事件数据条件不匹配");
|
logTriggerMatchFailure(message, trigger, "事件数据条件不匹配");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logMatchSuccess(message, trigger);
|
logTriggerMatchSuccess(message, trigger);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
||||||
|
|
||||||
|
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.enums.rule.IotSceneRuleConditionLevelEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备属性条件匹配器
|
||||||
|
* <p>
|
||||||
|
* 处理设备属性相关的子条件匹配逻辑
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DevicePropertyConditionMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.CONDITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
|
||||||
|
return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
|
||||||
|
return IotSceneRuleConditionLevelEnum.SECONDARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
// 1. 基础参数校验
|
||||||
|
if (!isBasicConditionValid(condition)) {
|
||||||
|
logConditionMatchFailure(message, condition, "条件基础参数无效");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查标识符是否匹配
|
||||||
|
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
||||||
|
if (!isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) {
|
||||||
|
logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查操作符和参数是否有效
|
||||||
|
if (!isConditionOperatorAndParamValid(condition)) {
|
||||||
|
logConditionMatchFailure(message, condition, "操作符或参数无效");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 获取属性值
|
||||||
|
Object propertyValue = message.getData();
|
||||||
|
if (propertyValue == null) {
|
||||||
|
logConditionMatchFailure(message, condition, "消息中属性值为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 使用条件评估器进行匹配
|
||||||
|
boolean matched = evaluateCondition(propertyValue, condition.getOperator(), condition.getParam());
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
logConditionMatchSuccess(message, condition);
|
||||||
|
} else {
|
||||||
|
logConditionMatchFailure(message, condition, "设备属性条件不匹配");
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 25; // 中等优先级
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,13 +15,18 @@ import org.springframework.stereotype.Component;
|
|||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备属性上报消息方法
|
* 设备属性上报消息方法
|
||||||
*/
|
*/
|
||||||
private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod();
|
private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.TRIGGER;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
||||||
return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
|
return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
|
||||||
@@ -31,33 +36,33 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
|
|||||||
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
// 1. 基础参数校验
|
// 1. 基础参数校验
|
||||||
if (!isBasicTriggerValid(trigger)) {
|
if (!isBasicTriggerValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "触发器基础参数无效");
|
logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查消息方法是否匹配
|
// 2. 检查消息方法是否匹配
|
||||||
if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) {
|
if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) {
|
||||||
logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod());
|
logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查标识符是否匹配
|
// 3. 检查标识符是否匹配
|
||||||
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
||||||
if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
|
if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
|
||||||
logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
|
logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 检查操作符和值是否有效
|
// 4. 检查操作符和值是否有效
|
||||||
if (!isOperatorAndValueValid(trigger)) {
|
if (!isTriggerOperatorAndValueValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "操作符或值无效");
|
logTriggerMatchFailure(message, trigger, "操作符或值无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 获取属性值
|
// 5. 获取属性值
|
||||||
Object propertyValue = message.getData();
|
Object propertyValue = message.getData();
|
||||||
if (propertyValue == null) {
|
if (propertyValue == null) {
|
||||||
logMatchFailure(message, trigger, "消息中属性值为空");
|
logTriggerMatchFailure(message, trigger, "消息中属性值为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +70,9 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
|
|||||||
boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
|
boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
|
||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
logMatchSuccess(message, trigger);
|
logTriggerMatchSuccess(message, trigger);
|
||||||
} else {
|
} else {
|
||||||
logMatchFailure(message, trigger, "属性值条件不匹配");
|
logTriggerMatchFailure(message, trigger, "属性值条件不匹配");
|
||||||
}
|
}
|
||||||
|
|
||||||
return matched;
|
return matched;
|
||||||
|
|||||||
@@ -15,13 +15,18 @@ import org.springframework.stereotype.Component;
|
|||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备服务调用消息方法
|
* 设备服务调用消息方法
|
||||||
*/
|
*/
|
||||||
private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod();
|
private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.TRIGGER;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
||||||
return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
|
return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
|
||||||
@@ -31,27 +36,27 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg
|
|||||||
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
// 1. 基础参数校验
|
// 1. 基础参数校验
|
||||||
if (!isBasicTriggerValid(trigger)) {
|
if (!isBasicTriggerValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "触发器基础参数无效");
|
logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查消息方法是否匹配
|
// 2. 检查消息方法是否匹配
|
||||||
if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) {
|
if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) {
|
||||||
logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod());
|
logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查标识符是否匹配
|
// 3. 检查标识符是否匹配
|
||||||
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
|
||||||
if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
|
if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
|
||||||
logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
|
logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 对于服务调用触发器,通常只需要匹配服务标识符即可
|
// 4. 对于服务调用触发器,通常只需要匹配服务标识符即可
|
||||||
// 不需要检查操作符和值,因为服务调用本身就是触发条件
|
// 不需要检查操作符和值,因为服务调用本身就是触发条件
|
||||||
|
|
||||||
logMatchSuccess(message, trigger);
|
logTriggerMatchSuccess(message, trigger);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
||||||
|
|
||||||
|
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.enums.rule.IotSceneRuleConditionLevelEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备状态条件匹配器
|
||||||
|
* <p>
|
||||||
|
* 处理设备状态相关的子条件匹配逻辑
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DeviceStateConditionMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.CONDITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
|
||||||
|
return IotSceneRuleConditionTypeEnum.DEVICE_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
|
||||||
|
return IotSceneRuleConditionLevelEnum.SECONDARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
// 1. 基础参数校验
|
||||||
|
if (!isBasicConditionValid(condition)) {
|
||||||
|
logConditionMatchFailure(message, condition, "条件基础参数无效");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查操作符和参数是否有效
|
||||||
|
if (!isConditionOperatorAndParamValid(condition)) {
|
||||||
|
logConditionMatchFailure(message, condition, "操作符或参数无效");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取设备状态值
|
||||||
|
// 设备状态通常在消息的 data 字段中
|
||||||
|
Object stateValue = message.getData();
|
||||||
|
if (stateValue == null) {
|
||||||
|
logConditionMatchFailure(message, condition, "消息中设备状态值为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 使用条件评估器进行匹配
|
||||||
|
boolean matched = evaluateCondition(stateValue, condition.getOperator(), condition.getParam());
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
logConditionMatchSuccess(message, condition);
|
||||||
|
} else {
|
||||||
|
logConditionMatchFailure(message, condition, "设备状态条件不匹配");
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 30; // 中等优先级
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,13 +14,18 @@ import org.springframework.stereotype.Component;
|
|||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备状态更新消息方法
|
* 设备状态更新消息方法
|
||||||
*/
|
*/
|
||||||
private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod();
|
private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.TRIGGER;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
||||||
return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
|
return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
|
||||||
@@ -30,26 +35,26 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
|
|||||||
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
// 1. 基础参数校验
|
// 1. 基础参数校验
|
||||||
if (!isBasicTriggerValid(trigger)) {
|
if (!isBasicTriggerValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "触发器基础参数无效");
|
logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查消息方法是否匹配
|
// 2. 检查消息方法是否匹配
|
||||||
if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) {
|
if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) {
|
||||||
logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod());
|
logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查操作符和值是否有效
|
// 3. 检查操作符和值是否有效
|
||||||
if (!isOperatorAndValueValid(trigger)) {
|
if (!isTriggerOperatorAndValueValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "操作符或值无效");
|
logTriggerMatchFailure(message, trigger, "操作符或值无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 获取设备状态值
|
// 4. 获取设备状态值
|
||||||
Object stateValue = message.getData();
|
Object stateValue = message.getData();
|
||||||
if (stateValue == null) {
|
if (stateValue == null) {
|
||||||
logMatchFailure(message, trigger, "消息中设备状态值为空");
|
logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +62,9 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
|
|||||||
boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
|
boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
|
||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
logMatchSuccess(message, trigger);
|
logTriggerMatchSuccess(message, trigger);
|
||||||
} else {
|
} else {
|
||||||
logMatchFailure(message, trigger, "状态值条件不匹配");
|
logTriggerMatchFailure(message, trigger, "状态值条件不匹配");
|
||||||
}
|
}
|
||||||
|
|
||||||
return matched;
|
return matched;
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
||||||
|
|
||||||
|
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.enums.rule.IotSceneRuleConditionLevelEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IoT 场景规则匹配器统一接口
|
||||||
|
* <p>
|
||||||
|
* 支持触发器匹配和条件匹配两种类型,遵循策略模式设计
|
||||||
|
* <p>
|
||||||
|
* 匹配器类型说明:
|
||||||
|
* - 触发器匹配器:用于匹配主触发条件(如设备消息类型、定时器等)
|
||||||
|
* - 条件匹配器:用于匹配子条件(如设备状态、属性值、时间条件等)
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
public interface IotSceneRuleMatcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配器类型枚举
|
||||||
|
*/
|
||||||
|
enum MatcherType {
|
||||||
|
/**
|
||||||
|
* 触发器匹配器 - 用于匹配主触发条件
|
||||||
|
*/
|
||||||
|
TRIGGER,
|
||||||
|
/**
|
||||||
|
* 条件匹配器 - 用于匹配子条件
|
||||||
|
*/
|
||||||
|
CONDITION
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取匹配器类型
|
||||||
|
*
|
||||||
|
* @return 匹配器类型
|
||||||
|
*/
|
||||||
|
MatcherType getMatcherType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取支持的触发器类型(仅触发器匹配器需要实现)
|
||||||
|
*
|
||||||
|
* @return 触发器类型枚举,条件匹配器返回 null
|
||||||
|
*/
|
||||||
|
default IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取支持的条件类型(仅条件匹配器需要实现)
|
||||||
|
*
|
||||||
|
* @return 条件类型枚举,触发器匹配器返回 null
|
||||||
|
*/
|
||||||
|
default IotSceneRuleConditionTypeEnum getSupportedConditionType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取支持的条件层级(仅条件匹配器需要实现)
|
||||||
|
*
|
||||||
|
* @return 条件层级枚举,触发器匹配器返回 null
|
||||||
|
*/
|
||||||
|
default IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查触发器是否匹配消息(仅触发器匹配器需要实现)
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param trigger 触发器配置
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
default boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
|
throw new UnsupportedOperationException("触发器匹配方法仅支持触发器匹配器");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查条件是否匹配消息(仅条件匹配器需要实现)
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param condition 触发条件
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
default boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
throw new UnsupportedOperationException("条件匹配方法仅支持条件匹配器");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取匹配优先级(数值越小优先级越高)
|
||||||
|
* <p>
|
||||||
|
* 用于在多个匹配器支持同一类型时确定优先级
|
||||||
|
*
|
||||||
|
* @return 优先级数值
|
||||||
|
*/
|
||||||
|
default int getPriority() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取匹配器名称,用于日志和调试
|
||||||
|
*
|
||||||
|
* @return 匹配器名称
|
||||||
|
*/
|
||||||
|
default String getMatcherName() {
|
||||||
|
return this.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用该匹配器
|
||||||
|
* <p>
|
||||||
|
* 可用于动态开关某些匹配器
|
||||||
|
*
|
||||||
|
* @return 是否启用
|
||||||
|
*/
|
||||||
|
default boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
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.enums.rule.IotSceneRuleConditionLevelEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IoT 场景规则匹配器统一管理器
|
||||||
|
* <p>
|
||||||
|
* 负责管理所有匹配器(触发器匹配器和条件匹配器),并提供统一的匹配入口
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class IotSceneRuleMatcherManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发器匹配器映射表
|
||||||
|
* Key: 触发器类型枚举
|
||||||
|
* Value: 对应的匹配器实例
|
||||||
|
*/
|
||||||
|
private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleMatcher> triggerMatcherMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件匹配器映射表
|
||||||
|
* Key: 条件类型枚举
|
||||||
|
* Value: 对应的匹配器实例
|
||||||
|
*/
|
||||||
|
private final Map<IotSceneRuleConditionTypeEnum, IotSceneRuleMatcher> conditionMatcherMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有匹配器列表(按优先级排序)
|
||||||
|
*/
|
||||||
|
private final List<IotSceneRuleMatcher> allMatchers;
|
||||||
|
|
||||||
|
public IotSceneRuleMatcherManager(List<IotSceneRuleMatcher> matchers) {
|
||||||
|
if (CollUtil.isEmpty(matchers)) {
|
||||||
|
log.warn("[IotSceneRuleMatcherManager][没有找到任何匹配器]");
|
||||||
|
this.triggerMatcherMap = new HashMap<>();
|
||||||
|
this.conditionMatcherMap = new HashMap<>();
|
||||||
|
this.allMatchers = new ArrayList<>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按优先级排序并过滤启用的匹配器
|
||||||
|
this.allMatchers = matchers.stream()
|
||||||
|
.filter(IotSceneRuleMatcher::isEnabled)
|
||||||
|
.sorted(Comparator.comparing(IotSceneRuleMatcher::getPriority))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 分离触发器匹配器和条件匹配器
|
||||||
|
List<IotSceneRuleMatcher> triggerMatchers = this.allMatchers.stream()
|
||||||
|
.filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.TRIGGER)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<IotSceneRuleMatcher> conditionMatchers = this.allMatchers.stream()
|
||||||
|
.filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 构建触发器匹配器映射表
|
||||||
|
this.triggerMatcherMap = triggerMatchers.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
IotSceneRuleMatcher::getSupportedTriggerType,
|
||||||
|
Function.identity(),
|
||||||
|
(existing, replacement) -> {
|
||||||
|
log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
|
||||||
|
existing.getSupportedTriggerType(),
|
||||||
|
existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
|
||||||
|
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
|
||||||
|
},
|
||||||
|
LinkedHashMap::new
|
||||||
|
));
|
||||||
|
|
||||||
|
// 构建条件匹配器映射表
|
||||||
|
this.conditionMatcherMap = conditionMatchers.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
IotSceneRuleMatcher::getSupportedConditionType,
|
||||||
|
Function.identity(),
|
||||||
|
(existing, replacement) -> {
|
||||||
|
log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]",
|
||||||
|
existing.getSupportedConditionType(),
|
||||||
|
existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
|
||||||
|
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
|
||||||
|
},
|
||||||
|
LinkedHashMap::new
|
||||||
|
));
|
||||||
|
|
||||||
|
log.info("[IotSceneRuleMatcherManager][初始化完成,共加载 {} 个匹配器,其中触发器匹配器 {} 个,条件匹配器 {} 个]",
|
||||||
|
this.allMatchers.size(), this.triggerMatcherMap.size(), this.conditionMatcherMap.size());
|
||||||
|
|
||||||
|
// 记录触发器匹配器详情
|
||||||
|
this.triggerMatcherMap.forEach((type, matcher) ->
|
||||||
|
log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}",
|
||||||
|
type, matcher.getMatcherName(), matcher.getPriority()));
|
||||||
|
|
||||||
|
// 记录条件匹配器详情
|
||||||
|
this.conditionMatcherMap.forEach((type, matcher) ->
|
||||||
|
log.info("[IotSceneRuleMatcherManager][条件匹配器] 类型: {}, 匹配器: {}, 优先级: {}, 层级: {}",
|
||||||
|
type, matcher.getMatcherName(), matcher.getPriority(), matcher.getSupportedConditionLevel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查触发器是否匹配消息(主条件匹配)
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param trigger 触发器配置
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
|
if (message == null || trigger == null || trigger.getType() == null) {
|
||||||
|
log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据触发器类型查找对应的匹配器
|
||||||
|
IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType());
|
||||||
|
if (triggerType == null) {
|
||||||
|
log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IotSceneRuleMatcher matcher = triggerMatcherMap.get(triggerType);
|
||||||
|
if (matcher == null) {
|
||||||
|
log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return matcher.isMatched(message, trigger);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}",
|
||||||
|
message, trigger, matcher.getMatcherName(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查子条件是否匹配消息
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param condition 触发条件
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
|
||||||
|
if (message == null || condition == null || condition.getType() == null) {
|
||||||
|
log.debug("[isConditionMatched][参数无效] message: {}, condition: {}", message, condition);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据条件类型查找对应的匹配器
|
||||||
|
IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType());
|
||||||
|
if (conditionType == null) {
|
||||||
|
log.warn("[isConditionMatched][未知的条件类型: {}]", condition.getType());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IotSceneRuleMatcher matcher = conditionMatcherMap.get(conditionType);
|
||||||
|
if (matcher == null) {
|
||||||
|
log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return matcher.isMatched(message, condition);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[isConditionMatched][条件匹配异常] message: {}, condition: {}, matcher: {}",
|
||||||
|
message, condition, matcher.getMatcherName(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据类型值查找条件类型枚举
|
||||||
|
*
|
||||||
|
* @param typeValue 类型值
|
||||||
|
* @return 条件类型枚举
|
||||||
|
*/
|
||||||
|
private IotSceneRuleConditionTypeEnum findConditionTypeEnum(Integer typeValue) {
|
||||||
|
return Arrays.stream(IotSceneRuleConditionTypeEnum.values())
|
||||||
|
.filter(type -> type.getType().equals(typeValue))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有支持的触发器类型
|
||||||
|
*
|
||||||
|
* @return 支持的触发器类型列表
|
||||||
|
*/
|
||||||
|
public Set<IotSceneRuleTriggerTypeEnum> getSupportedTriggerTypes() {
|
||||||
|
return new HashSet<>(triggerMatcherMap.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有支持的条件类型
|
||||||
|
*
|
||||||
|
* @return 支持的条件类型列表
|
||||||
|
*/
|
||||||
|
public Set<IotSceneRuleConditionTypeEnum> getSupportedConditionTypes() {
|
||||||
|
return new HashSet<>(conditionMatcherMap.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定触发器类型的匹配器
|
||||||
|
*
|
||||||
|
* @param triggerType 触发器类型
|
||||||
|
* @return 匹配器实例,如果不存在则返回 null
|
||||||
|
*/
|
||||||
|
public IotSceneRuleMatcher getTriggerMatcher(IotSceneRuleTriggerTypeEnum triggerType) {
|
||||||
|
return triggerMatcherMap.get(triggerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定条件类型的匹配器
|
||||||
|
*
|
||||||
|
* @param conditionType 条件类型
|
||||||
|
* @return 匹配器实例,如果不存在则返回 null
|
||||||
|
*/
|
||||||
|
public IotSceneRuleMatcher getConditionMatcher(IotSceneRuleConditionTypeEnum conditionType) {
|
||||||
|
return conditionMatcherMap.get(conditionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据条件层级获取匹配器列表
|
||||||
|
*
|
||||||
|
* @param level 条件层级
|
||||||
|
* @return 匹配器列表
|
||||||
|
*/
|
||||||
|
public List<IotSceneRuleMatcher> getMatchersByLevel(IotSceneRuleConditionLevelEnum level) {
|
||||||
|
return allMatchers.stream()
|
||||||
|
.filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
|
||||||
|
.filter(matcher -> matcher.getSupportedConditionLevel() == level)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有匹配器的统计信息
|
||||||
|
*
|
||||||
|
* @return 统计信息映射表
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getMatcherStatistics() {
|
||||||
|
Map<String, Object> statistics = new HashMap<>();
|
||||||
|
statistics.put("totalMatchers", allMatchers.size());
|
||||||
|
statistics.put("triggerMatchers", triggerMatcherMap.size());
|
||||||
|
statistics.put("conditionMatchers", conditionMatcherMap.size());
|
||||||
|
statistics.put("supportedTriggerTypes", getSupportedTriggerTypes());
|
||||||
|
statistics.put("supportedConditionTypes", getSupportedConditionTypes());
|
||||||
|
|
||||||
|
// 按层级统计条件匹配器
|
||||||
|
Map<IotSceneRuleConditionLevelEnum, Long> levelStats = allMatchers.stream()
|
||||||
|
.filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
IotSceneRuleMatcher::getSupportedConditionLevel,
|
||||||
|
Collectors.counting()
|
||||||
|
));
|
||||||
|
statistics.put("conditionLevelStatistics", levelStats);
|
||||||
|
|
||||||
|
// 触发器匹配器详情
|
||||||
|
Map<String, Object> triggerMatcherDetails = new HashMap<>();
|
||||||
|
triggerMatcherMap.forEach((type, matcher) -> {
|
||||||
|
Map<String, Object> detail = new HashMap<>();
|
||||||
|
detail.put("matcherName", matcher.getMatcherName());
|
||||||
|
detail.put("priority", matcher.getPriority());
|
||||||
|
detail.put("enabled", matcher.isEnabled());
|
||||||
|
triggerMatcherDetails.put(type.name(), detail);
|
||||||
|
});
|
||||||
|
statistics.put("triggerMatcherDetails", triggerMatcherDetails);
|
||||||
|
|
||||||
|
// 条件匹配器详情
|
||||||
|
Map<String, Object> conditionMatcherDetails = new HashMap<>();
|
||||||
|
conditionMatcherMap.forEach((type, matcher) -> {
|
||||||
|
Map<String, Object> detail = new HashMap<>();
|
||||||
|
detail.put("matcherName", matcher.getMatcherName());
|
||||||
|
detail.put("priority", matcher.getPriority());
|
||||||
|
detail.put("level", matcher.getSupportedConditionLevel());
|
||||||
|
detail.put("enabled", matcher.isEnabled());
|
||||||
|
conditionMatcherDetails.put(type.name(), detail);
|
||||||
|
});
|
||||||
|
statistics.put("conditionMatcherDetails", conditionMatcherDetails);
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
|
||||||
|
|
||||||
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.enums.rule.IotSceneRuleTriggerTypeEnum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IoT 场景规则触发器匹配策略接口
|
|
||||||
* <p>
|
|
||||||
* 用于实现不同类型触发器的匹配逻辑,遵循策略模式设计
|
|
||||||
*
|
|
||||||
* @author HUIHUI
|
|
||||||
*/
|
|
||||||
public interface IotSceneRuleTriggerMatcher {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取支持的触发器类型
|
|
||||||
*
|
|
||||||
* @return 触发器类型枚举
|
|
||||||
*/
|
|
||||||
IotSceneRuleTriggerTypeEnum getSupportedTriggerType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查触发器是否匹配消息
|
|
||||||
*
|
|
||||||
* @param message 设备消息
|
|
||||||
* @param trigger 触发器配置
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取匹配优先级(数值越小优先级越高)
|
|
||||||
* <p>
|
|
||||||
* 用于在多个匹配器支持同一触发器类型时确定优先级
|
|
||||||
*
|
|
||||||
* @return 优先级数值
|
|
||||||
*/
|
|
||||||
default int getPriority() {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取匹配器名称,用于日志和调试
|
|
||||||
*
|
|
||||||
* @return 匹配器名称
|
|
||||||
*/
|
|
||||||
default String getMatcherName() {
|
|
||||||
return this.getClass().getSimpleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否启用该匹配器
|
|
||||||
* <p>
|
|
||||||
* 可用于动态开关某些匹配器
|
|
||||||
*
|
|
||||||
* @return 是否启用
|
|
||||||
*/
|
|
||||||
default boolean isEnabled() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
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.enums.rule.IotSceneRuleTriggerTypeEnum;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IoT 场景规则触发器匹配管理器
|
|
||||||
* <p>
|
|
||||||
* 负责管理所有触发器匹配器,并提供统一的匹配入口
|
|
||||||
*
|
|
||||||
* @author HUIHUI
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
public class IotSceneRuleTriggerMatcherManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 触发器匹配器映射表
|
|
||||||
* Key: 触发器类型枚举
|
|
||||||
* Value: 对应的匹配器实例
|
|
||||||
*/
|
|
||||||
private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleTriggerMatcher> matcherMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有匹配器列表(按优先级排序)
|
|
||||||
*/
|
|
||||||
private final List<IotSceneRuleTriggerMatcher> allMatchers;
|
|
||||||
|
|
||||||
public IotSceneRuleTriggerMatcherManager(List<IotSceneRuleTriggerMatcher> matchers) {
|
|
||||||
if (CollUtil.isEmpty(matchers)) {
|
|
||||||
log.warn("[IotSceneRuleTriggerMatcherManager][没有找到任何触发器匹配器]");
|
|
||||||
this.matcherMap = new HashMap<>();
|
|
||||||
this.allMatchers = new ArrayList<>();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按优先级排序并过滤启用的匹配器
|
|
||||||
this.allMatchers = matchers.stream()
|
|
||||||
.filter(IotSceneRuleTriggerMatcher::isEnabled)
|
|
||||||
.sorted(Comparator.comparing(IotSceneRuleTriggerMatcher::getPriority))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
// 构建匹配器映射表
|
|
||||||
this.matcherMap = this.allMatchers.stream()
|
|
||||||
.collect(Collectors.toMap(
|
|
||||||
IotSceneRuleTriggerMatcher::getSupportedTriggerType,
|
|
||||||
Function.identity(),
|
|
||||||
(existing, replacement) -> {
|
|
||||||
log.warn("[IotSceneRuleTriggerMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
|
|
||||||
existing.getSupportedTriggerType(),
|
|
||||||
existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
|
|
||||||
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
|
|
||||||
},
|
|
||||||
LinkedHashMap::new
|
|
||||||
));
|
|
||||||
|
|
||||||
log.info("[IotSceneRuleTriggerMatcherManager][初始化完成,共加载 {} 个触发器匹配器]", this.matcherMap.size());
|
|
||||||
this.matcherMap.forEach((type, matcher) ->
|
|
||||||
log.info("[IotSceneRuleTriggerMatcherManager][触发器类型: {}, 匹配器: {}, 优先级: {}]",
|
|
||||||
type, matcher.getMatcherName(), matcher.getPriority()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查触发器是否匹配消息
|
|
||||||
*
|
|
||||||
* @param message 设备消息
|
|
||||||
* @param trigger 触发器配置
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
|
||||||
if (message == null || trigger == null || trigger.getType() == null) {
|
|
||||||
log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据触发器类型查找对应的匹配器
|
|
||||||
IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType());
|
|
||||||
if (triggerType == null) {
|
|
||||||
log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
IotSceneRuleTriggerMatcher matcher = matcherMap.get(triggerType);
|
|
||||||
if (matcher == null) {
|
|
||||||
log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return matcher.isMatched(message, trigger);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}",
|
|
||||||
message, trigger, matcher.getMatcherName(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有支持的触发器类型
|
|
||||||
*
|
|
||||||
* @return 支持的触发器类型列表
|
|
||||||
*/
|
|
||||||
public Set<IotSceneRuleTriggerTypeEnum> getSupportedTriggerTypes() {
|
|
||||||
return new HashSet<>(matcherMap.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定触发器类型的匹配器
|
|
||||||
*
|
|
||||||
* @param triggerType 触发器类型
|
|
||||||
* @return 匹配器实例,如果不存在则返回 null
|
|
||||||
*/
|
|
||||||
public IotSceneRuleTriggerMatcher getMatcher(IotSceneRuleTriggerTypeEnum triggerType) {
|
|
||||||
return matcherMap.get(triggerType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有匹配器的统计信息
|
|
||||||
*
|
|
||||||
* @return 统计信息映射表
|
|
||||||
*/
|
|
||||||
public Map<String, Object> getMatcherStatistics() {
|
|
||||||
Map<String, Object> statistics = new HashMap<>();
|
|
||||||
statistics.put("totalMatchers", allMatchers.size());
|
|
||||||
statistics.put("enabledMatchers", matcherMap.size());
|
|
||||||
statistics.put("supportedTriggerTypes", getSupportedTriggerTypes());
|
|
||||||
|
|
||||||
Map<String, Object> matcherDetails = new HashMap<>();
|
|
||||||
matcherMap.forEach((type, matcher) -> {
|
|
||||||
Map<String, Object> detail = new HashMap<>();
|
|
||||||
detail.put("matcherName", matcher.getMatcherName());
|
|
||||||
detail.put("priority", matcher.getPriority());
|
|
||||||
detail.put("enabled", matcher.isEnabled());
|
|
||||||
matcherDetails.put(type.name(), detail);
|
|
||||||
});
|
|
||||||
statistics.put("matcherDetails", matcherDetails);
|
|
||||||
|
|
||||||
return statistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,12 @@ import org.springframework.stereotype.Component;
|
|||||||
* @author HUIHUI
|
* @author HUIHUI
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
public class TimerTriggerMatcher extends AbstractIotSceneRuleMatcher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatcherType getMatcherType() {
|
||||||
|
return MatcherType.TRIGGER;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
|
||||||
@@ -26,13 +31,13 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
|||||||
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
|
||||||
// 1. 基础参数校验
|
// 1. 基础参数校验
|
||||||
if (!isBasicTriggerValid(trigger)) {
|
if (!isBasicTriggerValid(trigger)) {
|
||||||
logMatchFailure(message, trigger, "触发器基础参数无效");
|
logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查 CRON 表达式是否存在
|
// 2. 检查 CRON 表达式是否存在
|
||||||
if (StrUtil.isBlank(trigger.getCronExpression())) {
|
if (StrUtil.isBlank(trigger.getCronExpression())) {
|
||||||
logMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
|
logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,11 +46,11 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
|
|||||||
|
|
||||||
// 4. 可以添加 CRON 表达式格式验证
|
// 4. 可以添加 CRON 表达式格式验证
|
||||||
if (!isValidCronExpression(trigger.getCronExpression())) {
|
if (!isValidCronExpression(trigger.getCronExpression())) {
|
||||||
logMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
|
logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
logMatchSuccess(message, trigger);
|
logTriggerMatchSuccess(message, trigger);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,12 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
*/
|
*/
|
||||||
public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
|
public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
private IotSceneRuleTriggerMatcherManager matcherManager;
|
private IotSceneRuleMatcherManager matcherManager;
|
||||||
private List<IotSceneRuleTriggerMatcher> matchers;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
// 创建所有匹配器实例
|
// 创建所有匹配器实例
|
||||||
matchers = Arrays.asList(
|
List<IotSceneRuleMatcher> matchers = Arrays.asList(
|
||||||
new DeviceStateUpdateTriggerMatcher(),
|
new DeviceStateUpdateTriggerMatcher(),
|
||||||
new DevicePropertyPostTriggerMatcher(),
|
new DevicePropertyPostTriggerMatcher(),
|
||||||
new DeviceEventPostTriggerMatcher(),
|
new DeviceEventPostTriggerMatcher(),
|
||||||
@@ -35,7 +34,7 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 初始化匹配器管理器
|
// 初始化匹配器管理器
|
||||||
matcherManager = new IotSceneRuleTriggerMatcherManager(matchers);
|
matcherManager = new IotSceneRuleMatcherManager(matchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -197,4 +196,5 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
|
|||||||
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE));
|
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE));
|
||||||
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER));
|
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user