目录

二、项目文件结构以及数据库表结构

三、主要项目文件说明

1、pom文件

2、配置文件

3、实体类

4、添加优惠券业务实现

5、购物车结算业务实现

四、实现效果

1、添加优惠券

2、购物车结算

五、结语



一、前言

需求:动态添加优惠券以及购物车优惠结算

解析需求:

优惠券:优惠券金额、优惠券名称、优惠券编码;

购物车优惠结算结果=购物车金额-优惠券;

解决思路:

我们把购物车优惠结果作为一个fact对象,通过匹配规则文件,来修改其购物金额;但这个规则文件是不固定的,因此我们可以通过模板引擎,传入优惠券相关参数,生成规则内容存放在数据库或生成规则文件,如图所示:

开发环境及工具:

windows10、jdk11、Spring Boot 2.5.2、Drools 7.54.0、模板引擎、MySql 8.0.25、MyBatis;

idea 2021.1.3


二、项目文件结构以及数据库表结构

创建一个Spring Boot项目,项目的文件结构如下:

创建一个数据库表:promote,sql如下:

/*Navicat Premium Data TransferSource Server         : localhostSource Server Type    : MySQLSource Server Version : 80026Source Host           : localhost:3306Source Schema         : drools_testTarget Server Type    : MySQLTarget Server Version : 80026File Encoding         : 65001Date: 28/07/2021 14:31:23
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for promote
-- ----------------------------
DROP TABLE IF EXISTS `promote`;
CREATE TABLE `promote`  (`id` int(0) NOT NULL AUTO_INCREMENT,`promote_code` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '优惠券编码',`promote_rule` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '优惠规则',`promote_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '优惠券名称',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 45 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

三、主要项目文件说明

1、pom文件

  • drools相关:drools-compiler、drools-mvel(Drools7.44.0以下版本可以不添加)
  • Spring Boot相关:spring-boot-starter-web、spring-boot-starter-log4j2、spring-boot-starter-test
  • mybatis:mybatis-spring-boot-starter
  • mysql:mysql-connector-java
  • 模板引擎:ST4
  • 其他:thymeleaf、lombok、fastjson、 gson
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.2</version><relativePath/></parent><groupId>com.qyl</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>11</java.version><drools.version>7.54.0.Final</drools.version></properties><dependencies><!-- drools begin--><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>${drools.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>${drools.version}</version></dependency><!-- drools end --><!-- spring boot begin --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><!-- use log4j2 remove logging--><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!-- mysql 链接--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!--<scope>runtime</scope>--><version>8.0.25</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- lombok begin --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency><!-- lombok end --><!-- json --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><!-- Gson --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.7</version></dependency><dependency><groupId>org.antlr</groupId><artifactId>ST4</artifactId><version>4.0.8</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.5.2</version></plugin></plugins></build></project>

2、配置文件

application-dev.yml:

server:port: 8080# mysql配置
spring:jpa:show-sql: truedatasource:url: jdbc:mysql://localhost:3306/drools_testusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver # 高版本用这个驱动 低版本用com.mysql.jdbc.Driver
#thymeleaf配置thymeleaf:prefix: classpath:/templates/
# mybatis配置mybatis:mapper-locations: classpath*:mapper/*.xmltype-aliases-package: com.qyl.demo.dao

3、实体类

promote.java:优惠券实体类,包括优惠券名称、优惠券编码以及模板引擎生成的规则内容

package com.qyl.demo.model;import lombok.Data;/*** @author qiuyelin* @version 1.0* @Description: TODO 存放规则编码、规则姓名、规则内容等* @date 2021/7/20 13:49*/
@Data
public class Promote {// 促销编号private String promoteCode;// 促销名称private String promoteName;// 规则内容private String promoteRule;
}

RuleResultVo.java:优惠后实体类,包括优惠前价格、优惠后价格以及参加的优惠活动名称

