当前位置:   article > 正文

Easy Rules规则引擎实战

easy rules

简介

1、是什么

  • 基于java轻量级的规则引擎,学习成本更低、适用性更强
  • 本质一个函数,y=f(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

<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-core</artifactId>
    <version>3.3.0</version>
</dependency>

<!--规则定义文件格式,支持json,yaml等-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-support</artifactId>
    <version>3.3.0</version>
</dependency>

<!--支持mvel规则语法库-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-mvel</artifactId>
    <version>3.3.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.jeasy/easy-rules-spel -->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-spel</artifactId>
    <version>4.1.0</version>
</dependency>
  • 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

规则

大多数业务规则可以由以下定义表示:

  • 名称name:规则命名空间中的唯一规则名称

  • 说明description:规则的简要说明

  • 优先级priority:相对于其他规则的规则优先级,较小的值表示较高的优先级

  • 事实fact:去匹配规则时的一组已知事实

  • 条件condition:为了匹配该规则,在给定某些事实的情况下应满足的一组条件

    如果规则条件为true,则规则将被触发执行。否则,规则将被忽略

  • 动作action:当条件满足时要执行的一组动作(可以添加/删除/修改事实)

    它可以用于实现各种应用程序逻辑,例如更新数据、发送消息等。

抽象规则Rule

public interface Rule extends Comparable<Rule> {
    String DEFAULT_NAME = "rule";
    String DEFAULT_DESCRIPTION = "description";
    int DEFAULT_PRIORITY = 2147483646;

    boolean evaluate(Facts var1);

    void execute(Facts var1) throws Exception;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

基础规则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 {
    }
    
}
  • 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
事实类Facts:map
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 Iterable<Map.Entry<String, Object>> {
    private Map<String, 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);
    }
}
  • 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
  • 扩展:可以将普通对象(包含属性|方法)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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
条件接口
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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
动作接口
public interface Action {
    void execute(Facts var1) throws Exception;
}
  • 1
  • 2
  • 3

四种规则定义方式

注解方式

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 {

    @Override
    public boolean evaluate(Facts facts) {
        return false;
    }

    @Override
    public void execute(Facts facts) throws Exception {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2、纯注解

@Rule(name = "weather rule", description = "if it rains then take an umbrella", priority = 777)
public class WeatherRule {

    @Condition
    public 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
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
Mvel和Spel表达式

1、eg1:Mvel表达式

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!\");");
  • 1
  • 2
  • 3
  • 4
  • 5
  • when:对象属性

when方法参数,只能是facts对象中key对象,内部定义的属性或方法

.when("person.age > 18")
.when("person.isHappy() == true") 
也可以是类的静态方法
Demo.add(1,2)//这样when会执行去调用类方法add
  • 1
  • 2
  • 3
  • 4

2、补充:

  • when、then内容,可以从自定义的json文件中读取并解析成Mvel表达式形式。解析过程可参考场景6

3、eg2:Spel表达式同理

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!\");"
  • 1
  • 2
  • 3
  • 4
  • 5
  • condition:对象方法
condition: "person.isAdult() == false"
condition: "person.getResult(person.getQualifications())"
  • 1
  • 2

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!");"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 读取多条规则
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"));
  • 1
  • 2
  • 3
  • 4

常用规则类

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 List<Action> 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);
        }

    }
}
  • 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
SpELRule(Spring的表达式注入)

表达式注入

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 List<Action> 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;
    }
}
  • 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
  • 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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 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;
        }
    }
}
  • 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

组合规则

抽象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!\");");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 规则2
@Rule(name = "weather rule2", description = "if it rains then take an umbrella",priority = 777)
public class WeatherRule {

    @Condition
    public 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 执行
// 1.创建事实fact
        Facts 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.定义Rules
        Rules rules = new Rules();
        rules.register(myUnitRuleGroup);

        // 7.执行
        defaultRulesEngine.fire(rules, facts);
  • 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
  • 输出
rule2: action1-It rains, take an umbrella!
rule2: action2-边打伞,边听歌
rule1: It rains, move!
  • 1
  • 2
  • 3

先输出规则2(优先级为777),规则1(优先级为默认值:2147483646),值越小,优先级越高

规则引擎

引擎接口

public interface RulesEngine {
    RulesEngineParameters getParameters();

