From 057f924bd165d3253e06bea213321b77e2b983c8 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 19 Sep 2025 10:59:32 +0800 Subject: [PATCH] refactor: 【IoT 物联网】统一场景联动 ‘IoT’ 前缀 --- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcher.java | 228 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcher.java | 68 -------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcher.java | 60 ------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcher.java | 73 ------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcher.java | 75 --------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcher.java | 57 --------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcher.java | 69 --------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcher.java | 56 -------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/BaseMatcherTest.java | 33 --------------------------------- yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java | 32 ++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcherTest.java | 342 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcherTest.java | 492 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcherTest.java | 360 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcherTest.java | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcherTest.java | 492 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcherTest.java | 380 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcherTest.java | 344 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcherTest.java | 402 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcherTest.java | 278 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcherTest.java | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcherTest.java | 344 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcherTest.java | 402 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcherTest.java | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcherTest.java | 280 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 35 files changed, 3709 insertions(+), 3597 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcher.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/BaseMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcherTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcherTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcher.java deleted file mode 100644 index cb85f36..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcher.java +++ /dev/null @@ -1,228 +0,0 @@ -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; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; -import java.util.List; - -// TODO @puhui999:是不是 IoT 的前缀,都加下哈; -/** - * 当前时间条件匹配器:处理时间相关的子条件匹配逻辑 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class CurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher { - - /** - * 时间格式化器 - HH:mm:ss - */ - private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); - - /** - * 时间格式化器 - HH:mm - */ - private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern("HH:mm"); - - @Override - public IotSceneRuleConditionTypeEnum getSupportedConditionType() { - return IotSceneRuleConditionTypeEnum.CURRENT_TIME; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); - return false; - } - - // 1.2 检查操作符和参数是否有效 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); - return false; - } - - // 1.3 验证操作符是否为支持的时间操作符 - String operator = condition.getOperator(); - 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 executeTimeMatching(IotSceneRuleConditionOperatorEnum operatorEnum, String param) { - try { - LocalDateTime now = LocalDateTime.now(); - - if (isDateTimeOperator(operatorEnum)) { - // 日期时间匹配(时间戳) - long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8")); - return matchDateTime(currentTimestamp, operatorEnum, param); - } else { - // 当日时间匹配(HH:mm:ss) - return matchTime(now.toLocalTime(), operatorEnum, param); - } - } catch (Exception 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); - switch (operatorEnum) { - case DATE_TIME_GREATER_THAN: - return currentTimestamp > targetTimestamp; - case DATE_TIME_LESS_THAN: - return currentTimestamp < targetTimestamp; - case DATE_TIME_BETWEEN: - return matchDateTimeBetween(currentTimestamp, param); - default: - log.warn("[matchDateTime][operatorEnum({}) 不支持的日期时间操作符]", operatorEnum); - return false; - } - } catch (Exception e) { - log.error("[matchDateTime][operatorEnum({}) param({}) 日期时间匹配异常]", operatorEnum, param, e); - return false; - } - } - - /** - * 匹配日期时间区间 - */ - private boolean matchDateTimeBetween(long currentTimestamp, String param) { - List 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); - switch (operatorEnum) { - case TIME_GREATER_THAN: - return currentTime.isAfter(targetTime); - case TIME_LESS_THAN: - return currentTime.isBefore(targetTime); - case TIME_BETWEEN: - return matchTimeBetween(currentTime, param); - default: - log.warn("[matchTime][operatorEnum({}) 不支持的时间操作符]", operatorEnum); - return false; - } - } catch (Exception e) { - log.error("[matchTime][][operatorEnum({}) param({}) 时间解析异常]", operatorEnum, param, e); - return false; - } - } - - /** - * 匹配时间区间 - */ - private boolean matchTimeBetween(LocalTime currentTime, String param) { - List 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) { - Assert.isFalse(StrUtil.isBlank(timeStr), "时间字符串不能为空"); - - try { - // 尝试不同的时间格式 - if (timeStr.length() == 5) { // HH:mm - return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT); - } 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); - } - } - - @Override - public int getPriority() { - return 40; // 较低优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcher.java deleted file mode 100644 index f4316c0..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcher.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - - -/** - * 设备属性条件匹配器:处理设备属性相关的子条件匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class DevicePropertyConditionMatcher implements IotSceneRuleConditionMatcher { - - @Override - public IotSceneRuleConditionTypeEnum getSupportedConditionType() { - return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); - return false; - } - - // 1.2 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 1.3 检查操作符和参数是否有效 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); - return false; - } - - // 2.1. 获取属性值 - 使用工具类方法正确提取属性值 - Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, condition.getIdentifier()); - if (propertyValue == null) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空或未找到指定属性"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam()); - if (matched) { - IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); - } else { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备属性条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 25; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcher.java deleted file mode 100644 index ceb1ee8..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcher.java +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.springframework.stereotype.Component; - -/** - * 设备状态条件匹配器:处理设备状态相关的子条件匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class DeviceStateConditionMatcher implements IotSceneRuleConditionMatcher { - - @Override - public IotSceneRuleConditionTypeEnum getSupportedConditionType() { - return IotSceneRuleConditionTypeEnum.DEVICE_STATE; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); - return false; - } - - // 1.2 检查操作符和参数是否有效 - if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); - return false; - } - - // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值 - // 对于设备状态条件,状态值通过 getIdentifier 获取(实际是从 params.state 字段) - String stateValue = IotDeviceMessageUtils.getIdentifier(message); - if (stateValue == null) { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam()); - if (matched) { - IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); - } else { - IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备状态条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 30; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java new file mode 100644 index 0000000..2083beb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java @@ -0,0 +1,227 @@ +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; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +/** + * 当前时间条件匹配器:处理时间相关的子条件匹配逻辑 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotCurrentTimeConditionMatcher implements IotSceneRuleConditionMatcher { + + /** + * 时间格式化器 - HH:mm:ss + */ + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); + + /** + * 时间格式化器 - HH:mm + */ + private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern("HH:mm"); + + @Override + public IotSceneRuleConditionTypeEnum getSupportedConditionType() { + return IotSceneRuleConditionTypeEnum.CURRENT_TIME; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); + return false; + } + + // 1.2 检查操作符和参数是否有效 + if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); + return false; + } + + // 1.3 验证操作符是否为支持的时间操作符 + String operator = condition.getOperator(); + 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 executeTimeMatching(IotSceneRuleConditionOperatorEnum operatorEnum, String param) { + try { + LocalDateTime now = LocalDateTime.now(); + + if (isDateTimeOperator(operatorEnum)) { + // 日期时间匹配(时间戳) + long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8")); + return matchDateTime(currentTimestamp, operatorEnum, param); + } else { + // 当日时间匹配(HH:mm:ss) + return matchTime(now.toLocalTime(), operatorEnum, param); + } + } catch (Exception 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); + switch (operatorEnum) { + case DATE_TIME_GREATER_THAN: + return currentTimestamp > targetTimestamp; + case DATE_TIME_LESS_THAN: + return currentTimestamp < targetTimestamp; + case DATE_TIME_BETWEEN: + return matchDateTimeBetween(currentTimestamp, param); + default: + log.warn("[matchDateTime][operatorEnum({}) 不支持的日期时间操作符]", operatorEnum); + return false; + } + } catch (Exception e) { + log.error("[matchDateTime][operatorEnum({}) param({}) 日期时间匹配异常]", operatorEnum, param, e); + return false; + } + } + + /** + * 匹配日期时间区间 + */ + private boolean matchDateTimeBetween(long currentTimestamp, String param) { + List 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); + switch (operatorEnum) { + case TIME_GREATER_THAN: + return currentTime.isAfter(targetTime); + case TIME_LESS_THAN: + return currentTime.isBefore(targetTime); + case TIME_BETWEEN: + return matchTimeBetween(currentTime, param); + default: + log.warn("[matchTime][operatorEnum({}) 不支持的时间操作符]", operatorEnum); + return false; + } + } catch (Exception e) { + log.error("[matchTime][][operatorEnum({}) param({}) 时间解析异常]", operatorEnum, param, e); + return false; + } + } + + /** + * 匹配时间区间 + */ + private boolean matchTimeBetween(LocalTime currentTime, String param) { + List 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) { + Assert.isFalse(StrUtil.isBlank(timeStr), "时间字符串不能为空"); + + try { + // 尝试不同的时间格式 + if (timeStr.length() == 5) { // HH:mm + return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT); + } 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); + } + } + + @Override + public int getPriority() { + return 40; // 较低优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java new file mode 100644 index 0000000..c130c55 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; + + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; +import org.springframework.stereotype.Component; + + +/** + * 设备属性条件匹配器:处理设备属性相关的子条件匹配逻辑 + * + * @author HUIHUI + */ +@Component +public class IotDevicePropertyConditionMatcher implements IotSceneRuleConditionMatcher { + + @Override + public IotSceneRuleConditionTypeEnum getSupportedConditionType() { + return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); + return false; + } + + // 1.2 检查标识符是否匹配 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); + if (!IotSceneRuleMatcherHelper.isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier); + return false; + } + + // 1.3 检查操作符和参数是否有效 + if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); + return false; + } + + // 2.1. 获取属性值 - 使用工具类方法正确提取属性值 + Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, condition.getIdentifier()); + if (propertyValue == null) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中属性值为空或未找到指定属性"); + return false; + } + + // 2.2 使用条件评估器进行匹配 + boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, condition.getOperator(), condition.getParam()); + if (matched) { + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); + } else { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备属性条件不匹配"); + } + return matched; + } + + @Override + public int getPriority() { + return 25; // 中等优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java new file mode 100644 index 0000000..2328122 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; +import org.springframework.stereotype.Component; + +/** + * 设备状态条件匹配器:处理设备状态相关的子条件匹配逻辑 + * + * @author HUIHUI + */ +@Component +public class IotDeviceStateConditionMatcher implements IotSceneRuleConditionMatcher { + + @Override + public IotSceneRuleConditionTypeEnum getSupportedConditionType() { + return IotSceneRuleConditionTypeEnum.DEVICE_STATE; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicConditionValid(condition)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "条件基础参数无效"); + return false; + } + + // 1.2 检查操作符和参数是否有效 + if (!IotSceneRuleMatcherHelper.isConditionOperatorAndParamValid(condition)) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "操作符或参数无效"); + return false; + } + + // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值 + // 对于设备状态条件,状态值通过 getIdentifier 获取(实际是从 params.state 字段) + String stateValue = IotDeviceMessageUtils.getIdentifier(message); + if (stateValue == null) { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "消息中设备状态值为空"); + return false; + } + + // 2.2 使用条件评估器进行匹配 + boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateValue, condition.getOperator(), condition.getParam()); + if (matched) { + IotSceneRuleMatcherHelper.logConditionMatchSuccess(message, condition); + } else { + IotSceneRuleMatcherHelper.logConditionMatchFailure(message, condition, "设备状态条件不匹配"); + } + return matched; + } + + @Override + public int getPriority() { + return 30; // 中等优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcher.java deleted file mode 100644 index 2a843d1..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcher.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -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.springframework.stereotype.Component; - -/** - * 设备事件上报触发器匹配器:处理设备事件上报的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class DeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.EVENT_POST.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + - IotDeviceMessageMethodEnum.EVENT_POST.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + - trigger.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 2. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配 - // 但如果配置了操作符和值,则需要进行条件匹配 - if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { - Object eventData = message.getData(); - if (eventData == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中事件数据为空"); - return false; - } - - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventData, trigger.getOperator(), trigger.getValue()); - if (!matched) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "事件数据条件不匹配"); - return false; - } - } - - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - return true; - } - - @Override - public int getPriority() { - return 30; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcher.java deleted file mode 100644 index fdaf68e..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcher.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -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.springframework.stereotype.Component; - -/** - * 设备属性上报触发器匹配器:处理设备属性数据上报的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class DevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + - IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + - trigger.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 1.4 检查操作符和值是否有效 - if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); - return false; - } - - // 2.1 获取属性值 - 使用工具类方法正确提取属性值 - Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, trigger.getIdentifier()); - if (propertyValue == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空或未找到指定属性"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); - if (matched) { - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - } else { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "属性值条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 20; // 中等优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcher.java deleted file mode 100644 index 2c357fb..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcher.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -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.springframework.stereotype.Component; - -/** - * 设备服务调用触发器匹配器:处理设备服务调用的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class DeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查标识符是否匹配 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); - return false; - } - - // 2. 对于服务调用触发器,通常只需要匹配服务标识符即可 - // 不需要检查操作符和值,因为服务调用本身就是触发条件 - // TODO @puhui999: 服务调用时校验输入参数是否匹配条件 - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - return true; - } - - @Override - public int getPriority() { - return 40; // 较低优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcher.java deleted file mode 100644 index b51716f..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcher.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -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.springframework.stereotype.Component; - -/** - * 设备状态更新触发器匹配器:处理设备上下线状态变更的触发器匹配逻辑 - * - * @author HUIHUI - */ -@Component -public class DeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查消息方法是否匹配 - if (!IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod().equals(message.getMethod())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + - IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod() + ", 实际: " + message.getMethod()); - return false; - } - - // 1.3 检查操作符和值是否有效 - if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); - return false; - } - - // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值 - // 对于状态更新消息,状态值通过 getIdentifier 获取(实际是从 params.state 字段) - String stateIdentifier = IotDeviceMessageUtils.getIdentifier(message); - if (stateIdentifier == null) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空"); - return false; - } - - // 2.2 使用条件评估器进行匹配 - // 状态值通常是字符串或数字,直接使用标识符作为状态值 - boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateIdentifier, trigger.getOperator(), trigger.getValue()); - if (matched) { - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - } else { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "状态值条件不匹配"); - } - return matched; - } - - @Override - public int getPriority() { - return 10; // 高优先级 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java new file mode 100644 index 0000000..825b5ea --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +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.springframework.stereotype.Component; + +/** + * 设备事件上报触发器匹配器:处理设备事件上报的触发器匹配逻辑 + * + * @author HUIHUI + */ +@Component +public class IotDeviceEventPostTriggerMatcher implements IotSceneRuleTriggerMatcher { + + @Override + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { + return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); + return false; + } + + // 1.2 检查消息方法是否匹配 + if (!IotDeviceMessageMethodEnum.EVENT_POST.getMethod().equals(message.getMethod())) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + + IotDeviceMessageMethodEnum.EVENT_POST.getMethod() + ", 实际: " + message.getMethod()); + return false; + } + + // 1.3 检查标识符是否匹配 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); + if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + + trigger.getIdentifier() + ", 实际: " + messageIdentifier); + return false; + } + + // 2. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配 + // 但如果配置了操作符和值,则需要进行条件匹配 + if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) { + Object eventData = message.getData(); + if (eventData == null) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中事件数据为空"); + return false; + } + + boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(eventData, trigger.getOperator(), trigger.getValue()); + if (!matched) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "事件数据条件不匹配"); + return false; + } + } + + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); + return true; + } + + @Override + public int getPriority() { + return 30; // 中等优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java new file mode 100644 index 0000000..27cb02a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +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.springframework.stereotype.Component; + +/** + * 设备属性上报触发器匹配器:处理设备属性数据上报的触发器匹配逻辑 + * + * @author HUIHUI + */ +@Component +public class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerMatcher { + + @Override + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { + return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); + return false; + } + + // 1.2 检查消息方法是否匹配 + if (!IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod().equals(message.getMethod())) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + + IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() + ", 实际: " + message.getMethod()); + return false; + } + + // 1.3 检查标识符是否匹配 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); + if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + + trigger.getIdentifier() + ", 实际: " + messageIdentifier); + return false; + } + + // 1.4 检查操作符和值是否有效 + if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); + return false; + } + + // 2.1 获取属性值 - 使用工具类方法正确提取属性值 + Object propertyValue = IotDeviceMessageUtils.extractPropertyValue(message, trigger.getIdentifier()); + if (propertyValue == null) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中属性值为空或未找到指定属性"); + return false; + } + + // 2.2 使用条件评估器进行匹配 + boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); + if (matched) { + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); + } else { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "属性值条件不匹配"); + } + return matched; + } + + @Override + public int getPriority() { + return 20; // 中等优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java new file mode 100644 index 0000000..d3a73f8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +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.springframework.stereotype.Component; + +/** + * 设备服务调用触发器匹配器:处理设备服务调用的触发器匹配逻辑 + * + * @author HUIHUI + */ +@Component +public class IotDeviceServiceInvokeTriggerMatcher implements IotSceneRuleTriggerMatcher { + + @Override + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { + return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); + return false; + } + + // 1.2 检查消息方法是否匹配 + if (!IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod().equals(message.getMethod())) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod() + ", 实际: " + message.getMethod()); + return false; + } + + // 1.3 检查标识符是否匹配 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); + if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier); + return false; + } + + // 2. 对于服务调用触发器,通常只需要匹配服务标识符即可 + // 不需要检查操作符和值,因为服务调用本身就是触发条件 + // TODO @puhui999: 服务调用时校验输入参数是否匹配条件 + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); + return true; + } + + @Override + public int getPriority() { + return 40; // 较低优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java new file mode 100644 index 0000000..6b8c73a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +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.springframework.stereotype.Component; + +/** + * 设备状态更新触发器匹配器:处理设备上下线状态变更的触发器匹配逻辑 + * + * @author HUIHUI + */ +@Component +public class IotDeviceStateUpdateTriggerMatcher implements IotSceneRuleTriggerMatcher { + + @Override + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { + return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); + return false; + } + + // 1.2 检查消息方法是否匹配 + if (!IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod().equals(message.getMethod())) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + + IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod() + ", 实际: " + message.getMethod()); + return false; + } + + // 1.3 检查操作符和值是否有效 + if (!IotSceneRuleMatcherHelper.isTriggerOperatorAndValueValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "操作符或值无效"); + return false; + } + + // 2.1 获取设备状态值 - 使用工具类方法正确提取状态值 + // 对于状态更新消息,状态值通过 getIdentifier 获取(实际是从 params.state 字段) + String stateIdentifier = IotDeviceMessageUtils.getIdentifier(message); + if (stateIdentifier == null) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中设备状态值为空"); + return false; + } + + // 2.2 使用条件评估器进行匹配 + // 状态值通常是字符串或数字,直接使用标识符作为状态值 + boolean matched = IotSceneRuleMatcherHelper.evaluateCondition(stateIdentifier, trigger.getOperator(), trigger.getValue()); + if (matched) { + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); + } else { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "状态值条件不匹配"); + } + return matched; + } + + @Override + public int getPriority() { + return 10; // 高优先级 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java new file mode 100644 index 0000000..f980c24 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcher.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +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.IotSceneRuleTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; +import org.quartz.CronExpression; +import org.springframework.stereotype.Component; + +/** + * 定时触发器匹配器:处理定时触发的触发器匹配逻辑 + * + * 注意:定时触发器不依赖设备消息,主要用于定时任务场景 + * + * @author HUIHUI + */ +@Component +public class IotTimerTriggerMatcher implements IotSceneRuleTriggerMatcher { + + @Override + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { + return IotSceneRuleTriggerTypeEnum.TIMER; + } + + @Override + public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { + // 1.1 基础参数校验 + if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); + return false; + } + + // 1.2 检查 CRON 表达式是否存在 + if (StrUtil.isBlank(trigger.getCronExpression())) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); + return false; + } + + // 1.3 定时触发器通常不依赖具体的设备消息 + // 它是通过定时任务调度器触发的,这里主要是验证配置的有效性 + if (!CronExpression.isValidExpression(trigger.getCronExpression())) { + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); + return false; + } + + IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); + return true; + } + + @Override + public int getPriority() { + return 50; // 最低优先级,因为定时触发器不依赖消息 + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcher.java deleted file mode 100644 index 34cc595..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcher.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -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.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherHelper; -import org.quartz.CronExpression; -import org.springframework.stereotype.Component; - -/** - * 定时触发器匹配器:处理定时触发的触发器匹配逻辑 - * - * 注意:定时触发器不依赖设备消息,主要用于定时任务场景 - * - * @author HUIHUI - */ -@Component -public class TimerTriggerMatcher implements IotSceneRuleTriggerMatcher { - - @Override - public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() { - return IotSceneRuleTriggerTypeEnum.TIMER; - } - - @Override - public boolean matches(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { - // 1.1 基础参数校验 - if (!IotSceneRuleMatcherHelper.isBasicTriggerValid(trigger)) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "触发器基础参数无效"); - return false; - } - - // 1.2 检查 CRON 表达式是否存在 - if (StrUtil.isBlank(trigger.getCronExpression())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式"); - return false; - } - - // 1.3 定时触发器通常不依赖具体的设备消息 - // 它是通过定时任务调度器触发的,这里主要是验证配置的有效性 - if (!CronExpression.isValidExpression(trigger.getCronExpression())) { - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression()); - return false; - } - - IotSceneRuleMatcherHelper.logTriggerMatchSuccess(message, trigger); - return true; - } - - @Override - public int getPriority() { - return 50; // 最低优先级,因为定时触发器不依赖消息 - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/BaseMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/BaseMatcherTest.java deleted file mode 100644 index b53e48d..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/BaseMatcherTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; - -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; - -// TODO @puhui999:建议改成 IotBaseConditionMatcherTest -/** - * Matcher 测试基类 - * 提供通用的 Spring 测试配置 - * - * @author HUIHUI - */ -@SpringJUnitConfig -public abstract class BaseMatcherTest { - - /** - * 注入一下 SpringUtil,解析 EL 表达式时需要 - * {@link SpringExpressionUtils#parseExpression} - */ - @Configuration - static class TestConfig { - - @Bean - public SpringUtil springUtil() { - return new SpringUtil(); - } - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java new file mode 100644 index 0000000..5be63b5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +/** + * Matcher 测试基类 + * 提供通用的 Spring 测试配置 + * + * @author HUIHUI + */ +@SpringJUnitConfig +public abstract class IotBaseConditionMatcherTest { + + /** + * 注入一下 SpringUtil,解析 EL 表达式时需要 + * {@link SpringExpressionUtils#parseExpression} + */ + @Configuration + static class TestConfig { + + @Bean + public SpringUtil springUtil() { + return new SpringUtil(); + } + + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcherTest.java deleted file mode 100644 index f14da99..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcherTest.java +++ /dev/null @@ -1,342 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -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.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link CurrentTimeConditionMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class CurrentTimeConditionMatcherTest extends BaseMatcherTest { - - private CurrentTimeConditionMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new CurrentTimeConditionMatcher(); - } - - @Test - public void testGetSupportedConditionType() { - // 调用 - IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType(); - - // 断言 - assertEquals(IotSceneRuleConditionTypeEnum.CURRENT_TIME, result); - } - - @Test - public void testGetPriority() { - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(40, result); - } - - @Test - public void testIsEnabled() { - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - // ========== 时间戳条件测试 ========== - - @Test - public void testMatches_DateTimeGreaterThan_success() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - long pastTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8")); - IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( - IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), - String.valueOf(pastTimestamp) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_DateTimeGreaterThan_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); - IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( - IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), - String.valueOf(futureTimestamp) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_DateTimeLessThan_success() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); - IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( - IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN.getOperator(), - String.valueOf(futureTimestamp) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_DateTimeBetween_success() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - long startTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8")); - long endTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); - IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( - IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(), - startTimestamp + "," + endTimestamp - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_DateTimeBetween_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - long startTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); - long endTimestamp = LocalDateTime.now().plusHours(2).toEpochSecond(ZoneOffset.of("+8")); - IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( - IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(), - startTimestamp + "," + endTimestamp - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - // ========== 当日时间条件测试 ========== - - @Test - public void testMatches_TimeGreaterThan_earlyMorning() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createTimeCondition( - IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(), - "06:00:00" // 早上6点 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - // 结果取决于当前时间,如果当前时间大于6点则为true - assertNotNull(result); - } - - @Test - public void testMatches_TimeLessThan_lateNight() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createTimeCondition( - IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN.getOperator(), - "23:59:59" // 晚上11点59分59秒 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - // 大部分情况下应该为true,除非在午夜前1秒运行测试 - assertNotNull(result); - } - - @Test - public void testMatches_TimeBetween_allDay() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createTimeCondition( - IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), - "00:00:00,23:59:59" // 全天 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); // 全天范围应该总是匹配 - } - - @Test - public void testMatches_TimeBetween_workingHours() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createTimeCondition( - IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), - "09:00:00,17:00:00" // 工作时间 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - // 结果取决于当前时间是否在工作时间内 - assertNotNull(result); - } - - // ========== 异常情况测试 ========== - - @Test - public void testMatches_nullCondition() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullConditionType() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(null); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidOperator() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); - condition.setOperator(randomString()); // 随机无效操作符 - condition.setParam("12:00:00"); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidTimeFormat() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createTimeCondition( - IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(), - randomString() // 随机无效时间格式 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidTimestampFormat() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( - IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), - randomString() // 随机无效时间戳格式 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidBetweenFormat() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.TriggerCondition condition = createTimeCondition( - IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), - "09:00:00" // 缺少结束时间 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建设备消息 - */ - private IotDeviceMessage createDeviceMessage() { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - return message; - } - - /** - * 创建日期时间条件 - */ - private IotSceneRuleDO.TriggerCondition createDateTimeCondition(String operator, String param) { - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); - condition.setOperator(operator); - condition.setParam(param); - return condition; - } - - /** - * 创建当日时间条件 - */ - private IotSceneRuleDO.TriggerCondition createTimeCondition(String operator, String param) { - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); - condition.setOperator(operator); - condition.setParam(param); - return condition; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcherTest.java deleted file mode 100644 index ddfebc6..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcherTest.java +++ /dev/null @@ -1,492 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -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.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DevicePropertyConditionMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class DevicePropertyConditionMatcherTest extends BaseMatcherTest { - - private DevicePropertyConditionMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new DevicePropertyConditionMatcher(); - } - - @Test - public void testGetSupportedConditionType() { - // 调用 - IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType(); - - // 断言 - assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY, result); - } - - @Test - public void testGetPriority() { - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(25, result); // 修正:实际返回值是 25 - } - - @Test - public void testMatches_temperatureEquals_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "temperature"; - Double propertyValue = 25.5; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - String.valueOf(propertyValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_humidityGreaterThan_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "humidity"; - Integer propertyValue = 75; - Integer compareValue = 70; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_pressureLessThan_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "pressure"; - Double propertyValue = 1010.5; - Integer compareValue = 1020; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_statusNotEquals_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "status"; - String propertyValue = "active"; - String compareValue = "inactive"; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), - compareValue - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_propertyMismatch_fail() { - // 准备参数:创建属性上报消息,值不满足条件 - String propertyIdentifier = "temperature"; - Double propertyValue = 15.0; - Integer compareValue = 20; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_identifierMismatch_fail() { - // 准备参数:标识符不匹配 - String messageIdentifier = "temperature"; - String conditionIdentifier = "humidity"; - Double propertyValue = 25.5; - IotDeviceMessage message = createPropertyPostMessage(messageIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - conditionIdentifier, - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - String.valueOf(propertyValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullCondition_fail() { - // 准备参数 - IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullConditionType_fail() { - // 准备参数 - IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(null); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingIdentifier_fail() { - // 准备参数 - IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); - condition.setIdentifier(null); // 缺少标识符 - condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); - condition.setParam("20"); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingOperator_fail() { - // 准备参数 - IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); - condition.setIdentifier("temperature"); - condition.setOperator(null); // 缺少操作符 - condition.setParam("20"); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingParam_fail() { - // 准备参数 - IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); - condition.setIdentifier("temperature"); - condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); - condition.setParam(null); // 缺少参数 - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullMessage_fail() { - // 准备参数 - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - "temperature", - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - "20" - ); - - // 调用 - boolean result = matcher.matches(null, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullDeviceProperties_fail() { - // 准备参数:消息的 params 为 null - IotDeviceMessage message = new IotDeviceMessage(); - message.setParams(null); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - "temperature", - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - "20" - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_propertiesStructure_success() { - // 测试使用 properties 结构的消息(真实的属性上报场景) - String identifier = "temperature"; - Double propertyValue = 25.5; - IotDeviceMessage message = createPropertyPostMessageWithProperties(identifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - identifier, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - "20" - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言:修复后的实现应该能正确从 properties 中提取属性值 - assertTrue(result); - } - - @Test - public void testMatches_simpleValueMessage_success() { - // 测试简单值消息(params 直接是属性值) - Double propertyValue = 25.5; - IotDeviceMessage message = createSimpleValueMessage(propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - "any", // 对于简单值消息,标识符匹配会被跳过 - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - "20" - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言:修复后的实现应该能处理简单值消息 - // 但由于标识符匹配失败,结果为 false - assertFalse(result); - } - - @Test - public void testMatches_valueFieldStructure_success() { - // 测试使用 value 字段的消息结构 - String identifier = "temperature"; - Double propertyValue = 25.5; - - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod("thing.event.post"); - - Map params = new HashMap<>(); - params.put("identifier", identifier); - params.put("value", propertyValue); - message.setParams(params); - - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - identifier, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - "20" - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言:修复后的实现应该能从 value 字段提取属性值 - assertTrue(result); - } - - @Test - public void testMatches_voltageGreaterThanOrEquals_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "voltage"; - Double propertyValue = 12.0; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(), - String.valueOf(propertyValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_currentLessThanOrEquals_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "current"; - Double propertyValue = 2.5; - Double compareValue = 3.0; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_stringProperty_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "mode"; - String propertyValue = "auto"; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - propertyValue - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_booleanProperty_success() { - // 准备参数:创建属性上报消息 - String propertyIdentifier = "enabled"; - Boolean propertyValue = true; - IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - propertyIdentifier, - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - String.valueOf(propertyValue) - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建设备消息用于测试 - * - * 支持的消息格式: - * 1. 直接属性值:params 直接是属性值(适用于简单消息) - * 2. 标识符+值:params 包含 identifier 和对应的属性值 - * 3. properties 结构:params.properties[identifier] = value - * 4. data 结构:params.data[identifier] = value - * 5. value 字段:params.value = value - */ - private IotDeviceMessage createPropertyPostMessage(String identifier, Object value) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod("thing.event.post"); // 使用事件上报方法 - - // 创建符合修复后逻辑的 params 结构 - Map params = new HashMap<>(); - params.put("identifier", identifier); - // 直接将属性值放在标识符对应的字段中 - params.put(identifier, value); - message.setParams(params); - - return message; - } - - /** - * 创建使用 properties 结构的消息(模拟真实的属性上报消息) - */ - private IotDeviceMessage createPropertyPostMessageWithProperties(String identifier, Object value) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod("thing.property.post"); // 属性上报方法 - - Map properties = new HashMap<>(); - properties.put(identifier, value); - - Map params = new HashMap<>(); - params.put("properties", properties); - message.setParams(params); - - return message; - } - - /** - * 创建简单值消息(params 直接是属性值) - */ - private IotDeviceMessage createSimpleValueMessage(Object value) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod("thing.property.post"); - // 直接将属性值作为 params - message.setParams(value); - - return message; - } - - /** - * 创建有效的条件 - */ - private IotSceneRuleDO.TriggerCondition createValidCondition(String identifier, String operator, String param) { - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); - condition.setIdentifier(identifier); - condition.setOperator(operator); - condition.setParam(param); - return condition; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcherTest.java deleted file mode 100644 index d54575b..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcherTest.java +++ /dev/null @@ -1,360 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; - -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; -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.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DeviceStateConditionMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class DeviceStateConditionMatcherTest extends BaseMatcherTest { - - private DeviceStateConditionMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new DeviceStateConditionMatcher(); - } - - @Test - public void testGetSupportedConditionType() { - // 调用 - IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType(); - - // 断言 - assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_STATE, result); - } - - @Test - public void testGetPriority() { - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(30, result); - } - - @Test - public void testIsEnabled() { - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_onlineState_success() { - // 准备参数 - IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE; - IotDeviceMessage message = createDeviceMessage(deviceState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - deviceState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_offlineState_success() { - // 准备参数 - IotDeviceStateEnum deviceState = IotDeviceStateEnum.OFFLINE; - IotDeviceMessage message = createDeviceMessage(deviceState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - deviceState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_inactiveState_success() { - // 准备参数 - IotDeviceStateEnum deviceState = IotDeviceStateEnum.INACTIVE; - IotDeviceMessage message = createDeviceMessage(deviceState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - deviceState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_stateMismatch_fail() { - // 准备参数 - IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; - IotDeviceStateEnum expectedState = IotDeviceStateEnum.OFFLINE; - IotDeviceMessage message = createDeviceMessage(actualState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - expectedState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_notEqualsOperator_success() { - // 准备参数 - IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; - IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE; - IotDeviceMessage message = createDeviceMessage(actualState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), - compareState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_greaterThanOperator_success() { - // 准备参数 - IotDeviceStateEnum actualState = IotDeviceStateEnum.OFFLINE; // 状态值为 2 - IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1 - IotDeviceMessage message = createDeviceMessage(actualState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - compareState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_lessThanOperator_success() { - // 准备参数 - IotDeviceStateEnum actualState = IotDeviceStateEnum.INACTIVE; // 状态值为 0 - IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1 - IotDeviceMessage message = createDeviceMessage(actualState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), - compareState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_nullCondition_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullConditionType_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(null); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingOperator_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); - condition.setOperator(null); - condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString()); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingParam_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); - condition.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator()); - condition.setParam(null); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullMessage_fail() { - // 准备参数 - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.ONLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(null, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullDeviceState_fail() { - // 准备参数 - IotDeviceMessage message = new IotDeviceMessage(); - message.setParams(null); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.ONLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_greaterThanOrEqualsOperator_success() { - // 准备参数 - IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE; // 状态值为 1 - IotDeviceMessage message = createDeviceMessage(deviceState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(), - deviceState.getState().toString() // 比较值也为 1 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_lessThanOrEqualsOperator_success() { - // 准备参数 - IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; // 状态值为 1 - IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE; // 状态值为 2 - IotDeviceMessage message = createDeviceMessage(actualState.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(), - compareState.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_invalidOperator_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); - condition.setOperator(randomString()); // 随机无效操作符 - condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString()); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidParamFormat_fail() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.TriggerCondition condition = createValidCondition( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - randomString() // 随机无效状态值 - ); - - // 调用 - boolean result = matcher.matches(message, condition); - - // 断言 - assertFalse(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建设备消息 - */ - private IotDeviceMessage createDeviceMessage(Integer deviceState) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setParams(deviceState); - return message; - } - - /** - * 创建有效的条件 - */ - private IotSceneRuleDO.TriggerCondition createValidCondition(String operator, String param) { - IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); - condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); - condition.setOperator(operator); - condition.setParam(param); - return condition; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcherTest.java new file mode 100644 index 0000000..586d948 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcherTest.java @@ -0,0 +1,342 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; + +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.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotCurrentTimeConditionMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotCurrentTimeConditionMatcherTest extends IotBaseConditionMatcherTest { + + private IotCurrentTimeConditionMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotCurrentTimeConditionMatcher(); + } + + @Test + public void testGetSupportedConditionType() { + // 调用 + IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType(); + + // 断言 + assertEquals(IotSceneRuleConditionTypeEnum.CURRENT_TIME, result); + } + + @Test + public void testGetPriority() { + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(40, result); + } + + @Test + public void testIsEnabled() { + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + // ========== 时间戳条件测试 ========== + + @Test + public void testMatches_DateTimeGreaterThan_success() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + long pastTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8")); + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( + IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), + String.valueOf(pastTimestamp) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_DateTimeGreaterThan_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( + IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), + String.valueOf(futureTimestamp) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_DateTimeLessThan_success() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( + IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN.getOperator(), + String.valueOf(futureTimestamp) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_DateTimeBetween_success() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + long startTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8")); + long endTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( + IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(), + startTimestamp + "," + endTimestamp + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_DateTimeBetween_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + long startTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); + long endTimestamp = LocalDateTime.now().plusHours(2).toEpochSecond(ZoneOffset.of("+8")); + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( + IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(), + startTimestamp + "," + endTimestamp + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + // ========== 当日时间条件测试 ========== + + @Test + public void testMatches_TimeGreaterThan_earlyMorning() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( + IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(), + "06:00:00" // 早上6点 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + // 结果取决于当前时间,如果当前时间大于6点则为true + assertNotNull(result); + } + + @Test + public void testMatches_TimeLessThan_lateNight() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( + IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN.getOperator(), + "23:59:59" // 晚上11点59分59秒 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + // 大部分情况下应该为true,除非在午夜前1秒运行测试 + assertNotNull(result); + } + + @Test + public void testMatches_TimeBetween_allDay() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( + IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), + "00:00:00,23:59:59" // 全天 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); // 全天范围应该总是匹配 + } + + @Test + public void testMatches_TimeBetween_workingHours() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( + IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), + "09:00:00,17:00:00" // 工作时间 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + // 结果取决于当前时间是否在工作时间内 + assertNotNull(result); + } + + // ========== 异常情况测试 ========== + + @Test + public void testMatches_nullCondition() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullConditionType() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(null); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidOperator() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); + condition.setOperator(randomString()); // 随机无效操作符 + condition.setParam("12:00:00"); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidTimeFormat() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( + IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(), + randomString() // 随机无效时间格式 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidTimestampFormat() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( + IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), + randomString() // 随机无效时间戳格式 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidBetweenFormat() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( + IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), + "09:00:00" // 缺少结束时间 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建设备消息 + */ + private IotDeviceMessage createDeviceMessage() { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + return message; + } + + /** + * 创建日期时间条件 + */ + private IotSceneRuleDO.TriggerCondition createDateTimeCondition(String operator, String param) { + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); + condition.setOperator(operator); + condition.setParam(param); + return condition; + } + + /** + * 创建当日时间条件 + */ + private IotSceneRuleDO.TriggerCondition createTimeCondition(String operator, String param) { + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); + condition.setOperator(operator); + condition.setParam(param); + return condition; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcherTest.java new file mode 100644 index 0000000..5a40995 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcherTest.java @@ -0,0 +1,492 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; + +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.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotDevicePropertyConditionMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotDevicePropertyConditionMatcherTest extends IotBaseConditionMatcherTest { + + private IotDevicePropertyConditionMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotDevicePropertyConditionMatcher(); + } + + @Test + public void testGetSupportedConditionType() { + // 调用 + IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType(); + + // 断言 + assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY, result); + } + + @Test + public void testGetPriority() { + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(25, result); // 修正:实际返回值是 25 + } + + @Test + public void testMatches_temperatureEquals_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "temperature"; + Double propertyValue = 25.5; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + String.valueOf(propertyValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_humidityGreaterThan_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "humidity"; + Integer propertyValue = 75; + Integer compareValue = 70; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_pressureLessThan_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "pressure"; + Double propertyValue = 1010.5; + Integer compareValue = 1020; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_statusNotEquals_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "status"; + String propertyValue = "active"; + String compareValue = "inactive"; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), + compareValue + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_propertyMismatch_fail() { + // 准备参数:创建属性上报消息,值不满足条件 + String propertyIdentifier = "temperature"; + Double propertyValue = 15.0; + Integer compareValue = 20; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_identifierMismatch_fail() { + // 准备参数:标识符不匹配 + String messageIdentifier = "temperature"; + String conditionIdentifier = "humidity"; + Double propertyValue = 25.5; + IotDeviceMessage message = createPropertyPostMessage(messageIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + conditionIdentifier, + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + String.valueOf(propertyValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullCondition_fail() { + // 准备参数 + IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullConditionType_fail() { + // 准备参数 + IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(null); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingIdentifier_fail() { + // 准备参数 + IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); + condition.setIdentifier(null); // 缺少标识符 + condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); + condition.setParam("20"); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingOperator_fail() { + // 准备参数 + IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); + condition.setIdentifier("temperature"); + condition.setOperator(null); // 缺少操作符 + condition.setParam("20"); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingParam_fail() { + // 准备参数 + IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); + condition.setIdentifier("temperature"); + condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); + condition.setParam(null); // 缺少参数 + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullMessage_fail() { + // 准备参数 + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + "temperature", + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + "20" + ); + + // 调用 + boolean result = matcher.matches(null, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullDeviceProperties_fail() { + // 准备参数:消息的 params 为 null + IotDeviceMessage message = new IotDeviceMessage(); + message.setParams(null); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + "temperature", + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + "20" + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_propertiesStructure_success() { + // 测试使用 properties 结构的消息(真实的属性上报场景) + String identifier = "temperature"; + Double propertyValue = 25.5; + IotDeviceMessage message = createPropertyPostMessageWithProperties(identifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + identifier, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + "20" + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言:修复后的实现应该能正确从 properties 中提取属性值 + assertTrue(result); + } + + @Test + public void testMatches_simpleValueMessage_success() { + // 测试简单值消息(params 直接是属性值) + Double propertyValue = 25.5; + IotDeviceMessage message = createSimpleValueMessage(propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + "any", // 对于简单值消息,标识符匹配会被跳过 + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + "20" + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言:修复后的实现应该能处理简单值消息 + // 但由于标识符匹配失败,结果为 false + assertFalse(result); + } + + @Test + public void testMatches_valueFieldStructure_success() { + // 测试使用 value 字段的消息结构 + String identifier = "temperature"; + Double propertyValue = 25.5; + + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod("thing.event.post"); + + Map params = new HashMap<>(); + params.put("identifier", identifier); + params.put("value", propertyValue); + message.setParams(params); + + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + identifier, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + "20" + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言:修复后的实现应该能从 value 字段提取属性值 + assertTrue(result); + } + + @Test + public void testMatches_voltageGreaterThanOrEquals_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "voltage"; + Double propertyValue = 12.0; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(), + String.valueOf(propertyValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_currentLessThanOrEquals_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "current"; + Double propertyValue = 2.5; + Double compareValue = 3.0; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_stringProperty_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "mode"; + String propertyValue = "auto"; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + propertyValue + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_booleanProperty_success() { + // 准备参数:创建属性上报消息 + String propertyIdentifier = "enabled"; + Boolean propertyValue = true; + IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + propertyIdentifier, + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + String.valueOf(propertyValue) + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建设备消息用于测试 + * + * 支持的消息格式: + * 1. 直接属性值:params 直接是属性值(适用于简单消息) + * 2. 标识符+值:params 包含 identifier 和对应的属性值 + * 3. properties 结构:params.properties[identifier] = value + * 4. data 结构:params.data[identifier] = value + * 5. value 字段:params.value = value + */ + private IotDeviceMessage createPropertyPostMessage(String identifier, Object value) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod("thing.event.post"); // 使用事件上报方法 + + // 创建符合修复后逻辑的 params 结构 + Map params = new HashMap<>(); + params.put("identifier", identifier); + // 直接将属性值放在标识符对应的字段中 + params.put(identifier, value); + message.setParams(params); + + return message; + } + + /** + * 创建使用 properties 结构的消息(模拟真实的属性上报消息) + */ + private IotDeviceMessage createPropertyPostMessageWithProperties(String identifier, Object value) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod("thing.property.post"); // 属性上报方法 + + Map properties = new HashMap<>(); + properties.put(identifier, value); + + Map params = new HashMap<>(); + params.put("properties", properties); + message.setParams(params); + + return message; + } + + /** + * 创建简单值消息(params 直接是属性值) + */ + private IotDeviceMessage createSimpleValueMessage(Object value) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod("thing.property.post"); + // 直接将属性值作为 params + message.setParams(value); + + return message; + } + + /** + * 创建有效的条件 + */ + private IotSceneRuleDO.TriggerCondition createValidCondition(String identifier, String operator, String param) { + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); + condition.setIdentifier(identifier); + condition.setOperator(operator); + condition.setParam(param); + return condition; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java new file mode 100644 index 0000000..da59077 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java @@ -0,0 +1,360 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; + +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; +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.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotDeviceStateConditionMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotDeviceStateConditionMatcherTest extends IotBaseConditionMatcherTest { + + private IotDeviceStateConditionMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotDeviceStateConditionMatcher(); + } + + @Test + public void testGetSupportedConditionType() { + // 调用 + IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType(); + + // 断言 + assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_STATE, result); + } + + @Test + public void testGetPriority() { + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(30, result); + } + + @Test + public void testIsEnabled() { + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_onlineState_success() { + // 准备参数 + IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE; + IotDeviceMessage message = createDeviceMessage(deviceState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + deviceState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_offlineState_success() { + // 准备参数 + IotDeviceStateEnum deviceState = IotDeviceStateEnum.OFFLINE; + IotDeviceMessage message = createDeviceMessage(deviceState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + deviceState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_inactiveState_success() { + // 准备参数 + IotDeviceStateEnum deviceState = IotDeviceStateEnum.INACTIVE; + IotDeviceMessage message = createDeviceMessage(deviceState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + deviceState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_stateMismatch_fail() { + // 准备参数 + IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; + IotDeviceStateEnum expectedState = IotDeviceStateEnum.OFFLINE; + IotDeviceMessage message = createDeviceMessage(actualState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + expectedState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_notEqualsOperator_success() { + // 准备参数 + IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; + IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE; + IotDeviceMessage message = createDeviceMessage(actualState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), + compareState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_greaterThanOperator_success() { + // 准备参数 + IotDeviceStateEnum actualState = IotDeviceStateEnum.OFFLINE; // 状态值为 2 + IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1 + IotDeviceMessage message = createDeviceMessage(actualState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + compareState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_lessThanOperator_success() { + // 准备参数 + IotDeviceStateEnum actualState = IotDeviceStateEnum.INACTIVE; // 状态值为 0 + IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1 + IotDeviceMessage message = createDeviceMessage(actualState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), + compareState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_nullCondition_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullConditionType_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(null); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingOperator_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); + condition.setOperator(null); + condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString()); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingParam_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); + condition.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator()); + condition.setParam(null); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullMessage_fail() { + // 准备参数 + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.ONLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(null, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullDeviceState_fail() { + // 准备参数 + IotDeviceMessage message = new IotDeviceMessage(); + message.setParams(null); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.ONLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_greaterThanOrEqualsOperator_success() { + // 准备参数 + IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE; // 状态值为 1 + IotDeviceMessage message = createDeviceMessage(deviceState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(), + deviceState.getState().toString() // 比较值也为 1 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_lessThanOrEqualsOperator_success() { + // 准备参数 + IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; // 状态值为 1 + IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE; // 状态值为 2 + IotDeviceMessage message = createDeviceMessage(actualState.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(), + compareState.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_invalidOperator_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); + condition.setOperator(randomString()); // 随机无效操作符 + condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString()); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidParamFormat_fail() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.TriggerCondition condition = createValidCondition( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + randomString() // 随机无效状态值 + ); + + // 调用 + boolean result = matcher.matches(message, condition); + + // 断言 + assertFalse(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建设备消息 + */ + private IotDeviceMessage createDeviceMessage(Integer deviceState) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setParams(deviceState); + return message; + } + + /** + * 创建有效的条件 + */ + private IotSceneRuleDO.TriggerCondition createValidCondition(String operator, String param) { + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); + condition.setOperator(operator); + condition.setParam(param); + return condition; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcherTest.java deleted file mode 100644 index 4569d43..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcherTest.java +++ /dev/null @@ -1,380 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -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.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomInt; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DeviceEventPostTriggerMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class DeviceEventPostTriggerMatcherTest extends BaseMatcherTest { - - private DeviceEventPostTriggerMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new DeviceEventPostTriggerMatcher(); - } - - @Test - public void testGetSupportedTriggerType_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); - - // 断言 - assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST, result); - } - - @Test - public void testGetPriority_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(30, result); - } - - @Test - public void testIsEnabled_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_alarmEventSuccess() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .put("message", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_errorEventSuccess() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("code", randomInt()) - .put("description", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_infoEventSuccess() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("status", randomString()) - .put("timestamp", System.currentTimeMillis()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_eventIdentifierMismatch() { - // 准备参数 - String messageIdentifier = randomString(); - String triggerIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", messageIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_wrongMessageMethod() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) - .build(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法 - message.setParams(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerIdentifier() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); - trigger.setIdentifier(null); // 缺少标识符 - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullMessageParams() { - // 准备参数 - String eventIdentifier = randomString(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); - message.setParams(null); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidMessageParams() { - // 准备参数 - String eventIdentifier = randomString(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); - message.setParams(randomString()); // 不是 Map 类型 - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingEventIdentifierInParams() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) // 缺少 identifier 字段 - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTrigger() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerType() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(null); - trigger.setIdentifier(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_complexEventValueSuccess() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("type", randomString()) - .put("duration", randomInt()) - .put("components", new String[]{randomString(), randomString()}) - .put("priority", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_emptyEventValueSuccess() { - // 准备参数 - String eventIdentifier = randomString(); - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.ofEntries()) // 空的事件值 - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_caseSensitiveIdentifierMismatch() { - // 准备参数 - String eventIdentifier = randomString().toUpperCase(); // 大写 - String triggerIdentifier = eventIdentifier.toLowerCase(); // 小写 - Map eventParams = MapUtil.builder(new HashMap()) - .put("identifier", eventIdentifier) - .put("value", MapUtil.builder(new HashMap()) - .put("level", randomString()) - .build()) - .build(); - IotDeviceMessage message = createEventPostMessage(eventParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - // 根据实际实现,这里可能需要调整期望结果 - // 如果实现是大小写敏感的,则应该为 false - assertFalse(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建事件上报消息 - */ - private IotDeviceMessage createEventPostMessage(Map eventParams) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); - message.setParams(eventParams); - return message; - } - - /** - * 创建有效的触发器 - */ - private IotSceneRuleDO.Trigger createValidTrigger(String identifier) { - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); - trigger.setIdentifier(identifier); - return trigger; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcherTest.java deleted file mode 100644 index eb191eb..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcherTest.java +++ /dev/null @@ -1,344 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -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.IotSceneRuleTriggerTypeEnum; -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomDouble; -import static cn.hutool.core.util.RandomUtil.randomInt; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DevicePropertyPostTriggerMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class DevicePropertyPostTriggerMatcherTest extends BaseMatcherTest { - - private DevicePropertyPostTriggerMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new DevicePropertyPostTriggerMatcher(); - } - - @Test - public void testGetSupportedTriggerType_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); - - // 断言 - assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST, result); - } - - @Test - public void testGetPriority_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(20, result); - } - - @Test - public void testIsEnabled_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_numericPropertyGreaterThanSuccess() { - // 准备参数 - String propertyName = randomString(); - Double propertyValue = 25.5; - Integer compareValue = 20; - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, propertyValue) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_integerPropertyEqualsSuccess() { - // 准备参数 - String propertyName = randomString(); - Integer propertyValue = randomInt(); - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, propertyValue) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - String.valueOf(propertyValue) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_propertyValueNotMeetCondition() { - // 准备参数 - String propertyName = randomString(); - Double propertyValue = 15.0; - Integer compareValue = 20; - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, propertyValue) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_propertyNotFound() { - // 准备参数 - String existingProperty = randomString(); - String missingProperty = randomString(); - Map properties = MapUtil.builder(new HashMap()) - .put(existingProperty, randomDouble()) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - missingProperty, // 不存在的属性 - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(randomInt()) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_wrongMessageMethod() { - // 准备参数 - String propertyName = randomString(); - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, randomDouble()) - .build(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); - message.setParams(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(randomInt()) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerIdentifier() { - // 准备参数 - String propertyName = randomString(); - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, randomDouble()) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); - trigger.setIdentifier(null); // 缺少标识符 - trigger.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); - trigger.setValue(String.valueOf(randomInt())); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullMessageParams() { - // 准备参数 - String propertyName = randomString(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); - message.setParams(null); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(randomInt()) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidMessageParams() { - // 准备参数 - String propertyName = randomString(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); - message.setParams(randomString()); // 不是 Map 类型 - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - String.valueOf(randomInt()) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_lessThanOperatorSuccess() { - // 准备参数 - String propertyName = randomString(); - Double propertyValue = 15.0; - Integer compareValue = 20; - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, propertyValue) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), - String.valueOf(compareValue) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_notEqualsOperatorSuccess() { - // 准备参数 - String propertyName = randomString(); - String propertyValue = randomString(); - String compareValue = randomString(); - Map properties = MapUtil.builder(new HashMap()) - .put(propertyName, propertyValue) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - propertyName, - IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), - compareValue - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_multiplePropertiesTargetPropertySuccess() { - // 准备参数 - String targetProperty = randomString(); - Integer targetValue = randomInt(); - Map properties = MapUtil.builder(new HashMap()) - .put(randomString(), randomDouble()) - .put(targetProperty, targetValue) - .put(randomString(), randomString()) - .build(); - IotDeviceMessage message = createPropertyPostMessage(properties); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - targetProperty, - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - String.valueOf(targetValue) - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建属性上报消息 - */ - private IotDeviceMessage createPropertyPostMessage(Map properties) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); - message.setParams(properties); - return message; - } - - /** - * 创建有效的触发器 - */ - private IotSceneRuleDO.Trigger createValidTrigger(String identifier, String operator, String value) { - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); - trigger.setIdentifier(identifier); - trigger.setOperator(operator); - trigger.setValue(value); - return trigger; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcherTest.java deleted file mode 100644 index cfa3660..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcherTest.java +++ /dev/null @@ -1,402 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -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.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DeviceServiceInvokeTriggerMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class DeviceServiceInvokeTriggerMatcherTest extends BaseMatcherTest { - - private DeviceServiceInvokeTriggerMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new DeviceServiceInvokeTriggerMatcher(); - } - - @Test - public void testGetSupportedTriggerType_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); - - // 断言 - assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE, result); - } - - @Test - public void testGetPriority_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(40, result); - } - - @Test - public void testIsEnabled_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_serviceInvokeSuccess() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_configServiceSuccess() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("interval", randomInt()) - .put("enabled", randomBoolean()) - .put("threshold", randomDouble()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_updateServiceSuccess() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("version", randomString()) - .put("url", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_serviceIdentifierMismatch() { - // 准备参数 - String messageIdentifier = randomString(); - String triggerIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", messageIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); // 不匹配的服务标识符 - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_wrongMessageMethod() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法 - message.setParams(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerIdentifier() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); - trigger.setIdentifier(null); // 缺少标识符 - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullMessageParams() { - // 准备参数 - String serviceIdentifier = randomString(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); - message.setParams(null); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_invalidMessageParams() { - // 准备参数 - String serviceIdentifier = randomString(); - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); - message.setParams(randomString()); // 不是 Map 类型 - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_missingServiceIdentifierInParams() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) // 缺少 identifier 字段 - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTrigger() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerType() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(null); - trigger.setIdentifier(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_emptyInputDataSuccess() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.ofEntries()) // 空的输入数据 - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_noInputDataSuccess() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - // 没有 inputData 字段 - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_complexInputDataSuccess() { - // 准备参数 - String serviceIdentifier = randomString(); - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("sensors", new String[]{randomString(), randomString(), randomString()}) - .put("precision", randomDouble()) - .put("duration", randomInt()) - .put("autoSave", randomBoolean()) - .put("config", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .put("level", randomString()) - .build()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_caseSensitiveIdentifierMismatch() { - // 准备参数 - String serviceIdentifier = randomString().toUpperCase(); // 大写 - String triggerIdentifier = serviceIdentifier.toLowerCase(); // 小写 - Map serviceParams = MapUtil.builder(new HashMap()) - .put("identifier", serviceIdentifier) - .put("inputData", MapUtil.builder(new HashMap()) - .put("mode", randomString()) - .build()) - .build(); - IotDeviceMessage message = createServiceInvokeMessage(serviceParams); - IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - // 根据实际实现,这里可能需要调整期望结果 - // 如果实现是大小写敏感的,则应该为 false - assertFalse(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建服务调用消息 - */ - private IotDeviceMessage createServiceInvokeMessage(Map serviceParams) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); - message.setParams(serviceParams); - return message; - } - - /** - * 创建有效的触发器 - */ - private IotSceneRuleDO.Trigger createValidTrigger(String identifier) { - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); - trigger.setIdentifier(identifier); - return trigger; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcherTest.java deleted file mode 100644 index bee6e07..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcherTest.java +++ /dev/null @@ -1,278 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; -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.IotSceneRuleTriggerTypeEnum; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DeviceStateUpdateTriggerMatcher} 的单元测试 - * - * @author HUIHUI - */ -@SpringJUnitConfig(DeviceStateUpdateTriggerMatcherTest.TestConfig.class) -public class DeviceStateUpdateTriggerMatcherTest { - - @Configuration - static class TestConfig { - @Bean - public SpringUtil springUtil() { - return new SpringUtil(); - } - } - - private DeviceStateUpdateTriggerMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new DeviceStateUpdateTriggerMatcher(); - } - - @Test - public void testGetSupportedTriggerType_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); - - // 断言 - assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE, result); - } - - @Test - public void testGetPriority_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(10, result); - } - - @Test - public void testIsEnabled_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_onlineStateSuccess() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.ONLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_offlineStateSuccess() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.OFFLINE.getState()); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.OFFLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_stateMismatch() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.OFFLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTrigger() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerType() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(null); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_wrongMessageMethod() { - // 准备参数 - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); - message.setParams(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.ONLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerOperator() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); - trigger.setOperator(null); - trigger.setValue(IotDeviceStateEnum.ONLINE.getState().toString()); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerValue() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); - trigger.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator()); - trigger.setValue(null); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullMessageParams() { - // 准备参数 - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); - message.setParams(null); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), - IotDeviceStateEnum.ONLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_greaterThanOperatorSuccess() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), - IotDeviceStateEnum.INACTIVE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_notEqualsOperatorSuccess() { - // 准备参数 - IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); - IotSceneRuleDO.Trigger trigger = createValidTrigger( - IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), - IotDeviceStateEnum.OFFLINE.getState().toString() - ); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建设备状态更新消息 - */ - private IotDeviceMessage createStateUpdateMessage(Integer state) { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); - message.setParams(state); - return message; - } - - /** - * 创建有效的触发器 - */ - private IotSceneRuleDO.Trigger createValidTrigger(String operator, String value) { - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); - trigger.setOperator(operator); - trigger.setValue(value); - return trigger; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcherTest.java new file mode 100644 index 0000000..9cf5142 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcherTest.java @@ -0,0 +1,380 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +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.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomInt; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotDeviceEventPostTriggerMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotDeviceEventPostTriggerMatcherTest extends IotBaseConditionMatcherTest { + + private IotDeviceEventPostTriggerMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotDeviceEventPostTriggerMatcher(); + } + + @Test + public void testGetSupportedTriggerType_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); + + // 断言 + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST, result); + } + + @Test + public void testGetPriority_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(30, result); + } + + @Test + public void testIsEnabled_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_alarmEventSuccess() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .put("message", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_errorEventSuccess() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("code", randomInt()) + .put("description", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_infoEventSuccess() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("status", randomString()) + .put("timestamp", System.currentTimeMillis()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_eventIdentifierMismatch() { + // 准备参数 + String messageIdentifier = randomString(); + String triggerIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", messageIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_wrongMessageMethod() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) + .build(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法 + message.setParams(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerIdentifier() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); + trigger.setIdentifier(null); // 缺少标识符 + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullMessageParams() { + // 准备参数 + String eventIdentifier = randomString(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); + message.setParams(null); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidMessageParams() { + // 准备参数 + String eventIdentifier = randomString(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); + message.setParams(randomString()); // 不是 Map 类型 + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingEventIdentifierInParams() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) // 缺少 identifier 字段 + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTrigger() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerType() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(null); + trigger.setIdentifier(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_complexEventValueSuccess() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("type", randomString()) + .put("duration", randomInt()) + .put("components", new String[]{randomString(), randomString()}) + .put("priority", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_emptyEventValueSuccess() { + // 准备参数 + String eventIdentifier = randomString(); + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.ofEntries()) // 空的事件值 + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_caseSensitiveIdentifierMismatch() { + // 准备参数 + String eventIdentifier = randomString().toUpperCase(); // 大写 + String triggerIdentifier = eventIdentifier.toLowerCase(); // 小写 + Map eventParams = MapUtil.builder(new HashMap()) + .put("identifier", eventIdentifier) + .put("value", MapUtil.builder(new HashMap()) + .put("level", randomString()) + .build()) + .build(); + IotDeviceMessage message = createEventPostMessage(eventParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + // 根据实际实现,这里可能需要调整期望结果 + // 如果实现是大小写敏感的,则应该为 false + assertFalse(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建事件上报消息 + */ + private IotDeviceMessage createEventPostMessage(Map eventParams) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); + message.setParams(eventParams); + return message; + } + + /** + * 创建有效的触发器 + */ + private IotSceneRuleDO.Trigger createValidTrigger(String identifier) { + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); + trigger.setIdentifier(identifier); + return trigger; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcherTest.java new file mode 100644 index 0000000..fb15576 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcherTest.java @@ -0,0 +1,344 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +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.IotSceneRuleTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomDouble; +import static cn.hutool.core.util.RandomUtil.randomInt; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotDevicePropertyPostTriggerMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotDevicePropertyPostTriggerMatcherTest extends IotBaseConditionMatcherTest { + + private IotDevicePropertyPostTriggerMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotDevicePropertyPostTriggerMatcher(); + } + + @Test + public void testGetSupportedTriggerType_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); + + // 断言 + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST, result); + } + + @Test + public void testGetPriority_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(20, result); + } + + @Test + public void testIsEnabled_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_numericPropertyGreaterThanSuccess() { + // 准备参数 + String propertyName = randomString(); + Double propertyValue = 25.5; + Integer compareValue = 20; + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, propertyValue) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_integerPropertyEqualsSuccess() { + // 准备参数 + String propertyName = randomString(); + Integer propertyValue = randomInt(); + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, propertyValue) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + String.valueOf(propertyValue) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_propertyValueNotMeetCondition() { + // 准备参数 + String propertyName = randomString(); + Double propertyValue = 15.0; + Integer compareValue = 20; + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, propertyValue) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_propertyNotFound() { + // 准备参数 + String existingProperty = randomString(); + String missingProperty = randomString(); + Map properties = MapUtil.builder(new HashMap()) + .put(existingProperty, randomDouble()) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + missingProperty, // 不存在的属性 + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(randomInt()) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_wrongMessageMethod() { + // 准备参数 + String propertyName = randomString(); + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, randomDouble()) + .build(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); + message.setParams(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(randomInt()) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerIdentifier() { + // 准备参数 + String propertyName = randomString(); + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, randomDouble()) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); + trigger.setIdentifier(null); // 缺少标识符 + trigger.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); + trigger.setValue(String.valueOf(randomInt())); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullMessageParams() { + // 准备参数 + String propertyName = randomString(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); + message.setParams(null); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(randomInt()) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidMessageParams() { + // 准备参数 + String propertyName = randomString(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); + message.setParams(randomString()); // 不是 Map 类型 + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + String.valueOf(randomInt()) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_lessThanOperatorSuccess() { + // 准备参数 + String propertyName = randomString(); + Double propertyValue = 15.0; + Integer compareValue = 20; + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, propertyValue) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), + String.valueOf(compareValue) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_notEqualsOperatorSuccess() { + // 准备参数 + String propertyName = randomString(); + String propertyValue = randomString(); + String compareValue = randomString(); + Map properties = MapUtil.builder(new HashMap()) + .put(propertyName, propertyValue) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + propertyName, + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), + compareValue + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_multiplePropertiesTargetPropertySuccess() { + // 准备参数 + String targetProperty = randomString(); + Integer targetValue = randomInt(); + Map properties = MapUtil.builder(new HashMap()) + .put(randomString(), randomDouble()) + .put(targetProperty, targetValue) + .put(randomString(), randomString()) + .build(); + IotDeviceMessage message = createPropertyPostMessage(properties); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + targetProperty, + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + String.valueOf(targetValue) + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建属性上报消息 + */ + private IotDeviceMessage createPropertyPostMessage(Map properties) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); + message.setParams(properties); + return message; + } + + /** + * 创建有效的触发器 + */ + private IotSceneRuleDO.Trigger createValidTrigger(String identifier, String operator, String value) { + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); + trigger.setIdentifier(identifier); + trigger.setOperator(operator); + trigger.setValue(value); + return trigger; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcherTest.java new file mode 100644 index 0000000..a515f12 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcherTest.java @@ -0,0 +1,402 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +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.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotDeviceServiceInvokeTriggerMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotDeviceServiceInvokeTriggerMatcherTest extends IotBaseConditionMatcherTest { + + private IotDeviceServiceInvokeTriggerMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotDeviceServiceInvokeTriggerMatcher(); + } + + @Test + public void testGetSupportedTriggerType_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); + + // 断言 + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE, result); + } + + @Test + public void testGetPriority_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(40, result); + } + + @Test + public void testIsEnabled_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_serviceInvokeSuccess() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_configServiceSuccess() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("interval", randomInt()) + .put("enabled", randomBoolean()) + .put("threshold", randomDouble()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_updateServiceSuccess() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("version", randomString()) + .put("url", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_serviceIdentifierMismatch() { + // 准备参数 + String messageIdentifier = randomString(); + String triggerIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", messageIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); // 不匹配的服务标识符 + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_wrongMessageMethod() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法 + message.setParams(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerIdentifier() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); + trigger.setIdentifier(null); // 缺少标识符 + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullMessageParams() { + // 准备参数 + String serviceIdentifier = randomString(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); + message.setParams(null); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_invalidMessageParams() { + // 准备参数 + String serviceIdentifier = randomString(); + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); + message.setParams(randomString()); // 不是 Map 类型 + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_missingServiceIdentifierInParams() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) // 缺少 identifier 字段 + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTrigger() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerType() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(null); + trigger.setIdentifier(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_emptyInputDataSuccess() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.ofEntries()) // 空的输入数据 + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_noInputDataSuccess() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + // 没有 inputData 字段 + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_complexInputDataSuccess() { + // 准备参数 + String serviceIdentifier = randomString(); + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("sensors", new String[]{randomString(), randomString(), randomString()}) + .put("precision", randomDouble()) + .put("duration", randomInt()) + .put("autoSave", randomBoolean()) + .put("config", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .put("level", randomString()) + .build()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_caseSensitiveIdentifierMismatch() { + // 准备参数 + String serviceIdentifier = randomString().toUpperCase(); // 大写 + String triggerIdentifier = serviceIdentifier.toLowerCase(); // 小写 + Map serviceParams = MapUtil.builder(new HashMap()) + .put("identifier", serviceIdentifier) + .put("inputData", MapUtil.builder(new HashMap()) + .put("mode", randomString()) + .build()) + .build(); + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); + IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + // 根据实际实现,这里可能需要调整期望结果 + // 如果实现是大小写敏感的,则应该为 false + assertFalse(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建服务调用消息 + */ + private IotDeviceMessage createServiceInvokeMessage(Map serviceParams) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); + message.setParams(serviceParams); + return message; + } + + /** + * 创建有效的触发器 + */ + private IotSceneRuleDO.Trigger createValidTrigger(String identifier) { + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); + trigger.setIdentifier(identifier); + return trigger; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java new file mode 100644 index 0000000..2e8b17a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java @@ -0,0 +1,266 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; +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.IotSceneRuleTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotDeviceStateUpdateTriggerMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotDeviceStateUpdateTriggerMatcherTest extends IotBaseConditionMatcherTest { + + private IotDeviceStateUpdateTriggerMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotDeviceStateUpdateTriggerMatcher(); + } + + @Test + public void testGetSupportedTriggerType_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); + + // 断言 + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE, result); + } + + @Test + public void testGetPriority_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(10, result); + } + + @Test + public void testIsEnabled_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_onlineStateSuccess() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.ONLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_offlineStateSuccess() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.OFFLINE.getState()); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.OFFLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_stateMismatch() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.OFFLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTrigger() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerType() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(null); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_wrongMessageMethod() { + // 准备参数 + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); + message.setParams(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.ONLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerOperator() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); + trigger.setOperator(null); + trigger.setValue(IotDeviceStateEnum.ONLINE.getState().toString()); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerValue() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); + trigger.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator()); + trigger.setValue(null); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullMessageParams() { + // 准备参数 + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); + message.setParams(null); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), + IotDeviceStateEnum.ONLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_greaterThanOperatorSuccess() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), + IotDeviceStateEnum.INACTIVE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_notEqualsOperatorSuccess() { + // 准备参数 + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); + IotSceneRuleDO.Trigger trigger = createValidTrigger( + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), + IotDeviceStateEnum.OFFLINE.getState().toString() + ); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建设备状态更新消息 + */ + private IotDeviceMessage createStateUpdateMessage(Integer state) { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); + message.setParams(state); + return message; + } + + /** + * 创建有效的触发器 + */ + private IotSceneRuleDO.Trigger createValidTrigger(String operator, String value) { + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); + trigger.setOperator(operator); + trigger.setValue(value); + return trigger; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcherTest.java new file mode 100644 index 0000000..df47e6c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotTimerTriggerMatcherTest.java @@ -0,0 +1,280 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; + +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.IotBaseConditionMatcherTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotTimerTriggerMatcher} 的单元测试 + * + * @author HUIHUI + */ +public class IotTimerTriggerMatcherTest extends IotBaseConditionMatcherTest { + + private IotTimerTriggerMatcher matcher; + + @BeforeEach + public void setUp() { + matcher = new IotTimerTriggerMatcher(); + } + + @Test + public void testGetSupportedTriggerType_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); + + // 断言 + assertEquals(IotSceneRuleTriggerTypeEnum.TIMER, result); + } + + @Test + public void testGetPriority_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + int result = matcher.getPriority(); + + // 断言 + assertEquals(50, result); + } + + @Test + public void testIsEnabled_success() { + // 准备参数 + // 无需准备参数 + + // 调用 + boolean result = matcher.isEnabled(); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_validCronExpressionSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 0 12 * * ?"; // 每天中午12点 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_everyMinuteCronSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 * * * * ?"; // 每分钟 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_weekdaysCronSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 0 9 ? * MON-FRI"; // 工作日上午9点 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_invalidCronExpression() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = randomString(); // 随机无效的 cron 表达式 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_emptyCronExpression() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = ""; // 空的 cron 表达式 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullCronExpression() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); + trigger.setCronExpression(null); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTrigger() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + + // 调用 + boolean result = matcher.matches(message, null); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_nullTriggerType() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(null); + trigger.setCronExpression("0 0 12 * * ?"); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_complexCronExpressionSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 15 10 ? * 6#3"; // 每月第三个星期五上午10:15 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_incorrectCronFormat() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 0 12 * *"; // 缺少字段的 cron 表达式 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_specificDateCronSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 0 0 1 1 ? 2025"; // 2025年1月1日午夜 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_everySecondCronSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "* * * * * ?"; // 每秒执行 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + @Test + public void testMatches_invalidCharactersCron() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + String cronExpression = "0 0 12 * * @ #"; // 包含无效字符的 cron 表达式 + IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertFalse(result); + } + + @Test + public void testMatches_rangeCronSuccess() { + // 准备参数 + IotDeviceMessage message = createDeviceMessage(); + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9-17 * * MON-FRI"); // 工作日9-17点 + + // 调用 + boolean result = matcher.matches(message, trigger); + + // 断言 + assertTrue(result); + } + + // ========== 辅助方法 ========== + + /** + * 创建设备消息 + */ + private IotDeviceMessage createDeviceMessage() { + IotDeviceMessage message = new IotDeviceMessage(); + message.setDeviceId(randomLongId()); + return message; + } + + /** + * 创建有效的定时触发器 + */ + private IotSceneRuleDO.Trigger createValidTrigger(String cronExpression) { + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); + trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); + trigger.setCronExpression(cronExpression); + return trigger; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcherTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcherTest.java deleted file mode 100644 index f332f30..0000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcherTest.java +++ /dev/null @@ -1,280 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; - -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.BaseMatcherTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link TimerTriggerMatcher} 的单元测试 - * - * @author HUIHUI - */ -public class TimerTriggerMatcherTest extends BaseMatcherTest { - - private TimerTriggerMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = new TimerTriggerMatcher(); - } - - @Test - public void testGetSupportedTriggerType_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType(); - - // 断言 - assertEquals(IotSceneRuleTriggerTypeEnum.TIMER, result); - } - - @Test - public void testGetPriority_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - int result = matcher.getPriority(); - - // 断言 - assertEquals(50, result); - } - - @Test - public void testIsEnabled_success() { - // 准备参数 - // 无需准备参数 - - // 调用 - boolean result = matcher.isEnabled(); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_validCronExpressionSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 0 12 * * ?"; // 每天中午12点 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_everyMinuteCronSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 * * * * ?"; // 每分钟 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_weekdaysCronSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 0 9 ? * MON-FRI"; // 工作日上午9点 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_invalidCronExpression() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = randomString(); // 随机无效的 cron 表达式 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_emptyCronExpression() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = ""; // 空的 cron 表达式 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullCronExpression() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); - trigger.setCronExpression(null); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTrigger() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - - // 调用 - boolean result = matcher.matches(message, null); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_nullTriggerType() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(null); - trigger.setCronExpression("0 0 12 * * ?"); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_complexCronExpressionSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 15 10 ? * 6#3"; // 每月第三个星期五上午10:15 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_incorrectCronFormat() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 0 12 * *"; // 缺少字段的 cron 表达式 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_specificDateCronSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 0 0 1 1 ? 2025"; // 2025年1月1日午夜 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_everySecondCronSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "* * * * * ?"; // 每秒执行 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - @Test - public void testMatches_invalidCharactersCron() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - String cronExpression = "0 0 12 * * @ #"; // 包含无效字符的 cron 表达式 - IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression); - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertFalse(result); - } - - @Test - public void testMatches_rangeCronSuccess() { - // 准备参数 - IotDeviceMessage message = createDeviceMessage(); - IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9-17 * * MON-FRI"); // 工作日9-17点 - - // 调用 - boolean result = matcher.matches(message, trigger); - - // 断言 - assertTrue(result); - } - - // ========== 辅助方法 ========== - - /** - * 创建设备消息 - */ - private IotDeviceMessage createDeviceMessage() { - IotDeviceMessage message = new IotDeviceMessage(); - message.setDeviceId(randomLongId()); - return message; - } - - /** - * 创建有效的定时触发器 - */ - private IotSceneRuleDO.Trigger createValidTrigger(String cronExpression) { - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); - trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); - trigger.setCronExpression(cronExpression); - return trigger; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java new file mode 100644 index 0000000..3e38e93 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.iot.service.rule.scene.timer; + +import cn.hutool.core.collection.ListUtil; +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.framework.job.core.IotSchedulerManager; +import cn.iocoder.yudao.module.iot.job.rule.IotSceneRuleJob; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.quartz.SchedulerException; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * {@link IotSceneRuleTimerHandler} 的单元测试类 + * + * @author HUIHUI + */ +@ExtendWith(MockitoExtension.class) +public class IotSceneRuleTimerHandlerTest { + + @Mock + private IotSchedulerManager schedulerManager; + + @InjectMocks + private IotSceneRuleTimerHandler timerHandler; + + @BeforeEach + void setUp() { + // 重置 Mock 对象 + reset(schedulerManager); + } + + @Test + public void testRegisterTimerTriggers_success() throws SchedulerException { + // 准备参数 + Long sceneRuleId = 1L; + IotSceneRuleDO sceneRule = new IotSceneRuleDO(); + sceneRule.setId(sceneRuleId); + sceneRule.setStatus(0); // 0 表示启用 + // 创建定时触发器 + IotSceneRuleDO.Trigger timerTrigger = new IotSceneRuleDO.Trigger(); + timerTrigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); + timerTrigger.setCronExpression("0 0 12 * * ?"); // 每天中午12点 + sceneRule.setTriggers(ListUtil.toList(timerTrigger)); + + // 调用 + timerHandler.registerTimerTriggers(sceneRule); + + // 验证 + verify(schedulerManager, times(1)).addOrUpdateJob( + eq(IotSceneRuleJob.class), + eq("iot_scene_rule_timer_" + sceneRuleId), + eq("0 0 12 * * ?"), + eq(IotSceneRuleJob.buildJobDataMap(sceneRuleId)) + ); + } + + @Test + public void testRegisterTimerTriggers_noTimerTrigger() throws SchedulerException { + // 准备参数 - 没有定时触发器 + IotSceneRuleDO sceneRule = new IotSceneRuleDO(); + sceneRule.setStatus(0); // 0 表示启用 + // 创建非定时触发器 + IotSceneRuleDO.Trigger deviceTrigger = new IotSceneRuleDO.Trigger(); + deviceTrigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); + sceneRule.setTriggers(ListUtil.toList(deviceTrigger)); + + // 调用 + timerHandler.registerTimerTriggers(sceneRule); + + // 验证 - 不应该调用调度器 + verify(schedulerManager, never()).addOrUpdateJob(any(), any(), any(), any()); + } + + @Test + public void testRegisterTimerTriggers_emptyCronExpression() throws SchedulerException { + // 准备参数 - CRON 表达式为空 + Long sceneRuleId = 2L; + IotSceneRuleDO sceneRule = new IotSceneRuleDO(); + sceneRule.setId(sceneRuleId); + sceneRule.setStatus(0); // 0 表示启用 + // 创建定时触发器但没有 CRON 表达式 + IotSceneRuleDO.Trigger timerTrigger = new IotSceneRuleDO.Trigger(); + timerTrigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); + timerTrigger.setCronExpression(""); // 空的 CRON 表达式 + sceneRule.setTriggers(ListUtil.toList(timerTrigger)); + + // 调用 + timerHandler.registerTimerTriggers(sceneRule); + + // 验证 - 不应该调用调度器 + verify(schedulerManager, never()).addOrUpdateJob(any(), any(), any(), any()); + } + + @Test + public void testUnregisterTimerTriggers_success() throws SchedulerException { + // 准备参数 + Long sceneRuleId = 3L; + + // 调用 + timerHandler.unregisterTimerTriggers(sceneRuleId); + + // 验证 + verify(schedulerManager, times(1)).deleteJob("iot_scene_rule_timer_" + sceneRuleId); + } + + @Test + public void testPauseTimerTriggers_success() throws SchedulerException { + // 准备参数 + Long sceneRuleId = 4L; + + // 调用 + timerHandler.pauseTimerTriggers(sceneRuleId); + + // 验证 + verify(schedulerManager, times(1)).pauseJob("iot_scene_rule_timer_" + sceneRuleId); + } + +} -- libgit2 0.21.4