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 +19,17 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
19 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO; 19 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
20 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO; 20 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
21 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 21 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
22 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
23 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 22 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
24 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 23 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
25 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 24 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
26 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper; 25 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
27 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; 26 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
28 -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;  
29 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; 27 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
30 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; 28 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
31 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 29 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
32 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 30 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
33 import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction; 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 import jakarta.annotation.Resource; 33 import jakarta.annotation.Resource;
36 import lombok.extern.slf4j.Slf4j; 34 import lombok.extern.slf4j.Slf4j;
37 import org.springframework.stereotype.Service; 35 import org.springframework.stereotype.Service;
@@ -67,7 +65,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { @@ -67,7 +65,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
67 @Resource 65 @Resource
68 private IotDeviceService deviceService; 66 private IotDeviceService deviceService;
69 @Resource 67 @Resource
70 - private IotSceneRuleTriggerMatcherManager triggerMatcherManager; 68 + private IotSceneRuleMatcherManager matcherManager;
71 @Resource 69 @Resource
72 private List<IotSceneRuleAction> sceneRuleActions; 70 private List<IotSceneRuleAction> sceneRuleActions;
73 71
@@ -285,7 +283,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { @@ -285,7 +283,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
285 private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) { 283 private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {
286 try { 284 try {
287 // 2. 检查触发器的条件分组 285 // 2. 检查触发器的条件分组
288 - return triggerMatcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule); 286 + return matcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);
289 } catch (Exception e) { 287 } catch (Exception e) {
290 log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}", 288 log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}",
291 sceneRule.getId(), trigger.getType(), message, e); 289 sceneRule.getId(), trigger.getType(), message, e);
@@ -333,7 +331,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { @@ -333,7 +331,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
333 } 331 }
334 332
335 /** 333 /**
336 - * 基于消息,判断触发器的条件是否匹配 334 + * 基于消息,判断触发器的子条件是否匹配
337 * 335 *
338 * @param message 设备消息 336 * @param message 设备消息
339 * @param condition 触发条件 337 * @param condition 触发条件
@@ -344,21 +342,8 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { @@ -344,21 +342,8 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
344 private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, 342 private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
345 IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) { 343 IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
346 try { 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 } catch (Exception e) { 347 } catch (Exception e) {
363 log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]", 348 log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]",
364 sceneRule.getId(), trigger, e); 349 sceneRule.getId(), trigger, e);
@@ -366,105 +351,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { @@ -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 // TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。 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,14 +15,14 @@ import java.util.Map;
15 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 15 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
16 16
17 /** 17 /**
18 - * IoT 场景规则触发器匹配器抽象基类 18 + * IoT 场景规则匹配器抽象基类
19 * <p> 19 * <p>
20 - * 提供通用的条件评估逻辑和工具方法 20 + * 提供通用的条件评估逻辑和工具方法,支持触发器和条件两种匹配类型
21 * 21 *
22 * @author HUIHUI 22 * @author HUIHUI
23 */ 23 */
24 @Slf4j 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,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,45 +81,90 @@ public abstract class AbstractIotSceneRuleTriggerMatcher implements IotSceneRule
79 } 81 }
80 82
81 /** 83 /**
82 - * 检查操作符和值是否有效 84 + * 检查触发器操作符和值是否有效
83 * 85 *
84 * @param trigger 触发器配置 86 * @param trigger 触发器配置
85 * @return 是否有效 87 * @return 是否有效
86 */ 88 */
87 - protected boolean isOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) { 89 + protected boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
88 return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue()); 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 * @param message 设备消息 96 * @param message 设备消息
106 * @param trigger 触发器配置 97 * @param trigger 触发器配置
107 */ 98 */
108 - protected void logMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 99 + protected void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
109 log.debug("[{}][消息({}) 匹配触发器({}) 成功]", getMatcherName(), message.getRequestId(), trigger.getType()); 100 log.debug("[{}][消息({}) 匹配触发器({}) 成功]", getMatcherName(), message.getRequestId(), trigger.getType());
110 } 101 }
111 102
112 /** 103 /**
113 - * 记录匹配失败日志 104 + * 记录触发器匹配失败日志
114 * 105 *
115 * @param message 设备消息 106 * @param message 设备消息
116 * @param trigger 触发器配置 107 * @param trigger 触发器配置
117 * @param reason 失败原因 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 log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", getMatcherName(), message.getRequestId(), trigger.getType(), reason); 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,7 +16,7 @@ import org.springframework.stereotype.Component;
16 * @author HUIHUI 16 * @author HUIHUI
17 */ 17 */
18 @Component 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,6 +24,11 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
24 private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod(); 24 private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod();
25 25
26 @Override 26 @Override
  27 + public MatcherType getMatcherType() {
  28 + return MatcherType.TRIGGER;
  29 + }
  30 +
  31 + @Override