package com.qyl.demo.model;import lombok.Data;/*** @author qiuyelin* @version 1.0* @Description: TODO 返回值实体* @date 2021/7/20 14:35*/
@Data
public class RuleResultVo {// 参加活动商品优惠后的价格private double finallyMoney;// 参加优惠前价格private double beforeMoney;// 参加活动的名称private String promoteName;
}

4、添加优惠券业务实现

在PromoteService.java中添加如下方法:

 /** 生成优惠券* */public String editPromote(String money, String ruleName) {Promote promote = new Promote();double v = Double.parseDouble(money);// 根据模板引擎生成规则内容String rule = UUIDUtil.rule(ruleWorkMap(ruleName, v));// 生成编码String promoteCode = UUIDUtil.typeJoinTime();promote.setPromoteCode(promoteCode);promote.setPromoteName(ruleName);promote.setPromoteRule(rule);// 优惠券保存到数据库int i = promoteMapper.insertPromote(promote);return i > 0 ? "success" : "fail";}

根据优惠券名称及金额,组合业务规则Json:在PromoteService.java中添加如下方法:

 /** 组合业务规则Json方法* */private String ruleWorkMap(String name, Double money) {Map<String, Object> map = new HashMap<>();// Rule部分Map<String, Object> rule = new HashMap<>();rule.put("name", name);// When部分Map<String, Object> when = new HashMap<>();// then部分Map<String, Object> then = new HashMap<>();then.put("money", money);//组合规则 rule when and thenmap.put("rule", rule);map.put("condition", when);map.put("action", then);return JSONObject.toJSONString(map);}

 通过模板引擎生成规则业务内容:关于模板引擎是一个新的技术,可以定义一个模板组文件,有兴趣的可以查找一些相关资料。 在UUIDUtil.java工具类中添加生成规则业务内容的方法,注意workMoneyST的语法格式。

 /** 生成规则业务内容* */private static String ruleWordExchangeST(String json) {STGroup group = new STGroupString(workMoneyST);ST stFile = group.getInstanceOf("wordImport");ST stRule = group.getInstanceOf("ruleValue");JSONObject jsonObject = JSONObject.parseObject(json);JSONObject condition = jsonObject.getJSONObject("condition");JSONObject action = jsonObject.getJSONObject("action");JSONObject rule = jsonObject.getJSONObject("rule");stRule.add("condition", condition);stRule.add("action", action);stRule.add("rule", rule);stFile.add("ruleStr", stRule);return stFile.render();}//通过传参数 即可生成规则内容,将规则内容放在数据库或征程规则文件,为规则库的构建设定基础private static String workMoneyST="wordImport(ruleStr)::=<<\n" +"    package rules\n" +"    import com.qyl.demo.model.RuleResultVo;\n" +"    <ruleStr;separator=\"\\n\">\n" +">>\n" +"ruleValue(condition,action,rule)::=<<\n" +"    rule \"<rule.name>\"\n" +"    no-loop true\n" +"        when\n" +"            $r:RuleResultVo(true)\n" +"        then\n" +"            modify($r){\n" +"                setPromoteName(\"<rule.name>\")\n" +"                 <if(action)>,\n" +"                    setFinallyMoney($r.getBeforeMoney()-<action.money> <endif>)\n" +"           }\n" +"    end\n" +">>";

5、购物车结算业务实现

在PromoteService.java中添加如下方法:

 /** 购物车计算* */public Map<String, Object> calcPromote(String moneySum) {Map<String, Object> map = new HashMap<>();// 优惠后价格double afterMoney = Double.parseDouble(moneySum);// 查询优惠券List<Promote> promoteList = queryPromote();// 优惠后结果List<RuleResultVo> resultVoList = new ArrayList<>();if (promoteList.size() > 0) {// 说明有优惠券for (Promote promote : promoteList) {// 匹配优惠券规则RuleResultVo ruleResultVo = DrlExecute.rulePromote(promote, afterMoney);afterMoney = ruleResultVo.getFinallyMoney();resultVoList.add(ruleResultVo);}}map.put("before", moneySum);map.put("after", afterMoney);map.put("list", resultVoList);return map;}

匹配优惠券规则工具类:DrlExecute.java

package com.qyl.demo.util;import com.qyl.demo.model.Promote;
import com.qyl.demo.model.RuleResultVo;
import org.kie.api.KieBase;
import org.kie.api.runtime.StatelessKieSession;
import org.kie.internal.command.CommandFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;/*** @author qiuyelin* @version 1.0* @Description: TODO 规则工具类* @date 2021/7/20 14:41*/
public class DrlExecute {protected static Logger logger = LoggerFactory.getLogger(DrlExecute.class);/** 匹配优惠券规则* */public static RuleResultVo rulePromote(Promote promote, Double beforeMoney) {RuleResultVo resultVo = new RuleResultVo();resultVo.setBeforeMoney(beforeMoney);logger.info("优惠前价格" + beforeMoney);// 命令执行对象List cmdCondition = new ArrayList<>();cmdCondition.add(CommandFactory.newInsert(resultVo));// 初始化规则库KieBase KieBase = KieUtil.ruleKieBase(promote.getPromoteRule(), promote.getPromoteCode());// 无状态KieSessionStatelessKieSession kieSession = KieBase.newStatelessKieSession();// 调用规则kieSession.execute(CommandFactory.newBatchExecution(cmdCondition));logger.info("优惠后的价格" + resultVo.getFinallyMoney());return resultVo;}}

初始化规则库工具类:KieUtil.java

package com.qyl.demo.util;import org.kie.api.KieBase;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceType;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.utils.KieHelper;import java.io.StringReader;/*** @author qiuyelin* @version 1.0* @Description: TODO 初始化规则库* @date 2021/7/20 14:25*/
public class KieUtil {// 初始化规则库 是Kie的一个工具类public static KieBase ruleKieBase(String rule, String name) {KieHelper kieHelper = new KieHelper();try {kieHelper.kfs.write(generateResourceName(ResourceType.DRL, name), rule);return kieHelper.build();} catch (Exception e) {e.printStackTrace();throw new RuntimeException(" drools init failed!");}}private static String generateResourceName(ResourceType type, String name) {// 这里的路径对应模板引擎中的package包路径return "src/main/resources/rules/" + name + "." + type.getDefaultExtension();}}

四、实现效果

1、添加优惠券

在添加优惠券页面,填写信息

点击提交

如果结果为success,说明添加成功,数据库中也有相应数据

promote_rule的内容为:

    package rulesimport com.qyl.demo.model.RuleResultVo;rule "优惠券30元"no-loop truewhen$r:RuleResultVo(true)thenmodify($r){setPromoteName("优惠券30元"),setFinallyMoney($r.getBeforeMoney()-30.0 )}end

2、购物车结算

在购物车界面输入购物车当前总金额

点击提交,即可查看优惠后的金额:

我添加多个优惠券

计算后的效果为:


五、结语

最近在学习Drools,其中有一个示例是优惠券的动态添加,感觉挺有意思的,就稍加更改,分享给大家,如果有哪里不对的,请大家帮忙提出,避免误人子弟╰( ̄▽ ̄)╭

完整的项目代码在https://gitlab.com/yeliner/drools_springboot_demo

参考资料:Drools规则引擎技术指南_第8章

Spring Boot + Drools+模板引擎,优惠券的动态添加以及购物车结算相关推荐

  1. 软件架构-Spring boot集成模板引擎swagger2实现

    上次说过springboot其实就是一个CI工具,如何体验出来CI的作用就是持续集成,它可以集成各种的工具,这里说说关于模板的集成引擎和Swagger. (一)Spring boot 集成模板引擎实现 ...

  2. Spring Boot整合模板引擎jsp

    为什么80%的码农都做不了架构师?>>>    jsp也算是一种模板引擎吧.整合jsp前,先说一下运行SpringBoot项目的几种方式 1. 运行SpringBoot项目的几种方式 ...

  3. Spring Boot 使用模板引擎

    Spring Boot 推荐使用Thymeleaf.FreeMarker.Velocity.Groovy.Mustache等模板引擎.不建议使用JSP. Spring Boot 对以上几种引擎提供了良 ...

  4. Spring Boot 9-FreeMarker模板引擎

    springboot使用freemarker模板引擎 首先引入freemarker <!-- freemarker依赖包 --> <dependency><groupId ...

  5. Spring Boot - Thymeleaf模板简介以及集成

    文章目录 Spring Boot - Thymeleaf模板简介以及集成 1.什么是Thymeleaf? 2.标准表达式 2.1 变量表达式 2.2 选择表达式/星号表达式 2.3 URL表达式 2. ...

  6. Spring boot装载模板代码工程实践问题

    Spring boot装载模板代码工程实践问题 动态化补充 @KafkaListener 其它问题 JDK版本问题及解决 动态编译失败,出现程序包无法找到问题 Spring boot装载模板代码工程中 ...

  7. Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

    Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题 参考文章: (1)Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代 ...

  8. 后端开发实践——Spring Boot项目模板

    在我的工作中,我从零开始搭建了不少软件项目,其中包含了基础代码框架和持续集成基础设施等,这些内容在敏捷开发中通常被称为"第0个迭代"要做的事情.但是,当项目运行了一段时间之后再来反 ...

  9. Spring Boot默认模板方案Thymeleaf

    Thymeleaf入门 web开发离不开动态页面的开发,目前来说最主流的方案是Thymeleaf Thymeleaf是一个模板框架,它可以支持多种格式的内容动态渲染非常强大,而且天然是与HTML相融合 ...

最新文章

  1. tkinter学习-菜单与画布
  2. NET Core入门笔记
  3. STM32中C语言知识点:初学者必看,老鸟复习(长文总结)
  4. LeetCode 581. 最短无序连续子数组(排序单调栈)
  5. 关于xinetd报错
  6. 判空,空字符串,空对象,判null,判undefined等
  7. mariadb安装_MariaDB CentOS 安装的时候如何确定 Repo 地址
  8. zabbix agent启动不了
  9. python中logging模块的一些简单用法
  10. sqlserver management studio 2014 升级2016_画质提升音质更佳:Insta360影石ONE R迎来最强升级...
  11. 进程管理之工作管理详解(job control)
  12. 矩阵分析及应用(10章 - 张量分析)
  13. centos7设置静态IP地址方法
  14. (default-compile) on project datasource-demo: Fatal error compiling
  15. jQuery插件使用-瀑布流
  16. html+css+js制作点名器
  17. 如何有效的快速提高自己的编程能力
  18. 第五届新疆省ACM-ICPC程序设计竞赛(重现赛)
  19. 使用T-SQL语句插入、更新、删除数据表
  20. Docker 配置国内镜像源

热门文章

  1. 如何取消掉计算机更新图标,本文演示win10电脑更新图标怎么去掉的具体操作方式...
  2. 用Python找101-200之间的素数
  3. iOS截屏后仿今日头条实现一键分享
  4. 计算机的声音图标打不开怎么回事,win10电脑音量图标打不开怎么办
  5. ESP8266入门教程-ESP8266睡眠和唤醒功能
  6. UOS系统下安装软件打不开的解决方法
  7. html5火焰字体效果,CSS3文字特效属性text-shadow如何实现火焰文字的效果
  8. 自顶向下的GAMMA显著性——学习搜索复杂场景中的对象
  9. ORA -09925
  10. 音乐歌单Android,仿网易云音乐歌单界面 Header滑动效果