From 93aaffddfefad7ecc2039a2da448dcbe8cb7be96 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 15 Aug 2025 17:27:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E3=80=90IoT=20=E7=89=A9=E8=81=94=E7=BD=91?= =?UTF-8?q?=E3=80=91=E6=96=B0=E5=A2=9E=E5=9C=BA=E6=99=AF=E8=A7=84=E5=88=99?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=99=A8=E5=8C=B9=E9=85=8D=E5=AD=90=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E7=AD=96=E7=95=A5=E6=8E=A5=E5=8F=A3=E5=92=8C=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/IotSceneRuleConditionLevelEnum.java | 74 +++++ .../rule/scene/IotSceneRuleServiceImpl.java | 126 +------- ....java => AbstractIotSceneRuleMatcher.java} | 99 ++++-- .../matcher/CurrentTimeConditionMatcher.java | 177 +++++++++++ .../DeviceEventPostTriggerMatcher.java | 19 +- .../DevicePropertyConditionMatcher.java | 80 +++++ .../DevicePropertyPostTriggerMatcher.java | 23 +- .../DeviceServiceInvokeTriggerMatcher.java | 15 +- .../matcher/DeviceStateConditionMatcher.java | 73 +++++ .../DeviceStateUpdateTriggerMatcher.java | 21 +- .../scene/matcher/IotSceneRuleMatcher.java | 123 ++++++++ .../matcher/IotSceneRuleMatcherManager.java | 296 ++++++++++++++++++ .../matcher/IotSceneRuleTriggerMatcher.java | 63 ---- .../IotSceneRuleTriggerMatcherManager.java | 151 --------- .../scene/matcher/TimerTriggerMatcher.java | 15 +- .../IotSceneRuleTriggerMatcherTest.java | 8 +- 16 files changed, 965 insertions(+), 398 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionLevelEnum.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/{AbstractIotSceneRuleTriggerMatcher.java => AbstractIotSceneRuleMatcher.java} (64%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherManager.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionLevelEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionLevelEnum.java new file mode 100644 index 0000000000..c83b72c1f5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionLevelEnum.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IoT 场景规则条件层级枚举 + *

+ * 用于区分主条件(触发器级别)和子条件(条件分组级别) + * + * @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; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java index 295a796d4c..fc3e96798f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java @@ -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.IotSceneRuleSaveReqVO; 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.product.IotProductDO; 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.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.framework.job.core.IotSchedulerManager; 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.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 lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -67,7 +65,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { @Resource private IotDeviceService deviceService; @Resource - private IotSceneRuleTriggerMatcherManager triggerMatcherManager; + private IotSceneRuleMatcherManager matcherManager; @Resource private List sceneRuleActions; @@ -285,7 +283,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) { try { // 2. 检查触发器的条件分组 - return triggerMatcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule); + return matcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule); } catch (Exception e) { log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}", sceneRule.getId(), trigger.getType(), message, e); @@ -333,7 +331,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { } /** - * 基于消息,判断触发器的条件是否匹配 + * 基于消息,判断触发器的子条件是否匹配 * * @param message 设备消息 * @param condition 触发条件 @@ -344,21 +342,8 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) { try { - // 1. 根据条件类型进行匹配 - if (IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType().equals(condition.getType())) { - // 设备状态条件匹配 - 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; - } + // 使用重构后的条件匹配管理器进行匹配 + return matcherManager.isConditionMatched(message, condition); } catch (Exception e) { log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]", 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 springExpressionVariables = MapUtil.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 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 @芋艿:【可优化】可以考虑增加下单测,边界太多了。 /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleMatcher.java similarity index 64% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleTriggerMatcher.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleMatcher.java index 2314bbc4f6..a77854ef96 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleMatcher.java @@ -15,14 +15,14 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; /** - * IoT 场景规则触发器匹配器抽象基类 + * IoT 场景规则匹配器抽象基类 *

- * 提供通用的条件评估逻辑和工具方法 + * 提供通用的条件评估逻辑和工具方法,支持触发器和条件两种匹配类型 * * @author HUIHUI */ @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 触发器配置 * @return 是否有效 */ - protected boolean isOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) { + protected boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) { 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); } - /** - * 记录匹配成功日志 - * - * @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); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java new file mode 100644 index 0000000000..df11c666da --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java @@ -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; + +/** + * 当前时间条件匹配器 + *

+ * 处理时间相关的子条件匹配逻辑 + * + * @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; // 较低优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceEventPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceEventPostTriggerMatcher.java index 1ee0cdda81..3c832f6553 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceEventPostTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceEventPostTriggerMatcher.java @@ -16,13 +16,18 @@ import org.springframework.stereotype.Component; * @author HUIHUI */ @Component -public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { +public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleMatcher { /** * 设备事件上报消息方法 */ private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod(); + @Override + public MatcherType getMatcherType() { + return MatcherType.TRIGGER; + } + @Override public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; @@ -32,20 +37,20 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { // 1. 基础参数校验 if (!isBasicTriggerValid(trigger)) { - logMatchFailure(message, trigger, "触发器基础参数无效"); + logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); return false; } // 2. 检查消息方法是否匹配 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; } // 3. 检查标识符是否匹配 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); return false; } @@ -54,18 +59,18 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { Object eventData = message.getData(); if (eventData == null) { - logMatchFailure(message, trigger, "消息中事件数据为空"); + logTriggerMatchFailure(message, trigger, "消息中事件数据为空"); return false; } boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue()); if (!matched) { - logMatchFailure(message, trigger, "事件数据条件不匹配"); + logTriggerMatchFailure(message, trigger, "事件数据条件不匹配"); return false; } } - logMatchSuccess(message, trigger); + logTriggerMatchSuccess(message, trigger); return true; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java new file mode 100644 index 0000000000..70c789edcd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java @@ -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; + +/** + * 设备属性条件匹配器 + *

+ * 处理设备属性相关的子条件匹配逻辑 + * + * @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; // 中等优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java index 7ed00519ec..0953453ed2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java @@ -15,13 +15,18 @@ import org.springframework.stereotype.Component; * @author HUIHUI */ @Component -public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { +public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleMatcher { /** * 设备属性上报消息方法 */ private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(); + @Override + public MatcherType getMatcherType() { + return MatcherType.TRIGGER; + } + @Override public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; @@ -31,33 +36,33 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { // 1. 基础参数校验 if (!isBasicTriggerValid(trigger)) { - logMatchFailure(message, trigger, "触发器基础参数无效"); + logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); return false; } // 2. 检查消息方法是否匹配 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; } // 3. 检查标识符是否匹配 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); return false; } // 4. 检查操作符和值是否有效 - if (!isOperatorAndValueValid(trigger)) { - logMatchFailure(message, trigger, "操作符或值无效"); + if (!isTriggerOperatorAndValueValid(trigger)) { + logTriggerMatchFailure(message, trigger, "操作符或值无效"); return false; } // 5. 获取属性值 Object propertyValue = message.getData(); if (propertyValue == null) { - logMatchFailure(message, trigger, "消息中属性值为空"); + logTriggerMatchFailure(message, trigger, "消息中属性值为空"); return false; } @@ -65,9 +70,9 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); if (matched) { - logMatchSuccess(message, trigger); + logTriggerMatchSuccess(message, trigger); } else { - logMatchFailure(message, trigger, "属性值条件不匹配"); + logTriggerMatchFailure(message, trigger, "属性值条件不匹配"); } return matched; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceServiceInvokeTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceServiceInvokeTriggerMatcher.java index 996fe173f9..c2b7e4ef82 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceServiceInvokeTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceServiceInvokeTriggerMatcher.java @@ -15,13 +15,18 @@ import org.springframework.stereotype.Component; * @author HUIHUI */ @Component -public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { +public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleMatcher { /** * 设备服务调用消息方法 */ private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(); + @Override + public MatcherType getMatcherType() { + return MatcherType.TRIGGER; + } + @Override public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; @@ -31,27 +36,27 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { // 1. 基础参数校验 if (!isBasicTriggerValid(trigger)) { - logMatchFailure(message, trigger, "触发器基础参数无效"); + logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); return false; } // 2. 检查消息方法是否匹配 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; } // 3. 检查标识符是否匹配 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); return false; } // 4. 对于服务调用触发器,通常只需要匹配服务标识符即可 // 不需要检查操作符和值,因为服务调用本身就是触发条件 - logMatchSuccess(message, trigger); + logTriggerMatchSuccess(message, trigger); return true; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java new file mode 100644 index 0000000000..aa3acab2a1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java @@ -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; + +/** + * 设备状态条件匹配器 + *

+ * 处理设备状态相关的子条件匹配逻辑 + * + * @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; // 中等优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java index aec372c51b..a505e0d393 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java @@ -14,13 +14,18 @@ import org.springframework.stereotype.Component; * @author HUIHUI */ @Component -public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { +public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleMatcher { /** * 设备状态更新消息方法 */ private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(); + @Override + public MatcherType getMatcherType() { + return MatcherType.TRIGGER; + } + @Override public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; @@ -30,26 +35,26 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { // 1. 基础参数校验 if (!isBasicTriggerValid(trigger)) { - logMatchFailure(message, trigger, "触发器基础参数无效"); + logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); return false; } // 2. 检查消息方法是否匹配 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; } // 3. 检查操作符和值是否有效 - if (!isOperatorAndValueValid(trigger)) { - logMatchFailure(message, trigger, "操作符或值无效"); + if (!isTriggerOperatorAndValueValid(trigger)) { + logTriggerMatchFailure(message, trigger, "操作符或值无效"); return false; } // 4. 获取设备状态值 Object stateValue = message.getData(); if (stateValue == null) { - logMatchFailure(message, trigger, "消息中设备状态值为空"); + logTriggerMatchFailure(message, trigger, "消息中设备状态值为空"); return false; } @@ -57,9 +62,9 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue()); if (matched) { - logMatchSuccess(message, trigger); + logTriggerMatchSuccess(message, trigger); } else { - logMatchFailure(message, trigger, "状态值条件不匹配"); + logTriggerMatchFailure(message, trigger, "状态值条件不匹配"); } return matched; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java new file mode 100644 index 0000000000..5e5c35baf5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java @@ -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 场景规则匹配器统一接口 + *

+ * 支持触发器匹配和条件匹配两种类型,遵循策略模式设计 + *

+ * 匹配器类型说明: + * - 触发器匹配器:用于匹配主触发条件(如设备消息类型、定时器等) + * - 条件匹配器:用于匹配子条件(如设备状态、属性值、时间条件等) + * + * @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("条件匹配方法仅支持条件匹配器"); + } + + /** + * 获取匹配优先级(数值越小优先级越高) + *

+ * 用于在多个匹配器支持同一类型时确定优先级 + * + * @return 优先级数值 + */ + default int getPriority() { + return 100; + } + + /** + * 获取匹配器名称,用于日志和调试 + * + * @return 匹配器名称 + */ + default String getMatcherName() { + return this.getClass().getSimpleName(); + } + + /** + * 是否启用该匹配器 + *

+ * 可用于动态开关某些匹配器 + * + * @return 是否启用 + */ + default boolean isEnabled() { + return true; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java new file mode 100644 index 0000000000..9852e4acdb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java @@ -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 场景规则匹配器统一管理器 + *

+ * 负责管理所有匹配器(触发器匹配器和条件匹配器),并提供统一的匹配入口 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotSceneRuleMatcherManager { + + /** + * 触发器匹配器映射表 + * Key: 触发器类型枚举 + * Value: 对应的匹配器实例 + */ + private final Map triggerMatcherMap; + + /** + * 条件匹配器映射表 + * Key: 条件类型枚举 + * Value: 对应的匹配器实例 + */ + private final Map conditionMatcherMap; + + /** + * 所有匹配器列表(按优先级排序) + */ + private final List allMatchers; + + public IotSceneRuleMatcherManager(List 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 triggerMatchers = this.allMatchers.stream() + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.TRIGGER) + .toList(); + + List 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 getSupportedTriggerTypes() { + return new HashSet<>(triggerMatcherMap.keySet()); + } + + /** + * 获取所有支持的条件类型 + * + * @return 支持的条件类型列表 + */ + public Set 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 getMatchersByLevel(IotSceneRuleConditionLevelEnum level) { + return allMatchers.stream() + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION) + .filter(matcher -> matcher.getSupportedConditionLevel() == level) + .collect(Collectors.toList()); + } + + /** + * 获取所有匹配器的统计信息 + * + * @return 统计信息映射表 + */ + public Map getMatcherStatistics() { + Map 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 levelStats = allMatchers.stream() + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION) + .collect(Collectors.groupingBy( + IotSceneRuleMatcher::getSupportedConditionLevel, + Collectors.counting() + )); + statistics.put("conditionLevelStatistics", levelStats); + + // 触发器匹配器详情 + Map triggerMatcherDetails = new HashMap<>(); + triggerMatcherMap.forEach((type, matcher) -> { + Map 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 conditionMatcherDetails = new HashMap<>(); + conditionMatcherMap.forEach((type, matcher) -> { + Map 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; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcher.java deleted file mode 100644 index bf111a2663..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcher.java +++ /dev/null @@ -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 场景规则触发器匹配策略接口 - *

- * 用于实现不同类型触发器的匹配逻辑,遵循策略模式设计 - * - * @author HUIHUI - */ -public interface IotSceneRuleTriggerMatcher { - - /** - * 获取支持的触发器类型 - * - * @return 触发器类型枚举 - */ - IotSceneRuleTriggerTypeEnum getSupportedTriggerType(); - - /** - * 检查触发器是否匹配消息 - * - * @param message 设备消息 - * @param trigger 触发器配置 - * @return 是否匹配 - */ - boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger); - - /** - * 获取匹配优先级(数值越小优先级越高) - *

- * 用于在多个匹配器支持同一触发器类型时确定优先级 - * - * @return 优先级数值 - */ - default int getPriority() { - return 100; - } - - /** - * 获取匹配器名称,用于日志和调试 - * - * @return 匹配器名称 - */ - default String getMatcherName() { - return this.getClass().getSimpleName(); - } - - /** - * 是否启用该匹配器 - *

- * 可用于动态开关某些匹配器 - * - * @return 是否启用 - */ - default boolean isEnabled() { - return true; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherManager.java deleted file mode 100644 index 6e6c383a95..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherManager.java +++ /dev/null @@ -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 场景规则触发器匹配管理器 - *

- * 负责管理所有触发器匹配器,并提供统一的匹配入口 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class IotSceneRuleTriggerMatcherManager { - - /** - * 触发器匹配器映射表 - * Key: 触发器类型枚举 - * Value: 对应的匹配器实例 - */ - private final Map matcherMap; - - /** - * 所有匹配器列表(按优先级排序) - */ - private final List allMatchers; - - public IotSceneRuleTriggerMatcherManager(List 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 getSupportedTriggerTypes() { - return new HashSet<>(matcherMap.keySet()); - } - - /** - * 获取指定触发器类型的匹配器 - * - * @param triggerType 触发器类型 - * @return 匹配器实例,如果不存在则返回 null - */ - public IotSceneRuleTriggerMatcher getMatcher(IotSceneRuleTriggerTypeEnum triggerType) { - return matcherMap.get(triggerType); - } - - /** - * 获取所有匹配器的统计信息 - * - * @return 统计信息映射表 - */ - public Map getMatcherStatistics() { - Map statistics = new HashMap<>(); - statistics.put("totalMatchers", allMatchers.size()); - statistics.put("enabledMatchers", matcherMap.size()); - statistics.put("supportedTriggerTypes", getSupportedTriggerTypes()); - - Map matcherDetails = new HashMap<>(); - matcherMap.forEach((type, matcher) -> { - Map 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; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java index c37a10a13f..a5d536cb8f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java @@ -15,7 +15,12 @@ import org.springframework.stereotype.Component; * @author HUIHUI */ @Component -public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { +public class TimerTriggerMatcher extends AbstractIotSceneRuleMatcher { + + @Override + public MatcherType getMatcherType() { + return MatcherType.TRIGGER; + } @Override public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { @@ -26,13 +31,13 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { // 1. 基础参数校验 if (!isBasicTriggerValid(trigger)) { - logMatchFailure(message, trigger, "触发器基础参数无效"); + logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); return false; } // 2. 检查 CRON 表达式是否存在 if (StrUtil.isBlank(trigger.getCronExpression())) { - logMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); + logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); return false; } @@ -41,11 +46,11 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { // 4. 可以添加 CRON 表达式格式验证 if (!isValidCronExpression(trigger.getCronExpression())) { - logMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); + logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); return false; } - logMatchSuccess(message, trigger); + logTriggerMatchSuccess(message, trigger); return true; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java index 9903e8cc2f..7483182566 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java @@ -20,13 +20,12 @@ import static org.junit.jupiter.api.Assertions.*; */ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { - private IotSceneRuleTriggerMatcherManager matcherManager; - private List matchers; + private IotSceneRuleMatcherManager matcherManager; @BeforeEach void setUp() { // 创建所有匹配器实例 - matchers = Arrays.asList( + List matchers = Arrays.asList( new DeviceStateUpdateTriggerMatcher(), new DevicePropertyPostTriggerMatcher(), new DeviceEventPostTriggerMatcher(), @@ -35,7 +34,7 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { ); // 初始化匹配器管理器 - matcherManager = new IotSceneRuleTriggerMatcherManager(matchers); + matcherManager = new IotSceneRuleMatcherManager(matchers); } @Test @@ -197,4 +196,5 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE)); assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER)); } + }