27 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 32 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
28 return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; 33 return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
29 } 34 }
@@ -32,20 +37,20 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa @@ -32,20 +37,20 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
32 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 37 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
33 // 1. 基础参数校验 38 // 1. 基础参数校验
34 if (!isBasicTriggerValid(trigger)) { 39 if (!isBasicTriggerValid(trigger)) {
35 - logMatchFailure(message, trigger, "触发器基础参数无效"); 40 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
36 return false; 41 return false;
37 } 42 }
38 43
39 // 2. 检查消息方法是否匹配 44 // 2. 检查消息方法是否匹配
40 if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) { 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 return false; 47 return false;
43 } 48 }
44 49
45 // 3. 检查标识符是否匹配 50 // 3. 检查标识符是否匹配
46 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 51 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
47 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { 52 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
48 - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); 53 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
49 return false; 54 return false;
50 } 55 }
51 56
@@ -54,18 +59,18 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa @@ -54,18 +59,18 @@ public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleTriggerMa
54 if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { 59 if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
55 Object eventData = message.getData(); 60 Object eventData = message.getData();
56 if (eventData == null) { 61 if (eventData == null) {
57 - logMatchFailure(message, trigger, "消息中事件数据为空"); 62 + logTriggerMatchFailure(message, trigger, "消息中事件数据为空");
58 return false; 63 return false;
59 } 64 }
60 65
61 boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue()); 66 boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
62 if (!matched) { 67 if (!matched) {
63 - logMatchFailure(message, trigger, "事件数据条件不匹配"); 68 + logTriggerMatchFailure(message, trigger, "事件数据条件不匹配");
64 return false; 69 return false;
65 } 70 }
66 } 71 }
67 72
68 - logMatchSuccess(message, trigger); 73 + logTriggerMatchSuccess(message, trigger);
69 return true; 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,7 +15,7 @@ import org.springframework.stereotype.Component;
15 * @author HUIHUI 15 * @author HUIHUI
16 */ 16 */
17 @Component 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,6 +23,11 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
23 private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(); 23 private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod();
24 24
25 @Override 25 @Override
  26 + public MatcherType getMatcherType() {
  27 + return MatcherType.TRIGGER;
  28 + }
  29 +
  30 + @Override
26 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 31 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
27 return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; 32 return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
28 } 33 }
@@ -31,33 +36,33 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge @@ -31,33 +36,33 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
31 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 36 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
32 // 1. 基础参数校验 37 // 1. 基础参数校验
33 if (!isBasicTriggerValid(trigger)) { 38 if (!isBasicTriggerValid(trigger)) {
34 - logMatchFailure(message, trigger, "触发器基础参数无效"); 39 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
35 return false; 40 return false;
36 } 41 }
37 42
38 // 2. 检查消息方法是否匹配 43 // 2. 检查消息方法是否匹配
39 if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) { 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 return false; 46 return false;
42 } 47 }
43 48
44 // 3. 检查标识符是否匹配 49 // 3. 检查标识符是否匹配
45 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 50 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
46 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { 51 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
47 - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); 52 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
48 return false; 53 return false;
49 } 54 }
50 55
51 // 4. 检查操作符和值是否有效 56 // 4. 检查操作符和值是否有效
52 - if (!isOperatorAndValueValid(trigger)) {  
53 - logMatchFailure(message, trigger, "操作符或值无效"); 57 + if (!isTriggerOperatorAndValueValid(trigger)) {
  58 + logTriggerMatchFailure(message, trigger, "操作符或值无效");
54 return false; 59 return false;
55 } 60 }
56 61
57 // 5. 获取属性值 62 // 5. 获取属性值
58 Object propertyValue = message.getData(); 63 Object propertyValue = message.getData();
59 if (propertyValue == null) { 64 if (propertyValue == null) {
60 - logMatchFailure(message, trigger, "消息中属性值为空"); 65 + logTriggerMatchFailure(message, trigger, "消息中属性值为空");
61 return false; 66 return false;
62 } 67 }
63 68
@@ -65,9 +70,9 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge @@ -65,9 +70,9 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleTrigge
65 boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); 70 boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
66 71
67 if (matched) { 72 if (matched) {
68 - logMatchSuccess(message, trigger); 73 + logTriggerMatchSuccess(message, trigger);
69 } else { 74 } else {
70 - logMatchFailure(message, trigger, "属性值条件不匹配"); 75 + logTriggerMatchFailure(message, trigger, "属性值条件不匹配");
71 } 76 }
72 77
73 return matched; 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,7 +15,7 @@ import org.springframework.stereotype.Component;
15 * @author HUIHUI 15 * @author HUIHUI
16 */ 16 */
17 @Component 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,6 +23,11 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg
23 private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(); 23 private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod();
24 24
25 @Override 25 @Override
  26 + public MatcherType getMatcherType() {
  27 + return MatcherType.TRIGGER;
  28 + }
  29 +
  30 + @Override
