2026-06-24 f4bd1f3c89d906131495a0aca5aaf82966378510
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
 
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import lombok.extern.slf4j.Slf4j;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 
/**
 * IoT 场景规则匹配器工具类:提供通用的条件评估逻辑和工具方法,供触发器和条件匹配器使用
 *
 * 该类包含了匹配器实现中常用的工具方法,如条件评估、参数校验、日志记录等
 *
 * @author HUIHUI
 */
@Slf4j
public final class IotSceneRuleMatcherHelper {
 
    /**
     * 私有构造函数,防止实例化
     */
    private IotSceneRuleMatcherHelper() {
    }
 
    /**
     * 评估条件是否匹配
     *
     * @param sourceValue 源值(来自消息)
     * @param operator    操作符
     * @param paramValue  参数值(来自条件配置)
     * @return 是否匹配
     */
    public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
        try {
            // 1. 校验操作符是否合法
            IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
            if (operatorEnum == null) {
                log.warn("[evaluateCondition][operator({}) 操作符无效]", operator);
                return false;
            }
 
            // 2. 构建 Spring 表达式变量
            return evaluateConditionWithOperatorEnum(sourceValue, operatorEnum, paramValue);
        } catch (Exception e) {
            log.error("[evaluateCondition][sourceValue({}) operator({}) paramValue({}) 条件评估异常]",
                    sourceValue, operator, paramValue, e);
            return false;
        }
    }
 
    /**
     * 使用操作符枚举评估条件是否匹配
     *
     * @param sourceValue  源值(来自消息)
     * @param operatorEnum 操作符枚举
     * @param paramValue   参数值(来自条件配置)
     * @return 是否匹配
     */
    @SuppressWarnings("DataFlowIssue")
    public static boolean evaluateConditionWithOperatorEnum(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {
        try {
            // 1. 构建 Spring 表达式变量
            Map<String, Object> springExpressionVariables = buildSpringExpressionVariables(sourceValue, operatorEnum, paramValue);
 
            // 2. 计算 Spring 表达式
            return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
        } catch (Exception e) {
            log.error("[evaluateConditionWithOperatorEnum][sourceValue({}) operatorEnum({}) paramValue({}) 条件评估异常]",
                    sourceValue, operatorEnum, paramValue, e);
            return false;
        }
    }
 
    /**
     * 构建 Spring 表达式变量
     */
    private static Map<String, Object> buildSpringExpressionVariables(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {
        Map<String, Object> springExpressionVariables = new HashMap<>();
 
        // 设置源值
        springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, StrUtil.toString(sourceValue));
 
        // 处理参数值
        if (StrUtil.isNotBlank(paramValue)) {
            List<String> parameterValues = StrUtil.splitTrim(paramValue, CharPool.COMMA);
 
            // 设置原始参数值
            springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, paramValue);
            springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);
 
            // 特殊处理:解决数字比较问题
            // Spring 表达式基于 compareTo 方法,对数字的比较存在问题,需要转换为数字类型
            if (isNumericComparisonOperator(operatorEnum) && isNumericComparison(sourceValue, parameterValues)) {
                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,
                        NumberUtil.parseDouble(String.valueOf(sourceValue)));
                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
                        NumberUtil.parseDouble(paramValue));
                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
                        convertList(parameterValues, NumberUtil::parseDouble));
            }
        }
 
        return springExpressionVariables;
    }
 
    /**
     * 判断是否为数字比较操作符
     */
    private static boolean isNumericComparisonOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {
        return ObjectUtils.equalsAny(operatorEnum,
                IotSceneRuleConditionOperatorEnum.BETWEEN,
                IotSceneRuleConditionOperatorEnum.NOT_BETWEEN,
                IotSceneRuleConditionOperatorEnum.GREATER_THAN,
                IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS,
                IotSceneRuleConditionOperatorEnum.LESS_THAN,
                IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS);
    }
 
    /**
     * 判断是否为数字比较场景
     */
    private static boolean isNumericComparison(Object sourceValue, List<String> parameterValues) {
        return NumberUtil.isNumber(String.valueOf(sourceValue)) && NumberUtils.isAllNumber(parameterValues);
    }
 
    // ========== 【触发器】相关工具方法 ==========
 
    /**
     * 检查基础触发器参数是否有效
     *
     * @param trigger 触发器配置
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isBasicTriggerValid(IotSceneRuleDO.Trigger trigger) {
        return trigger != null && trigger.getType() != null;
    }
 
    /**
     * 检查触发器操作符和值是否有效
     *
     * @param trigger 触发器配置
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
        return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());
    }
 
    /**
     * 记录触发器匹配成功日志
     *
     * @param message     设备消息
     * @param trigger     触发器配置
     */
    public static void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
        log.debug("[isMatched][message({}) trigger({}) 匹配触发器成功]",
                message != null ? message.getRequestId() : null,
                trigger != null ? trigger.getType() : null);
    }
 
    /**
     * 记录触发器匹配失败日志
     *
     * @param message     设备消息
     * @param trigger     触发器配置
     * @param reason      失败原因
     */
    public static void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
        log.debug("[isMatched][message({}) trigger({}) reason({}) 匹配触发器失败]",
                message != null ? message.getRequestId() : null,
                trigger != null ? trigger.getType() : null,
                reason);
    }
 
    // ========== 【条件】相关工具方法 ==========
 
    /**
     * 检查基础条件参数是否有效
     *
     * @param condition 触发条件
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) {
        return condition != null && condition.getType() != null;
    }
 
    /**
     * 检查条件操作符和参数是否有效
     *
     * @param condition 触发条件
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) {
        return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam());
    }
 
    /**
     * 记录条件匹配成功日志
     *
     * @param message     设备消息
     * @param condition   触发条件
     */
    public static void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
        log.debug("[isMatched][message({}) condition({}) 匹配条件成功]",
                message != null ? message.getRequestId() : null,
                condition != null ? condition.getType() : null);
    }
 
    /**
     * 记录条件匹配失败日志
     *
     * @param message     设备消息
     * @param condition   触发条件
     * @param reason      失败原因
     */
    public static void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
        log.debug("[isMatched][message({}) condition({}) reason({}) 匹配条件失败]",
                message != null ? message.getRequestId() : null,
                condition != null ? condition.getType() : null,
                reason);
    }
 
    // ========== 【通用】工具方法 ==========
 
    /**
     * 检查标识符是否匹配
     *
     * @param expectedIdentifier 期望的标识符
     * @param actualIdentifier   实际的标识符
     * @return 是否匹配
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) {
        return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
    }
 
}