    List<RuleListener> getRuleListeners();

    List<RulesEngineListener> getRulesEngineListeners();

    void fire(Rules var1, Facts var2);

    Map<Rule, Boolean> check(Rules var1, Facts var2);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

引擎抽象类

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 Map<Rule, Boolean> check(Rules rules, Facts facts) {
        this.triggerListenersBeforeRules(rules, facts);
        Map<Rule, Boolean> result = this.doCheck(rules, facts);
        this.triggerListenersAfterRules(rules, facts);
        return result;
    }

    private Map<Rule, Boolean> doCheck(Rules rules, Facts facts) {
        LOGGER.debug("Checking rules");
        Map<Rule, 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;
    }
}
  • 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
规则引擎参数(决定规则之间是否互斥|中断|跳过等)

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

实战

场景1- 恒打印

规则description

默认打印Hello World

规则
public class MyRule extends BasicRule {

    @Override
    public String getName() {
        return "my rule";
    }

    @Override
    public String getDescription() {
        return "my rule description";
    }
    
    @Override
    public boolean evaluate(Facts facts) {
        return true;
    }

    @Override
    public void execute(Facts facts) throws Exception {
        System.out.println("我是一个打印Hello World的规则");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
规则引擎:使用DefaultRulesEngine
执行
public static void main(String[] args) {
        // 1.创建事实fact
        Facts facts = new Facts();

        // 2.创建条件condition(这里MyRule中默认为true,就不自定义Condition实现类了)

        // 3.创建动作action(这里MyRule中默认为:打印HelloWorld,就不自定义Action实现类了)

        // 4.创建规则
        MyRule myRule = new MyRule();

        // 5.定义Rules
        Rules rules = new Rules();
        rules.register(myRule);

        // 6.创建规则引擎
        DefaultRulesEngine defaultRulesEngine = new DefaultRulesEngine();

        // 7.执行
        defaultRulesEngine.fire(rules, facts);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

输出

DefaultRulesEngineListener - Rules evaluation started
DefaultRuleListener - Rule 'my rule' triggered

我是一个打印Hello World的规则

DefaultRuleListener - Rule 'my rule' performed successfully
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
fire方法执行流程
第一步:
        List<RulesEngineListener> rulesEngineListeners;
  		执行 void beforeEvaluate(Rule var1, Facts var2)
              
第二步:
        MyRuleboolean evaluate(Facts facts)
              
第三步:
        List<RuleListener> ruleListeners;
  		执行void afterEvaluate(Rule var1, Facts var2, boolean evaluateResult)
              
第四步:
        List<RuleListener> ruleListeners;
  		执行void beforeExecute(Rule var1, Facts var2);
  		
第五步:
       MyRulevoid execute(Facts facts)

第六步:
       List<RuleListener> ruleListeners;
	   执行void onSuccess(Rule var1, Facts var2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

场景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!\");"
  • 1
  • 2
  • 3
  • 4
  • 5

3、自定义规则引擎:使用默认的

4、执行

// 1.创建事实fact
        Facts 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.定义Rules
        Rules rules = new Rules();
        rules.register(weatherRule);

        // 7.执行
        defaultRulesEngine.fire(rules, facts);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

场景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);
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、使用Easy Rules规则引擎实现

将每个需求编写一条规则:

  • rule1
@Rule(name = "Mod5Rule", description = "mod5", priority = 1)
public class Mod5Rule {

    @Condition
    public boolean canMod5(@Fact("number") Integer number) {
        return number % 5 == 0;
    }

    @Action
    public void action(Facts facts) {
        Integer i = facts.get("number");
        System.out.print(i + " :是5的倍数");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • rule2
@Rule(name = "Mod7Rule", description = "mod7", priority = 2)
public class Mod7Rule {

    @Condition
    public boolean canMod7(@Fact("number") Integer number) {
        return number % 7 == 0;
    }

    @Action
    public void action(Facts facts) {
        Integer i = facts.get("number");
        System.out.print(i + " :是7的倍数");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • rule3
public class Mod5And7Rule extends UnitRuleGroup {

    public Mod5And7Rule(Object... rules) {
        for (Object rule : rules) {
            addRule(rule);
        }
    }

    @Override
    public int getPriority() {
        return 0;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • rule4
@Rule(name = "OtherRule", description = "不是5和7的倍数", priority = 3)
public class OtherRule {

    @Condition
    public boolean canNotMod5Or7(@Fact("number") Integer number) {
        return number % 5 != 0 || number % 7 != 0;
    }

    @Action
    public void action(@Fact("number") Integer number) {
        System.out.print(number);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 执行
// 创建规则引擎
        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);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 这里规则引擎参数:skipOnFirstAppliedRule(true):告诉引擎,被触发时跳过后面的规则。

    即当i = 5时,满足规则1(mod5),执行完action1,就不会再去匹配其他rule2、3、4规则了。使用场景是:各个规则之间互斥

场景4-动态规则MVEL表达式+Json字符串

如图1

在这里插入图片描述

输入facts:NORMAL_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;
    }
}
  • 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
  • 逻辑运算符(&&、||、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 = "||";
}
  • 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
  • 工具类(负责解析自定义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();
    }

     @Data
     public static class RuleModel {
        private String ruleId;
        String ruleName;
        String description;
        String whenExpression;
        String thenExpression;
    }

    @Data
    public static class RuleResult {
        // 规则主键
        private String ruleId;

        // 是否匹配, 默认false
        boolean isMatch = false;

        // 匹配信息,默认为匹配失败
        String message = "匹配失败";

        /**
         * 匹配成功后设置成功信息
         */
        public void setValue(String message){
            this.message = message;
            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
  • 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
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 测试
		// 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. 设置fact
        String 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);
  • 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

结果分析:显然结果匹配成功。原因如图2

在这里插入图片描述

facts:NORMAL_NUMBER = 10、ERROR_NUMBER = 10

condition:如图1

显然NORMAL_NUMBER = 10,满足第一个条件 < 11,直接返回true。

如果我们设置fact:NORMAL_NUMBER = 12,则NORMAL_NUMBER 不满足第一个条件。

但是fact中ERROR_NUMBER = 10 <= 11满足第二个条件,直接返回True

场景5-QLExpress

1、使用阿里的QLExpress

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>QLExpress</artifactId>
            <version>3.3.2</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
ExpressRunner runner = new ExpressRunner();

        DefaultContext<String, 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;
        DefaultContext<String, Object> computeContext = new DefaultContext<>();
        for (Map.Entry<String, MetaRuleResult> entry : context.entrySet()) {
            computeContext.put(entry.getKey(), entry.getValue().getResult());
        }
        String ruleExpress = "o&&l&&s&&w&&lo&&llo";
        result = runner.execute(ruleExpress, computeContext, null, true, false);
        Boolean bResult = (Boolean) result;
        System.out.println(bResult);//false
  • 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
  • MetaRuleResult
@Data
@Builder
public class MetaRuleResult {
    private Long skuId;
    private Boolean result;
    private String metaRule;
    private String failureReason;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2、使用EasyRules实现上述功能

  • 规则1
@Rule(name = "oRule", description = "修改值>=OR可修改")
public class ORule {

    @Condition
    public boolean when(@Fact("oRule") MetaRuleResult oRule) {
        return oRule.getResult();
    }

    @Action
    public void then(Facts facts) {
        System.out.println("修改值>=OR可修改");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里的规则是原子规则

  • 规则2
@Rule(name = "lRule", description = "锁库不可更改")
public class LRule {
    @Condition
    public boolean when(@Fact("lRule") MetaRuleResult lRule) {
        return lRule.getResult();
    }

    @Action
    public void then(Facts facts) {
        System.out.println("没锁库可更改");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 自定义组合规则
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);
        }
    }
}
  • 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

作用:这里的规则是组合规则,是原子规则的组合形式,可扩展

这里的自定义规则组合,是快速失败机制:即l&&o中如果lRule的condiotion为false,则直接失败,使用facts记录一个失败原因。也可以自定义将每个rule-condition为false的原因都记录下来

  • 自定义condition-after-listeren
public class MyRuleListener implements RuleListener {
    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        return true;
    }

    @Override
    public void afterEvaluate(Rule rule, Facts facts, boolean b) {
        String ruleName = rule.getName();
        if (b) {
            // 只有l&&o为true,才会走到这个逻辑,否则走下面逻辑
            facts.put(ruleName, MetaRuleResult.builder().skuId(1L).result(true).metaRule(ruleName).failureReason("").build());
        } else {
            // l&&o有一个不满足,则总体失败,将各个失败的原因都记录下来
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> fact : facts) {
                String key = fact.getKey();
                if (key.contains("失败")) {
                    MetaRuleResult result = (MetaRuleResult)fact.getValue();
                    // 这里result中有中间参数比如lockStatus,则原因就可以写:lockStatus=true,已锁库,不允许修改
                    sb.append(result.getFailureReason()+ "且");
                }
            }
            facts.put(ruleName, MetaRuleResult.builder().skuId(1L).result(false).metaRule(ruleName).failureReason(sb.toString()).build());
        }
    }

    @Override
    public void beforeExecute(Rule rule, Facts facts) {

    }

    @Override
    public void onSuccess(Rule rule, Facts facts) {

    }

    @Override
    public void onFailure(Rule rule, Facts facts, Exception e) {

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

作用:组合规则,执行结果。成功|失败,已经失败原因

  • 执行
		// 1.fact
        Facts 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.rule
        ORule oRule = new ORule();
        LRule lRule = new LRule();
        String oAndLRuleName = "o&&l";
        MyUnitRuleGroup oAndL = new MyUnitRuleGroup(oAndLRuleName, ">=OR且未锁过库规则校验");
        oAndL.addRule(oRule);
        oAndL.addRule(lRule);

        // 3.rules
        Rules 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());
        }
  • 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
  • 扩展

1)db获取规则表达式:

先根据网店+品类+角色+修改类型,查询db获取组合规则,比如l&&o

2)工厂模式解析组合规则

然后根据l&&o,解析出规则为l和o,组合成l&&o

3)facts获取数据

自定义策略模式,key为枚举类型,value为对应的rpc查询接口

facts.put("o",          MetaRuleResult.builder().skuId(1L).result(queryData("o")).metaRule("o").failureReason("").build());
  • 1

这里的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(), "我们指定的规则名称")) {
        
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3、场景

  • 输入一个人的信息,信息中包含了这个人的学历等级,作为规则事实

    • 方式一:condition中制定(推荐,可以动态配置0和11)
    • @Condition修饰方法入参( @Fact(“person”) Person person )
    [
    	{
          "name": "newEducationAdd",
          "description": "修改学历添加列表",
          "condition": "person.getQualifications() >= 0 && person.getQualifications()<=11",
          "priority": 3,
          "actions": [
            "System.out.println(\"新规则执行了\")"
          ]
    	}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 方式二:将condition条件判断,自定在fact-key中对象的属性和方法中(不推荐)
[{
  "name": "newEducationAdd",
  "description": "修改学历添加列表",
  "condition": "person.getResult(person.getQualifications())",
  "priority": 3,
  "actions": [
    "System.out.println(\"新规则执行了\")"
  ]
}]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
@Data
public class Person {
    // 姓名
    private String name;

    // 年龄
    private int age;

    // 描述
    private String dec;

    // 学历等级
    private int qualifications;

    private List<Education> 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;
    }
}
  • 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
  • 如果学历等级符合规则,则去查询学历证书情况(集合存储)

  • 查出完学历证书后,在检测学历证书与他的学历等级是否匹配,匹配规则为:

    • 学历证书数量与学历等级相同
    • 最大学历证书的学历等级与学历等级一致
  • 匹配通过则学历真实,信息中会添加真实学历匹配结果

  • 未匹配通过则学历造假嫌疑,信息中会添加造假学历信息

上线

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(), "我们指定的规则名称")) {
        
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3、场景

  • 输入一个人的信息,信息中包含了这个人的学历等级,作为规则事实

    • 方式一:condition中制定(推荐,可以动态配置0和11)
    • @Condition修饰方法入参( @Fact(“person”) Person person )
    [
    	{
          "name": "newEducationAdd",
          "description": "修改学历添加列表",
          "condition": "person.getQualifications() >= 0 && person.getQualifications()<=11",
          "priority": 3,
          "actions": [
            "System.out.println(\"新规则执行了\")"
          ]
    	}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 方式二:将condition条件判断,自定在fact-key中对象的属性和方法中(不推荐)
[{
  "name": "newEducationAdd",
  "description": "修改学历添加列表",
  "condition": "person.getResult(person.getQualifications())",
  "priority": 3,
  "actions": [
    "System.out.println(\"新规则执行了\")"
  ]
}]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
@Data
public class Person {
    // 姓名
    private String name;

    // 年龄
    private int age;

    // 描述
    private String dec;

    // 学历等级
    private int qualifications;

    private List<Education> 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;
    }
}
  • 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
  • 如果学历等级符合规则,则去查询学历证书情况(集合存储)

  • 查出完学历证书后,在检测学历证书与他的学历等级是否匹配,匹配规则为:

    • 学历证书数量与学历等级相同
    • 最大学历证书的学历等级与学历等级一致
  • 匹配通过则学历真实,信息中会添加真实学历匹配结果

  • 未匹配通过则学历造假嫌疑,信息中会添加造假学历信息

场景7-履约缺货处罚金额计算

1、背景

在这里插入图片描述

如果商家发生了履约缺货,则需要对商家进行罚款,罚款金额计算的逻辑如下:

这里以skuId=1,skuName=“苹果”, 供应商id=777为例

  • 假设供应商777,履约缺货了3件,即less = 3 pcs
  • 此sku的销售Gmv即saleGmv = 100元
  • 此sku的销售件数即saleCount = 5 pcs

则需要对777这个商家,关于skuId=1的品进行罚款,罚款金额Java代码实现如下

BigDecimal saleGmv = BigDecimal.valueOf(100);
        BigDecimal saleCount = BigDecimal.valueOf(5);
        BigDecimal less = BigDecimal.valueOf(3);
        BigDecimal price = saleGmv.divide(saleCount, 2, RoundingMode.HALF_UP);

        Double fineMoney;
        if (price.compareTo(BigDecimal.valueOf(10)) >= 0) {
            BigDecimal money = saleGmv.multiply(BigDecimal.valueOf(0.1)).add(less.multiply(BigDecimal.valueOf(10)));
            double val = money.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            fineMoney = Math.max(val, 200.0);
        } else {
            BigDecimal money = saleGmv.multiply(BigDecimal.valueOf(0.1)).add(less.multiply(BigDecimal.valueOf(4)));
            double val = money.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            fineMoney = Math.max(val, 200.0);
        }

        System.out.println(fineMoney);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 缺点1

这里商家skuId=1缺货了3件,按照这个逻辑Java代码实现没有问题,加入skuid=1缺货了300件,则不能按照这个罚款的计算逻辑,计算逻辑就要重新Java代码实现,或者大量的if-else。这里仅有6中分支,加入分支很多很多,则代码变得异常臃肿。

  • 缺点2

假如代码实现后,那天业务变更了计算公式的一个值,或者干脆改变了计算逻辑max->min,则只能通过代码上线。

  • 使用when-then形式解决大量分支问题,而且支持动态加载,无需上线代码

实现如下

2、实现

public void calculateFineMoneyBySku() {
        Facts facts = new Facts();

        // 1.单个sku信息
        // 这里直接使用facts.put值,实际业务中,具体的值是从外部获取到的
        facts.put("saleGmv", BigDecimal.valueOf(100));
        facts.put("saleCount", BigDecimal.valueOf(5));
        facts.put("less",BigDecimal.valueOf(30));

        BigDecimal a = facts.get("saleGmv");
        BigDecimal b= facts.get("saleCount");
        facts.put("price", a.divide(b, 2, RoundingMode.HALF_UP));

        // 2.从配置中获取规则(这里举例子获取到2个规则)
        String ruleName1 = "rule1";
        String whenExpress1 = "less > 0 && less <= 10";
        String thenExp1 = "price < 10 ? Math.max(0.1 * saleGmv + less * 4, 200) : Math.max(0.1 * saleGmv + less * 10, 200)";
        facts.put(ruleName1 + "Exp", thenExp1);//给Listener使用的
        Rule lessRule1 = new MVELRule()
                .name(ruleName1)
                .description("履约缺货规则1")
                .when(whenExpress1)
                .then(thenExp1);

        String ruleName2 = "rule2";
        String whenExpress2 = "less > 10 && less <= 50";
        String thenExp2 = "price < 10 ? Math.max(0.1 * saleGmv + less * 4, 500) : Math.max(0.1 * saleGmv + less * 10, 500)";
        facts.put(ruleName2 + "Exp", thenExp2);
        Rule lessRule2 = new MVELRule()
                .name(ruleName2)
                .description("履约缺货规则2")
                .when(whenExpress2)
                .then(thenExp2);


        Rules rules = new Rules();
        rules.register(lessRule1);
        rules.register(lessRule2);
        DefaultRulesEngine engine = new DefaultRulesEngine();
        MyRuleListener listener = new MyRuleListener();
        engine.registerRuleListener(listener);
        engine.fire(rules, facts);

        Map<String, Object> map = facts.asMap();
        Set<String> keys = map.keySet();
        for (String key : keys) {
            if (key.contains("Result")) {
                Object res = facts.get(key);
                System.out.println(res);
                break;
            }
        }
    }
  • 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

3.监听器

通过监听器,如果then执行成功了,则一定会执行监听器的onSuccess方法。故在此再执行一遍then表达式,获取then的执行结果存入map中(这里之所以要通过监听器来存then的结果,原因是原生的MvelRule规则的execute方法返回值类型是void,它只关系then中的表达式是否执行成功,不关系表达式执行的结果)

package com.mjp.easyrules.qlexpress;

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.RuleListener;
import org.mvel2.MVEL;

/**
 * Author:majinpeng
 * Date: 2023/12/31 9:40
 */
public class MyRuleListener implements RuleListener {
    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        return true;
    }

    @Override
    public void afterEvaluate(Rule rule, Facts facts, boolean b) {
    }

    @Override
    public void beforeExecute(Rule rule, Facts facts) {

    }

    @Override
    public void onSuccess(Rule rule, Facts facts) {
        String ruleName = rule.getName();
        String key = ruleName + "Exp";
        String actionExp = facts.get(key);
        Object result = MVEL.eval(actionExp, facts.asMap());
        facts.put(ruleName+"Result", result);
    }

    @Override
    public void onFailure(Rule rule, Facts facts, Exception e) {

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

补充:

1.调用类的带参数方法

        MyTest obj = new MyTest();
        Map<String, Object> map = new HashMap<>();
        map.put("key1", 1);
        map.put("key2", 2);

        String expression = "func(map)";
        Map<String, Object> vars = new HashMap<>();
        vars.put("map", map);

        MVEL.executeExpression(MVEL.compileExpression(expression), obj, vars);
        System.out.println(map);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.调用类不带参方法

@Test
    public void t() throws Exception{
        Facts facts = new Facts();
        facts.put("myTest",new MyTest());

        Rule lessRule = new MVELRule()
                .name(ruleName)
                .description("履约缺货规则")
                .when(whenExpress)
                .then("myTest.func()");


        Rules rules = new Rules();
        rules.register(lessRule);
        DefaultRulesEngine engine = new DefaultRulesEngine();
        engine.fire(rules, facts);
        System.out.println(facts);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

场景8:Mvel表达式when-then调用类方法

1、背景

当满足when(指定类的方法),则执行then(执行类的方法)

2、举例

  • 定义类的when-then方法
public class Demo {
    public static boolean whenFunc(Long id) {
        return id > 0;
    }
    
    public static void thenFunc1(Long id, Map<String, Object> map) {
        System.out.println(id);
        System.out.println(map);
    }

    public static String thenFunc2(Long id, String name) {
        return id + name;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 执行
		Facts facts = new Facts();
        facts.put("id",1);
        facts.put("name","mjp");
        facts.put("Demo", Demo.class);

        Facts ff = new Facts();
        ff.put("age", 18);
        facts.put("ff", ff);

        MVELRule rule = new MVELRule().name("rule").description("test")
                .when("Demo.whenFunc(id)")
                .then("Demo.thenFunc1(id,ff.asMap())")
                .then("Demo.thenFunc2(id,name)");

        Rules rules = new Rules();
        rules.register(rule);

        DefaultRulesEngine engine = new DefaultRulesEngine();
        engine.fire(rules,facts);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

场景9:终极模式之规则引擎+职责链

我mjp愿称之为EasyRules最强实战场景2024.01.07

1、背景

综合上述场景7和8。

我们知道规则的处罚动作actions可能不止一个。除了对商家进行罚款,还可以对商家停排期,甚至可以降低商家商品的曝光度等等一系列处罚动作。

基于此,我们可以将处罚actions配置为json字符串

{
    "fineMoney":"price < 10 ? Math.max(0.1 * saleGmv + less * 4, 200) : Math.max(0.1 * saleGmv + less * 10, 200)",
    
    "stopSchedule":3,
    "降低曝光度":true
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2、特殊化

规则1

  • when是 0<less<=10
  • actions是
{
    "fineMoney":"price < 10 ? Math.max(0.1 * saleGmv + less * 4, 200) : Math.max(0.1 * saleGmv + less * 10, 200)",
    
    "stopSchedule":3,
    "降低曝光度":true
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

规则2

  • when是 0<less<=10
  • actions是
{
    "fineMoney":"price < 10 ? Math.max(0.1 * saleGmv + less * 4, 200) : Math.max(0.1 * saleGmv + less * 10, 200)"
}
  • 1
  • 2
  • 3

即规则2的处罚动作只有罚款

那么问题来了,如何为每个规则,定制化处罚actions动作

答案:通过职责链模式

3、思想

  • 我们代码中使用职责链模式,定义一系列的Handler处理器。

然后每一条规则的actions =》映射成List handlerList,然后遍历执行处理器。

  • 因为不同的规则,处罚动作actions可能不一样,所以,其处理器handlerList也不尽相同,需要自己定制化

4、代码实现

  • 业务代码执行
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApplicationLoader.class)
@Slf4j
public class SpringTest {
    @Resource
    private Demo demo;

    @Test
    public void t() {

        Facts facts = new Facts();
        facts.put("id",1);
        facts.put("name","mjp");
        facts.put("Demo", demo);

        Facts ff = new Facts();
        ff.put("age", 18);
        facts.put("ff", ff);

        MVELRule rule = new MVELRule().name("rule").description("test")
                .when("Demo.whenFunc(id)")
                .then("Demo.thenFunc1(id,ff.asMap())");

        Rules rules = new Rules();
        rules.register(rule);

        DefaultRulesEngine engine = new DefaultRulesEngine();
        engine.fire(rules,facts);
    }

}
  • 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
  • 方法类Demo
package com.mjp.easyrules.less;

import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Author:mjp
 * Date: 2024/01/07 9:50
 */
@Service
public class Demo {

    @Resource
    private List<Handler> handlerList;

    public boolean whenFunc(Long id) {
        return id > 0;
    }

    public void thenFunc1(Long id, Map<String, Object> map) {
        System.out.println(id);
        // 方式一:所有处理器都参与
        for (Handler handler : handlerList) {
            handler.exe();
        }

        // 方式二:不同的规则有不同的处罚actions,对应不同的处理器集合,这里可以定制化处理器集合
        // 根据入参内容(从入参map中获取信息,可知得知actions-json字符串内容),这样我们就可以据此,构造定制化的处罚器集合
        handlerList = handlerList.stream().filter(Handler::needExe).collect(Collectors.toList());
        for (Handler handler : handlerList) {
            handler.exe();
        }
    }
}
  • 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
  • 处理器接口
public interface Handler {
    /**
     * 惩罚体
     */
    void exe();

    /**
     * 是否需要执行此处理器
     * @return
     */
    default boolean needExe() {
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 处理器实现类

    罚款处理器

@Order(value = 1)
@Service
public class FineMoneyHandler implements Handler{
    @Override
    public void exe() {
        System.out.println("fineMoney");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

​ 停排处理器

@Order(value = 2)
@Service
public class StopScheduleHandler implements Handler{
    @Override
    public void exe() {
        System.out.println("停排期");
    }

    /**
     * 不执行停排期惩罚
     * @return
     */
    @Override
    public boolean needExe() {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 根据入参定制化处理器集合

    处理器接口默认都不参与,根据json的key添加谁参与

    public interface Handler {
        /**
         * 惩罚体
         */
        void exe();
    
        /**
         * 是否需要执行此处理器
         * @return
         */
        default boolean needExe() {
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
@Service
public class Demo {

    @Resource
    private List<Handler> handlerList;

    @Resource
    private ApplicationContext applicationContext;

    public boolean whenFunc(Long id) {
        return id > 0;
    }

    public void thenFunc1(Long id, Map<String, Object> map) {
        System.out.println(id);
        // 方式一:所有处理器都参与
        for (Handler handler : handlerList) {
            handler.exe();
        }
    }

    public void thenFunc2(Long id, Map<String, Object> map) throws ClassNotFoundException {
        // 根据actions的json字符串的key,key存在,则说明对应的处罚动作存在,则映射成对应的处理器

        // 1.遍历所有的json k-v
        // 2.拿到key
        String name = "FineMoney";
        String className = "com.mjp.easyrules.less." + name + "Handler";
        Class<?> aClass = Class.forName(className);
        // 3.生成对应的处理器,并添加到集合中
        Handler bean = (Handler)applicationContext.getBean(aClass);
        handlerList.add(bean);

        // 4.执行所有处罚动作
        for (Handler handler : handlerList) {
            handler.exe();
        }
    }
}
  • 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

5、罚款处理器扩展

罚款处理器中,也可以使用Mvel表达式,直接计算罚款多少钱

@Order(value = 1)
@Service
public class FineMoneyHandler implements Handler{
    @Override
    public void exe(Map<String, Object> facts) {
        System.out.println("fineMoney");
        // 使用Mvel表达式直接计算罚款金额

        // 这里fineMoneyExp也是方法传递过来的,然后每个参数都可以在facts拿到
        String fineMoneyExp = "price < 10 ? Math.max(0.1 * saleGmv + less * 4, 500) : Math.max(0.1 * saleGmv + less * 10, 500)";
        Object fineMoney = MVEL.eval(fineMoneyExp, facts);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
补充:MVEL直接调用类方法

1.调用类的带参数方法

        MyTest obj = new MyTest();
        Map<String, Object> map = new HashMap<>();
        map.put("key1", 1);
        map.put("key2", 2);

        String expression = "func(map)";
        Map<String, Object> vars = new HashMap<>();
        vars.put("map", map);

        MVEL.executeExpression(MVEL.compileExpression(expression), obj, vars);
        System.out.println(map);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.调用类不带参方法

@Test
    public void t() throws Exception{
        Facts facts = new Facts();
        facts.put("myTest",new MyTest());

        Rule lessRule = new MVELRule()
                .name(ruleName)
                .description("履约缺货规则")
                .when(whenExpress)
                .then("myTest.func()");


        Rules rules = new Rules();
        rules.register(lessRule);
        DefaultRulesEngine engine = new DefaultRulesEngine();
        engine.fire(rules, facts);
        System.out.println(facts);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

撒花2024.01.07 10:45-mjp

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/557292
推荐阅读
相关标签
  

闽ICP备14008679号