perf:【IoT 物联网】场景联动优化 review 提到的逻辑

This commit is contained in:
puhui999
2025-08-18 11:34:18 +08:00
parent 34f1a2ed71
commit fb35807ebf
13 changed files with 350 additions and 306 deletions

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.enums.rule;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -60,7 +61,9 @@ public enum IotSceneRuleTriggerTypeEnum implements ArrayValuable<Integer> {
return ARRAYS;
}
// TODO @puhui999可以参考下别的枚举哈方法名和实现都可以更简洁of(String type) { firstMatch
public static IotSceneRuleTriggerTypeEnum typeOf(Integer type) {
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
}
/**
* 根据类型值查找触发器类型枚举
*

View File

@@ -26,16 +26,6 @@ public interface IotSceneRuleMatcher {
return 100;
}
// TODO @puhui999如果目前没自定义体感可以删除哈
/**
* 获取匹配器名称,用于日志和调试
*
* @return 匹配器名称
*/
default String getMatcherName() {
return this.getClass().getSimpleName();
}
/**
* 是否启用该匹配器
* <p>

View File

@@ -1,7 +1,10 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
@@ -40,44 +43,99 @@ public final class IotSceneRuleMatcherHelper {
* @param paramValue 参数值(来自条件配置)
* @return 是否匹配
*/
@SuppressWarnings("DataFlowIssue")
public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
try {
// 1. 校验操作符是否合法
IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
if (operatorEnum == null) {
log.warn("[evaluateCondition][存在错误的操作符({})]", operator);
log.warn("[evaluateCondition][operator({}) 操作符无效]", operator);
return false;
}
// 2. 构建 Spring 表达式变量
Map<String, Object> springExpressionVariables = new HashMap<>();
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue);
// 处理参数值
if (StrUtil.isNotBlank(paramValue)) {
// 处理多值情况(如 IN、BETWEEN 操作符)
// TODO @puhui999使用这个会不会有问题例如说string 恰好有 分隔?
if (paramValue.contains(",")) {
List<String> paramValues = StrUtil.split(paramValue, ',');
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
convertList(paramValues, NumberUtil::parseDouble));
} else {
// 处理单值情况
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
NumberUtil.parseDouble(paramValue));
}
}
// 3. 计算 Spring 表达式
return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
return evaluateConditionWithOperatorEnum(sourceValue, operatorEnum, paramValue);
} catch (Exception e) {
log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}",
log.error("[evaluateCondition][sourceValue({}) operator({}) paramValue({}) 条件评估异常]",
sourceValue, operator, paramValue, e);
return false;
}
}
/**
* 使用操作符枚举评估条件是否匹配
*
* @param sourceValue 源值(来自消息)
* @param operatorEnum 操作符枚举
* @param paramValue 参数值(来自条件配置)
* @return 是否匹配
*/
@SuppressWarnings("DataFlowIssue")
public static boolean evaluateConditionWithOperatorEnum(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {
try {
// 1. 构建 Spring 表达式变量
Map<String, Object> springExpressionVariables = buildSpringExpressionVariables(sourceValue, operatorEnum, paramValue);
// 2. 计算 Spring 表达式
return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
} catch (Exception e) {
log.error("[evaluateConditionWithOperatorEnum][sourceValue({}) operatorEnum({}) paramValue({}) 条件评估异常]",
sourceValue, operatorEnum, paramValue, e);
return false;
}
}
/**
* 构建 Spring 表达式变量
*/
private static Map<String, Object> buildSpringExpressionVariables(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {
Map<String, Object> springExpressionVariables = new HashMap<>();
// 设置源值
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue);
// 处理参数值
if (StrUtil.isNotBlank(paramValue)) {
List<String> parameterValues = StrUtil.splitTrim(paramValue, CharPool.COMMA);
// 设置原始参数值
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, paramValue);
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);
// 特殊处理:解决数字比较问题
// Spring 表达式基于 compareTo 方法,对数字的比较存在问题,需要转换为数字类型
if (isNumericComparisonOperator(operatorEnum) && isNumericComparison(sourceValue, parameterValues)) {
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,
NumberUtil.parseDouble(String.valueOf(sourceValue)));
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
NumberUtil.parseDouble(paramValue));
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
convertList(parameterValues, NumberUtil::parseDouble));
}
}
return springExpressionVariables;
}
/**
* 判断是否为数字比较操作符
*/
private static boolean isNumericComparisonOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {
return ObjectUtils.equalsAny(operatorEnum,
IotSceneRuleConditionOperatorEnum.BETWEEN,
IotSceneRuleConditionOperatorEnum.NOT_BETWEEN,
IotSceneRuleConditionOperatorEnum.GREATER_THAN,
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS,
IotSceneRuleConditionOperatorEnum.LESS_THAN,
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS);
}
/**
* 判断是否为数字比较场景
*/
private static boolean isNumericComparison(Object sourceValue, List<String> parameterValues) {
return NumberUtil.isNumber(String.valueOf(sourceValue)) && NumberUtils.isAllNumber(parameterValues);
}
// ========== 【触发器】相关工具方法 ==========
/**
@@ -103,24 +161,22 @@ public final class IotSceneRuleMatcherHelper {
/**
* 记录触发器匹配成功日志
*
* @param matcherName 匹配器名称
* @param message 设备消息
* @param trigger 触发器配置
*/
public static void logTriggerMatchSuccess(String matcherName, IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
log.debug("[{}][消息({}) 匹配触发器({}) 成功]", matcherName, message.getRequestId(), trigger.getType());
public static void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
log.debug("[isMatched][message({}) trigger({}) 匹配触发器成功]", message.getRequestId(), trigger.getType());
}
/**
* 记录触发器匹配失败日志
*
* @param matcherName 匹配器名称
* @param message 设备消息
* @param trigger 触发器配置
* @param reason 失败原因
*/
public static void logTriggerMatchFailure(String matcherName, IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", matcherName, message.getRequestId(), trigger.getType(), reason);
public static void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
log.debug("[isMatched][message({}) trigger({}) reason({}) 匹配触发器失败]", message.getRequestId(), trigger.getType(), reason);
}
// ========== 【条件】相关工具方法 ==========
@@ -148,24 +204,22 @@ public final class IotSceneRuleMatcherHelper {
/**
* 记录条件匹配成功日志
*
* @param matcherName 匹配器名称
* @param message 设备消息
* @param condition 触发条件
*/
public static void logConditionMatchSuccess(String matcherName, IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
log.debug("[{}][消息({}) 匹配条件({}) 成功]", matcherName, message.getRequestId(), condition.getType());
public static void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
log.debug("[isMatched][message({}) condition({}) 匹配条件成功]", message.getRequestId(), condition.getType());
}
/**
* 记录条件匹配失败日志
*
* @param matcherName 匹配器名称
* @param message 设备消息
* @param condition 触发条件
* @param reason 失败原因
*/
public static void logConditionMatchFailure(String matcherName, IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
log.debug("[{}][消息({}) 匹配条件({}) 失败: {}]", matcherName, message.getRequestId(), condition.getType(), reason);
public static void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
log.debug("[isMatched][message({}) condition({}) reason({}) 匹配条件失败]", message.getRequestId(), condition.getType(), reason);
}
// ========== 【通用】工具方法 ==========

View File

@@ -14,7 +14,7 @@ import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* IoT 场景规则匹配器统一管理器
@@ -72,42 +72,34 @@ public class IotSceneRuleMatcherManager {
.toList();
// 构建触发器匹配器映射表
// TODO @puhui999convertMap()
this.triggerMatchers = triggerMatchers.stream()
.collect(Collectors.toMap(
IotSceneRuleTriggerMatcher::getSupportedTriggerType,
Function.identity(),
(existing, replacement) -> {
log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
existing.getSupportedTriggerType(),
existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
},
LinkedHashMap::new
));
this.triggerMatchers = convertMap(triggerMatchers, IotSceneRuleTriggerMatcher::getSupportedTriggerType,
Function.identity(),
(existing, replacement) -> {
log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
existing.getSupportedTriggerType(),
existing.getPriority() <= replacement.getPriority() ?
existing.getSupportedTriggerType() : replacement.getSupportedTriggerType());
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
}, LinkedHashMap::new);
// 构建条件匹配器映射表
this.conditionMatchers = conditionMatchers.stream()
.collect(Collectors.toMap(
IotSceneRuleConditionMatcher::getSupportedConditionType,
Function.identity(),
(existing, replacement) -> {
log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]",
existing.getSupportedConditionType(),
existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
},
LinkedHashMap::new
));
this.conditionMatchers = convertMap(conditionMatchers, IotSceneRuleConditionMatcher::getSupportedConditionType,
Function.identity(),
(existing, replacement) -> {
log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]",
existing.getSupportedConditionType(),
existing.getPriority() <= replacement.getPriority() ?
existing.getSupportedConditionType() : replacement.getSupportedConditionType());
return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
},
LinkedHashMap::new);
// 日志输出初始化信息
log.info("[IotSceneRuleMatcherManager][初始化完成,共加载 {} 个匹配器,其中触发器匹配器 {} 个,条件匹配器 {} 个]",
log.info("[IotSceneRuleMatcherManager][初始化完成,共加载({})个匹配器,其中触发器匹配器({})个,条件匹配器({})个]",
this.allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size());
this.triggerMatchers.forEach((type, matcher) ->
log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}",
type, matcher.getMatcherName(), matcher.getPriority()));
log.info("[IotSceneRuleMatcherManager][触发器匹配器类型: ({}), 优先级: ({})] ", type, matcher.getPriority()));
this.conditionMatchers.forEach((type, matcher) ->
log.info("[IotSceneRuleMatcherManager][条件匹配器] 类型: {}, 匹配器: {}, 优先级: {}",
type, matcher.getMatcherName(), matcher.getPriority()));
log.info("[IotSceneRuleMatcherManager][条件匹配器类型: ({}), 优先级: ({})]", type, matcher.getPriority()));
}
/**
@@ -118,27 +110,25 @@ public class IotSceneRuleMatcherManager {
* @return 是否匹配
*/
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
// TODO @puhui999日志优化下claude 打出来的日志风格,和项目有点不一样哈;
if (message == null || trigger == null || trigger.getType() == null) {
log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger);
log.debug("[isMatched][message({}) trigger({}) 参数无效]", message, trigger);
return false;
}
IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType());
IotSceneRuleTriggerTypeEnum triggerType = IotSceneRuleTriggerTypeEnum.typeOf(trigger.getType());
if (triggerType == null) {
log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType());
log.warn("[isMatched][triggerType({}) 未知的触发器类型]", trigger.getType());
return false;
}
IotSceneRuleTriggerMatcher matcher = triggerMatchers.get(triggerType);
if (matcher == null) {
log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType);
log.warn("[isMatched][triggerType({}) 没有对应的匹配器]", triggerType);
return false;
}
try {
return matcher.isMatched(message, trigger);
} catch (Exception e) {
log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}",
message, trigger, matcher.getMatcherName(), e);
log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}", message, trigger, e);
return false;
}
}
@@ -152,28 +142,27 @@ public class IotSceneRuleMatcherManager {
*/
public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
if (message == null || condition == null || condition.getType() == null) {
log.debug("[isConditionMatched][参数无效] message: {}, condition: {}", message, condition);
log.debug("[isConditionMatched][message({}) condition({}) 参数无效]", message, condition);
return false;
}
// 根据条件类型查找对应的匹配器
IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType());
if (conditionType == null) {
log.warn("[isConditionMatched][未知的条件类型: {}]", condition.getType());
log.warn("[isConditionMatched][conditionType({}) 未知的条件类型]", condition.getType());
return false;
}
IotSceneRuleConditionMatcher matcher = conditionMatchers.get(conditionType);
if (matcher == null) {
log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType);
log.warn("[isConditionMatched][conditionType({}) 没有对应的匹配器]", conditionType);
return false;
}
try {
return matcher.isMatched(message, condition);
} catch (Exception e) {
log.error("[isConditionMatched][条件匹配异常] message: {}, condition: {}, matcher: {}",
message, condition, matcher.getMatcherName(), e);
log.error("[isConditionMatched][message({}) condition({}) 条件匹配异常]", message, condition, e);
return false;
}
}

