中国做的儿童编程网站,网站的seo 如何优化,网站建设包括哪些流程,企查查企业信息查询免费文章目录 简介pom 规则抽象规则Rule基础规则BasicRule事实类Facts#xff1a;map条件接口动作接口 四种规则定义方式注解方式RuleBuilder 链式Mvel和Spel表达式Yml配置 常用规则类DefaultRuleSpELRule#xff08;Spring的表达式注入#xff09; 组合规则UnitRuleGroup 规则引… 文章目录 简介pom 规则抽象规则Rule基础规则BasicRule事实类Factsmap条件接口动作接口 四种规则定义方式注解方式RuleBuilder 链式Mvel和Spel表达式Yml配置 常用规则类DefaultRuleSpELRuleSpring的表达式注入 组合规则UnitRuleGroup 规则引擎引擎接口引擎抽象类引擎类-DefaultRulesEngine规则引擎参数(决定规则之间是否互斥|中断|跳过等) 实战场景1- 恒打印规则description规则规则引擎使用DefaultRulesEngine执行fire方法执行流程 场景2-yml场景3 简单if-else场景4-动态规则MVEL表达式Json字符串场景5-QLExpress场景6- 动态规则Mvel Json文件场景7-履约缺货处罚金额计算 简介
1、是什么
基于java轻量级的规则引擎学习成本更低、适用性更强本质一个函数yf(x1,x2,…,xn)Easy Rules 每条规则都有一个条件condition和一个动作action简单地说可以将其看作是一组 IF THEN 或类似SQL的when-then语句
2、作用
解决业务代码和业务规则分离实现了将业务决策从应用程序代码中分离。 接受数据输入解释业务规则并根据业务规则做出业务决策。
业务系统在应用过程中常常包含着要处理复杂、多变的部分这部分往往是业务规则或者是数据的处理逻辑。因此这部分的动态规则的问题往往需要可配置并对系统性能和热部署有一定的要求。从开发与业务的视角主要突出以下的一些问题
从开发人员视角来看
1逻辑复杂要使用大量if-else来实现或者使用设计模式。但过于复杂的规则逻辑使用设计模式也往往是存在大量并且关系复杂的类导致代码难于维护对新加入的同学极不友好。
2变更时需要从头梳理逻辑在适当的地方进行if…else…代码逻辑调整耗费大量时间进行梳理。
3开发周期较长当需求发生变更时需要研发人员安排开发周期上线对于当下快速变化的业务传统的开发工作方式显得捉襟见肘。
从业务人员视角来看
1业务人员期望友好的管理界面不需要专业的开发技能就能够完成规则的管理、发布。
2期望能够实现热部署由业务人员配置好之后即配即用。
3减少业务规则对开发人员的依赖。
4降低需求变动的时间成本快速验证发布
3、怎么做
你可以自己构建一个简单的规则引擎。你所需要做的就是创建一组带有条件和动作的规则对象rule将它们存储在一个集合中rules然后遍历它们以评估fire条件condition并执行这些动作action。
pom
dependencygroupIdorg.jeasy/groupIdartifactIdeasy-rules-core/artifactIdversion3.3.0/version
/dependency!--规则定义文件格式支持json,yaml等--
dependencygroupIdorg.jeasy/groupIdartifactIdeasy-rules-support/artifactIdversion3.3.0/version
/dependency!--支持mvel规则语法库--
dependencygroupIdorg.jeasy/groupIdartifactIdeasy-rules-mvel/artifactIdversion3.3.0/version
/dependency!-- https://mvnrepository.com/artifact/org.jeasy/easy-rules-spel --
dependencygroupIdorg.jeasy/groupIdartifactIdeasy-rules-spel/artifactIdversion4.1.0/version
/dependency规则
大多数业务规则可以由以下定义表示 名称name规则命名空间中的唯一规则名称 说明description规则的简要说明 优先级priority相对于其他规则的规则优先级较小的值表示较高的优先级 事实fact去匹配规则时的一组已知事实 条件condition为了匹配该规则在给定某些事实的情况下应满足的一组条件 如果规则条件为true则规则将被触发执行。否则规则将被忽略 动作action当条件满足时要执行的一组动作可以添加/删除/修改事实 它可以用于实现各种应用程序逻辑例如更新数据、发送消息等。
抽象规则Rule
public interface Rule extends ComparableRule {String DEFAULT_NAME rule;String DEFAULT_DESCRIPTION description;int DEFAULT_PRIORITY 2147483646;boolean evaluate(Facts var1);void execute(Facts var1) throws Exception;
}基础规则BasicRule
package org.jeasy.rules.core;import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;public class BasicRule implements Rule {protected String name;//名称protected String description;//说明protected int priority;//优先/*** 此方法封装规则的条件conditions** param facts :事实* return 如果提供的事实适用于该规则条件返回true*/public boolean evaluate(Facts facts) {return false;}/*** 此方法封装规则的操作actions* evaluate方法值为TRUE才能触发execute方法在满足规则条件时应执行的操作。* throws 如果在执行过程中发生错误将抛出Exception*/public void execute(Facts facts) throws Exception {}}事实类Factsmap
package org.jeasy.rules.api;import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;public class Facts implements IterableMap.EntryString, Object {private MapString, Object facts new HashMap();public Object put(String name, Object fact) {Objects.requireNonNull(name);return this.facts.put(name, fact);}public Object remove(String name) {Objects.requireNonNull(name);return this.facts.remove(name);}public T T get(String name) {Objects.requireNonNull(name);return this.facts.get(name);}
}扩展可以将普通对象包含属性|方法facts.put()。也可以将rpc对象put进来
Resource
private MqService mqService;facts.put(mqService, mqService)这样condition方法就可以使用mqService的方法
.when(mqService.doRuleConditions(conditionList,tmpParam))
或
Override
public boolean evaluate(Facts(mqService) MqService mqService) {mqService.doRuleConditions();
}条件接口
public interface Condition {Condition FALSE new Condition() {public boolean evaluate(Facts facts) {return false;}};Condition TRUE new Condition() {public boolean evaluate(Facts facts) {return true;}};boolean evaluate(Facts var1);
}动作接口
public interface Action {void execute(Facts var1) throws Exception;
}四种规则定义方式
注解方式
1、eg1也可以实现Rule接口
package com.mjp.easyrules.drmo1;import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.core.BasicRule;public class MyRule extends BasicRule {Overridepublic boolean evaluate(Facts facts) {return false;}Overridepublic void execute(Facts facts) throws Exception {}
}2、纯注解
Rule(name weather rule, description if it rains then take an umbrella, priority 777)
public class WeatherRule {Conditionpublic boolean itRains(Fact(rain) boolean rain, Fact(other) String other) {return rain;}Action(order 1)public void takeAnUmbrella() {System.out.println(It rains, take an umbrella!);}Action(order 2)public void then2(Facts facts) throws Exception {//my actions2}
}RuleBuilder 链式
1、eg1
Rule rule new RuleBuilder().name(myRule).description(myRuleDescription).priority(2).when(facts - true).then(facts - {// do Action1}).then(facts - {// do Action2}).build();2、eg2
Rule weatherRule new RuleBuilder().name(weather rule).description(if it rains then take an umbrella).when(facts - facts.get(rain).equals(true)).then(facts - System.out.println(It rains, take an umbrella!)).build();Mvel和Spel表达式
1、eg1Mvel表达式
Mvel表达式
Rule weatherRule new MVELRule().name(weather rule).description(if it rains then take an umbrella).when(rain true).then(System.out.println(\It rains, take an umbrella!\););when对象属性
when方法参数只能是facts对象中key对象内部定义的属性或方法
.when(person.age 18)
.when(person.isHappy() true)
也可以是类的静态方法
Demo.add(1,2)//这样when会执行去调用类方法add2、补充
when、then内容可以从自定义的json文件中读取并解析成Mvel表达式形式。解析过程可参考场景6
3、eg2Spel表达式同理
Spel表达式
Yml配置
eg1: weather-rule.yml
name: weather rule
description: if it rains then take an umbrella
condition: true
actions:- System.out.println(\It rains, take an umbrella!\);condition对象方法
condition: person.isAdult() false
condition: person.getResult(person.getQualifications())eg2一个yml文件创建多个规则
---
name: adult rule
description: when age is greater than 18, then mark as adult
priority: 1
condition: person.age 18
actions:- person.setAdult(true);---
name: weather rule
description: when it rains, then take an umbrella
priority: 2
condition: rain true
actions:- System.out.println(It rains, take an umbrella!);读取多条规则
MVELRuleFactory ruleFactory new MVELRuleFactory(new YamlRuleDefinitionReader());Rule weatherRule ruleFactory.createRule(new FileReader(D:\\CodeBetter\\src\\main\\resources\\weather-rule.yml));
// 多条规则
Rules rules ruleFactory.createRules(new FileReader(rules.yml));常用规则类
DefaultRule
package org.jeasy.rules.core;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jeasy.rules.api.Action;
import org.jeasy.rules.api.Condition;
import org.jeasy.rules.api.Facts;class DefaultRule extends BasicRule {private Condition condition;//一个规则一个条件。private ListAction actions;//一个规则当满足条件时可以有多个动作public boolean evaluate(Facts facts) {return this.condition.evaluate(facts);}public void execute(Facts facts) throws Exception {Iterator var2 this.actions.iterator();while(var2.hasNext()) {Action action (Action)var2.next();action.execute(facts);}}
}SpELRuleSpring的表达式注入
表达式注入
package org.jeasy.rules.spel;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jeasy.rules.api.Action;
import org.jeasy.rules.api.Condition;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.core.BasicRule;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ParserContext;public class SpELRule extends BasicRule {private Condition condition;//条件private final ListAction actions;//动作private final ParserContext parserContext;//文本解析private BeanResolver beanResolver;public boolean evaluate(Facts facts) {return this.condition.evaluate(facts);}public void execute(Facts facts) throws Exception {Iterator var2 this.actions.iterator();while(var2.hasNext()) {Action action (Action)var2.next();action.execute(facts);}}public SpELRule when(String condition) {this.condition new SpELCondition(condition, this.parserContext, this.beanResolver);return this;}public SpELRule then(String action) {this.actions.add(new SpELAction(action, this.parserContext, this.beanResolver));return this;}
}when方法-SpELCondition
public class SpELCondition implements Condition {private final ExpressionParser parser;private final Expression compiledExpression;private BeanResolver beanResolver;public SpELCondition(String expression, ParserContext parserContext, BeanResolver beanResolver) {this.parser new SpelExpressionParser();this.beanResolver beanResolver;this.compiledExpression this.parser.parseExpression(expression, parserContext);}public boolean evaluate(Facts facts) {StandardEvaluationContext context new StandardEvaluationContext();context.setRootObject(facts.asMap());context.setVariables(facts.asMap());if (this.beanResolver ! null) {context.setBeanResolver(this.beanResolver);}return (Boolean)this.compiledExpression.getValue(context, Boolean.class);}
}then方法- SpELAction
public class SpELAction implements Action {private static final Logger LOGGER LoggerFactory.getLogger(SpELAction.class);private final ExpressionParser parser;private final String expression;private final Expression compiledExpression;private BeanResolver beanResolver;public SpELAction(String expression, ParserContext parserContext, BeanResolver beanResolver) {this.parser new SpelExpressionParser();this.expression expression;this.beanResolver beanResolver;this.compiledExpression this.parser.parseExpression(expression, parserContext);}public void execute(Facts facts) {try {StandardEvaluationContext context new StandardEvaluationContext();context.setRootObject(facts.asMap());context.setVariables(facts.asMap());if (this.beanResolver ! null) {context.setBeanResolver(this.beanResolver);}this.compiledExpression.getValue(context);} catch (Exception var3) {LOGGER.error(Unable to evaluate expression: this.expression on facts: facts, var3);throw var3;}}
}组合规则
抽象CompositeRule类由一组规则组成。这是一个典型地组合设计模式的实现。可以以不同方式触发组合规则。
三种CompositeRule具体子类 UnitRuleGroup : 要么应用所有规则要么不应用任何规则AND逻辑 ActivationRuleGroup : 它触发第一个适用规则并忽略组中的其他规则XOR逻辑 ConditionalRuleGroup : 如果具有最高优先级的规则计算结果为true则触发其余规则 UnitRuleGroup
规则1 Rule weatherRule1 new MVELRule().name(weather rule1).description(if it rains then move).when(isRain true).then(System.out.println(\rule1: It rains, move!\););规则2
Rule(name weather rule2, description if it rains then take an umbrella,priority 777)
public class WeatherRule {Conditionpublic boolean itRains(Fact(isRain) boolean isRain) {return isRain;}Action(order 1)public void takeAnUmbrella() {System.out.println(rule2: action1-It rains, take an umbrella!);}Action(order 2)public void then(Facts facts) throws Exception {System.out.println(rule2: action2-边打伞边听歌);}
}执行
// 1.创建事实factFacts facts new Facts();facts.put(isRain, true);// 2.创建规则1(Mvel形式)Rule weatherRule1 new MVELRule().name(weather rule1).description(if it rains then move).when(isRain true).then(System.out.println(\rule1: It rains, move!\););// 2.创建规则1(注解形式)WeatherRule weatherRule2 new WeatherRule();// 3.创建组合规则UnitRuleGroup myUnitRuleGroup new UnitRuleGroup(myUnitRuleGroup, unit of weatherRule1 and weatherRule2);myUnitRuleGroup.addRule(weatherRule1);myUnitRuleGroup.addRule(weatherRule2);// 4.创建规则引擎DefaultRulesEngine defaultRulesEngine new DefaultRulesEngine();// 5.定义RulesRules rules new Rules();rules.register(myUnitRuleGroup);// 7.执行defaultRulesEngine.fire(rules, facts);输出
rule2: action1-It rains, take an umbrella!
rule2: action2-边打伞边听歌
rule1: It rains, move!先输出规则2优先级为777规则1优先级为默认值2147483646值越小优先级越高
规则引擎
引擎接口
public interface RulesEngine {RulesEngineParameters getParameters();ListRuleListener getRuleListeners();ListRulesEngineListener getRulesEngineListeners();void fire(Rules var1, Facts var2);MapRule, Boolean check(Rules var1, Facts var2);
}引擎抽象类
AbstractRuleEngine作用就是抽出多个引擎类共有的不需要再各自额外重复去实现
引擎类-DefaultRulesEngine
public final class DefaultRulesEngine extends AbstractRuleEngine {private static final Logger LOGGER LoggerFactory.getLogger(DefaultRulesEngine.class);public void fire(Rules rules, Facts facts) {this.triggerListenersBeforeRules(rules, facts);this.doFire(rules, facts);this.triggerListenersAfterRules(rules, facts);}void doFire(Rules rules, Facts facts) {Iterator var3 rules.iterator();while(var3.hasNext()) {Rule rule (Rule)var3.next();String name rule.getName();int priority rule.getPriority();if (rule.evaluate(facts)) {this.triggerListenersAfterEvaluate(rule, facts, true);try {this.triggerListenersBeforeExecute(rule, facts);rule.execute(facts);this.triggerListenersOnSuccess(rule, facts);if (this.parameters.isSkipOnFirstAppliedRule()) {LOGGER.debug(Next rules will be skipped since parameter skipOnFirstAppliedRule is set);break;}} catch (Exception var8) {this.triggerListenersOnFailure(rule, var8, facts);if (this.parameters.isSkipOnFirstFailedRule()) {LOGGER.debug(Next rules will be skipped since parameter skipOnFirstFailedRule is set);break;}}} else {this.triggerListenersAfterEvaluate(rule, facts, false);if (this.parameters.isSkipOnFirstNonTriggeredRule()) {LOGGER.debug(Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set);break;}}}}public MapRule, Boolean check(Rules rules, Facts facts) {this.triggerListenersBeforeRules(rules, facts);MapRule, Boolean result this.doCheck(rules, facts);this.triggerListenersAfterRules(rules, facts);return result;}private MapRule, Boolean doCheck(Rules rules, Facts facts) {LOGGER.debug(Checking rules);MapRule, Boolean result new HashMap();Iterator var4 rules.iterator();while(var4.hasNext()) {Rule rule (Rule)var4.next();if (this.shouldBeEvaluated(rule, facts)) {result.put(rule, rule.evaluate(facts));}}return result;}
}规则引擎参数(决定规则之间是否互斥|中断|跳过等)
RulesEngineParameters
skipOnFirstAppliedRule为true告诉引擎规则被触发时跳过后面的规则。使用场景是各个规则之间互斥a || b || c || d如果a为true则b、c、d不执行当发现一个满足条件的规则并执行了相关操作后便不再继续判断其他规则skipOnFirstFailedRule失败时跳过后面的规则。a b c d如果a为false则b、c、d不执行skipOnFirstNonTriggeredRule一个规则不会被触发跳过后面的规则。如果满足当前的规则则执行相应的操作直到遇到不满足条件的规则为止并且也不会对其他规则进行判断了
rulePriorityThreshold如果优先级超过定义的阈值则跳过下一个规则。版本3.3已经不支持更改默认MaxInt。实战
场景1- 恒打印
规则description
默认打印Hello World
规则
public class MyRule extends BasicRule {Overridepublic String getName() {return my rule;}Overridepublic String getDescription() {return my rule description;}Overridepublic boolean evaluate(Facts facts) {return true;}Overridepublic void execute(Facts facts) throws Exception {System.out.println(我是一个打印Hello World的规则);}
}规则引擎使用DefaultRulesEngine
执行
public static void main(String[] args) {// 1.创建事实factFacts facts new Facts();// 2.创建条件condition(这里MyRule中默认为true就不自定义Condition实现类了)// 3.创建动作action(这里MyRule中默认为打印HelloWorld就不自定义Action实现类了)// 4.创建规则MyRule myRule new MyRule();// 5.定义RulesRules rules new Rules();rules.register(myRule);// 6.创建规则引擎DefaultRulesEngine defaultRulesEngine new DefaultRulesEngine();// 7.执行defaultRulesEngine.fire(rules, facts);}输出
DefaultRulesEngineListener - Rules evaluation started
DefaultRuleListener - Rule my rule triggered我是一个打印Hello World的规则DefaultRuleListener - Rule my rule performed successfullyfire方法执行流程
第一步ListRulesEngineListener rulesEngineListeners;执行 void beforeEvaluate(Rule var1, Facts var2)第二步MyRule的boolean evaluate(Facts facts)第三步ListRuleListener ruleListeners;执行void afterEvaluate(Rule var1, Facts var2, boolean evaluateResult)第四步ListRuleListener ruleListeners;执行void beforeExecute(Rule var1, Facts var2);第五步MyRule的 void execute(Facts facts)第六步ListRuleListener ruleListeners;执行void onSuccess(Rule var1, Facts var2);场景2-yml
1、规则description
if it rains then take an umbrella
2、定义规则
weather-rule.yml
name: weather rule
description: if it rains then take an umbrella
condition: isRain true
actions:- System.out.println(\It rains, take an umbrella!\);3、自定义规则引擎使用默认的
4、执行
// 1.创建事实factFacts facts new Facts();facts.put(isRain, true);// 2.创建条件condition(weatherRule中:isRain true就不自定义Condition实现类了)// 3.创建动作action(weatherRule中默认为It rains, take an umbrella!就不自定义Action实现类了)// 4.创建规则(Yml形式)MVELRuleFactory ruleFactory new MVELRuleFactory(new YamlRuleDefinitionReader());Rule weatherRule ruleFactory.createRule(new FileReader(D:\\CodeBetter\\src\\main\\resources\\weather-rule.yml));// 5.创建规则引擎DefaultRulesEngine defaultRulesEngine new DefaultRulesEngine();// 6.定义RulesRules rules new Rules();rules.register(weatherRule);// 7.执行defaultRulesEngine.fire(rules, facts);场景3 简单if-else
1、功能描述
从1数到100并且:
需求1如果数字是5的倍数则打印i :是5的倍数需求2如果数字是7的倍数请打印i :是7的倍数需求3如果数字是5和7的倍数请打印i :是5的倍数、7的倍数需求4否则打印数字本身
2、常规实现方法
public class FizzBuzz {public static void main(String[] args) {for(int i 1; i 100; i) {if (((i % 5) 0) ((i % 7) 0))System.out.print(fizzbuzz);else if ((i % 5) 0) System.out.print(fizz);else if ((i % 7) 0) System.out.print(buzz);else System.out.print(i);}}
}3、使用Easy Rules规则引擎实现
将每个需求编写一条规则:
rule1
Rule(name Mod5Rule, description mod5, priority 1)
public class Mod5Rule {Conditionpublic boolean canMod5(Fact(number) Integer number) {return number % 5 0;}Actionpublic void action(Facts facts) {Integer i facts.get(number);System.out.print(i :是5的倍数);}
}rule2
Rule(name Mod7Rule, description mod7, priority 2)
public class Mod7Rule {Conditionpublic boolean canMod7(Fact(number) Integer number) {return number % 7 0;}Actionpublic void action(Facts facts) {Integer i facts.get(number);System.out.print(i :是7的倍数);}
}rule3
public class Mod5And7Rule extends UnitRuleGroup {public Mod5And7Rule(Object... rules) {for (Object rule : rules) {addRule(rule);}}Overridepublic int getPriority() {return 0;}
}rule4
Rule(name OtherRule, description 不是5和7的倍数, priority 3)
public class OtherRule {Conditionpublic boolean canNotMod5Or7(Fact(number) Integer number) {return number % 5 ! 0 || number % 7 ! 0;}Actionpublic void action(Fact(number) Integer number) {System.out.print(number);}
}执行
// 创建规则引擎RulesEngineParameters parameters new RulesEngineParameters().skipOnFirstAppliedRule(true);RulesEngine fizzBuzzEngine new DefaultRulesEngine(parameters);// 创建规则Rules rules new Rules();rules.register(new Mod5Rule());rules.register(new Mod7Rule());rules.register(new Mod5And7Rule(new Mod5Rule(), new Mod7Rule()));rules.register(new OtherRule());// 触发规则Facts facts new Facts();for (int i 1; i 100; i) {facts.put(number, i);fizzBuzzEngine.fire(rules, facts);}这里规则引擎参数skipOnFirstAppliedRule(true)告诉引擎被触发时跳过后面的规则。 即当i 5时满足规则1mod5执行完action1就不会再去匹配其他rule2、3、4规则了。使用场景是各个规则之间互斥
场景4-动态规则MVEL表达式Json字符串
如图1 输入factsNORMAL_NUMBER和ERROR_NUMBER两个值使用Mvel表达式解析facts是否满足上述json中定义的condition
操作符枚举类(加减乘除 、)
package com.mjp.easyrules.csdn;public enum OperationEnum {GREATER_THAN(GREATER_THAN, %s %s, 大于),GREATER_THAN_EQUAL(GREATER_THAN_EQUAL, %s %s, 大于等于),LESS_THAN(LESS_THAN, %s %s, 小于),LESS_THAN_EQUAL(LESS_THAN_EQUAL, %s %s, 小于等于),EQUAL(EQUAL, %s %s, 等于),UNEQUAL(UNEQUAL, %s ! %s, 不等于),BETWEEN(BETWEEN, %s %s %s %s, 介于之间),OUT_OF_RANGE(OUT_OF_RANGE, %s %s || %s %s, 超出范围),CONTAINS(CONTAINS, %s.contains(\%s\), 包含),STARTSWITH(STARTSWITH, %s.startsWith(\%s\), 前缀),ENDSWITH(ENDSWITH, %s.endsWith(\%s\), 后缀),;public static OperationEnum getOperationByOperator(String operator) {OperationEnum[] list OperationEnum.values();for (OperationEnum item : list) {String compareOperator item.getOperator();if (compareOperator.equals(operator)) {return item;}}return null;}private final String operator;private final String expression;private final String remark;OperationEnum(String operator, String expression, String remark) {this.operator operator;this.expression expression;this.remark remark;}public String getOperator() {return operator;}public String getExpression() {return expression;}public String getRemark() {return remark;}
}逻辑运算符、||、and、or
public class EasyRulesConstants {// 事实别名public static final String FACT_ALIAS fact;// 结果别名public static final String RESULT_ALIAS result;// and关系public static final String RELATION_AND and;// or关系public static final String RELATION_OR or;// 匹配成功信息public static final String MATCH_SUCCESS_MESSAGE 匹配成功;public static final String FIELD_TYPE type;public static final String FIELD_OPERATOR operator;public static final String FIELD_NAME metricName;public static final String FIELD_VALUE value;public static final String FIELD_CHILDREN children;public static final String EXPRESSION_TYPE EXPRESSION;public static final String RELATION_TYPE RELATION;public static final String LEFT_BRACKETS (;public static final String RIGHT_BRACKETS );public static final String SYMBOL_SPACE ;public static final String SYMBOL_EMPTY ;public static final String LOGICAL_AND ;public static final String LOGICAL_OR ||;
}工具类负责解析自定义json字符串中的condition和action内容赋值给Mvel规则when、then
package com.mjp.easyrules.csdn;Slf4j
public class EasyRulesUtil {/*** 执行规则匹配* param fact 事实json* param ruleModel 规则模型*/public static RuleResult match(JSONObject fact, RuleModel ruleModel){// 结果RuleResult result new RuleResult();result.setRuleId(ruleModel.getRuleId());// 规则实例Facts facts new Facts();facts.put(FACT_ALIAS, fact);facts.put(RESULT_ALIAS, result);// 规则内容Rule mvelrule new MVELRule().name(ruleModel.getRuleName()).description(ruleModel.getDescription()).when(ruleModel.getWhenExpression()).then(ruleModel.getThenExpression());// 规则集合Rules rules new Rules();// 将规则添加到集合rules.register(mvelrule);// 创建规则执行引擎并执行规则RulesEngine rulesEngine new DefaultRulesEngine();rulesEngine.fire(rules, facts);return result;}/*** 构建mvel条件表达式* param json 节点json例如* {* type: EXPRESSION,* operator: LESS_THAN,* metricName: NORMAL_NUMBER,* value: 11,* children: []* }*/public static String buildWhenExpression(JSONObject json) {StringBuilder mvelExpressionSB new StringBuilder();String type json.getString(FIELD_TYPE);String operator json.getString(FIELD_OPERATOR);switch (type) {case EXPRESSION_TYPE:String fieldName json.getString(FIELD_NAME);String fieldValue json.getString(FIELD_VALUE);mvelExpressionSB.append(buildOperatorExpress(operator, fieldName, fieldValue));break;case RELATION_TYPE:JSONArray children json.getJSONArray(FIELD_CHILDREN);if (children.size() 0) {return SYMBOL_EMPTY;}operator convertRelationExpress(operator);StringBuilder sb new StringBuilder();for (int i 0; i children.size(); i) {JSONObject child children.getJSONObject(i);// 递归构建单个规则条件String childExpression buildWhenExpression(child);if (!childExpression.isEmpty()) {if (sb.length() 0) {sb.append(SYMBOL_SPACE).append(operator).append(SYMBOL_SPACE);}sb.append(LEFT_BRACKETS).append(childExpression).append(RIGHT_BRACKETS);}}mvelExpressionSB.append(sb);break;default:break;}return mvelExpressionSB.toString();}/*** 构建mvel表达式* param operator 操作符* param fieldName 字段名称* param value 字段值*/private static String buildOperatorExpress(String operator, String fieldName, Object value) {OperationEnum operationEnum OperationEnum.getOperationByOperator(operator);if (ObjectUtils.isNotEmpty(operationEnum)) {String expression operationEnum.getExpression();return String.format(expression, buildValueExpress(fieldName), value);}return SYMBOL_EMPTY;}/*** 构建mvel取值表达式* param fieldName 字段名称*/private static String buildValueExpress(String fieldName) {return String.format(%s.get(\%s\), FACT_ALIAS, fieldName);}/*** 转换条件连接符* param relation 条件连接符*/private static String convertRelationExpress(String relation) {if (StringUtils.isEmpty(relation)){return SYMBOL_EMPTY;} else if(relation.equalsIgnoreCase(RELATION_AND)){return LOGICAL_AND;} else if(relation.equalsIgnoreCase(RELATION_OR)){return LOGICAL_OR;}return relation;}/*** 构建mvel结果表达式*/public static String buildThenExpression() {StringBuilder sb new StringBuilder();sb.append(RESULT_ALIAS).append(.setValue(\).append(MATCH_SUCCESS_MESSAGE).append(\););log.info(thenExpression: {}, sb);return sb.toString();}Datapublic static class RuleModel {private String ruleId;String ruleName;String description;String whenExpression;String thenExpression;}Datapublic static class RuleResult {// 规则主键private String ruleId;// 是否匹配, 默认falseboolean isMatch false;// 匹配信息默认为匹配失败String message 匹配失败;/*** 匹配成功后设置成功信息*/public void setValue(String message){this.message message;this.isMatch true;}}
}测试 // 1. 新增规则EasyRulesUtil.RuleModel ruleModel new EasyRulesUtil.RuleModel();ruleModel.setRuleId(1);ruleModel.setRuleName(rule1);ruleModel.setDescription(测试规则);// 2. 设置规则条件String ruleJson {\n \validateCondition\: {\n \type\: \RELATION\,\n \operator\: \OR\,\n \children\: [\n {\n \type\: \EXPRESSION\,\n \operator\: \LESS_THAN\,\n \metricName\: \NORMAL_NUMBER\,\n \value\: \11\,\n \children\: []\n },\n {\n \type\: \EXPRESSION\,\n \operator\: \LESS_THAN_EQUAL\,\n \metricName\: \ERROR_NUMBER\,\n \value\: \11\,\n \children\: []\n },\n {\n \type\: \RELATION\,\n \children\: [\n {\n \type\: \EXPRESSION\,\n \metricName\: \NORMAL_NUMBER\,\n \operator\: \GREATER_THAN\,\n \value\: 10,\n \children\: []\n },\n {\n \type\: \EXPRESSION\,\n \metricName\: \ERROR_NUMBER\,\n \operator\: \GREATER_THAN\,\n \value\: 100,\n \children\: []\n },\n {\n \type\: \RELATION\,\n \children\: [\n {\n \type\: \EXPRESSION\,\n \metricName\: \NORMAL_NUMBER\,\n \operator\: \EQUAL\,\n \value\: 1,\n \children\: []\n },\n {\n \type\: \EXPRESSION\,\n \metricName\: \ERROR_NUMBER\,\n \operator\: \EQUAL\,\n \value\: 1,\n \children \: []\n }\n ],\n \operator\: \OR\\n }\n ],\n \operator\: \OR\\n }\n ]\n }\n };JSONObject conditionJson JSON.parseObject(ruleJson);// 3. 设置factString whenExpression EasyRulesUtil.buildWhenExpression(conditionJson.getJSONObject(validateCondition));ruleModel.setWhenExpression(whenExpression);// 4. 设置结果表达式ruleModel.setThenExpression(EasyRulesUtil.buildThenExpression());// 5. 设置匹配条件JSONObject json new JSONObject();json.put(NORMAL_NUMBER, 12);json.put(ERROR_NUMBER, 12);json.put(省陕西市西安;, 100);// 6. 调用规则匹配EasyRulesUtil.RuleResult result EasyRulesUtil.match(json, ruleModel);System.out.println(result);结果分析显然结果匹配成功。原因如图2 factsNORMAL_NUMBER 10、ERROR_NUMBER 10
condition如图1
显然NORMAL_NUMBER 10满足第一个条件 11直接返回true。
如果我们设置factNORMAL_NUMBER 12则NORMAL_NUMBER 不满足第一个条件。
但是fact中ERROR_NUMBER 10 11满足第二个条件直接返回True
场景5-QLExpress
1、使用阿里的QLExpress dependencygroupIdcom.alibaba/groupIdartifactIdQLExpress/artifactIdversion3.3.2/version/dependencyExpressRunner runner new ExpressRunner();DefaultContextString, MetaRuleResult context new DefaultContext();context.put(o, MetaRuleResult.builder().skuId(1L).result(true).metaRule(o).failureReason().build());context.put(l, MetaRuleResult.builder().skuId(1L).result(false).metaRule(l).failureReason(锁库存不可更改).build());context.put(s, MetaRuleResult.builder().skuId(1L).result(true).metaRule(s).failureReason().build());context.put(w, MetaRuleResult.builder().skuId(1L).result(true).metaRule(w).failureReason(售罄预警).build());context.put(lo, MetaRuleResult.builder().skuId(1L).result(true).metaRule(lo).failureReason().build());context.put(llo, MetaRuleResult.builder().skuId(1L).result(false).metaRule(llo).failureReason(锁库且修改值小于等于OR值可以更改).build());Object result;DefaultContextString, Object computeContext new DefaultContext();for (Map.EntryString, MetaRuleResult entry : context.entrySet()) {computeContext.put(entry.getKey(), entry.getValue().getResult());}String ruleExpress olswlollo;result runner.execute(ruleExpress, computeContext, null, true, false);Boolean bResult (Boolean) result;System.out.println(bResult);//falseMetaRuleResult
Data
Builder
public class MetaRuleResult {private Long skuId;private Boolean result;private String metaRule;private String failureReason;
}2、使用EasyRules实现上述功能
规则1
Rule(name oRule, description 修改值OR可修改)
public class ORule {Conditionpublic boolean when(Fact(oRule) MetaRuleResult oRule) {return oRule.getResult();}Actionpublic void then(Facts facts) {System.out.println(修改值OR可修改);}
}这里的规则是原子规则
规则2
Rule(name lRule, description 锁库不可更改)
public class LRule {Conditionpublic boolean when(Fact(lRule) MetaRuleResult lRule) {return lRule.getResult();}Actionpublic void then(Facts facts) {System.out.println(没锁库可更改);}
}自定义组合规则
public class MyUnitRuleGroup extends CompositeRule {public MyUnitRuleGroup() {}public MyUnitRuleGroup(String name) {super(name);}public MyUnitRuleGroup(String name, String description) {super(name, description);}public MyUnitRuleGroup(String name, String description, int priority) {super(name, description, priority);}public boolean evaluate(Facts facts) {if (!this.rules.isEmpty()) {Iterator var2 this.rules.iterator();Rule rule;do {if (!var2.hasNext()) {return true;}rule (Rule)var2.next();} while(rule.evaluate(facts));// 将失败的facts记录失败的原因String ruleName rule.getName();MetaRuleResult metaRuleResult facts.get(ruleName);facts.put(执行失败 ruleName, metaRuleResult);return false;} else {return false;}}public void execute(Facts facts) throws Exception {Iterator var2 this.rules.iterator();while(var2.hasNext()) {Rule rule (Rule)var2.next();rule.execute(facts);}}
}作用这里的规则是组合规则是原子规则的组合形式可扩展
这里的自定义规则组合是快速失败机制即lo中如果lRule的condiotion为false则直接失败使用facts记录一个失败原因。也可以自定义将每个rule-condition为false的原因都记录下来
自定义condition-after-listeren
public class MyRuleListener implements RuleListener {Overridepublic boolean beforeEvaluate(Rule rule, Facts facts) {return true;}Overridepublic void afterEvaluate(Rule rule, Facts facts, boolean b) {String ruleName rule.getName();if (b) {// 只有lo为true才会走到这个逻辑否则走下面逻辑facts.put(ruleName, MetaRuleResult.builder().skuId(1L).result(true).metaRule(ruleName).failureReason().build());} else {// lo有一个不满足则总体失败将各个失败的原因都记录下来StringBuilder sb new StringBuilder();for (Map.EntryString, Object fact : facts) {String key fact.getKey();if (key.contains(失败)) {MetaRuleResult result (MetaRuleResult)fact.getValue();// 这里result中有中间参数比如lockStatus则原因就可以写lockStatustrue已锁库不允许修改sb.append(result.getFailureReason() 且);}}facts.put(ruleName, MetaRuleResult.builder().skuId(1L).result(false).metaRule(ruleName).failureReason(sb.toString()).build());}}Overridepublic void beforeExecute(Rule rule, Facts facts) {}Overridepublic void onSuccess(Rule rule, Facts facts) {}Overridepublic void onFailure(Rule rule, Facts facts, Exception e) {}
}作用组合规则执行结果。成功|失败已经失败原因
执行 // 1.factFacts facts new Facts();facts.put(oRule, MetaRuleResult.builder().skuId(1L).result(true).metaRule(o).failureReason().build());facts.put(lRule, MetaRuleResult.builder().skuId(1L).result(false).metaRule(l).failureReason().build());// 2.ruleORule oRule new ORule();LRule lRule new LRule();String oAndLRuleName ol;MyUnitRuleGroup oAndL new MyUnitRuleGroup(oAndLRuleName, OR且未锁过库规则校验);oAndL.addRule(oRule);oAndL.addRule(lRule);// 3.rulesRules rules new Rules();rules.register(oAndL);// 4.引擎DefaultRulesEngine engine new DefaultRulesEngine();engine.registerRuleListener(new MyRuleListener());engine.fire(rules,facts);MetaRuleResult result facts.get(oAndLRuleName);if (!result.getResult()) {System.out.println(oAndLRuleName result.getFailureReason());}扩展
1db获取规则表达式
先根据网店品类角色修改类型查询db获取组合规则比如lo
2工厂模式解析组合规则
然后根据lo解析出规则为l和o组合成lo
3facts获取数据
自定义策略模式key为枚举类型value为对应的rpc查询接口
facts.put(o, MetaRuleResult.builder().skuId(1L).result(queryData(o)).metaRule(o).failureReason().build());这里的queryData方法根据规则类型o获取对应的Rpc接口-ORGateway然后查询or值然后比较结果
4)组合规则中判断每个原子规则是否执行通过失败则记录对应执行失败原因
5)在condition-after中自定义listeren如果组合规则condition为false则记录组合规则整体的执行失败以及失败原因
6)如果组合规则整体执行失败则本次结果为false
场景6- 动态规则Mvel Json文件
1、背景
动态规则就是由于业务场景的变化之前的规则已经不适用现在的业务场景需要更改相对应的规则。
例如之前是满300减30现在是满200-30
正常情况下我们需要改规则类里面的比较参数代码然后打包上线。如果使用动态规则修改一下配置中的规则json文件即可线上代码会读取最新的规则配置文件无需上线
2、前提说明
1规则类中的condtion方法可以入参传入Facts参数然后使用facts.get()方法获取内容 但是规则文件(eg:json)的condtion中无法传入Facts参数也就无法使用此参数
2 自定义RuleListener监听会作用到所有执行的规则如何仅处理我们指定的规则
Override
public void afterEvaluate(Rule rule, Facts facts, boolean evaluateResult) {if(evaluateResult Objects.equals(rule.getName(), 我们指定的规则名称)) {}
}3、场景 输入一个人的信息信息中包含了这个人的学历等级作为规则事实 方式一condition中制定推荐可以动态配置0和11Condition修饰方法入参( Fact(“person”) Person person ) [{name: newEducationAdd,description: 修改学历添加列表,condition: person.getQualifications() 0 person.getQualifications()11,priority: 3,actions: [System.out.println(\新规则执行了\)]}
]方式二将condition条件判断自定在fact-key中对象的属性和方法中不推荐
[{name: newEducationAdd,description: 修改学历添加列表,condition: person.getResult(person.getQualifications()),priority: 3,actions: [System.out.println(\新规则执行了\)]
}]Data
public class Person {// 姓名private String name;// 年龄private int age;// 描述private String dec;// 学历等级private int qualifications;private ListEducation educationList;public boolean getResult(int level){return AddEducation.getResult(level);}
}UtilityClass
public class AddEducation {public static boolean getResult(int level){return level 0 level 11;}
}如果学历等级符合规则则去查询学历证书情况(集合存储) 查出完学历证书后在检测学历证书与他的学历等级是否匹配匹配规则为 学历证书数量与学历等级相同最大学历证书的学历等级与学历等级一致 匹配通过则学历真实信息中会添加真实学历匹配结果 未匹配通过则学历造假嫌疑信息中会添加造假学历信息
上线
2、前提说明
1规则类中的condtion方法可以入参传入Facts参数然后使用facts.get()方法获取内容 但是规则文件(eg:json)的condtion中无法传入Facts参数也就无法使用此参数
2 自定义RuleListener监听会作用到所有执行的规则如何仅处理我们指定的规则
Override
public void afterEvaluate(Rule rule, Facts facts, boolean evaluateResult) {if(evaluateResult Objects.equals(rule.getName(), 我们指定的规则名称)) {}
}3、场景 输入一个人的信息信息中包含了这个人的学历等级作为规则事实 方式一condition中制定推荐可以动态配置0和11Condition修饰方法入参( Fact(“person”) Person person ) [{name: newEducationAdd,description: 修改学历添加列表,condition: person.getQualifications() 0 person.getQualifications()11,priority: 3,actions: [System.out.println(\新规则执行了\)]}
]方式二将condition条件判断自定在fact-key中对象的属性和方法中不推荐
[{name: newEducationAdd,description: 修改学历添加列表,condition: person.getResult(person.getQualifications()),priority: 3,actions: [System.out.println(\新规则执行了\)]
}]Data
public class Person {// 姓名private String name;// 年龄private int age;// 描述private String dec;// 学历等级private int qualifications;private ListEducation educationList;public boolean getResult(int level){return AddEducation.getResult(level);}
}UtilityClass
public class AddEducation {public static boolean getResult(int level){return level 0 level 11;}
}如果学历等级符合规则则去查询学历证书情况(集合存储) 查出完学历证书后在检测学历证书与他的学历等级是否匹配匹配规则为 学历证书数量与学历等级相同最大学历证书的学历等级与学历等级一致 匹配通过则学历真实信息中会添加真实学历匹配结果 未匹配通过则学历造假嫌疑信息中会添加造假学历信息
场景7-履约缺货处罚金额计算