Commit 93aaffddfefad7ecc2039a2da448dcbe8cb7be96

Authored by puhui999
1 parent cfb5230c

feat:【IoT 物联网】新增场景规则触发器匹配子条件策略接口和实现

Showing 15 changed files with 905 additions and 338 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionLevelEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.rule;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * IoT 场景规则条件层级枚举
  8 + * <p>
  9 + * 用于区分主条件(触发器级别)和子条件(条件分组级别)
  10 + *
  11 + * @author HUIHUI
  12 + */
  13 +@AllArgsConstructor
  14 +@Getter
  15 +public enum IotSceneRuleConditionLevelEnum {
  16 +
  17 + /**
  18 + * 主条件 - 触发器级别的条件
  19 + * 用于判断触发器本身是否匹配(如消息类型、设备标识等)
  20 + */
  21 + PRIMARY(1, "主条件"),
  22 +
  23 + /**
  24 + * 子条件 - 条件分组级别的条件
  25 + * 用于判断具体的业务条件(如设备状态、属性值、时间条件等)
  26 + */
  27 + SECONDARY(2, "子条件");
  28 +
  29 + /**
  30 + * 条件层级
  31 + */
  32 + private final Integer level;
  33 +
  34 + /**
  35 + * 条件层级名称
  36 + */
  37 + private final String name;
  38 +
  39 + /**
  40 + * 根据层级值获取枚举
  41 + *
  42 + * @param level 层级值
  43 + * @return 条件层级枚举
  44 + */
  45 + public static IotSceneRuleConditionLevelEnum levelOf(Integer level) {
  46 + if (level == null) {
  47 + return null;
  48 + }
  49 + for (IotSceneRuleConditionLevelEnum levelEnum : values()) {
  50 + if (levelEnum.getLevel().equals(level)) {
  51 + return levelEnum;
  52 + }
  53 + }
  54 + return null;
  55 + }
  56 +
  57 + /**
  58 + * 判断是否为主条件
  59 + *
  60 + * @return 是否为主条件
  61 + */
  62 + public boolean isPrimary() {
  63 + return this == PRIMARY;
  64 + }
  65 +
  66 + /**
  67 + * 判断是否为子条件
  68 + *
  69 + * @return 是否为子条件
  70 + */
  71 + public boolean isSecondary() {
  72 + return this == SECONDARY;
  73 + }
  74 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java
... ... @@ -19,19 +19,17 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
19 19 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
20 20 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
21 21 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
22   -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
23 22 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
24 23 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
25 24 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
26 25 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
27 26 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
28   -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
29 27 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
30 28 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
31 29 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
32 30 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
33 31 import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
34   -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleTriggerMatcherManager;
  32 +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager;
35 33 import jakarta.annotation.Resource;
36 34 import lombok.extern.slf4j.Slf4j;
37 35 import org.springframework.stereotype.Service;
... ... @@ -67,7 +65,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
67 65 @Resource
68 66 private IotDeviceService deviceService;
69 67 @Resource
70   - private IotSceneRuleTriggerMatcherManager triggerMatcherManager;
  68 + private IotSceneRuleMatcherManager matcherManager;
71 69 @Resource
72 70 private List<IotSceneRuleAction> sceneRuleActions;
73 71  
... ... @@ -285,7 +283,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
285 283 private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {
286 284 try {
287 285 // 2. 检查触发器的条件分组
288   - return triggerMatcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);
  286 + return matcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);
289 287 } catch (Exception e) {
290 288 log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}",
291 289 sceneRule.getId(), trigger.getType(), message, e);
... ... @@ -333,7 +331,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
333 331 }
334 332  
335 333 /**
336   - * 基于消息,判断触发器的条件是否匹配
  334 + * 基于消息,判断触发器的子条件是否匹配
337 335 *
338 336 * @param message 设备消息
339 337 * @param condition 触发条件
... ... @@ -344,21 +342,8 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
344 342 private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
345 343 IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
346 344 try {
347   - // 1. 根据条件类型进行匹配
348   - if (IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType().equals(condition.getType())) {
349   - // 设备状态条件匹配
350   - return matchDeviceStateCondition(message, condition);
351   - } else if (IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType().equals(condition.getType())) {
352   - // 设备属性条件匹配
353   - return matchDevicePropertyCondition(message, condition);
354   - } else if (IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType().equals(condition.getType())) {
355   - // 当前时间条件匹配
356   - return matchCurrentTimeCondition(condition);
357   - } else {
358   - log.warn("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 存在未知的条件类型({})]",
359   - sceneRule.getId(), trigger, condition.getType());
360   - return false;
361   - }
  345 + // 使用重构后的条件匹配管理器进行匹配
  346 + return matcherManager.isConditionMatched(message, condition);
362 347 } catch (Exception e) {
363 348 log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]",
364 349 sceneRule.getId(), trigger, e);
... ... @@ -366,105 +351,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
366 351 }
367 352 }
368 353  
369   - /**
370   - * 匹配设备状态条件
371   - *
372   - * @param message 设备消息
373   - * @param condition 触发条件
374   - * @return 是否匹配
375   - */
376   - private boolean matchDeviceStateCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
377   - // TODO @芋艿:需要根据设备状态进行匹配
378   - // 这里需要检查消息中的设备状态是否符合条件中定义的状态
379   - log.debug("[matchDeviceStateCondition][设备状态条件匹配逻辑待实现] condition: {}", condition);
380   - return false;
381   - }
382   -
383   - /**
384   - * 匹配设备属性条件
385   - *
386   - * @param message 设备消息
387   - * @param condition 触发条件
388   - * @return 是否匹配
389   - */
390   - private boolean matchDevicePropertyCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
391   - // 1. 检查标识符是否匹配
392   - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
393   - if (StrUtil.isBlank(condition.getIdentifier()) || !condition.getIdentifier().equals(messageIdentifier)) {
394   - return false;
395   - }
396   -
397   - // 2. 获取消息中的属性值
398   - Object messageValue = message.getData();
399   - if (messageValue == null) {
400   - return false;
401   - }
402   -
403   - // 3. 根据操作符进行匹配
404   - return evaluateCondition(messageValue, condition.getOperator(), condition.getParam());
405   - }
406   -
407   - /**
408   - * 匹配当前时间条件
409   - *
410   - * @param condition 触发条件
411   - * @return 是否匹配
412   - */
413   - private boolean matchCurrentTimeCondition(IotSceneRuleDO.TriggerCondition condition) {
414   - // TODO @芋艿:需要根据当前时间进行匹配
415   - // 这里需要检查当前时间是否符合条件中定义的时间范围
416   - log.debug("[matchCurrentTimeCondition][当前时间条件匹配逻辑待实现] condition: {}", condition);
417   - return false;
418   - }
419   -
420   - /**
421   - * 评估条件是否匹配
422   - *
423   - * @param sourceValue 源值(来自消息)
424   - * @param operator 操作符
425   - * @param paramValue 参数值(来自条件配置)
426   - * @return 是否匹配
427   - */
428   - private boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
429   - try {
430   - // 1. 校验操作符是否合法
431   - IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
432   - if (operatorEnum == null) {
433   - log.warn("[evaluateCondition][存在错误的操作符({})]", operator);
434   - return false;
435   - }
436   -
437   - // 2.1 构建 Spring 表达式的变量
438   - Map<String, Object> springExpressionVariables = MapUtil.<String, Object>builder()
439   - .put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue)
440   - .build();
441   - // 2.2 根据操作符类型处理参数值
442   - if (StrUtil.isNotBlank(paramValue)) {
443   - // TODO @puhui999:这里是不是在 IotSceneRuleConditionOperatorEnum 加个属性;
444   - if (operatorEnum == IotSceneRuleConditionOperatorEnum.IN
445   - || operatorEnum == IotSceneRuleConditionOperatorEnum.NOT_IN
446   - || operatorEnum == IotSceneRuleConditionOperatorEnum.BETWEEN
447   - || operatorEnum == IotSceneRuleConditionOperatorEnum.NOT_BETWEEN) {
448   - // 处理多值情况
449   - List<String> paramValues = StrUtil.split(paramValue, CharPool.COMMA);
450   - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
451   - convertList(paramValues, NumberUtil::parseDouble));
452   - } else {
453   - // 处理单值情况
454   - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
455   - NumberUtil.parseDouble(paramValue));
456   - }
457   - }
458   -
459   - // 3. 计算 Spring 表达式
460   - return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
461   - } catch (Exception e) {
462   - log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}",
463   - sourceValue, operator, paramValue, e);
464   - return false;
465   - }
466   - }
467   -
468 354 // TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。
469 355  
470 356 /**
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleTriggerMatcher.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleMatcher.java
... ... @@ -15,14 +15,14 @@ import java.util.Map;
15 15 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
16 16  
17 17 /**
18   - * IoT 场景规则触发器匹配器抽象基类
  18 + * IoT 场景规则匹配器抽象基类
19 19 * <p>
20   - * 提供通用的条件评估逻辑和工具方法
  20 + * 提供通用的条件评估逻辑和工具方法,支持触发器和条件两种匹配类型
21 21 *
22 22 * @author HUIHUI
23 23 */
24 24 @Slf4j
25   -public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRuleTriggerMatcher {
  25 +public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher {
26 26  
27 27 /**
28 28 * 评估条件是否匹配
... ... @@ -68,6 +68,8 @@ public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRule
68 68 }
69 69 }
70 70  
  71 + // ========== 触发器相关工具方法 ==========
  72 +
71 73 /**
72 74 * 检查基础触发器参数是否有效
73 75 *
... ... @@ -79,45 +81,90 @@ public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRule
79 81 }
80 82  
81 83 /**
82   - * 检查操作符和值是否有效
  84 + * 检查触发器操作符和值是否有效
83 85 *
84 86 * @param trigger 触发器配置
85 87 * @return 是否有效
86 88 */
87   - protected boolean isOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
  89 + protected boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
88 90 return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());
89 91 }
90 92  
91 93 /**
92   - * 检查标识符是否匹配
93   - *
94   - * @param expectedIdentifier 期望的标识符
95   - * @param actualIdentifier 实际的标识符
96   - * @return 是否匹配
97   - */
98   - protected boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) {
99   - return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
100   - }
101   -
102   - /**
103   - * 记录匹配成功日志
  94 + * 记录触发器匹配成功日志
104 95 *
105 96 * @param message 设备消息
106 97 * @param trigger 触发器配置
107 98 */
108   - protected void logMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  99 + protected void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
109 100 log.debug("[{}][消息({}) 匹配触发器({}) 成功]", getMatcherName(), message.getRequestId(), trigger.getType());
110 101 }
111 102  
112 103 /**
113   - * 记录匹配失败日志
  104 + * 记录触发器匹配失败日志
114 105 *
115 106 * @param message 设备消息
116 107 * @param trigger 触发器配置
117 108 * @param reason 失败原因
118 109 */
119   - protected void logMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
  110 + protected void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