View File

@@ -1,8 +1,11 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
import lombok.extern.slf4j.Slf4j;
@@ -11,6 +14,7 @@ import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* 当前时间条件匹配器
@@ -40,115 +44,175 @@ public class CurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效");
return false;
}
// 2. 检查操作符和参数是否有效
// 1.2 检查操作符和参数是否有效
if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效");
return false;
}
// 3. 根据操作符类型进行不同的时间匹配
LocalDateTime now = LocalDateTime.now();
// 1.3 验证操作符是否为支持的时间操作符
String operator = condition.getOperator();
String param = condition.getParam();
boolean matched;
try {
if (operator.startsWith("date_time_")) {
// 日期时间匹配(时间戳)
matched = matchDateTime(now, operator, param);
} else if (operator.startsWith("time_")) {
// 当日时间匹配HH:mm:ss
matched = matchTime(now.toLocalTime(), operator, param);
} else {
// 其他操作符,使用通用条件评估器
matched = IotSceneRuleMatcherHelper.evaluateCondition(now.toEpochSecond(java.time.ZoneOffset.of("+8")), operator, param);
}
if (matched) {
IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition);
} else {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "时间条件不匹配");
}
} catch (Exception e) {
log.error("[CurrentTimeConditionMatcher][时间条件匹配异常] operator: {}, param: {}", operator, param, e);
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "时间条件匹配异常: " + e.getMessage());
matched = false;
IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
if (operatorEnum == null) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "无效的操作符: " + operator);
return false;
}
if (!isTimeOperator(operatorEnum)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "不支持的时间操作符: " + operator);
return false;
}
// 2.1 执行时间匹配
boolean matched = executeTimeMatching(operatorEnum, condition.getParam());
// 2.2 记录匹配结果
if (matched) {
IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);
} else {
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "时间条件不匹配");
}
return matched;
}
/**
* 匹配日期时间(时间戳)
* 执行时间匹配逻辑
* 直接实现时间条件匹配,不使用 Spring EL 表达式
*/
private boolean matchDateTime(LocalDateTime now, String operator, String param) {
long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8"));
return IotSceneRuleMatcherHelper.evaluateCondition(currentTimestamp, operator.substring("date_time_".length()), param);
}
/**
* 匹配当日时间HH:mm:ss
*/
private boolean matchTime(LocalTime currentTime, String operator, String param) {
private boolean executeTimeMatching(IotSceneRuleConditionOperatorEnum operatorEnum, String param) {
try {
String actualOperator = operator.substring("time_".length());
LocalDateTime now = LocalDateTime.now();
// TODO @puhui999if return 简化;
if ("between".equals(actualOperator)) {
// 时间区间匹配
String[] timeRange = param.split(",");
if (timeRange.length != 2) {
return false;
}
LocalTime startTime = parseTime(timeRange[0].trim());
LocalTime endTime = parseTime(timeRange[1].trim());
return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime);
if (isDateTimeOperator(operatorEnum)) {
// 日期时间匹配(时间戳)
long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8"));
return matchDateTime(currentTimestamp, operatorEnum, param);
} else {
// 单个时间比较
LocalTime targetTime = parseTime(param);
// TODO @puhui999枚举类
switch (actualOperator) {
case ">":
return currentTime.isAfter(targetTime);
case "<":
return currentTime.isBefore(targetTime);
case ">=":
return !currentTime.isBefore(targetTime);
case "<=":
return !currentTime.isAfter(targetTime);
case "=":
return currentTime.equals(targetTime);
default:
return false;
}
// 当日时间匹配HH:mm:ss
return matchTime(now.toLocalTime(), operatorEnum, param);
}
} catch (Exception e) {
// TODO @puhui9991日志格式 [][]2方法名不对哈
log.error("[CurrentTimeConditionMatcher][时间解析异常] param: {}", param, e);
log.error("[executeTimeMatching][operatorEnum({}) param({}) 时间匹配异常]", operatorEnum, param, e);
return false;
}
}
/**
* 判断是否为日期时间操作符
*/
private boolean isDateTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {
return operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN ||
operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN ||
operatorEnum == IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN;
}
/**
* 判断是否为时间操作符
*/
private boolean isTimeOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {
return operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN ||
operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN ||
operatorEnum == IotSceneRuleConditionOperatorEnum.TIME_BETWEEN ||
isDateTimeOperator(operatorEnum);
}
/**
* 匹配日期时间(时间戳)
* 直接实现时间戳比较逻辑
*/
private boolean matchDateTime(long currentTimestamp, IotSceneRuleConditionOperatorEnum operatorEnum, String param) {
try {
long targetTimestamp = Long.parseLong(param);
return switch (operatorEnum) {
case DATE_TIME_GREATER_THAN -> currentTimestamp > targetTimestamp;
case DATE_TIME_LESS_THAN -> currentTimestamp < targetTimestamp;
case DATE_TIME_BETWEEN -> matchDateTimeBetween(currentTimestamp, param);
default -> {
log.warn("[matchDateTime][operatorEnum({}) 不支持的日期时间操作符]", operatorEnum);
yield false;
}
};
} catch (Exception e) {
log.error("[matchDateTime][operatorEnum({}) param({}) 日期时间匹配异常]", operatorEnum, param, e);
return false;
}
}
/**
* 匹配日期时间区间
*/
private boolean matchDateTimeBetween(long currentTimestamp, String param) {
List<String> timestampRange = StrUtil.splitTrim(param, CharPool.COMMA);
if (timestampRange.size() != 2) {
log.warn("[matchDateTimeBetween][param({}) 时间戳区间参数格式错误]", param);
return false;
}
long startTimestamp = Long.parseLong(timestampRange.get(0).trim());
long endTimestamp = Long.parseLong(timestampRange.get(1).trim());
return currentTimestamp >= startTimestamp && currentTimestamp <= endTimestamp;
}
/**
* 匹配当日时间HH:mm:ss
* 直接实现时间比较逻辑
*/
private boolean matchTime(LocalTime currentTime, IotSceneRuleConditionOperatorEnum operatorEnum, String param) {
try {
LocalTime targetTime = parseTime(param);
return switch (operatorEnum) {
case TIME_GREATER_THAN -> currentTime.isAfter(targetTime);
case TIME_LESS_THAN -> currentTime.isBefore(targetTime);
case TIME_BETWEEN -> matchTimeBetween(currentTime, param);
default -> {
log.warn("[matchTime][operatorEnum({}) 不支持的时间操作符]", operatorEnum);
yield false;
}
};
} catch (Exception e) {
log.error("[matchTime][][operatorEnum({}) param({}) 时间解析异常]", operatorEnum, param, e);
return false;
}
}
/**
* 匹配时间区间
*/
private boolean matchTimeBetween(LocalTime currentTime, String param) {
List<String> timeRange = StrUtil.splitTrim(param, CharPool.COMMA);
if (timeRange.size() != 2) {
log.warn("[matchTimeBetween][param({}) 时间区间参数格式错误]", param);
return false;
}
LocalTime startTime = parseTime(timeRange.get(0).trim());
LocalTime endTime = parseTime(timeRange.get(1).trim());
return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime);
}
/**
* 解析时间字符串
* 支持 HH:mm 和 HH:mm:ss 两种格式
*/
private LocalTime parseTime(String timeStr) {
// TODO @puhui999可以用 hutool Assert 类简化
if (StrUtil.isBlank(timeStr)) {
throw new IllegalArgumentException("时间字符串不能为空");
}
// 尝试不同的时间格式
Assert.isFalse(StrUtil.isBlank(timeStr), "时间字符串不能为空");
try {
// 尝试不同的时间格式
if (timeStr.length() == 5) { // HH:mm
return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT);
} else { // HH:mm:ss
} else if (timeStr.length() == 8) { // HH:mm:ss
return LocalTime.parse(timeStr, TIME_FORMATTER);
} else {
throw new IllegalArgumentException("时间格式长度不正确,期望 HH:mm 或 HH:mm:ss 格式");
}
} catch (Exception e) {
log.error("[parseTime][timeStr({}) 时间格式解析失败]", timeStr, e);
throw new IllegalArgumentException("时间格式无效: " + timeStr, e);
}
}

