抽奖规则过滤-责任链
2024年11月18日大约 3 分钟
抽奖规则过滤-责任链
功能概述
在我们的流程设计中,用户执行抽奖时会判断是否已经超过N积分,如果超过N积分则可以在限定范围内进行抽奖(权重)。同时如果用户是黑名单范围的羊毛党用户,则只返回固定的奖品ID(黑名单)。
为了刺激用户消费,我们又设计了部分奖品需要抽取一定次数后才能解锁,所以在抽奖中又要进行判断这个奖品是否有抽一定次数后解锁的规则。
以及包括抽奖后的一些后置规则判断。
所以我们在设计抽奖的系统的时候,要时刻记住松耦合。就像 Spring 源码中拆解一个 Bean 对象为不同阶段一样,我们这里也把抽奖拆解为不同时间段的过程,以用于可以在各个节点添加所需的功能流程。这样的设计也就更加便于后续的功能迭代了。
流程设计
最开始在流程实现中,我们设计出抽奖的前中后置过程,并在每个阶段设计对应的操作规则。运用了策略模式、工厂模式、模板模式,来完成抽奖流程的定义和抽奖过程前、中、后,规则的过滤处理。
但在我们规则处理的流程中,因为前置规则的校验含带了抽奖的行为处理,这样绑定到规则流程实现中会显得有些臃肿,让规则负责的事情变得更多。所以我后续使用责任链模式进行优化完善,让整个代码流程变得更加清晰。
责任链模式核心实现
节点接口
public interface ILogicChainArmory {
ILogicChain next();
ILogicChain appendNext(ILogicChain next);
}
public interface ILogicChain extends ILogicChainArmory{
Integer logic(String userId, Long strategyId);
}
部分节点实现
@Slf4j
@Component("rule_blacklist")
public class BackListLogicChain extends AbstractLogicChain {
@Resource
private IStrategyRepository repository;
@Override
public Integer logic(String userId, Long strategyId) {
log.info("抽奖责任链-黑名单开始 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
// 查询规则值配置
String ruleValue = repository.queryStrategyRuleValue(strategyId, ruleModel());
String[] splitRuleValue = ruleValue.split(Constants.COLON);
Integer awardId = Integer.parseInt(splitRuleValue[0]);
// 黑名单抽奖判断
String[] userBlackIds = splitRuleValue[1].split(Constants.SPLIT);
for (String userBlackId : userBlackIds) {
if (userId.equals(userBlackId)) {
log.info("抽奖责任链-黑名单接管 userId: {} strategyId: {} ruleModel: {} awardId: {}", userId, strategyId, ruleModel(), awardId);
return awardId;
}
}
// 过滤其他责任链
log.info("抽奖责任链-黑名单放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
return next().logic(userId, strategyId);
}
@Override
protected String ruleModel() {
return "rule_blacklist";
}
}
责任链工厂装配
因为所有的规则节点都被我注册成了Bean,所以这里我使用Spring的@Service注解,并使用构造函数装配Bean。 这样就会把所有ILogicChain的实现类装配进Map使用
@Service
public class DefaultChainFactory {
private final Map<String, ILogicChain> logicChainGroup;
protected IStrategyRepository repository;
public DefaultChainFactory(Map<String, ILogicChain> logicChainGroup, IStrategyRepository repository) {
this.logicChainGroup = logicChainGroup;
this.repository = repository;
}
/**
* 通过策略ID,构建责任链
*
* @param strategyId 策略ID
* @return LogicChain
*/
public ILogicChain openLogicChain(Long strategyId) {
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
String[] ruleModels = strategy.ruleModels();
// 如果未配置策略规则,则只装填一个默认责任链
if (null == ruleModels || 0 == ruleModels.length) return logicChainGroup.get("default");
// 按照配置顺序装填用户配置的责任链;rule_blacklist、rule_weight 「注意此数据从Redis缓存中获取,如果更新库表,记得在测试阶段手动处理缓存」
ILogicChain logicChain = logicChainGroup.get(ruleModels[0]);
ILogicChain current = logicChain;
for (int i = 1; i < ruleModels.length; i++) {
ILogicChain nextChain = logicChainGroup.get(ruleModels[i]);
current = current.appendNext(nextChain);
}
// 责任链的最后装填默认责任链
current.appendNext(logicChainGroup.get("default"));
return logicChain;
}
}