120 111 log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", getMatcherName(), message.getRequestId(), trigger.getType(), reason);
121 112 }
122 113  
  114 + // ========== 条件相关工具方法 ==========
  115 +
  116 + /**
  117 + * 检查基础条件参数是否有效
  118 + *
  119 + * @param condition 触发条件
  120 + * @return 是否有效
  121 + */
  122 + protected boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) {
  123 + return condition != null && condition.getType() != null;
  124 + }
  125 +
  126 + /**
  127 + * 检查条件操作符和参数是否有效
  128 + *
  129 + * @param condition 触发条件
  130 + * @return 是否有效
  131 + */
  132 + protected boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) {
  133 + return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam());
  134 + }
  135 +
  136 + /**
  137 + * 记录条件匹配成功日志
  138 + *
  139 + * @param message 设备消息
  140 + * @param condition 触发条件
  141 + */
  142 + protected void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  143 + log.debug("[{}][消息({}) 匹配条件({}) 成功]", getMatcherName(), message.getRequestId(), condition.getType());
  144 + }
  145 +
  146 + /**
  147 + * 记录条件匹配失败日志
  148 + *
  149 + * @param message 设备消息
  150 + * @param condition 触发条件
  151 + * @param reason 失败原因
  152 + */
  153 + protected void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
  154 + log.debug("[{}][消息({}) 匹配条件({}) 失败: {}]", getMatcherName(), message.getRequestId(), condition.getType(), reason);
  155 + }
  156 +
  157 + // ========== 通用工具方法 ==========
  158 +
  159 + /**
  160 + * 检查标识符是否匹配
  161 + *
  162 + * @param expectedIdentifier 期望的标识符
  163 + * @param actualIdentifier 实际的标识符
  164 + * @return 是否匹配
  165 + */
  166 + protected boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) {
  167 + return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
  168 + }
  169 +