View File

@@ -22,41 +22,40 @@ public class DevicePropertyConditionMatcher implements IotSceneRuleConditionMatc
return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY;
}
// TODO @puhui999参数校验的要不要 1.1 1.2 1.3 1.4 ?这样最终看到 2. 3. 就是核心逻辑列;
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效");
return false;
}
// 2. 检查标识符是否匹配
// 1.2 检查标识符是否匹配
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier);
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier);
return false;
}
// 3. 检查操作符和参数是否有效
// 1.3 检查操作符和参数是否有效
if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效");
return false;
}
// 4. 获取属性值
Object propertyValue = message.getData();
// 2.1. 获取属性值
Object propertyValue = message.getParams();
if (propertyValue == null) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "消息中属性值为空");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空");
return false;
}
// 5. 使用条件评估器进行匹配
// 2.2 使用条件评估器进行匹配
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam());
if (matched) {
IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition);
IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);
} else {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "设备属性条件不匹配");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备属性条件不匹配");
}
return matched;
}

View File

@@ -23,32 +23,31 @@ public class DeviceStateConditionMatcher implements IotSceneRuleConditionMatcher
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "条件基础参数无效");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效");
return false;
}
// 2. 检查操作符和参数是否有效
// 1.2 检查操作符和参数是否有效
if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "操作符或参数无效");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效");
return false;
}
// 3. 获取设备状态值
// 设备状态通常在消息的 data 字段中
Object stateValue = message.getData();
// 2.1 获取设备状态值
Object stateValue = message.getParams();
if (stateValue == null) {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "消息中设备状态值为空");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空");
return false;
}
// 4. 使用条件评估器进行匹配
// 2.2 使用条件评估器进行匹配
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam());
if (matched) {
IotSceneRuleMatcherHelper.logConditionMatchSuccess(getMatcherName(), message, condition);
IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition);
} else {
IotSceneRuleMatcherHelper.logConditionMatchFailure(getMatcherName(), message, condition, "设备状态条件不匹配");
IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备状态条件不匹配");
}
return matched;
}

