easy-rules规则引擎最佳落地实践
寫作目的
這是一個頭部互聯網公司中的一個問題。因為有很多業務產品線,作為一個新人或者團隊外的人員是很難區分不同的產品線之間的區別的,因此需要給某個產品線一個描述。但是隨著業務的發展,產品線下可能又根據某個字段進一步劃分,那么子產品線就是父產品線 + 字段 去區分。后面根據兩個字段劃分…。人都麻了。因為不同的組合有不同的鏈路。因此針對一個產品,我們要提供針對這個產品的具體規則描述,從而減少答疑。
接下來以 餐廳、套餐、菜品進行舉例。
比如你想加盟XX火鍋店,你需要像區域經理申請開店。
經理說 你開一個 牛肉火鍋(prouductId = 1) 自營店(type =1)。
經理讓李四 開一個 羊肉火鍋(prouductId = 2) 自營店(type =1)。
經理讓王五 開一個 羊肉火鍋(prouductId = 2) 旗艦店(type =2)。
。。。。。
那么針對不同的場景(product && type),需要走的審批流程不一樣。
-
牛肉火鍋(prouductId = 1) 自營店(type =1)
開店規則:需要審批菜品,審批通過后套餐自動審批通過,套餐都審批通過后 餐廳自動審批通過,審批通過后即可運營。
-
羊肉火鍋(prouductId = 2) 自營店(type =1)
開店規則:只審批餐廳,審批通過后即可運營。
-
羊肉火鍋(prouductId = 2) 旗艦店(type =2)
開店規則:
只審批餐廳,審批通過后即可運營。
但是菜品也可以申請,審批通過后套餐自動審批通過,審批通過的套餐可以每天贈送100份。
那么問題來了,如果你作為審批流程客服工作人員,當一個開店的審批工單來了以后,總有人問你為什么他的工單還在審批中,你怎么辦呢?最好的方式就是你告訴他你的工單是菜品、套餐、餐廳沒審批通過,請找相關同學咨詢。
源碼下載Gitee (親測可用,真實有效)
gitee代碼下載地址
啟動方法和工程目錄如下
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);看打印結果
上面還存在以下問題
- 規則還是手動通過代碼定義的,如果通過配置文件定義那就最好了
- 命中的規則后結果只能打印,我想獲取規則的一些信息比如規則描述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;}@Datapublic 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 ;@Overridepublic boolean beforeEvaluate(Rule rule, Facts facts) {return true;}@Overridepublic void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {}@Overridepublic void beforeExecute(Rule rule, Facts facts) {}@Overridepublic void onSuccess(Rule rule, Facts facts) {//獲取需要驗證的對象,比如 【餐廳、套餐、菜品 implement RuleCommonInterface】RuleCommonInterface ruleCommon = facts.get(entityType);//把規則信息進行一個賦值ruleCommon.setDescription(rule.getDescription());}@Overridepublic 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;"總結
- 這樣的一個工具案例其實寫文章還挺難組織思路的,代碼貼的多顯示不出核心思路。代碼貼的少大家又看不太懂。
- 百度了一些文章,其實有些都沒有跑通,所以自己寫一篇文章。
- 其實單場景下對一個實體類進行規則校驗那很簡單,本文通過工廠模式設計的是對多實體類進行規則校驗。總體還是有難度的。
參考
https://www.cnblogs.com/rongfengliang/p/12686702.html
https://www.jianshu.com/p/3bc5773a1545
總結
以上是生活随笔為你收集整理的easy-rules规则引擎最佳落地实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ 1129 Channel All
- 下一篇: 全电发票的最新进展:有关咨询整理(下篇)