今天放福利干货, 由于接触到了遇到了很多关于匹配条件判断的业务场景和需求,很多人可能不假思索的就直接干if-else,可能对代码有点追求的上了层次干策略模式,噼噼啪啪绞尽脑汁设计了一啪优雅牛逼的代码接口和实现,其实做过风控系统的同学们应该对规则引擎都不陌生,可以执行百度,规则引擎有好多,传统的我曾经用过的和感觉最容易上手的当属drools了,今天就干他,分享一下
直接开干
普通规则写法
先来常常鲜,堆叠式的规则写法,将所有的判断条件全部写一个规则,重复累赘
package rules;
import com.example.drools.Fd.domain.TradeRecord
import java.math.BigDecimal
import org.slf4j.LoggerFactory
global org.slf4j.Logger logger
rule "Between3And12"
when
trd:TradeRecord(getFqNum() >=3 && getFqNum() < 12)
then
trd.setFee(new BigDecimal("0.05").multiply(trd.getAmount()));
logger = LoggerFactory.getLogger("Drools-Between3And12");
logger.info("命中费率规则 :12期以上24期以内");
end
rule "Between12And24"
when
trd:TradeRecord(getFqNum() >= 12 && getFqNum() < 24)
then
trd.setFee(new BigDecimal("0.25").multiply(trd.getAmount()));
logger = LoggerFactory.getLogger("Drools-Between12And24");
logger.info("命中费率规则 :12期以上24期以内");
end
今天来个进阶的,首先编写一段规则模板,对我又在用模板方式
模板脚本:
这是一个根据交易分期数来匹配对应的期数费率区间,其实延伸开来,例如价格区间优化折扣、等这种带区间的需求都可以这样去实现,动态,易于维护,逻辑简单清晰易懂
package rules;
dialect "java"
import java.math.BigDecimal
import com.example.drools.Fd.domain.TradeRecord
import org.slf4j.LoggerFactory
global com.example.drools.Fd.domain.Fenqi fqs
global org.slf4j.Logger logger
rule "BetweenMinAndMax"
when
trd:TradeRecord(getFqNum() >= fqs.getMin() && getFqNum() < fqs.getMax())
then
trd.setFee(new BigDecimal(fqs.getRadio()).multiply(trd.getAmount()));
logger = LoggerFactory.getLogger("Drools-BetweenMinAndMax");
logger.info("命中费率规则 : {} 期以上(包含),{} 期以内",fqs.getMin(),fqs.getMax());
end
规则获取参数
- 可以使用global关键字将java参数定义成一个全局变量参数,在整个规则中都能使用到
- 使用 session.setGlobal("fqs",fenqishu) 传递全局参数
- 传参,使用session.insert(tradeRecord),规则中就像java代码一样丝滑
java执行代码:
简单模拟
- 一笔交易事实,100元的交易分了6期
- 3期到12期之间执行的费率是0.25%
- 命中规则,并之间计算分期费用,规则结果返回费用结果
public void getOutRuleFiles() throws IOException {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
File[] ruleFiles = Paths.get("d://temp/rules//").toFile().listFiles();
for (File file : ruleFiles) {
kieFileSystem.write(ResourceFactory.newFileResource(file));
}
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem).buildAll();
KieContainer kContainer = kieServices.newKieContainer(kieBuilder.getKieModule().getReleaseId());
KieBase kieBase = kContainer.getKieBase();
KieSession session = kieBase.newKieSession();
//交易事实
TradeRecord tradeRecord = new TradeRecord();
tradeRecord.setAmount(new BigDecimal(100));
tradeRecord.setFqNum(6);
tradeRecord.setMerchantName("删古");
session.insert(tradeRecord);
//条件事实
Fenqi fenqishu = new Fenqi();
fenqishu.setMax(12);
fenqishu.setMin(3);
fenqishu.setRadio(0.25);
session.setGlobal("fqs",fenqishu);
session.fireAllRules();
session.dispose();
System.out.println("命中规则计费结果:"+tradeRecord.getFee());
}
工程结构:spring-boot
pom依赖
总结
其实drools不是什么新技术,很老的东西了,但是网上很多用法是直接将规则写到工程本身的/main/resources/目录下,试想一下,如果这样就不太方便了,如果规则随时需要变动,那不是要重新打包吗,所以结合实践,把规则拿出来放在指定外部目录中是非常有必要的,就几行代码就能解决多少if判断,将规则做成API,也能实现解耦,技术不复杂,思路和解决方案更重要,希望对路过的朋友有所帮助。