幸运25-大R和后台赠送

This commit is contained in:
khalil
2025-05-04 19:11:13 +08:00
parent 8a1721706a
commit f3f6f995d8
14 changed files with 558 additions and 445 deletions

View File

@@ -1451,7 +1451,7 @@ public enum RedisKey {
lucky_25_robot_push_msg, lucky_25_robot_push_msg,
lucky_25_status, // 礼物消息的状态 lucky_25_status, // 礼物消息的状态
lock_lucky_25_message, // 消费送礼物消息锁 lock_lucky_25_message, // 消费送礼物消息锁
lucky_25_user_10w_stat, // 消费送礼物消息锁 lucky_25_high_recharge_pool,
; ;
public String getKey() { public String getKey() {

View File

@@ -24,6 +24,7 @@ public enum Lucky25PoolTypeEnum {
BLACK_POOL(3, "黑名单奖池"), BLACK_POOL(3, "黑名单奖池"),
HIGH_POOL(4, "保底奖池"), HIGH_POOL(4, "保底奖池"),
LOW_POOL(5, "衰减奖池"), LOW_POOL(5, "衰减奖池"),
HIGH_INPUT_POOL(6, "大R奖池"),
; ;
/** /**

View File

@@ -12,23 +12,18 @@ public class Lucky25GiftConfig {
private BigDecimal platformRatio; private BigDecimal platformRatio;
private BigDecimal receiverRatio; private BigDecimal receiverRatio;
//n //n
private BigDecimal productionRatio; private BigDecimal productionRatio_N;
private Map<Integer, Lucky25GiftConfig> ratioPartitionMap; private Map<Integer, Lucky25GiftConfig> 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 poolSize = 500;
private Integer newUserPoolCount = 0; private Integer newUserPoolCount = 0;
private BigDecimal todayInput;
private BigDecimal todayProductionRatio; private BigDecimal todayProductionRatio;
private Long highRechargeThreshold_M;
private Integer specialTipMulti; private Integer specialTipMulti;
private Long allRoomChatToastValue; private Long allRoomChatToastValue;
@@ -42,8 +37,6 @@ public class Lucky25GiftConfig {
private Map<Long, BigDecimal> whiteUidProductionRatioMap; private Map<Long, BigDecimal> whiteUidProductionRatioMap;
private List<Long> blackUidList; private List<Long> blackUidList;
private String diamondIcon;
public Lucky25GiftConfig getRatioByPartitionId(Integer partitionId){ public Lucky25GiftConfig getRatioByPartitionId(Integer partitionId){
return ratioPartitionMap.getOrDefault(partitionId, this); return ratioPartitionMap.getOrDefault(partitionId, this);
} }

View File

@@ -1,12 +1,21 @@
package com.accompany.business.dto.lucky; package com.accompany.business.dto.lucky;
import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor @NoArgsConstructor
public class Lucky25Result extends Lucky24Result { public class Lucky25Result extends Lucky24Result {
private Long receiverUid;
public Lucky25Result(Integer poolId, Long input, long output, Boolean isSupplement) { public Lucky25Result(Integer poolId, Long input, long output, Boolean isSupplement) {
super(poolId, input, output, 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;
}
} }

View File

@@ -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<Lucky25Record> draw(Lucky25GiftConfig config, Long senderUid, int partitionId,
Gift gift, int everyGiftNum, long everyoneGoldNum,
List<Long> receiverList, Room room, Date sendGiftTime) {
List<Lucky25Result> resultList = new ArrayList<>();
// 大R
long totalGoldNum = everyoneGoldNum * receiverList.size();
RMap<String, Number> 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;
}
}

View File

@@ -1,36 +1,23 @@
package com.accompany.business.service.gift; package com.accompany.business.service.gift;
import com.accompany.business.dto.lucky.*; import com.accompany.business.dto.lucky.Lucky25GiftConfig;
import com.accompany.business.message.Lucky24Message; import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
import com.accompany.business.message.Lucky25Message;
import com.accompany.business.model.Gift; import com.accompany.business.model.Gift;
import com.accompany.business.service.lucky.*; import com.accompany.business.service.lucky.*;
import com.accompany.business.service.mq.RocketMQService;
import com.accompany.common.constant.Constant; import com.accompany.common.constant.Constant;
import com.accompany.common.redis.RedisKey;
import com.accompany.common.status.BusiStatus; import com.accompany.common.status.BusiStatus;
import com.accompany.common.utils.RandomUtil;
import com.accompany.core.exception.ServiceException; import com.accompany.core.exception.ServiceException;
import com.accompany.core.model.Room; import com.accompany.core.model.Room;
import com.accompany.core.service.SysConfService; import com.accompany.core.service.SysConfService;
import com.accompany.core.service.common.JedisService;
import com.accompany.sharding.model.Lucky25Record; import com.accompany.sharding.model.Lucky25Record;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMap;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@@ -41,21 +28,11 @@ public class Lucky25GiftSendService {
@Autowired @Autowired
private Lucky25StockService stockService; private Lucky25StockService stockService;
@Autowired @Autowired
private Lucky25UserMetaService userMetaService; private Lucky25DrawService drawService;
@Autowired
private Lucky25PoolService poolService;
@Autowired
private Lucky25RecordService recordService;
@Autowired @Autowired
private Lucky25IncomeAllotService incomeAllotService; private Lucky25IncomeAllotService incomeAllotService;
@Autowired @Autowired
private Lucky25SettlementService settlementService; private Lucky25MqService mqService;
@Autowired
private Lucky25RobotMsgService robotMsgService;
@Autowired
private RocketMQService rocketMQService;
@Autowired
private JedisService jedisService;
public void draw(long senderUid, Integer partitionId, Room room, List<Long> receiverList, public void draw(long senderUid, Integer partitionId, Room room, List<Long> receiverList,
Gift gift, int everyGiftNum, Date sendGiftTime) { Gift gift, int everyGiftNum, Date sendGiftTime) {
@@ -63,190 +40,18 @@ public class Lucky25GiftSendService {
long everyoneGoldNum = everyGiftNum * gift.getGoldPrice(); long everyoneGoldNum = everyGiftNum * gift.getGoldPrice();
Lucky25GiftConfig config = getConfig(); Lucky25GiftConfig config = getConfig();
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId); SuperLuckyGiftIncomeAllot incomeAllot = incomeAllotService.calculate(config, partitionId, gift, everyGiftNum, receiverList);
SuperLuckyGiftIncomeAllot incomeAllot = incomeAllotService.calculate(partitionConfig, gift, everyGiftNum, receiverList);
// 增加库存 // 增加库存
BigDecimal afterStock = stockService.addStock(partitionId, incomeAllot.getRemainValue()); BigDecimal afterStock = stockService.addStock(partitionId, incomeAllot.getRemainValue());
log.info("[lucky25] uid {}, partitionId {}, addStockGoldNum {}, afterStock {}", log.info("[lucky25] uid {}, partitionId {}, addStockGoldNum {}, afterStock {}",
senderUid, partitionId, incomeAllot.getRemainValue(), afterStock); senderUid, partitionId, incomeAllot.getRemainValue(), afterStock);
Map<Long, Lucky25Record> recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime); List<Lucky25Record> recordList = drawService.draw(config, senderUid, partitionId,
log.info("[lucky25] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordMap)); gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
log.info("[lucky25] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordList));
sendMq(recordMap); mqService.sendMq(recordList);
}
public Map<Long, Lucky25Record> draw(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long senderUid, int partitionId,
Gift gift, int everyGiftNum, long everyoneGoldNum,
List<Long> 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<String, Number> userStatCacheMap = userMetaService.getUser10wStat(senderUid);
Map<String, Number> userStatMap = userStatCacheMap.readAllMap();
if (userStatMap.isEmpty()){
List<Lucky25Result> 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<Long, Lucky25Record> recordMap) {
Map<String, String> caches = new HashMap<>(recordMap.size());
List<Lucky25Message> 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;
} }
public Lucky25GiftConfig getConfig() { public Lucky25GiftConfig getConfig() {

View File

@@ -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;
}
}

View File

@@ -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<String, String> statusMap;
public void sendMq(List<Lucky25Record> recordList) {
Map<String, String> caches = new HashMap<>(recordList.size());
List<Lucky25Message> 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());
}
}

View File

@@ -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<Lucky25Result> 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<Lucky25Result> 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<Lucky25Pool> poolList = poolMapper.selectList(Wrappers.<Lucky25Pool>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<Lucky25Result> partitionPool, Lucky25Pool pool) {
List<Lucky25Pool.Lucky24PoolItem> poolItemList = pool.getItemList().stream().filter(item->item.getNum()>0).collect(Collectors.toList());
List<Integer> winList = buildWinList(poolItemList);
int[] poolArray = new int[config.getPoolSize()];
for (int i = 0; i < config.getPoolSize(); i++) {
poolArray[i] = 0;
}
if (!winList.isEmpty()){
List<Integer> 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<Lucky25Result> 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<Integer> buildWinList(List<Lucky25Pool.Lucky24PoolItem> poolItemList) {
List<Integer> 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<Lucky25Result> getHighRechargePool(Integer partitionId) {
return redissonClient.getDeque(RedisKey.lucky_25_high_recharge_pool.getKey(partitionId.toString()));
}
}

View File

@@ -25,7 +25,10 @@ public class Lucky25IncomeAllotService implements LuckyGiftIncomeAllotService {
@Autowired @Autowired
private BillRecordService billRecordService; private BillRecordService billRecordService;
public SuperLuckyGiftIncomeAllot calculate(Lucky25GiftConfig config, Gift gift, int giftNum, List<Long> receiverUids) { public SuperLuckyGiftIncomeAllot calculate(Lucky25GiftConfig config, Integer partitionId,
Gift gift, int giftNum, List<Long> receiverUids) {
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
BigDecimal giftNumB = BigDecimal.valueOf(giftNum); BigDecimal giftNumB = BigDecimal.valueOf(giftNum);
BigDecimal totalNum = BigDecimal.valueOf(receiverUids.size()).multiply(giftNumB); BigDecimal totalNum = BigDecimal.valueOf(receiverUids.size()).multiply(giftNumB);
// 单价 // 单价
@@ -35,9 +38,9 @@ public class Lucky25IncomeAllotService implements LuckyGiftIncomeAllotService {
BigDecimal totalValue = giftValue.multiply(totalNum); 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<Long, BigDecimal> receiverIncomeMap = new HashMap<>(); Map<Long, BigDecimal> receiverIncomeMap = new HashMap<>();
for (Long receiver: receiverUids){ 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); BigDecimal addStockGoldNum = totalValue.subtract(platformGoldNum);

View File

@@ -3,8 +3,6 @@ package com.accompany.business.service.lucky;
import com.accompany.business.constant.Lucky25PoolTypeEnum; import com.accompany.business.constant.Lucky25PoolTypeEnum;
import com.accompany.business.dto.lucky.Lucky25GiftConfig; import com.accompany.business.dto.lucky.Lucky25GiftConfig;
import com.accompany.business.dto.lucky.Lucky25Result; 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.model.lucky.Lucky25Pool;
import com.accompany.business.mybatismapper.lucky.Lucky25PoolMapper; import com.accompany.business.mybatismapper.lucky.Lucky25PoolMapper;
import com.accompany.common.redis.RedisKey; import com.accompany.common.redis.RedisKey;
@@ -173,15 +171,15 @@ public class Lucky25PoolService {
String todayInputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.INPUT_KEY); String todayInputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.INPUT_KEY);
String todayOutputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.OUTPUT_KEY); String todayOutputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.OUTPUT_KEY);
RMap<String, Number> userMetaMap = userMetaService.getUserMeta(uid); Map<String, Number> userMetaMap = userMetaService.getUserMeta(uid).readAllMap();
BigDecimal input = BigDecimal.valueOf(userMetaMap.getOrDefault(todayInputKey, 0L).longValue()); BigDecimal input = BigDecimal.valueOf(userMetaMap.getOrDefault(todayInputKey, 0L).longValue());
BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(todayOutputKey, 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); 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<Lucky25Pool> excludePoolList = poolList.stream() List<Lucky25Pool> excludePoolList = poolList.stream()
.filter(pool->pool.getType() == Lucky25PoolTypeEnum.NORMAL_POOL.getType()) .filter(pool->pool.getType() == Lucky25PoolTypeEnum.NORMAL_POOL.getType())
.filter(pool-> pool.getItemList().stream().noneMatch(poolItem->poolItem.getMulti() == 1000 && poolItem.getNum() > 0)) .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()); .collect(Collectors.toList());
int randomIndex = RandomUtil.randomByRange(0, excludePoolList.size()); int randomIndex = RandomUtil.randomByRange(0, excludePoolList.size());
@@ -215,7 +213,7 @@ public class Lucky25PoolService {
private BigDecimal calExpect(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long uid) { private BigDecimal calExpect(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long uid) {
BigDecimal n = !CollectionUtils.isEmpty(config.getWhiteUidProductionRatioMap()) && config.getWhiteUidProductionRatioMap().containsKey(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); BigDecimal userLast2000ProductionRatio = userMetaService.getUserLast2000ProductionRatio(uid);
if (userLast2000ProductionRatio.compareTo(n) <= 0){ if (userLast2000ProductionRatio.compareTo(n) <= 0){
@@ -260,42 +258,4 @@ public class Lucky25PoolService {
return redissonClient.getDeque(RedisKey.lucky_25_user_pool.getKey(uid.toString())); return redissonClient.getDeque(RedisKey.lucky_25_user_pool.getKey(uid.toString()));
} }
public void updateUserMulti(Long uid) {
RDeque<Lucky25Result> 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<Lucky25Result> 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();
}
}
}
} }

View File

@@ -1,6 +1,5 @@
package com.accompany.business.service.lucky; package com.accompany.business.service.lucky;
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
import com.accompany.business.dto.lucky.Lucky25Result; import com.accompany.business.dto.lucky.Lucky25Result;
import com.accompany.common.redis.RedisKey; import com.accompany.common.redis.RedisKey;
import com.accompany.common.utils.DateTimeUtil; import com.accompany.common.utils.DateTimeUtil;
@@ -26,9 +25,13 @@ public class Lucky25UserMetaService {
public static final String INPUT_KEY = "input"; public static final String INPUT_KEY = "input";
public static final String OUTPUT_KEY = "output"; public static final String OUTPUT_KEY = "output";
private static final String POOL_TYPE = "pool_type";
private static final String TODAY = "today"; 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 @Autowired
private RedissonClient redissonClient; private RedissonClient redissonClient;
@@ -39,33 +42,21 @@ public class Lucky25UserMetaService {
public BigDecimal getUserLast2000ProductionRatio(Long uid) { public BigDecimal getUserLast2000ProductionRatio(Long uid) {
RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid); RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid);
int size = historyQueue.size(); //保留2000条记录
//保留20000条记录 int num = 2000;
int num = Math.min(size, 2000);
List<Lucky25Result> historyResult = historyQueue.range(num); List<Lucky25Result> historyResult = historyQueue.range(num);
long historyInput = historyResult.stream().limit(num).mapToLong(Lucky25Result::getInput).sum(); 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(); 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 {}", log.info("[lucky25] buildPool last2000 uid {}, historyInput {}, historyOutput {}, num {}, productionRatio {}",
uid, historyInput, historyOutput, num, productionRatio); uid, historyInput, historyOutput, num, productionRatio);
if (size > HISTORY_QUEUE_SIZE){
historyQueue.trim(0, HISTORY_QUEUE_SIZE);
}
return productionRatio; return productionRatio;
} }
public List<Lucky25Result> getUserLast10wHistory(Long uid) {
RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid);
int size = historyQueue.size();
//保留20000条记录
int num = Math.min(size, HISTORY_QUEUE_SIZE);
List<Lucky25Result> historyResult = historyQueue.range(num);
if (size > HISTORY_QUEUE_SIZE){
historyQueue.trim(0, HISTORY_QUEUE_SIZE);
}
return historyResult;
}
public BigDecimal getUserProductionRatio(Long uid) { public BigDecimal getUserProductionRatio(Long uid) {
RMap<String, Number> userMetaMap = getUserMeta(uid); RMap<String, Number> userMetaMap = getUserMeta(uid);
BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(OUTPUT_KEY, 0L).longValue()); BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(OUTPUT_KEY, 0L).longValue());
@@ -76,51 +67,21 @@ public class Lucky25UserMetaService {
return productionRatio; 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<String, Number> 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) { public void updateUserMeta(long senderUid, int partitionId, long input, long output) {
RList<Lucky25Result> historyQueue = getUserHistoryQueue(senderUid); RList<Lucky25Result> historyQueue = getUserHistoryQueue(senderUid);
historyQueue.add(0, new Lucky25Result(null, input, output, null)); historyQueue.add(0, new Lucky25Result(null, input, output, null));
historyQueue.expire(Duration.of(14, ChronoUnit.DAYS)); historyQueue.expire(Duration.of(14, ChronoUnit.DAYS));
if (output > 0L){
getUser10wStat(senderUid).delete();
}
RMap<String, Number> userMetaMap = getUserMeta(senderUid); RMap<String, Number> userMetaMap = getUserMeta(senderUid);
long times = userMetaMap.addAndGet(TIMES_KEY, 1L).longValue(); 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(); long totalOutput = userMetaMap.addAndGet(OUTPUT_KEY, output).longValue();
log.info("[lucky25] updateUserMeta uid {} times {} totalInput {} totalOutput {}", //log.info("[lucky25] updateUserMeta uid {} times {} totalInput {} totalOutput {}",
senderUid, times, totalInput, totalOutput); // senderUid, times, totalInput, totalOutput);
log.info("[lucky25] updateUserMeta uid {} times {} totalOutput {}",
senderUid, times, totalOutput);
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId); PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId);
long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId()); long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
@@ -145,6 +106,32 @@ public class Lucky25UserMetaService {
senderUid, times, todayStartTimeLong, todayInput, todayOutput); senderUid, times, todayStartTimeLong, todayInput, todayOutput);
} }
public int subAdminTicketNum(long uid, int superTicketNum) {
RMap<String, Number> 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<String, Number> 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<String, Number> getUserMeta(Long uid) { public RMap<String, Number> getUserMeta(Long uid) {
return redissonClient.getMap(RedisKey.lucky_25_user_meta.getKey(uid.toString())); 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())); return redissonClient.getList(RedisKey.lucky_25_user_history.getKey(uid.toString()));
} }
public RMap<String, Number> getUser10wStat(Long uid) {
return redissonClient.getMapCache(RedisKey.lucky_25_user_10w_stat.getKey(uid.toString()));
}
} }

View File

@@ -1,7 +1,7 @@
package com.accompany.mq.consumer; package com.accompany.mq.consumer;
import com.accompany.business.message.Lucky25Message; 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.constant.MqConstant;
import com.accompany.mq.listener.AbstractMessageListener; import com.accompany.mq.listener.AbstractMessageListener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -17,12 +17,12 @@ import org.springframework.stereotype.Component;
public class Lucky25MessageConsumer extends AbstractMessageListener<Lucky25Message> { public class Lucky25MessageConsumer extends AbstractMessageListener<Lucky25Message> {
@Autowired @Autowired
private Lucky25MessageService messageService; private Lucky25MqService messageService;
@Override @Override
public void onMessage(Lucky25Message giftMessage) { public void onMessage(Lucky25Message giftMessage) {
log.info("onMessage lucky25Message: {}", giftMessage.toString()); log.info("onMessage lucky25Message: {}", giftMessage.toString());
messageService.handleGiftMessage(giftMessage); messageService.handleMq(giftMessage);
} }
} }

View File

@@ -1,7 +1,7 @@
package com.accompany.scheduler.task.luckyBag; package com.accompany.scheduler.task.luckyBag;
import com.accompany.business.message.Lucky25Message; 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.business.service.lucky.Lucky25RecordService;
import com.accompany.common.redis.RedisKey; import com.accompany.common.redis.RedisKey;
import com.accompany.common.utils.DateTimeUtil; import com.accompany.common.utils.DateTimeUtil;
@@ -37,7 +37,7 @@ public class Lucky25Task {
@Autowired @Autowired
private JedisService jedisService; private JedisService jedisService;
@Autowired @Autowired
private Lucky25MessageService messageService; private Lucky25MqService mqService;
/** /**
* 重新消费队列的消息 * 重新消费队列的消息
@@ -58,7 +58,7 @@ public class Lucky25Task {
String val = entry.getValue(); String val = entry.getValue();
Lucky25Message giftMessage = JSON.parseObject(val, Lucky25Message.class); Lucky25Message giftMessage = JSON.parseObject(val, Lucky25Message.class);
if (curTime - giftMessage.getCreateTime() > gapTime) { if (curTime - giftMessage.getCreateTime() > gapTime) {
messageService.handleGiftMessage(giftMessage); mqService.handleMq(giftMessage);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("retryLucky25Queue error", e); log.error("retryLucky25Queue error", e);