From f3f6f995d832b470a27e5335b96d161b878ab270 Mon Sep 17 00:00:00 2001 From: khalil Date: Sun, 4 May 2025 19:11:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B9=B8=E8=BF=9025-=E5=A4=A7R=E5=92=8C?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E8=B5=A0=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/accompany/common/redis/RedisKey.java | 2 +- .../constant/Lucky25PoolTypeEnum.java | 1 + .../business/dto/lucky/Lucky25GiftConfig.java | 15 +- .../business/dto/lucky/Lucky25Result.java | 9 + .../service/gift/Lucky25DrawService.java | 156 +++++++++++++ .../service/gift/Lucky25GiftSendService.java | 213 +----------------- .../service/gift/Lucky25MessageService.java | 114 ---------- .../service/gift/Lucky25MqService.java | 171 ++++++++++++++ .../lucky/Lucky25HighRechargePoolService.java | 146 ++++++++++++ .../lucky/Lucky25IncomeAllotService.java | 11 +- .../service/lucky/Lucky25PoolService.java | 48 +--- .../service/lucky/Lucky25UserMetaService.java | 105 ++++----- .../mq/consumer/Lucky25MessageConsumer.java | 6 +- .../scheduler/task/luckyBag/Lucky25Task.java | 6 +- 14 files changed, 558 insertions(+), 445 deletions(-) create mode 100644 accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25DrawService.java delete mode 100644 accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MessageService.java create mode 100644 accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MqService.java create mode 100644 accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25HighRechargePoolService.java diff --git a/accompany-base/accompany-core/src/main/java/com/accompany/common/redis/RedisKey.java b/accompany-base/accompany-core/src/main/java/com/accompany/common/redis/RedisKey.java index bacb9d980..f0674e959 100644 --- a/accompany-base/accompany-core/src/main/java/com/accompany/common/redis/RedisKey.java +++ b/accompany-base/accompany-core/src/main/java/com/accompany/common/redis/RedisKey.java @@ -1451,7 +1451,7 @@ public enum RedisKey { lucky_25_robot_push_msg, lucky_25_status, // 礼物消息的状态 lock_lucky_25_message, // 消费送礼物消息锁 - lucky_25_user_10w_stat, // 消费送礼物消息锁 + lucky_25_high_recharge_pool, ; public String getKey() { diff --git a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/constant/Lucky25PoolTypeEnum.java b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/constant/Lucky25PoolTypeEnum.java index 849aef3dc..6e400df38 100644 --- a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/constant/Lucky25PoolTypeEnum.java +++ b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/constant/Lucky25PoolTypeEnum.java @@ -24,6 +24,7 @@ public enum Lucky25PoolTypeEnum { BLACK_POOL(3, "黑名单奖池"), HIGH_POOL(4, "保底奖池"), LOW_POOL(5, "衰减奖池"), + HIGH_INPUT_POOL(6, "大R奖池"), ; /** diff --git a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25GiftConfig.java b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25GiftConfig.java index 580447fce..b4129dd3d 100644 --- a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25GiftConfig.java +++ b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25GiftConfig.java @@ -12,23 +12,18 @@ public class Lucky25GiftConfig { private BigDecimal platformRatio; private BigDecimal receiverRatio; //n - private BigDecimal productionRatio; + private BigDecimal productionRatio_N; private Map ratioPartitionMap; - private Long highInput_A; - private BigDecimal highInputExpect_D; - - private Long preJudgeValue_H; - private BigDecimal totalInput_J; - private BigDecimal totalInputOffset_K1; - private BigDecimal totalInputOffset_K2; - private Integer poolSize = 500; private Integer newUserPoolCount = 0; + private BigDecimal todayInput; private BigDecimal todayProductionRatio; + private Long highRechargeThreshold_M; + private Integer specialTipMulti; private Long allRoomChatToastValue; @@ -42,8 +37,6 @@ public class Lucky25GiftConfig { private Map whiteUidProductionRatioMap; private List blackUidList; - private String diamondIcon; - public Lucky25GiftConfig getRatioByPartitionId(Integer partitionId){ return ratioPartitionMap.getOrDefault(partitionId, this); } diff --git a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25Result.java b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25Result.java index 57b9e1337..6426b6512 100644 --- a/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25Result.java +++ b/accompany-business/accompany-business-sdk/src/main/java/com/accompany/business/dto/lucky/Lucky25Result.java @@ -1,12 +1,21 @@ package com.accompany.business.dto.lucky; +import lombok.Data; import lombok.NoArgsConstructor; +@Data @NoArgsConstructor public class Lucky25Result extends Lucky24Result { + private Long receiverUid; + public Lucky25Result(Integer poolId, Long input, long output, Boolean isSupplement) { super(poolId, input, output, isSupplement); } + public Lucky25Result(Integer poolId, Long receiverUid, Long input, long output, Boolean isSupplement) { + super(poolId, input, output, isSupplement); + this.receiverUid = receiverUid; + } + } diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25DrawService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25DrawService.java new file mode 100644 index 000000000..0537e54d9 --- /dev/null +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25DrawService.java @@ -0,0 +1,156 @@ +package com.accompany.business.service.gift; + +import com.accompany.business.dto.lucky.Lucky25GiftConfig; +import com.accompany.business.dto.lucky.Lucky25Result; +import com.accompany.business.model.Gift; +import com.accompany.business.service.lucky.*; +import com.accompany.core.model.Room; +import com.accompany.sharding.model.Lucky25Record; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class Lucky25DrawService { + + @Autowired + private Lucky25StockService stockService; + @Autowired + private Lucky25UserMetaService userMetaService; + @Autowired + private Lucky25PoolService poolService; + @Autowired + private Lucky25HighRechargePoolService highRechargePoolService; + @Autowired + private Lucky25RecordService recordService; + @Autowired + private Lucky25SettlementService settlementService; + @Autowired + private Lucky25RobotMsgService robotMsgService; + + public List draw(Lucky25GiftConfig config, Long senderUid, int partitionId, + Gift gift, int everyGiftNum, long everyoneGoldNum, + List receiverList, Room room, Date sendGiftTime) { + List resultList = new ArrayList<>(); + + // 大R + long totalGoldNum = everyoneGoldNum * receiverList.size(); + RMap userMetaMap = userMetaService.getUserMeta(senderUid); + long afterTotalInput = userMetaMap.addAndGet(Lucky25UserMetaService.INPUT_KEY, totalGoldNum).longValue(); + + Lucky25Result highRechargeResult = drawHighRechargeUserPool(config, senderUid, partitionId, + totalGoldNum, afterTotalInput); + if (null != highRechargeResult){ + Long receiver = receiverList.remove(0); + highRechargeResult.setReceiverUid(receiver); + //todo log + resultList.add(highRechargeResult); + + if (CollectionUtils.isEmpty(receiverList)){ + return resultList.stream().map(drawResult-> + updateMeta(config, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, room, sendGiftTime, drawResult)) + .collect(Collectors.toList()); + } + } + + // admin 1000 + + int adminSuperNum = userMetaMap.getOrDefault(Lucky25UserMetaService.ADMIN_1000_NUM_KEY, 0).intValue(); + if (adminSuperNum > 0){ + int trySuperNum = Math.min(adminSuperNum, receiverList.size()); + int afterSuperNum = userMetaService.subAdminTicketNum(senderUid, trySuperNum); + for (int i = 0; i < afterSuperNum; i++) { + Long receiver = receiverList.remove(0); + //todo log + Lucky25Result adminSuperResult = new Lucky25Result(null, receiver, everyoneGoldNum, 1000L, true); + resultList.add(adminSuperResult); + } + + if (CollectionUtils.isEmpty(receiverList)){ + return resultList.stream().map(drawResult-> + updateMeta(config, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, room, sendGiftTime, drawResult)) + .collect(Collectors.toList()); + } + } + + // todo 比较 + + + // todo 原数组 + + + return receiverList.parallelStream() + .map(receiverUid-> drawMultiple(config, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime)) + .collect(Collectors.toList()); + } + + private Lucky25Result drawHighRechargeUserPool(Lucky25GiftConfig config, long uid, int partitionId, long totalGoldNum, long afterInput) { + Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId); + if (null == partitionConfig || null == partitionConfig.getHighRechargeThreshold_M()){ + return null; + } + + long beforeInput = afterInput - totalGoldNum; + long beforeThreshold = beforeInput / partitionConfig.getHighRechargeThreshold_M(); + long afterThreshold = afterInput / partitionConfig.getHighRechargeThreshold_M(); + if (beforeThreshold >= afterThreshold){ + return null; + } + + return highRechargePoolService.drawMultipleFromPool(config, uid, partitionId); + } + + public Lucky25Record drawMultiple(Lucky25GiftConfig config, long senderUid, int partitionId, + Gift gift, int giftNum, long receiverUid, long everyoneGoldNum, Room room, Date sendGiftTime) { + Lucky25Result drawResult = poolService.drawMultipleFromPool(config, senderUid, partitionId); + drawResult.setReceiverUid(receiverUid); + return updateMeta(config, senderUid, partitionId, gift, giftNum, everyoneGoldNum, room, sendGiftTime, drawResult); + } + + private Lucky25Record updateMeta(Lucky25GiftConfig config, long senderUid, int partitionId, + Gift gift, int giftNum, long everyoneGoldNum, Room room, Date sendGiftTime, + Lucky25Result drawResult){ + long receiverUid = drawResult.getReceiverUid(); + long drawMultiple = drawResult.getOutput(); + long afterMultiple = drawMultiple; + // 平台库存 + if (afterMultiple > 0L){ + BigDecimal preWinGoldNum = BigDecimal.valueOf(afterMultiple * everyoneGoldNum); + if (!judgeStock(partitionId, preWinGoldNum, senderUid, receiverUid)){ + afterMultiple = 0L; + } + } + long winGoldNum = afterMultiple * everyoneGoldNum; + userMetaService.updateUserMeta(senderUid, partitionId, everyoneGoldNum, winGoldNum); + + if (winGoldNum > 0L){ + settlementService.syncSendReward(config, senderUid, room, gift, winGoldNum, afterMultiple); + } + + return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null, + receiverUid, drawResult.getPoolId(), Boolean.TRUE.equals(drawResult.getIsSupplement()), + drawMultiple, afterMultiple, sendGiftTime); + } + + private boolean judgeStock(Integer partitionId, BigDecimal winGoldNum, Long senderUid, Long receiverUid){ + BigDecimal afterStock = stockService.subStock(partitionId, winGoldNum); + boolean enough = afterStock.compareTo(BigDecimal.ZERO) >= 0; + if (!enough){ + log.info("[lucky25] drawMultiple sender {} receiver {} 产出大于库存 winGoldNum {} afterStock {}", + senderUid, receiverUid, winGoldNum, afterStock); + afterStock = stockService.addStock(partitionId, winGoldNum); + robotMsgService.pushStockNotEnough(partitionId, afterStock); + } + return enough; + } + +} diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25GiftSendService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25GiftSendService.java index 122732457..3f2b08125 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25GiftSendService.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25GiftSendService.java @@ -1,36 +1,23 @@ package com.accompany.business.service.gift; -import com.accompany.business.dto.lucky.*; -import com.accompany.business.message.Lucky24Message; -import com.accompany.business.message.Lucky25Message; +import com.accompany.business.dto.lucky.Lucky25GiftConfig; +import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot; import com.accompany.business.model.Gift; import com.accompany.business.service.lucky.*; -import com.accompany.business.service.mq.RocketMQService; import com.accompany.common.constant.Constant; -import com.accompany.common.redis.RedisKey; import com.accompany.common.status.BusiStatus; -import com.accompany.common.utils.RandomUtil; import com.accompany.core.exception.ServiceException; import com.accompany.core.model.Room; import com.accompany.core.service.SysConfService; -import com.accompany.core.service.common.JedisService; import com.accompany.sharding.model.Lucky25Record; import com.alibaba.fastjson.JSON; -import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import lombok.extern.slf4j.Slf4j; -import org.redisson.api.RMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; @Slf4j @Service @@ -41,21 +28,11 @@ public class Lucky25GiftSendService { @Autowired private Lucky25StockService stockService; @Autowired - private Lucky25UserMetaService userMetaService; - @Autowired - private Lucky25PoolService poolService; - @Autowired - private Lucky25RecordService recordService; + private Lucky25DrawService drawService; @Autowired private Lucky25IncomeAllotService incomeAllotService; @Autowired - private Lucky25SettlementService settlementService; - @Autowired - private Lucky25RobotMsgService robotMsgService; - @Autowired - private RocketMQService rocketMQService; - @Autowired - private JedisService jedisService; + private Lucky25MqService mqService; public void draw(long senderUid, Integer partitionId, Room room, List receiverList, Gift gift, int everyGiftNum, Date sendGiftTime) { @@ -63,190 +40,18 @@ public class Lucky25GiftSendService { long everyoneGoldNum = everyGiftNum * gift.getGoldPrice(); Lucky25GiftConfig config = getConfig(); - Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId); - SuperLuckyGiftIncomeAllot incomeAllot = incomeAllotService.calculate(partitionConfig, gift, everyGiftNum, receiverList); + SuperLuckyGiftIncomeAllot incomeAllot = incomeAllotService.calculate(config, partitionId, gift, everyGiftNum, receiverList); // 增加库存 BigDecimal afterStock = stockService.addStock(partitionId, incomeAllot.getRemainValue()); log.info("[lucky25] uid {}, partitionId {}, addStockGoldNum {}, afterStock {}", senderUid, partitionId, incomeAllot.getRemainValue(), afterStock); - Map recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime); - log.info("[lucky25] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordMap)); + List recordList = drawService.draw(config, senderUid, partitionId, + gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime); + log.info("[lucky25] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordList)); - sendMq(recordMap); - } - - public Map draw(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long senderUid, int partitionId, - Gift gift, int everyGiftNum, long everyoneGoldNum, - List receiverList, Room room, Date sendGiftTime) { - if (everyoneGoldNum < partitionConfig.getHighInput_A() - || (!CollectionUtils.isEmpty(config.getBlackUidList()) && config.getBlackUidList().contains(senderUid))){ - log.error("[lucky25] uid {} totalInput {} configHighInput {} 原", senderUid, everyoneGoldNum, partitionConfig.getHighInput_A()); - return receiverList.parallelStream() - .collect(Collectors.toMap(receiverUid-> receiverUid, - receiverUid-> drawMultiple(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null))); - } - - BigDecimal totalInput_A = BigDecimal.valueOf(everyoneGoldNum); - BigDecimal historyInput = BigDecimal.ZERO; - BigDecimal historyOutput = BigDecimal.ZERO; - - RMap userStatCacheMap = userMetaService.getUser10wStat(senderUid); - Map userStatMap = userStatCacheMap.readAllMap(); - if (userStatMap.isEmpty()){ - List historyResult = userMetaService.getUserLast10wHistory(senderUid); - historyInput = BigDecimal.valueOf(historyResult.stream().mapToLong(Lucky25Result::getInput).sum()); - historyOutput = BigDecimal.valueOf(historyResult.stream().mapToLong(Lucky25Result::getOutput).sum()); - - userStatCacheMap.fastPut(Lucky25UserMetaService.INPUT_KEY, historyInput); - userStatCacheMap.fastPut(Lucky25UserMetaService.OUTPUT_KEY, historyOutput); - userStatCacheMap.expire(Duration.of(10, ChronoUnit.SECONDS)); - } else { - historyInput = BigDecimal.valueOf(userStatMap.getOrDefault(Lucky25UserMetaService.INPUT_KEY, 0L).longValue()); - historyOutput = BigDecimal.valueOf(userStatMap.getOrDefault(Lucky25UserMetaService.OUTPUT_KEY, 0L).longValue()); - } - - AtomicBoolean space1000flag = new AtomicBoolean(false); - AtomicBoolean space100flag = new AtomicBoolean(false); - - BigDecimal productionRatio_B = historyOutput.compareTo(BigDecimal.ZERO) != 0 ? - historyOutput.divide(historyInput,2, RoundingMode.HALF_UP): BigDecimal.ZERO; - BigDecimal historyDiff_C = historyInput.subtract(historyOutput); - BigDecimal expect_D = partitionConfig.getHighInputExpect_D(); - BigDecimal X = productionRatio_B.compareTo(BigDecimal.ONE) != 0 ? - historyDiff_C.divide(BigDecimal.ONE.subtract(productionRatio_B), 2, RoundingMode.HALF_UP): BigDecimal.ZERO; - BigDecimal water = X.multiply(BigDecimal.ONE.subtract(expect_D).add(BigDecimal.valueOf(0.01D))); - BigDecimal space1000 = (historyDiff_C.subtract(water)).divide(BigDecimal.valueOf(1000L), 2, RoundingMode.HALF_UP); - if (space1000.compareTo(totalInput_A) > 0){ - return receiverList.parallelStream() - .collect(Collectors.toMap(receiverUid-> receiverUid, - receiverUid-> { - //left - int randomIndex = RandomUtil.randomByRange(0, 10); - long drawMultiple = randomIndex < 1? 1000L: randomIndex < 2? 500L: randomIndex < 5? 100L: 0L; - log.info("[lucky25] uid {} space1000 {} totalInput {} left receiverUid {} randomIndex {} drawMultiple {}", - senderUid, space1000, totalInput_A, receiverUid, randomIndex, drawMultiple); - if (drawMultiple > 0L && !space1000flag.compareAndSet(false, true)){ - drawMultiple = 0L; - log.info("[lucky25] uid {} space1000 cas false {} totalInput {} left receiverUid {} randomIndex {} drawMultiple {}", - senderUid, space1000, totalInput_A, receiverUid, randomIndex, drawMultiple); - } - Lucky25Result result = new Lucky25Result(null, everyoneGoldNum, drawMultiple, false); - return updateMeta(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null, result); - })); - } - BigDecimal space100 = (historyDiff_C.subtract(water)).divide(BigDecimal.valueOf(100L), 2, RoundingMode.HALF_UP); - if (space100.compareTo(totalInput_A) > 0){ - return receiverList.parallelStream() - .collect(Collectors.toMap(receiverUid-> receiverUid, - receiverUid-> { - //middle - int randomIndex = RandomUtil.randomByRange(0, 100); - long drawMultiple = randomIndex < 25? 100L: randomIndex < 55? 5L: 0L; - log.info("[lucky25] uid {} space100 {} totalInput {} middle receiver {} randomIndex {} drawMultiple {}", - senderUid, space100, totalInput_A, receiverUid, randomIndex, drawMultiple); - if (drawMultiple > 0L && !space100flag.compareAndSet(false, true)){ - drawMultiple = 0L; - log.info("[lucky25] uid {} space100 cas false {} totalInput {} left receiverUid {} randomIndex {} drawMultiple {}", - senderUid, space1000, totalInput_A, receiverUid, randomIndex, drawMultiple); - } - Lucky25Result result = new Lucky25Result(null, everyoneGoldNum, drawMultiple, false); - return updateMeta(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null, result); - })); - } - log.info("[lucky25] uid {} space100 {} totalInput {} 原来 10L", senderUid, space1000, totalInput_A); - return receiverList.parallelStream() - .collect(Collectors.toMap(receiverUid-> receiverUid, - receiverUid-> drawMultiple(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null))); - } - - private void sendMq(Map recordMap) { - Map caches = new HashMap<>(recordMap.size()); - List messageList = new ArrayList<>(); - - DefaultIdentifierGenerator idGenerator = DefaultIdentifierGenerator.getInstance(); - - for (Lucky25Record record: recordMap.values()){ - String id = idGenerator.nextUUID(null); - - Lucky25Message message = new Lucky25Message(); - message.setMessId(id); - message.setPartitionId(record.getPartitionId()); - message.setUid(record.getUid()); - message.setReceiverUid(record.getReceiverUid()); - message.setRoomUid(record.getRoomUid()); - message.setGiftId(record.getGiftId()); - message.setGiftGoldPrice(record.getGiftGoldPrice()); - message.setGiftNum(record.getGiftNum()); - message.setPoolId(record.getPoolId()); - message.setIsSupplement(record.getIsSupplement()); - message.setDrawMultiple(record.getDrawMultiple()); - message.setAfterMultiple(record.getAfterMultiple()); - message.setWinGoldNum(record.getWinGoldNum()); - message.setCreateTime(record.getCreateTime().getTime()); - - messageList.add(message); - - caches.put(id, JSON.toJSONString(message)); - } - - jedisService.hwrite(RedisKey.lucky_25_status.getKey(), caches); - - rocketMQService.sendBatchLucky25Message(messageList); - } - - public Lucky25Record drawMultiple(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, long senderUid, int partitionId, - Gift gift, int giftNum, long receiverUid, long everyoneGoldNum, Room room, Date sendGiftTime, - Long maxMultiple) { - Lucky25Result drawResult = poolService.drawMultipleFromPool(config, senderUid, partitionId); - return updateMeta(config, partitionConfig, senderUid, partitionId, gift, giftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, maxMultiple, drawResult); - } - - private Lucky25Record updateMeta(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, long senderUid, int partitionId, - Gift gift, int giftNum, long receiverUid, long everyoneGoldNum, Room room, Date sendGiftTime, - Long maxMultiple, Lucky25Result drawResult){ - long drawMultiple = drawResult.getOutput(); - long afterMultiple = drawMultiple; - if (null != maxMultiple) { - afterMultiple = Math.min(afterMultiple, maxMultiple); - } - // 个人库存 - if (afterMultiple > 0L){ - long preWinGoldNum = afterMultiple * everyoneGoldNum; - if (!userMetaService.judgePersonalStock(partitionConfig, senderUid, receiverUid, everyoneGoldNum, preWinGoldNum)){ - afterMultiple = 0L; - } - } - // 平台库存 - if (afterMultiple > 0L){ - BigDecimal preWinGoldNum = BigDecimal.valueOf(afterMultiple * everyoneGoldNum); - if (!judgeStock(partitionId, preWinGoldNum, senderUid, receiverUid)){ - afterMultiple = 0L; - } - } - long winGoldNum = afterMultiple * everyoneGoldNum; - userMetaService.updateUserMeta(senderUid, partitionId, everyoneGoldNum, winGoldNum); - - if (winGoldNum > 0L){ - settlementService.syncSendReward(config, senderUid, room, gift, winGoldNum, afterMultiple); - } - - return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null, - receiverUid, drawResult.getPoolId(), Boolean.TRUE.equals(drawResult.getIsSupplement()), - drawMultiple, afterMultiple, sendGiftTime); - } - - private boolean judgeStock(Integer partitionId, BigDecimal winGoldNum, Long senderUid, Long receiverUid){ - BigDecimal afterStock = stockService.subStock(partitionId, winGoldNum); - boolean enough = afterStock.compareTo(BigDecimal.ZERO) >= 0; - if (!enough){ - log.info("[lucky25] drawMultiple sender {} receiver {} 产出大于库存 winGoldNum {} afterStock {}", - senderUid, receiverUid, winGoldNum, afterStock); - afterStock = stockService.addStock(partitionId, winGoldNum); - robotMsgService.pushStockNotEnough(partitionId, afterStock); - } - return enough; + mqService.sendMq(recordList); } public Lucky25GiftConfig getConfig() { diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MessageService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MessageService.java deleted file mode 100644 index 611deb1c7..000000000 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MessageService.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.accompany.business.service.gift; - -import com.accompany.business.dto.lucky.Lucky25GiftConfig; -import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot; -import com.accompany.business.message.Lucky25Message; -import com.accompany.business.model.Gift; -import com.accompany.business.service.lucky.*; -import com.accompany.business.service.lucky.rank.Lucky24SendWeekRankService; -import com.accompany.business.service.room.RoomService; -import com.accompany.common.redis.RedisKey; -import com.accompany.core.model.Room; -import com.accompany.core.service.base.BaseService; -import com.accompany.sharding.model.Lucky25Record; -import com.alibaba.fastjson.JSON; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.Collections; -import java.util.Date; - -@Slf4j -@Service -public class Lucky25MessageService extends BaseService { - - @Autowired - private Lucky25RecordService recordService; - @Autowired - private Lucky25IncomeAllotService incomeAllotService; - @Autowired - private SuperLuckyGiftSendService superLuckyGiftSendService; - @Autowired - private Lucky25RobotMsgService robotMsgService; - @Autowired - private Lucky25GiftSendService sendService; - @Autowired - private RoomService roomService; - @Autowired - private GiftService giftService; - @Autowired - private Lucky24SendWeekRankService lucky24SendWeekRankService; - - public void handleGiftMessage(Lucky25Message giftMessage) { - // 防止消息被重复消费 - if (!jedisLockService.isExist(RedisKey.lock_lucky_25_message.getKey(giftMessage.getMessId()), 30)) { - logger.warn("handleLucky25Message giftMessage had handle, mess: " + giftMessage); - return; - } - - if (!jedisService.hexists(RedisKey.lucky_25_status.getKey(), giftMessage.getMessId())){ - logger.warn("handleLucky25Message giftMessage had handle, mess: " + giftMessage); - return; - } - - logger.info("【处理Lucky25 mq】 开始处理 giftMessage: {}", JSON.toJSONString(giftMessage)); - - Room room = null != giftMessage.getRoomUid()? roomService.getRoomByUid(giftMessage.getRoomUid()): null; - Gift gift = giftService.getGiftById(giftMessage.getGiftId()); - Date createTime = new Date(giftMessage.getCreateTime()); - - Lucky25Record record = insertRecord(giftMessage); - - log.info("【处理Lucky25 mq】 record 插入成功 messId:{} recordId:{} record:{}", - giftMessage.getMessId(), record.getId(), JSON.toJSONString(record)); - - // 收礼者收益 - Lucky25GiftConfig config = sendService.getConfig(); - Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(giftMessage.getPartitionId()); - SuperLuckyGiftIncomeAllot receiverIncomeAllot = incomeAllotService.calculate(partitionConfig, gift, giftMessage.getGiftNum(), Collections.singletonList(record.getReceiverUid())); - superLuckyGiftSendService.syncSettlement(giftMessage.getUid(), gift, giftMessage.getGiftNum(), giftMessage.getGiftNum(), room, receiverIncomeAllot, createTime); - - logger.info("【处理Lucky25 mq】 收礼收益已发放 messId: {} incomeAllot: {}", giftMessage.getMessId(), JSON.toJSONString(receiverIncomeAllot)); - - // 后面都是异步发消息 -// if (record.getAfterMultiple() >= config.getWarnMulti()){ -// long totalGoldNum = giftMessage.getGiftNum() * giftMessage.getGiftGoldPrice(); -// robotMsgService.pushSuperMulti(record.getUid(), record.getReceiverUid(), record.getAfterMultiple(), totalGoldNum, record.getWinGoldNum(), -// record.getRoomUid()); -// } - - if (CollectionUtils.isEmpty(config.getFollowUidList()) && config.getFollowUidList().contains(record.getUid())){ - robotMsgService.pushFollowUser(record.getUid(), record.getReceiverUid(), record.getRoomUid()); - } - - lucky24SendWeekRankService.updateRank(record); - - // 删除该标识,表示消息已经消费过 - jedisService.hdel(RedisKey.lucky_25_status.getKey(), giftMessage.getMessId()); - } - - private Lucky25Record insertRecord(Lucky25Message giftMessage) { - Lucky25Record record = new Lucky25Record(); - record.setMessId(giftMessage.getMessId()); - record.setPartitionId(giftMessage.getPartitionId()); - record.setUid(giftMessage.getUid()); - record.setReceiverUid(giftMessage.getReceiverUid()); - record.setRoomUid(giftMessage.getRoomUid()); - record.setGiftId(giftMessage.getGiftId()); - record.setGiftGoldPrice(giftMessage.getGiftGoldPrice()); - record.setGiftNum(giftMessage.getGiftNum()); - record.setPoolId(giftMessage.getPoolId()); - record.setIsSupplement(giftMessage.getIsSupplement()); - record.setDrawMultiple(giftMessage.getDrawMultiple()); - record.setAfterMultiple(giftMessage.getAfterMultiple()); - record.setWinGoldNum(giftMessage.getWinGoldNum()); - record.setCreateTime(new Date(giftMessage.getCreateTime())); - - recordService.insertRecord(record); - - return record; - } - -} diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MqService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MqService.java new file mode 100644 index 000000000..160cb568c --- /dev/null +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/gift/Lucky25MqService.java @@ -0,0 +1,171 @@ +package com.accompany.business.service.gift; + +import com.accompany.business.dto.lucky.Lucky25GiftConfig; +import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot; +import com.accompany.business.message.Lucky25Message; +import com.accompany.business.model.Gift; +import com.accompany.business.service.lucky.*; +import com.accompany.business.service.lucky.rank.Lucky24SendWeekRankService; +import com.accompany.business.service.mq.RocketMQService; +import com.accompany.business.service.room.RoomService; +import com.accompany.common.redis.RedisKey; +import com.accompany.common.status.BusiStatus; +import com.accompany.core.exception.ServiceException; +import com.accompany.core.model.Room; +import com.accompany.sharding.model.Lucky25Record; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RMap; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +public class Lucky25MqService implements InitializingBean { + + @Autowired + private RedissonClient redissonClient; + @Autowired + private Lucky25RecordService recordService; + @Autowired + private Lucky25IncomeAllotService incomeAllotService; + @Autowired + private SuperLuckyGiftSendService superLuckyGiftSendService; + @Autowired + private Lucky25RobotMsgService robotMsgService; + @Autowired + private Lucky25GiftSendService sendService; + @Autowired + private RoomService roomService; + @Autowired + private GiftService giftService; + @Autowired + private Lucky24SendWeekRankService lucky24SendWeekRankService; + @Autowired + private RocketMQService rocketMQService; + + private RMap statusMap; + + public void sendMq(List recordList) { + Map caches = new HashMap<>(recordList.size()); + List messageList = new ArrayList<>(); + + DefaultIdentifierGenerator idGenerator = DefaultIdentifierGenerator.getInstance(); + + for (Lucky25Record record: recordList){ + String id = idGenerator.nextUUID(null); + + Lucky25Message message = new Lucky25Message(); + message.setMessId(id); + message.setPartitionId(record.getPartitionId()); + message.setUid(record.getUid()); + message.setReceiverUid(record.getReceiverUid()); + message.setRoomUid(record.getRoomUid()); + message.setGiftId(record.getGiftId()); + message.setGiftGoldPrice(record.getGiftGoldPrice()); + message.setGiftNum(record.getGiftNum()); + message.setPoolId(record.getPoolId()); + message.setIsSupplement(record.getIsSupplement()); + message.setDrawMultiple(record.getDrawMultiple()); + message.setAfterMultiple(record.getAfterMultiple()); + message.setWinGoldNum(record.getWinGoldNum()); + message.setCreateTime(record.getCreateTime().getTime()); + + messageList.add(message); + + caches.put(id, JSON.toJSONString(message)); + } + + statusMap.putAll(caches); + + rocketMQService.sendBatchLucky25Message(messageList); + } + + public void handleMq(Lucky25Message giftMessage) { + // 防止消息被重复消费 + boolean locked = false; + RLock lock = redissonClient.getLock(RedisKey.lock_lucky_25_message.getKey(giftMessage.getMessId())); + try { + locked = lock.tryLock(15, 5, TimeUnit.SECONDS); + if (!locked) { + log.warn("handleLucky25Message giftMessage lock fail, mess: {}", JSON.toJSONString(giftMessage)); + throw new ServiceException(BusiStatus.SERVERBUSY); + } + + if (!statusMap.containsKey(giftMessage.getMessId())){ + log.warn("handleLucky25Message giftMessage had handle, mess: " + giftMessage); + return; + } + + log.info("【处理Lucky25 mq】 开始处理 giftMessage: {}", JSON.toJSONString(giftMessage)); + + Room room = null != giftMessage.getRoomUid()? roomService.getRoomByUid(giftMessage.getRoomUid()): null; + Gift gift = giftService.getGiftById(giftMessage.getGiftId()); + Date createTime = new Date(giftMessage.getCreateTime()); + + Lucky25Record record = insertRecord(giftMessage); + + log.info("【处理Lucky25 mq】 record 插入成功 messId:{} recordId:{} record:{}", + giftMessage.getMessId(), record.getId(), JSON.toJSONString(record)); + + // 收礼者收益 + Lucky25GiftConfig config = sendService.getConfig(); + SuperLuckyGiftIncomeAllot receiverIncomeAllot = incomeAllotService.calculate(config, giftMessage.getPartitionId(), gift, giftMessage.getGiftNum(), Collections.singletonList(record.getReceiverUid())); + superLuckyGiftSendService.syncSettlement(giftMessage.getUid(), gift, giftMessage.getGiftNum(), giftMessage.getGiftNum(), room, receiverIncomeAllot, createTime); + + log.info("【处理Lucky25 mq】 收礼收益已发放 messId: {} incomeAllot: {}", giftMessage.getMessId(), JSON.toJSONString(receiverIncomeAllot)); + + if (CollectionUtils.isEmpty(config.getFollowUidList()) && config.getFollowUidList().contains(record.getUid())){ + robotMsgService.pushFollowUser(record.getUid(), record.getReceiverUid(), record.getRoomUid()); + } + + lucky24SendWeekRankService.updateRank(record); + + // 删除该标识,表示消息已经消费过 + statusMap.fastRemove(giftMessage.getMessId()); + + } catch (InterruptedException e) { + log.error("handleLucky25Message giftMessage lock overtime, mess: {}", JSON.toJSONString(giftMessage), e); + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + + private Lucky25Record insertRecord(Lucky25Message giftMessage) { + Lucky25Record record = new Lucky25Record(); + record.setMessId(giftMessage.getMessId()); + record.setPartitionId(giftMessage.getPartitionId()); + record.setUid(giftMessage.getUid()); + record.setReceiverUid(giftMessage.getReceiverUid()); + record.setRoomUid(giftMessage.getRoomUid()); + record.setGiftId(giftMessage.getGiftId()); + record.setGiftGoldPrice(giftMessage.getGiftGoldPrice()); + record.setGiftNum(giftMessage.getGiftNum()); + record.setPoolId(giftMessage.getPoolId()); + record.setIsSupplement(giftMessage.getIsSupplement()); + record.setDrawMultiple(giftMessage.getDrawMultiple()); + record.setAfterMultiple(giftMessage.getAfterMultiple()); + record.setWinGoldNum(giftMessage.getWinGoldNum()); + record.setCreateTime(new Date(giftMessage.getCreateTime())); + + recordService.insertRecord(record); + + return record; + } + + @Override + public void afterPropertiesSet() throws Exception { + statusMap = redissonClient.getMap(RedisKey.lucky_25_status.getKey()); + } +} diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25HighRechargePoolService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25HighRechargePoolService.java new file mode 100644 index 000000000..437e1d972 --- /dev/null +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25HighRechargePoolService.java @@ -0,0 +1,146 @@ +package com.accompany.business.service.lucky; + +import com.accompany.business.constant.Lucky25PoolTypeEnum; +import com.accompany.business.dto.lucky.Lucky25GiftConfig; +import com.accompany.business.dto.lucky.Lucky25Result; +import com.accompany.business.model.lucky.Lucky25Pool; +import com.accompany.business.mybatismapper.lucky.Lucky25PoolMapper; +import com.accompany.common.redis.RedisKey; +import com.accompany.common.status.BusiStatus; +import com.accompany.common.utils.RandomUtil; +import com.accompany.core.exception.ServiceException; +import com.accompany.payment.service.HighRechargeUserService; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RDeque; +import org.redisson.api.RLock; +import org.redisson.api.RQueue; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class Lucky25HighRechargePoolService { + + @Autowired + private RedissonClient redissonClient; + @Autowired + private HighRechargeUserService highRechargeUserService; + @Autowired + private Lucky25PoolMapper poolMapper; + + public Lucky25Result drawMultipleFromPool(Lucky25GiftConfig config, Long uid, Integer partitionId){ + if (!highRechargeUserService.isHighRechargeUser(uid)){ + return null; + } + + RQueue partitionPool = getHighRechargePool(partitionId); + for (int i = 0; i < 3; i++) { + Lucky25Result result = partitionPool.poll(); + if (null != result) { + partitionPool.expireAsync(Duration.of(14, ChronoUnit.DAYS)); + return result; + } + genPool(config, partitionId, partitionPool); + } + throw new ServiceException(BusiStatus.SERVERBUSY); + } + + private void genPool(Lucky25GiftConfig config, Integer partitionId, RQueue partitionPool) { + boolean locked = false; + RLock lock = redissonClient.getLock(RedisKey.lucky_25_user_lock.getKey(partitionId.toString())); + try { + locked = lock.tryLock(5,3, TimeUnit.SECONDS); + + if (!partitionPool.isEmpty()){ + log.info("[lucky25] genPool uid {} 已生成pool,无需再生成", partitionId); + return; + } + + if (!locked) { + throw new ServiceException(BusiStatus.SERVERBUSY); + } + + List poolList = poolMapper.selectList(Wrappers.lambdaQuery() + .eq(Lucky25Pool::getType, Lucky25PoolTypeEnum.HIGH_INPUT_POOL.getType())); + if (CollectionUtils.isEmpty(poolList)){ + throw new ServiceException(BusiStatus.SEIZE_TREASURE_POOL_CONFIG_ERROR); + } + + int randomIndex = RandomUtil.randomByRange(0, poolList.size()); + Lucky25Pool randomPool = poolList.get(randomIndex); + log.info("[lucky25] genHighRechargePool pooList {}, randomIndex {}, expect {}", + JSON.toJSONString(poolList.stream().map(Lucky25Pool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect()); + + buildPool(config, partitionId, partitionPool, randomPool); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (locked) { + lock.unlock(); + } + } + } + + private void buildPool(Lucky25GiftConfig config, Integer partitionId, RQueue partitionPool, Lucky25Pool pool) { + List poolItemList = pool.getItemList().stream().filter(item->item.getNum()>0).collect(Collectors.toList()); + List winList = buildWinList(poolItemList); + int[] poolArray = new int[config.getPoolSize()]; + for (int i = 0; i < config.getPoolSize(); i++) { + poolArray[i] = 0; + } + + if (!winList.isEmpty()){ + List winIndexList = new ArrayList<>(); + int winDistance = config.getPoolSize() / winList.size(); + for (int i = 0; i < winList.size(); i++) { + int nextWinIndex = config.getPoolSize() - 1 - i * winDistance; + // 确保索引不会超出超集范围 + if (nextWinIndex > 0) { + poolArray[nextWinIndex] = winList.get(i); + winIndexList.add(nextWinIndex); + } + } + log.info("[lucky25] genHighRechargePool buildPool partitionId {}, winIndexList {}", partitionId, JSON.toJSONString(winIndexList)); + } + + List poolList = Arrays.stream(poolArray).boxed().map(output->{ + Lucky25Result result = new Lucky25Result(); + result.setPoolId(pool.getId()); + result.setOutput(output); + return result; + }).collect(Collectors.toList()); + partitionPool.addAll(poolList); + } + + private List buildWinList(List poolItemList) { + List resultList = new ArrayList<>(); + + for (Lucky25Pool.Lucky24PoolItem item : poolItemList) { + int multi = item.getMulti(); + int num = item.getNum(); + + // 使用循环生成 num 个 multi 值,并添加到 resultList 中 + for (int i = 0; i < num; i++) { + resultList.add(multi); + } + } + + Collections.shuffle(resultList); + return resultList; + } + + private RDeque getHighRechargePool(Integer partitionId) { + return redissonClient.getDeque(RedisKey.lucky_25_high_recharge_pool.getKey(partitionId.toString())); + } +} diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25IncomeAllotService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25IncomeAllotService.java index 34310bab8..d0a3179b4 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25IncomeAllotService.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25IncomeAllotService.java @@ -25,7 +25,10 @@ public class Lucky25IncomeAllotService implements LuckyGiftIncomeAllotService { @Autowired private BillRecordService billRecordService; - public SuperLuckyGiftIncomeAllot calculate(Lucky25GiftConfig config, Gift gift, int giftNum, List receiverUids) { + public SuperLuckyGiftIncomeAllot calculate(Lucky25GiftConfig config, Integer partitionId, + Gift gift, int giftNum, List receiverUids) { + Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId); + BigDecimal giftNumB = BigDecimal.valueOf(giftNum); BigDecimal totalNum = BigDecimal.valueOf(receiverUids.size()).multiply(giftNumB); // 单价 @@ -35,9 +38,9 @@ public class Lucky25IncomeAllotService implements LuckyGiftIncomeAllotService { BigDecimal totalValue = giftValue.multiply(totalNum); // 收礼者收益单价 - BigDecimal receiverIncome = everyTotalValue.multiply(config.getReceiverRatio()); + BigDecimal receiverIncome = everyTotalValue.multiply(partitionConfig.getReceiverRatio()); - BigDecimal receiverTotalIncome = totalValue.multiply(config.getReceiverRatio()); + BigDecimal receiverTotalIncome = totalValue.multiply(partitionConfig.getReceiverRatio()); Map receiverIncomeMap = new HashMap<>(); for (Long receiver: receiverUids){ @@ -45,7 +48,7 @@ public class Lucky25IncomeAllotService implements LuckyGiftIncomeAllotService { } // 平台抽成 - BigDecimal platformGoldNum = totalValue.multiply(config.getPlatformRatio()); + BigDecimal platformGoldNum = totalValue.multiply(partitionConfig.getPlatformRatio()); // 增加库存 BigDecimal addStockGoldNum = totalValue.subtract(platformGoldNum); diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25PoolService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25PoolService.java index e5e5ff2ba..e308e19b7 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25PoolService.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25PoolService.java @@ -3,8 +3,6 @@ package com.accompany.business.service.lucky; import com.accompany.business.constant.Lucky25PoolTypeEnum; import com.accompany.business.dto.lucky.Lucky25GiftConfig; import com.accompany.business.dto.lucky.Lucky25Result; -import com.accompany.business.dto.lucky.Lucky25GiftConfig; -import com.accompany.business.dto.lucky.Lucky25Result; import com.accompany.business.model.lucky.Lucky25Pool; import com.accompany.business.mybatismapper.lucky.Lucky25PoolMapper; import com.accompany.common.redis.RedisKey; @@ -173,15 +171,15 @@ public class Lucky25PoolService { String todayInputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.INPUT_KEY); String todayOutputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.OUTPUT_KEY); - RMap userMetaMap = userMetaService.getUserMeta(uid); + Map userMetaMap = userMetaService.getUserMeta(uid).readAllMap(); BigDecimal input = BigDecimal.valueOf(userMetaMap.getOrDefault(todayInputKey, 0L).longValue()); BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(todayOutputKey, 0L).longValue()); BigDecimal todayProductionRatio = BigDecimal.ZERO.equals(output)? BigDecimal.ZERO: output.divide(input,2, RoundingMode.HALF_UP); - if (input.compareTo(BigDecimal.valueOf(300000)) >= 0 && todayProductionRatio.compareTo(partitionConfig.getTodayProductionRatio()) >= 0){ + if (input.compareTo(partitionConfig.getTodayInput()) >= 0 && todayProductionRatio.compareTo(partitionConfig.getTodayProductionRatio()) >= 0){ List excludePoolList = poolList.stream() .filter(pool->pool.getType() == Lucky25PoolTypeEnum.NORMAL_POOL.getType()) .filter(pool-> pool.getItemList().stream().noneMatch(poolItem->poolItem.getMulti() == 1000 && poolItem.getNum() > 0)) - .filter(pool-> partitionConfig.getProductionRatio().compareTo(pool.getExpect()) > 0) + .filter(pool-> partitionConfig.getProductionRatio_N().compareTo(pool.getExpect()) > 0) .collect(Collectors.toList()); int randomIndex = RandomUtil.randomByRange(0, excludePoolList.size()); @@ -215,7 +213,7 @@ public class Lucky25PoolService { private BigDecimal calExpect(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long uid) { BigDecimal n = !CollectionUtils.isEmpty(config.getWhiteUidProductionRatioMap()) && config.getWhiteUidProductionRatioMap().containsKey(uid) ? - config.getWhiteUidProductionRatioMap().get(uid): partitionConfig.getProductionRatio(); + config.getWhiteUidProductionRatioMap().get(uid): partitionConfig.getProductionRatio_N(); BigDecimal userLast2000ProductionRatio = userMetaService.getUserLast2000ProductionRatio(uid); if (userLast2000ProductionRatio.compareTo(n) <= 0){ @@ -260,42 +258,4 @@ public class Lucky25PoolService { return redissonClient.getDeque(RedisKey.lucky_25_user_pool.getKey(uid.toString())); } - public void updateUserMulti(Long uid) { - RDeque userPool = getUserPool(uid); - if (userPool.isEmpty()){ - log.info("[lucky25] updateUserMulti uid {} 未生成pool", uid); - throw new ServiceException(BusiStatus.SERVERBUSY, "用户未生成奖池"); - } - boolean locked = false; - RLock lock = redissonClient.getLock(RedisKey.lucky_25_user_lock.getKey(uid.toString())); - try { - locked = lock.tryLock(5,3, TimeUnit.SECONDS); - if (!locked) { - throw new ServiceException(BusiStatus.SERVERBUSY); - } - - if (userPool.isEmpty()){ - log.info("[lucky25] updateUserMulti uid {} 未生成pool", uid); - throw new ServiceException(BusiStatus.SERVERBUSY, "用户未生成奖池"); - } - - List list = userPool.poll(11); - for (int i = list.size() - 1; i >= 0; i--) { - Lucky25Result result = list.get(i); - if (i >= list.size() - 1) { - result.setIsSupplement(Boolean.TRUE); - result.setOutput(1000); - } - userPool.addFirst(result); - } - - } catch (InterruptedException e) { - throw new RuntimeException(e); - } finally { - if (locked) { - lock.unlock(); - } - } - } - } diff --git a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25UserMetaService.java b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25UserMetaService.java index 02a92aa1c..5cd89f8df 100644 --- a/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25UserMetaService.java +++ b/accompany-business/accompany-business-service/src/main/java/com/accompany/business/service/lucky/Lucky25UserMetaService.java @@ -1,6 +1,5 @@ package com.accompany.business.service.lucky; -import com.accompany.business.dto.lucky.Lucky25GiftConfig; import com.accompany.business.dto.lucky.Lucky25Result; import com.accompany.common.redis.RedisKey; import com.accompany.common.utils.DateTimeUtil; @@ -26,9 +25,13 @@ public class Lucky25UserMetaService { public static final String INPUT_KEY = "input"; public static final String OUTPUT_KEY = "output"; + private static final String POOL_TYPE = "pool_type"; + private static final String TODAY = "today"; - private static final int HISTORY_QUEUE_SIZE = 40000; + public static final String ADMIN_1000_NUM_KEY = "admin_1000_num"; + + private static final BigDecimal HISTORY_QUEUE_INPUT_MAX = BigDecimal.valueOf(100000L); @Autowired private RedissonClient redissonClient; @@ -39,33 +42,21 @@ public class Lucky25UserMetaService { public BigDecimal getUserLast2000ProductionRatio(Long uid) { RList historyQueue = getUserHistoryQueue(uid); - int size = historyQueue.size(); - //保留20000条记录 - int num = Math.min(size, 2000); + //保留2000条记录 + int num = 2000; List historyResult = historyQueue.range(num); long historyInput = historyResult.stream().limit(num).mapToLong(Lucky25Result::getInput).sum(); + BigDecimal historyInputB = BigDecimal.valueOf(historyInput); long historyOutput = historyResult.stream().limit(num).mapToLong(Lucky25Result::getOutput).sum(); - BigDecimal productionRatio = num > 0 ? BigDecimal.valueOf(historyOutput).divide(BigDecimal.valueOf(historyInput),2, RoundingMode.HALF_UP): BigDecimal.ZERO; + BigDecimal historyOutputB = BigDecimal.valueOf(historyOutput); + + BigDecimal productionRatio = historyOutputB.compareTo(BigDecimal.ZERO) > 0 ? + historyOutputB.divide(historyInputB,2, RoundingMode.HALF_UP): BigDecimal.ZERO; log.info("[lucky25] buildPool last2000 uid {}, historyInput {}, historyOutput {}, num {}, productionRatio {}", uid, historyInput, historyOutput, num, productionRatio); - if (size > HISTORY_QUEUE_SIZE){ - historyQueue.trim(0, HISTORY_QUEUE_SIZE); - } return productionRatio; } - public List getUserLast10wHistory(Long uid) { - RList historyQueue = getUserHistoryQueue(uid); - int size = historyQueue.size(); - //保留20000条记录 - int num = Math.min(size, HISTORY_QUEUE_SIZE); - List historyResult = historyQueue.range(num); - if (size > HISTORY_QUEUE_SIZE){ - historyQueue.trim(0, HISTORY_QUEUE_SIZE); - } - return historyResult; - } - public BigDecimal getUserProductionRatio(Long uid) { RMap userMetaMap = getUserMeta(uid); BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(OUTPUT_KEY, 0L).longValue()); @@ -76,51 +67,21 @@ public class Lucky25UserMetaService { return productionRatio; } - public boolean judgePersonalStock(Lucky25GiftConfig config, long senderUid, long receiverUid, long thisInput, long preWinGoldNum) { - if (preWinGoldNum < config.getPreJudgeValue_H()){ - return true; - } - - BigDecimal preWinGoldNumB = BigDecimal.valueOf(preWinGoldNum); - - RMap userMetaMap = getUserMeta(senderUid); - BigDecimal input = BigDecimal.valueOf(userMetaMap.addAndGet(INPUT_KEY, thisInput).longValue()); - BigDecimal output = BigDecimal.valueOf(userMetaMap.addAndGet(OUTPUT_KEY, preWinGoldNum).longValue()); - BigDecimal inputOffset = input.compareTo(config.getTotalInput_J()) >= 0 ? config.getTotalInputOffset_K1() : config.getTotalInputOffset_K2(); - - BigDecimal beforeInput = input.subtract(BigDecimal.valueOf(thisInput)); - BigDecimal beforeOutput = output.subtract(preWinGoldNumB); - BigDecimal personalStock = beforeInput.multiply(inputOffset).subtract(beforeOutput); - if (personalStock.compareTo(preWinGoldNumB) < 0){ - userMetaMap.addAndGet(INPUT_KEY, -thisInput); - userMetaMap.addAndGet(OUTPUT_KEY, -preWinGoldNum); - log.info("[lucky25] personalStock false uid {} input {} J {} inputOffset {} output {} personalStock {} preWinGoldNum {} receiver {}", - senderUid, beforeInput, config.getTotalInput_J(), inputOffset, beforeOutput, personalStock, preWinGoldNum, receiverUid); - return false; - } - userMetaMap.addAndGet(INPUT_KEY, -thisInput); - userMetaMap.addAndGet(OUTPUT_KEY, -preWinGoldNum); - log.info("[lucky25] personalStock true uid {} input {} J {} inputOffset {} output {} personalStock {} preWinGoldNum {} receiver {}", - senderUid, beforeInput, config.getTotalInput_J(), inputOffset, beforeOutput, personalStock, preWinGoldNum, receiverUid); - return true; - } - public void updateUserMeta(long senderUid, int partitionId, long input, long output) { RList historyQueue = getUserHistoryQueue(senderUid); historyQueue.add(0, new Lucky25Result(null, input, output, null)); historyQueue.expire(Duration.of(14, ChronoUnit.DAYS)); - if (output > 0L){ - getUser10wStat(senderUid).delete(); - } - RMap userMetaMap = getUserMeta(senderUid); long times = userMetaMap.addAndGet(TIMES_KEY, 1L).longValue(); - long totalInput = userMetaMap.addAndGet(INPUT_KEY, input).longValue(); + //long totalInput = userMetaMap.addAndGet(INPUT_KEY, input).longValue(); long totalOutput = userMetaMap.addAndGet(OUTPUT_KEY, output).longValue(); - log.info("[lucky25] updateUserMeta uid {} times {} totalInput {} totalOutput {}", - senderUid, times, totalInput, totalOutput); + //log.info("[lucky25] updateUserMeta uid {} times {} totalInput {} totalOutput {}", + // senderUid, times, totalInput, totalOutput); + + log.info("[lucky25] updateUserMeta uid {} times {} totalOutput {}", + senderUid, times, totalOutput); PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId); long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId()); @@ -145,6 +106,32 @@ public class Lucky25UserMetaService { senderUid, times, todayStartTimeLong, todayInput, todayOutput); } + public int subAdminTicketNum(long uid, int superTicketNum) { + RMap userMetaMap = getUserMeta(uid); + int afterNum = userMetaMap.addAndGet(ADMIN_1000_NUM_KEY, -superTicketNum).intValue(); + if (afterNum >= 0){ + return superTicketNum; + } + for (int i = superTicketNum - 1; i > 0; i--) { + afterNum++; + if (afterNum >= 0){ + int returnTicketNum = superTicketNum - i; + int userTicketNumAfterReturn = userMetaMap.addAndGet(ADMIN_1000_NUM_KEY, returnTicketNum).intValue(); + log.info("[lucky25] subAdminTicketNum uid {} not enough ticket num, return {} after {}", uid, returnTicketNum, userTicketNumAfterReturn); + return i; + } + } + int userTicketNumAfterReturn = userMetaMap.addAndGet(ADMIN_1000_NUM_KEY, superTicketNum).intValue(); + log.info("[lucky25] subAdminTicketNum uid {} not enough ticket num, return all {} after {}", uid, superTicketNum, userTicketNumAfterReturn); + return 0; + } + + public void updateUserMulti(Long uid) { + RMap userMetaMap = getUserMeta(uid); + int remainNum = userMetaMap.addAndGet(ADMIN_1000_NUM_KEY, 1).intValue(); + log.info("[lucky25] admin send 1000 multi to uid {} remainNum {}", uid, remainNum); + } + public RMap getUserMeta(Long uid) { return redissonClient.getMap(RedisKey.lucky_25_user_meta.getKey(uid.toString())); } @@ -153,8 +140,4 @@ public class Lucky25UserMetaService { return redissonClient.getList(RedisKey.lucky_25_user_history.getKey(uid.toString())); } - public RMap getUser10wStat(Long uid) { - return redissonClient.getMapCache(RedisKey.lucky_25_user_10w_stat.getKey(uid.toString())); - } - } diff --git a/accompany-mq/accompany-mq-web/src/main/java/com/accompany/mq/consumer/Lucky25MessageConsumer.java b/accompany-mq/accompany-mq-web/src/main/java/com/accompany/mq/consumer/Lucky25MessageConsumer.java index f046d268d..d64289439 100644 --- a/accompany-mq/accompany-mq-web/src/main/java/com/accompany/mq/consumer/Lucky25MessageConsumer.java +++ b/accompany-mq/accompany-mq-web/src/main/java/com/accompany/mq/consumer/Lucky25MessageConsumer.java @@ -1,7 +1,7 @@ package com.accompany.mq.consumer; import com.accompany.business.message.Lucky25Message; -import com.accompany.business.service.gift.Lucky25MessageService; +import com.accompany.business.service.gift.Lucky25MqService; import com.accompany.mq.constant.MqConstant; import com.accompany.mq.listener.AbstractMessageListener; import lombok.extern.slf4j.Slf4j; @@ -17,12 +17,12 @@ import org.springframework.stereotype.Component; public class Lucky25MessageConsumer extends AbstractMessageListener { @Autowired - private Lucky25MessageService messageService; + private Lucky25MqService messageService; @Override public void onMessage(Lucky25Message giftMessage) { log.info("onMessage lucky25Message: {}", giftMessage.toString()); - messageService.handleGiftMessage(giftMessage); + messageService.handleMq(giftMessage); } } diff --git a/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/luckyBag/Lucky25Task.java b/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/luckyBag/Lucky25Task.java index e10f63fdd..e29c12d36 100644 --- a/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/luckyBag/Lucky25Task.java +++ b/accompany-scheduler/accompany-scheduler-service/src/main/java/com/accompany/scheduler/task/luckyBag/Lucky25Task.java @@ -1,7 +1,7 @@ package com.accompany.scheduler.task.luckyBag; import com.accompany.business.message.Lucky25Message; -import com.accompany.business.service.gift.Lucky25MessageService; +import com.accompany.business.service.gift.Lucky25MqService; import com.accompany.business.service.lucky.Lucky25RecordService; import com.accompany.common.redis.RedisKey; import com.accompany.common.utils.DateTimeUtil; @@ -37,7 +37,7 @@ public class Lucky25Task { @Autowired private JedisService jedisService; @Autowired - private Lucky25MessageService messageService; + private Lucky25MqService mqService; /** * 重新消费队列的消息 @@ -58,7 +58,7 @@ public class Lucky25Task { String val = entry.getValue(); Lucky25Message giftMessage = JSON.parseObject(val, Lucky25Message.class); if (curTime - giftMessage.getCreateTime() > gapTime) { - messageService.handleGiftMessage(giftMessage); + mqService.handleMq(giftMessage); } } catch (Exception e) { log.error("retryLucky25Queue error", e);