Springboot集成Easy Rules引擎,实现一个商品优惠券系统

Easy Rules是一个轻量级的Java规则引擎,它允许开发者将业务规则从代码中解耦出来,使规则的管理和执行更加灵活。

现在让我们一起利用Spring Boot结合Easy Rules和MyBatis技术栈,可以实现一个高效且易于维护的优惠券系统例子。

1. 添加依赖项

pom.xml 文件中确保包含以下依赖项:

go 复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MyBatis Framework -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.1</version>
    </dependency>

    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Easy Rules Core -->
    <dependency>
        <groupId>org.jeasy</groupId>
        <artifactId>easy-rules-core</artifactId>
        <version>4.4.0</version>
    </dependency>

    <!-- Easy Rules Support for Spring -->
    <dependency>
        <groupId>org.jeasy</groupId>
        <artifactId>easy-rules-support-spring</artifactId>
        <version>4.4.0</version>
    </dependency>

    <!-- Lombok (Optional, for reducing boilerplate code) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- Test Dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2. 配置 MyBatis 和数据库连接

application.properties 文件中配置数据库连接信息和 MyBatis 设置:

go 复制代码
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/coupon_system?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis 配置
mybatis.type-aliases-package=com.example.couponsystemdb.model
mybatis.mapper-locations=classpath*:mapper/*.xml

3. 定义业务实体类

我们需要定义一些业务实体类来表示购物车、商品和优惠券信息。

ShoppingCart.java

go 复制代码
package com.example.couponsystemdb.model;

import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
publicclass ShoppingCart {
    privatedouble totalPrice = 0; // 购物车总价格
    privatefinal List<Item> items = new ArrayList<>(); // 商品列表

    /**
     * 向购物车添加商品
     * @param item 商品对象
     */
    public void addItem(Item item) {
        items.add(item);
        totalPrice += item.getPrice();
    }

    /**
     * 应用折扣
     * @param discount 折扣比例(例如 0.10 表示 10% 折扣)
     */
    public void applyDiscount(double discount) {
        totalPrice -= totalPrice * discount;
    }
}

Item.java

go 复制代码
package com.example.couponsystemdb.model;

import lombok.Data;

@Data
publicclass Item {
    private String name; // 商品名称
    private String category; // 商品类别
    privatedouble price; // 商品价格

    /**
     * 构造函数
     * @param name 商品名称
     * @param category 商品类别
     * @param price 商品价格
     */
    public Item(String name, String category, double price) {
        this.name = name;
        this.category = category;
        this.price = price;
    }
}

CouponRule.java

这个类用于从数据库加载规则信息。

go 复制代码
package com.example.couponsystemdb.model;

import lombok.Data;

@Data
public class CouponRule {
    private Long id; // 规则ID
    private String ruleName; // 规则名称
    private String description; // 规则描述
    private String conditionExpression; // 条件表达式
    private String actionExpression; // 动作表达式
}

4. 创建数据库表和初始化数据

创建一个名为 coupon_system 的数据库,并在其中创建 coupon_rule 表。然后插入一些初始规则数据。

SQL 脚本

go 复制代码
CREATE DATABASE coupon_system;

USE coupon_system;

CREATETABLE coupon_rule (
    idBIGINT AUTO_INCREMENT PRIMARY KEY,
    rule_name VARCHAR(255) NOTNULL,
    description TEXT,
    condition_expression TEXTNOTNULL,
    action_expression TEXTNOTNULL
);

INSERTINTO coupon_rule (rule_name, description, condition_expression, action_expression) VALUES
('Total Price Discount Rule', 'Offers a 10% discount if the total price exceeds 1000',
'cart.getTotalPrice() > 1000',
'cart.applyDiscount(0.10); System.out.println("Applied 10% discount for total price exceeding 1000");'),

('Category Discount Rule', 'Offers an additional 5% discount on Electronics category',
'cart.getItems().stream().anyMatch(item -> item.getCategory().equals("Electronics"))',
'cart.applyDiscount(0.05); System.out.println("Applied 5% discount for purchasing electronics");');

5. 定义动态规则

我们将使用 Easy Rules 的 DynamicRule 类来从数据库加载规则并执行。

DynamicCouponRule.java

go 复制代码
package com.example.couponsystemdb.rule;

import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.core.DynamicRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
publicclass DynamicCouponRule extends DynamicRule {

    @Autowired
    private CouponService couponService;

    /**
     * 评估条件
     * @param facts 事实对象
     * @return 是否满足条件
     * @throws Exception 异常
     */
    @Override
    @Condition
    public boolean evaluate(Facts facts) throws Exception {
        returnsuper.evaluate(facts);
    }

    /**
     * 执行动作
     * @param facts 事实对象
     * @throws Exception 异常
     */
    @Override
    @Action
    public void execute(Facts facts) throws Exception {
        super.execute(facts);
    }

    /**
     * 从数据库设置规则
     * @param ruleId 规则ID
     */
    public void setRuleFromDatabase(Long ruleId) {
        CouponRule couponRule = couponService.getCouponRuleById(ruleId);
        setName(couponRule.getRuleName());
        setDescription(couponRule.getDescription());
        setConditionExpression(couponRule.getConditionExpression());
        setActionExpression(couponRule.getActionExpression());
    }
}

6. 配置规则引擎

我们需要配置规则引擎并将规则注册到 Spring 上下文中。

