easy-rules规则引擎最佳落地实践

简介: 写作目的这是一个头部互联网公司中的一个问题。因为有很多业务产品线,作为一个新人或者团队外的人员是很难区分不同的产品线之间的区别的,因此需要给某个产品线一个描述。但是随着业务的发展,产品线下可能又根据某个字段进一步划分,那么子产品线就是父产品线 + 字段 去区分。后面根据两个字段划分…。人都麻了。因为不同的组合有不同的链路。因此针对一个产品,我们要提供针对这个产品的具体规则描述,从而减少答疑。

接下来以 餐厅、套餐、菜品进行举例。


1.png


比如你想加盟XX火锅店,你需要像区域经理申请开店。

经理说 你开一个 牛肉火锅(prouductId = 1) 自营店(type =1)。

经理让李四 开一个 羊肉火锅(prouductId = 2) 自营店(type =1)。

经理让王五 开一个 羊肉火锅(prouductId = 2) 旗舰店(type =2)。

。。。。。


那么针对不同的场景(product && type),需要走的审批流程不一样。


●牛肉火锅(prouductId = 1) 自营店(type =1)

开店规则:需要审批菜品,审批通过后套餐自动审批通过,套餐都审批通过后 餐厅自动审批通过,审批通过后即可运营。


2.png


●羊肉火锅(prouductId = 2) 自营店(type =1)

开店规则:只审批餐厅,审批通过后即可运营。


3.png


●羊肉火锅(prouductId = 2) 旗舰店(type =2)

开店规则:

只审批餐厅,审批通过后即可运营。

但是菜品也可以申请,审批通过后套餐自动审批通过,审批通过的套餐可以每天赠送100份。


4.png


那么问题来了,如果你作为审批流程客服工作人员,当一个开店的审批工单来了以后,总有人问你为什么他的工单还在审批中,你怎么办呢?最好的方式就是你告诉他你的工单是菜品、套餐、餐厅没审批通过,请找相关同学咨询。


源码下载Gitee (亲测可用,真实有效)


启动方法和工程目录如下


5.png


java规则引擎easy-rules简单使用


以上面的牛肉火锅(prouductId = 1) 自营店(type =1) 为例


正常情况下可以写代码判断


      int productId = 1;
      int type = 1;
      if(productId == 1 && type ==1){
        System.out.println("牛肉火锅自营店。请从【餐品】开始进行向上申请");
      }


如果这样的规则能通过配置的方式进行实现,那简直无敌。


下面先写一个demo版本


    Canteen canteen = new Canteen().setProductId(1).setType(1);
    // define rules 定义规则
    Rule canteenRule =
        new RuleBuilder()
            .name("牛肉火锅自营店") // 规则名称
            .description("productId = 1 && type =1 。文案:牛肉火锅自营店。请从【餐品】开始进行向上申请") // 规则描述
            .when(facts -> facts.get("productId").equals(1) && facts.get("type").equals(1)) // 规则条件
            .then(facts -> System.out.println("牛肉火锅自营店。请从【餐品】开始进行向上申请")) // 命中规则后的操作
            .build();
    // 定义规则集合
    Rules rules = new Rules();
    rules.register(canteenRule);
    // fire rules on known facts  创建执行引擎
    RulesEngine rulesEngine = new DefaultRulesEngine();
    // define facts 定义需要验证的参数
    Facts facts = new Facts();
    facts.put("productId", canteen.getProductId());
    facts.put("type", canteen.getType());
    // 进行规则校验
    rulesEngine.fire(rules, facts);


看打印结果


6.png


上面还存在以下问题


  • ●规则还是手动通过代码定义的,如果通过配置文件定义那就最好了
  • ●命中的规则后结果只能打印,我想获取规则的一些信息比如规则描述description应该怎么办


最佳落地实践


注意:部分代码没有展示,可以去仓库查看全部源码


通过配置文件定义规则 canteenRule.yml


---
name: "牛肉火锅自营店"
description: "prouductId = 1 && type = 1 "
condition: "canteen.productId==1&&canteen.type==1"
priority: 1
actions:
  - "System.out.println(1);"
---
name: "牛肉火锅旗舰店"
description: "prouductId = 1 && type = 2"
condition: "canteen.productId == 2 && canteen.type == 1"
priority: 2
actions:
  - "System.out.println(2);"


