抽奖案例一:按设置的概率实时抽奖-库存扣减(后台管理配置)

按设置的概率实时抽奖-库存扣减(后台管理配置)

场景:表结构参考奖品配置步骤1.配置奖池2.配置奖品信息,关联到奖池

用户抽奖步骤1.完成任务获得抽奖资格2.按配置的概率初步得到中奖结果3.中奖用户扣减对应库存

上次讲了保证概率的,这次我们加上库存。顺便再加一个活动。

场景:

用户参加某个活动,完成活动后获得抽奖资格,可实时抽奖,根据实际库存情况返回中奖结果。

表结构参考

拼拼凑凑几个表:

活动表活动详情表奖池表奖品表 以上几个表之间都两两关联,又零零散散几个表用户获奖资格记录用户抽奖记录用户活动记录

具体的表结构?没有的哈 ,自己的作业自己做

奖品配置步骤

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 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 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 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,得到 以上、