淘宝返利软件后端架构中的防刷单风控规则引擎设计(Drools 应用)
大家好,我是 微赚淘客系统3.0 的研发者省赚客!
在高并发返利场景下,恶意用户通过脚本、虚拟设备、多账号等方式批量下单套取佣金,严重侵蚀平台利润。为应对复杂多变的刷单行为,微赚淘客系统3.0 引入基于 Drools 规则引擎 的动态风控体系,实现规则热更新、多维度判定与毫秒级拦截。
一、风控对象建模
首先定义风控上下文实体,作为规则判断依据:
java
package juwatech.cn.risk.model;
import java.time.LocalDateTime;
import java.util.List;
public class OrderRiskContext {
private Long userId;
private String deviceId;
private String ip;
private Long orderId;
private Long orderAmount; // 订单金额(分)
private LocalDateTime createTime;
private List<String> recentOrderIps; // 近1小时订单IP列表
private int sameDeviceOrderCount24h; // 24小时内同设备订单数
private boolean isNewUser; // 是否注册<7天
private boolean isHighRiskIp; // 是否高危IP(来自情报库)
private boolean blocked; // 是否被拦截(输出字段)
// getters & setters
public void block() { this.blocked = true; }
}
二、Drools 规则文件示例
规则文件 fraud-rules.drl 存放于 resources/rules/ 目录,支持动态加载:
drl
package juwatech.cn.rules
import juwatech.cn.risk.model.OrderRiskContext
// 规则1:新用户大额订单
rule "NewUserLargeOrder"
when
$ctx: OrderRiskContext(
isNewUser == true,
orderAmount > 50000, // >500元
!blocked
)
then
System.out.println("Blocked: New user large order, userId=" + $ctx.getUserId());
$ctx.block();
end
// 规则2:同一设备高频下单
rule "HighFrequencySameDevice"
when
$ctx: OrderRiskContext(
sameDeviceOrderCount24h >= 10,
!blocked
)
then
System.out.println("Blocked: High frequency on device=" + $ctx.getDeviceId());
$ctx.block();
end
// 规则3:IP异常跳转(1小时内多个省份)
rule "MultiProvinceIpJump"
when
$ctx: OrderRiskContext(
recentOrderIps != null,
recentOrderIps.size > 3,
isHighRiskIp == false,
!blocked
)
eval( hasDifferentProvinces($ctx.recentOrderIps) )
then
System.out.println("Blocked: Multi-province IP jump for userId=" + $ctx.getUserId());
$ctx.block();
end
其中 hasDifferentProvinces 为自定义函数,需在 DRL 中声明或通过 Java 调用:
java
package juwatech.cn.risk.util;
import juwatech.cn.geo.IpGeoService;
public class RiskFunctions {
public static boolean hasDifferentProvinces(List<String> ips) {
IpGeoService geo = new IpGeoService();
long provinceCount = ips.stream()
.map(geo::getProvince)
.filter(p -> p != null && !p.isEmpty())
.distinct()
.count();
return provinceCount > 2;
}
}
并在 DRL 文件顶部导入:
drl
import function juwatech.cn.risk.util.RiskFunctions.hasDifferentProvinces
三、Drools 引擎初始化与调用
通过 Spring Boot 集成 Drools:
java
package juwatech.cn.risk.config;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DroolsConfig {
private static final String RULES_PATH = "rules/fraud-rules.drl";
@Bean
public KieContainer kieContainer() {
KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
kfs.write(RULES_PATH, getResource(RULES_PATH));
KieBuilder kb = ks.newKieBuilder(kfs);
kb.buildAll();
return ks.newKieContainer(ks.getRepository().getDefaultReleaseId());
}
private String getResource(String path) {
// 从 classpath 或远程配置中心加载规则内容
return getClass().getClassLoader().getResourceAsStream(path).readAllBytes();
}
@Bean
public KieSession kieSession(KieContainer kieContainer) {
return kieContainer.newKieSession();
}
}
四、风控服务调用流程
在订单创建前插入风控检查:
java
package juwatech.cn.risk.service;
import juwatech.cn.risk.model.OrderRiskContext;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Service;
@Service
public class FraudDetectionService {
private final KieSession kieSession;
private final RiskDataEnricher enricher;
public FraudDetectionService(KieSession kieSession, RiskDataEnricher enricher) {
this.kieSession = kieSession;
this.enricher = enricher;
}
public boolean isFraudulent(Long userId, Long orderId) {
OrderRiskContext ctx = new OrderRiskContext();
ctx.setUserId(userId);
ctx.setOrderId(orderId);
// 填充基础字段
enricher.enrich(ctx); // 补全IP、设备、历史行为等
kieSession.insert(ctx);
kieSession.fireAllRules();
kieSession.dispose(); // 注意:生产环境应使用有状态会话池或无状态KieBase
return ctx.isBlocked();
}
}
其中 RiskDataEnricher 负责从 Redis、MySQL、风控情报库中聚合数据:
java
package juwatech.cn.risk.service;
import juwatech.cn.risk.model.OrderRiskContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class RiskDataEnricher {
private final RedisTemplate<String, Object> redisTemplate;
private final IpRiskService ipRiskService;
public void enrich(OrderRiskContext ctx) {
String deviceKey = "device:orders:" + ctx.getDeviceId();
Integer count = (Integer) redisTemplate.opsForHash().get(deviceKey, "count_24h");
ctx.setSameDeviceOrderCount24h(count != null ? count : 0);
ctx.setRecentOrderIps(getRecentIpsFromRedis(ctx.getUserId()));
ctx.setIsHighRiskIp(ipRiskService.isHighRisk(ctx.getIp()));
ctx.setIsNewUser(isNewUser(ctx.getUserId()));
}
// 省略具体实现
}
五、规则热更新机制
为避免重启服务,我们监听 Nacos 或 Apollo 配置变更,动态重载 KieContainer:
java
@EventListener
public void onRuleUpdate(RuleConfigChangeEvent event) {
String newDrl = event.getNewContent();
// 写入临时文件或直接构建 KieModule
KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
kfs.write("rules/fraud-rules.drl", newDrl);
KieBuilder kb = ks.newKieBuilder(kfs).buildAll();
if (kb.getResults().hasMessages(org.kie.api.builder.Message.Level.ERROR)) {
log.error("Rule compile error: {}", kb.getResults().getMessages());
return;
}
KieContainer newContainer = ks.newKieContainer(ks.getRepository().getDefaultReleaseId());
// 原子替换全局引用
this.kieContainerRef.set(newContainer);
}
六、性能与扩展性
- 单次规则执行耗时 < 5ms(P99);
- 支持并行处理,每个线程使用独立 KieSession;
- 规则版本可灰度发布,按用户ID哈希分流。
通过 Drools 规则引擎,风控策略从硬编码解耦,运营人员可配合技术团队快速上线新规则,有效应对新型刷单手段。
本文著作权归 微赚淘客系统3.0 研发团队,转载请注明出处!