26 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 31 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
27 return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; 32 return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
28 } 33 }
@@ -31,27 +36,27 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg @@ -31,27 +36,27 @@ public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleTrigg
31 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 36 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
32 // 1. 基础参数校验 37 // 1. 基础参数校验
33 if (!isBasicTriggerValid(trigger)) { 38 if (!isBasicTriggerValid(trigger)) {
34 - logMatchFailure(message, trigger, "触发器基础参数无效"); 39 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
35 return false; 40 return false;
36 } 41 }
37 42
38 // 2. 检查消息方法是否匹配 43 // 2. 检查消息方法是否匹配
39 if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) { 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 return false; 46 return false;
42 } 47 }
43 48
44 // 3. 检查标识符是否匹配 49 // 3. 检查标识符是否匹配
45 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); 50 String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
46 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { 51 if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
47 - logMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); 52 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
48 return false; 53 return false;
49 } 54 }
50 55
51 // 4. 对于服务调用触发器,通常只需要匹配服务标识符即可 56 // 4. 对于服务调用触发器,通常只需要匹配服务标识符即可
52 // 不需要检查操作符和值,因为服务调用本身就是触发条件 57 // 不需要检查操作符和值,因为服务调用本身就是触发条件
53 58
54 - logMatchSuccess(message, trigger); 59 + logTriggerMatchSuccess(message, trigger);
55 return true; 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,7 +14,7 @@ import org.springframework.stereotype.Component;
14 * @author HUIHUI 14 * @author HUIHUI
15 */ 15 */
16 @Component 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,6 +22,11 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
22 private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(); 22 private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod();
23 23
24 @Override 24 @Override
  25 + public MatcherType getMatcherType() {
  26 + return MatcherType.TRIGGER;
  27 + }
  28 +
  29 + @Override
