Commit fb35807ebf952001e661cb0283b6fcaeb0222991

Authored by puhui999
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 package cn.iocoder.yudao.module.iot.enums.rule; 1 package cn.iocoder.yudao.module.iot.enums.rule;
2 2
  3 +import cn.hutool.core.util.ArrayUtil;
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 4 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 import lombok.Getter; 5 import lombok.Getter;
5 import lombok.RequiredArgsConstructor; 6 import lombok.RequiredArgsConstructor;
@@ -60,7 +61,9 @@ public enum IotSceneRuleTriggerTypeEnum implements ArrayValuable<Integer> { @@ -60,7 +61,9 @@ public enum IotSceneRuleTriggerTypeEnum implements ArrayValuable<Integer> {
60 return ARRAYS; 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,16 +26,6 @@ public interface IotSceneRuleMatcher {
26 return 100; 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 * <p> 31 * <p>
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java
1 package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; 1 package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
2 2
  3 +import cn.hutool.core.text.CharPool;
3 import cn.hutool.core.util.NumberUtil; 4 import cn.hutool.core.util.NumberUtil;
4 import cn.hutool.core.util.StrUtil; 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 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; 8 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
@@ -40,44 +43,99 @@ public final class IotSceneRuleMatcherHelper { @@ -40,44 +43,99 @@ public final class IotSceneRuleMatcherHelper {
40 * @param paramValue 参数值(来自条件配置) 43 * @param paramValue 参数值(来自条件配置)
41 * @return 是否匹配 44 * @return 是否匹配
42 */ 45 */
43 - @SuppressWarnings("DataFlowIssue")  
44 public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) { 46 public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
45 try { 47 try {
46 // 1. 校验操作符是否合法 48 // 1. 校验操作符是否合法
47 IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator); 49 IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
48 if (operatorEnum == null) { 50 if (operatorEnum == null) {
49 - log.warn("[evaluateCondition][存在错误的操作符({})]", operator); 51 + log.warn("[evaluateCondition][operator({}) 操作符无效]", operator);
50 return false; 52 return false;
51 } 53 }
52 54
53 // 2. 构建 Spring 表达式变量 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 return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables); 79 return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
74 } catch (Exception e) { 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 return false; 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,24 +161,22 @@ public final class IotSceneRuleMatcherHelper {
103 /** 161 /**
104 * 记录触发器匹配成功日志 162 * 记录触发器匹配成功日志
105 * 163 *
106 - * @param matcherName 匹配器名称  
107 * @param message 设备消息 164 * @param message 设备消息
108 * @param trigger 触发器配置 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 * @param message 设备消息 174 * @param message 设备消息
119 * @param trigger 触发器配置 175 * @param trigger 触发器配置
120 * @param reason 失败原因 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,24 +204,22 @@ public final class IotSceneRuleMatcherHelper {
148 /** 204 /**
149 * 记录条件匹配成功日志 205 * 记录条件匹配成功日志
150 * 206 *
151 - * @param matcherName 匹配器名称  
152 * @param message 设备消息 207 * @param message 设备消息
153 * @param condition 触发条件 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 * @param message 设备消息 217 * @param message 设备消息
164 * @param condition 触发条件 218 * @param condition 触发条件
165 * @param reason 失败原因 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,7 +14,7 @@ import java.util.*;
14 import java.util.function.Function; 14 import java.util.function.Function;
15 import java.util.stream.Collectors; 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 * IoT 场景规则匹配器统一管理器 20 * IoT 场景规则匹配器统一管理器
@@ -72,42 +72,34 @@ public class IotSceneRuleMatcherManager { @@ -72,42 +72,34 @@ public class IotSceneRuleMatcherManager {
72 .toList(); 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 this.allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size()); 98 this.allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size());
105 this.triggerMatchers.forEach((type, matcher) -> 99 this.triggerMatchers.forEach((type, matcher) ->
106 - log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}",  
107 - type, matcher.getMatcherName(), matcher.getPriority())); 100 + log.info("[IotSceneRuleMatcherManager][触发器匹配器类型: ({}), 优先级: ({})] ", type, matcher.getPriority()));
108 this.conditionMatchers.forEach((type, matcher) -> 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,27 +110,25 @@ public class IotSceneRuleMatcherManager {
118 * @return 是否匹配 110 * @return 是否匹配
119 */ 111 */
120 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 112 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
121 - // TODO @puhui999:日志优化下;claude 打出来的日志风格,和项目有点不一样哈;  
122 if (message == null || trigger == null || trigger.getType() == null) { 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 return false; 115 return false;
125 } 116 }
126 - IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType()); 117 + IotSceneRuleTriggerTypeEnum triggerType = IotSceneRuleTriggerTypeEnum.typeOf(trigger.getType());
127 if (triggerType == null) { 118 if (triggerType == null) {
128 - log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType()); 119 + log.warn("[isMatched][triggerType({}) 未知的触发器类型]", trigger.getType());
129 return false; 120 return false;
130 } 121 }
131 IotSceneRuleTriggerMatcher matcher = triggerMatchers.get(triggerType); 122 IotSceneRuleTriggerMatcher matcher = triggerMatchers.get(triggerType);
132 if (matcher == null) { 123 if (matcher == null) {
133 - log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType); 124 + log.warn("[isMatched][triggerType({}) 没有对应的匹配器]", triggerType);
134 return false; 125 return false;
135 } 126 }
136 127
137 try { 128 try {
138 return matcher.isMatched(message, trigger); 129 return matcher.isMatched(message, trigger);
139 } catch (Exception e) { 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 return false; 132 return false;
143 } 133 }
144 } 134 }
@@ -152,28 +142,27 @@ public class IotSceneRuleMatcherManager { @@ -152,28 +142,27 @@ public class IotSceneRuleMatcherManager {
152 */ 142 */
153 public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { 143 public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
154 if (message == null || condition == null || condition.getType() == null) { 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 return false; 146 return false;
157 } 147 }
158 148
159 // 根据条件类型查找对应的匹配器 149 // 根据条件类型查找对应的匹配器
160 IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType()); 150 IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType());
161 if (conditionType == null) { 151 if (conditionType == null) {
162 - log.warn("[isConditionMatched][未知的条件类型: {}]", condition.getType()); 152 + log.warn("[isConditionMatched][conditionType({}) 未知的条件类型]", condition.getType());
163 return false; 153 return false;
164 } 154 }
165 155
166 IotSceneRuleConditionMatcher matcher = conditionMatchers.get(conditionType); 156 IotSceneRuleConditionMatcher matcher = conditionMatchers.get(conditionType);
167 if (matcher == null) { 157 if (matcher == null) {
168 - log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType); 158 + log.warn("[isConditionMatched][conditionType({}) 没有对应的匹配器]", conditionType);
169 return false; 159 return false;
170 } 160 }
171 161
172 try { 162 try {
173 return matcher.isMatched(message, condition); 163 return matcher.isMatched(message, condition);
174 } catch (Exception e) { 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 return false; 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 package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; 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 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
6 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; 9 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
7 import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; 10 import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
8 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
@@ -11,6 +14,7 @@ import org.springframework.stereotype.Component; @@ -11,6 +14,7 @@ import org.springframework.stereotype.Component;
11 import java.time.LocalDateTime; 14 import java.time.LocalDateTime;
12 import java.time.LocalTime; 15 import java.time.LocalTime;
13 import java.time.format.DateTimeFormatter; 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,115 +44,175 @@ public class CurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher
40 44
41 @Override 45 @Override
42 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { 46 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
43 - // 1. 基础参数校验 47 + // 1.1 基础参数校验
44 if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { 48 if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {
45 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效"); 49 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效");
46 return false; 50 return false;
47 } 51 }
48 52
49 - // 2. 检查操作符和参数是否有效 53 + // 1.2 检查操作符和参数是否有效
50 if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { 54 if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {
51 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效"); 55 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效");
52 return false; 56 return false;
53 } 57 }
54 58
55 - // 3. 根据操作符类型进行不同的时间匹配  
56 - LocalDateTime now = LocalDateTime.now(); 59 + // 1.3 验证操作符是否为支持的时间操作符
57 String operator = condition.getOperator(); 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 try { 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 } else { 97 } else {
75 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "时间条件不匹配"); 98 + // 当日时间匹配(HH:mm:ss)
  99 + return matchTime(now.toLocalTime(), operatorEnum, param);
76 } 100 }
77 } catch (Exception e) { 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 * 匹配当日时间(HH:mm:ss) 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 try { 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 } catch (Exception e) { 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 return false; 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 private LocalTime parseTime(String timeStr) { 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 try { 205 try {
  206 + // 尝试不同的时间格式
146 if (timeStr.length() == 5) { // HH:mm 207 if (timeStr.length() == 5) { // HH:mm
147 return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT); 208 return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT);
148 - } else { // HH:mm:ss 209 + } else if (timeStr.length() == 8) { // HH:mm:ss
149 return LocalTime.parse(timeStr, TIME_FORMATTER); 210 return LocalTime.parse(timeStr, TIME_FORMATTER);
  211 + } else {
  212 + throw new IllegalArgumentException("时间格式长度不正确,期望 HH:mm 或 HH:mm:ss 格式");
150 } 213 }
151 } catch (Exception e) { 214 } catch (Exception e) {
  215 + log.error("[parseTime][timeStr({}) 时间格式解析失败]", timeStr, e);
152 throw new IllegalArgumentException("时间格式无效: " + timeStr, e); 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,41 +22,40 @@ public class DevicePropertyConditionMatcher implements IotSceneRuleConditionMatc
22 return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY; 22 return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY;
23 } 23 }
24 24
25 - // TODO @puhui999:参数校验的,要不要 1.1 1.2 1.3 1.4 ?这样最终看到 2. 3. 就是核心逻辑列;  
26 @Override 25 @Override
27 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { 26 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
28 - // 1. 基础参数校验 27 + // 1.1 基础参数校验
29 if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { 28 if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {
30 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效"); 29 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效");
31 return false; 30 return false;
32 } 31 }
33 32
34 - // 2. 检查标识符是否匹配 33 + // 1.2 检查标识符是否匹配
35 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 34 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
36 if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) { 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 return false; 37 return false;
39 } 38 }
40 39
41 - // 3. 检查操作符和参数是否有效 40 + // 1.3 检查操作符和参数是否有效
42 if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { 41 if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {
43 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效"); 42 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效");
44 return false; 43 return false;
45 } 44 }
46 45
47 - // 4. 获取属性值  
48 - Object propertyValue = message.getData(); 46 + // 2.1. 获取属性值
  47 + Object propertyValue = message.getParams();