123 170 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionLevelEnum;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +import java.time.LocalDateTime;
  12 +import java.time.LocalTime;
  13 +import java.time.format.DateTimeFormatter;
  14 +
  15 +/**
  16 + * 当前时间条件匹配器
  17 + * <p>
  18 + * 处理时间相关的子条件匹配逻辑
  19 + *
  20 + * @author HUIHUI
  21 + */
  22 +@Component
  23 +@Slf4j
  24 +public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher {
  25 +
  26 + /**
  27 + * 时间格式化器 - HH:mm:ss
  28 + */
  29 + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
  30 +
  31 + /**
  32 + * 时间格式化器 - HH:mm
  33 + */
  34 + private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern("HH:mm");
  35 +
  36 + @Override
  37 + public MatcherType getMatcherType() {
  38 + return MatcherType.CONDITION;
  39 + }
  40 +
  41 + @Override
  42 + public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  43 + return IotSceneRuleConditionTypeEnum.CURRENT_TIME;
  44 + }
  45 +
  46 + @Override
  47 + public IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
  48 + return IotSceneRuleConditionLevelEnum.SECONDARY;
  49 + }
  50 +
  51 + @Override
  52 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  53 + // 1. 基础参数校验
  54 + if (!isBasicConditionValid(condition)) {
  55 + logConditionMatchFailure(message, condition, "条件基础参数无效");
  56 + return false;
  57 + }
  58 +
  59 + // 2. 检查操作符和参数是否有效
  60 + if (!isConditionOperatorAndParamValid(condition)) {
  61 + logConditionMatchFailure(message, condition, "操作符或参数无效");
  62 + return false;
  63 + }
  64 +
  65 + // 3. 获取当前时间
  66 + LocalDateTime now = LocalDateTime.now();
  67 +
  68 + // 4. 根据操作符类型进行不同的时间匹配
  69 + String operator = condition.getOperator();
  70 + String param = condition.getParam();
  71 +
  72 + boolean matched = false;
  73 +
  74 + try {
  75 + if (operator.startsWith("date_time_")) {
  76 + // 日期时间匹配(时间戳)
  77 + matched = matchDateTime(now, operator, param);
  78 + } else if (operator.startsWith("time_")) {
  79 + // 当日时间匹配(HH:mm:ss)
  80 + matched = matchTime(now.toLocalTime(), operator, param);
  81 + } else {
  82 + // 其他操作符,使用通用条件评估器
  83 + matched = evaluateCondition(now.toEpochSecond(java.time.ZoneOffset.of("+8")), operator, param);
  84 + }
  85 +
  86 + if (matched) {
  87 + logConditionMatchSuccess(message, condition);
  88 + } else {
  89 + logConditionMatchFailure(message, condition, "时间条件不匹配");
  90 + }
  91 +
  92 + } catch (Exception e) {
  93 + log.error("[CurrentTimeConditionMatcher][时间条件匹配异常] operator: {}, param: {}", operator, param, e);
  94 + logConditionMatchFailure(message, condition, "时间条件匹配异常: " + e.getMessage());
  95 + matched = false;
  96 + }
  97 +
  98 + return matched;
  99 + }
  100 +
  101 + /**
  102 + * 匹配日期时间(时间戳)
  103 + */
  104 + private boolean matchDateTime(LocalDateTime now, String operator, String param) {
  105 + long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8"));
  106 + return evaluateCondition(currentTimestamp, operator.substring("date_time_".length()), param);
  107 + }
  108 +
  109 + /**
  110 + * 匹配当日时间(HH:mm:ss)
  111 + */
  112 + private boolean matchTime(LocalTime currentTime, String operator, String param) {
  113 + try {
  114 + String actualOperator = operator.substring("time_".length());
  115 +
  116 + if ("between".equals(actualOperator)) {
  117 + // 时间区间匹配
  118 + String[] timeRange = param.split(",");
  119 + if (timeRange.length != 2) {
  120 + return false;
  121 + }
  122 +
  123 + LocalTime startTime = parseTime(timeRange[0].trim());
  124 + LocalTime endTime = parseTime(timeRange[1].trim());
  125 +
  126 + return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime);
  127 + } else {
  128 + // 单个时间比较
  129 + LocalTime targetTime = parseTime(param);
  130 +
  131 + switch (actualOperator) {
  132 + case ">":
  133 + return currentTime.isAfter(targetTime);
  134 + case "<":
  135 + return currentTime.isBefore(targetTime);
  136 + case ">=":
  137 + return !currentTime.isBefore(targetTime);
  138 + case "<=":
  139 + return !currentTime.isAfter(targetTime);
  140 + case "=":
  141 + return currentTime.equals(targetTime);
  142 + default:
  143 + return false;
  144 + }
  145 + }
  146 + } catch (Exception e) {
  147 + log.error("[CurrentTimeConditionMatcher][时间解析异常] param: {}", param, e);
  148 + return false;
  149 + }
  150 + }
  151 +
  152 + /**
  153 + * 解析时间字符串
  154 + */
  155 + private LocalTime parseTime(String timeStr) {
  156 + if (StrUtil.isBlank(timeStr)) {
  157 + throw new IllegalArgumentException("时间字符串不能为空");
  158 + }
  159 +
  160 + // 尝试不同的时间格式
  161 + try {
  162 + if (timeStr.length() == 5) { // HH:mm
  163 + return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT);
  164 + } else { // HH:mm:ss
  165 + return LocalTime.parse(timeStr, TIME_FORMATTER);
  166 + }
  167 + } catch (Exception e) {
  168 + throw new IllegalArgumentException("时间格式无效: " + timeStr, e);
  169 + }
  170 + }
  171 +
  172 + @Override
  173 + public int getPriority() {
  174 + return 40; // 较低优先级
  175 + }
  176 +
  177 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceEventPostTriggerMatcher.java
