按设置的概率实时抽奖-库存扣减(后台管理配置)
上次讲了保证概率的,这次我们加上库存。顺便再加一个活动。
场景:
用户参加某个活动,完成活动后获得抽奖资格,可实时抽奖,根据实际库存情况返回中奖结果。
表结构参考
拼拼凑凑几个表:
- 活动表
- 活动详情表
- 奖池表
- 奖品表
以上几个表之间都两两关联,又零零散散几个表 - 用户获奖资格记录
- 用户抽奖记录
- 用户活动记录
具体的表结构?没有的哈 ,自己的作业自己做
奖品配置步骤
1.配置奖池
2.配置奖品信息,关联到奖池
用户抽奖步骤
1.完成任务获得抽奖资格
2.按配置的概率初步得到中奖结果
这一步怎么实现在上一篇提过了
(幸运的是 三位用户在这个流程中都中奖了,不幸的事儿还在后头(* ̄︶ ̄)
)
3.中奖用户扣减对应库存
现在我们可以看到,场上选手2 和选手3来到了赛点,究竟这个1元红包会花落谁家呢,请各位观众拭目以待!
战况直播:
用户为了争抢锁,大打出手!战况非常激烈
最终用户2获得了粉锁的欢心,让我们来恭喜
这个B这位用户
那么这个用户3怎么处理呢?
这个就看我们具体的需求了,你可以让他免为其难收下其他奖品(比如汤臣一品),或者狠心一点,把他算作未中奖(男人嘛,区区汤臣一品有什么是吧?)
可以看到为了保证在高并发的时候,超卖的情况,我们使用了锁。具体怎么用,来,进来,我告诉你
//中奖后查找对应的奖品信息
Prize priz = prizes.stream().filter(prize -> resultPrizeId.equals(prize.getId())).findFirst().get();
//扣减库存
ValueOperations vlo = redisTemplate.opsForValue();
Boolean absent = vlo.setIfAbsent("LOCK_PRIZE_" + resultPrizeId, 1);
try {
if (absent) {
//TODO 抢占锁失败,扣减抽奖次数,记录用户未中奖信息
// log.info("当前锁被占用 LOCK_PRIZE_" + resultPrizeId);
return "锁被占用";
} else {
//TODO 减少库存
//TODO 成功扣减库存后,变更用户抽奖次数,记录用户中奖信息
//TODO 如果库存为0,则返回未中奖
return prizeDto.getName();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
redisTemplate.delete("LOCK_PRIZE_" + resultPrizeId);
// log.info("释放锁 LOCK_PRIZE_" + resultPrizeId);
}
}
随便创建几个用户和线程,模拟一下场景
public String result(@PathVariable String id, @RequestParam("times") int times) {
Map<String, String> map = new HashMap<>();
Runnable drawTask = () -> {
String draw = null;
Random random = new Random();
int i = random.nextInt();
draw = lotteryService.draw(id, String.valueOf(i));
map.put(String.valueOf(i), draw);
};
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < times; i++) {
Thread thread = new Thread(drawTask);
threads.add(thread);
}
threads.stream().forEach(thread -> thread.start());
threads.stream().forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
log.error(e.getMessage());
}
});
//统计中奖情况
Map<String, Long> collect = map.values().stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
collect.forEach((value,count)-> System.out.println(count+"人中:"+value));
return JSON.toJSONString(collect);
}
浅试一下
这个并发比较严重,所以看到锁经常被占用,其实我这个实际业务中 不太会同时有这么多用户抽奖。
再浅试一下
每个线程加入的时候都sleep100ms,得到
以上、