25 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 30 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
26 return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; 31 return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
27 } 32 }
@@ -30,26 +35,26 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger @@ -30,26 +35,26 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
30 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 35 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
31 // 1. 基础参数校验 36 // 1. 基础参数校验
32 if (!isBasicTriggerValid(trigger)) { 37 if (!isBasicTriggerValid(trigger)) {
33 - logMatchFailure(message, trigger, "触发器基础参数无效"); 38 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
34 return false; 39 return false;
35 } 40 }
36 41
37 // 2. 检查消息方法是否匹配 42 // 2. 检查消息方法是否匹配
38 if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) { 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 return false; 45 return false;
41 } 46 }
42 47
43 // 3. 检查操作符和值是否有效 48 // 3. 检查操作符和值是否有效
44 - if (!isOperatorAndValueValid(trigger)) {  
45 - logMatchFailure(message, trigger, "操作符或值无效"); 49 + if (!isTriggerOperatorAndValueValid(trigger)) {
  50 + logTriggerMatchFailure(message, trigger, "操作符或值无效");
46 return false; 51 return false;
47 } 52 }
48 53
49 // 4. 获取设备状态值 54 // 4. 获取设备状态值
50 Object stateValue = message.getData(); 55 Object stateValue = message.getData();
51 if (stateValue == null) { 56 if (stateValue == null) {
52 - logMatchFailure(message, trigger, "消息中设备状态值为空"); 57 + logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
53 return false; 58 return false;
54 } 59 }
55 60
@@ -57,9 +62,9 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger @@ -57,9 +62,9 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleTrigger
57 boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue()); 62 boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
58 63
59 if (matched) { 64 if (matched) {
60 - logMatchSuccess(message, trigger); 65 + logTriggerMatchSuccess(message, trigger);
61 } else { 66 } else {
62 - logMatchFailure(message, trigger, "状态值条件不匹配"); 67 + logTriggerMatchFailure(message, trigger, "状态值条件不匹配");
63 } 68 }
64 69
65 return matched; 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,37 +2,97 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
2 2
3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 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 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; 7 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
6 8
7 /** 9 /**
8 - * IoT 场景规则触发器匹配策略接口 10 + * IoT 场景规则匹配器统一接口
9 * <p> 11 * <p>
10 - * 用于实现不同类型触发器的匹配逻辑,遵循策略模式设计 12 + * 支持触发器匹配和条件匹配两种类型,遵循策略模式设计
  13 + * <p>
  14 + * 匹配器类型说明:
  15 + * - 触发器匹配器:用于匹配主触发条件(如设备消息类型、定时器等)
  16 + * - 条件匹配器:用于匹配子条件(如设备状态、属性值、时间条件等)
11 * 17 *
12 * @author HUIHUI 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 * @param message 设备消息 73 * @param message 设备消息
27 * @param trigger 触发器配置 74 * @param trigger 触发器配置
28 * @return 是否匹配 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 * <p> 94 * <p>
35 - * 用于在多个匹配器支持同一触发器类型时确定优先级 95 + * 用于在多个匹配器支持同一类型时确定优先级
36 * 96 *
37 * @return 优先级数值 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,7 +15,12 @@ import org.springframework.stereotype.Component;
15 * @author HUIHUI 15 * @author HUIHUI
16 */ 16 */
17 @Component 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 @Override 25 @Override
21 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { 26 public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
@@ -26,13 +31,13 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { @@ -26,13 +31,13 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
26 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { 31 public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
27 // 1. 基础参数校验 32 // 1. 基础参数校验
28 if (!isBasicTriggerValid(trigger)) { 33 if (!isBasicTriggerValid(trigger)) {
29 - logMatchFailure(message, trigger, "触发器基础参数无效"); 34 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
30 return false; 35 return false;
31 } 36 }
32 37
33 // 2. 检查 CRON 表达式是否存在 38 // 2. 检查 CRON 表达式是否存在
34 if (StrUtil.isBlank(trigger.getCronExpression())) { 39 if (StrUtil.isBlank(trigger.getCronExpression())) {
35 - logMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); 40 + logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
36 return false; 41 return false;
37 } 42 }
38 43
@@ -41,11 +46,11 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher { @@ -41,11 +46,11 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleTriggerMatcher {
41 46
42 // 4. 可以添加 CRON 表达式格式验证 47 // 4. 可以添加 CRON 表达式格式验证
43 if (!isValidCronExpression(trigger.getCronExpression())) { 48 if (!isValidCronExpression(trigger.getCronExpression())) {
44 - logMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); 49 + logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
45 return false; 50 return false;
46 } 51 }
47 52
48 - logMatchSuccess(message, trigger); 53 + logTriggerMatchSuccess(message, trigger);
49 return true; 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,13 +20,12 @@ import static org.junit.jupiter.api.Assertions.*;
20 */ 20 */
21 public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { 21 public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
22 22
23 - private IotSceneRuleTriggerMatcherManager matcherManager;  
24 - private List<IotSceneRuleTriggerMatcher> matchers; 23 + private IotSceneRuleMatcherManager matcherManager;
25 24
26 @BeforeEach 25 @BeforeEach
27 void setUp() { 26 void setUp() {
28 // 创建所有匹配器实例 27 // 创建所有匹配器实例
29 - matchers = Arrays.asList( 28 + List<IotSceneRuleMatcher> matchers = Arrays.asList(
30 new DeviceStateUpdateTriggerMatcher(), 29 new DeviceStateUpdateTriggerMatcher(),
31 new DevicePropertyPostTriggerMatcher(), 30 new DevicePropertyPostTriggerMatcher(),
32 new DeviceEventPostTriggerMatcher(), 31 new DeviceEventPostTriggerMatcher(),
@@ -35,7 +34,7 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { @@ -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 @Test 40 @Test
@@ -197,4 +196,5 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { @@ -197,4 +196,5 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
197 assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE)); 196 assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE));
198 assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER)); 197 assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER));
199 } 198 }
  199 +
200 } 200 }