... ... @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component;
16 16 * @author HUIHUI
17 17 */
18 18 @Component
19   -public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
  19 +public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleMatcher {
20 20  
21 21 /**
22 22 * 设备事件上报消息方法
... ... @@ -24,6 +24,11 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
24 24 private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod();
25 25  
26 26 @Override
  27 + public MatcherType getMatcherType() {
  28 + return MatcherType.TRIGGER;
  29 + }
  30 +
  31 + @Override
27 32 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
28 33 return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
29 34 }
... ... @@ -32,20 +37,20 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
32 37 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
33 38 // 1. 基础参数校验
34 39 if (!isBasicTriggerValid(trigger)) {
35   - logMatchFailure(message, trigger, "触发器基础参数无效");
  40 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
36 41 return false;
37 42 }
38 43  
39 44 // 2. 检查消息方法是否匹配
40 45 if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) {
41   - logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod());
  46 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod());
42 47 return false;
43 48 }
44 49  
45 50 // 3. 检查标识符是否匹配
46 51 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
47 52 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
48   - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
  53 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
49 54 return false;
50 55 }
51 56  
... ... @@ -54,18 +59,18 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
54 59 if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
55 60 Object eventData = message.getData();
56 61 if (eventData == null) {
57   - logMatchFailure(message, trigger, "消息中事件数据为空");
  62 + logTriggerMatchFailure(message, trigger, "消息中事件数据为空");
58 63 return false;
59 64 }
60 65  
61 66 boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
62 67 if (!matched) {
63   - logMatchFailure(message, trigger, "事件数据条件不匹配");
  68 + logTriggerMatchFailure(message, trigger, "事件数据条件不匹配");
64 69 return false;
65 70 }
66 71 }
67 72  
68   - logMatchSuccess(message, trigger);
  73 + logTriggerMatchSuccess(message, trigger);
69 74 return true;
70 75 }
71 76  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionLevelEnum;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +/**
  11 + * 设备属性条件匹配器
  12 + * <p>
  13 + * 处理设备属性相关的子条件匹配逻辑
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Component
  18 +public class DevicePropertyConditionMatcher extends AbstractIotSceneRuleMatcher {
  19 +
  20 + @Override
  21 + public MatcherType getMatcherType() {
  22 + return MatcherType.CONDITION;
  23 + }
  24 +
  25 + @Override
  26 + public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  27 + return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY;
  28 + }
  29 +
  30 + @Override
  31 + public IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
  32 + return IotSceneRuleConditionLevelEnum.SECONDARY;
  33 + }
  34 +
  35 + @Override
  36 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  37 + // 1. 基础参数校验
  38 + if (!isBasicConditionValid(condition)) {
  39 + logConditionMatchFailure(message, condition, "条件基础参数无效");
  40 + return false;
  41 + }
  42 +
  43 + // 2. 检查标识符是否匹配
  44 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
  45 + if (!isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) {
  46 + logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier);
  47 + return false;
  48 + }
  49 +
  50 + // 3. 检查操作符和参数是否有效
  51 + if (!isConditionOperatorAndParamValid(condition)) {
  52 + logConditionMatchFailure(message, condition, "操作符或参数无效");
  53 + return false;
  54 + }
  55 +
  56 + // 4. 获取属性值
  57 + Object propertyValue = message.getData();
  58 + if (propertyValue == null) {
  59 + logConditionMatchFailure(message, condition, "消息中属性值为空");
  60 + return false;
  61 + }
  62 +
  63 + // 5. 使用条件评估器进行匹配
  64 + boolean matched = evaluateCondition(propertyValue, condition.getOperator(), condition.getParam());
  65 +
  66 + if (matched) {
  67 + logConditionMatchSuccess(message, condition);
  68 + } else {
  69 + logConditionMatchFailure(message, condition, "设备属性条件不匹配");
  70 + }
  71 +
  72 + return matched;
  73 + }
  74 +
  75 + @Override
  76 + public int getPriority() {
  77 + return 25; // 中等优先级
  78 + }
  79 +
  80 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java
... ... @@ -15,7 +15,7 @@ import org.springframework.stereotype.Component;
15 15 * @author HUIHUI
16 16 */
17 17 @Component
18   -public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
  18 +public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleMatcher {
19 19  
20 20 /**
21 21 * 设备属性上报消息方法
... ... @@ -23,6 +23,11 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
23 23 private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod();
24 24  
25 25 @Override
  26 + public MatcherType getMatcherType() {
  27 + return MatcherType.TRIGGER;
  28 + }
  29 +
  30 + @Override
26 31 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
27 32 return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
28 33 }
... ... @@ -31,33 +36,33 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
31 36 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
32 37 // 1. 基础参数校验
33 38 if (!isBasicTriggerValid(trigger)) {
34   - logMatchFailure(message, trigger, "触发器基础参数无效");
  39 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
35 40 return false;
36 41 }
37 42  
38 43 // 2. 检查消息方法是否匹配
39 44 if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) {
40   - logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod());
  45 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod());
41 46 return false;
42 47 }
43 48  
44 49 // 3. 检查标识符是否匹配
45 50 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
46 51 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
47   - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
  52 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
48 53 return false;
49 54 }
50 55  
51 56 // 4. 检查操作符和值是否有效
52   - if (!isOperatorAndValueValid(trigger)) {
53   - logMatchFailure(message, trigger, "操作符或值无效");
  57 + if (!isTriggerOperatorAndValueValid(trigger)) {
  58 + logTriggerMatchFailure(message, trigger, "操作符或值无效");
54 59 return false;
55 60 }
56 61  
57 62 // 5. 获取属性值
58 63 Object propertyValue = message.getData();
59 64 if (propertyValue == null) {
60   - logMatchFailure(message, trigger, "消息中属性值为空");
  65 + logTriggerMatchFailure(message, trigger, "消息中属性值为空");
61 66 return false;
62 67 }
63 68  
... ... @@ -65,9 +70,9 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
65 70 boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
66 71  
67 72 if (matched) {
68   - logMatchSuccess(message, trigger);
  73 + logTriggerMatchSuccess(message, trigger);
69 74 } else {
70   - logMatchFailure(message, trigger, "属性值条件不匹配");
  75 + logTriggerMatchFailure(message, trigger, "属性值条件不匹配");
71 76 }
72 77  
73 78 return matched;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceServiceInvokeTriggerMatcher.java
... ... @@ -15,7 +15,7 @@ import org.springframework.stereotype.Component;
15 15 * @author HUIHUI
16 16 */
17 17 @Component
18   -public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
  18 +public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleMatcher {
19 19  
20 20 /**
21 21 * 设备服务调用消息方法
... ... @@ -23,6 +23,11 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg
23 23 private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod();
24 24  
25 25 @Override
  26 + public MatcherType getMatcherType() {
  27 + return MatcherType.TRIGGER;
  28 + }
  29 +
  30 + @Override
26 31 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
27 32 return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
28 33 }
... ... @@ -31,27 +36,27 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg
31 36 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
32 37 // 1. 基础参数校验
33 38 if (!isBasicTriggerValid(trigger)) {
34   - logMatchFailure(message, trigger, "触发器基础参数无效");
  39 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
35 40 return false;
36 41 }
37 42  
38 43 // 2. 检查消息方法是否匹配
39 44 if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) {
40   - logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod());
  45 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod());
41 46 return false;
42 47 }
43 48  
44 49 // 3. 检查标识符是否匹配
45 50 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
46 51 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
47   - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
  52 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
48 53 return false;
49 54 }
50 55  
51 56 // 4. 对于服务调用触发器,通常只需要匹配服务标识符即可
52 57 // 不需要检查操作符和值,因为服务调用本身就是触发条件
53 58  
54   - logMatchSuccess(message, trigger);
  59 + logTriggerMatchSuccess(message, trigger);
55 60 return true;
56 61 }
57 62  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionLevelEnum;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 设备状态条件匹配器
  11 + * <p>
  12 + * 处理设备状态相关的子条件匹配逻辑
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Component
  17 +public class DeviceStateConditionMatcher extends AbstractIotSceneRuleMatcher {
  18 +
  19 + @Override
  20 + public MatcherType getMatcherType() {
  21 + return MatcherType.CONDITION;
  22 + }
  23 +
  24 + @Override
  25 + public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  26 + return IotSceneRuleConditionTypeEnum.DEVICE_STATE;
  27 + }
  28 +
  29 + @Override
  30 + public IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
  31 + return IotSceneRuleConditionLevelEnum.SECONDARY;
  32 + }
  33 +
  34 + @Override
  35 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  36 + // 1. 基础参数校验
  37 + if (!isBasicConditionValid(condition)) {
  38 + logConditionMatchFailure(message, condition, "条件基础参数无效");
  39 + return false;
  40 + }
  41 +
  42 + // 2. 检查操作符和参数是否有效
  43 + if (!isConditionOperatorAndParamValid(condition)) {
  44 + logConditionMatchFailure(message, condition, "操作符或参数无效");
  45 + return false;
  46 + }
  47 +
  48 + // 3. 获取设备状态值
  49 + // 设备状态通常在消息的 data 字段中
  50 + Object stateValue = message.getData();
  51 + if (stateValue == null) {
  52 + logConditionMatchFailure(message, condition, "消息中设备状态值为空");
  53 + return false;
  54 + }
  55 +
  56 + // 4. 使用条件评估器进行匹配
  57 + boolean matched = evaluateCondition(stateValue, condition.getOperator(), condition.getParam());
  58 +
  59 + if (matched) {
  60 + logConditionMatchSuccess(message, condition);
  61 + } else {
  62 + logConditionMatchFailure(message, condition, "设备状态条件不匹配");
  63 + }
  64 +
  65 + return matched;
  66 + }
  67 +
  68 + @Override
  69 + public int getPriority() {
  70 + return 30; // 中等优先级
  71 + }
  72 +
  73 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java
... ... @@ -14,7 +14,7 @@ import org.springframework.stereotype.Component;
14 14 * @author HUIHUI
15 15 */
16 16 @Component
17   -public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
  17 +public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleMatcher {
18 18  
19 19 /**
20 20 * 设备状态更新消息方法
... ... @@ -22,6 +22,11 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
22 22 private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod();
23 23  
24 24 @Override
  25 + public MatcherType getMatcherType() {
  26 + return MatcherType.TRIGGER;
  27 + }
  28 +
  29 + @Override
25 30 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
26 31 return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
27 32 }
... ... @@ -30,26 +35,26 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
30 35 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
31 36 // 1. 基础参数校验
32 37 if (!isBasicTriggerValid(trigger)) {
33   - logMatchFailure(message, trigger, "触发器基础参数无效");
  38 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
34 39 return false;
35 40 }
36 41  
37 42 // 2. 检查消息方法是否匹配
38 43 if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) {
39   - logMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod());
  44 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod());
40 45 return false;
41 46 }
42 47  
43 48 // 3. 检查操作符和值是否有效
44   - if (!isOperatorAndValueValid(trigger)) {
45   - logMatchFailure(message, trigger, "操作符或值无效");
  49 + if (!isTriggerOperatorAndValueValid(trigger)) {
  50 + logTriggerMatchFailure(message, trigger, "操作符或值无效");
46 51 return false;
47 52 }
48 53  
49 54 // 4. 获取设备状态值
50 55 Object stateValue = message.getData();
51 56 if (stateValue == null) {
52   - logMatchFailure(message, trigger, "消息中设备状态值为空");
  57 + logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
53 58 return false;
54 59 }
55 60  
... ... @@ -57,9 +62,9 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
57 62 boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
58 63  
59 64 if (matched) {
60   - logMatchSuccess(message, trigger);
  65 + logTriggerMatchSuccess(message, trigger);
61 66 } else {
62   - logMatchFailure(message, trigger, "状态值条件不匹配");
  67 + logTriggerMatchFailure(message, trigger, "状态值条件不匹配");
63 68 }
64 69  
65 70 return matched;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcher.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java
... ... @@ -2,37 +2,97 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
2 2  
3 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionLevelEnum;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
5 7 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
6 8  
7 9 /**
8   - * IoT 场景规则触发器匹配策略接口
  10 + * IoT 场景规则匹配器统一接口
9 11 * <p>
10   - * 用于实现不同类型触发器的匹配逻辑,遵循策略模式设计
  12 + * 支持触发器匹配和条件匹配两种类型,遵循策略模式设计
  13 + * <p>
  14 + * 匹配器类型说明:
  15 + * - 触发器匹配器:用于匹配主触发条件(如设备消息类型、定时器等)
  16 + * - 条件匹配器:用于匹配子条件(如设备状态、属性值、时间条件等)
11 17 *
12 18 * @author HUIHUI
13 19 */
14   -public interface IotSceneRuleTriggerMatcher {
  20 +public interface IotSceneRuleMatcher {
  21 +
  22 + /**
  23 + * 匹配器类型枚举
  24 + */
  25 + enum MatcherType {
  26 + /**
  27 + * 触发器匹配器 - 用于匹配主触发条件
  28 + */
  29 + TRIGGER,
  30 + /**
  31 + * 条件匹配器 - 用于匹配子条件
  32 + */
  33 + CONDITION
  34 + }
  35 +
  36 + /**
  37 + * 获取匹配器类型
  38 + *
  39 + * @return 匹配器类型
  40 + */
  41 + MatcherType getMatcherType();
15 42  
16 43 /**
17   - * 获取支持的触发器类型
  44 + * 获取支持的触发器类型(仅触发器匹配器需要实现)
18 45 *
19   - * @return 触发器类型枚举
  46 + * @return 触发器类型枚举,条件匹配器返回 null
20 47 */
21   - IotSceneRuleTriggerTypeEnum getSupportedTriggerType();
  48 + default IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  49 + return null;
  50 + }
22 51  
23 52 /**
24   - * 检查触发器是否匹配消息
  53 + * 获取支持的条件类型(仅条件匹配器需要实现)
  54 + *
  55 + * @return 条件类型枚举,触发器匹配器返回 null
  56 + */
  57 + default IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  58 + return null;
  59 + }
  60 +
  61 + /**
  62 + * 获取支持的条件层级(仅条件匹配器需要实现)
  63 + *
  64 + * @return 条件层级枚举,触发器匹配器返回 null
  65 + */
  66 + default IotSceneRuleConditionLevelEnum getSupportedConditionLevel() {
  67 + return null;
  68 + }
  69 +
  70 + /**
  71 + * 检查触发器是否匹配消息(仅触发器匹配器需要实现)
25 72 *
26 73 * @param message 设备消息
27 74 * @param trigger 触发器配置
28 75 * @return 是否匹配
29 76 */
30   - boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger);
  77 + default boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  78 + throw new UnsupportedOperationException("触发器匹配方法仅支持触发器匹配器");
  79 + }
  80 +
  81 + /**
  82 + * 检查条件是否匹配消息(仅条件匹配器需要实现)
  83 + *
  84 + * @param message 设备消息
  85 + * @param condition 触发条件
  86 + * @return 是否匹配
  87 + */
  88 + default boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  89 + throw new UnsupportedOperationException("条件匹配方法仅支持条件匹配器");
  90 + }
31 91  
32 92 /**
33 93 * 获取匹配优先级(数值越小优先级越高)
34 94 * <p>
35   - * 用于在多个匹配器支持同一触发器类型时确定优先级
  95 + * 用于在多个匹配器支持同一类型时确定优先级
36 96 *
37 97 * @return 优先级数值
38 98 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionLevelEnum;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +import java.util.*;
  13 +import java.util.function.Function;
  14 +import java.util.stream.Collectors;
  15 +
  16 +import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum;
  17 +
  18 +/**
  19 + * IoT 场景规则匹配器统一管理器
  20 + * <p>
  21 + * 负责管理所有匹配器(触发器匹配器和条件匹配器),并提供统一的匹配入口
  22 + *
  23 + * @author HUIHUI
  24 + */
  25 +@Component
  26 +@Slf4j
  27 +public class IotSceneRuleMatcherManager {
  28 +
  29 + /**
  30 + * 触发器匹配器映射表
  31 + * Key: 触发器类型枚举
  32 + * Value: 对应的匹配器实例
  33 + */
  34 + private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleMatcher> triggerMatcherMap;
  35 +
  36 + /**
  37 + * 条件匹配器映射表
  38 + * Key: 条件类型枚举
  39 + * Value: 对应的匹配器实例
  40 + */
  41 + private final Map<IotSceneRuleConditionTypeEnum, IotSceneRuleMatcher> conditionMatcherMap;
  42 +
  43 + /**
  44 + * 所有匹配器列表(按优先级排序)
  45 + */
  46 + private final List<IotSceneRuleMatcher> allMatchers;
  47 +
  48 + public IotSceneRuleMatcherManager(List<IotSceneRuleMatcher> matchers) {
  49 + if (CollUtil.isEmpty(matchers)) {
  50 + log.warn("[IotSceneRuleMatcherManager][没有找到任何匹配器]");
  51 + this.triggerMatcherMap = new HashMap<>();
  52 + this.conditionMatcherMap = new HashMap<>();
  53 + this.allMatchers = new ArrayList<>();
  54 + return;
  55 + }
  56 +
  57 + // 按优先级排序并过滤启用的匹配器
  58 + this.allMatchers = matchers.stream()
  59 + .filter(IotSceneRuleMatcher::isEnabled)
  60 + .sorted(Comparator.comparing(IotSceneRuleMatcher::getPriority))
  61 + .collect(Collectors.toList());
  62 +
  63 + // 分离触发器匹配器和条件匹配器
  64 + List<IotSceneRuleMatcher> triggerMatchers = this.allMatchers.stream()
  65 + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.TRIGGER)
  66 + .toList();
  67 +
  68 + List<IotSceneRuleMatcher> conditionMatchers = this.allMatchers.stream()
  69 + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
  70 + .toList();
  71 +
  72 + // 构建触发器匹配器映射表
  73 + this.triggerMatcherMap = triggerMatchers.stream()
  74 + .collect(Collectors.toMap(
  75 + IotSceneRuleMatcher::getSupportedTriggerType,
  76 + Function.identity(),
  77 + (existing, replacement) -> {
  78 + log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
  79 + existing.getSupportedTriggerType(),
  80 + existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
  81 + return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
  82 + },
  83 + LinkedHashMap::new
  84 + ));
  85 +
  86 + // 构建条件匹配器映射表
  87 + this.conditionMatcherMap = conditionMatchers.stream()
  88 + .collect(Collectors.toMap(
  89 + IotSceneRuleMatcher::getSupportedConditionType,
  90 + Function.identity(),
  91 + (existing, replacement) -> {
  92 + log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]",
  93 + existing.getSupportedConditionType(),
  94 + existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
  95 + return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
  96 + },
  97 + LinkedHashMap::new
  98 + ));
  99 +
  100 + log.info("[IotSceneRuleMatcherManager][初始化完成,共加载 {} 个匹配器,其中触发器匹配器 {} 个,条件匹配器 {} 个]",
  101 + this.allMatchers.size(), this.triggerMatcherMap.size(), this.conditionMatcherMap.size());
  102 +
  103 + // 记录触发器匹配器详情
  104 + this.triggerMatcherMap.forEach((type, matcher) ->
  105 + log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}",
  106 + type, matcher.getMatcherName(), matcher.getPriority()));
  107 +
  108 + // 记录条件匹配器详情
  109 + this.conditionMatcherMap.forEach((type, matcher) ->
  110 + log.info("[IotSceneRuleMatcherManager][条件匹配器] 类型: {}, 匹配器: {}, 优先级: {}, 层级: {}",
  111 + type, matcher.getMatcherName(), matcher.getPriority(), matcher.getSupportedConditionLevel()));
  112 + }
  113 +
  114 + /**
  115 + * 检查触发器是否匹配消息(主条件匹配)
  116 + *
  117 + * @param message 设备消息
  118 + * @param trigger 触发器配置
  119 + * @return 是否匹配
  120 + */
  121 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  122 + if (message == null || trigger == null || trigger.getType() == null) {
  123 + log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger);
  124 + return false;
  125 + }
  126 +
  127 + // 根据触发器类型查找对应的匹配器
  128 + IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType());
  129 + if (triggerType == null) {
  130 + log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType());
  131 + return false;
  132 + }
  133 +
  134 + IotSceneRuleMatcher matcher = triggerMatcherMap.get(triggerType);
  135 + if (matcher == null) {
  136 + log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType);
  137 + return false;
  138 + }
  139 +
  140 + try {
  141 + return matcher.isMatched(message, trigger);
  142 + } catch (Exception e) {
  143 + log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}",
  144 + message, trigger, matcher.getMatcherName(), e);
  145 + return false;
  146 + }
  147 + }
  148 +
  149 + /**
  150 + * 检查子条件是否匹配消息
  151 + *
  152 + * @param message 设备消息
  153 + * @param condition 触发条件
  154 + * @return 是否匹配
  155 + */
  156 + public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  157 + if (message == null || condition == null || condition.getType() == null) {
  158 + log.debug("[isConditionMatched][参数无效] message: {}, condition: {}", message, condition);
  159 + return false;
  160 + }
  161 +
  162 + // 根据条件类型查找对应的匹配器
  163 + IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType());
  164 + if (conditionType == null) {
  165 + log.warn("[isConditionMatched][未知的条件类型: {}]", condition.getType());
  166 + return false;
  167 + }
  168 +
  169 + IotSceneRuleMatcher matcher = conditionMatcherMap.get(conditionType);
  170 + if (matcher == null) {
  171 + log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType);
  172 + return false;
  173 + }
  174 +
  175 + try {
  176 + return matcher.isMatched(message, condition);
  177 + } catch (Exception e) {
  178 + log.error("[isConditionMatched][条件匹配异常] message: {}, condition: {}, matcher: {}",
  179 + message, condition, matcher.getMatcherName(), e);
  180 + return false;
  181 + }
  182 + }
  183 +
  184 + /**
  185 + * 根据类型值查找条件类型枚举
  186 + *
  187 + * @param typeValue 类型值
  188 + * @return 条件类型枚举
  189 + */
  190 + private IotSceneRuleConditionTypeEnum findConditionTypeEnum(Integer typeValue) {
  191 + return Arrays.stream(IotSceneRuleConditionTypeEnum.values())
  192 + .filter(type -> type.getType().equals(typeValue))
  193 + .findFirst()
  194 + .orElse(null);
  195 + }
  196 +
  197 + /**
  198 + * 获取所有支持的触发器类型
  199 + *
  200 + * @return 支持的触发器类型列表
  201 + */
  202 + public Set<IotSceneRuleTriggerTypeEnum> getSupportedTriggerTypes() {
  203 + return new HashSet<>(triggerMatcherMap.keySet());
  204 + }
  205 +
  206 + /**
  207 + * 获取所有支持的条件类型
  208 + *
  209 + * @return 支持的条件类型列表
  210 + */
  211 + public Set<IotSceneRuleConditionTypeEnum> getSupportedConditionTypes() {
  212 + return new HashSet<>(conditionMatcherMap.keySet());
  213 + }
  214 +
  215 + /**
  216 + * 获取指定触发器类型的匹配器
  217 + *
  218 + * @param triggerType 触发器类型
  219 + * @return 匹配器实例,如果不存在则返回 null
  220 + */
  221 + public IotSceneRuleMatcher getTriggerMatcher(IotSceneRuleTriggerTypeEnum triggerType) {
  222 + return triggerMatcherMap.get(triggerType);
  223 + }
  224 +
  225 + /**
  226 + * 获取指定条件类型的匹配器
  227 + *
  228 + * @param conditionType 条件类型
  229 + * @return 匹配器实例,如果不存在则返回 null
  230 + */
  231 + public IotSceneRuleMatcher getConditionMatcher(IotSceneRuleConditionTypeEnum conditionType) {
  232 + return conditionMatcherMap.get(conditionType);
  233 + }
  234 +
  235 + /**
  236 + * 根据条件层级获取匹配器列表
  237 + *
  238 + * @param level 条件层级
  239 + * @return 匹配器列表
  240 + */
  241 + public List<IotSceneRuleMatcher> getMatchersByLevel(IotSceneRuleConditionLevelEnum level) {
  242 + return allMatchers.stream()
  243 + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
  244 + .filter(matcher -> matcher.getSupportedConditionLevel() == level)
  245 + .collect(Collectors.toList());
  246 + }
  247 +
  248 + /**
  249 + * 获取所有匹配器的统计信息
  250 + *
  251 + * @return 统计信息映射表
  252 + */
  253 + public Map<String, Object> getMatcherStatistics() {
  254 + Map<String, Object> statistics = new HashMap<>();
  255 + statistics.put("totalMatchers", allMatchers.size());
  256 + statistics.put("triggerMatchers", triggerMatcherMap.size());
  257 + statistics.put("conditionMatchers", conditionMatcherMap.size());
  258 + statistics.put("supportedTriggerTypes", getSupportedTriggerTypes());
  259 + statistics.put("supportedConditionTypes", getSupportedConditionTypes());
  260 +
  261 + // 按层级统计条件匹配器
  262 + Map<IotSceneRuleConditionLevelEnum, Long> levelStats = allMatchers.stream()
  263 + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
  264 + .collect(Collectors.groupingBy(
  265 + IotSceneRuleMatcher::getSupportedConditionLevel,
  266 + Collectors.counting()
  267 + ));
  268 + statistics.put("conditionLevelStatistics", levelStats);
  269 +
  270 + // 触发器匹配器详情
  271 + Map<String, Object> triggerMatcherDetails = new HashMap<>();
  272 + triggerMatcherMap.forEach((type, matcher) -> {
  273 + Map<String, Object> detail = new HashMap<>();
  274 + detail.put("matcherName", matcher.getMatcherName());
  275 + detail.put("priority", matcher.getPriority());
  276 + detail.put("enabled", matcher.isEnabled());
  277 + triggerMatcherDetails.put(type.name(), detail);
  278 + });
  279 + statistics.put("triggerMatcherDetails", triggerMatcherDetails);
  280 +
  281 + // 条件匹配器详情
  282 + Map<String, Object> conditionMatcherDetails = new HashMap<>();
  283 + conditionMatcherMap.forEach((type, matcher) -> {
  284 + Map<String, Object> detail = new HashMap<>();
  285 + detail.put("matcherName", matcher.getMatcherName());
  286 + detail.put("priority", matcher.getPriority());
  287 + detail.put("level", matcher.getSupportedConditionLevel());
  288 + detail.put("enabled", matcher.isEnabled());
  289 + conditionMatcherDetails.put(type.name(), detail);
  290 + });
  291 + statistics.put("conditionMatcherDetails", conditionMatcherDetails);
  292 +
  293 + return statistics;
  294 + }
  295 +
  296 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherManager.java deleted