View File

@@ -19,11 +19,6 @@ import org.springframework.stereotype.Component;
@Component
public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher {
/**
* 设备事件上报消息方法
*/
private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod();
@Override
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
@@ -31,42 +26,44 @@ public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
return false;
}
// 2. 检查消息方法是否匹配
if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod());
// 1.2 检查消息方法是否匹配
if (!IotDeviceMessageMethodEnum.EVENT_POST.getMethod().equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " +
IotDeviceMessageMethodEnum.EVENT_POST.getMethod() + ", 实际: " + message.getMethod());
return false;
}
// 3. 检查标识符是否匹配
// 1.3 检查标识符是否匹配
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " +
trigger.getIdentifier() + ", 实际: " + messageIdentifier);
return false;
}
// 4. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配
// 2. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配
// 但如果配置了操作符和值,则需要进行条件匹配
if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
Object eventData = message.getData();
if (eventData == null) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中事件数据为空");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中事件数据为空");
return false;
}
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
if (!matched) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "事件数据条件不匹配");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "事件数据条件不匹配");
return false;
}
}
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger);
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
return true;
}

View File

@@ -18,12 +18,6 @@ import org.springframework.stereotype.Component;
@Component
public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher {
/**
* 设备属性上报消息方法
*/
// TODO @puhui999是不是不用枚举哈直接使用 IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()
private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod();
@Override
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
@@ -31,44 +25,46 @@ public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatc
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
return false;
}
// 2. 检查消息方法是否匹配
if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod());
// 1.2 检查消息方法是否匹配
if (!IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod().equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " +
IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() + ", 实际: " + message.getMethod());
return false;
}
// 3. 检查标识符是否匹配
// 1.3 检查标识符是否匹配
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " +
trigger.getIdentifier() + ", 实际: " + messageIdentifier);
return false;
}
// 4. 检查操作符和值是否有效
// 1.4 检查操作符和值是否有效
if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "操作符或值无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效");
return false;
}
// 5. 获取属性值
Object propertyValue = message.getData();
// 2.1 获取属性值
Object propertyValue = message.getParams();
if (propertyValue == null) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中属性值为空");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空");
return false;
}
// 6. 使用条件评估器进行匹配
// 2.2 使用条件评估器进行匹配
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
if (matched) {
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger);
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
} else {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "属性值条件不匹配");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "属性值条件不匹配");
}
return matched;
}

