首页 >> 知识 >> 优惠券使用规则引擎来计算优惠 (drools)

优惠券使用规则引擎来计算优惠 (drools)

电商的促销花样越来越多,规则也也越来越复杂,因此,规则的频繁变更可能会带来频繁的版本开发上线,因此,业务希望能够快速上线,这就要求产品能够做到不修改代码快速上线。

平心而论,优惠券目前的几种玩法已经比较固定,通常都是通用券,折扣券,满减券,满赠券,即使不用规则引擎,大部分优惠券的设计都能够支撑业务侧的需求。对于业务侧的一些比较复杂的规则, 例如叠加规则,互斥规则,通常也是在优惠券可配置的一部分,在优惠券的价格计算中,已经实现了互斥,叠加。

其实,优惠券的计算逻辑非常复杂,尤其是可以使用多张优惠券的情况下,还要考虑不同级别的优惠券,在规则引擎中去实现,还是非常麻烦的。此外,由于 drools 的表达能力只能是 when-then 的方式, 并没有实现完整的编程语言的范式,因此,drools 脚本中很难实现复杂的业务逻辑。

蜜桃成人网站入口还对 groovy 脚本进行了调研,在下一篇文章中,蜜桃成人网站入口用 groovy 来实现优惠券的业务逻辑,在优惠券的场景中,能够做到比 drools 更加灵活。

规则引擎

规则引擎的核心包括两部分:

规则脚本;规则脚本的编译,解释执行;

通常,规则脚本都是独立的语言实现,大部分规则引擎都是使用 java 的开源库 antlr 。antlr 是开源的语法解析器,规则脚本语法虽然简单,但也是一门独立的语言,因此,语法解析,词法解析是 必须要有的,此外,大部分规则引擎都可以做到和 JVM 相互调用,这部分的处理应该还是比较复杂的,有兴趣的可以研究下源代码。

drools 规则引擎

drools 规则引擎主要是应用于风控、反欺诈、智能营销、网点监控、智能核保、业务流自动化等场景中,核心是将业务的逻辑代码由 java 代码移到 drools 脚本,如果需要修改业务逻辑,只需要修改 drools 脚本,而不需要修改后台代码。

通常在实际中,蜜桃成人网站入口把脚本保存在数据库中,大部分时候,不需要修改 drools 脚本。如果业务逻辑发生变化,可以通过修改 drools 脚本,然后 java 代码重新从数据库中 load 脚本,这样,就实现了

通过将业务逻辑代码与后台代码的分离,做到了可以随时修改

一个 drools 规则引擎的基本流程是:

rule rule001when条件then执行结果end

drools 有几个重要的概念,分别是:

Facts

drools 中的 Facts,可以简单的理解为输入

Working memory

working memory 可以简单的理解为 drools 的运行环境

LHS RHS

LHS:条件部分,即 When

RHS:结果部分,即 then

与 java 的交互

drools 的强大之处在于,可以和 java 深度结合,引用 java 的代码,调用 java 的方法。规则在执行的过程中,经常需要与 java 交互,传递参数,判断条件,更新结果等。

KieServices, kieSession, KieContainer, KieFileSystem, KieModuleKieServices: kie 整体的入口,可以用来创建 Container,resource,fileSystem 等KieContainer:KieContainer 就是一个 KieBase 的容器,通过 KieContainer 来获取具体的 KieSessionKieFileSystem:Kie 的虚拟文件系统,包括资源和组织结构,drools 脚本可以通过 KieFileSystem 来加载KieModule:是一个包含了多个 kiebase 定义的容器。KieRepository:是一个 KieModule 的仓库,包含了所有的 KieModule 描述,用一个 ReleaseId 做区分

概念很多,很难理解,我想这也是为什么很多人说 drools 很重的原因吧。

java 使用流程

其实,蜜桃成人网站入口暂时不用关心这么多宏观的概念,先从 hello world 搞起。

通常,java 的使用流程是:

获取 kieSession;将变量插入 kieSession 中调用 kieSession.fireAllRules()

假设蜜桃成人网站入口把 drools 脚本放在 resource/rules 目录下,获取 kieSession 的代码如下:

private static Resource[] getRuleFiles() throws IOException { ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); return resourcePatternResolver.getResources("classpath*:rules/" + "**/*.drl"); } private static KieSession getSession() throws Exception { KieServices kieServices = KieServices.Factory.get(); KieFileSystem kfs = kieServices.newKieFileSystem(); for (Resource file : getRuleFiles()) { log.info("rule file: " + file.getFilename()); try { kfs.write(ResourceFactory.newClassPathResource("rules/" + file.getFilename(), "UTF-8")); } catch (Exception e) { e.printStackTrace(); } } KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll(); Results results = kieBuilder.getResults(); if (results.hasMessages(Message.Level.ERROR)) { for (Message msg : results.getMessages()) { log.error("drools script error info : " + msg.getText()); } throw new Exception("drools script error"); } return kieServices.newKieContainer(KieServices.Factory.get().getRepository().getDefaultReleaseId()).newKieSession();}

执行 rule 的代码:

KieSession kieSession = getSession(); kieSession.insert(order); kieSession.insert(coupon); kieSession.insert(result); int hit = kieSession.fireAllRules(); // hit 是所有规则被命中的规则数代码讲解

代码基于 spring boot,该项目仅为演示项目,因此,并未涉及数据库部分,在实际中可以细化这部分实现。

代码已开源:http://github.com/guotie/drools

核心流程

优惠券的核心在于计算价格的接口,也就是常说的询价接口。

因此,蜜桃成人网站入口写了这么几个 drools 脚本:

折扣类优惠计算脚本满减类优惠计算脚本满赠类优惠计算脚本支付类优惠计算脚本其他

例如,折扣类优惠计算脚本的内容如下:

package com.mall.coupon.drools.rules;// 折扣型import com.mall.coupon.drools.model.Couponimport com.mall.coupon.drools.model.Orderimport com.mall.coupon.drools.model.OrderItemimport com.mall.coupon.drools.model.EnquiryResultglobal com.mall.coupon.drools.service.CouponBatchService couponBatchServiceglobal com.mall.coupon.drools.service.UserCouponService userCouponService//global com.mall.coupon.drools.service.UserCouponService userCouponServiceglobal com.mall.coupon.drools.service.UserService userService// 折扣类优惠券// order 对整个订单打折rule "rule-discount-order"when $result: EnquiryResult() $order: Order() $coupon: Coupon(couponType == "5" && subCouponType == "1" && (minBuyAmount == 0 || $order.totalAmount >= minBuyAmount))then System.out.println("命中 discount-order"); $result.setTotalDiscount($order.getTotalAmount() * $coupon.getNominal() / 100);end// sku 对特定的sku商品打折rule "rule-discount-sku"when $item: OrderItem() $result: EnquiryResult() $coupon: Coupon(couponType == "5" && subCouponType == "2" && (minBuyAmount == 0 || $item.totalAmount >= minBuyAmount) && couponBatchService.skuUsable($coupon.getCouponBatchCode(), $item.getProductSkuId()))then System.out.println("coupon code: " + $coupon.getCouponBatchCode()); System.out.println("couponBatchService: " + couponBatchService); System.out.println("命中 discount-sku"); $result.setTotalDiscount($item.getTotalAmount() * (100 - $coupon.getNominal()) / 100);end总结

如果蜜桃成人网站入口需要新增一种优惠券,那么蜜桃成人网站入口只需要新增该类型优惠券的 drools 脚本,测试无误后,让后台代码重新加载即可,也就实现了业务规则的快速部署。

网站地图