49 if (propertyValue == null) { 48 if (propertyValue == null) {
50 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "消息中属性值为空"); 49 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空");
51 return false; 50 return false;
52 } 51 }
53 52
54 - // 5. 使用条件评估器进行匹配 53 + // 2.2 使用条件评估器进行匹配
55 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam()); 54 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam());
56 if (matched) { 55 if (matched) {
57 - IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition); 56 + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);
58 } else { 57 } else {
59 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "设备属性条件不匹配"); 58 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备属性条件不匹配");
60 } 59 }
61 return matched; 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,32 +23,31 @@ public class DeviceStateConditionMatcher implements IotSceneRuleConditionMatcher
23 23
24 @Override 24 @Override
25 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { 25 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
26 - // 1. 基础参数校验 26 + // 1.1 基础参数校验
27 if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { 27 if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {
28 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效"); 28 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效");
29 return false; 29 return false;
30 } 30 }
31 31
32 - // 2. 检查操作符和参数是否有效 32 + // 1.2 检查操作符和参数是否有效
33 if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { 33 if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {
34 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效"); 34 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效");
35 return false; 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 if (stateValue == null) { 40 if (stateValue == null) {
42 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "消息中设备状态值为空"); 41 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空");
43 return false; 42 return false;
44 } 43 }
45 44
46 - // 4. 使用条件评估器进行匹配 45 + // 2.2 使用条件评估器进行匹配
47 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam()); 46 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam());
48 if (matched) { 47 if (matched) {
49 - IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition); 48 + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);
50 } else { 49 } else {
51 - IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "设备状态条件不匹配"); 50 + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备状态条件不匹配");
52 } 51 }
53 return matched; 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,11 +19,6 @@ import org.springframework.stereotype.Component;
19 @Component 19 @Component
20 public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher { 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 @Override 22 @Override
28 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 23 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
29 return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; 24 return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
@@ -31,42 +26,44 @@ public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher @@ -31,42 +26,44 @@ public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher
31 26
32 @Override 27 @Override
33 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 28 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
34 - // 1. 基础参数校验 29 + // 1.1 基础参数校验
35 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { 30 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
36 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); 31 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
37 return false; 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 return false; 39 return false;
44 } 40 }
45 41
46 - // 3. 检查标识符是否匹配 42 + // 1.3 检查标识符是否匹配
47 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 43 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
48 if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { 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 return false; 47 return false;
51 } 48 }
52 49
53 - // 4. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配 50 + // 2. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配
54 // 但如果配置了操作符和值,则需要进行条件匹配 51 // 但如果配置了操作符和值,则需要进行条件匹配
55 if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { 52 if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
56 Object eventData = message.getData(); 53 Object eventData = message.getData();
57 if (eventData == null) { 54 if (eventData == null) {
58 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中事件数据为空"); 55 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中事件数据为空");
59 return false; 56 return false;
60 } 57 }
61 58
62 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventData, trigger.getOperator(), trigger.getValue()); 59 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
63 if (!matched) { 60 if (!matched) {
64 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "事件数据条件不匹配"); 61 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "事件数据条件不匹配");
65 return false; 62 return false;
66 } 63 }
67 } 64 }
68 65
69 - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); 66 + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
70 return true; 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,12 +18,6 @@ import org.springframework.stereotype.Component;
18 @Component 18 @Component
19 public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher { 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 @Override 21 @Override
28 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 22 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
29 return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; 23 return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
@@ -31,44 +25,46 @@ public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatc @@ -31,44 +25,46 @@ public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatc
31 25
32 @Override 26 @Override
33 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 27 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
34 - // 1. 基础参数校验 28 + // 1.1 基础参数校验
35 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { 29 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
36 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); 30 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
37 return false; 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 return false; 38 return false;
44 } 39 }
45 40
46 - // 3. 检查标识符是否匹配 41 + // 1.3 检查标识符是否匹配
47 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 42 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
48 if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { 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 return false; 46 return false;
51 } 47 }
52 48
53 - // 4. 检查操作符和值是否有效 49 + // 1.4 检查操作符和值是否有效
54 if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { 50 if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) {
55 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "操作符或值无效"); 51 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效");
56 return false; 52 return false;
57 } 53 }
58 54
59 - // 5. 获取属性值  
60 - Object propertyValue = message.getData(); 55 + // 2.1 获取属性值
  56 + Object propertyValue = message.getParams();