View File

@@ -18,11 +18,6 @@ import org.springframework.stereotype.Component;
@Component
public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher {
/**
* 设备服务调用消息方法
*/
private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod();
@Override
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
@@ -30,29 +25,29 @@ public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMat
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
return false;
}
// 2. 检查消息方法是否匹配
if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod());
// 1.2 检查消息方法是否匹配
if (!IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod().equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod() + ", 实际: " + message.getMethod());
return false;
}
// 3. 检查标识符是否匹配
// 1.3 检查标识符是否匹配
String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
return false;
}
// 4. 对于服务调用触发器,通常只需要匹配服务标识符即可
// 2. 对于服务调用触发器,通常只需要匹配服务标识符即可
// 不需要检查操作符和值,因为服务调用本身就是触发条件
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger);
// TODO @puhui999: 服务调用时校验输入参数是否匹配条件
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
return true;
}

View File

@@ -17,12 +17,6 @@ import org.springframework.stereotype.Component;
@Component
public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher {
// TODO @puhui999是不是不用枚举哈
/**
* 设备状态更新消息方法
*/
private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod();
@Override
public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
@@ -30,37 +24,39 @@ public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatch
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
return false;
}
// 2. 检查消息方法是否匹配
if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod());
// 1.2 检查消息方法是否匹配
if (!IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod().equals(message.getMethod())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " +
IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod() + ", 实际: " + message.getMethod());
return false;
}
// 3. 检查操作符和值是否有效
// 1.3 检查操作符和值是否有效
if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "操作符或值无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效");
return false;
}
// 4. 获取设备状态值
Object stateValue = message.getData();
// 2.1 获取设备状态值
Object stateValue = message.getParams();
if (stateValue == null) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "消息中设备状态值为空");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
return false;
}
// 5. 使用条件评估器进行匹配
// 2.2 使用条件评估器进行匹配
// TODO @puhui999: 状态匹配重新实现
boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
if (matched) {
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger);
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
} else {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "状态值条件不匹配");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "状态值条件不匹配");
}
return matched;
}

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper;
import org.quartz.CronExpression;
import org.springframework.stereotype.Component;
/**
@@ -25,58 +26,32 @@ public class TimerTriggerMatcher implements IotSceneRuleTriggerMatcher {
@Override
public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
// 1. 基础参数校验
// 1.1 基础参数校验
if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "触发器基础参数无效");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
return false;
}
// 2. 检查 CRON 表达式是否存在
// 1.2 检查 CRON 表达式是否存在
if (StrUtil.isBlank(trigger.getCronExpression())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "定时触发器缺少 CRON 表达式");
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
return false;
}
// 3. 定时触发器通常不依赖具体的设备消息
// 1.3 定时触发器通常不依赖具体的设备消息
// 它是通过定时任务调度器触发的,这里主要是验证配置的有效性
// 4. 可以添加 CRON 表达式格式验证
if (!isValidCronExpression(trigger.getCronExpression())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(getMatcherName(), message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
if (!CronExpression.isValidExpression(trigger.getCronExpression())) {
IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
return false;
}
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(getMatcherName(), message, trigger);
IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger);
return true;
}
/**
* 验证 CRON 表达式格式是否有效
*
* @param cronExpression CRON 表达式
* @return 是否有效
*/
private boolean isValidCronExpression(String cronExpression) {
// TODO @puhui999CronExpression.isValidExpression(cronExpression);
try {
// 简单的 CRON 表达式格式验证
// 标准 CRON 表达式应该有 6 或 7 个字段(秒 分 时 日 月 周 [年]
String[] fields = cronExpression.trim().split("\\s+");
return fields.length >= 6 && fields.length <= 7;
} catch (Exception e) {
return false;
}
}
@Override
public int getPriority() {
return 50; // 最低优先级,因为定时触发器不依赖消息
}
@Override
public boolean isEnabled() {
// 定时触发器可以根据配置动态启用/禁用
return true;
}
}

View File

@@ -172,19 +172,6 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
assertFalse(matched, "无效的触发器类型应该不匹配");
}
@Test
void testMatcherManagerStatistics() {
// 1. 执行测试
var statistics = matcherManager.getMatcherStatistics();
// 2. 验证结果
assertNotNull(statistics);
assertEquals(5, statistics.get("totalMatchers"));
assertEquals(5, statistics.get("enabledMatchers"));
assertNotNull(statistics.get("supportedTriggerTypes"));
assertNotNull(statistics.get("matcherDetails"));
}
@Test
void testGetSupportedTriggerTypes() {
// 1. 执行测试