SpringBoot + Aviator + 规则中心:轻量级表达式引擎实现营销优惠动态计算

电商大促活动时,营销规则复杂多变,今天满300减50,明天买2送1,后天又变成阶梯式折扣?每次改规则都得改代码、重新发布,简直是开发人员的噩梦!今天就来聊聊如何用SpringBoot + Aviator表达式引擎,搭建一个灵活的营销规则中心,让运营同学也能轻松配置营销规则,再也不用求着开发改代码了!

一、营销规则的痛点

1.1 传统if-else的困境

在没有规则引擎之前,营销优惠计算通常是这样写的:

java 复制代码
// 伪代码:传统的营销优惠计算
public BigDecimal calculateDiscount(Order order) {
    if (order.getUserLevel().equals("VIP")) {
        if (order.getAmount().compareTo(new BigDecimal("1000")) > 0) {
            return order.getAmount().multiply(new BigDecimal("0.8")); // VIP用户满1000打8折
        } else {
            return order.getAmount().multiply(new BigDecimal("0.9")); // VIP用户其他情况打9折
        }
    } else if (order.getUserLevel().equals("GOLD")) {
        if (order.getAmount().compareTo(new BigDecimal("500")) > 0) {
            return order.getAmount().multiply(new BigDecimal("0.85")); // 金牌用户满500打8.5折
        } else {
            return order.getAmount().multiply(new BigDecimal("0.95")); // 金牌用户其他情况打9.5折
        }
    } else {
        if (order.getAmount().compareTo(new BigDecimal("1000")) > 0) {
            return order.getAmount().multiply(new BigDecimal("0.9")); // 普通用户满1000打9折
        } else {
            return order.getAmount(); // 普通用户其他情况无折扣
        }
    }
}

这种写法的问题显而易见:

  • 代码复杂:复杂的if-else嵌套,难以维护
  • 修改困难:每次改规则都要改代码、重新发布
  • 扩展性差:新增规则类型需要修改核心代码
  • 测试困难:各种规则组合需要大量测试用例

1.2 业务规则变化频繁

电商行业的营销活动变化极快:

  • 节日促销:双11、双12、618等
  • 会员权益:不同等级用户享受不同优惠
  • 限时活动:秒杀、拼团、砍价等
  • 渠道差异:APP、小程序、H5不同渠道的差异化策略

面对这些变化,传统的硬编码方式显然跟不上节奏。

二、Aviator表达式引擎的优势

2.1 什么是Aviator

Aviator是一个高性能、轻量级的Java表达式引擎,专门用于动态求值表达式。它的特点:

  • 高性能:通过编译成Java字节码执行,性能优异
  • 轻量级:依赖包仅450K,核心部分仅70K
  • 功能丰富:支持算术运算、逻辑运算、正则表达式等
  • 安全可靠:不支持赋值语句和外部函数调用,防止安全问题

2.2 Aviator vs 其他规则引擎

特性 Aviator Drools QLExpress Groovy
性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐
体积 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
学习成本 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐
功能丰富度 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
适用场景 简单计算 复杂业务规则 中等复杂度 通用脚本

推荐场景

  • Aviator:简单到中等复杂度的计算场景
  • Drools:复杂业务规则,有图形化编辑器需求
  • QLExpress:需要中文语法的业务场景

三、SpringBoot + Aviator 实战

3.1 项目依赖配置

首先在pom.xml中添加Aviator依赖:

xml 复制代码
<dependency>
    <groupId>com.googlecode.aviator</groupId>
    <artifactId>aviator</artifactId>
    <version>5.3.3</version>
</dependency>

3.2 核心代码实现

创建Aviator规则引擎服务:

java 复制代码
@Component
@Slf4j
public class AviatorRuleEngine {
    
    /**
     * 缓存编译后的表达式,提高执行效率
     */
    private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>();
    