1   -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
2   -
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
6   -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
7   -import lombok.extern.slf4j.Slf4j;
8   -import org.springframework.stereotype.Component;
9   -
10   -import java.util.*;
11   -import java.util.function.Function;
12   -import java.util.stream.Collectors;
13   -
14   -import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum;
15   -
16   -/**
17   - * IoT 场景规则触发器匹配管理器
18   - * <p>
19   - * 负责管理所有触发器匹配器,并提供统一的匹配入口
20   - *
21   - * @author HUIHUI
22   - */
23   -@Component
24   -@Slf4j
25   -public class IotSceneRuleTriggerMatcherManager {
26   -
27   - /**
28   - * 触发器匹配器映射表
29   - * Key: 触发器类型枚举
30   - * Value: 对应的匹配器实例
31   - */
32   - private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleTriggerMatcher> matcherMap;
33   -
34   - /**
35   - * 所有匹配器列表(按优先级排序)
36   - */
37   - private final List<IotSceneRuleTriggerMatcher> allMatchers;
38   -
39   - public IotSceneRuleTriggerMatcherManager(List<IotSceneRuleTriggerMatcher> matchers) {
40   - if (CollUtil.isEmpty(matchers)) {
41   - log.warn("[IotSceneRuleTriggerMatcherManager][没有找到任何触发器匹配器]");
42   - this.matcherMap = new HashMap<>();
43   - this.allMatchers = new ArrayList<>();
44   - return;
45   - }
46   -
47   - // 按优先级排序并过滤启用的匹配器
48   - this.allMatchers = matchers.stream()
49   - .filter(IotSceneRuleTriggerMatcher::isEnabled)
50   - .sorted(Comparator.comparing(IotSceneRuleTriggerMatcher::getPriority))
51   - .collect(Collectors.toList());
52   -
53   - // 构建匹配器映射表
54   - this.matcherMap = this.allMatchers.stream()
55   - .collect(Collectors.toMap(
56   - IotSceneRuleTriggerMatcher::getSupportedTriggerType,
57   - Function.identity(),
58   - (existing, replacement) -> {
59   - log.warn("[IotSceneRuleTriggerMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
60   - existing.getSupportedTriggerType(),
61   - existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
62   - return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
63   - },
64   - LinkedHashMap::new
65   - ));
66   -
67   - log.info("[IotSceneRuleTriggerMatcherManager][初始化完成,共加载 {} 个触发器匹配器]", this.matcherMap.size());
68   - this.matcherMap.forEach((type, matcher) ->
69   - log.info("[IotSceneRuleTriggerMatcherManager][触发器类型: {}, 匹配器: {}, 优先级: {}]",
70   - type, matcher.getMatcherName(), matcher.getPriority()));
71   - }
72   -
73   - /**
74   - * 检查触发器是否匹配消息
75   - *
76   - * @param message 设备消息
77   - * @param trigger 触发器配置
78   - * @return 是否匹配
79   - */
80   - public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
81   - if (message == null || trigger == null || trigger.getType() == null) {
82   - log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger);
83   - return false;
84   - }
85   -
86   - // 根据触发器类型查找对应的匹配器
87   - IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType());
88   - if (triggerType == null) {
89   - log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType());
90   - return false;
91   - }
92   -
93   - IotSceneRuleTriggerMatcher matcher = matcherMap.get(triggerType);
94   - if (matcher == null) {
95   - log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType);
96   - return false;
97   - }
98   -
99   - try {
100   - return matcher.isMatched(message, trigger);
101   - } catch (Exception e) {
102   - log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}",
103   - message, trigger, matcher.getMatcherName(), e);
104   - return false;
105   - }
106   - }
107   -
108   - /**
109   - * 获取所有支持的触发器类型
110   - *
111   - * @return 支持的触发器类型列表
112   - */
113   - public Set<IotSceneRuleTriggerTypeEnum> getSupportedTriggerTypes() {
114   - return new HashSet<>(matcherMap.keySet());
115   - }
116   -
117   - /**
118   - * 获取指定触发器类型的匹配器
119   - *
120   - * @param triggerType 触发器类型
121   - * @return 匹配器实例,如果不存在则返回 null
122   - */
123   - public IotSceneRuleTriggerMatcher getMatcher(IotSceneRuleTriggerTypeEnum triggerType) {
124   - return matcherMap.get(triggerType);
125   - }
126   -
127   - /**
128   - * 获取所有匹配器的统计信息
129   - *
130   - * @return 统计信息映射表
131   - */
132   - public Map<String, Object> getMatcherStatistics() {
133   - Map<String, Object> statistics = new HashMap<>();
134   - statistics.put("totalMatchers", allMatchers.size());
135   - statistics.put("enabledMatchers", matcherMap.size());
136   - statistics.put("supportedTriggerTypes", getSupportedTriggerTypes());
137   -
138   - Map<String, Object> matcherDetails = new HashMap<>();
139   - matcherMap.forEach((type, matcher) -> {
140   - Map<String, Object> detail = new HashMap<>();
141   - detail.put("matcherName", matcher.getMatcherName());
142   - detail.put("priority", matcher.getPriority());
143   - detail.put("enabled", matcher.isEnabled());
144   - matcherDetails.put(type.name(), detail);
145   - });
146   - statistics.put("matcherDetails", matcherDetails);
147   -
148   - return statistics;
149   - }
150   -
151   -}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java
... ... @@ -15,7 +15,12 @@ import org.springframework.stereotype.Component;
15 15 * @author HUIHUI
16 16 */
17 17 @Component
18   -public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
  18 +public class TimerTriggerMatcher extends AbstractIotSceneRuleMatcher {
  19 +
  20 + @Override
  21 + public MatcherType getMatcherType() {
  22 + return MatcherType.TRIGGER;
  23 + }
19 24  
20 25 @Override
21 26 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
... ... @@ -26,13 +31,13 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
26 31 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
27 32 // 1. 基础参数校验
28 33 if (!isBasicTriggerValid(trigger)) {
29   - logMatchFailure(message, trigger, "触发器基础参数无效");
  34 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
30 35 return false;
31 36 }
32 37  
33 38 // 2. 检查 CRON 表达式是否存在
34 39 if (StrUtil.isBlank(trigger.getCronExpression())) {
35   - logMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
  40 + logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
36 41 return false;
37 42 }
38 43  
... ... @@ -41,11 +46,11 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
41 46  
42 47 // 4. 可以添加 CRON 表达式格式验证
43 48 if (!isValidCronExpression(trigger.getCronExpression())) {
44   - logMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
  49 + logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
45 50 return false;
46 51 }
47 52  
48   - logMatchSuccess(message, trigger);
  53 + logTriggerMatchSuccess(message, trigger);
49 54 return true;
50 55 }
51 56  
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java
... ... @@ -20,13 +20,12 @@ import static org.junit.jupiter.api.Assertions.*;
20 20 */
21 21 public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
22 22  
23   - private IotSceneRuleTriggerMatcherManager matcherManager;
24   - private List<IotSceneRuleTriggerMatcher> matchers;
  23 + private IotSceneRuleMatcherManager matcherManager;
25 24  
26 25 @BeforeEach
27 26 void setUp() {
28 27 // 创建所有匹配器实例
29   - matchers = Arrays.asList(
  28 + List<IotSceneRuleMatcher> matchers = Arrays.asList(
30 29 new DeviceStateUpdateTriggerMatcher(),
31 30 new DevicePropertyPostTriggerMatcher(),
32 31 new DeviceEventPostTriggerMatcher(),
... ... @@ -35,7 +34,7 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
35 34 );
36 35  
37 36 // 初始化匹配器管理器
38   - matcherManager = new IotSceneRuleTriggerMatcherManager(matchers);
  37 + matcherManager = new IotSceneRuleMatcherManager(matchers);
39 38 }
40 39  
41 40 @Test
... ... @@ -197,4 +196,5 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
197 196 assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE));
198 197 assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER));
199 198 }
  199 +
200 200 }
... ...