抽奖规则过滤-决策树
2024年11月20日大约 3 分钟
抽奖规则过滤-决策树
功能概述
在之前的过滤规则中,我们使用责任链模式来实现,对于抽奖策略的前置规则过滤是顺序一条链的,有一个成功就可以返回。比如;黑名单抽奖、权重人群抽奖、默认抽奖,总之它只能有一种情况,所以这样的流程是适合责任链的。
但对于抽奖中到抽奖后的规则,它是一个树形的规则过滤每次过滤规则后根据结果不同会有不同的流程,比如抽奖中该奖品是需要一定次数解锁的,就要过滤用户是否满足要求,不满足就可以发别的幸运奖,满足要求再过滤库存,足够的话就正常发奖,不成功的话就走兜底流程。抽奖后是否需要发放优惠券、积分、实物奖品等。所以单独的责任链是不能满足的。如果是拆分开抽奖中规则和抽奖后规则分阶段处理,中间单独写逻辑处理库存操作。那么是可以实现的。不过后续的规则开发仍需要在代码上改造。
流程设计
所以在规则过滤上,我将前置规则这种单一流程的链路交给责任链,将抽奖中到抽奖后的多分支树形规则过滤交给决策树实现。
核心实现
对于每个树节点,都要实现定义好的节点接口
public interface ILogicTreeNode {
DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId);
}
具体实现拿库存扣减节点举例
@Override
public DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId, String ruleValue) {
log.info("规则过滤-库存扣减 userId:{} strategyId:{} awardId:{}", userId, strategyId, awardId);
// 扣减库存
Boolean status = strategyDispatch.subtractionAwardStock(strategyId, awardId);
// true;库存扣减成功,TAKE_OVER 规则节点接管,返回奖品ID,奖品规则配置
if (status) {
log.info("规则过滤-库存扣减-成功 userId:{} strategyId:{} awardId:{}", userId, strategyId, awardId);
// 写入延迟队列,延迟消费更新数据库记录。
strategyRepository.awardStockConsumeSendQueue(StrategyAwardStockKeyVO.builder()
.strategyId(strategyId)
.awardId(awardId)
.build());
return DefaultTreeFactory.TreeActionEntity.builder()
.ruleLogicCheckType(RuleLogicCheckTypeVO.TAKE_OVER)
.strategyAwardVO(DefaultTreeFactory.StrategyAwardVO.builder()
.awardId(awardId)
.awardRuleValue(ruleValue)
.build())
.build();
}
// 如果库存不足,则直接返回放行
log.warn("规则过滤-库存扣减-告警,库存不足。userId:{} strategyId:{} awardId:{}", userId, strategyId, awardId);
return DefaultTreeFactory.TreeActionEntity.builder()
.ruleLogicCheckType(RuleLogicCheckTypeVO.ALLOW)
.build();
}
执行引擎,具体使用时会通过工厂构建树,再通过工厂调用引擎使用
public class DecisionTreeEngine implements IDecisionTreeEngine {
private final Map<String, ILogicTreeNode> logicTreeNodeGroup;
private final RuleTreeVO ruleTreeVO;
public DecisionTreeEngine(Map<String, ILogicTreeNode> logicTreeNodeGroup, RuleTreeVO ruleTreeVO) {
this.logicTreeNodeGroup = logicTreeNodeGroup;
this.ruleTreeVO = ruleTreeVO;
}
@Override
public DefaultTreeFactory.StrategyAwardData process(String userId, Long strategyId, Integer awardId) {
DefaultTreeFactory.StrategyAwardData strategyAwardData = null;
// 获取基础信息
String nextNode = ruleTreeVO.getTreeRootRuleNode();
Map<String, RuleTreeNodeVO> treeNodeMap = ruleTreeVO.getTreeNodeMap();
// 获取起始节点「根节点记录了第一个要执行的规则」
RuleTreeNodeVO ruleTreeNode = treeNodeMap.get(nextNode);
while (null != nextNode) {
// 获取决策节点
ILogicTreeNode logicTreeNode = logicTreeNodeGroup.get(ruleTreeNode.getRuleKey());
// 决策节点计算
DefaultTreeFactory.TreeActionEntity logicEntity = logicTreeNode.logic(userId, strategyId, awardId);
RuleLogicCheckTypeVO ruleLogicCheckTypeVO = logicEntity.getRuleLogicCheckType();
strategyAwardData = logicEntity.getStrategyAwardData();
log.info("决策树引擎【{}】treeId:{} node:{} code:{}", ruleTreeVO.getTreeName(), ruleTreeVO.getTreeId(), nextNode, ruleLogicCheckTypeVO.getCode());
// 获取下个节点
nextNode = nextNode(ruleLogicCheckTypeVO.getCode(), ruleTreeNode.getTreeNodeLineVOList());
ruleTreeNode = treeNodeMap.get(nextNode);
}
// 返回最终结果
return strategyAwardData;
}
// ... 省略部分代码
}