package com.webchat.ugc.service.redpacket; import com.webchat.common.constants.RedPacketConstants; import com.webchat.common.enums.RedisKeyEnum; import com.webchat.common.exception.BusinessException; import com.webchat.domain.vo.response.RedPacketBaseVO; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** * 方案三:预先计算好每分红包金额,走队列一次获取 */ @Service public class OpenRedPacketWithQueueService extends AbstractOpenRedPacketService { @Override public int openRedPacket(RedPacketBaseVO redPacket, String userId) { Long redPacketId = redPacket.getId(); String queueKey = this.redPacketAmountQueueCacheKey(redPacket.getId()); // 走队列获取预先计算好的红包金额,因为redis的单线程命令执行特性,所以这里是并发安全的 String amount = redisService.lrightPop(queueKey); if (StringUtils.isBlank(amount)) { // 红包被拆分完 super.updateRedPacketStatus(redPacketId, RedPacketConstants.RedPacketStatus.END); throw new BusinessException("来晚了,红包已拆完"); } int amountVal = Integer.valueOf(amount); // 添加用户到红包拆分人员名单缓存 addUser2OpenRedPacketUsersCache(redPacketId, userId); return amountVal; } public List preGeneratedRedPackets(Long redPacketId, int totalMoneyFen, int gradCount) { List redPackets = new ArrayList<>(); // 递归生成红包金额 this.doRecursivePreGenerated(totalMoneyFen, gradCount, redPackets); // 加入队列 String queueKey = this.redPacketAmountQueueCacheKey(redPacketId); redisService.lleftPushAll(queueKey, redPackets); return redPackets; } private String redPacketAmountQueueCacheKey(Long redPacketId) { return RedisKeyEnum.QUEUE_RED_PACKET_AMOUNT.getKey(String.valueOf(redPacketId)); } private void doRecursivePreGenerated(int balanceMoneyFen, int gradCountBalance, List redPackets) { if (gradCountBalance == 1) { // 跳出条件 redPackets.add(balanceMoneyFen); return; } // 二倍均值 BigDecimal avgAmount = new BigDecimal(balanceMoneyFen).divide(new BigDecimal(gradCountBalance), 0, BigDecimal.ROUND_HALF_UP); BigDecimal avgAmount2 = avgAmount.multiply(new BigDecimal(2)); int maxVal = avgAmount2.intValue(); // 在[minVal, maxVal]之间随机一个红包金额:openAmount int openAmount = ThreadLocalRandom.current().nextInt(RedPacketConstants.MIN_AMOUNT, maxVal + 1); redPackets.add(openAmount); doRecursivePreGenerated(balanceMoneyFen - openAmount, gradCountBalance - 1, redPackets); } public static void main(String[] args) { List redPackets = new ArrayList<>(); int gradCount = 10; // 设置红包金额(分) int totalMoneyFen = new BigDecimal("100").multiply(new BigDecimal(100)).intValue(); // 递归生成红包金额 // doRecursivePreGenerated(totalMoneyFen, gradCount, redPackets); System.out.printf("预先计算好红包:" + redPackets); } }