这是一个头部互联网公司中的一个问题。因为有很多业务产品线,作为一个新人或者团队外的人员是很难区分不同的产品线之间的区别的,因此需要给某个产品线一个描述。但是随着业务的发展,产品线下可能又根据某个字段进一步划分,那么子产品线就是父产品线 + 字段 去区分。后面根据两个字段划分…。人都麻了。因为不同的组合有不同的链路。因此针对一个产品,我们要提供针对这个产品的具体规则描述,从而减少答疑。
接下来以 餐厅、套餐、菜品进行举例。
比如你想加盟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代码下载地址
启动方法和工程目录如下
以上面的牛肉火锅(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);
看打印结果
上面还存在以下问题
注意:部分代码没有展示,可以去仓库查看全部源码
---
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);"
目的:上述例子中,规则引擎不可能只为 餐厅 服务,还需要为 套餐、菜品服务。因此肯定是有不同的规则和规则引擎的。因此这里需要一个工厂。
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
目的:其实有有的时候命中规则后我们要做一些事情,比如取到规则的一些描述等信息好组织文案
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;
}
}
有的时候会有转换操作,针对本文提出的案例。我想让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