• springboot + easyRules 搭建规则引擎服务


    依赖

            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-core</artifactId>
                <version>4.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-mvel</artifactId>
                <version>4.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-spel</artifactId>
                <version>4.0.0</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    输入

    为更灵活的发挥规则引擎的功能,输入可以使用JSONObject,这样增删改字段无需修改代码。

    输出

    @Data
    public class RuleResult {
        /**
         * 规则主键
         */
        Long ruleId;
        /**
         * 规则名
         * 默认false
         */
        boolean isMatch = false;
    
        /**
         * 规则名
         */
        String value;
    
        /**
         * 满足规则后执行
         */
        public void setValue(String msg){
            this.value = msg;
            this.isMatch = true;
        }
    }
    
    • 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

    规则引擎本身的目的是为了业务与开发解耦,因此只输出是否满足规则,至于满足规则后要干什么业务,这个就是调用方的事。

    简单的测试

        @Test
        void jsonExpressionTest() {
            // 输入项
            JSONObject dataSource = new JSONObject();
            dataSource.put("name","大帅比");
            dataSource.put("age",18);
            // 结果
            RuleResult result = new RuleResult();
            // 规则实例
            Facts facts = new Facts();
            facts.put("fact",dataSource);
            facts.put("result",result);
    
            // when
            String when = "fact.get(\"name\")==\"大帅比\"";
            // then
            String then = "result.setValue(\"确实帅\");";
            // 规则内容
            Rule testModeRule = new MVELRule()
                    .name("test rule")
                    .description("rule for test")
                    .when(when)
                    .then(then);
            // 规则集合
            Rules rules = new Rules();
            // 将规则添加到集合
            rules.register(testModeRule);
            //创建规则执行引擎,并执行规则
            RulesEngine rulesEngine = new DefaultRulesEngine();
            rulesEngine.fire(rules, facts);
            log.info("result:{}",result.toString());
        }
    
    • 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

    完善下,把规则抽象出来,最终的目的就是通过调用方传入的参数,生成when和then表达式;

    规则

    @Data
    @ToString(callSuper = true)
    @TableName("EXPRESSION_RULE")
    public class ExpressionRule {
        /**
         * 主键
         */
        Long id;
        /**
         * 规则分组
         */
        String ruleGroup;
        /**
         * 规则标识(英文,且唯一)
         */
        String ruleCode;
        /**
         * 规则名
         */
        String ruleName;
    
        /**
         * 规则描述
         */
        String ruleDescription;
    
        /**
         * 满足规则后的消息
         */
        String message;
    
        /**
         * 规则表达式,通过conditionList生成
         */
        String whenExpression;
    
        /**
         * 匹配规则操作表达式
         */
        String thenExpression;
    
        /**
         * 创建人工号
         */
        String creatUser;
    
        /**
         * 创建时间
         */
        Date createTime;
    
        /**
         * 最近更新时间
         */
        Date lastUpdateTime;
    
        /**
         * 最近更新人
         */
        String lastUpdateUser;
    
        /**
         * 是否启用
         */
        boolean isEnable;
    
        /**
         * 条件列表
         */
        @TableField(exist = false)
        List<ExpressionRuleCondition> conditionList;
    }
    
    
    • 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
    规则下的条件
    @Data
    @ToString(callSuper = true)
    @TableName("EXPRESSION_RULE_CONDITION")
    public class ExpressionRuleCondition {
    
        /**
         * 主键
         */
        Long id;
    
        /**
         * 主表ID
         */
        Long masterId;
    
        /**
         * 左括号
         * ( ~ (((((
         */
        String leftBrackets;
    
        /**
         * 字段
         */
        String field;
    
        /**
         * 操作
         * 大于,小于,等于,不等于,包含,不包含,前缀,后缀
         * >  , <  , ==, !=  , contains , !contains, startWith, endWith
         *
         */
        String operator;
    
        /**
         * 值字段类型
         * 自定义输入 或 实体字段
         *  input , field
         */
        String valueType;
    
        /**
         * 字段规则命中值
         */
        String value;
    
        /**
         * 右括号
         * ) ~ )))))
         */
        String rightBrackets;
    
        /**
         * 多条件连接符号
         * && ~ ||
         */
        String link;
    }
    
    
    • 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

    RuleUtils

    @Slf4j
    public class RuleUtils {
    
        public static RuleResult match(JSONObject dataSource, ExpressionRule rule){
            // 结果
            RuleResult result = new RuleResult();
            // 规则实例
            Facts facts = new Facts();
            facts.put("fact",dataSource);
            facts.put("result",result);
            // 规则内容
            Rule mvelrule = new MVELRule()
                    .name(rule.getRuleName())
                    .description(rule.getRuleDescription())
                    .when(rule.getWhenExpression())
                    .then(rule.getThenExpression());
            // 规则集合
            Rules rules = new Rules();
            // 将规则添加到集合
            rules.register(mvelrule);
            //创建规则执行引擎,并执行规则
            RulesEngine rulesEngine = new DefaultRulesEngine();
            rulesEngine.fire(rules, facts);
            result.setRuleId(rule.getId());
            return result;
        }
    
    }
    
    • 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

    新建规则

    controller

        @PostMapping("/addRule")
        @ApiOperation("新增表达式规则")
        public ResponseResult<Long> addRule(@RequestBody BaseRuleRo ruleRo){
            return ruleService.addRule(ruleRo);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    serviceImpl

        /**
         * 新增表达式规则
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public ResponseResult<Long> addRule(BaseRuleRo rule) {
            ExpressionRule expressionRule = new ExpressionRule();
            expressionRule.setId(ruleMapper.nextSque());
            expressionRule.setCreateTime(new Date());
            expressionRule.setEnable(true);
            expressionRule.setRuleGroup(rule.getRuleGroup());
            expressionRule.setRuleCode(rule.getRuleCode());
            expressionRule.setRuleDescription(rule.getRuleDescription());
            expressionRule.setRuleName(rule.getRuleName());
            expressionRule.setMessage(rule.getMessage());
            // 核心代码段
            expressionRule.setWhenExpression(rule.getWhenExpression("fact"));
            expressionRule.setThenExpression(rule.getThenExpression("result"));
            // TODO 获取用户信息
            // expressionRule.setCreatUser(userHandler.getUserName());
            ruleMapper.insert(expressionRule);
            // 条件表
            List<ExpressionRuleCondition> conditionList = new ArrayList<>();
            rule.getConditionRoList().forEach(condition->{
                ExpressionRuleCondition expressionRuleCondition = new ExpressionRuleCondition();
                expressionRuleCondition.setId(conditionMapper.nextSque());
                expressionRuleCondition.setMasterId(expressionRule.getId());
                expressionRuleCondition.setLeftBrackets(condition.getLeftBrackets());
                expressionRuleCondition.setField(condition.getField());
                expressionRuleCondition.setOperator(condition.getOperator());
                expressionRuleCondition.setValueType(condition.getValueType());
                expressionRuleCondition.setValue(condition.getValue());
                expressionRuleCondition.setRightBrackets(condition.getRightBrackets());
                expressionRuleCondition.setLink(condition.getLink());
                conditionList.add(expressionRuleCondition);
            });
            conditionMapper.insertBatch(conditionList);
            return ResponseResult.buildSucc(expressionRule.getId());
        }
    
    • 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

    RO

    @Slf4j
    @Data
    @ToString
    public class BaseRuleRo {
    
        /**
         * 主键
         */
        @ApiModelProperty(value = "id")
        Long id;
        /**
         * 规则组
         */
        @ApiModelProperty(value = "ruleGroup",required = true)
        String ruleGroup;
        /**
         * 规则标识(英文,且唯一)
         */
        @ApiModelProperty(value = "ruleCode",required = true)
        String ruleCode;
        /**
         * 规则名
         */
        @ApiModelProperty(value = "ruleName",required = true)
        String ruleName;
    
        /**
         * 规则描述
         */
        @ApiModelProperty(value = "ruleDescription",required = true)
        String ruleDescription;
    
        /**
         * 条件
         */
        @ApiModelProperty(value = "conditionRoList",required = true)
        List<ConditionRo> conditionRoList;
    
        /**
         * 满足规则后可以带这个值
         */
        @ApiModelProperty(value = "message",required = true)
        String message;
    
        /**
         * 是否启用
         */
        @ApiModelProperty(value = "isEnable")
        boolean isEnable = true;
    
        /**
         * 条件表达式
         */
        @ApiModelProperty(hidden=true)
        @JsonIgnore
        private String whenExpression;
    
        /**
         * 结果表达式
         */
        @ApiModelProperty(hidden=true)
        @JsonIgnore
        private String thenExpression;
    
        /**
         * 组装when表达式
         */
        public String getWhenExpression(String factAlias) throws UnSupportException {
            StringBuilder expression = new StringBuilder();
            conditionRoList.forEach(conditionRo -> {
                expression
                        .append(conditionRo.leftBrackets)
                        .append(factAlias)
                        // 从JSON中取出字段值
                        .append(".get(\"")
                        .append(conditionRo.field)
                        .append("\")")
                        .append(conditionRo.getOperatorExpress(factAlias))
                        .append(conditionRo.rightBrackets)
                        .append(conditionRo.getLinkExpress());
            });
            log.info("whenExpression: {}",expression.toString());
            return expression.toString();
        }
    
        /**
         * 组装then表达式
         * 不做太过灵活的操作,值返回是否命中规则,至于命中后改干什么业务代码自己实现
         */
        public String getThenExpression(String resultAlias){
            StringBuilder expression = new StringBuilder();
            expression.append(resultAlias).append(".setValue(\"").append(message).append("\");");
            log.info("thenExpression: {}",expression.toString());
            return expression.toString();
        }
    }
    
    • 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
    @Data
    @ToString(callSuper = true)
    public class ConditionRo {
    
        /**
         * 左括号
         * ( ~ (((((
         */
        @ApiModelProperty(value = "leftBrackets",required = true)
        String leftBrackets;
    
        /**
         * 字段
         */
        @ApiModelProperty(value = "field",required = true)
        String field;
    
        /**
         * 操作
         * 大于,小于,等于,不等于,包含,不包含,前缀,后缀
         * >  , <  , ==, !=  , contains , !contains, startWith, endWith
         *
         */
        @ApiModelProperty(value = "operator",required = true)
        String operator;
    
        /**
         * 值字段类型
         * 自定义输入 或 实体字段
         *  input , field
         */
        @ApiModelProperty(value = "valueType",required = true)
        String valueType;
    
        /**
         * 字段规则命中值
         */
        @ApiModelProperty(value = "value",required = true)
        String value;
    
        /**
         * 右括号
         * ) ~ )))))
         */
        @ApiModelProperty(value = "rightBrackets",required = true)
        String rightBrackets;
    
        /**
         * 多条件连接符号
         * && ~ ||
         */
        @ApiModelProperty(value = "link",required = true)
        String link;
    
        /**
         * 运算符表达式
         */
        String getOperatorExpress(String factAlias) throws UnSupportException {
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_GREATER_THEN)){
                return ">"+getValueExpress(factAlias);
            }
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_LESS_THEN)){
                return "<"+getValueExpress(factAlias);
            }
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_QEUAL)){
                return "=="+getValueExpress(factAlias);
            }
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_UNQEUAL)){
                return "!="+getValueExpress(factAlias);
            }
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_CONTAIN)){
                return ".contains("+getValueExpress(factAlias)+")";
            }
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_PREFIX)){
                return ".startsWith("+getValueExpress(factAlias)+")";
            }
            if(operator.equalsIgnoreCase(RuleOperation.OPERATOR_SUFFIX)){
                return ".endsWith("+getValueExpress(factAlias)+")";
            }
            throw new UnSupportException(operator+" is not support ");
        }
    
        /**
         * 比较值表达式
         */
        private String getValueExpress(String factAlias) throws UnSupportException{
            if(!valueType.equalsIgnoreCase(RuleOperation.INPUT_VALUE) && !valueType.equalsIgnoreCase(RuleOperation.FIELD_VALUE)){
                throw new UnSupportException(valueType+" is not support ");
            }
            return valueType.equalsIgnoreCase(RuleOperation.INPUT_VALUE)? String.format("\"%s\"", value) : String.format("%s.get(\"%s\")", factAlias, value);
        }
    
        /**
         * 连接符表达式
         */
        String getLinkExpress() throws UnSupportException{
            if(StringUtils.isEmpty(link)){
                return "";
            }
            if(link.equalsIgnoreCase(RuleOperation.LINK_AND)){
                return "&&";
            }
            if(link.equalsIgnoreCase(RuleOperation.LINK_OR)){
                return "||";
            }
            throw new UnSupportException(link+" is not support ");
        }
    }
    
    
    • 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

    匹配规则

    controller

        /**
         * 匹配规则
         */
        @PostMapping("/match")
        @ApiOperation("匹配规则")
        public ResponseResult<List<RuleResult>> match(@RequestBody RuleMatchRo matchRo){
            return ruleService.match(matchRo);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    serviceImpl

        /**
         * 匹配规则
         */
        @Override
        public ResponseResult<List<RuleResult>> match(RuleMatchRo ruleRo) {
            try{
                if(CollectionUtils.isEmpty(ruleRo.getRuleIds())) {
                    return ResponseResult.buildFail("ruleIds为空");
                }
                // 查询规则,先查redis,再查DB
                List<ExpressionRule> rules = getRuleList(ruleRo.getRuleIds());
                // 进行规则匹配
                List<RuleResult> results = new ArrayList<>();
                rules.forEach(rule -> {
                    RuleResult matchResult = RuleUtils.match(ruleRo.getFact(),rule);
                    results.add(matchResult);
                });
                return ResponseResult.buildSucc(results);
            }catch (Exception e){
                log.error("匹配规则异常",e);
                return ResponseResult.buildFail("匹配规则异常!");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    RO

    @Data
    public class RuleMatchRo {
        /**
         * 需要匹配的规则ID列表
         */
        @ApiModelProperty(value = "ruleIds")
        List<Long> ruleIds;
    
        /**
         * 数据源
         */
        @ApiModelProperty(value = "fact")
        JSONObject fact;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    第1章 初识视图文件(*.vue)
    【Verilog教程】2.1基本语法
    hahahaha发到这里吧
    Centos 安装 OpenLDAP
    python基于django的健身资讯网站 vue
    无重复字符的最长子串 - 力扣(LeetCode)
    The-MIFARE-Hack-1 -mifare技术
    Socks5代理:数字化时代的技术支柱
    Linux进程控制
    代码随想录训练营day52
  • 原文地址:https://blog.csdn.net/fh_luchenxi/article/details/126885994