OpenRedPacketWithQueueService.java 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package com.webchat.ugc.service.redpacket;
  2. import com.webchat.common.constants.RedPacketConstants;
  3. import com.webchat.common.enums.RedisKeyEnum;
  4. import com.webchat.common.exception.BusinessException;
  5. import com.webchat.domain.vo.response.RedPacketBaseVO;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.springframework.stereotype.Service;
  8. import java.math.BigDecimal;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. import java.util.concurrent.ThreadLocalRandom;
  12. /**
  13. * 方案三:预先计算好每分红包金额,走队列一次获取
  14. */
  15. @Service
  16. public class OpenRedPacketWithQueueService extends AbstractOpenRedPacketService {
  17. @Override
  18. public int openRedPacket(RedPacketBaseVO redPacket, String userId) {
  19. Long redPacketId = redPacket.getId();
  20. String queueKey = this.redPacketAmountQueueCacheKey(redPacket.getId());
  21. // 走队列获取预先计算好的红包金额,因为redis的单线程命令执行特性,所以这里是并发安全的
  22. String amount = redisService.lrightPop(queueKey);
  23. if (StringUtils.isBlank(amount)) {
  24. // 红包被拆分完
  25. super.updateRedPacketStatus(redPacketId, RedPacketConstants.RedPacketStatus.END);
  26. throw new BusinessException("来晚了,红包已拆完");
  27. }
  28. int amountVal = Integer.valueOf(amount);
  29. // 添加用户到红包拆分人员名单缓存
  30. addUser2OpenRedPacketUsersCache(redPacketId, userId);
  31. return amountVal;
  32. }
  33. public List<Integer> preGeneratedRedPackets(Long redPacketId, int totalMoneyFen, int gradCount) {
  34. List<Integer> redPackets = new ArrayList<>();
  35. // 递归生成红包金额
  36. this.doRecursivePreGenerated(totalMoneyFen, gradCount, redPackets);
  37. // 加入队列
  38. String queueKey = this.redPacketAmountQueueCacheKey(redPacketId);
  39. redisService.lleftPushAll(queueKey, redPackets);
  40. return redPackets;
  41. }
  42. private String redPacketAmountQueueCacheKey(Long redPacketId) {
  43. return RedisKeyEnum.QUEUE_RED_PACKET_AMOUNT.getKey(String.valueOf(redPacketId));
  44. }
  45. private void doRecursivePreGenerated(int balanceMoneyFen, int gradCountBalance, List<Integer> redPackets) {
  46. if (gradCountBalance == 1) {
  47. // 跳出条件
  48. redPackets.add(balanceMoneyFen);
  49. return;
  50. }
  51. // 二倍均值
  52. BigDecimal avgAmount = new BigDecimal(balanceMoneyFen).divide(new BigDecimal(gradCountBalance), 0, BigDecimal.ROUND_HALF_UP);
  53. BigDecimal avgAmount2 = avgAmount.multiply(new BigDecimal(2));
  54. int maxVal = avgAmount2.intValue();
  55. // 在[minVal, maxVal]之间随机一个红包金额:openAmount
  56. int openAmount = ThreadLocalRandom.current().nextInt(RedPacketConstants.MIN_AMOUNT, maxVal + 1);
  57. redPackets.add(openAmount);
  58. doRecursivePreGenerated(balanceMoneyFen - openAmount, gradCountBalance - 1, redPackets);
  59. }
  60. public static void main(String[] args) {
  61. List<Integer> redPackets = new ArrayList<>();
  62. int gradCount = 10;
  63. // 设置红包金额(分)
  64. int totalMoneyFen = new BigDecimal("100").multiply(new BigDecimal(100)).intValue();
  65. // 递归生成红包金额
  66. // doRecursivePreGenerated(totalMoneyFen, gradCount, redPackets);
  67. System.out.printf("预先计算好红包:" + redPackets);
  68. }
  69. }