    /**
     * 执行规则表达式
     */
    public Object executeRule(String expression, Map<String, Object> env) {
        try {
            // 检查缓存中是否存在编译后的表达式
            Expression compiledExpression = expressionCache.get(expression);
            if (compiledExpression == null) {
                // 编译表达式并缓存
                compiledExpression = AviatorEvaluator.compile(expression, true);
                expressionCache.put(expression, compiledExpression);
                log.debug("缓存表达式: {}", expression);
            }
            
            // 执行表达式
            Object result = compiledExpression.execute(env);
            log.debug("表达式执行结果: {} = {}", expression, result);
            return result;
        } catch (Exception e) {
            log.error("执行规则表达式失败: {}", expression, e);
            throw new RuntimeException("规则执行失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 验证表达式语法
     */
    public boolean validateExpression(String expression) {
        try {
            AviatorEvaluator.compile(expression, true);
            return true;
        } catch (Exception e) {
            log.error("表达式语法错误: {}", expression, e);
            return false;
        }
    }
}

3.3 营销规则服务实现

java 复制代码
@Service
@Slf4j
public class RuleService {
    
    @Autowired
    private AviatorRuleEngine aviatorRuleEngine;
    
    /**
     * 根据订单金额和用户等级计算折扣
     */
    public Double calculateDiscount(Double orderAmount, String userLevel, String productCategory) {
        Map<String, Object> context = new HashMap<>();
        context.put("orderAmount", orderAmount);
        context.put("userLevel", userLevel);
        context.put("productCategory", productCategory);
        
        // 根据不同用户等级和订单金额计算折扣
        String ruleExpression;
        if ("VIP".equals(userLevel)) {
            // VIP用户:订单金额>1000打8折,否则打9折
            ruleExpression = "orderAmount > 1000 ? 0.8 : 0.9";
        } else if ("GOLD".equals(userLevel)) {
            // 金牌用户:订单金额>500打8.5折,否则打9.5折
            ruleExpression = "orderAmount > 500 ? 0.85 : 0.95";
        } else {
            // 普通用户:订单金额>1000打9折,否则无折扣
            ruleExpression = "orderAmount > 1000 ? 0.9 : 1.0";
        }
        
        Object result = aviatorRuleEngine.executeRule(ruleExpression, context);
        return Double.valueOf(result.toString());
    }
    
    /**
     * 计算满减优惠
     */
    public Double calculateCouponDiscount(Double orderAmount, String couponType) {
        Map<String, Object> context = new HashMap<>();
        context.put("orderAmount", orderAmount);
        context.put("couponType", couponType);
        
        String ruleExpression;
        if ("MANYIJIAN".equals(couponType)) {
            // 满减券:满200减20,满500减50
            ruleExpression = "orderAmount >= 500 ? 50 : (orderAmount >= 200 ? 20 : 0)";
        } else if ("ZHEKOU".equals(couponType)) {
            // 折扣券:满100打9折
            ruleExpression = "orderAmount >= 100 ? orderAmount * 0.1 : 0";
        } else {
            ruleExpression = "0";
        }
        
        Object result = aviatorRuleEngine.executeRule(ruleExpression, context);
        return Double.valueOf(result.toString());
    }
}

3.4 API接口实现

java 复制代码
@RestController
@RequestMapping("/api/marketing")
@Slf4j
public class MarketingRuleController {
    
    @Autowired
    private RuleService ruleService;
    
    /**
     * 计算订单折扣
     */
    @PostMapping("/calculate-discount")
    public ResponseEntity<Map<String, Object>> calculateDiscount(@RequestBody Map<String, Object> request) {
        try {
            Double orderAmount = Double.valueOf(request.get("orderAmount").toString());
            String userLevel = request.get("userLevel").toString();
            String productCategory = request.get("productCategory").toString();
            
            Double discountRate = ruleService.calculateDiscount(orderAmount, userLevel, productCategory);
            
            Map<String, Object> result = new HashMap<>();
            result.put("originalAmount", orderAmount);
            result.put("discountRate", discountRate);
            result.put("discountedAmount", orderAmount * discountRate);
            result.put("savedAmount", orderAmount * (1 - discountRate));
            result.put("userLevel", userLevel);
            
            log.info("订单折扣计算完成: {}", result);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            log.error("计算订单折扣失败", e);
            Map<String, Object> error = new HashMap<>();
            error.put("error", e.getMessage());
            return ResponseEntity.badRequest().body(error);
        }
    }
}

3.5 常用的营销规则表达式

java 复制代码
// 1. 满减规则:满200减20,满500减50
String manjianRule = "orderAmount >= 500 ? 50 : (orderAmount >= 200 ? 20 : 0)";

// 2. 折扣规则:VIP用户打8折,金牌用户打8.5折
String zhekouRule = "userLevel == 'VIP' ? 0.8 : (userLevel == 'GOLD' ? 0.85 : 1.0)";

// 3. 阶梯规则:消费金额越高折扣越大
String jietiRule = "orderAmount >= 1000 ? 0.7 : (orderAmount >= 500 ? 0.8 : (orderAmount >= 200 ? 0.9 : 1.0))";

// 4. 组合规则:多条件判断
String complexRule = "(userLevel == 'VIP' and orderAmount >= 1000) ? 0.7 : " +
                    "(userLevel == 'GOLD' and orderAmount >= 500) ? 0.8 : 1.0";

// 5. 时间规则:特定时间段内有效
String timeRule = "orderTime >= '2023-11-11 00:00:00' and orderTime <= '2023-11-11 23:59:59' ? 0.5 : 1.0";

四、规则中心架构设计

4.1 整体架构

复制代码
前端运营系统 ←→ 规则配置API ←→ 规则引擎服务 ←→ 缓存 ←→ 数据库
                                        ↓
                                   业务服务调用

4.2 规则存储设计

java 复制代码
@Entity
@Table(name = "t_rule")
@Data
public class Rule {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    /**
     * 规则编码
     */
    @Column(unique = true, nullable = false)
    private String ruleCode;
    
    /**
     * 规则名称
     */
    private String ruleName;
    
    /**
     * 规则表达式
     */
    @Lob
    private String ruleExpression;
    
    /**
     * 规则描述
     */
    private String description;
    
    /**
     * 规则类型
     */
    private String ruleType;
    
    /**
     * 是否启用
     */
    private Boolean enabled = true;
    
    /**
     * 版本号
     */
    private Integer version = 1;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime = LocalDateTime.now();
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime = LocalDateTime.now();
}

4.3 规则缓存策略

java 复制代码
@Service
public class CachedRuleService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RuleRepository ruleRepository;
    
    private static final String RULE_CACHE_PREFIX = "rule:expression:";
    
    /**
     * 获取规则表达式(带缓存)
     */
    public String getRuleExpression(String ruleCode) {
        String cacheKey = RULE_CACHE_PREFIX + ruleCode;
        
        // 先从缓存获取
        String expression = (String) redisTemplate.opsForValue().get(cacheKey);
        if (expression != null) {
            return expression;
        }
        
        // 缓存未命中,从数据库获取
        Rule rule = ruleRepository.findByRuleCodeAndEnabledTrue(ruleCode);
        if (rule != null) {
            expression = rule.getRuleExpression();
            // 存入缓存,设置过期时间
            redisTemplate.opsForValue().set(cacheKey, expression, 300, TimeUnit.SECONDS);
            return expression;
        }
        
        return null;
    }
    
    /**
     * 更新规则时清除缓存
     */
    public void updateRule(Rule rule) {
        ruleRepository.save(rule);
        
        // 清除缓存
        String cacheKey = RULE_CACHE_PREFIX + rule.getRuleCode();
        redisTemplate.delete(cacheKey);
    }
}

五、最佳实践建议

5.1 规则设计原则

  1. 单一职责:每个规则只负责一个业务逻辑
  2. 可测试性:规则表达式应该易于单元测试
  3. 可读性:复杂的规则应该拆分成多个简单规则
  4. 性能考虑:避免过于复杂的嵌套表达式

5.2 安全性考虑

java 复制代码
// 1. 表达式验证
public boolean validateRuleExpression(String expression) {
    try {
        // 编译表达式验证语法
        Expression compiled = AviatorEvaluator.compile(expression, true);
        return true;
    } catch (Exception e) {
        log.error("表达式验证失败: {}", expression, e);
        return false;
    }
}

// 2. 沙箱执行(可选)
public Object executeRuleSafely(String expression, Map<String, Object> env) {
    // 设置执行超时时间,防止死循环
    Map<String, Object> envWithTimeout = new HashMap<>(env);
    envWithTimeout.put("max_execution_time", 1000); // 1秒超时
    
    return AviatorEvaluator.execute(expression, envWithTimeout);
}

5.3 监控和日志

java 复制代码
@Service
@Slf4j
public class MonitoredRuleService {
    
    @Autowired
    private MeterRegistry meterRegistry;
    
    public Object executeRuleWithMonitor(String ruleCode, String expression, Map<String, Object> env) {
        Timer.Sample sample = Timer.start(meterRegistry);
        
        try {
            Object result = aviatorRuleEngine.executeRule(expression, env);
            
            // 记录成功指标
            sample.stop(Timer.builder("rule.execution.time")
                .tag("rule_code", ruleCode)
                .tag("result", "success")
                .register(meterRegistry));
                
            log.info("规则执行成功: ruleCode={}, result={}", ruleCode, result);
            return result;
        } catch (Exception e) {
            // 记录失败指标
            sample.stop(Timer.builder("rule.execution.time")
                .tag("rule_code", ruleCode)
                .tag("result", "error")
                .register(meterRegistry));
                
            log.error("规则执行失败: ruleCode={}, expression={}", ruleCode, expression, e);
            throw e;
        }
    }
}

5.4 规则版本管理

java 复制代码
// 规则版本管理
@Entity
@Table(name = "t_rule_version")
@Data
public class RuleVersion {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String ruleCode;
    private String ruleExpression;
    private Integer version;
    private String description;
    private LocalDateTime createTime;
    private String createdBy;
    
    // 是否为当前版本
    private Boolean current = false;
}

六、总结

通过SpringBoot + Aviator的组合,我们可以轻松构建一个灵活的营销规则引擎:

  1. 业务灵活性:运营人员可以动态配置营销规则,无需开发介入
  2. 系统性能:Aviator高性能表达式引擎,满足高并发场景
  3. 维护性:规则与代码分离,降低维护成本
  4. 扩展性:支持复杂的业务规则表达式

这套方案特别适合电商、金融等营销活动频繁变化的业务场景。记住,技术的价值在于解决业务问题,选择合适的工具才能事半功倍!

掌握了这套规则引擎方案,相信你再面对复杂的营销规则时会更加从容不迫,让运营同学也能轻松玩转营销活动!


本文由服务端技术精选原创,转载请注明出处。关注我们,获取更多后端技术干货!

源码下载

相关推荐
陌殇殇11 小时前
001 Spring AI Alibaba框架整合百炼大模型平台 — 快速入门
人工智能·spring boot·ai
言慢行善11 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星11 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟12 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z12 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可12 小时前
Java 中的实现类是什么
java·开发语言
He少年12 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新12 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏49412 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构