文章目录
开篇介绍:将按照渐进的顺序逐步编写一个完整的抽奖项目。
责任链模式
通过责任链模式我们可以轻松的实现链式自动前置规则过滤。
例如在抽奖之前我需要先执行黑名单的过滤,黑名单过滤之后还需要执行抽奖权重规则的校验,都放行之后才去继续执行我随机抽奖的步骤,那么黑名单,权重就可以按照链的方式配置好来进行测试。
责任链类结构
ILogicChain接口
这个接口定义了最基本的责任链需要包含的功能;
public interface ILogicChain {
// 执行链中逻辑判断的功能
DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId);
// 为当前链添加下一个链
ILogicChain appendNext(ILogicChain next);
// 获得下一个链
ILogicChain next();
}
BlackListLogicChain 黑名单
通过实现上面的抽象类 这个抽象类中实现了上面的接口 变为责任链中的一环
为什么这里要多一层抽象类包装呢?可以看到我们的ILogicChain中只有logic是每个链独立的其他方法都可以共有因此可以用抽象类包装一层把把公共的方法封装好
public abstract class AbstractLogicChain implements ILogicChain {
private ILogicChain next;
@Override
public ILogicChain appendNext(ILogicChain next) {
this.next = next;
return next;
}
@Override
public ILogicChain next() {
return next;
}
protected abstract String ruleModel();
}
@Slf4j
@Component("rule_blacklist")
public class BlackListLogicChain extends AbstractLogicChain {
@Resource
private IStrategyRepository repository;
// 校验的逻辑
@Override
public DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId) {
String ruleValue = repository.queryStrategyRuleValue(strategyId, null, ruleModel());
String[] splitRuleValue = ruleValue.split(Constants.COLON);
// 拿到奖品id
Integer awardId = Integer.parseInt(splitRuleValue[0]);
// 拿到黑名单的用户id
String[] userBlackIds = splitRuleValue[1].split(Constants.SPLIT);
for (String userBlackId : userBlackIds) {
if (userId.equals(userBlackId)) {
log.info("抽奖责任链-黑名单接管");
return DefaultChainFactory.StrategyAwardVO.builder()
.awardId(awardId)
.logicModel(ruleModel())
.build();
}
}
log.info("抽奖责任链-黑名单放行");
return next().logic(userId,strategyId);
}
@Override
protected String ruleModel() {
return "rule_blacklist";
}
}
RuleWeightLogicChain 权重规则的责任链
@Slf4j
@Component("rule_weight")
public class RuleWeightLogicChain extends AbstractLogicChain {
@Resource
private IStrategyRepository repository;
@Resource
private IStrategyDispatch strategyDispatch;
private Long userScore = 5500L;
@Override
public DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId) {
String ruleValue = repository.queryStrategyRuleValue(strategyId,null,ruleModel());
Map<Long, String> analyticalValue = getAnalyticalValue(ruleValue);
if(null == analyticalValue || analyticalValue.isEmpty()){
return null;
}
// 转换keys 和 排序
List<Long> analyticalSortedKeys = new ArrayList<>(analyticalValue.keySet());
Collections.sort(analyticalSortedKeys);
Long nextValue = analyticalSortedKeys.stream()
.filter(key -> userScore >= key)
.findFirst()
.orElse(null);
if(null != nextValue){
Integer awardId = strategyDispatch.getRandomAwardId(strategyId, analyticalValue.get(nextValue));
log.info("抽奖责任链-权重接管");
return DefaultChainFactory.StrategyAwardVO.builder()
.awardId(awardId)
.logicModel(ruleModel())
.build();
}
// 放行走下一条规则.
return next().logic(userId,strategyId);
}
private Map<Long,String> getAnalyticalValue(String ruleValue){
String[] ruleValueGroups = ruleValue.split(Constants.SPACE);
Map<Long,String> ruleValueMap = new HashMap<>();
for (String ruleValueGroup : ruleValueGroups) {
if(ruleValueGroup == null || ruleValueGroup.isEmpty()) return ruleValueMap;
String[] parts = ruleValueGroup.split(Constants.COLON);
if(parts.length != 2){
throw new IllegalArgumentException("rule_weight invalid input" + ruleValueGroup);
}
// 存入积分值以及对应的规则 4000: 4000:101,102,103
ruleValueMap.put(Long.parseLong(parts[0]),ruleValueGroup);
}
return ruleValueMap;
}
@Override
protected String ruleModel() {
return "rule_weight";
}
}
默认规则 default
@Slf4j
@Component("default")
public class DefaultLogicChain extends AbstractLogicChain {
@Resource
protected IStrategyDispatch strategyDispatch;
@Override
public DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId) {
// 抽奖返回.
Integer awardId = strategyDispatch.getRandomAwardId(strategyId);
log.info("抽奖责任链 默认处理 awardId: {}",awardId);
return DefaultChainFactory.StrategyAwardVO.builder()
.awardId(awardId)
.logicModel(ruleModel())
.build();
}
@Override
protected String ruleModel() {
return "rule_default";
}
}
默认规则也是抽奖前置责任链过滤的最后一环如果能走到这里证明前面两个均放行,执行抽奖的流程返回一个抽到的奖品id。
DefaultChainFactory 责任链工厂
为什么这里需要工厂呢,有了责任链也就是继承了ILogicChain的3个实现类我们可以通过工厂的方式将他们给串联起来,同时可以将工厂用@Service,在构造方法参数里通过Map的形式自动注入全部的责任链模块方便操作
private final Map<String, ILogicChain> logicChainGroup;
public DefaultChainFactory(Map<String, ILogicChain> logicChainGroup, IStrategyRepository repository) {
this.logicChainGroup = logicChainGroup;
this.repository = repository;
}
openLogicChain方法
public ILogicChain openLogicChain(Long strategyId){
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
// 10002L 对应 ruleModels: rule_blacklist,rule_weight
String[] ruleModels = strategy.ruleModels();
if(null == ruleModels || 0 == ruleModels.length) return logicChainGroup.get("default");
// 这里因为logicChainGroup已经将实现了接口的方法全部注入了因此可以直接使用。
ILogicChain chain = logicChainGroup.get(ruleModels[0]);
ILogicChain current = chain;
// 添加责任链
for(int i=1;i<ruleModels.length;i++){
ILogicChain nextChain = logicChainGroup.get(ruleModels[i]);
current = current.appendNext(nextChain);
}
// 添加默认值 也就是链尾
current.appendNext(logicChainGroup.get("default"));
return chain;
}
StrategyAwardVO 工厂里的静态内部类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class StrategyAwardVO {
/** 抽奖奖品ID - 内部流转使用 */
private Integer awardId;
/** */
private String logicModel;
}
在每个链进行logic时最后会返回一个StrategyAwardVO这个工厂类中封装的奖品对象,
通过对象的字段来判断这个类是被接管了还是放行走的default逻辑。
具体使用
@Override
public DefaultChainFactory.StrategyAwardVO raffleLogicChain(String userId, Long strategyId) {
ILogicChain chain = chainFactory.openLogicChain(strategyId);
DefaultChainFactory.StrategyAwardVO strategyAwardVO = chain.logic(userId, strategyId);
return strategyAwardVO;
}