61 if (propertyValue == null) { 57 if (propertyValue == null) {
62 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中属性值为空"); 58 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空");
63 return false; 59 return false;
64 } 60 }
65 61
66 - // 6. 使用条件评估器进行匹配 62 + // 2.2 使用条件评估器进行匹配
67 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); 63 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
68 if (matched) { 64 if (matched) {
69 - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); 65 + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
70 } else { 66 } else {
71 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "属性值条件不匹配"); 67 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "属性值条件不匹配");
72 } 68 }
73 return matched; 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,11 +18,6 @@ import org.springframework.stereotype.Component;
18 @Component 18 @Component
19 public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher { 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 @Override 21 @Override
27 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 22 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
28 return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; 23 return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
@@ -30,29 +25,29 @@ public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMat @@ -30,29 +25,29 @@ public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMat
30 25
31 @Override 26 @Override
32 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 27 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
33 - // 1. 基础参数校验 28 + // 1.1 基础参数校验
34 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { 29 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
35 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); 30 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
36 return false; 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 return false; 37 return false;
43 } 38 }
44 39
45 - // 3. 检查标识符是否匹配 40 + // 1.3 检查标识符是否匹配
46 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 41 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
47 if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { 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 return false; 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 return true; 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,12 +17,6 @@ import org.springframework.stereotype.Component;
17 @Component 17 @Component
18 public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher { 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 @Override 20 @Override
27 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 21 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
28 return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; 22 return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
@@ -30,37 +24,39 @@ public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatch @@ -30,37 +24,39 @@ public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatch
30 24
31 @Override 25 @Override
32 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 26 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
33 - // 1. 基础参数校验 27 + // 1.1 基础参数校验
34 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { 28 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
35 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); 29 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
36 return false; 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 return false; 37 return false;
43 } 38 }
44 39
45 - // 3. 检查操作符和值是否有效 40 + // 1.3 检查操作符和值是否有效
46 if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { 41 if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) {
47 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "操作符或值无效"); 42 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效");
48 return false; 43 return false;
49 } 44 }
50 45
51 - // 4. 获取设备状态值  
52 - Object stateValue = message.getData(); 46 + // 2.1 获取设备状态值
  47 + Object stateValue = message.getParams();