创建规则引擎工厂类RulesEngineFactory


目的:上述例子中,规则引擎不可能只为 餐厅 服务,还需要为 套餐、菜品服务。因此肯定是有不同的规则和规则引擎的。因此这里需要一个工厂。


package com.example.demo.rulesEngine.listener;
import com.example.demo.rulesEngine.common.RuleCommonInterface;
import lombok.Data;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.mvel.MVELRuleFactory;
import org.jeasy.rules.support.YamlRuleDefinitionReader;
import java.io.FileReader;
/**
 * @author chaird
 * @create 2022-11-26 13:02
 */
public class RulesEngineFactory {
  /**
   * 构建食堂规则。特殊
   *
   * @return
   */
  public static BizRuleEngine buildRuleEngine4Canteen() {
    String entityType = "canteen";
    String reulePath =
        "D:\\work\\IntelliJ IDEA 2018.2.4Workspace\\Demooo\\springboot-easu-rules-demo\\src\\main\\resources\\canteenRule.yml";
    return buildRuleEngine(entityType, reulePath);
  }
  // 可以有N个
  public static BizRuleEngine buildRuleEngine4MealGroup() {
    String entityType = "mealGroup";
    String reulePath = "xxxxx";
    // return buildRuleEngine(entityType, reulePath);
    return null;
  }
  private static BizRuleEngine buildRuleEngine(String entityType, String rulePath) {
    BizRuleEngine bizRuleEngine = new BizRuleEngine(entityType, rulePath);
    return bizRuleEngine;
  }
  @Data
  public static class BizRuleEngine {
    private String entityType;
    private MVELRuleFactory ruleFactory;
    private DefaultRulesEngine rulesEngine;
    private Rules rules;
    public BizRuleEngine(String entityType, String rulePath) {
      try {
        this.entityType = entityType;
        ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
        rules = ruleFactory.createRules(new FileReader(rulePath));
        rulesEngine = new DefaultRulesEngine();
        rulesEngine.registerRuleListener(new YmlRulesListener(entityType));
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    public void fire(RuleCommonInterface input) {
      Facts facts = new Facts();
      facts.put(entityType, input);
      rulesEngine.fire(rules, facts);
    }
  }
}


这样我就可以针对餐厅这样一个特殊的实例创建自己独有的规则引擎


RulesEngineFactory.BizRuleEngine canteenRuleEngine = RulesEngineFactory.buildRuleEngine4Canteen();
Canteen canteen = new Canteen().setName("西餐厅").setProductId(1).setType(1);
//todo


创建监听器YmlRulesListener


目的:其实有有的时候命中规则后我们要做一些事情,比如取到规则的一些描述等信息好组织文案


package com.example.demo.rulesEngine.listener;
import com.example.demo.rulesEngine.common.RuleCommonInterface;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.RuleListener;
/**
 * @author chaird
 * @create 2022-11-26 1:54
 */
public class YmlRulesListener implements RuleListener {
  private String entityType ;
  @Override
  public boolean beforeEvaluate(Rule rule, Facts facts) {
    return true;
  }
  @Override
  public void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {
  }
  @Override
  public void beforeExecute(Rule rule, Facts facts) {
  }
  @Override
  public void onSuccess(Rule rule, Facts facts) {
    //获取需要验证的对象,比如 【餐厅、套餐、菜品 implement RuleCommonInterface】
    RuleCommonInterface ruleCommon = facts.get(entityType);
    //把规则信息进行一个赋值
    ruleCommon.setDescription(rule.getDescription());
  }
  @Override
  public void onFailure(Rule rule, Facts facts, Exception exception) {
  }
  public YmlRulesListener(){
  }
  public YmlRulesListener(String entityType) {
    this.entityType = entityType;
  }
}


可以直接通过规则action进行赋值


有的时候会有转换操作,针对本文提出的案例。我想让productId =2的时候和productId = 9527的后续流程一样,可以在actions中使用下面的命令


name: "牛肉火锅旗舰店"
description: "prouductId = 1 && type = 2"
condition: "canteen.productId == 2 && canteen.type == 1"
priority: 2
actions:
  - "canteen.productId = 9527;"


总结


●这样的一个工具案例其实写文章还挺难组织思路的,代码贴的多显示不出核心思路。代码贴的少大家又看不太懂。

●百度了一些文章,其实有些都没有跑通,所以自己写一篇文章。

●其实单场景下对一个实体类进行规则校验那很简单,本文通过工厂模式设计的是对多实体类进行规则校验。总体还是有难度的。

目录
相关文章
|
6月前
|
JavaScript 小程序 Java
ES6使用实践总结
ES6使用实践总结
43 0
|
6月前
|
数据采集 大数据 数据安全/隐私保护
掌握网络抓取技术:利用RobotRules库的Perl下载器一览小红书的世界
本文探讨了使用Perl和RobotRules库在遵循robots.txt规则下抓取小红书数据的方法。通过分析小红书的robots.txt文件,配合亿牛云爬虫代理隐藏真实IP,以及实现多线程抓取,提高了数据采集效率。示例代码展示了如何创建一个尊重网站规则的数据下载器,并强调了代理IP稳定性和抓取频率控制的重要性。
掌握网络抓取技术:利用RobotRules库的Perl下载器一览小红书的世界
|
3月前
|
前端开发 数据处理 数据库
Angular与Firebase的完美联合:掌握实时数据同步技术——从环境配置到数据服务的详细实现指南
【8月更文挑战第31天】在现代Web应用中,实时数据同步对于提升用户体验至关重要。本文档详细介绍如何在Angular应用中集成Firebase实时数据库,包括准备工作、配置环境、实现实时数据同步及在组件中使用数据服务等步骤。通过本教程,开发者将掌握利用Angular与Firebase高效实现数据同步的方法,增强应用的实时互动性。
41 0
|
3月前
|
JavaScript 前端开发 测试技术
Angular 与 Node.js 无缝对接简直太牛啦!前后端分离最佳实践,开启高效开发新时代!
【8月更文挑战第31天】随着互联网技术的发展,前后端分离模式日益受到开发者青睐。本文综述了 Angular 与 Node.js 的优势及无缝对接技术,探讨了前后端分离的最佳实践。Angular 以其组件化开发、双向数据绑定等特性成为优秀的前端框架;Node.js 则依靠高并发处理能力和全栈 JavaScript 开发的优势在后端大放异彩。两者结合通过 HTTP 请求实现高效通信,并可通过数据库实现数据共享。此外,文章还强调了接口设计、代码分离、测试及部署等方面的最佳实践,为开发者提供了全面的指导。
79 0
|
5月前
|
Python
Mandelbrot集的最新变化形态一览——MandelBox,Mandelbulb,Burning Ship,NebulaBrot
该文介绍了几种基于Mandelbrot集的衍生形态,包括Mandelbulb、MandelBox、Burning Ship和NebulaBrot。Mandelbulb是3D扩展,使用球坐标;MandelBox利用盒映射创造复杂形状;Burning Ship以复数模和实部迭代;NebulaBrot则结合多种分形特征。文中提供了简单的Python代码示例来生成这些图形,并提到了相关学习资源。
|
6月前
|
Web App开发 JavaScript Cloud Native
构建高效可扩展的RESTful API:Node.js与Express框架实践指南构建未来:云原生架构在企业数字化转型中的关键作用
【5月更文挑战第29天】 在数字化时代的驱动下,后端服务架构的稳定性与效率成为企业竞争力的关键。本文深入探讨了如何利用Node.js结合Express框架构建一个高效且可扩展的RESTful API。我们将从设计理念、核心模块、中间件应用以及性能优化等方面进行系统性阐述。通过实例引导读者理解RESTful接口设计的最佳实践,并展示如何应对大规模并发请求的挑战,确保系统的高可用性与安全性。
|
6月前
|
前端开发
平台设计-moduleID的使用
整个平台上有很多相同的功能
|
存储 NoSQL 算法
使用秘籍|如何实现图数据库 NebulaGraph 的高效建模、快速导入、性能优化
NebulaGraph 技术社区用户的实践优化心得集大成者,从数据建模开始,解决数据膨胀问题,再到软硬皆施搞定数据导入的速率,到最后的查询语句优化。一站式搞定数据库的性能问题。
674 0
使用秘籍|如何实现图数据库 NebulaGraph 的高效建模、快速导入、性能优化
|
JavaScript 前端开发 Oracle
ES6:基础使用,积累
ES6:基础使用,积累
49 0
|
JSON 分布式数据库 数据格式
ES 10000条限制解决方案
ES 10000条限制解决方案
879 0