前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Spring EL实现一个简单的电商打折优惠规则引擎

基于Spring EL实现一个简单的电商打折优惠规则引擎

作者头像
业余草
发布2020-11-24 11:42:40
1.6K1
发布2020-11-24 11:42:40
举报
文章被收录于专栏:业余草业余草

你知道的越多,不知道的就越多,业余的像一棵小草!

你来,我们一起精进!你不来,我和你的竞争对手一起精进!

编辑:业余草

juejin.im/post/6894075149784449031

推荐:https://www.xttblog.com/?p=5116

基于Spring EL实现一个简单的电商打折优惠规则引擎

在日常工作中,能遇到使用规则引擎的地方非常的多。比如,电商中的打折优惠场景,复杂多变。再比如,考勤分析时每个部门都有不同的分析策略。还有薪资计算,公司中不同职级,不同办公地点的算薪方式都不同。这些场景都非常的适合使用规则引擎。

说到规则引擎,大部分人可能首先想到的是 Drools,但其实 Spring EL 也很好用。

WTF?Spring EL还能做规则引擎?

你没有看错,Spring EL不仅能做规则引擎,我还在生产环境大范围的使用了。

为什么要用Spring EL做规则引擎?

相对于别的规则引擎,Spring EL 更加轻量级,学习成本更低,与函数式编程配合表现可能超乎你的想象!

规则引擎的使用场景

当一个系统的某项业务规则多变时,需要设计的非常的灵活,如果用传统硬编码的方式去做,可能导致代码逻辑异常复杂,而且需要改动得非常频繁。

为了解决这个问题,我打算引入脚本引擎去简化开发,降低系统复杂度,在考察过后,最终决定用 Spring EL。

如何做

首先,我们先来定义一个结算的基础对象。

代码语言:javascript
复制
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private String userId;
    private Integer age;
    //是否是新客
    private Boolean isNew;
    private LocalDate orderDate;
    private BigDecimal price;
}

如果有一个供应商有一条结算规则是分成订单金额的80%,我们可以这样计算。

代码语言:javascript
复制
public static void main(String[] args){
    ExpressionParser expressionParser = new SpelExpressionParser();
    Expression expression = expressionParser.parseExpression("price * 0.8");
    Order order = new Order();
    order.setPrice(BigDecimal.TEN);
    BigDecimal value = expression.getValue(order, BigDecimal.class);
    System.out.println(value);
}

这样就能算出应该结算 8元,当然这个例子太简单了,我们定义几个复杂的结算规则,看看应该如何去做。

规则1:周一周五新客结算,结算金额为 price * 0.2

规则2: 年龄大于18岁、金额大于10的结算,结算金额为(price - 10) * 0.8 我们来看上面的规则,实际上是分成了两步,第一步是过滤掉不结算的订单,第二步是真正的计算金额,因此代码我们也可以分这两步走

我先定义几个测试用例

代码语言:javascript
复制
List<Order> orders = new ArrayList<Order>(){{
    //年龄19,不是新客,周一下单,金额11
    add(new Order("张三",19,false,LocalDate.of(2020,11,9),new BigDecimal(11)));
    //年龄17,是新客,周五下单,金额19
    add(new Order("李四",17,true,LocalDate.of(2020,11,13),new BigDecimal(19)));
    //年龄17,不是新客,周六下单,金额9
    add(new Order("王五",17,true,LocalDate.of(2020,11,14),new BigDecimal(9)));
}};

主要逻辑如下,先过滤掉不需要的订单,然后对剩下的订单进行结算。

代码语言:javascript
复制
public static void settle(List<Order> orders, List<String> filterRule,
                          String settleRule, Map<String, Expression> expressionCache) {
    Stream<Order> stream = orders.stream();
    for (String rule : filterRule) {
        Expression expression = FunctionUtil
                .cacheFunction(s -> expressionParser.parseExpression(s), rule, expressionCache);
        stream = filter(stream, expression);
    }
    Expression expression = FunctionUtil
            .cacheFunction(s -> expressionParser.parseExpression(s), settleRule, expressionCache);
    stream.forEach(o -> System.out.println(o.getUserId() + expression.getValue(o)));
}

public static <T> Stream<T> filter(Stream<T> stream, Expression expression) {
    return stream.filter(s -> expression.getValue(s, Boolean.class));
}

FunctionUtil.cacheFunction() 的作用是对 Expression 进行缓存,因为创建 Expression 的代价比较高,因此把 String 规则作为 key,Expression 作为 value 缓存一下。

执行一下代码。

代码语言:javascript
复制
public static void main(String[] args) {
    Map<String, Expression> expressionCache = new HashMap<String, Expression>();

    System.out.println("结算rule1");
    List<String> filterRule1 =
            Arrays.asList("orderDate.getDayOfWeek().getValue() == 1 || orderDate.getDayOfWeek().getValue() == 5", "isNew");
    String settleRule1 = "price * 0.2";
    settle(orders, filterRule1, settleRule1, expressionCache);

    System.out.println("结算rule2");
    List<String> filterRule2 = Arrays.asList("age > 18", "price > 10");
    String settleRule2 = "(price - 10) * 0.8";
    settle(orders, filterRule2, settleRule2, expressionCache);
}

执行结果。

代码语言:javascript
复制
结算rule1
李四3.8
结算rule2
张三0.8

可以看到,通过 Spring EL 和函数式编程,我们只需要编写规则就可以实现复杂的结算逻辑。

以上,只是 Spring EL 的冰山一角。更多用法建议大家参考官方文档。本文只是一个入门,并带领大家学会在什么样的场景下应该使用规则引擎。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-11-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 你知道的越多,不知道的就越多,业余的像一棵小草!
  • 编辑:业余草
  • juejin.im/post/6894075149784449031
  • 推荐:https://www.xttblog.com/?p=5116
  • 为什么要用Spring EL做规则引擎?
  • 规则引擎的使用场景
  • 如何做
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档