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

目录

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

三、主要项目文件说明

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 Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80026
 Source Host           : localhost:3306
 Source Schema         : drools_test

 Target Server Type    : MySQL
 Target Server Version : 80026
 File Encoding         : 65001

 Date: 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: true
  datasource:
    url: jdbc:mysql://localhost:3306/drools_test
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver # 高版本用这个驱动 低版本用com.mysql.jdbc.Driver
#thymeleaf配置
  thymeleaf:
    prefix: classpath:/templates/
# mybatis配置

mybatis:
  mapper-locations: classpath*:mapper/*.xml
  type-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 then
        map.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());
        // 无状态KieSession
        StatelessKieSession 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 rules
    import com.qyl.demo.model.RuleResultVo;
        rule "优惠券30元"
        no-loop true
            when
                $r:RuleResultVo(true)
            then
                modify($r){
                    setPromoteName("优惠券30元")
                     ,
                                         setFinallyMoney($r.getBeforeMoney()-30.0 )
               }
        end

2、购物车结算

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

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

 我添加多个优惠券

 计算后的效果为:

 


五、结语

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

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

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

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个简单的Spring Boot使用Drools的示例: 1. 添加Drools依赖 在pom.xml文件中添加Drools的依赖: ```xml <dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artifactId> <version>7.53.0.Final</version> </dependency> ``` 2. 创建Drools规则文件 在resources目录下创建一个名为"rules.drl"的文件,并添加以下规则: ```drools package com.example.demo.rules import com.example.demo.model.Message rule "Hello World" when m : Message( status == Message.HELLO, message : message ) then System.out.println( message ); m.setMessage( "Goodbye cruel world" ); m.setStatus( Message.GOODBYE ); update( m ); end ``` 3. 创建模型类 创建一个名为Message的Java类,它将用于在规则文件中定义条件和操作。 ```java public class Message { public static final int HELLO = 0; public static final int GOODBYE = 1; private String message; private int status; public String getMessage() { return this.message; } public void setMessage(String message) { this.message = message; } public int getStatus() { return this.status; } public void setStatus(int status) { this.status = status; } } ``` 4. 创建规则执行器 创建一个名为"RuleExecutor"的Java类,它将用于加载规则文件并执行规则。 ```java @Service public class RuleExecutor { private KieSession kieSession; @Autowired public RuleExecutor(KieContainer kieContainer) { kieSession = kieContainer.newKieSession(); } public void executeRules(Message message) { kieSession.insert(message); kieSession.fireAllRules(); } } ``` 5. 创建控制器 创建一个名为"DemoController"的Java类,它将用于处理HTTP请求并调用规则执行器。 ```java @RestController public class DemoController { @Autowired private RuleExecutor ruleExecutor; @GetMapping("/hello") public String hello() { Message message = new Message(); message.setStatus(Message.HELLO); message.setMessage("Hello World"); ruleExecutor.executeRules(message); return message.getMessage(); } } ``` 6. 运行应用程序 现在,您可以运行Spring Boot应用程序并访问"http://localhost:8080/hello",该应用程序将打印“Hello World”并将消息更新为“Goodbye cruel world”。 请注意,这只是一个简单的示例。在实际应用程序中,您可能需要为规则文件定义更复杂的条件和操作,并使用更复杂的模型类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值