Commit 6791c628589b66b7419bf46a3e476d29d0d77470
1 parent
f88d30f1
review:【iot 物联网】场景联动相关实现
Showing
13 changed files
with
65 additions
and
62 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleTriggerTypeEnum.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisRuleAction.java
| ... | ... | @@ -45,7 +45,6 @@ public class IotRedisRuleAction extends |
| 45 | 45 | // 2. 根据数据结构类型执行不同的操作 |
| 46 | 46 | String messageJson = JsonUtils.toJsonString(message); |
| 47 | 47 | IotRedisDataStructureEnum dataStructure = getDataStructureByType(config.getDataStructure()); |
| 48 | - | |
| 49 | 48 | switch (dataStructure) { |
| 50 | 49 | case STREAM: |
| 51 | 50 | executeStream(redisTemplate, config, messageJson); | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java
| ... | ... | @@ -58,12 +58,15 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { |
| 58 | 58 | @Resource |
| 59 | 59 | private IotSceneRuleMapper sceneRuleMapper; |
| 60 | 60 | |
| 61 | + // TODO @puhui999:定时任务,基于它调度; | |
| 61 | 62 | @Resource(name = "iotSchedulerManager") |
| 62 | 63 | private IotSchedulerManager schedulerManager; |
| 63 | 64 | @Resource |
| 64 | 65 | private IotProductService productService; |
| 65 | 66 | @Resource |
| 66 | 67 | private IotDeviceService deviceService; |
| 68 | + | |
| 69 | + // TODO @puhui999:sceneRuleMatcherManager 变量名 | |
| 67 | 70 | @Resource |
| 68 | 71 | private IotSceneRuleMatcherManager matcherManager; |
| 69 | 72 | @Resource |
| ... | ... | @@ -135,7 +138,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { |
| 135 | 138 | return sceneRuleMapper.selectListByStatus(status); |
| 136 | 139 | } |
| 137 | 140 | |
| 138 | - // TODO 芋艿,缓存待实现 | |
| 141 | + // TODO 芋艿,缓存待实现 @puhui999 | |
| 139 | 142 | @Override |
| 140 | 143 | @TenantIgnore // 忽略租户隔离:因为 IotSceneRuleMessageHandler 调用时,一般未传递租户,所以需要忽略 |
| 141 | 144 | public List<IotSceneRuleDO> getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId) { |
| ... | ... | @@ -177,7 +180,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { |
| 177 | 180 | |
| 178 | 181 | @Override |
| 179 | 182 | public void executeSceneRuleByDevice(IotDeviceMessage message) { |
| 180 | - // TODO @芋艿:这里的 tenantId,通过设备获取; | |
| 183 | + // TODO @芋艿:这里的 tenantId,通过设备获取;@puhui999: | |
| 181 | 184 | TenantUtils.execute(message.getTenantId(), () -> { |
| 182 | 185 | // 1. 获得设备匹配的规则场景 |
| 183 | 186 | List<IotSceneRuleDO> sceneRules = getMatchedSceneRuleListByMessage(message); |
| ... | ... | @@ -223,7 +226,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { |
| 223 | 226 | */ |
| 224 | 227 | private List<IotSceneRuleDO> getMatchedSceneRuleListByMessage(IotDeviceMessage message) { |
| 225 | 228 | // 1. 匹配设备 |
| 226 | - // TODO @芋艿:可能需要 getSelf(); 缓存 | |
| 229 | + // TODO @芋艿:可能需要 getSelf(); 缓存 @puhui999; | |
| 227 | 230 | // 1.1 通过 deviceId 获取设备信息 |
| 228 | 231 | IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId()); |
| 229 | 232 | if (device == null) { |
| ... | ... | @@ -342,7 +345,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { |
| 342 | 345 | private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, |
| 343 | 346 | IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) { |
| 344 | 347 | try { |
| 345 | - // 使用重构后的条件匹配管理器进行匹配 | |
| 346 | 348 | return matcherManager.isConditionMatched(message, condition); |
| 347 | 349 | } catch (Exception e) { |
| 348 | 350 | log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]", |
| ... | ... | @@ -351,8 +353,7 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { |
| 351 | 353 | } |
| 352 | 354 | } |
| 353 | 355 | |
| 354 | - // TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。 | |
| 355 | - | |
| 356 | + // TODO @puhui999:下面还需要么? | |
| 356 | 357 | /** |
| 357 | 358 | * 判断触发器的条件参数是否匹配 |
| 358 | 359 | * | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleMatcher.java
| ... | ... | @@ -24,6 +24,8 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. |
| 24 | 24 | @Slf4j |
| 25 | 25 | public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher { |
| 26 | 26 | |
| 27 | + // TODO @puhui999:这个是不是也是【通用】条件哈? | |
| 28 | + | |
| 27 | 29 | /** |
| 28 | 30 | * 评估条件是否匹配 |
| 29 | 31 | * |
| ... | ... | @@ -32,6 +34,7 @@ public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher |
| 32 | 34 | * @param paramValue 参数值(来自条件配置) |
| 33 | 35 | * @return 是否匹配 |
| 34 | 36 | */ |
| 37 | + @SuppressWarnings("DataFlowIssue") | |
| 35 | 38 | protected boolean evaluateCondition(Object sourceValue, String operator, String paramValue) { |
| 36 | 39 | try { |
| 37 | 40 | // 1. 校验操作符是否合法 |
| ... | ... | @@ -48,6 +51,7 @@ public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher |
| 48 | 51 | // 处理参数值 |
| 49 | 52 | if (StrUtil.isNotBlank(paramValue)) { |
| 50 | 53 | // 处理多值情况(如 IN、BETWEEN 操作符) |
| 54 | + // TODO @puhui999:使用这个,会不会有问题?例如说:string 恰好有 , 分隔? | |
| 51 | 55 | if (paramValue.contains(",")) { |
| 52 | 56 | List<String> paramValues = StrUtil.split(paramValue, ','); |
| 53 | 57 | springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, |
| ... | ... | @@ -68,7 +72,7 @@ public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher |
| 68 | 72 | } |
| 69 | 73 | } |
| 70 | 74 | |
| 71 | - // ========== 触发器相关工具方法 ========== | |
| 75 | + // ========== 【触发器】相关工具方法 ========== | |
| 72 | 76 | |
| 73 | 77 | /** |
| 74 | 78 | * 检查基础触发器参数是否有效 |
| ... | ... | @@ -111,7 +115,7 @@ public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher |
| 111 | 115 | log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", getMatcherName(), message.getRequestId(), trigger.getType(), reason); |
| 112 | 116 | } |
| 113 | 117 | |
| 114 | - // ========== 条件相关工具方法 ========== | |
| 118 | + // ========== 【条件】相关工具方法 ========== | |
| 115 | 119 | |
| 116 | 120 | /** |
| 117 | 121 | * 检查基础条件参数是否有效 |
| ... | ... | @@ -154,7 +158,7 @@ public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher |
| 154 | 158 | log.debug("[{}][消息({}) 匹配条件({}) 失败: {}]", getMatcherName(), message.getRequestId(), condition.getType(), reason); |
| 155 | 159 | } |
| 156 | 160 | |
| 157 | - // ========== 通用工具方法 ========== | |
| 161 | + // ========== 【通用】工具方法 ========== | |
| 158 | 162 | |
| 159 | 163 | /** |
| 160 | 164 | * 检查标识符是否匹配 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java
| ... | ... | @@ -56,15 +56,11 @@ public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher { |
| 56 | 56 | return false; |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | - // 3. 获取当前时间 | |
| 59 | + // 3. 根据操作符类型进行不同的时间匹配 | |
| 60 | 60 | LocalDateTime now = LocalDateTime.now(); |
| 61 | - | |
| 62 | - // 4. 根据操作符类型进行不同的时间匹配 | |
| 63 | 61 | String operator = condition.getOperator(); |
| 64 | 62 | String param = condition.getParam(); |
| 65 | - | |
| 66 | - boolean matched = false; | |
| 67 | - | |
| 63 | + boolean matched; | |
| 68 | 64 | try { |
| 69 | 65 | if (operator.startsWith("date_time_")) { |
| 70 | 66 | // 日期时间匹配(时间戳) |
| ... | ... | @@ -82,13 +78,11 @@ public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher { |
| 82 | 78 | } else { |
| 83 | 79 | logConditionMatchFailure(message, condition, "时间条件不匹配"); |
| 84 | 80 | } |
| 85 | - | |
| 86 | 81 | } catch (Exception e) { |
| 87 | 82 | log.error("[CurrentTimeConditionMatcher][时间条件匹配异常] operator: {}, param: {}", operator, param, e); |
| 88 | 83 | logConditionMatchFailure(message, condition, "时间条件匹配异常: " + e.getMessage()); |
| 89 | 84 | matched = false; |
| 90 | 85 | } |
| 91 | - | |
| 92 | 86 | return matched; |
| 93 | 87 | } |
| 94 | 88 | |
| ... | ... | @@ -107,21 +101,20 @@ public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher { |
| 107 | 101 | try { |
| 108 | 102 | String actualOperator = operator.substring("time_".length()); |
| 109 | 103 | |
| 104 | + // TODO @puhui999:if return 简化; | |
| 110 | 105 | if ("between".equals(actualOperator)) { |
| 111 | 106 | // 时间区间匹配 |
| 112 | 107 | String[] timeRange = param.split(","); |
| 113 | 108 | if (timeRange.length != 2) { |
| 114 | 109 | return false; |
| 115 | 110 | } |
| 116 | - | |
| 117 | 111 | LocalTime startTime = parseTime(timeRange[0].trim()); |
| 118 | 112 | LocalTime endTime = parseTime(timeRange[1].trim()); |
| 119 | - | |
| 120 | 113 | return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); |
| 121 | 114 | } else { |
| 122 | 115 | // 单个时间比较 |
| 123 | 116 | LocalTime targetTime = parseTime(param); |
| 124 | - | |
| 117 | + // TODO @puhui999:枚举类; | |
| 125 | 118 | switch (actualOperator) { |
| 126 | 119 | case ">": |
| 127 | 120 | return currentTime.isAfter(targetTime); |
| ... | ... | @@ -138,6 +131,7 @@ public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher { |
| 138 | 131 | } |
| 139 | 132 | } |
| 140 | 133 | } catch (Exception e) { |
| 134 | + // TODO @puhui999:1)日志格式 [][];2)方法名不对哈; | |
| 141 | 135 | log.error("[CurrentTimeConditionMatcher][时间解析异常] param: {}", param, e); |
| 142 | 136 | return false; |
| 143 | 137 | } |
| ... | ... | @@ -147,10 +141,10 @@ public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher { |
| 147 | 141 | * 解析时间字符串 |
| 148 | 142 | */ |
| 149 | 143 | private LocalTime parseTime(String timeStr) { |
| 144 | + // TODO @puhui999:可以用 hutool Assert 类简化 | |
| 150 | 145 | if (StrUtil.isBlank(timeStr)) { |
| 151 | 146 | throw new IllegalArgumentException("时间字符串不能为空"); |
| 152 | 147 | } |
| 153 | - | |
| 154 | 148 | // 尝试不同的时间格式 |
| 155 | 149 | try { |
| 156 | 150 | if (timeStr.length() == 5) { // HH:mm | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java
| ... | ... | @@ -26,6 +26,7 @@ public class DevicePropertyConditionMatcher extends AbstractIotSceneRuleMatcher |
| 26 | 26 | return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY; |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | + // TODO @puhui999:参数校验的,要不要 1.1 1.2 1.3 1.4 ?这样最终看到 2. 3. 就是核心逻辑列; | |
| 29 | 30 | @Override |
| 30 | 31 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) { |
| 31 | 32 | // 1. 基础参数校验 |
| ... | ... | @@ -56,13 +57,11 @@ public class DevicePropertyConditionMatcher extends AbstractIotSceneRuleMatcher |
| 56 | 57 | |
| 57 | 58 | // 5. 使用条件评估器进行匹配 |
| 58 | 59 | boolean matched = evaluateCondition(propertyValue, condition.getOperator(), condition.getParam()); |
| 59 | - | |
| 60 | 60 | if (matched) { |
| 61 | 61 | logConditionMatchSuccess(message, condition); |
| 62 | 62 | } else { |
| 63 | 63 | logConditionMatchFailure(message, condition, "设备属性条件不匹配"); |
| 64 | 64 | } |
| 65 | - | |
| 66 | 65 | return matched; |
| 67 | 66 | } |
| 68 | 67 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java
| ... | ... | @@ -20,6 +20,7 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleMatche |
| 20 | 20 | /** |
| 21 | 21 | * 设备属性上报消息方法 |
| 22 | 22 | */ |
| 23 | + // TODO @puhui999:是不是不用枚举哈?直接使用 IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod() | |
| 23 | 24 | private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(); |
| 24 | 25 | |
| 25 | 26 | @Override |
| ... | ... | @@ -68,13 +69,11 @@ public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleMatche |
| 68 | 69 | |
| 69 | 70 | // 6. 使用条件评估器进行匹配 |
| 70 | 71 | boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue()); |
| 71 | - | |
| 72 | 72 | if (matched) { |
| 73 | 73 | logTriggerMatchSuccess(message, trigger); |
| 74 | 74 | } else { |
| 75 | 75 | logTriggerMatchFailure(message, trigger, "属性值条件不匹配"); |
| 76 | 76 | } |
| 77 | - | |
| 78 | 77 | return matched; |
| 79 | 78 | } |
| 80 | 79 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java
| ... | ... | @@ -49,13 +49,11 @@ public class DeviceStateConditionMatcher extends AbstractIotSceneRuleMatcher { |
| 49 | 49 | |
| 50 | 50 | // 4. 使用条件评估器进行匹配 |
| 51 | 51 | boolean matched = evaluateCondition(stateValue, condition.getOperator(), condition.getParam()); |
| 52 | - | |
| 53 | 52 | if (matched) { |
| 54 | 53 | logConditionMatchSuccess(message, condition); |
| 55 | 54 | } else { |
| 56 | 55 | logConditionMatchFailure(message, condition, "设备状态条件不匹配"); |
| 57 | 56 | } |
| 58 | - | |
| 59 | 57 | return matched; |
| 60 | 58 | } |
| 61 | 59 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java
| ... | ... | @@ -16,6 +16,7 @@ import org.springframework.stereotype.Component; |
| 16 | 16 | @Component |
| 17 | 17 | public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleMatcher { |
| 18 | 18 | |
| 19 | + // TODO @puhui999:是不是不用枚举哈; | |
| 19 | 20 | /** |
| 20 | 21 | * 设备状态更新消息方法 |
| 21 | 22 | */ |
| ... | ... | @@ -60,13 +61,11 @@ public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleMatcher |
| 60 | 61 | |
| 61 | 62 | // 5. 使用条件评估器进行匹配 |
| 62 | 63 | boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue()); |
| 63 | - | |
| 64 | 64 | if (matched) { |
| 65 | 65 | logTriggerMatchSuccess(message, trigger); |
| 66 | 66 | } else { |
| 67 | 67 | logTriggerMatchFailure(message, trigger, "状态值条件不匹配"); |
| 68 | 68 | } |
| 69 | - | |
| 70 | 69 | return matched; |
| 71 | 70 | } |
| 72 | 71 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java
| ... | ... | @@ -18,18 +18,23 @@ import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; |
| 18 | 18 | */ |
| 19 | 19 | public interface IotSceneRuleMatcher { |
| 20 | 20 | |
| 21 | + // TODO @puhui999:MatcherTypeEnum; | |
| 22 | + // TODO @puhui999:可以考虑根据类型,新建 trigger、condition 包,然后把对应的实现类放进去哈; | |
| 21 | 23 | /** |
| 22 | 24 | * 匹配器类型枚举 |
| 23 | 25 | */ |
| 24 | 26 | enum MatcherType { |
| 27 | + | |
| 25 | 28 | /** |
| 26 | 29 | * 触发器匹配器 - 用于匹配主触发条件 |
| 27 | 30 | */ |
| 28 | 31 | TRIGGER, |
| 32 | + | |
| 29 | 33 | /** |
| 30 | 34 | * 条件匹配器 - 用于匹配子条件 |
| 31 | 35 | */ |
| 32 | 36 | CONDITION |
| 37 | + | |
| 33 | 38 | } |
| 34 | 39 | |
| 35 | 40 | /** |
| ... | ... | @@ -39,6 +44,10 @@ public interface IotSceneRuleMatcher { |
| 39 | 44 | */ |
| 40 | 45 | MatcherType getMatcherType(); |
| 41 | 46 | |
| 47 | + // TODO @puhui999:【重要】有个思路,IotSceneRuleMatcher 拆分成 2 种 mather 接口;然后 AbstractIotSceneRuleMatcher 是个 Helper 工具类; | |
| 48 | + | |
| 49 | + // TODO @puhui999:是不是和 AbstractSceneRuleMatcher 一样,分下块; | |
| 50 | + | |
| 42 | 51 | /** |
| 43 | 52 | * 获取支持的触发器类型(仅触发器匹配器需要实现) |
| 44 | 53 | * |
| ... | ... | @@ -90,6 +99,7 @@ public interface IotSceneRuleMatcher { |
| 90 | 99 | return 100; |
| 91 | 100 | } |
| 92 | 101 | |
| 102 | + // TODO @puhui999:如果目前没自定义,体感可以删除哈; | |
| 93 | 103 | /** |
| 94 | 104 | * 获取匹配器名称,用于日志和调试 |
| 95 | 105 | * | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java
| ... | ... | @@ -30,14 +30,14 @@ public class IotSceneRuleMatcherManager { |
| 30 | 30 | * Key: 触发器类型枚举 |
| 31 | 31 | * Value: 对应的匹配器实例 |
| 32 | 32 | */ |
| 33 | - private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleMatcher> triggerMatcherMap; | |
| 33 | + private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleMatcher> triggerMatchers; | |
| 34 | 34 | |
| 35 | 35 | /** |
| 36 | 36 | * 条件匹配器映射表 |
| 37 | 37 | * Key: 条件类型枚举 |
| 38 | 38 | * Value: 对应的匹配器实例 |
| 39 | 39 | */ |
| 40 | - private final Map<IotSceneRuleConditionTypeEnum, IotSceneRuleMatcher> conditionMatcherMap; | |
| 40 | + private final Map<IotSceneRuleConditionTypeEnum, IotSceneRuleMatcher> conditionMatchers; | |
| 41 | 41 | |
| 42 | 42 | /** |
| 43 | 43 | * 所有匹配器列表(按优先级排序) |
| ... | ... | @@ -47,8 +47,8 @@ public class IotSceneRuleMatcherManager { |
| 47 | 47 | public IotSceneRuleMatcherManager(List<IotSceneRuleMatcher> matchers) { |
| 48 | 48 | if (CollUtil.isEmpty(matchers)) { |
| 49 | 49 | log.warn("[IotSceneRuleMatcherManager][没有找到任何匹配器]"); |
| 50 | - this.triggerMatcherMap = new HashMap<>(); | |
| 51 | - this.conditionMatcherMap = new HashMap<>(); | |
| 50 | + this.triggerMatchers = new HashMap<>(); | |
| 51 | + this.conditionMatchers = new HashMap<>(); | |
| 52 | 52 | this.allMatchers = new ArrayList<>(); |
| 53 | 53 | return; |
| 54 | 54 | } |
| ... | ... | @@ -63,13 +63,13 @@ public class IotSceneRuleMatcherManager { |
| 63 | 63 | List<IotSceneRuleMatcher> triggerMatchers = this.allMatchers.stream() |
| 64 | 64 | .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.TRIGGER) |
| 65 | 65 | .toList(); |
| 66 | - | |
| 67 | 66 | List<IotSceneRuleMatcher> conditionMatchers = this.allMatchers.stream() |
| 68 | 67 | .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION) |
| 69 | 68 | .toList(); |
| 70 | 69 | |
| 71 | 70 | // 构建触发器匹配器映射表 |
| 72 | - this.triggerMatcherMap = triggerMatchers.stream() | |
| 71 | + // TODO @puhui999:convertMap() | |
| 72 | + this.triggerMatchers = triggerMatchers.stream() | |
| 73 | 73 | .collect(Collectors.toMap( |
| 74 | 74 | IotSceneRuleMatcher::getSupportedTriggerType, |
| 75 | 75 | Function.identity(), |
| ... | ... | @@ -81,9 +81,8 @@ public class IotSceneRuleMatcherManager { |
| 81 | 81 | }, |
| 82 | 82 | LinkedHashMap::new |
| 83 | 83 | )); |
| 84 | - | |
| 85 | 84 | // 构建条件匹配器映射表 |
| 86 | - this.conditionMatcherMap = conditionMatchers.stream() | |
| 85 | + this.conditionMatchers = conditionMatchers.stream() | |
| 87 | 86 | .collect(Collectors.toMap( |
| 88 | 87 | IotSceneRuleMatcher::getSupportedConditionType, |
| 89 | 88 | Function.identity(), |
| ... | ... | @@ -96,16 +95,13 @@ public class IotSceneRuleMatcherManager { |
| 96 | 95 | LinkedHashMap::new |
| 97 | 96 | )); |
| 98 | 97 | |
| 98 | + // 日志输出初始化信息 | |
| 99 | 99 | log.info("[IotSceneRuleMatcherManager][初始化完成,共加载 {} 个匹配器,其中触发器匹配器 {} 个,条件匹配器 {} 个]", |
| 100 | - this.allMatchers.size(), this.triggerMatcherMap.size(), this.conditionMatcherMap.size()); | |
| 101 | - | |
| 102 | - // 记录触发器匹配器详情 | |
| 103 | - this.triggerMatcherMap.forEach((type, matcher) -> | |
| 100 | + this.allMatchers.size(), this.triggerMatchers.size(), this.conditionMatchers.size()); | |
| 101 | + this.triggerMatchers.forEach((type, matcher) -> | |
| 104 | 102 | log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}", |
| 105 | 103 | type, matcher.getMatcherName(), matcher.getPriority())); |
| 106 | - | |
| 107 | - // 记录条件匹配器详情 | |
| 108 | - this.conditionMatcherMap.forEach((type, matcher) -> | |
| 104 | + this.conditionMatchers.forEach((type, matcher) -> | |
| 109 | 105 | log.info("[IotSceneRuleMatcherManager][条件匹配器] 类型: {}, 匹配器: {}, 优先级: {}", |
| 110 | 106 | type, matcher.getMatcherName(), matcher.getPriority())); |
| 111 | 107 | } |
| ... | ... | @@ -118,19 +114,17 @@ public class IotSceneRuleMatcherManager { |
| 118 | 114 | * @return 是否匹配 |
| 119 | 115 | */ |
| 120 | 116 | public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) { |
| 117 | + // TODO @puhui999:日志优化下;claude 打出来的日志风格,和项目有点不一样哈; | |
| 121 | 118 | if (message == null || trigger == null || trigger.getType() == null) { |
| 122 | 119 | log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger); |
| 123 | 120 | return false; |
| 124 | 121 | } |
| 125 | - | |
| 126 | - // 根据触发器类型查找对应的匹配器 | |
| 127 | 122 | IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType()); |
| 128 | 123 | if (triggerType == null) { |
| 129 | 124 | log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType()); |
| 130 | 125 | return false; |
| 131 | 126 | } |
| 132 | - | |
| 133 | - IotSceneRuleMatcher matcher = triggerMatcherMap.get(triggerType); | |
| 127 | + IotSceneRuleMatcher matcher = triggerMatchers.get(triggerType); | |
| 134 | 128 | if (matcher == null) { |
| 135 | 129 | log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType); |
| 136 | 130 | return false; |
| ... | ... | @@ -165,7 +159,7 @@ public class IotSceneRuleMatcherManager { |
| 165 | 159 | return false; |
| 166 | 160 | } |
| 167 | 161 | |
| 168 | - IotSceneRuleMatcher matcher = conditionMatcherMap.get(conditionType); | |
| 162 | + IotSceneRuleMatcher matcher = conditionMatchers.get(conditionType); | |
| 169 | 163 | if (matcher == null) { |
| 170 | 164 | log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType); |
| 171 | 165 | return false; |
| ... | ... | @@ -199,7 +193,7 @@ public class IotSceneRuleMatcherManager { |
| 199 | 193 | * @return 支持的触发器类型列表 |
| 200 | 194 | */ |
| 201 | 195 | public Set<IotSceneRuleTriggerTypeEnum> getSupportedTriggerTypes() { |
| 202 | - return new HashSet<>(triggerMatcherMap.keySet()); | |
| 196 | + return new HashSet<>(triggerMatchers.keySet()); | |
| 203 | 197 | } |
| 204 | 198 | |
| 205 | 199 | /** |
| ... | ... | @@ -208,9 +202,11 @@ public class IotSceneRuleMatcherManager { |
| 208 | 202 | * @return 支持的条件类型列表 |
| 209 | 203 | */ |
| 210 | 204 | public Set<IotSceneRuleConditionTypeEnum> getSupportedConditionTypes() { |
| 211 | - return new HashSet<>(conditionMatcherMap.keySet()); | |
| 205 | + return new HashSet<>(conditionMatchers.keySet()); | |
| 212 | 206 | } |
| 213 | 207 | |
| 208 | + // TODO @puhui999:用不到的方法,可以去掉先哈; | |
| 209 | + | |
| 214 | 210 | /** |
| 215 | 211 | * 获取指定触发器类型的匹配器 |
| 216 | 212 | * |
| ... | ... | @@ -218,7 +214,7 @@ public class IotSceneRuleMatcherManager { |
| 218 | 214 | * @return 匹配器实例,如果不存在则返回 null |
| 219 | 215 | */ |
| 220 | 216 | public IotSceneRuleMatcher getTriggerMatcher(IotSceneRuleTriggerTypeEnum triggerType) { |
| 221 | - return triggerMatcherMap.get(triggerType); | |
| 217 | + return triggerMatchers.get(triggerType); | |
| 222 | 218 | } |
| 223 | 219 | |
| 224 | 220 | /** |
| ... | ... | @@ -228,9 +224,10 @@ public class IotSceneRuleMatcherManager { |
| 228 | 224 | * @return 匹配器实例,如果不存在则返回 null |
| 229 | 225 | */ |
| 230 | 226 | public IotSceneRuleMatcher getConditionMatcher(IotSceneRuleConditionTypeEnum conditionType) { |
| 231 | - return conditionMatcherMap.get(conditionType); | |
| 227 | + return conditionMatchers.get(conditionType); | |
| 232 | 228 | } |
| 233 | 229 | |
| 230 | + // TODO @puhui999:是不是不用这个哈;直接 @Getter,单测直接处理; | |
| 234 | 231 | /** |
| 235 | 232 | * 获取所有匹配器的统计信息 |
| 236 | 233 | * |
| ... | ... | @@ -239,14 +236,14 @@ public class IotSceneRuleMatcherManager { |
| 239 | 236 | public Map<String, Object> getMatcherStatistics() { |
| 240 | 237 | Map<String, Object> statistics = new HashMap<>(); |
| 241 | 238 | statistics.put("totalMatchers", allMatchers.size()); |
| 242 | - statistics.put("triggerMatchers", triggerMatcherMap.size()); | |
| 243 | - statistics.put("conditionMatchers", conditionMatcherMap.size()); | |
| 239 | + statistics.put("triggerMatchers", triggerMatchers.size()); | |
| 240 | + statistics.put("conditionMatchers", conditionMatchers.size()); | |
| 244 | 241 | statistics.put("supportedTriggerTypes", getSupportedTriggerTypes()); |
| 245 | 242 | statistics.put("supportedConditionTypes", getSupportedConditionTypes()); |
| 246 | 243 | |
| 247 | 244 | // 触发器匹配器详情 |
| 248 | 245 | Map<String, Object> triggerMatcherDetails = new HashMap<>(); |
| 249 | - triggerMatcherMap.forEach((type, matcher) -> { | |
| 246 | + triggerMatchers.forEach((type, matcher) -> { | |
| 250 | 247 | Map<String, Object> detail = new HashMap<>(); |
| 251 | 248 | detail.put("matcherName", matcher.getMatcherName()); |
| 252 | 249 | detail.put("priority", matcher.getPriority()); |
| ... | ... | @@ -257,7 +254,7 @@ public class IotSceneRuleMatcherManager { |
| 257 | 254 | |
| 258 | 255 | // 条件匹配器详情 |
| 259 | 256 | Map<String, Object> conditionMatcherDetails = new HashMap<>(); |
| 260 | - conditionMatcherMap.forEach((type, matcher) -> { | |
| 257 | + conditionMatchers.forEach((type, matcher) -> { | |
| 261 | 258 | Map<String, Object> detail = new HashMap<>(); |
| 262 | 259 | detail.put("matcherName", matcher.getMatcherName()); |
| 263 | 260 | detail.put("priority", matcher.getPriority()); | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java
| ... | ... | @@ -61,6 +61,7 @@ public class TimerTriggerMatcher extends AbstractIotSceneRuleMatcher { |
| 61 | 61 | * @return 是否有效 |
| 62 | 62 | */ |
| 63 | 63 | private boolean isValidCronExpression(String cronExpression) { |
| 64 | + // TODO @puhui999:CronExpression.isValidExpression(cronExpression); | |
| 64 | 65 | try { |
| 65 | 66 | // 简单的 CRON 表达式格式验证 |
| 66 | 67 | // 标准 CRON 表达式应该有 6 或 7 个字段(秒 分 时 日 月 周 [年]) | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java
| ... | ... | @@ -20,6 +20,8 @@ import static org.junit.jupiter.api.Assertions.*; |
| 20 | 20 | */ |
| 21 | 21 | public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { |
| 22 | 22 | |
| 23 | + // TODO @puhui999:public 都加下哈; | |
| 24 | + | |
| 23 | 25 | private IotSceneRuleMatcherManager matcherManager; |
| 24 | 26 | |
| 25 | 27 | @BeforeEach |
| ... | ... | @@ -42,13 +44,13 @@ public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { |
| 42 | 44 | // 1. 准备测试数据 |
| 43 | 45 | IotDeviceMessage message = IotDeviceMessage.builder() |
| 44 | 46 | .requestId("test-001") |
| 45 | - .method("thing.state.update") | |
| 46 | - .data(1) // 在线状态 | |
| 47 | + .method("thing.state.update") // TODO @puhui999:这里的枚举; | |
| 48 | + .data(1) // 在线状态 TODO @puhui999:这里的枚举; | |
| 47 | 49 | .build(); |
| 48 | 50 | |
| 49 | 51 | IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); |
| 50 | 52 | trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); |
| 51 | - trigger.setOperator("="); | |
| 53 | + trigger.setOperator("="); // TODO @puhui999:这里的枚举;下面也是类似; | |
| 52 | 54 | trigger.setValue("1"); |
| 53 | 55 | |
| 54 | 56 | // 2. 执行测试 | ... | ... |