Loterty抽奖项目【策略装配01】

开篇介绍:将按照渐进的顺序逐步编写一个完整的抽奖项目。

策略装配

策略库表设计

strategy 策略表信息

CREATE TABLE `strategy` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `strategy_id` bigint(8) NOT NULL COMMENT '抽奖策略ID',
  `strategy_desc` varchar(128) NOT NULL COMMENT '抽奖策略描述',
  `rule_models` varchar(256) NOT NULL COMMENT '''规则模型,rule配置的模型同步到此表,便于使用'',',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_strategy_id` (`strategy_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

数据库中体现
首先根据抽奖策略,将策略对应的数据装配到redis中,分别包括策略对应的全部奖品,以及奖品的库存,同时还包括权重策略装配(这里指的是抽奖时需要积分,权重策略规定,如果当前用户积分满4000则必中奖品的id)
库表设计如下:

strategy_award表
CREATE TABLE `strategy_award` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `strategy_id` bigint(8) NOT NULL COMMENT '抽奖策略ID',
  `award_id` int(8) NOT NULL COMMENT '抽奖奖品ID - 内部流转使用',
  `award_title` varchar(128) NOT NULL COMMENT '抽奖奖品标题',
  `award_subtitle` varchar(128) DEFAULT NULL COMMENT '抽奖奖品副标题',
  `award_count` int(8) NOT NULL DEFAULT '0' COMMENT '奖品库存总量',
  `award_count_surplus` int(8) NOT NULL DEFAULT '0' COMMENT '奖品库存剩余',
  `award_rate` decimal(6,4) NOT NULL COMMENT '奖品中奖概率',
  `rule_models` varchar(256) DEFAULT NULL COMMENT '规则模型,rule配置的模型同步到此表,便于使用',
  `sort` int(2) NOT NULL DEFAULT '0' COMMENT '排序',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  KEY `idx_strategy_id_award_id` (`strategy_id`,`award_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;

表数据

strategy_rule表
CREATE TABLE `strategy_rule` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `strategy_id` int(8) NOT NULL COMMENT '抽奖策略ID',
  `award_id` int(8) DEFAULT NULL COMMENT '抽奖奖品ID【规则类型为策略,则不需要奖品ID】',
  `rule_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '抽象规则类型;1-策略规则、2-奖品规则',
  `rule_model` varchar(16) NOT NULL COMMENT '抽奖规则类型【rule_random - 随机值计算、rule_lock - 抽奖几次后解锁、rule_luck_award - 幸运奖(兜底奖品)】',
  `rule_value` varchar(256) NOT NULL COMMENT '抽奖规则比值',
  `rule_desc` varchar(128) NOT NULL COMMENT '抽奖规则描述',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_strategy_id_award_id` (`strategy_id`,`award_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4;

表信息

策略装配

StrategyArmory编写

该类实现了IStrategyArmory接口编写重写了assembleLotteryStrategy(Long strategyId) 装配对应的策略id的奖品数据以及奖品库存以权重策略到redis中。

奖品数据装配:
private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities) {
	// 1. 获取最小概率值
	BigDecimal minAwardRate = strategyAwardEntities.stream()
	        .map(StrategyAwardEntity::getAwardRate)
	        .min(BigDecimal::compareTo)
	        .orElse(BigDecimal.ZERO);
	
	// 2. 获取概率值总和
	BigDecimal totalAwardRate = strategyAwardEntities.stream()
	        .map(StrategyAwardEntity::getAwardRate)
	        .reduce(BigDecimal.ZERO, BigDecimal::add);
	
	// 3. 用 1 % 0.0001 获得概率范围,百分位、千分位、万分位
	BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING);
	
	// 4. 生成策略奖品概率查找表「这里指需要在list集合中,存放上对应的奖品占位即可,占位越多等于概率越高」
	List<Integer> 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).setScale(0, RoundingMode.CEILING).intValue(); i++) {
	        strategyAwardSearchRateTables.add(awardId);
	    }
	}
	
	// 5. 对存储的奖品进行乱序操作
	Collections.shuffle(strategyAwardSearchRateTables);
	
	// 6. 生成出Map集合,key值,对应的就是后续的概率值。通过概率来获得对应的奖品ID
	Map<Integer, Integer> shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>();
	for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
	    shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
	}
	
	// 7. 存放到 Redis
	repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable);
}
奖品库存装配
/ 缓存奖品的库存
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);
}
权重规则装配

和奖品装配类似通过计算好的奖品数组将符合权重规则的奖品单独复制成一个数组然后装配到redis中。

// 查询策略的规则
StrategyRuleEntity strategyRuleEntity = repository.queryStrategyRule(strategyId, ruleWeight);
if (null == strategyRuleEntity) {
   throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
}
Map<String, List<Integer>> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValues();
Set<String> keys = ruleWeightValueMap.keySet();
// key是规则策略的积分id:4000
for (String key : keys) {
   // 拿到4000对应的奖品id: 101 102 103
   List<Integer> ruleWeightValues = ruleWeightValueMap.get(key);
   ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
   strategyAwardEntitiesClone.removeIf(entity -> !ruleWeightValues.contains(entity.getAwardId()));
   assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key), strategyAwardEntitiesClone);
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,Java幸运抽奖项目可以按照以下步骤实现: 1. 定义奖品类,包括奖品名称、奖品数量、中奖概率等属性。 2. 定义用户类,包括用户ID、用户名、中奖状态等属性。 3. 创建一个用户列表,用于存储所有参与抽奖的用户。 4. 根据奖品数量和中奖概率,随机生成每个奖品的中奖数量。 5. 编写抽奖方法,随机从用户列表中选择一个用户,并根据中奖概率判断该用户是否中奖,如果中奖,则随机选择一个奖品进行发放。 6. 循环执行抽奖方法,直到所有奖品都被发放完毕。 以下是一个简单的Java幸运抽奖项目示例代码: ```java import java.util.ArrayList; import java.util.List; import java.util.Random; public class LuckyDraw { // 奖品列表 private List<Prize> prizes = new ArrayList<>(); // 用户列表 private List<User> users = new ArrayList<>(); // 随机数生成器 private Random random = new Random(); // 添加奖品 public void addPrize(Prize prize) { prizes.add(prize); } // 添加用户 public void addUser(User user) { users.add(user); } // 抽奖方法 public void draw() { // 计算每个奖品的中奖数量 for (Prize prize : prizes) { prize.setWinningCount(random.nextInt(prize.getTotalCount() / 2) + 1); } // 循环抽奖,直到所有奖品都被发放完毕 while (!prizes.isEmpty()) { // 随机选择一个用户 int index = random.nextInt(users.size()); User user = users.get(index); // 判断该用户是否已经中过奖 if (user.isWinning()) { continue; } // 随机选择一个奖品 index = random.nextInt(prizes.size()); Prize prize = prizes.get(index); // 判断该奖品是否还有剩余 if (prize.getWinningCount() <= 0) { prizes.remove(index); continue; } // 根据中奖概率判断该用户是否中奖 if (random.nextDouble() <= prize.getWinningProbability()) { user.setWinning(true); prize.setWinningCount(prize.getWinningCount() - 1); System.out.println(user.getName() + "中了" + prize.getName() + "!"); } } } public static void main(String[] args) { // 创建幸运抽奖对象 LuckyDraw luckyDraw = new LuckyDraw(); // 添加奖品 luckyDraw.addPrize(new Prize("一等奖", 10, 0.01)); luckyDraw.addPrize(new Prize("二等奖", 20, 0.05)); luckyDraw.addPrize(new Prize("三等奖", 50, 0.1)); // 添加用户 for (int i = 1; i <= 1000; i++) { luckyDraw.addUser(new User(i, "用户" + i)); } // 抽奖 luckyDraw.draw(); } } // 奖品类 class Prize { private String name; // 奖品名称 private int totalCount; // 奖品总数量 private int winningCount; // 中奖数量 private double winningProbability; // 中奖概率 public Prize(String name, int totalCount, double winningProbability) { this.name = name; this.totalCount = totalCount; this.winningProbability = winningProbability; } public String getName() { return name; } public int getTotalCount() { return totalCount; } public int getWinningCount() { return winningCount; } public void setWinningCount(int winningCount) { this.winningCount = winningCount; } public double getWinningProbability() { return winningProbability; } } // 用户类 class User { private int id; // 用户ID private String name; // 用户名 private boolean winning; // 是否中奖 public User(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; } public boolean isWinning() { return winning; } public void setWinning(boolean winning) { this.winning = winning; } } ``` 以上代码仅供参考,具体实现可以根据实际需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值