EasyRulesConfig.java

go 复制代码
package com.example.couponsystemdb.config;

import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
publicclass EasyRulesConfig {

    /**
     * 配置规则引擎
     * @return 规则引擎实例
     */
    @Bean
    public RulesEngine rulesEngine() {
        returnnew DefaultRulesEngine();
    }
}

7. 创建 Service 层

创建一个服务层来处理与数据库的交互。

CouponService.java

go 复制代码
package com.example.couponsystemdb.service;

import com.example.couponsystemdb.mapper.CouponRuleMapper;
import com.example.couponsystemdb.model.CouponRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
publicclass CouponService {

    @Autowired
    private CouponRuleMapper couponRuleMapper;

    /**
     * 根据规则ID获取规则
     * @param id 规则ID
     * @return 规则对象
     */
    public CouponRule getCouponRuleById(Long id) {
        return couponRuleMapper.selectById(id);
    }
}

8. 创建 Mapper 接口

使用 MyBatis 映射器接口来操作数据库。

CouponRuleMapper.java

go 复制代码
package com.example.couponsystemdb.mapper;

import com.example.couponsystemdb.model.CouponRule;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface CouponRuleMapper {

    /**
     * 根据规则ID查询规则
     * @param id 规则ID
     * @return 规则对象
     */
    @Select("SELECT * FROM coupon_rule WHERE id = #{id}")
    CouponRule selectById(Long id);
}

9. 编写主应用程序类

最后,在主应用程序类中使用规则引擎来处理购物车中的商品。

CouponSystemApplication.java

go 复制代码
package com.example.couponsystemdb;

import com.example.couponsystemdb.model.ShoppingCart;
import com.example.couponsystemdb.model.Item;
import com.example.couponsystemdb.rule.DynamicCouponRule;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
publicclass CouponSystemApplication implements CommandLineRunner {

    @Autowired
    private RulesEngine rulesEngine; // 规则引擎

    @Autowired
    private DynamicCouponRule dynamicCouponRule; // 动态规则

    @Autowired
    private CouponService couponService; // 优惠券服务

    public static void main(String[] args) {
        SpringApplication.run(CouponSystemApplication.class, args);
    }

    /**
     * 应用启动时运行的方法
     * @param args 命令行参数
     * @throws Exception 异常
     */
    @Override
    public void run(String... args) throws Exception {
        ShoppingCart cart = new ShoppingCart(); // 创建购物车
        cart.addItem(new Item("Laptop", "Electronics", 1200)); // 添加商品
        cart.addItem(new Item("Book", "Books", 30)); // 添加商品

        Facts facts = new Facts(); // 创建事实对象
        facts.put("cart", cart); // 将购物车放入事实对象

        Rules rules = new Rules(); // 创建规则集合

        // 从数据库加载规则并注册到规则引擎
        Long[] ruleIds = {1L, 2L}; // 数据库中的规则ID
        for (Long ruleId : ruleIds) {
            dynamicCouponRule.setRuleFromDatabase(ruleId); // 从数据库设置规则
            rules.register(dynamicCouponRule); // 注册规则
        }

        rulesEngine.fire(rules, facts); // 执行规则引擎

        System.out.println("Final price after discounts: " + cart.getTotalPrice()); // 输出最终价格
    }
}

运行应用程序

保存所有文件并在终端中运行以下命令启动应用程序:

go 复制代码
mvn spring-boot:run

你应该会看到以下输出:

go 复制代码
Applied 10% discount for total price exceeding 1000
Applied 5% discount for purchasing electronics
Final price after discounts: 1071.0

总结

这个示例展示了如何在 Spring Boot 应用程序中集成 Easy Rules 和 MyBatis,以便通过数据库灵活配置优惠券规则。通过这种方式,你可以在不修改代码的情况下添加新的规则。以下是关键点总结:

  1. 数据库配置:使用 MyBatis 和 MySQL 来存储和管理规则。

  2. 动态规则 :使用 Easy Rules 的 DynamicRule 类来加载和执行从数据库获取的规则。

  3. 服务层:通过服务层来处理与数据库的交互。

  4. 规则引擎配置:配置规则引擎并注册动态规则。

关注我!Java从此不迷路!

1590929382495721.jpg
相关推荐
小码哥_常1 小时前
别再被误导!try...catch性能大揭秘
后端
无巧不成书02183 小时前
30分钟入门Java:从历史到Hello World的小白指南
java·开发语言
苍何3 小时前
30分钟用 Agent 搓出一家跨境网店,疯了
后端
ssshooter4 小时前
Tauri 2 iOS 开发避坑指南:文件保存、Dialog 和 Documents 目录的那些坑
前端·后端·ios
追逐时光者4 小时前
一个基于 .NET Core + Vue3 构建的开源全栈平台 Admin 系统
后端·.net
程序员飞哥4 小时前
90后大龄程序员失业4个月终于上岸了
后端·面试·程序员
zs宝来了5 小时前
Playwright 自动发布 CSDN 的完整实践
java
彭于晏Yan5 小时前
Redisson分布式锁
spring boot·redis·分布式
吴声子夜歌6 小时前
TypeScript——基础类型(三)
java·linux·typescript
GetcharZp6 小时前
Git 命令行太痛苦?这款 75k Star 的神级工具,让你告别“合并冲突”恐惧症!
后端