drools规则引擎
1 单个文件
这个大多搜索导的都是把规则放到一个文件,这个是基础,但是实际应用就不太方便。如果你使用的jdk1.8,那么对应的drools版本为7.x
1.1 pom依赖
<drools.version>7.74.1.Final</drools.version>
<dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>${drools.version}</version> <!-- 请根据需要选择合适的版本 --></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>${drools.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-decisiontables</artifactId><version>${drools.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>${drools.version}</version></dependency>
1.2 droolsconfig
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DroolsConfig {@Beanpublic KieContainer initKieContainer() {KieServices kieServices = KieServices.Factory.get();KieFileSystem kieFileSystem = kieServices.newKieFileSystem();kieFileSystem.write(ResourceFactory.newClassPathResource("rules/gszy.drl"));KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);kieBuilder.buildAll();KieModule kieModule = kieBuilder.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}}
1.3 controller层
注意下面请求和响应返回值是分开的
@Autowiredprivate KieContainer kisContainer;@PostMapping("gszy")public ResponseResult<GszyRespDto> gszy(@RequestBody GszyReqDto req){GszyRespDto gszyRespDto = new GszyRespDto();KieSession kieSession = kisContainer.newKieSession();kieSession.insert(req);kieSession.insert(gszyRespDto);kieSession.fireAllRules();kieSession.dispose();//ResponseResult<GszyRespDto> resp = new ResponseResult<>(true);resp.setData(gszyRespDto);return resp;}
1.4 drl文件
在idea中下载jboss插件,安装drools插件。
这里讲请求和响应参数分开,drl中用的java语言,可以自定义function。从下面来看可以写的比较复杂的业务逻辑。
package com.mms.rules;
import com.dzmsoft.acc.mms.rules.dto.req.GszyReqDto;
import com.dzmsoft.acc.mms.rules.dto.resp.GszyRespDto;rule "验证输入"when$req : GszyReqDto(dzsr == 0.0)thenthrow new IllegalArgumentException("请输入年收入总额");
endrule "检查总收入是否足以支付社保和公积金"when$req : GszyReqDto(dzsr < (dsb * 12.0 + dgjj * 12.0))thenthrow new IllegalArgumentException("总收入不足以负担社保和公积金");
endrule "规则1"when$req : GszyReqDto()$res : GszyRespDto()$dzsr : Double() from $req.getDzsr()$dsb : Double() from $req.getDsb()$dgjj : Double() from $req.getDgjj()$dckx : Double() from $req.getDckx()thendouble dAllCKX = 60000.0 + $dsb * 12.0 + $dgjj * 12.0 + $dckx * 12.0;$res.setDAllCKX(dAllCKX);modify($res) {};
endrule "规则2"when$req : GszyReqDto()$res : GszyRespDto()eval($req.getDzsr() <= $res.getDAllCKX())thendouble dygz = $req.getDzsr() / 12.0;int nygz = (int) dygz;if (nygz % 50 != 0) {nygz += (50 - (nygz % 50));dygz = nygz;}$res.setDygz(dygz);$res.setDnzj(0.0);$res.setDgs(0.0);$res.setDsfl(0.0);$res.setDshsr($req.getDzsr() - ($req.getDsb() + $req.getDgjj()) * 12.0);modify($res) {};
endrule "规则3"when$req : GszyReqDto()$res : GszyRespDto()eval($req.getDzsr() > $res.getDAllCKX())thendouble dygzTemp = $res.getDAllCKX() / 12.0;int nygz = (int) dygzTemp;if (nygz % 50 != 0) {nygz += (50 - (nygz % 50));dygzTemp = nygz;}double dMings = 100000000.0; // 初始设置为一个很大的值double dOptygz = 0.0;while (true) {if (dygzTemp * 12.0 > $req.getDzsr()) {break;}double dgzynssde = dygzTemp * 12.0 - $res.getDAllCKX();double dnzjynssde = $req.getDzsr() - dygzTemp * 12.0;double dgzgs = calGzgs(dgzynssde);double dnzjgs = calNzjgs(dnzjynssde);if (dgzgs + dnzjgs < dMings) {dMings = dgzgs + dnzjgs;dOptygz = dygzTemp;}dygzTemp += 50.0;}$res.setDygz(dOptygz);$res.setDnzj($req.getDzsr() - dOptygz * 12.0);$res.setDgs(dMings);$res.setDsfl(dMings / $req.getDzsr() * 100.0);$res.setDshsr($req.getDzsr() - ($req.getDsb() + $req.getDgjj()) * 12.0 - dMings);modify($res) {};end
2 多个规则文件
2.1 写定路径方式
可以看到下面的DRL文件,多了一个就写一个,这种方式也可以可以的。
@Beanpublic KieContainer initKieContainer() {KieServices kieServices = KieServices.Factory.get();KieFileSystem kieFileSystem = kieServices.newKieFileSystem();// 添加多个 DRL 文件kieFileSystem.write(ResourceFactory.newClassPathResource("rules/gszy.drl"));kieFileSystem.write(ResourceFactory.newClassPathResource("rules/nzjzy.drl"));// 构建 KieModuleKieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);kieBuilder.buildAll();// 检查构建是否有错误if (kieBuilder.getResults().hasMessages(org.kie.api.builder.Message.Level.ERROR)) {throw new RuntimeException("Build Errors:\n" + kieBuilder.getResults().getMessages().toString());}KieModule kieModule = kieBuilder.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}
但为什么启动会重复,看来规则文件虽然可以拆分,但是规则名称不能重复。
2.2 模糊路径方式
drools本身是不支持的,需要通过java代码来实现,按照相对路径的写法。
@Beanpublic KieContainer initKieContainer() {KieServices kieServices = KieServices.Factory.get();KieFileSystem kieFileSystem = kieServices.newKieFileSystem();// 添加多个 DRL 文件// 指定 DRL 文件所在的目录String drlDirectory = "rules";// 获取类路径资源ClassPathResource resource = new ClassPathResource(drlDirectory);// 扫描目录并加载所有 DRL 文件try {File file = resource.getFile();Path directoryPath = file.toPath();// 扫描目录并加载所有 DRL 文件try (Stream<Path> paths = Files.walk(directoryPath)) {List<File> drlFiles = paths.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".drl")).map(Path::toFile).collect(Collectors.toList());for (File drlFile : drlFiles) {kieFileSystem.write(ResourceFactory.newFileResource(drlFile));}}} catch (IOException e) {throw new RuntimeException("Error loading DRL files from directory: " + drlDirectory, e);}// 构建 KieModuleKieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);kieBuilder.buildAll();// 检查构建是否有错误if (kieBuilder.getResults().hasMessages(org.kie.api.builder.Message.Level.ERROR)) {throw new RuntimeException("Build Errors:\n" + kieBuilder.getResults().getMessages().toString());}KieModule kieModule = kieBuilder.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}