53 if (stateValue == null) { 48 if (stateValue == null) {
54 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中设备状态值为空"); 49 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
55 return false; 50 return false;
56 } 51 }
57 52
58 - // 5. 使用条件评估器进行匹配 53 + // 2.2 使用条件评估器进行匹配
  54 + // TODO @puhui999: 状态匹配重新实现
59 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue()); 55 boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
60 if (matched) { 56 if (matched) {
61 - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); 57 + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
62 } else { 58 } else {
63 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "状态值条件不匹配"); 59 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "状态值条件不匹配");
64 } 60 }
65 return matched; 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,6 +5,7 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 5 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
6 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; 6 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
7 import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; 7 import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
  8 +import org.quartz.CronExpression;
8 import org.springframework.stereotype.Component; 9 import org.springframework.stereotype.Component;
9 10
10 /** 11 /**
@@ -25,58 +26,32 @@ public class TimerTriggerMatcher implements IotSceneRuleTriggerMatcher { @@ -25,58 +26,32 @@ public class TimerTriggerMatcher implements IotSceneRuleTriggerMatcher {
25 26
26 @Override 27 @Override
27 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 28 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
28 - // 1. 基础参数校验 29 + // 1.1 基础参数校验
29 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { 30 if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
30 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效"); 31 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
31 return false; 32 return false;
32 } 33 }
33 34
34 - // 2. 检查 CRON 表达式是否存在 35 + // 1.2 检查 CRON 表达式是否存在
35 if (StrUtil.isBlank(trigger.getCronExpression())) { 36 if (StrUtil.isBlank(trigger.getCronExpression())) {
36 - IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "定时触发器缺少 CRON 表达式"); 37 + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
37 return false; 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 return false; 45 return false;
47 } 46 }
48 47
49 - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger); 48 + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
50 return true; 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 @Override 52 @Override
72 public int getPriority() { 53 public int getPriority() {
73 return 50; // 最低优先级,因为定时触发器不依赖消息 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,19 +173,6 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
173 } 173 }
174 174
175 @Test 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 void testGetSupportedTriggerTypes() { 176 void testGetSupportedTriggerTypes() {
190 // 1. 执行测试 177 // 1. 执行测试
191 var supportedTypes = matcherManager.getSupportedTriggerTypes(); 178 var supportedTypes = matcherManager.getSupportedTriggerTypes();