Commit fb35807ebf952001e661cb0283b6fcaeb0222991
1 parent
34f1a2ed
perf:【IoT 物联网】场景联动优化 review 提到的逻辑
Showing
13 changed files
with
341 additions
and
297 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleTriggerTypeEnum.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.enums.rule; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ArrayUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.core.ArrayValuable; |
| 4 | 5 | import lombok.Getter; |
| 5 | 6 | import lombok.RequiredArgsConstructor; |
| ... | ... | @@ -60,7 +61,9 @@ public enum IotSceneRuleTriggerTypeEnum implements ArrayValuable<Integer> { |
| 60 | 61 | return ARRAYS; |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | - // TODO @puhui999:可以参考下别的枚举哈,方法名,和实现都可以更简洁;of(String type) { firstMatch | |
| 64 | + public static IotSceneRuleTriggerTypeEnum typeOf(Integer type) { | |
| 65 | + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); | |
| 66 | + } | |
| 64 | 67 | /** |
| 65 | 68 | * 根据类型值查找触发器类型枚举 |
| 66 | 69 | * | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java
| ... | ... | @@ -26,16 +26,6 @@ public interface IotSceneRuleMatcher { |
| 26 | 26 | return 100; |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | - // TODO @puhui999:如果目前没自定义,体感可以删除哈; | |
| 30 | - /** | |
| 31 | - * 获取匹配器名称,用于日志和调试 | |
| 32 | - * | |
| 33 | - * @return 匹配器名称 | |
| 34 | - */ | |
| 35 | - default String getMatcherName() { | |
| 36 | - return this.getClass().getSimpleName(); | |
| 37 | - } | |
| 38 | - | |
| 39 | 29 | /** |
| 40 | 30 | * 是否启用该匹配器 |
| 41 | 31 | * <p> | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.text.CharPool; | |
| 3 | 4 | import cn.hutool.core.util.NumberUtil; |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 7 | +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | |
| 5 | 8 | import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; |
| 6 | 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 7 | 10 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| ... | ... | @@ -40,44 +43,99 @@ public final class IotSceneRuleMatcherHelper { |
| 40 | 43 | * @param paramValue 参数值(来自条件配置) |
| 41 | 44 | * @return 是否匹配 |
| 42 | 45 | */ |
| 43 | - @SuppressWarnings("DataFlowIssue") | |
| 44 | 46 | public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) { |
| 45 | 47 | try { |
| 46 | 48 | // 1. 校验操作符是否合法 |
| 47 | 49 | IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator); |
| 48 | 50 | if (operatorEnum == null) { |
| 49 | - log.warn("[evaluateCondition][存在错误的操作符({})]", operator); | |
| 51 | + log.warn("[evaluateCondition][operator({}) 操作符无效]", operator); | |
| 50 | 52 | return false; |
| 51 | 53 | } |
| 52 | 54 | |
| 53 | 55 | // 2. 构建 Spring 表达式变量 |
| 54 | - Map<String, Object> springExpressionVariables = new HashMap<>(); | |
| 55 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue); | |
| 56 | - | |
| 57 | - // 处理参数值 | |
| 58 | - if (StrUtil.isNotBlank(paramValue)) { | |
| 59 | - // 处理多值情况(如 IN、BETWEEN 操作符) | |
| 60 | - // TODO @puhui999:使用这个,会不会有问题?例如说:string 恰好有 , 分隔? | |
| 61 | - if (paramValue.contains(",")) { | |
| 62 | - List<String> paramValues = StrUtil.split(paramValue, ','); | |
| 63 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, | |
| 64 | - convertList(paramValues, NumberUtil::parseDouble)); | |
| 65 | - } else { | |
| 66 | - // 处理单值情况 | |
| 67 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, | |
| 68 | - NumberUtil.parseDouble(paramValue)); | |
| 69 | - } | |
| 70 | - } | |
| 56 | + return evaluateConditionWithOperatorEnum(sourceValue, operatorEnum, paramValue); | |
| 57 | + } catch (Exception e) { | |
| 58 | + log.error("[evaluateCondition][sourceValue({}) operator({}) paramValue({}) 条件评估异常]", | |
| 59 | + sourceValue, operator, paramValue, e); | |
| 60 | + return false; | |
| 61 | + } | |
| 62 | + } | |
| 71 | 63 | |
| 72 | - // 3. 计算 Spring 表达式 | |
| 64 | + /** | |
| 65 | + * 使用操作符枚举评估条件是否匹配 | |
| 66 | + * | |
| 67 | + * @param sourceValue 源值(来自消息) | |
| 68 | + * @param operatorEnum 操作符枚举 | |
| 69 | + * @param paramValue 参数值(来自条件配置) | |
| 70 | + * @return 是否匹配 | |
| 71 | + */ | |
| 72 | + @SuppressWarnings("DataFlowIssue") | |
| 73 | + public static boolean evaluateConditionWithOperatorEnum(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) { | |
| 74 | + try { | |
| 75 | + // 1. 构建 Spring 表达式变量 | |
| 76 | + Map<String, Object> springExpressionVariables = buildSpringExpressionVariables(sourceValue, operatorEnum, paramValue); | |
| 77 | + | |
| 78 | + // 2. 计算 Spring 表达式 | |
| 73 | 79 | return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables); |
| 74 | 80 | } catch (Exception e) { |
| 75 | - log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}", | |
| 76 | - sourceValue, operator, paramValue, e); | |
| 81 | + log.error("[evaluateConditionWithOperatorEnum][sourceValue({}) operatorEnum({}) paramValue({}) 条件评估异常]", | |
| 82 | + sourceValue, operatorEnum, paramValue, e); | |
| 77 | 83 | return false; |
| 78 | 84 | } |
| 79 | 85 | } |
| 80 | 86 | |
| 87 | + /** | |
| 88 | + * 构建 Spring 表达式变量 | |
| 89 | + */ | |
| 90 | + private static Map<String, Object> buildSpringExpressionVariables(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) { | |
| 91 | + Map<String, Object> springExpressionVariables = new HashMap<>(); | |
| 92 | + | |
| 93 | + // 设置源值 | |
| 94 | + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue); | |
| 95 | + | |
| 96 | + // 处理参数值 | |
| 97 | + if (StrUtil.isNotBlank(paramValue)) { | |
| 98 | + List<String> parameterValues = StrUtil.splitTrim(paramValue, CharPool.COMMA); | |
| 99 | + | |
| 100 | + // 设置原始参数值 | |
| 101 | + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, paramValue); | |
| 102 | + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); | |
| 103 | + | |
| 104 | + // 特殊处理:解决数字比较问题 | |
| 105 | + // Spring 表达式基于 compareTo 方法,对数字的比较存在问题,需要转换为数字类型 | |
| 106 | + if (isNumericComparisonOperator(operatorEnum) && isNumericComparison(sourceValue, parameterValues)) { | |
| 107 | + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, | |
| 108 | + NumberUtil.parseDouble(String.valueOf(sourceValue))); | |
| 109 | + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, | |
| 110 | + NumberUtil.parseDouble(paramValue)); | |
| 111 | + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, | |
| 112 | + convertList(parameterValues, NumberUtil::parseDouble)); | |
| 113 | + } | |
| 114 | + } | |
| 115 | + | |
| 116 | + return springExpressionVariables; | |
| 117 | + } | |
| 118 | + | |
| 119 | + /** | |
| 120 | + * 判断是否为数字比较操作符 | |
| 121 | + */ | |
| 122 | + private static boolean isNumericComparisonOperator(IotSceneRuleConditionOperatorEnum operatorEnum) { | |
| 123 | + return ObjectUtils.equalsAny(operatorEnum, | |
| 124 | + IotSceneRuleConditionOperatorEnum.BETWEEN, | |
| 125 | + IotSceneRuleConditionOperatorEnum.NOT_BETWEEN, | |
| 126 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN, | |
| 127 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS, | |
| 128 | + IotSceneRuleConditionOperatorEnum.LESS_THAN, | |
| 129 | + IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS); | |
| 130 | + } | |
| 131 | + | |
| 132 | + /** | |
| 133 | + * 判断是否为数字比较场景 | |
| 134 | + */ | |
| 135 | + private static boolean isNumericComparison(Object sourceValue, List<String> parameterValues) { | |
| 136 | + return NumberUtil.isNumber(String.valueOf(sourceValue)) && NumberUtils.isAllNumber(parameterValues); | |
| 137 | + } | |
| 138 | + | |
| 81 | 139 | // ========== 【触发器】相关工具方法 ========== |
| 82 | 140 | |
| 83 | 141 | /** |
| ... | ... | @@ -103,24 +161,22 @@ public final class IotSceneRuleMatcherHelper { |
| 103 | 161 | /** |
| 104 | 162 | * 记录触发器匹配成功日志 |
| 105 | 163 | * |
| 106 | - * @param matcherName 匹配器名称 | |
| 107 | 164 | * @param message 设备消息 |
| 108 | 165 | * @param trigger 触发器配置 |
| 109 | 166 | */ |
| 110 | - public static void logTriggerMatchSuccess(String matcherName, IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { | |
| 111 | - log.debug("[{}][消息({}) 匹配触发器({}) 成功]", matcherName, message.getRequestId(), trigger.getType()); | |
| 167 | + public static void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { | |
| 168 | + log.debug("[isMatched][message({}) trigger({}) 匹配触发器成功]", message.getRequestId(), trigger.getType()); | |
| 112 | 169 | } |
| 113 | 170 | |
| 114 | 171 | /** |
| 115 | 172 | * 记录触发器匹配失败日志 |
| 116 | 173 | * |
| 117 | - * @param matcherName 匹配器名称 | |
| 118 | 174 | * @param message 设备消息 |
| 119 | 175 | * @param trigger 触发器配置 |
| 120 | 176 | * @param reason 失败原因 |
| 121 | 177 | */ |
| 122 | - public static void logTriggerMatchFailure(String matcherName, IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) { | |
| 123 | - log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", matcherName, message.getRequestId(), trigger.getType(), reason); | |
| 178 | + public static void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) { | |
| 179 | + log.debug("[isMatched][message({}) trigger({}) reason({}) 匹配触发器失败]", message.getRequestId(), trigger.getType(), reason); | |
| 124 | 180 | } |
| 125 | 181 | |
| 126 | 182 | // ========== 【条件】相关工具方法 ========== |
| ... | ... | @@ -148,24 +204,22 @@ public final class IotSceneRuleMatcherHelper { |
| 148 | 204 | /** |
| 149 | 205 | * 记录条件匹配成功日志 |
| 150 | 206 | * |
| 151 | - * @param matcherName 匹配器名称 | |
| 152 | 207 | * @param message 设备消息 |
| 153 | 208 | * @param condition 触发条件 |
| 154 | 209 | */ |
| 155 | - public static void logConditionMatchSuccess(String matcherName, IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { | |
| 156 | - log.debug("[{}][消息({}) 匹配条件({}) 成功]", matcherName, message.getRequestId(), condition.getType()); | |
| 210 | + public static void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { | |
| 211 | + log.debug("[isMatched][message({}) condition({}) 匹配条件成功]", message.getRequestId(), condition.getType()); | |
| 157 | 212 | } |
| 158 | 213 | |
| 159 | 214 | /** |
| 160 | 215 | * 记录条件匹配失败日志 |
| 161 | 216 | * |
| 162 | - * @param matcherName 匹配器名称 | |
| 163 | 217 | * @param message 设备消息 |
| 164 | 218 | * @param condition 触发条件 |
| 165 | 219 | * @param reason 失败原因 |
| 166 | 220 | */ |
| 167 | - public static void logConditionMatchFailure(String matcherName, IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) { | |
| 168 | - log.debug("[{}][消息({}) 匹配条件({}) 失败: {}]", matcherName, message.getRequestId(), condition.getType(), reason); | |
| 221 | + public static void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) { | |
| 222 | + log.debug("[isMatched][message({}) condition({}) reason({}) 匹配条件失败]", message.getRequestId(), condition.getType(), reason); | |
| 169 | 223 | } |
| 170 | 224 | |
| 171 | 225 | // ========== 【通用】工具方法 ========== | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java
| ... | ... | @@ -14,7 +14,7 @@ import java.util.*; |
| 14 | 14 | import java.util.function.Function; |
| 15 | 15 | import java.util.stream.Collectors; |
| 16 | 16 | |
| 17 | -import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum; | |
| 17 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 18 | 18 | |
| 19 | 19 | /** |
| 20 | 20 | * IoT 场景规则匹配器统一管理器 |
| ... | ... | @@ -72,42 +72,34 @@ public class IotSceneRuleMatcherManager { |
| 72 | 72 | .toList(); |
| 73 | 73 | |
| 74 | 74 | // 构建触发器匹配器映射表 |
| 75 | - // TODO @puhui999:convertMap() | |
| 76 | - this.triggerMatchers = triggerMatchers.stream() | |
| 77 | - .collect(Collectors.toMap( | |
| 78 | - IotSceneRuleTriggerMatcher::getSupportedTriggerType, | |
| 79 | - Function.identity(), | |
| 80 | - (existing, replacement) -> { | |
| 81 | - log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]", | |
| 82 | - existing.getSupportedTriggerType(), | |
| 83 | - existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName()); | |
| 84 | - return existing.getPriority() <= replacement.getPriority() ? existing : replacement; | |
| 85 | - }, | |
| 86 | - LinkedHashMap::new | |
| 87 | - )); | |
| 75 | + this.triggerMatchers = convertMap(triggerMatchers, IotSceneRuleTriggerMatcher::getSupportedTriggerType, | |
| 76 | + Function.identity(), | |
| 77 | + (existing, replacement) -> { | |
| 78 | + log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]", | |
| 79 | + existing.getSupportedTriggerType(), | |
| 80 | + existing.getPriority() <= replacement.getPriority() ? | |
| 81 | + existing.getSupportedTriggerType() : replacement.getSupportedTriggerType()); | |
| 82 | + return existing.getPriority() <= replacement.getPriority() ? existing : replacement; | |
| 83 | + }, LinkedHashMap::new); | |
| 88 | 84 | // 构建条件匹配器映射表 |
| 89 | - this.conditionMatchers = conditionMatchers.stream() | |
| 90 | - .collect(Collectors.toMap( | |
| 91 | - IotSceneRuleConditionMatcher::getSupportedConditionType, | |
| 92 | - Function.identity(), | |
| 93 | - (existing, replacement) -> { | |
| 94 | - log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]", | |
| 95 | - existing.getSupportedConditionType(), | |
| 96 | - existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName()); | |
| 97 | - return existing.getPriority() <= replacement.getPriority() ? existing : replacement; | |
| 98 | - }, | |
| 99 | - LinkedHashMap::new | |
| 100 | - )); | |
| 85 | + this.conditionMatchers = convertMap(conditionMatchers, IotSceneRuleConditionMatcher::getSupportedConditionType, | |
| 86 | + Function.identity(), | |
| 87 | + (existing, replacement) -> { | |
| 88 | + log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]", | |
| 89 | + existing.getSupportedConditionType(), | |
| 90 | + existing.getPriority() <= replacement.getPriority() ? | |
| 91 | + existing.getSupportedConditionType() : replacement.getSupportedConditionType()); | |
| 92 | + return existing.getPriority() <= replacement.getPriority() ? existing : replacement; | |
| 93 | + }, | |
| 94 | + LinkedHashMap::new); | |
| 101 | 95 | |
| 102 | 96 | // 日志输出初始化信息 |
| 103 | - log.info("[IotSceneRuleMatcherManager][初始化完成,共加载 {} 个匹配器,其中触发器匹配器 {} 个,条件匹配器 {} 个]", | |
| 97 | + log.info("[IotSceneRuleMatcherManager][初始化完成,共加载({})个匹配器,其中触发器匹配器({})个,条件匹配器({})个]", | |
| 104 | 98 | this.allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size()); |
| 105 | 99 | this.triggerMatchers.forEach((type, matcher) -> |
| 106 | - log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}", | |
| 107 | - type, matcher.getMatcherName(), matcher.getPriority())); | |
| 100 | + log.info("[IotSceneRuleMatcherManager][触发器匹配器类型: ({}), 优先级: ({})] ", type, matcher.getPriority())); | |
| 108 | 101 | this.conditionMatchers.forEach((type, matcher) -> |
| 109 | - log.info("[IotSceneRuleMatcherManager][条件匹配器] 类型: {}, 匹配器: {}, 优先级: {}", | |
| 110 | - type, matcher.getMatcherName(), matcher.getPriority())); | |
| 102 | + log.info("[IotSceneRuleMatcherManager][条件匹配器类型: ({}), 优先级: ({})]", type, matcher.getPriority())); | |
| 111 | 103 | } |
| 112 | 104 | |
| 113 | 105 | /** |
| ... | ... | @@ -118,27 +110,25 @@ public class IotSceneRuleMatcherManager { |
| 118 | 110 | * @return 是否匹配 |
| 119 | 111 | */ |
| 120 | 112 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 121 | - // TODO @puhui999:日志优化下;claude 打出来的日志风格,和项目有点不一样哈; | |
| 122 | 113 | if (message == null || trigger == null || trigger.getType() == null) { |
| 123 | - log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger); | |
| 114 | + log.debug("[isMatched][message({}) trigger({}) 参数无效]", message, trigger); | |
| 124 | 115 | return false; |
| 125 | 116 | } |
| 126 | - IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType()); | |
| 117 | + IotSceneRuleTriggerTypeEnum triggerType = IotSceneRuleTriggerTypeEnum.typeOf(trigger.getType()); | |
| 127 | 118 | if (triggerType == null) { |
| 128 | - log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType()); | |
| 119 | + log.warn("[isMatched][triggerType({}) 未知的触发器类型]", trigger.getType()); | |
| 129 | 120 | return false; |
| 130 | 121 | } |
| 131 | 122 | IotSceneRuleTriggerMatcher matcher = triggerMatchers.get(triggerType); |
| 132 | 123 | if (matcher == null) { |
| 133 | - log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType); | |
| 124 | + log.warn("[isMatched][triggerType({}) 没有对应的匹配器]", triggerType); | |
| 134 | 125 | return false; |
| 135 | 126 | } |
| 136 | 127 | |
| 137 | 128 | try { |
| 138 | 129 | return matcher.isMatched(message, trigger); |
| 139 | 130 | } catch (Exception e) { |
| 140 | - log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}", | |
| 141 | - message, trigger, matcher.getMatcherName(), e); | |
| 131 | + log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}", message, trigger, e); | |
| 142 | 132 | return false; |
| 143 | 133 | } |
| 144 | 134 | } |
| ... | ... | @@ -152,28 +142,27 @@ public class IotSceneRuleMatcherManager { |
| 152 | 142 | */ |
| 153 | 143 | public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { |
| 154 | 144 | if (message == null || condition == null || condition.getType() == null) { |
| 155 | - log.debug("[isConditionMatched][参数无效] message: {}, condition: {}", message, condition); | |
| 145 | + log.debug("[isConditionMatched][message({}) condition({}) 参数无效]", message, condition); | |
| 156 | 146 | return false; |
| 157 | 147 | } |
| 158 | 148 | |
| 159 | 149 | // 根据条件类型查找对应的匹配器 |
| 160 | 150 | IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType()); |
| 161 | 151 | if (conditionType == null) { |
| 162 | - log.warn("[isConditionMatched][未知的条件类型: {}]", condition.getType()); | |
| 152 | + log.warn("[isConditionMatched][conditionType({}) 未知的条件类型]", condition.getType()); | |
| 163 | 153 | return false; |
| 164 | 154 | } |
| 165 | 155 | |
| 166 | 156 | IotSceneRuleConditionMatcher matcher = conditionMatchers.get(conditionType); |
| 167 | 157 | if (matcher == null) { |
| 168 | - log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType); | |
| 158 | + log.warn("[isConditionMatched][conditionType({}) 没有对应的匹配器]", conditionType); | |
| 169 | 159 | return false; |
| 170 | 160 | } |
| 171 | 161 | |
| 172 | 162 | try { |
| 173 | 163 | return matcher.isMatched(message, condition); |
| 174 | 164 | } catch (Exception e) { |
| 175 | - log.error("[isConditionMatched][条件匹配异常] message: {}, condition: {}, matcher: {}", | |
| 176 | - message, condition, matcher.getMatcherName(), e); | |
| 165 | + log.error("[isConditionMatched][message({}) condition({}) 条件匹配异常]", message, condition, e); | |
| 177 | 166 | return false; |
| 178 | 167 | } |
| 179 | 168 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcher.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.hutool.core.text.CharPool; | |
| 3 | 5 | import cn.hutool.core.util.StrUtil; |
| 4 | 6 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 5 | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | |
| 6 | 9 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; |
| 7 | 10 | import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; |
| 8 | 11 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -11,6 +14,7 @@ import org.springframework.stereotype.Component; |
| 11 | 14 | import java.time.LocalDateTime; |
| 12 | 15 | import java.time.LocalTime; |
| 13 | 16 | import java.time.format.DateTimeFormatter; |
| 17 | +import java.util.List; | |
| 14 | 18 | |
| 15 | 19 | /** |
| 16 | 20 | * 当前时间条件匹配器 |
| ... | ... | @@ -40,115 +44,175 @@ public class CurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher |
| 40 | 44 | |
| 41 | 45 | @Override |
| 42 | 46 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { |
| 43 | - // 1. 基础参数校验 | |
| 47 | + // 1.1 基础参数校验 | |
| 44 | 48 | if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { |
| 45 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效"); | |
| 49 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); | |
| 46 | 50 | return false; |
| 47 | 51 | } |
| 48 | 52 | |
| 49 | - // 2. 检查操作符和参数是否有效 | |
| 53 | + // 1.2 检查操作符和参数是否有效 | |
| 50 | 54 | if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { |
| 51 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效"); | |
| 55 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); | |
| 52 | 56 | return false; |
| 53 | 57 | } |
| 54 | 58 | |
| 55 | - // 3. 根据操作符类型进行不同的时间匹配 | |
| 56 | - LocalDateTime now = LocalDateTime.now(); | |
| 59 | + // 1.3 验证操作符是否为支持的时间操作符 | |
| 57 | 60 | String operator = condition.getOperator(); |
| 58 | - String param = condition.getParam(); | |
| 59 | - boolean matched; | |
| 61 | + IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator); | |
| 62 | + if (operatorEnum == null) { | |
| 63 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "无效的操作符: " + operator); | |
| 64 | + return false; | |
| 65 | + } | |
| 66 | + | |
| 67 | + if (!isTimeOperator(operatorEnum)) { | |
| 68 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "不支持的时间操作符: " + operator); | |
| 69 | + return false; | |
| 70 | + } | |
| 71 | + | |
| 72 | + // 2.1 执行时间匹配 | |
| 73 | + boolean matched = executeTimeMatching(operatorEnum, condition.getParam()); | |
| 74 | + | |
| 75 | + // 2.2 记录匹配结果 | |
| 76 | + if (matched) { | |
| 77 | + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); | |
| 78 | + } else { | |
| 79 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "时间条件不匹配"); | |
| 80 | + } | |
| 81 | + | |
| 82 | + return matched; | |
| 83 | + } | |
| 84 | + | |
| 85 | + /** | |
| 86 | + * 执行时间匹配逻辑 | |
| 87 | + * 直接实现时间条件匹配,不使用 Spring EL 表达式 | |
| 88 | + */ | |
| 89 | + private boolean executeTimeMatching(IotSceneRuleConditionOperatorEnum operatorEnum, String param) { | |
| 60 | 90 | try { |
| 61 | - if (operator.startsWith("date_time_")) { | |
| 62 | - // 日期时间匹配(时间戳) | |
| 63 | - matched = matchDateTime(now, operator, param); | |
| 64 | - } else if (operator.startsWith("time_")) { | |
| 65 | - // 当日时间匹配(HH:mm:ss) | |
| 66 | - matched = matchTime(now.toLocalTime(), operator, param); | |
| 67 | - } else { | |
| 68 | - // 其他操作符,使用通用条件评估器 | |
| 69 | - matched = IotSceneRuleMatcherHelper.evaluateCondition(now.toEpochSecond(java.time.ZoneOffset.of("+8")), operator, param); | |
| 70 | - } | |
| 91 | + LocalDateTime now = LocalDateTime.now(); | |
| 71 | 92 | |
| 72 | - if (matched) { | |
| 73 | - IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition); | |
| 93 | + if (isDateTimeOperator(operatorEnum)) { | |
| 94 | + // 日期时间匹配(时间戳) | |
| 95 | + long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8")); | |
| 96 | + return matchDateTime(currentTimestamp, operatorEnum, param); | |
| 74 | 97 | } else { |
| 75 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "时间条件不匹配"); | |
| 98 | + // 当日时间匹配(HH:mm:ss) | |
| 99 | + return matchTime(now.toLocalTime(), operatorEnum, param); | |
| 76 | 100 | } |
| 77 | 101 | } catch (Exception e) { |
| 78 | - log.error("[CurrentTimeConditionMatcher][时间条件匹配异常] operator: {}, param: {}", operator, param, e); | |
| 79 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "时间条件匹配异常: " + e.getMessage()); | |
| 80 | - matched = false; | |
| 102 | + log.error("[executeTimeMatching][operatorEnum({}) param({}) 时间匹配异常]", operatorEnum, param, e); | |
| 103 | + return false; | |
| 81 | 104 | } |
| 82 | - return matched; | |
| 105 | + } | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * 判断是否为日期时间操作符 | |
| 109 | + */ | |
| 110 | + private boolean isDateTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) { | |
| 111 | + return operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN || | |
| 112 | + operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN || | |
| 113 | + operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN; | |
| 114 | + } | |
| 115 | + | |
| 116 | + /** | |
| 117 | + * 判断是否为时间操作符 | |
| 118 | + */ | |
| 119 | + private boolean isTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) { | |
| 120 | + return operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN || | |
| 121 | + operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN || | |
| 122 | + operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_BETWEEN || | |
| 123 | + isDateTimeOperator(operatorEnum); | |
| 83 | 124 | } |
| 84 | 125 | |
| 85 | 126 | /** |
| 86 | 127 | * 匹配日期时间(时间戳) |
| 128 | + * 直接实现时间戳比较逻辑 | |
| 87 | 129 | */ |
| 88 | - private boolean matchDateTime(LocalDateTime now, String operator, String param) { | |
| 89 | - long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8")); | |
| 90 | - return IotSceneRuleMatcherHelper.evaluateCondition(currentTimestamp, operator.substring("date_time_".length()), param); | |
| 130 | + private boolean matchDateTime(long currentTimestamp, IotSceneRuleConditionOperatorEnum operatorEnum, String param) { | |
| 131 | + try { | |
| 132 | + long targetTimestamp = Long.parseLong(param); | |
| 133 | + return switch (operatorEnum) { | |
| 134 | + case DATE_TIME_GREATER_THAN -> currentTimestamp > targetTimestamp; | |
| 135 | + case DATE_TIME_LESS_THAN -> currentTimestamp < targetTimestamp; | |
| 136 | + case DATE_TIME_BETWEEN -> matchDateTimeBetween(currentTimestamp, param); | |
| 137 | + default -> { | |
| 138 | + log.warn("[matchDateTime][operatorEnum({}) 不支持的日期时间操作符]", operatorEnum); | |
| 139 | + yield false; | |
| 140 | + } | |
| 141 | + }; | |
| 142 | + } catch (Exception e) { | |
| 143 | + log.error("[matchDateTime][operatorEnum({}) param({}) 日期时间匹配异常]", operatorEnum, param, e); | |
| 144 | + return false; | |
| 145 | + } | |
| 146 | + } | |
| 147 | + | |
| 148 | + /** | |
| 149 | + * 匹配日期时间区间 | |
| 150 | + */ | |
| 151 | + private boolean matchDateTimeBetween(long currentTimestamp, String param) { | |
| 152 | + List<String> timestampRange = StrUtil.splitTrim(param, CharPool.COMMA); | |
| 153 | + if (timestampRange.size() != 2) { | |
| 154 | + log.warn("[matchDateTimeBetween][param({}) 时间戳区间参数格式错误]", param); | |
| 155 | + return false; | |
| 156 | + } | |
| 157 | + long startTimestamp = Long.parseLong(timestampRange.get(0).trim()); | |
| 158 | + long endTimestamp = Long.parseLong(timestampRange.get(1).trim()); | |
| 159 | + return currentTimestamp >= startTimestamp && currentTimestamp <= endTimestamp; | |
| 91 | 160 | } |
| 92 | 161 | |
| 93 | 162 | /** |
| 94 | 163 | * 匹配当日时间(HH:mm:ss) |
| 164 | + * 直接实现时间比较逻辑 | |
| 95 | 165 | */ |
| 96 | - private boolean matchTime(LocalTime currentTime, String operator, String param) { | |
| 166 | + private boolean matchTime(LocalTime currentTime, IotSceneRuleConditionOperatorEnum operatorEnum, String param) { | |
| 97 | 167 | try { |
| 98 | - String actualOperator = operator.substring("time_".length()); | |
| 99 | - | |
| 100 | - // TODO @puhui999:if return 简化; | |
| 101 | - if ("between".equals(actualOperator)) { | |
| 102 | - // 时间区间匹配 | |
| 103 | - String[] timeRange = param.split(","); | |
| 104 | - if (timeRange.length != 2) { | |
| 105 | - return false; | |
| 106 | - } | |
| 107 | - LocalTime startTime = parseTime(timeRange[0].trim()); | |
| 108 | - LocalTime endTime = parseTime(timeRange[1].trim()); | |
| 109 | - return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); | |
| 110 | - } else { | |
| 111 | - // 单个时间比较 | |
| 112 | - LocalTime targetTime = parseTime(param); | |
| 113 | - // TODO @puhui999:枚举类; | |
| 114 | - switch (actualOperator) { | |
| 115 | - case ">": | |
| 116 | - return currentTime.isAfter(targetTime); | |
| 117 | - case "<": | |
| 118 | - return currentTime.isBefore(targetTime); | |
| 119 | - case ">=": | |
| 120 | - return !currentTime.isBefore(targetTime); | |
| 121 | - case "<=": | |
| 122 | - return !currentTime.isAfter(targetTime); | |
| 123 | - case "=": | |
| 124 | - return currentTime.equals(targetTime); | |
| 125 | - default: | |
| 126 | - return false; | |
| 168 | + LocalTime targetTime = parseTime(param); | |
| 169 | + return switch (operatorEnum) { | |
| 170 | + case TIME_GREATER_THAN -> currentTime.isAfter(targetTime); | |
| 171 | + case TIME_LESS_THAN -> currentTime.isBefore(targetTime); | |
| 172 | + case TIME_BETWEEN -> matchTimeBetween(currentTime, param); | |
| 173 | + default -> { | |
| 174 | + log.warn("[matchTime][operatorEnum({}) 不支持的时间操作符]", operatorEnum); | |
| 175 | + yield false; | |
| 127 | 176 | } |
| 128 | - } | |
| 177 | + }; | |
| 129 | 178 | } catch (Exception e) { |
| 130 | - // TODO @puhui999:1)日志格式 [][];2)方法名不对哈; | |
| 131 | - log.error("[CurrentTimeConditionMatcher][时间解析异常] param: {}", param, e); | |
| 179 | + log.error("[matchTime][][operatorEnum({}) param({}) 时间解析异常]", operatorEnum, param, e); | |
| 132 | 180 | return false; |
| 133 | 181 | } |
| 134 | 182 | } |
| 135 | 183 | |
| 136 | 184 | /** |
| 185 | + * 匹配时间区间 | |
| 186 | + */ | |
| 187 | + private boolean matchTimeBetween(LocalTime currentTime, String param) { | |
| 188 | + List<String> timeRange = StrUtil.splitTrim(param, CharPool.COMMA); | |
| 189 | + if (timeRange.size() != 2) { | |
| 190 | + log.warn("[matchTimeBetween][param({}) 时间区间参数格式错误]", param); | |
| 191 | + return false; | |
| 192 | + } | |
| 193 | + LocalTime startTime = parseTime(timeRange.get(0).trim()); | |
| 194 | + LocalTime endTime = parseTime(timeRange.get(1).trim()); | |
| 195 | + return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); | |
| 196 | + } | |
| 197 | + | |
| 198 | + /** | |
| 137 | 199 | * 解析时间字符串 |
| 200 | + * 支持 HH:mm 和 HH:mm:ss 两种格式 | |
| 138 | 201 | */ |
| 139 | 202 | private LocalTime parseTime(String timeStr) { |
| 140 | - // TODO @puhui999:可以用 hutool Assert 类简化 | |
| 141 | - if (StrUtil.isBlank(timeStr)) { | |
| 142 | - throw new IllegalArgumentException("时间字符串不能为空"); | |
| 143 | - } | |
| 144 | - // 尝试不同的时间格式 | |
| 203 | + Assert.isFalse(StrUtil.isBlank(timeStr), "时间字符串不能为空"); | |
| 204 | + | |
| 145 | 205 | try { |
| 206 | + // 尝试不同的时间格式 | |
| 146 | 207 | if (timeStr.length() == 5) { // HH:mm |
| 147 | 208 | return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT); |
| 148 | - } else { // HH:mm:ss | |
| 209 | + } else if (timeStr.length() == 8) { // HH:mm:ss | |
| 149 | 210 | return LocalTime.parse(timeStr, TIME_FORMATTER); |
| 211 | + } else { | |
| 212 | + throw new IllegalArgumentException("时间格式长度不正确,期望 HH:mm 或 HH:mm:ss 格式"); | |
| 150 | 213 | } |
| 151 | 214 | } catch (Exception e) { |
| 215 | + log.error("[parseTime][timeStr({}) 时间格式解析失败]", timeStr, e); | |
| 152 | 216 | throw new IllegalArgumentException("时间格式无效: " + timeStr, e); |
| 153 | 217 | } |
| 154 | 218 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcher.java
| ... | ... | @@ -22,41 +22,40 @@ public class DevicePropertyConditionMatcher implements IotSceneRuleConditionMatc |
| 22 | 22 | return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY; |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | - // TODO @puhui999:参数校验的,要不要 1.1 1.2 1.3 1.4 ?这样最终看到 2. 3. 就是核心逻辑列; | |
| 26 | 25 | @Override |
| 27 | 26 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { |
| 28 | - // 1. 基础参数校验 | |
| 27 | + // 1.1 基础参数校验 | |
| 29 | 28 | if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { |
| 30 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效"); | |
| 29 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); | |
| 31 | 30 | return false; |
| 32 | 31 | } |
| 33 | 32 | |
| 34 | - // 2. 检查标识符是否匹配 | |
| 33 | + // 1.2 检查标识符是否匹配 | |
| 35 | 34 | String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); |
| 36 | 35 | if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) { |
| 37 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 36 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 38 | 37 | return false; |
| 39 | 38 | } |
| 40 | 39 | |
| 41 | - // 3. 检查操作符和参数是否有效 | |
| 40 | + // 1.3 检查操作符和参数是否有效 | |
| 42 | 41 | if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { |
| 43 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效"); | |
| 42 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); | |
| 44 | 43 | return false; |
| 45 | 44 | } |
| 46 | 45 | |
| 47 | - // 4. 获取属性值 | |
| 48 | - Object propertyValue = message.getData(); | |
| 46 | + // 2.1. 获取属性值 | |
| 47 | + Object propertyValue = message.getParams(); | |
| 49 | 48 | if (propertyValue == null) { |
| 50 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "消息中属性值为空"); | |
| 49 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空"); | |
| 51 | 50 | return false; |
| 52 | 51 | } |
| 53 | 52 | |
| 54 | - // 5. 使用条件评估器进行匹配 | |
| 53 | + // 2.2 使用条件评估器进行匹配 | |
| 55 | 54 | boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam()); |
| 56 | 55 | if (matched) { |
| 57 | - IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition); | |
| 56 | + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); | |
| 58 | 57 | } else { |
| 59 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "设备属性条件不匹配"); | |
| 58 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备属性条件不匹配"); | |
| 60 | 59 | } |
| 61 | 60 | return matched; |
| 62 | 61 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcher.java
| ... | ... | @@ -23,32 +23,31 @@ public class DeviceStateConditionMatcher implements IotSceneRuleConditionMatcher |
| 23 | 23 | |
| 24 | 24 | @Override |
| 25 | 25 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { |
| 26 | - // 1. 基础参数校验 | |
| 26 | + // 1.1 基础参数校验 | |
| 27 | 27 | if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { |
| 28 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效"); | |
| 28 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); | |
| 29 | 29 | return false; |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | - // 2. 检查操作符和参数是否有效 | |
| 32 | + // 1.2 检查操作符和参数是否有效 | |
| 33 | 33 | if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { |
| 34 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效"); | |
| 34 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); | |
| 35 | 35 | return false; |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | - // 3. 获取设备状态值 | |
| 39 | - // 设备状态通常在消息的 data 字段中 | |
| 40 | - Object stateValue = message.getData(); | |
| 38 | + // 2.1 获取设备状态值 | |
| 39 | + Object stateValue = message.getParams(); | |
| 41 | 40 | if (stateValue == null) { |
| 42 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "消息中设备状态值为空"); | |
| 41 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空"); | |
| 43 | 42 | return false; |
| 44 | 43 | } |
| 45 | 44 | |
| 46 | - // 4. 使用条件评估器进行匹配 | |
| 45 | + // 2.2 使用条件评估器进行匹配 | |
| 47 | 46 | boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam()); |
| 48 | 47 | if (matched) { |
| 49 | - IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition); | |
| 48 | + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); | |
| 50 | 49 | } else { |
| 51 | - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "设备状态条件不匹配"); | |
| 50 | + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备状态条件不匹配"); | |
| 52 | 51 | } |
| 53 | 52 | return matched; |
| 54 | 53 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcher.java
| ... | ... | @@ -19,11 +19,6 @@ import org.springframework.stereotype.Component; |
| 19 | 19 | @Component |
| 20 | 20 | public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher { |
| 21 | 21 | |
| 22 | - /** | |
| 23 | - * 设备事件上报消息方法 | |
| 24 | - */ | |
| 25 | - private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod(); | |
| 26 | - | |
| 27 | 22 | @Override |
| 28 | 23 | public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { |
| 29 | 24 | return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; |
| ... | ... | @@ -31,42 +26,44 @@ public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher |
| 31 | 26 | |
| 32 | 27 | @Override |
| 33 | 28 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 34 | - // 1. 基础参数校验 | |
| 29 | + // 1.1 基础参数校验 | |
| 35 | 30 | if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { |
| 36 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); | |
| 31 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); | |
| 37 | 32 | return false; |
| 38 | 33 | } |
| 39 | 34 | |
| 40 | - // 2. 检查消息方法是否匹配 | |
| 41 | - if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) { | |
| 42 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod()); | |
| 35 | + // 1.2 检查消息方法是否匹配 | |
| 36 | + if (!IotDeviceMessageMethodEnum.EVENT_POST.getMethod().equals(message.getMethod())) { | |
| 37 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + | |
| 38 | + IotDeviceMessageMethodEnum.EVENT_POST.getMethod() + ", 实际: " + message.getMethod()); | |
| 43 | 39 | return false; |
| 44 | 40 | } |
| 45 | 41 | |
| 46 | - // 3. 检查标识符是否匹配 | |
| 42 | + // 1.3 检查标识符是否匹配 | |
| 47 | 43 | String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); |
| 48 | 44 | if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { |
| 49 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 45 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + | |
| 46 | + trigger.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 50 | 47 | return false; |
| 51 | 48 | } |
| 52 | 49 | |
| 53 | - // 4. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配 | |
| 50 | + // 2. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配 | |
| 54 | 51 | // 但如果配置了操作符和值,则需要进行条件匹配 |
| 55 | 52 | if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { |
| 56 | 53 | Object eventData = message.getData(); |
| 57 | 54 | if (eventData == null) { |
| 58 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中事件数据为空"); | |
| 55 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中事件数据为空"); | |
| 59 | 56 | return false; |
| 60 | 57 | } |
| 61 | 58 | |
| 62 | 59 | boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventData, trigger.getOperator(), trigger.getValue()); |
| 63 | 60 | if (!matched) { |
| 64 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "事件数据条件不匹配"); | |
| 61 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "事件数据条件不匹配"); | |
| 65 | 62 | return false; |
| 66 | 63 | } |
| 67 | 64 | } |
| 68 | 65 | |
| 69 | - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); | |
| 66 | + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); | |
| 70 | 67 | return true; |
| 71 | 68 | } |
| 72 | 69 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcher.java
| ... | ... | @@ -18,12 +18,6 @@ import org.springframework.stereotype.Component; |
| 18 | 18 | @Component |
| 19 | 19 | public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher { |
| 20 | 20 | |
| 21 | - /** | |
| 22 | - * 设备属性上报消息方法 | |
| 23 | - */ | |
| 24 | - // TODO @puhui999:是不是不用枚举哈?直接使用 IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() | |
| 25 | - private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(); | |
| 26 | - | |
| 27 | 21 | @Override |
| 28 | 22 | public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { |
| 29 | 23 | return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; |
| ... | ... | @@ -31,44 +25,46 @@ public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatc |
| 31 | 25 | |
| 32 | 26 | @Override |
| 33 | 27 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 34 | - // 1. 基础参数校验 | |
| 28 | + // 1.1 基础参数校验 | |
| 35 | 29 | if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { |
| 36 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); | |
| 30 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); | |
| 37 | 31 | return false; |
| 38 | 32 | } |
| 39 | 33 | |
| 40 | - // 2. 检查消息方法是否匹配 | |
| 41 | - if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) { | |
| 42 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod()); | |
| 34 | + // 1.2 检查消息方法是否匹配 | |
| 35 | + if (!IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod().equals(message.getMethod())) { | |
| 36 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + | |
| 37 | + IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() + ", 实际: " + message.getMethod()); | |
| 43 | 38 | return false; |
| 44 | 39 | } |
| 45 | 40 | |
| 46 | - // 3. 检查标识符是否匹配 | |
| 41 | + // 1.3 检查标识符是否匹配 | |
| 47 | 42 | String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); |
| 48 | 43 | if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { |
| 49 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 44 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + | |
| 45 | + trigger.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 50 | 46 | return false; |
| 51 | 47 | } |
| 52 | 48 | |
| 53 | - // 4. 检查操作符和值是否有效 | |
| 49 | + // 1.4 检查操作符和值是否有效 | |
| 54 | 50 | if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { |
| 55 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "操作符或值无效"); | |
| 51 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); | |
| 56 | 52 | return false; |
| 57 | 53 | } |
| 58 | 54 | |
| 59 | - // 5. 获取属性值 | |
| 60 | - Object propertyValue = message.getData(); | |
| 55 | + // 2.1 获取属性值 | |
| 56 | + Object propertyValue = message.getParams(); | |
| 61 | 57 | if (propertyValue == null) { |
| 62 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中属性值为空"); | |
| 58 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空"); | |
| 63 | 59 | return false; |
| 64 | 60 | } |
| 65 | 61 | |
| 66 | - // 6. 使用条件评估器进行匹配 | |
| 62 | + // 2.2 使用条件评估器进行匹配 | |
| 67 | 63 | boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); |
| 68 | 64 | if (matched) { |
| 69 | - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); | |
| 65 | + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); | |
| 70 | 66 | } else { |
| 71 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "属性值条件不匹配"); | |
| 67 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "属性值条件不匹配"); | |
| 72 | 68 | } |
| 73 | 69 | return matched; |
| 74 | 70 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcher.java
| ... | ... | @@ -18,11 +18,6 @@ import org.springframework.stereotype.Component; |
| 18 | 18 | @Component |
| 19 | 19 | public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher { |
| 20 | 20 | |
| 21 | - /** | |
| 22 | - * 设备服务调用消息方法 | |
| 23 | - */ | |
| 24 | - private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(); | |
| 25 | - | |
| 26 | 21 | @Override |
| 27 | 22 | public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { |
| 28 | 23 | return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; |
| ... | ... | @@ -30,29 +25,29 @@ public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMat |
| 30 | 25 | |
| 31 | 26 | @Override |
| 32 | 27 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 33 | - // 1. 基础参数校验 | |
| 28 | + // 1.1 基础参数校验 | |
| 34 | 29 | if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { |
| 35 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); | |
| 30 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); | |
| 36 | 31 | return false; |
| 37 | 32 | } |
| 38 | 33 | |
| 39 | - // 2. 检查消息方法是否匹配 | |
| 40 | - if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) { | |
| 41 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod()); | |
| 34 | + // 1.2 检查消息方法是否匹配 | |
| 35 | + if (!IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod().equals(message.getMethod())) { | |
| 36 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod() + ", 实际: " + message.getMethod()); | |
| 42 | 37 | return false; |
| 43 | 38 | } |
| 44 | 39 | |
| 45 | - // 3. 检查标识符是否匹配 | |
| 40 | + // 1.3 检查标识符是否匹配 | |
| 46 | 41 | String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); |
| 47 | 42 | if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { |
| 48 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 43 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); | |
| 49 | 44 | return false; |
| 50 | 45 | } |
| 51 | 46 | |
| 52 | - // 4. 对于服务调用触发器,通常只需要匹配服务标识符即可 | |
| 47 | + // 2. 对于服务调用触发器,通常只需要匹配服务标识符即可 | |
| 53 | 48 | // 不需要检查操作符和值,因为服务调用本身就是触发条件 |
| 54 | - | |
| 55 | - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); | |
| 49 | + // TODO @puhui999: 服务调用时校验输入参数是否匹配条件 | |
| 50 | + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); | |
| 56 | 51 | return true; |
| 57 | 52 | } |
| 58 | 53 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcher.java
| ... | ... | @@ -17,12 +17,6 @@ import org.springframework.stereotype.Component; |
| 17 | 17 | @Component |
| 18 | 18 | public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher { |
| 19 | 19 | |
| 20 | - // TODO @puhui999:是不是不用枚举哈; | |
| 21 | - /** | |
| 22 | - * 设备状态更新消息方法 | |
| 23 | - */ | |
| 24 | - private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(); | |
| 25 | - | |
| 26 | 20 | @Override |
| 27 | 21 | public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { |
| 28 | 22 | return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; |
| ... | ... | @@ -30,37 +24,39 @@ public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatch |
| 30 | 24 | |
| 31 | 25 | @Override |
| 32 | 26 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 33 | - // 1. 基础参数校验 | |
| 27 | + // 1.1 基础参数校验 | |
| 34 | 28 | if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { |
| 35 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); | |
| 29 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); | |
| 36 | 30 | return false; |
| 37 | 31 | } |
| 38 | 32 | |
| 39 | - // 2. 检查消息方法是否匹配 | |
| 40 | - if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) { | |
| 41 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod()); | |
| 33 | + // 1.2 检查消息方法是否匹配 | |
| 34 | + if (!IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod().equals(message.getMethod())) { | |
| 35 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + | |
| 36 | + IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod() + ", 实际: " + message.getMethod()); | |
| 42 | 37 | return false; |
| 43 | 38 | } |
| 44 | 39 | |
| 45 | - // 3. 检查操作符和值是否有效 | |
| 40 | + // 1.3 检查操作符和值是否有效 | |
| 46 | 41 | if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { |
| 47 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "操作符或值无效"); | |
| 42 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); | |
| 48 | 43 | return false; |
| 49 | 44 | } |
| 50 | 45 | |
| 51 | - // 4. 获取设备状态值 | |
| 52 | - Object stateValue = message.getData(); | |
| 46 | + // 2.1 获取设备状态值 | |
| 47 | + Object stateValue = message.getParams(); | |
| 53 | 48 | if (stateValue == null) { |
| 54 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中设备状态值为空"); | |
| 49 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空"); | |
| 55 | 50 | return false; |
| 56 | 51 | } |
| 57 | 52 | |
| 58 | - // 5. 使用条件评估器进行匹配 | |
| 53 | + // 2.2 使用条件评估器进行匹配 | |
| 54 | + // TODO @puhui999: 状态匹配重新实现 | |
| 59 | 55 | boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue()); |
| 60 | 56 | if (matched) { |
| 61 | - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); | |
| 57 | + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); | |
| 62 | 58 | } else { |
| 63 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "状态值条件不匹配"); | |
| 59 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "状态值条件不匹配"); | |
| 64 | 60 | } |
| 65 | 61 | return matched; |
| 66 | 62 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcher.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; |
| 8 | +import org.quartz.CronExpression; | |
| 8 | 9 | import org.springframework.stereotype.Component; |
| 9 | 10 | |
| 10 | 11 | /** |
| ... | ... | @@ -25,58 +26,32 @@ public class TimerTriggerMatcher implements IotSceneRuleTriggerMatcher { |
| 25 | 26 | |
| 26 | 27 | @Override |
| 27 | 28 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 28 | - // 1. 基础参数校验 | |
| 29 | + // 1.1 基础参数校验 | |
| 29 | 30 | if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { |
| 30 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); | |
| 31 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); | |
| 31 | 32 | return false; |
| 32 | 33 | } |
| 33 | 34 | |
| 34 | - // 2. 检查 CRON 表达式是否存在 | |
| 35 | + // 1.2 检查 CRON 表达式是否存在 | |
| 35 | 36 | if (StrUtil.isBlank(trigger.getCronExpression())) { |
| 36 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "定时触发器缺少 CRON 表达式"); | |
| 37 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); | |
| 37 | 38 | return false; |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | - // 3. 定时触发器通常不依赖具体的设备消息 | |
| 41 | + // 1.3 定时触发器通常不依赖具体的设备消息 | |
| 41 | 42 | // 它是通过定时任务调度器触发的,这里主要是验证配置的有效性 |
| 42 | - | |
| 43 | - // 4. 可以添加 CRON 表达式格式验证 | |
| 44 | - if (!isValidCronExpression(trigger.getCronExpression())) { | |
| 45 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); | |
| 43 | + if (!CronExpression.isValidExpression(trigger.getCronExpression())) { | |
| 44 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); | |
| 46 | 45 | return false; |
| 47 | 46 | } |
| 48 | 47 | |
| 49 | - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); | |
| 48 | + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); | |
| 50 | 49 | return true; |
| 51 | 50 | } |
| 52 | 51 | |
| 53 | - /** | |
| 54 | - * 验证 CRON 表达式格式是否有效 | |
| 55 | - * | |
| 56 | - * @param cronExpression CRON 表达式 | |
| 57 | - * @return 是否有效 | |
| 58 | - */ | |
| 59 | - private boolean isValidCronExpression(String cronExpression) { | |
| 60 | - // TODO @puhui999:CronExpression.isValidExpression(cronExpression); | |
| 61 | - try { | |
| 62 | - // 简单的 CRON 表达式格式验证 | |
| 63 | - // 标准 CRON 表达式应该有 6 或 7 个字段(秒 分 时 日 月 周 [年]) | |
| 64 | - String[] fields = cronExpression.trim().split("\\s+"); | |
| 65 | - return fields.length >= 6 && fields.length <= 7; | |
| 66 | - } catch (Exception e) { | |
| 67 | - return false; | |
| 68 | - } | |
| 69 | - } | |
| 70 | - | |
| 71 | 52 | @Override |
| 72 | 53 | public int getPriority() { |
| 73 | 54 | return 50; // 最低优先级,因为定时触发器不依赖消息 |
| 74 | 55 | } |
| 75 | 56 | |
| 76 | - @Override | |
| 77 | - public boolean isEnabled() { | |
| 78 | - // 定时触发器可以根据配置动态启用/禁用 | |
| 79 | - return true; | |
| 80 | - } | |
| 81 | - | |
| 82 | 57 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java
| ... | ... | @@ -173,19 +173,6 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | 175 | @Test |
| 176 | - void testMatcherManagerStatistics() { | |
| 177 | - // 1. 执行测试 | |
| 178 | - var statistics = matcherManager.getMatcherStatistics(); | |
| 179 | - | |
| 180 | - // 2. 验证结果 | |
| 181 | - assertNotNull(statistics); | |
| 182 | - assertEquals(5, statistics.get("totalMatchers")); | |
| 183 | - assertEquals(5, statistics.get("enabledMatchers")); | |
| 184 | - assertNotNull(statistics.get("supportedTriggerTypes")); | |
| 185 | - assertNotNull(statistics.get("matcherDetails")); | |
| 186 | - } | |
| 187 | - | |
| 188 | - @Test | |
| 189 | 176 | void testGetSupportedTriggerTypes() { |
| 190 | 177 | // 1. 执行测试 |
| 191 | 178 | var supportedTypes = matcherManager.getSupportedTriggerTypes(); | ... | ... |