From f440f6b3b48373fe96bc7e6c36a2064d26f7439d Mon Sep 17 00:00:00 2001
From: yongfeng <2283865573@qq.com>
Date: Thu, 23 Jan 2025 23:55:54 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8A=BD=E5=A5=96=E7=AD=96?=
=?UTF-8?q?=E7=95=A5=EF=BC=8C=E5=B0=86=E6=8A=BD=E5=A5=96=E5=88=86=E4=B8=BA?=
=?UTF-8?q?=E6=8A=BD=E5=A5=96=E7=AD=96=E7=95=A5=E6=8A=BD=E5=A5=96=E7=B1=BB?=
=?UTF-8?q?=EF=BC=9A=E6=A6=82=E7=8E=87=E8=A3=85=E9=85=8D=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=E3=80=81=E6=8A=BD=E5=A5=96=E8=B0=83=E5=BA=A6=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=20=E4=BB=A5=E5=8F=8A=20=E6=8A=BD=E5=A5=96=E7=AD=96=E7=95=A5?=
=?UTF-8?q?=E7=AE=97=E6=B3=95=E5=AE=9E=E7=8E=B0=E7=B1=BB=E4=BE=9D=E8=B5=96?=
=?UTF-8?q?=E6=8A=BD=E5=A5=96=E7=AE=97=E6=B3=95=E6=8A=BD=E8=B1=A1=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../mybatis/mapper/strategy_award_mapper.xml | 4 +
.../repository/IStrategyRepository.java | 26 ++-
.../armory/AbstractStrategyAlgorithm.java | 130 +++++++++++
.../armory/StrategyArmoryDispatch.java | 220 +++---------------
.../armory/algorithm/AbstractAlgorithm.java | 31 +++
.../service/armory/algorithm/IAlgorithm.java | 16 ++
.../armory/algorithm/impl/O1Algorithm.java | 55 +++++
.../armory/algorithm/impl/OLogNAlgorithm.java | 158 +++++++++++++
.../persistent/dao/IStrategyAwardDao.java | 2 +-
.../repository/StrategyRepository.java | 40 +++-
.../cn/bugstack/types/common/Constants.java | 1 +
11 files changed, 494 insertions(+), 189 deletions(-)
create mode 100644 bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/AbstractStrategyAlgorithm.java
create mode 100644 bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/AbstractAlgorithm.java
create mode 100644 bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/IAlgorithm.java
create mode 100644 bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/O1Algorithm.java
create mode 100644 bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/OLogNAlgorithm.java
diff --git a/bigmarket-app/src/main/resources/mybatis/mapper/strategy_award_mapper.xml b/bigmarket-app/src/main/resources/mybatis/mapper/strategy_award_mapper.xml
index 192a902..2ba6bec 100644
--- a/bigmarket-app/src/main/resources/mybatis/mapper/strategy_award_mapper.xml
+++ b/bigmarket-app/src/main/resources/mybatis/mapper/strategy_award_mapper.xml
@@ -46,5 +46,9 @@
from strategy_award
where strategy_id = #{strategyId} and award_id = #{awardId}
+
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java
index 616a595..d851ad1 100644
--- a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java
@@ -21,12 +21,14 @@ public interface IStrategyRepository {
List queryStrategyAwardList(Long strategyId);
- void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map strategyAwardSearchRateTable);
+ void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map strategyAwardSearchRateTable);
+ Map getMap(String key);
Integer getStrategyAwardAssemble(String key, Integer rateKey);
int getRateRange(Long strategyId);
+
int getRateRange(String key);
StrategyEntity queryStrategyEntityByStrategyId(Long strategyId);
@@ -142,5 +144,27 @@ public interface IStrategyRepository {
* @return 权重规则
*/
List queryAwardRuleWeight(Long strategyId);
+ /**
+ * 查询有效活动的奖品配置
+ *
+ * @return 奖品配置列表
+ */
+ List queryOpenActivityStrategyAwardList();
+
+ /**
+ * 存储抽奖策略对应的Bean算法
+ *
+ * @param key 策略ID
+ * @param beanName 策略对象名称
+ */
+ void cacheStrategyArmoryAlgorithm(String key, String beanName);
+
+ /**
+ * 获取存储抽奖策略对应的Bean算法
+ *
+ * @param key 策略ID
+ * @return 策略对象名称
+ */
+ String queryStrategyArmoryAlgorithmFromCache(String key);
}
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/AbstractStrategyAlgorithm.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/AbstractStrategyAlgorithm.java
new file mode 100644
index 0000000..a25d09a
--- /dev/null
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/AbstractStrategyAlgorithm.java
@@ -0,0 +1,130 @@
+package cn.bugstack.domain.strategy.service.armory;
+
+import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
+import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
+import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity;
+import cn.bugstack.domain.strategy.repository.IStrategyRepository;
+import cn.bugstack.types.common.Constants;
+import cn.bugstack.types.enums.ResponseCode;
+import cn.bugstack.types.exception.AppException;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.security.SecureRandom;
+import java.util.*;
+/**
+ * @ClassName: AbstractStrageyAlgorithm
+ * @Description: 抽奖策略算法抽象类
+ * @Author: zhaoyongfeng
+ * @Date: 2025/1/23 22:22
+ */
+public abstract class AbstractStrategyAlgorithm implements IStrategyArmory, IStrategyDispatch {
+ @Resource
+ protected IStrategyRepository repository;
+ protected final SecureRandom secureRandom = new SecureRandom();
+
+ @Override
+ public boolean assembleLotteryStrategyByActivityId(Long activityId){
+ Long strategyId = repository.queryStrategyIdByActivityId(activityId);
+ return assembleLotteryStrategy(strategyId);
+ }
+ @Override
+ public boolean assembleLotteryStrategy(Long strategyId){
+ // 1.查询策略配置
+ List strategyAwardEntities = repository.queryStrategyAwardList(strategyId);
+ // 2. 缓存奖品库存【用于decr扣减库存使用】
+ for (StrategyAwardEntity strategyAward : strategyAwardEntities) {
+ Integer awardId = strategyAward.getAwardId();
+ Integer awardCountSurplus = strategyAward.getAwardCountSurplus();
+ cacheStrategyAwardCount(strategyId,awardId,awardCountSurplus);
+ }
+ // 3.1 默认装配配置【全量抽奖概率】
+ armoryAlgorithm(String.valueOf(strategyId),strategyAwardEntities);
+ // 3.2 权重策略配置 - 适用于 rule_weight 权重规则配置【4000:102,103,104,105 5000:102,103,104,105,106,107 6000:102,103,104,105,106,107,108,109】
+ StrategyEntity strategyEntity = repository.queryStrategyEntityByStrategyId(strategyId);
+ String ruleWeight = strategyEntity.getRuleWeight();
+ // 为空抛出异常,设定异常
+ if(null == ruleWeight) return true;
+ StrategyRuleEntity strategyRuleEntity = repository.queryStrategyRule(strategyId, ruleWeight);
+ // 业务异常,策略规则中 rule_weight 权重规则已适用但未配置
+ if(null == strategyRuleEntity){
+ throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
+ }
+ Map> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValues();
+ for (String key : ruleWeightValueMap.keySet()) {
+ List ruleWeightValues = ruleWeightValueMap.get(key);
+ ArrayList strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
+ strategyAwardEntitiesClone.removeIf(entity -> !ruleWeightValues.contains(entity.getAwardId()));
+ armoryAlgorithm(String.valueOf(strategyId).concat(Constants.UNDERLINE).concat(key), strategyAwardEntitiesClone);
+ }
+ return true;
+ }
+
+ /*
+ * @description: 装配算法
+ * @param: key 为策略ID、权重ID、strategyAwardEntities 对应的奖品概率
+ * @return: void
+ * @author zhaoyongfeng
+ * @date: 2025/1/23 23:02
+ */
+ protected abstract void armoryAlgorithm(String key, List strategyAwardEntities);
+ /*
+ * @description: 缓存奖品库存到Redis
+ * @param: strategyId 策略ID awardId 奖品ID awardCount 奖品库存
+ * @author zhaoyongfeng
+ * @date: 2025/1/23 23:04
+ */
+ private void cacheStrategyAwardCount(Long strategyId, Integer awardId, Integer awardCount){
+ String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
+ repository.cacheStrategyAwardCount(cacheKey, awardCount);
+ }
+ protected abstract Integer dispatchAlgorithm(String key);
+ @Override
+ public Integer getRandomAwardId(Long strategyId) {
+ return dispatchAlgorithm(String.valueOf(strategyId));
+ }
+ @Override
+ public Integer getRandomAwardId(Long strategyId, String ruleWeightValue) {
+ String key = String.valueOf(strategyId).concat(Constants.UNDERLINE).concat(ruleWeightValue);
+ return getRandomAwardId(key);
+ }
+ @Override
+ public Integer getRandomAwardId(String key) {
+ return dispatchAlgorithm(key);
+ }
+
+ @Override
+ public Boolean subtractionAwardStock(Long strategyId, Integer awardId, Date endDateTime) {
+ String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
+ return repository.subtractionAwardStock(cacheKey, endDateTime);
+ }
+ // 概率最小值
+ protected BigDecimal minAwardRate(List strategyAwardEntities){
+ return strategyAwardEntities.stream()
+ .map(StrategyAwardEntity::getAwardRate)
+ .min(BigDecimal::compareTo)
+ .orElse(BigDecimal.ZERO);
+ }
+ // 概率范围值,百分位、千分位、万分位
+ protected double convert(double min) {
+ if (0 == min) return 1D;
+
+ String minStr = String.valueOf(min);
+
+ // 小数点前
+ String beginVale = minStr.substring(0, minStr.indexOf("."));
+ int beginLength = 0;
+ if (Double.parseDouble(beginVale) > 0) {
+ beginLength = minStr.substring(0, minStr.indexOf(".")).length();
+ }
+
+ // 小数点后
+ String endValue = minStr.substring(minStr.indexOf(".") + 1);
+ int endLength = 0;
+ if (Double.parseDouble(endValue) > 0) {
+ endLength = minStr.substring(minStr.indexOf(".") + 1).length();
+ }
+
+ return Math.pow(10, beginLength + endLength);
+ }
+}
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java
index 9aaf28d..daf7a06 100644
--- a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java
@@ -1,207 +1,59 @@
package cn.bugstack.domain.strategy.service.armory;
import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
-import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
-import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity;
-import cn.bugstack.domain.strategy.repository.IStrategyRepository;
-import cn.bugstack.types.common.Constants;
-import cn.bugstack.types.enums.ResponseCode;
-import cn.bugstack.types.exception.AppException;
+import cn.bugstack.domain.strategy.service.armory.algorithm.AbstractAlgorithm;
+import cn.bugstack.domain.strategy.service.armory.algorithm.IAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
-import javax.annotation.Resource;
import java.math.BigDecimal;
-import java.security.SecureRandom;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
/**
- * @author Fuzhengwei bugstack.cn @小傅哥
- * @description 策略装配库(兵工厂),负责初始化策略计算
- * @create 2023-12-23 10:02
+ * @ClassName: StrategyArmoryDispatch
+ * @Description:
+ * @Author: zhaoyongfeng
+ * @Date: 2025/1/23 23:26
*/
@Slf4j
-@Service
-public class StrategyArmoryDispatch implements IStrategyArmory, IStrategyDispatch {
+@Service("strategyArmoryDispatch")
+public class StrategyArmoryDispatch extends AbstractStrategyAlgorithm{
+ // 抽奖策略算法
+ private final Map algorithmMap;
+ // 抽奖算法阈值,在多少范围内开始选择不同选择
+ private final Integer ALGORITHM_THRESHOLD_VALUE = 10000;
- @Resource
- private IStrategyRepository repository;
-
- private final SecureRandom secureRandom = new SecureRandom();
-
- @Override
- public boolean assembleLotteryStrategyByActivityId(Long activityId) {
- Long strategyId = repository.queryStrategyIdByActivityId(activityId);
- return assembleLotteryStrategy(strategyId);
+ public StrategyArmoryDispatch(Map algorithmMap) {
+ this.algorithmMap = algorithmMap;
}
- @Override
- public boolean assembleLotteryStrategy(Long strategyId) {
- // 1. 查询策略配置
- List strategyAwardEntities = repository.queryStrategyAwardList(strategyId);
-
- // 2 缓存奖品库存【用于decr扣减库存使用】
- for (StrategyAwardEntity strategyAward : strategyAwardEntities) {
- Integer awardId = strategyAward.getAwardId();
- Integer awardCount = strategyAward.getAwardCountSurplus();
- cacheStrategyAwardCount(strategyId, awardId, awardCount);
- }
-
- // 3.1 默认装配配置【全量抽奖概率】
- assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities);
-
- // 3.2 权重策略配置 - 适用于 rule_weight 权重规则配置【4000:102,103,104,105 5000:102,103,104,105,106,107 6000:102,103,104,105,106,107,108,109】
- StrategyEntity strategyEntity = repository.queryStrategyEntityByStrategyId(strategyId);
- String ruleWeight = strategyEntity.getRuleWeight();
- if (null == ruleWeight) return true;
-
- StrategyRuleEntity strategyRuleEntity = repository.queryStrategyRule(strategyId, ruleWeight);
- // 业务异常,策略规则中 rule_weight 权重规则已适用但未配置
- if (null == strategyRuleEntity) {
- throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
- }
-
- Map> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValues();
- for (String key : ruleWeightValueMap.keySet()) {
- List ruleWeightValues = ruleWeightValueMap.get(key);
- ArrayList strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
- strategyAwardEntitiesClone.removeIf(entity -> !ruleWeightValues.contains(entity.getAwardId()));
- assembleLotteryStrategy(String.valueOf(strategyId).concat(Constants.UNDERLINE).concat(key), strategyAwardEntitiesClone);
- }
-
- return true;
- }
-
- /**
- * 计算公式;
- * 1. 找到范围内最小的概率值,比如 0.1、0.02、0.003,需要找到的值是 0.003
- * 2. 基于1找到的最小值,0.003 就可以计算出百分比、千分比的整数值。这里就是1000
- * 3. 那么「概率 * 1000」分别占比100个、20个、3个,总计是123个
- * 4. 后续的抽奖就用123作为随机数的范围值,生成的值100个都是0.1概率的奖品、20个是概率0.02的奖品、最后是3个是0.003的奖品。
- */
- private void assembleLotteryStrategy(String key, List strategyAwardEntities) {
- // 1. 获取最小概率值
- BigDecimal minAwardRate = strategyAwardEntities.stream()
- .map(StrategyAwardEntity::getAwardRate)
- .min(BigDecimal::compareTo)
- .orElse(BigDecimal.ZERO);
-
- // 2. 循环计算找到概率范围值
- BigDecimal rateRange = BigDecimal.valueOf(convert(minAwardRate.doubleValue()));
-
- // 3. 生成策略奖品概率查找表「这里指需要在list集合中,存放上对应的奖品占位即可,占位越多等于概率越高」
- List strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
- for (StrategyAwardEntity strategyAward : strategyAwardEntities) {
- Integer awardId = strategyAward.getAwardId();
- BigDecimal awardRate = strategyAward.getAwardRate();
- // 计算出每个概率值需要存放到查找表的数量,循环填充
- for (int i = 0; i < rateRange.multiply(awardRate).intValue(); i++) {
- strategyAwardSearchRateTables.add(awardId);
- }
- }
-
- // 4. 对存储的奖品进行乱序操作
- Collections.shuffle(strategyAwardSearchRateTables);
-
- // 5. 生成出Map集合,key值,对应的就是后续的概率值。通过概率来获得对应的奖品ID
- Map shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>();
- for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
- shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
- }
-
- // 6. 存放到 Redis
- repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable);
- }
-
- /**
- * 转换计算,只根据小数位来计算。如【0.01返回100】、【0.009返回1000】、【0.0018返回10000】
- */
- private double convertOld1(double min) {
- if (0 == min) return 1D;
-
- double current = min;
- double max = 1;
- while (current % 1 != 0) {
- current = current * 10;
- max = max * 10;
- }
- return max;
- }
-
- private double convertOld2(double min) {
- if (min == 0) return 1D;
-
- String minStr = Double.toString(min);
- int decimalPlaces = 0;
-
- int decimalPointIndex = minStr.indexOf('.');
- if (decimalPointIndex != -1) {
- decimalPlaces = minStr.length() - decimalPointIndex - 1;
- }
-
- return Math.pow(10, decimalPlaces);
- }
-
- private double convert(double min) {
- if (0 == min) return 1D;
-
- String minStr = String.valueOf(min);
-
- // 小数点前
- String beginVale = minStr.substring(0, minStr.indexOf("."));
- int beginLength = 0;
- if (Double.parseDouble(beginVale) > 0) {
- beginLength = minStr.substring(0, minStr.indexOf(".")).length();
- }
-
- // 小数点后
- String endValue = minStr.substring(minStr.indexOf(".") + 1);
- int endLength = 0;
- if (Double.parseDouble(endValue) > 0) {
- endLength = minStr.substring(minStr.indexOf(".") + 1).length();
- }
-
- return Math.pow(10, beginLength + endLength);
- }
-
- /**
- * 缓存奖品库存到Redis
- *
- * @param strategyId 策略ID
- * @param awardId 奖品ID
- * @param awardCount 奖品库存
- */
- private void cacheStrategyAwardCount(Long strategyId, Integer awardId, Integer awardCount) {
- String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
- repository.cacheStrategyAwardCount(cacheKey, awardCount);
- }
@Override
- public Integer getRandomAwardId(Long strategyId) {
- // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
- int rateRange = repository.getRateRange(strategyId);
- // 通过生成的随机值,获取概率值奖品查找表的结果
- return repository.getStrategyAwardAssemble(String.valueOf(strategyId), secureRandom.nextInt(rateRange));
- }
-
- @Override
- public Integer getRandomAwardId(Long strategyId, String ruleWeightValue) {
- String key = String.valueOf(strategyId).concat(Constants.UNDERLINE).concat(ruleWeightValue);
- return getRandomAwardId(key);
+ protected void armoryAlgorithm(String key, List strategyAwardEntities) {
+ // 1. 概率最小值
+ BigDecimal minAwardRate = minAwardRate(strategyAwardEntities);
+ // 2. 概率范围值
+ double rateRange = convert(minAwardRate.doubleValue());
+ // 3. 根据概率值范围选择算法
+ if (rateRange <= ALGORITHM_THRESHOLD_VALUE) {
+ IAlgorithm o1Algorithm = algorithmMap.get(AbstractAlgorithm.Algorithm.O1.getKey());
+ o1Algorithm.armoryAlgorithm(key, strategyAwardEntities, new BigDecimal(rateRange));
+ repository.cacheStrategyArmoryAlgorithm(key, AbstractAlgorithm.Algorithm.O1.getKey());
+ } else {
+ IAlgorithm oLogNAlgorithm = algorithmMap.get(AbstractAlgorithm.Algorithm.OLogN.getKey());
+ oLogNAlgorithm.armoryAlgorithm(key, strategyAwardEntities, new BigDecimal(rateRange));
+ repository.cacheStrategyArmoryAlgorithm(key, AbstractAlgorithm.Algorithm.OLogN.getKey());
+ }
}
@Override
- public Integer getRandomAwardId(String key) {
- // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
- int rateRange = repository.getRateRange(key);
- // 通过生成的随机值,获取概率值奖品查找表的结果
- return repository.getStrategyAwardAssemble(key, secureRandom.nextInt(rateRange));
+ protected Integer dispatchAlgorithm(String key) {
+ String beanName = repository.queryStrategyArmoryAlgorithmFromCache(key);
+ if (null == beanName) throw new RuntimeException("key " + key + " beanName is " + beanName);
+ IAlgorithm algorithm = algorithmMap.get(beanName);
+ return algorithm.dispatchAlgorithm(key);
}
- @Override
- public Boolean subtractionAwardStock(Long strategyId, Integer awardId, Date endDateTime) {
- String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
- return repository.subtractionAwardStock(cacheKey, endDateTime);
- }
}
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/AbstractAlgorithm.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/AbstractAlgorithm.java
new file mode 100644
index 0000000..d10267b
--- /dev/null
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/AbstractAlgorithm.java
@@ -0,0 +1,31 @@
+package cn.bugstack.domain.strategy.service.armory.algorithm;
+
+import cn.bugstack.domain.strategy.repository.IStrategyRepository;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.annotation.Resource;
+import java.security.SecureRandom;
+
+/**
+ * @ClassName: AbstractAlgorithm
+ * @Description: 抽奖算法抽象类
+ * @Author: zhaoyongfeng
+ * @Date: 2025/1/23 23:32
+ */
+
+public abstract class AbstractAlgorithm implements IAlgorithm{
+ @Resource
+ protected IStrategyRepository repository;
+
+ protected final SecureRandom secureRandom = new SecureRandom();
+ @Getter
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public enum Algorithm {
+ O1("o1Algorithm"), OLogN("oLogNAlgorithm");
+
+ private String key;
+ }
+}
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/IAlgorithm.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/IAlgorithm.java
new file mode 100644
index 0000000..3cd9208
--- /dev/null
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/IAlgorithm.java
@@ -0,0 +1,16 @@
+package cn.bugstack.domain.strategy.service.armory.algorithm;
+
+import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
+
+import java.math.BigDecimal;
+import java.util.List;
+/**
+ * @author
+ * @description 抽奖算法
+ * @create 2024-08-24 13:02
+ */
+public interface IAlgorithm {
+ void armoryAlgorithm(String key, List strategyAwardEntities, BigDecimal rateRange);
+
+ Integer dispatchAlgorithm(String key);
+}
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/O1Algorithm.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/O1Algorithm.java
new file mode 100644
index 0000000..d742f31
--- /dev/null
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/O1Algorithm.java
@@ -0,0 +1,55 @@
+package cn.bugstack.domain.strategy.service.armory.algorithm.impl;
+
+import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
+import cn.bugstack.domain.strategy.service.armory.algorithm.AbstractAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @ClassName: O1Algorithm
+ * @Description: o(1) 时间复杂度算法,将抽奖概率分配到 Redis Map 结构中,1个概率对应的奖品就是一个Key,抽奖时直接通过 get 获取。
+ * @Author: zhaoyongfeng
+ * @Date: 2025/1/23 23:34
+ */
+@Slf4j
+@Component("o1Algorithm")
+public class O1Algorithm extends AbstractAlgorithm {
+ @Override
+ public void armoryAlgorithm(String key, List strategyAwardEntities, BigDecimal rateRange) {
+ log.info("抽奖算法 O(1) 装配 key:{}", key);
+ // 1. 生成策略奖品概率查找表「这里指需要在list集合中,存放上对应的奖品占位即可,占位越多等于概率越高」
+ List strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
+ for (StrategyAwardEntity strategyAward : strategyAwardEntities) {
+ Integer awardId = strategyAward.getAwardId();
+ BigDecimal awardRate = strategyAward.getAwardRate();
+ // 计算出每个概率值需要存放到查找表的数量,循环填充
+ for (int i = 0; i < rateRange.multiply(awardRate).intValue(); i++) {
+ strategyAwardSearchRateTables.add(awardId);
+ }
+ }
+
+ // 2. 对存储的奖品进行乱序操作
+ Collections.shuffle(strategyAwardSearchRateTables);
+
+ // 3. 生成出Map集合,key值,对应的就是后续的概率值。通过概率来获得对应的奖品ID
+ Map shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>();
+ for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
+ shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
+ }
+
+ // 4. 存放到 Redis
+ repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable);
+ }
+
+ @Override
+ public Integer dispatchAlgorithm(String key) {
+ log.info("抽奖算法 O(1) 抽奖计算 key:{}", key);
+ // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。
+ int rateRange = repository.getRateRange(key);
+ // 通过生成的随机值,获取概率值奖品查找表的结果
+ return repository.getStrategyAwardAssemble(key, secureRandom.nextInt(rateRange));
+ }
+}
diff --git a/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/OLogNAlgorithm.java b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/OLogNAlgorithm.java
new file mode 100644
index 0000000..da3ffde
--- /dev/null
+++ b/bigmarket-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/algorithm/impl/OLogNAlgorithm.java
@@ -0,0 +1,158 @@
+package cn.bugstack.domain.strategy.service.armory.algorithm.impl;
+
+import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
+import cn.bugstack.domain.strategy.service.armory.algorithm.AbstractAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName: OLogNAlgorithm
+ * @Description: 循环、二分、多线程,查找抽奖算法
+ * @Author: zhaoyongfeng
+ * @Date: 2025/1/23 23:37
+ */
+@Slf4j
+@Component("oLogNAlgorithm")
+public class OLogNAlgorithm extends AbstractAlgorithm {
+
+ @Resource
+ private ThreadPoolExecutor threadPoolExecutor;
+
+ /**
+ * 预热活动概率值
+ * 如概率值为;3、4、2、9,存储为 [1~3]、[4~7]、[8~9]、[10~18],抽奖时,for循环匹配。
+ *
+ * @param key 为策略ID、权重ID
+ * @param strategyAwardEntities 对应的奖品概率
+ */
+ @Override
+ public void armoryAlgorithm(String key, List strategyAwardEntities, BigDecimal rateRange) {
+ log.info("抽奖算法 OLog(n) 装配 key:{}", key);
+ int from = 1;
+ int to = 0;
+
+ Map