springboot + easyRules 搭建规则引擎服务

令狐昂雄
2023-12-01

依赖

        <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>

输入

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

输出

@Data
public class RuleResult {
    /**
     * 规则主键
     */
    Long ruleId;
    /**
     * 规则名
     * 默认false
     */
    boolean isMatch = false;

    /**
     * 规则名
     */
    String value;

    /**
     * 满足规则后执行
     */
    public void setValue(String msg){
        this.value = msg;
        this.isMatch = true;
    }
}

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

简单的测试

    @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());
    }

完善下,把规则抽象出来,最终的目的就是通过调用方传入的参数,生成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;
}

规则下的条件

@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;
}

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;
    }

}

新建规则

controller

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

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());
    }

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();
    }
}
@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 ");
    }
}

匹配规则

controller

    /**
     * 匹配规则
     */
    @PostMapping("/match")
    @ApiOperation("匹配规则")
    public ResponseResult<List<RuleResult>> match(@RequestBody RuleMatchRo matchRo){
        return ruleService.match(matchRo);
    }

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("匹配规则异常!");
        }
    }

RO

@Data
public class RuleMatchRo {
    /**
     * 需要匹配的规则ID列表
     */
    @ApiModelProperty(value = "ruleIds")
    List<Long> ruleIds;

    /**
     * 数据源
     */
    @ApiModelProperty(value = "fact")
    JSONObject fact;
}
 类似资料: