SpringBoot + Aviator + 规则中心:轻量级表达式引擎实现营销优惠动态计算
电商大促活动时,营销规则复杂多变,今天满300减50,明天买2送1,后天又变成阶梯式折扣?每次改规则都得改代码、重新发布,简直是开发人员的噩梦!今天就来聊聊如何用SpringBoot + Aviator表达式引擎,搭建一个灵活的营销规则中心,让运营同学也能轻松配置营销规则,再也不用求着开发改代码了!
一、营销规则的痛点
1.1 传统if-else的困境
在没有规则引擎之前,营销优惠计算通常是这样写的:
// 伪代码:传统的营销优惠计算
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依赖:
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.3</version>
</dependency>
3.2 核心代码实现
创建Aviator规则引擎服务:
@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 营销规则服务实现
@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接口实现
@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 常用的营销规则表达式
// 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 规则存储设计
@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 规则缓存策略
@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 规则设计原则
- 单一职责:每个规则只负责一个业务逻辑
- 可测试性:规则表达式应该易于单元测试
- 可读性:复杂的规则应该拆分成多个简单规则
- 性能考虑:避免过于复杂的嵌套表达式
5.2 安全性考虑
// 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 监控和日志
@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 规则版本管理
// 规则版本管理
@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的组合,我们可以轻松构建一个灵活的营销规则引擎:
- 业务灵活性:运营人员可以动态配置营销规则,无需开发介入
- 系统性能:Aviator高性能表达式引擎,满足高并发场景
- 维护性:规则与代码分离,降低维护成本
- 扩展性:支持复杂的业务规则表达式
这套方案特别适合电商、金融等营销活动频繁变化的业务场景。记住,技术的价值在于解决业务问题,选择合适的工具才能事半功倍!
掌握了这套规则引擎方案,相信你再面对复杂的营销规则时会更加从容不迫,让运营同学也能轻松玩转营销活动!
本文由服务端技术精选原创,转载请注明出处。关注我们,获取更多后端技术干货!
标题:SpringBoot + Aviator + 规则中心:轻量级表达式引擎实现营销优惠动态计算
作者:jiangyi
地址:http://jiangyi.space/articles/2026/01/08/1767880888150.html
0 评论