一、背景
二、规则引擎使用
规则引擎是由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策 从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。大多数规则引擎都支持规则的次序和规则冲突检验,支持简单脚本语言的规则实现,支 持通用开发语言的嵌入开发。目前业内有多个规则引擎可供使用,其中包括商业和开放源码选择。开源的代表是 Drools,商业的代表是 Visual Rules,I Log。
使用规则引擎带来的好处:
package 包名
rule "规则名"
(属性)attributewhen
(条件) - 也叫作规则的 LHS(Left Hand Side)
then
(动作/结果) - 也叫作规则的 RHS(Right Hand Side)
end
2.4 绩效核算实现
经销商顾问绩效包含车商汇、硬广、智慧网销、智能展厅等产品,其中车商汇产品中又区分签约版本类型(标准版、豪华版、智慧版、智慧科技版等)、升版本续约、降版本续约、平版本续约等业绩,详细步骤如下:
2.4.1 数据抽取
数据抽取是由统一调度中心设定执行周期、可按月、天、时执行任务,从媒介系统、CRM系统、HR系统等业务系统抽取基础数据并且进行基本条件过滤,记录同步数据日志,历史数据归档等数据存储;比如:从媒介系统抽取合同信息、合同订单信息、框架信息、回款信息、发布额等信息;CRM及主数据系统抽取拜访、培训记录、经销商数据、经销商与顾问关系等数据;HR系统抽取人员、组织等基本信息。
2.4.2 数据加工
车商汇业绩核算流程(如下图):
步骤一(合同范围):每隔一小时由调度中心执行任务遍历合同表的所有数据,把符合合同范围为部门是经销商-营销管理部 AND 合同为正式合同AND 合同订单类型是i车商的合同数据结果数据插入业绩基础表(A1),并且记录不符合合同的具体原因信息以便查找核对,继续执行步骤二任务;
步骤二(签约版本):遍历结果(A1)的合同从合同订单宽表中查找到根据时间排序获取到该合同最后一个订单的版本类型,根据版本类型来确定合同的签约版本类型,如版本类型是"i车商(智慧版)"则合同签约类型为: 智慧版;如版本类型是"i车商(智慧科技版)"则合同签约类型为:智慧科技版;如版本类型包含 "豪华版" 的则合同版本类型为:豪华版;其他的合同版本类型为标准版;执行完规则后更新业绩基础表(A1)中的签约版本类型字段等相关信息字段,并执行步骤三;
步骤三(签约性质):遍历结果(A1)的合同从合同订单宽表中查找到该订单信息(B1),根据订单信息在合同订单宽表中查找 B1开始时间-730天内该经销商所有订单信息 ,如果没有记录则合同签约性质为"新签" ;如果有根据订单合同开始时间倒序,查找前2个订单信息,进行版本对比,第一个订单版本大于第二个版本则合同签约性质为"升级"(智慧科技版>智慧版>豪华版>标准版),如果版本一致则为"平级",第一个订单版本小于第二个版本则合同签约性质为"降级",其他的情况合同签约性质则为 "其他";
步骤四(0天续约):遍历结果(A1)的合同从合同宽表中查找该经销商小于当前合同的开始时间取结束时间最晚的一条数据(C1),如果有数据的情况下,C1的结束时间+1天=当前合同的开始时间 则为 0天续约成功,如果C1的结束时间+1天 != 当前合同的开始时间则为 0天续约失败;执行完成后更新业绩基础表(A1)中的是否0天续约成功字段;
步骤五:遍历结果(A1)的合同数据,如果 0天续约字段是成功并且签约性质是升级的合同则业绩类型为"升版本续约",如果 0天续约字段是成功并且签约性质是平级的合同则业绩类型为"平版本续约",如果0天续约字段是成功并且签约性质是降级的合同则业绩类型为"降版本续约"
数据流转图
2.5 规则管理
如2.4.2的步骤一需要新增一个条件:合同开始时间为2019-01-01后的合同,即新规则为:把符合合同范围为部门是经销商-营销管理部 AND 合同为正式合同AND 合同订单类型是i车商 AND 合同开始时间>= 2019-01-01 的合同数据结果数据插入业绩基础表(A1),按照常规开发我们需要找到代码,修改代码,线上部署;但是使用可视化规则管理后根据下图1架构,只需要通过可视化规则管理找到 "车商汇合同范围" 规则,在线调整增加合同开始时间条件大于2019-01-01即可,可以进行在线规则验证正确性、动态更新线上规则、做到无需代码开发即可完成规则变更及最新业绩核算,同时可以查看规则修改记录,版本记录。
三、实践与应用
准备环境IDEA+ JDK1.8 + SpringBoot 2.0 + Drools 7.0.0.Final + Mybatis + Ant Design Pro +Maven 3;
1、在pom.xml 文件添加如下包依赖:
<!-- drools -->
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.0.0.Final</version>
</dependency>
2、集成SpringBoot的Drools配置类(DroolsAutoConfiguration.class),该类可以获取到KieServices对象,并且通过该对象访问KIE关于构建和运行的相关对象,比如说可以获取到KieContainer、利用KieContainer来访问KBase和KSession等信息;核心内容如下:
@Configuration
public class DroolsAutoConfiguration {
......
@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws IOException {
KieUtils.setKieServices(getKieServices());
KieUtils.setKieRepository(getKieServices().getRepository());
new ReloadRules().reloadAll();//初始化所有规则
return KieUtils.getKieContainer();
}
......
}
规则初始化及更新规则类内容如下:
public class ReloadRules {
/**
* 加载所有规则
* @author gaonq
*/
public static void reloadAll() {
KieServices kieServices = KieUtils.getKieServices();
final KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(new KieModule() {
@Override
public ReleaseId getReleaseId() {
return kieRepository.getDefaultReleaseId();
}
});
KieHelper kieHelper = KieUtils.getKieHelper();
for (Map.Entry<String, String> entry : getRules().entrySet()) {
kieHelper.addContent(entry.getValue(), "rules/" + entry.getKey() + ".drl");
}
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.ERROR)) {
System.out.println("更新规则错误:" + results.getMessages());
throw new IllegalStateException("更新规则错误");
}
KieSession kSession = kieHelper.build().newKieSession();
KieUtils.setKieSession(kSession);
KieContainer kieContainer = kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
KieUtils.setKieContainer(kieContainer);
}
......
/**
* 获取数据库所有规则初始化
* @return 规则list
*/
public static HashMap<String, String> getRules() {
HashMap<String, String> rules = new HashMap<>();
rules.put("rule1", "package rules.zhwx;\n" +
"rule \"rule1\"\n" +
" when\n" +
" eval(true)\n" +
" then\n" +
" System.out.println(\"rule1\");\n" +
"end");
return rules;
}
}
/**
* 常用的方法
* @author gaonq
*/
public class KieUtils {
......
/**
* 加载了某个特定规则组
* @param agendaGroupName 规则组名称
* @return 返回 KieSession
* @author gaonq
*/
public static KieSession getKieSession(String agendaGroupName) {
if (StringUtils.isNoneBlank(agendaGroupName)) {
kieSession.getAgenda().getAgendaGroup(agendaGroupName).setFocus();
}
return kieSession;
}
}
执行规则方法,内容如下:
KieSession kieSession = KieUtils.getKieSession("规则组名称");
kieSession.fireAllRules();
KieUtils.disposeKieSession(kieSession);
注:
• Duplicate rule name:"规则名" 不允许有重复,否则规则加载编译会报错。
• 尽量使用aganda-group组属性,同时设置lock-on-active为true,是将若干规则分到一个组里,这个组里的规则只有一个会被执行,执行的先后顺序可以通过salience属性进行控制。
[1] Drools规则引擎技术指南 作者:来志辉 出版社:北京大学出版社
[2] 官方最新文档: https://docs.jboss.org/drools/release/7.0.0.Final/drools-docs/html_single/index.html
[3] Drools7.0.0.Final规则引擎教程:https://blog.csdn.net/wo541075754/article/category/9269332/2