最近的项目中,使用的是flowable工作流来处理业务流程,但是在业务规则的配置中,是在代码中直接固定写死的,领导说这样不好,需要规则可以动态变化,可以通过页面去动态配置改变,所以就花了几天时间去研究了一下drools规则引擎框架。然后应用到了项目中。
首先在项目中引入规则引擎相关依赖
<properties> <java.version>1.8</java.version> <drools.version>7.20.0.final</drools.version> </properties><dependencies> <!--引入规则引擎--> <dependency> <groupid>org.kie</groupid> <artifactid>kie-spring</artifactid> <version>${drools.version}</version> <exclusions> <exclusion> <groupid>org.springframework</groupid> <artifactid>spring-tx</artifactid> </exclusion> <exclusion> <groupid>org.springframework</groupid> <artifactid>spring-beans</artifactid> </exclusion> <exclusion> <groupid>org.springframework</groupid> <artifactid>spring-core</artifactid> </exclusion> <exclusion> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> </exclusion> </exclusions> </dependency> <dependency> <groupid>org.drools</groupid> <artifactid>drools-compiler</artifactid> <version>${drools.version}</version> </dependency> <dependency> 垃圾袋英文 <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> <scope>provided</scope> <version>1.18.20</version> </dependency></dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.*</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build>
这里的drools版本使用的是7.20.0.final,如果想和flowable结合使用,在流程画图中插入规则任务,可以将drools版本和flowable starter中管理的drools版本 一致,比如我现在的项目中用到的
flowable-srping-boot-starter的依赖版本是6.5.0,点进去这个jar包的pom文件
再进一步点parent标签
然后再点parent标签的依赖
然后再点parent标签内的flowable-root,然后搜索drools
可以看到flowable starter集成管理的drools版本是7.6.0-final,所以最好和这个版本保持一致
当然你需要在flowable modeler画图项目中引入,然后启动flowable modeler程序,在画图界面
任务类型中就可以看到一个business rule task,商业规则任务。
如果只是独立使用,则可以直接使用我最开始引入的那个版本7.20.0.final
还有一个问题就是如果你的项目中引入了spring boot的热部署工具,
需要把这个依赖注释掉,项目中不能引入这个jar包,不然这个jar包会影响drools规则引擎执行生成的规则,而且在运行规则的时候也不会报错,这是个很隐蔽的坑,我在项目中已经踩过坑了,所以特别提示一下,就是这个jar包存在,规则引擎在触发执行规则的时候,是不会执行的,在日志信息中一直显示的是执行规则0条,即使你的规则文件语法没有任何错误,直接将这个依赖删除后,就可以正常执行规则了。
引入相关依赖后,需要在项目中添加配置类:
在config包下创建droolsautoconfiguration类
import cn.hutool.core.util.chartutil;import lombok.extern.slf4j.slf4j;import org.kie.api.kieba;import org.kie.api.kiervices;import org.kie.api.builder.*;import org.kie.api.runtime.kiecontainer;import org.kie.api.runtime.kiession;import org.kie.internal.io.resourcefactory;import org.kie.spring.kmodulebeanfactorypostprocessor;import org.springframework.boot.autoconfigure.condition.conditionalonmissingbean;import org.springframework.context.annotation.bean;import org.spri微笑日图片ngframework.context.annotation.configuration;import org.springframework.core.io.resource;import org.springframework.core.io.support.pathmatchingresourcepatternresolver;import org.springframework.core.io.support.resourcepatternresolver; import java.io.ioexception; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2021/12/6 9:30 * @description drools配置类 */@configuration@slf4jpublic class droolsautoconfiguration { public static final string rule_path="rules/"; public kiervices getkiervices(){ kiervices kiervices = kiervices.factory.get(); return kiervices; } /** * 管理规则文件的位置路径信息 * @return * @throws ioexception */ @bean @conditionalonmissingbean(kiefilesystem.class) public kiefilesystem kiefilesystem() throws ioexception { kiefilesystem kiefilesystem = getkiervices().newkiefilesystem(); for (resource file:getrulefiles()) { kiefilesystem.write(resourcefactory.newclasspathresource(rule_path+file.getfilename(), chartutil.utf_8)); } return kiefilesystem; } @bean @conditionalonmissingbean(kiecontainer.class) public kiecontainer kiecontainer() throws ioexception { kiervices kiervices = getkiervices(); kierepository kierepository = kiervices.getrepository(); kierepository.addkiemodule(new kiemodule() { @override public releaid getreleaid() { return kierepository.getdefaultreleaid(); } }); kiebuilder kiebuilder = kiervices.newkiebuilder(kiefilesystem()); kiebuilder.buildall(); results results = kiebuilder.getresults(); if (results.hasmessages(message.level.error)){ log.error(results.getmessages().tostring()); } kiecontainer kiecontainer = kiervices.newkiecontainer(kierepository.getdefaultreleaid()); return kiecontainer; } @bean @conditionalonmissingbean(kieba.class) public kieba kieba() throws ioexception { kieba kieba = kiecontainer().getkieba(); return kieba; } @bean @conditionalonmissingbean(kiession.class) public kiession kiession() throws ioexception { return kiecontainer().newkiession(); } @bean @conditionalonmissingbean(kmodulebeanfactorypostprocessor.class) public kmodulebeanfactorypostprocessor kmodulebeanfactorypostprocessor(){ kmodulebeanfactorypostprocessor kmodulebeanfactorypostprocessor = new kmodulebeanfactorypostprocessor(); return kmodulebeanfactorypostprocessor; } /** * 获取规则文件资源 * @return * @throws ioexception */ private resource[] getrulefiles() throws ioexception { resourcepatternresolver resourcepatternresolver =new pathmatchingresourcepatternresolver(); resource[] resources = resourcepatternresolver.getresources("classpath*:" + rule_path + "**/*.*"); return resources; }}
然后在项目的resources下创建rules文件夹存放规则文件
创建一个drl后缀的规则文件fixratecostcalculatorrule.drl
//package 可以随意指定,没有具体的要求,可以命名成和项目相关的,或者直接rulespackage com.drools//或者这样//package rules import java.math.bigdecimalimport java.lang.integerimport org.apache.commons.lang3.math.numberutils;import com.drools.bo.guatanteecost//这里设置的全局变量只相当于声明变量,需要在代码执行规则前给该变量赋值初始化global org.slf4j.logger loggerrule "rule1"//dialect "java"salience 30//防止死循环//no-loop trueenabled fal when $guaranteecost:guatanteecost(amount>numberutils.double_zero && amount<=300000) then $guaranteecost.tcost(200d); logger.info("保费"+200); update($guaranteecost) end rule "rule2" enabled fal salience 20 when $guaranteecost:guatanteecost(amount>300000,amount<=500000) then $guaranteecost.tcost(300d); logger.info("保费"+300); update($guaranteecost) endrule "rule3"enabled falsalience 10when$guaranteecost:guatanteecost(amount>500000,amount<=800000) then// 效果和上边两条范围中的更新数据效果一样 modify($guaranteecost){ tcost(400d) } logger.info("保费"+400); end
然后需要创建一个java对象guatanteecost,用于向规则文件中传递fact(java对象)变量值
amount是guatanteecost类中的属性
@data@noargsconstructor@allargsconstructorpublic class guatanteecost { /** * 保证金金额 */ private double amount; /** * 保费金额 */ private double cost;}
然后就可以写一个单元测试方法,或者创建一个controller进行测试
import cn.hutool.core.util.chartutil;import com.baomidou.mybatisplus.core.toolkit.wrappers;import com.drools.bo.guatanteecost;import com.drools.entity.droolsruleconfig;import com.drools.rvice.droolsruleconfigrvice;import com.github.xiaoymin.knife4j.annotations.apisort;import io.swagger.annotations.api;import io.swagger.annotations.apiimplicitparam;import io.swagger.annotations.apioperation;import lombok.allargsconstructor;import lombok.extern.slf4j.slf4j;import org.apache.commons.lang3.stringutils;import org.drools.template.objectdatacompiler;import org.kie.api.io.resourcetype;import org.kie.api.runtime.kiession;import org.kie.internal.io.resourcefactory;import org.kie.internal.utils.kiehelper;import org.springframework.beans.factory.annotation.autowired;import org.springframework.web.bind.annotation.postmapping;import org.springframework.web.bind.annotation.requestmapping;import org.springframework.web.bind.annotation.restcontroller; import java.io.ioexception;import java.io.inputstream;import java.math.bigdecimal;import java.util.list; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2021/12/6 15:41 * @description */@restcontroller@requestmapping("/drools")@allargsconstructor(onconstructor_={@autowired})@api(tags = "drools规则引擎测试接口")@apisort(30)@slf4jpublic class droolstestcontroller { @autowired private kiession kiession; @autowired private droolsruleconfigrvice droolsruleconfigrvice; @apioperation("测试计算保费规则") @apiimplicitparam(name="bzjamount",value = "保证金金额(单位元)") @postmapping("test/rule") public string testdrools(bigdecimal bzjamount){ guatanteecost guatanteecost = new guatanteecost(); guatanteecost.tamount(bzjamount.doublevalue()); kiession.inrt(guatante防震减灾手抄报图片简单又漂亮ecost); kiession.tglobal("logger",log); int allrules = kiession.fireallrules(); double cost = guatanteecost.getcost(); log.info("成功执行{}条规则",allrules); log.info("计算保费{}元", cost); kiession.dispo(); return cost+""; } @apioperation("测试使用规则模板计算保费规则") @apiimplicitparam(name="bzjamount",value = "保证金金额(单位元)") @postmapping("test/ruletemplate") public string testdroolsruletemplate(bigdecimal bzjamount){ guatanteecost guatanteecost = new guatanteecost(); guatanteecost.tamount(bzjamount.doublevalue()); list<droolsruleconfig> droolsruleconfiglist = droolsruleconfigrvice.list(wrappers.<droolsruleconfig>lambdaquery() .eq(droolsruleconfig::getrulename, "fix")); objectdatacompiler converter = new objectdatacompiler(); string drlcontent = stringutils.empty; try(inputstream dis= resourcefactory. newclasspathresource("rules/fixratecostcalculatorrule.drt", chartutil.utf_8) .getinputstream()){// 填充模板内容 drlcontent=converter.compile(droolsruleconfiglist, dis); log.info("生成的规则内容:{}",drlcontent); }catch (ioexception e) { log.error("获取规则模板文件出错:{}",e.getmessage()); } kiehelper helper = new kiehelper(); helper.addcontent(drlcontent, resourcetype.drl); kiession ks = helper.build().newkiession(); ks.inrt(guatanteecost); // kiession.tglobal("logger",log); int allrules = ks.fireallrules(); double cost = guatanteecost.getcost(); log.info("成功执行{}条规则",allrules); log.info("计算保费{}元", cost); kiession.dispo(); return cost+""; } }
至此,可以先忽视第二个接口方法,使用第一个接口方法来测试规则的运行
计算的费用是200,执行的是rule1规则,200000介于0-300000之间,所以保费计算的是200
这种直接写drl规则文件,在里边设定规则的方式比较简便,但是却不灵活,如果我想再添加几条范围,那么就需要重新再来修改这个drl文件,所以在项目中可以使用规则模板drt
然后在项目resources的rules目录下再创建一个drt文件fixratecostcalculatorrule.drt
//模板文件template pro什么意思headerminmaxfixedfee package drools.templatesimport com.drools.bo.guatanteecosttemplate "fixrate"rule "calculate rule_@{row.rownumber}"dialect "mvel"no-loop truewhen $guaranteecost:guatanteecost(amount>@{min} && amount<=@{max})then modify($guaranteecost){ tcost(@{fixedfee}) } end end template
然后创建一个表,用于保存min,max和fixed的参数值(注意事项:template header下边的min,max和fixedfee都相当于声明的参数,但是不能在min上边加一行注释如://参数说明,在解析规则模板时候会把“//参数说明”也当做声明的参数变量),因为这些值可以动态变化了,所以范围规则也相当于可以动态变化,范围就不是之前设置的固定的啦
创建这样一个表,这样就可以灵活配置范围和保费金额了
create table `biz_drools_rule_config` ( `id` bigint(20) not null, `rule_code` varchar(255) default null comment '规则编码', `rule_name` varchar(255) default null comment '规则名称', `min` int(10) default null comment '保证金范围最小值', `max` int(10) default null comment '保证金范围最大值', `fixed_fee` decimal(10,2) default null comment '固定保费', `fee_rate` decimal(5,3) default null comment '费率(小数)', `create_by` varchar(25) default null, `create_time` datetime default null, `update_by` varchar(25) default null, `update_time` datetime default null, primary key (`id`) using btree) engine=innodb default chart=utf8mb4 row_format=dynamic;
然后创建对应的实体类,mapper和rvice,可以使用项目中的代码生成器快捷生成或者idea的插件生成
然后就可以使用controller中的第二个接口方法来测试了
数据库中的数据插入,可以在项目页面中写一个用于添加规则配置参数的页面,在里边插入几条数
这里我是先随意手动添加了几条数据
然后在knife4j文档页面执行接口测试
加上oauth2的验证信息
输入amount的值,也计算的固定保费200
同时从数据库中查询出来3条数据,对应三个范围,生成了三个规则(rule),可以从项目日志中查看
此时可以把数据库中的最大最小值改变一下,再测试一下
此时再传入一个保证金amount值20万,就会计算出保费费用是300元,执行代码时,会再次生成新的规则,每次执行规则模板动态生成的规则drl内容实际上保存在内存中的,并不像最开始创建的drl文件那样
再次执行,就会发现保费计算就成了300元
同时规则模板动态生成的规则内容也对应发生了变化
为了方便使用这个规则模板,可以将测试规则模板的这个接口方法封装成一个辅助类,在业务使用时,可以直接调用
package com.drools.util; import cn.hutool.core.util.chartutil;import com.baomidou.mybatisplus.core.toolkit.wrappers;import com.drools.bo.guatanteecost;import com.drools.entity.droolsruleconfig;import com.drools.rvice.droolsruleconfigrvice;import lombok.extern.slf4j.slf4j;import org.apache.commons.lang3.stringutils;import org.drools.template.objectdatacompiler;import org.kie.api.io.resourcetype;import org.kie.api.runtime.kiession;import org.kie.internal.io.resourcefactory;import org.kie.internal.utils.kiehelper;import org.springframework.beans.factory.annotation.autowired;import org.springframework.stereotype.component; import java.io.ioexception;import java.io.inputstream;import java.math.bigdecimal;import java.util.list; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2021/12/8 16:22 * @description 根据规则引擎模板获取保费金额 */@component@slf4jpublic class calculatecostutil { @autowired private droolsruleconfigrvice droolsruleconfigrvice; /** * @description: 获取固定保费 * @author: xiaomifeng1010 * @date: 2021/12/8 * @param bzjamount * @return: bigdecimal **/ public bigdecimal getfixedfee(bigdecimal bzjamount){ guatanteecost guatanteecost = new guatanteecost(); guatanteecost.tamount(bzjamount.doublevalue()); list<droolsruleconfig> droolsruleconfiglist = droolsruleconfigrvice.list(wrappers.<droolsruleconfig>lambdaquery() .eq(droolsruleconfig::getrulename, "fix")); objectdatacompiler converter = new objectdatacompiler(); string drlcontent = stringutils.empty; try(inputstrea不吃药减肥最快秘籍m dis= resourcefactory. newclasspathresource("rules/fixratecostcalculatorrule.drt", chartutil.utf_8) .getinputstream()){// 填充模板内容 drlcontent=converter.compile(droolsruleconfiglist, dis); log.info("生成的规则内容:{}",drlcontent); }catch (ioexception e) { log.error("获取规则模板文件出错:{}",e.getmessage()); } kiehelper helper = new kiehelper(); helper.addcontent(drlcontent, resourcetype.drl); kiession ks = helper.build().newkiession(); ks.inrt(guatanteecost); int allrules = ks.fireallrules(); double cost = guatanteecost.getcost(); log.info("成功执行{}条规则",allrules); log.info("计算保费{}元", cost); ks.dispo(); return bigdecimal.valueof(cost); } }
暂时就研究了这些点东西,算是刚刚入门这个框架,买的drools图书,还得再多读几遍,多实践操作一下,以后再做一些更深入的总结
到此这篇关于springboot整合drools规则引擎动态生成业务规则的实现的文章就介绍到这了,更多相关springboot整合drools生成业务规则内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-04 02:26:44,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/700610fff7bed46876a248462789372e.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:SpringBoot整合Drools规则引擎动态生成业务规则的实现.doc
本文 PDF 下载地址:SpringBoot整合Drools规则引擎动态生成业务规则的实现.pdf
留言与评论(共有 0 条评论) |