|
@@ -0,0 +1,116 @@
|
|
|
|
+package com.seamew.lottery.domain.strategy.service.draw;
|
|
|
|
+
|
|
|
|
+import com.seamew.lottery.common.Constants;
|
|
|
|
+import com.seamew.lottery.domain.strategy.model.aggregates.StrategyRich;
|
|
|
|
+import com.seamew.lottery.domain.strategy.model.req.DrawReq;
|
|
|
|
+import com.seamew.lottery.domain.strategy.model.res.DrawResult;
|
|
|
|
+import com.seamew.lottery.domain.strategy.model.vo.AwardRateInfo;
|
|
|
|
+import com.seamew.lottery.domain.strategy.model.vo.DrawAwardInfo;
|
|
|
|
+import com.seamew.lottery.domain.strategy.service.algorithm.IDrawAlgorithm;
|
|
|
|
+import com.seamew.lottery.infrastructure.po.Award;
|
|
|
|
+import com.seamew.lottery.infrastructure.po.Strategy;
|
|
|
|
+import com.seamew.lottery.infrastructure.po.StrategyDetail;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.List;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @Author: seamew
|
|
|
|
+ * @Title: AbstractDrawBase
|
|
|
|
+ * @CreateTime: 2023年02月14日 18:09:00
|
|
|
|
+ * @Description: 定义抽象抽奖过程,模板模式
|
|
|
|
+ * @Version: 1.0
|
|
|
|
+ */
|
|
|
|
+@Slf4j
|
|
|
|
+public abstract class AbstractDrawBase extends DrawStrategySupport implements IDrawExec {
|
|
|
|
+ @Override
|
|
|
|
+ public DrawResult doDrawExec(DrawReq req) {
|
|
|
|
+ // 1. 获取抽奖策略
|
|
|
|
+ StrategyRich strategyRich = queryStrategyRich(req.getStrategyId());
|
|
|
|
+ Strategy strategy = strategyRich.getStrategy();
|
|
|
|
+
|
|
|
|
+ // 2. 校验抽奖策略是否已经初始化到内存
|
|
|
|
+ checkAndInitRateData(req.getStrategyId(), strategy.getStrategyMode(), strategyRich.getStrategyDetailList());
|
|
|
|
+
|
|
|
|
+ // 3. 获取不在抽奖范围内的列表,包括:奖品库存为空、风控策略、临时调整等
|
|
|
|
+ List<String> excludeAwardIds = queryExcludeAwardIds(req.getStrategyId());
|
|
|
|
+
|
|
|
|
+ // 4. 执行抽奖算法
|
|
|
|
+ String awardId = drawAlgorithm(req.getStrategyId(), drawAlgorithmGroup.get(strategy.getStrategyMode()), excludeAwardIds);
|
|
|
|
+
|
|
|
|
+ // 5. 包装中奖结果
|
|
|
|
+ return buildDrawResult(req.getUId(), req.getStrategyId(), awardId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取不在抽奖范围内的列表,包括:奖品库存为空、风控策略、临时调整等,这类数据是含有业务逻辑的,所以需要由具体地实现方决定
|
|
|
|
+ *
|
|
|
|
+ * @param strategyId 策略ID
|
|
|
|
+ * @return 排除的奖品ID集合
|
|
|
|
+ */
|
|
|
|
+ protected abstract List<String> queryExcludeAwardIds(Long strategyId);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 执行抽奖算法
|
|
|
|
+ *
|
|
|
|
+ * @param strategyId 策略ID
|
|
|
|
+ * @param drawAlgorithm 抽奖算法模型
|
|
|
|
+ * @param excludeAwardIds 排除的抽奖ID集合
|
|
|
|
+ * @return 中奖奖品ID
|
|
|
|
+ */
|
|
|
|
+ protected abstract String drawAlgorithm(Long strategyId, IDrawAlgorithm drawAlgorithm, List<String> excludeAwardIds);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 校验抽奖策略是否已经初始化到内存
|
|
|
|
+ *
|
|
|
|
+ * @param strategyId 抽奖策略ID
|
|
|
|
+ * @param strategyMode 抽奖策略模式
|
|
|
|
+ * @param strategyDetailList 抽奖策略详情
|
|
|
|
+ */
|
|
|
|
+ private void checkAndInitRateData(Long strategyId, Integer strategyMode, List<StrategyDetail> strategyDetailList) {
|
|
|
|
+ // 非单项概率,不必存入缓存
|
|
|
|
+ // 暂时注释,有BUG
|
|
|
|
+ // if (!Constants.StrategyMode.SINGLE.getCode().equals(strategyMode)) {
|
|
|
|
+ // return;
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ IDrawAlgorithm drawAlgorithm = drawAlgorithmGroup.get(strategyMode);
|
|
|
|
+
|
|
|
|
+ // 已初始化过的数据,不必重复初始化
|
|
|
|
+ if (drawAlgorithm.isExistRateTuple(strategyId)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 解析并初始化中奖概率数据到散列表
|
|
|
|
+ List<AwardRateInfo> awardRateInfoList = new ArrayList<>(strategyDetailList.size());
|
|
|
|
+ for (StrategyDetail strategyDetail : strategyDetailList) {
|
|
|
|
+ awardRateInfoList.add(new AwardRateInfo(strategyDetail.getAwardId(), strategyDetail.getAwardRate()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ drawAlgorithm.initRateTuple(strategyId, awardRateInfoList);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 包装抽奖结果
|
|
|
|
+ *
|
|
|
|
+ * @param uId 用户ID
|
|
|
|
+ * @param strategyId 策略ID
|
|
|
|
+ * @param awardId 奖品ID,null 情况:并发抽奖情况下,库存临界值1 -> 0,会有用户中奖结果为 null
|
|
|
|
+ * @return 中奖结果
|
|
|
|
+ */
|
|
|
|
+ private DrawResult buildDrawResult(String uId, Long strategyId, String awardId) {
|
|
|
|
+ if (null == awardId) {
|
|
|
|
+ log.info("执行策略抽奖完成【未中奖】,用户:{} 策略ID:{}", uId, strategyId);
|
|
|
|
+ return new DrawResult(uId, strategyId, Constants.DrawState.FAIL.getCode());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Award award = queryAwardInfoByAwardId(awardId);
|
|
|
|
+ DrawAwardInfo drawAwardInfo = new DrawAwardInfo(award.getAwardId(), award.getAwardName());
|
|
|
|
+ log.info("执行策略抽奖完成【已中奖】,用户:{} 策略ID:{} 奖品ID:{} 奖品名称:{}", uId, strategyId, awardId, award.getAwardName());
|
|
|
|
+
|
|
|
|
+ return new DrawResult(uId, strategyId, Constants.DrawState.SUCCESS.getCode(), drawAwardInfo);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|