依赖
<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;}
}
规则引擎本身的目的是为了业务与开发解耦,因此只输出是否满足规则,至于满足规则后要干什么业务,这个就是调用方的事。
简单的测试
@Testvoid 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);// whenString when = "fact.get(\"name\")==\"大帅比\"";// thenString 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)@JsonIgnoreprivate String whenExpression;/*** 结果表达式*/@ApiModelProperty(hidden=true)@JsonIgnoreprivate 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
/*** 匹配规则*/@Overridepublic ResponseResult<List<RuleResult>> match(RuleMatchRo ruleRo) {try{if(CollectionUtils.isEmpty(ruleRo.getRuleIds())) {return ResponseResult.buildFail("ruleIds为空");}// 查询规则,先查redis,再查DBList<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;
}