前言
作为一名后端开发,你是否遇到过这样的场景:
- 运营同学说:"双11活动规则临时调整,满300减60改成满200减50,能马上上线吗?"
- 产品经理说:"风控策略需要微调一下,这个规则能不能今晚就生效?"
- 老板说:"竞品降价了,我们的VIP折扣也要调整,最好现在就能改!"
每次遇到这种情况,我们都要:修改代码 → 重新打包 → 发布部署 → 重启服务。这个流程短则半小时,长则几个小时,错过最佳时机不说,还要承担发布风险。
今天,我们来聊聊如何用动态规则引擎彻底解决这个痛点!
业务痛点:规则变更的"老大难"问题
传统方式的困境
在传统的业务系统中,业务规则通常硬编码在业务逻辑中:
java
// 传统的硬编码方式
public BigDecimal calculateVipDiscount(String userLevel, BigDecimal price) {
if ("GOLD".equals(userLevel)) {
return price.multiply(new BigDecimal("0.8")); // 8折
} else if ("SILVER".equals(userLevel)) {
return price.multiply(new BigDecimal("0.9")); // 9折
}
return price; // 无折扣
}
这种方式存在诸多问题:
- 响应慢:规则变更需要完整的开发流程
- 风险高:每次发布都可能影响整个系统
- 成本大:需要开发、测试、运维多方配合
- 不灵活:无法快速响应市场变化
真实的业务场景
电商行业:
- 每逢大促活动,优惠规则频繁调整
- A/B测试需要不同的定价策略
- 竞品价格变动需要快速跟进
金融行业:
- 风控模型需要根据市场情况实时调整
- 利率政策变化需要快速响应
- 审核规则需要根据业务发展动态优化
内容平台:
- 推荐算法需要不断优化
- 内容审核规则需要及时更新
- 用户等级体系需要灵活调整
解决方案:动态规则引擎
核心思路
动态规则引擎的本质是将业务规则 与业务代码分离:
-
业务代码:负责流程控制和数据处理
-
业务规则:以脚本形式独立存储,运行时动态执行
传统方式:业务逻辑 = 流程控制 + 业务规则(硬编码)
动态规则:业务逻辑 = 流程控制 + 业务规则(脚本化)
技术选型
本演示DEMO我选择了以下技术栈:
- 规则引擎:QLExpress - 阿里开源
- 后端框架:Spring Boot - 快速开发,生态完善
- 前端技术:HTML5 + TailwindCSS
核心实现:手把手搭建动态规则引擎
1. 项目结构设计
csharp
springboot-dynamic-rule/
├── entity/
│ ├── RuleScript.java # 规则实体
│ ├── OrderProcessResult.java # 订单处理结果
│ └── RuleExecuteResponse.java # 规则执行响应
├── service/
│ ├── DynamicRuleEngine.java # 规则引擎核心
│ └── OrderService.java # 业务服务
├── controller/
│ ├── RuleController.java # 规则管理API
│ └── OrderController.java # 业务API
└── resources/
└── static/
├── index.html # 规则管理页面
└── business.html # 业务演示页面
2. 规则引擎核心实现
java
@Slf4j
@Service
public class DynamicRuleEngine {
// 内存存储规则,实际项目可使用数据库
private final Map<String, RuleScript> ruleCache = new ConcurrentHashMap<>();
private final ExpressRunner expressRunner = new ExpressRunner();
@PostConstruct
public void init() {
initDefaultRules();
}
private void initDefaultRules() {
// VIP折扣规则
addRule(new RuleScript("vip_discount",
"if (userLevel == \"GOLD\") { return price * 0.8; } " +
"else if (userLevel == \"SILVER\") { return price * 0.9; } " +
"else { return price; }",
"VIP用户折扣规则"));
// 满减活动规则
addRule(new RuleScript("full_reduction",
"if (totalAmount >= 200) { return totalAmount - 50; } " +
"else if (totalAmount >= 100) { return totalAmount - 20; } " +
"else { return totalAmount; }",
"满减活动规则"));
}
public RuleExecuteResponse executeRule(String ruleName, Map<String, Object> params) {
try {
RuleScript rule = ruleCache.get(ruleName);
if (rule == null || !rule.isEnabled()) {
return RuleExecuteResponse.error("规则不存在或已禁用: " + ruleName);
}
DefaultContext<String, Object> context = new DefaultContext<>();
if (params != null) {
params.forEach(context::put);
}
Object result = expressRunner.execute(rule.getScript(), context, null, true, false);
log.info("执行规则: {}, 结果: {}", ruleName, result);
return RuleExecuteResponse.success(result);
} catch (Exception e) {
log.error("执行规则失败: {}", ruleName, e);
return RuleExecuteResponse.error("执行失败: " + e.getMessage());
}
}
}
3. 业务服务集成
java
@Service
@RequiredArgsConstructor
public class OrderService {
private final DynamicRuleEngine ruleEngine;
public OrderProcessResult processOrder(Order order) {
List<OrderProcessResult.ProcessStep> steps = new ArrayList<>();
BigDecimal currentAmount = order.getOriginalAmount();
// 1. 应用VIP折扣规则
BigDecimal discountedAmount = applyVipDiscount(order, currentAmount, steps);
// 2. 应用满减规则
BigDecimal finalAmount = applyFullReduction(order, discountedAmount, steps);
// 3. 计算积分奖励
Integer points = calculatePoints(finalAmount, order);
return new OrderProcessResult(/* 构建返回结果 */);
}
private BigDecimal applyVipDiscount(Order order, BigDecimal currentAmount,
List<OrderProcessResult.ProcessStep> steps) {
Map<String, Object> params = new HashMap<>();
params.put("userLevel", order.getUserLevel());
params.put("price", currentAmount);
RuleExecuteResponse response = ruleEngine.executeRule("vip_discount", params);
if (response.isSuccess()) {
BigDecimal result = new BigDecimal(response.getResult().toString());
BigDecimal reduction = currentAmount.subtract(result);
// 记录处理步骤
steps.add(new OrderProcessResult.ProcessStep(
"VIP折扣",
"根据用户等级 " + order.getUserLevel() + " 应用折扣",
currentAmount, result, reduction, "vip_discount"
));
return result;
}
return currentAmount;
}
}
4. REST API接口
java
@RestController
@RequestMapping("/api/rules")
@RequiredArgsConstructor
public class RuleController {
private final DynamicRuleEngine dynamicRuleEngine;
@GetMapping
public ResponseEntity<List<RuleScript>> getAllRules() {
return ResponseEntity.ok(dynamicRuleEngine.getAllRules());
}
@PostMapping
public ResponseEntity<String> addRule(@RequestBody RuleScript ruleScript) {
try {
dynamicRuleEngine.addRule(ruleScript);
return ResponseEntity.ok("规则添加成功");
} catch (Exception e) {
return ResponseEntity.badRequest().body("添加失败: " + e.getMessage());
}
}
@PutMapping("/{ruleName}")
public ResponseEntity<String> updateRule(@PathVariable String ruleName,
@RequestBody RuleScript ruleScript) {
try {
dynamicRuleEngine.updateRule(ruleName, ruleScript);
return ResponseEntity.ok("规则更新成功");
} catch (Exception e) {
return ResponseEntity.badRequest().body("更新失败: " + e.getMessage());
}
}
@PostMapping("/execute/{ruleName}")
public ResponseEntity<RuleExecuteResponse> executeRule(
@PathVariable String ruleName,
@RequestBody Map<String, Object> params) {
RuleExecuteResponse response = dynamicRuleEngine.executeRule(ruleName, params);
return ResponseEntity.ok(response);
}
}
操作界面
为了方便大家快速直观的体验规则引擎,DEMO提供了一套简洁的操作界面,包含规则配置与业务场景模拟。
规则管理页面
主要功能:
- 规则列表:展示所有规则及状态
- 在线编辑:支持规则脚本的在线修改
- 实时测试:规则修改后立即测试效果
- 状态控制:一键启用/禁用规则
业务演示页面
主要功能:
- 订单模拟器:输入订单信息,查看处理结果
- 详细步骤:清晰展示每个规则的执行过程
- 处理历史:记录历史处理结果
关键前端代码:
javascript
// 动态执行规则
async function executeRule(ruleName, params) {
try {
const response = await fetch(`/api/rules/execute/${ruleName}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
});
const result = await response.json();
if (result.success) {
displayResult(`执行成功!结果: ${result.result}`);
} else {
displayResult(`执行失败: ${result.errorMessage}`);
}
} catch (error) {
displayResult(`请求失败: ${error.message}`);
}
}
// 渲染处理步骤
function displayProcessSteps(processSteps) {
const stepsHtml = processSteps.map((step, index) => {
const beforeAmount = parseFloat(step.beforeAmount);
const afterAmount = parseFloat(step.afterAmount);
const reduction = parseFloat(step.reduction);
return `
<div class="process-step">
<div class="step-number">${index + 1}</div>
<div class="step-content">
<h4>${step.stepName}</h4>
<p>${step.description}</p>
</div>
<div class="step-result">
<div class="amount-change">¥${beforeAmount} → ¥${afterAmount}</div>
<div class="reduction">节省 ¥${reduction.toFixed(2)}</div>
</div>
</div>
`;
}).join('');
document.getElementById('processingSteps').innerHTML = stepsHtml;
}
应用场景实战
场景1:电商平台动态定价
需求:根据用户等级和订单金额,动态计算优惠价格
传统方式:
java
// 硬编码,修改需要重新发布
if ("GOLD".equals(userLevel) && amount >= 100) {
return amount * 0.8;
}
动态规则方式:
javascript
// 规则脚本,可随时修改
if (userLevel == "GOLD" && totalAmount >= 100) {
return totalAmount * 0.8;
} else if (userLevel == "SILVER" && totalAmount >= 50) {
return totalAmount * 0.9;
} else {
return totalAmount;
}
效果对比:
- 响应速度:从小时级降低到分钟级
- 发布风险:从系统级降低到规则级
- 操作门槛:从开发人员扩展到业务人员
场景2:风控策略实时调整
需求:根据实时风险情况,动态调整审核策略
规则示例:
javascript
// 风控评分规则
score = baseScore;
if (userAge < 18) { score = score - 20; }
if (creditLevel == "HIGH") { score = score + 30; }
if (monthlyIncome > 10000) { score = score + 15; }
return score >= 60 ? "PASS" : "REJECT";
业务价值:
- 快速响应:市场风险变化时立即调整
- 精细化控制:不同场景使用不同策略
- A/B测试:同时运行多套策略进行对比
场景3:营销活动灵活配置
需求:支持复杂的营销活动规则
规则示例:
javascript
// 双11活动规则
if (activityType == "DOUBLE11") {
if (totalAmount >= 1000) { return totalAmount - 200; }
else if (totalAmount >= 500) { return totalAmount - 80; }
else if (totalAmount >= 200) { return totalAmount - 30; }
}
return totalAmount;
运营效果:
- 活动预热:提前配置规则,定时生效
- 实时调整:根据活动效果实时优化
- 快速止损:发现问题立即回滚规则
总结
通过将业务规则从代码中剥离出来,我们实现了:
- 业务敏捷:规则变更从小时级提升到分钟级
- 系统稳定:减少了系统发布频次和风险
- 团队协作:业务人员可以直接参与规则配置
- 成本降低:减少了开发、测试、运维的工作量