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