幸运24-额外-分充值等级取值判断

This commit is contained in:
2025-10-14 11:55:26 +08:00
parent 4cbc4a0aea
commit 77737e198e
4 changed files with 112 additions and 78 deletions

View File

@@ -6,6 +6,7 @@ import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap;
@Data @Data
public class Lucky24GiftConfig { public class Lucky24GiftConfig {
@@ -63,11 +64,26 @@ public class Lucky24GiftConfig {
private BigDecimal storeRatio; private BigDecimal storeRatio;
private Integer startHour; private Integer startHour;
private Integer endHour; private Integer endHour;
@Deprecated
private Integer inputThreshold; private Integer inputThreshold;
@Deprecated
private Set<String> userRechargeLevels; private Set<String> userRechargeLevels;
@Deprecated
private BigDecimal todayProductionRatio; private BigDecimal todayProductionRatio;
@Deprecated
private Long todayDiff; private Long todayDiff;
@Deprecated
private Integer twoDayCountLimit; private Integer twoDayCountLimit;
private Integer dayCountLimit;
private List<Integer> timesJudgeArray;
private Map<String, UserRechargeLevelJudgeConfig> userRechargeLevelJudgeConfigMap;
@Data
public static class UserRechargeLevelJudgeConfig{
private int historyTimes;
private TreeMap<BigDecimal, Integer> productionRatioMultipleMap;
}
} }
} }

View File

@@ -102,22 +102,24 @@ public class Lucky24GiftSendService {
private Map<Long, Lucky24Record> draw(Lucky24GiftConfig config, Lucky24GiftConfig partitionConfig, long senderUid, Integer partitionId, private Map<Long, Lucky24Record> draw(Lucky24GiftConfig config, Lucky24GiftConfig partitionConfig, long senderUid, Integer partitionId,
Gift gift, int everyGiftNum, long everyoneGoldNum, Gift gift, int everyGiftNum, long everyoneGoldNum,
List<Long> receiverList, Room room, Date sendGiftTime, boolean extraSwitch) { List<Long> receiverList, Room room, Date sendGiftTime, boolean extraSwitch) {
if (!extraSwitch){ if (!extraSwitch
|| config.getBlackUidList().contains(senderUid)
|| userMetaService.getTimes(senderUid) <= (long) config.getNewUserPoolCount() * config.getPoolSize()){
return draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime); return draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
} }
Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig = partitionConfig.getExtraPoolConfig(); Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig = partitionConfig.getExtraPoolConfig();
Long extraLucker = extraService.selectExtraLucker(extraPoolConfig, senderUid, partitionId, everyoneGoldNum, receiverList); Long extraLuckerUid = extraService.selectExtraLucker(extraPoolConfig, senderUid, partitionId, receiverList);
if (null == extraLucker){ if (null == extraLuckerUid){
return draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime); return draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
} }
Lucky24Record extraRecord = extraService.randomExtraRecord(config, senderUid, partitionId, extraLucker, gift, everyGiftNum, everyoneGoldNum, room, sendGiftTime); Lucky24Record extraRecord = extraService.randomExtraRecord(config, extraPoolConfig, senderUid, partitionId, extraLuckerUid, gift, everyGiftNum, everyoneGoldNum, room, sendGiftTime);
List<Long> receiverUidList = new ArrayList<>(receiverList); List<Long> receiverUidList = new ArrayList<>(receiverList);
receiverUidList.remove(extraLucker); receiverUidList.remove(extraLuckerUid);
Map<Long, Lucky24Record> recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverUidList, room, sendGiftTime); Map<Long, Lucky24Record> recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverUidList, room, sendGiftTime);
recordMap.put(extraLucker, extraRecord); recordMap.put(extraLuckerUid, extraRecord);
return recordMap; return recordMap;
} }

View File

@@ -2,6 +2,7 @@ package com.accompany.business.service.lucky;
import com.accompany.business.constant.Lucky24PoolTypeEnum; import com.accompany.business.constant.Lucky24PoolTypeEnum;
import com.accompany.business.dto.lucky.Lucky24GiftConfig; import com.accompany.business.dto.lucky.Lucky24GiftConfig;
import com.accompany.business.dto.lucky.Lucky24Result;
import com.accompany.business.model.Gift; import com.accompany.business.model.Gift;
import com.accompany.common.utils.DateTimeUtil; import com.accompany.common.utils.DateTimeUtil;
import com.accompany.common.utils.RandomUtil; import com.accompany.common.utils.RandomUtil;
@@ -10,7 +11,9 @@ import com.accompany.core.model.Room;
import com.accompany.payment.service.UserRechargeLevelService; import com.accompany.payment.service.UserRechargeLevelService;
import com.accompany.sharding.model.Lucky24Record; import com.accompany.sharding.model.Lucky24Record;
import com.accompany.sharding.vo.Lucky24StockResultVo; import com.accompany.sharding.vo.Lucky24StockResultVo;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RList;
import org.redisson.api.RMap; 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;
@@ -18,9 +21,7 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Date; import java.util.*;
import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
@Service @Service
@@ -38,83 +39,109 @@ public class Lucky24ExtraService {
private Lucky24SettlementService settlementService; private Lucky24SettlementService settlementService;
@Autowired @Autowired
private Lucky24RecordService recordService; private Lucky24RecordService recordService;
public BigDecimal addStock(Integer partitionId, BigDecimal addScore) { public BigDecimal addStock(Integer partitionId, BigDecimal addScore) {
return stockService.addStock(partitionId, addScore); return stockService.addStock(partitionId, addScore);
} }
public Long selectExtraLucker(Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig, long senderUid, Integer partitionId, long everyoneGoldNum, List<Long> receiverList) { public Long selectExtraLucker(Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig, long senderUid, Integer partitionId, List<Long> receiverList) {
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId); PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId);
ZonedDateTime zdt = DateTimeUtil.getDateTimeByZoneId(partitionEnum.getZoneId()); ZonedDateTime zdt = DateTimeUtil.getDateTimeByZoneId(partitionEnum.getZoneId());
if (zdt.getHour() < extraPoolConfig.getStartHour() || zdt.getHour() > extraPoolConfig.getEndHour()){ if (zdt.getHour() < extraPoolConfig.getStartHour() || zdt.getHour() > extraPoolConfig.getEndHour()){
return null; return null;
} }
if (everyoneGoldNum < extraPoolConfig.getInputThreshold()){
return null;
}
String userRechargeLevel = userRechargeLevelService.getLevelByUid(senderUid);
if (!extraPoolConfig.getUserRechargeLevels().contains(userRechargeLevel)){
return null;
}
RMap<String, Number> userMetaMap = userMetaService.getUserMeta(senderUid); RMap<String, Number> userMetaMap = userMetaService.getUserMeta(senderUid);
Map<String, Number> userMetaSnapshot = userMetaMap.readAllMap(); Map<String, Number> userMetaSnapshot = userMetaMap.readAllMap();
long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId()); long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
String today = String.valueOf(todayStartTimeLong); String today = String.valueOf(todayStartTimeLong);
String todayInputKey = String.join("_", today, Lucky24UserMetaService.INPUT_KEY);
long todayInput = userMetaSnapshot.getOrDefault(todayInputKey, 0L).longValue();
String todayOutputKey = String.join("_", today, Lucky24UserMetaService.OUTPUT_KEY);
long todayOutput = userMetaSnapshot.getOrDefault(todayOutputKey, 0L).longValue();
BigDecimal todayProductionRatio = todayInput > 0L && todayOutput > 0L ? BigDecimal.valueOf(todayOutput).divide(BigDecimal.valueOf(todayInput), 4, RoundingMode.HALF_UP) : BigDecimal.ZERO; String todayDayCountKey = String.join("_", today, Lucky24UserMetaService.EXTRA_POOL_COUNT);
if (todayProductionRatio.compareTo(extraPoolConfig.getTodayProductionRatio()) >= 0){ int todayDayCount = userMetaSnapshot.getOrDefault(todayDayCountKey, 0).intValue();
log.info("[lucky24] extra todayProductionRation fail uid {} partitionId {} todayInput {} todayOutput {} pr {} configPr {}", if (todayDayCount >= extraPoolConfig.getDayCountLimit()){
senderUid, partitionId, todayInput, todayOutput, todayProductionRatio, extraPoolConfig.getTodayProductionRatio());
return null; return null;
} }
long todayDiff = todayInput - todayOutput; String todayTimesKey = String.join("_", today, Lucky24UserMetaService.TIMES_KEY);
if (todayDiff < extraPoolConfig.getTodayDiff()){ long todayTimes = userMetaSnapshot.getOrDefault(todayTimesKey, 0L).longValue();
log.info("[lucky24] extra todayDiff fail uid {} partitionId {} todayInput {} todayOutput {} todayDiff {} configTodayDiff {}",
senderUid, partitionId, todayInput, todayOutput, todayDiff, extraPoolConfig.getTodayDiff());
return null;
}
String yesterday = zdt.minusDays(1L).format(DateTimeUtil.dateFormatter); List<Integer> timesJudgeList = extraPoolConfig.getTimesJudgeArray();
String lastTwoDayKey = String.join("_", Lucky24UserMetaService.EXTRA_POOL_COUNT, yesterday); int maxJudgeTimes = timesJudgeList.get(timesJudgeList.size() - 1);
int lastTwoDayCount = userMetaSnapshot.getOrDefault(lastTwoDayKey, 0).intValue();
if (lastTwoDayCount >= extraPoolConfig.getTwoDayCountLimit()){
return null;
}
int expectedValue = lastTwoDayCount + 1; Long luckyer = null;
int result = userMetaMap.compute(lastTwoDayKey, (key, currentVal) -> {
if (currentVal == null || currentVal.intValue() == lastTwoDayCount) { long todayTimesBefore = todayTimes;
return expectedValue; for (Long receiver: receiverList){
long todayTimesAfter = todayTimesBefore + 1;
if (todayTimesBefore < maxJudgeTimes){
for (Integer judgeTimes: timesJudgeList){
if (todayTimesBefore < judgeTimes && todayTimesAfter >= judgeTimes){
luckyer = receiver;
break;
} else if (judgeTimes == maxJudgeTimes && todayTimesAfter >= maxJudgeTimes){
long beforeMod = todayTimesBefore % maxJudgeTimes;
long afterMod = todayTimesAfter % maxJudgeTimes;
if (beforeMod < afterMod){
luckyer = receiver;
break;
}
}
}
if (null != luckyer){
break;
}
} else {
long beforeMod = todayTimesBefore % maxJudgeTimes;
long afterMod = todayTimesAfter % maxJudgeTimes;
if (beforeMod < afterMod){
luckyer = receiver;
break;
}
} }
return currentVal; // 如果当前值不等于期望值,则返回原值,不做修改
}).intValue(); todayTimesBefore = todayTimesAfter;
if (result != expectedValue){ }
log.error("[lucky24] extra cas failure uid {} partitionId {} lastTwoDayKey {} expectedValue {} result {}",
senderUid, partitionId, lastTwoDayCount, expectedValue, result); if (null == luckyer){
return null; return null;
} }
String todayKey = String.join("_", Lucky24UserMetaService.EXTRA_POOL_COUNT, zdt.format(DateTimeUtil.dateFormatter)); // cas 当天剩余额外次数
int todayAfter = userMetaMap.addAndGet(todayKey, 1).intValue(); int afterValue = userMetaMap.addAndGet(todayDayCountKey, 1).intValue();
if (afterValue > extraPoolConfig.getDayCountLimit()){
log.error("[lucky24] extra addAndGet fail uid {} partitionId {} todayDayKey {} afterValue {}", senderUid, partitionId, todayDayCount, afterValue);
return null;
}
log.error("[lucky24] extra cas success uid {} partitionId {} lastTwoDayKey {} expectedValue {} result {} todayKey {} todayAfter {} lucker {}", log.info("[lucky24] extra addAndGet true uid {} partitionId {} todayDayKey {} afterValue {}", senderUid, partitionId, todayDayCount, afterValue);
senderUid, partitionId, lastTwoDayKey, expectedValue, result, todayKey, todayAfter, receiverList.get(0));
return receiverList.get(0); return luckyer;
} }
public Lucky24Record randomExtraRecord(Lucky24GiftConfig config, long senderUid, Integer partitionId, Long receiverUid, public Lucky24Record randomExtraRecord(Lucky24GiftConfig config, Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig, long senderUid, Integer partitionId, Long receiverUid,
Gift gift, int giftNum, long everyoneGoldNum, Room room, Date sendGiftTime) { Gift gift, int giftNum, long everyoneGoldNum, Room room, Date sendGiftTime) {
int random = RandomUtil.randomByRange(0, 10);
int drawMultiple = random < 5 ? 1000: random < 9 ? 500: 250; String userRechargeLevel = userRechargeLevelService.getLevelByUid(senderUid);
Lucky24GiftConfig.Lucky24ExtraPoolConfig.UserRechargeLevelJudgeConfig judgeConfig = extraPoolConfig.getUserRechargeLevelJudgeConfigMap().get(userRechargeLevel);
RList<Lucky24Result> historyQueue = userMetaService.getUserHistoryQueue(senderUid);
int historySize = historyQueue.size();
int num = Math.min(historySize, judgeConfig.getHistoryTimes());
List<Lucky24Result> historyResultList = historyQueue.range(num);
BigDecimal totalInput = historyResultList.stream().map(Lucky24Result::getInput).map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalOutput = historyResultList.stream().map(Lucky24Result::getOutput).map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalProductionRatio = totalOutput.compareTo(BigDecimal.ZERO) <= 0 || totalInput.compareTo(BigDecimal.ZERO) <= 0 ? BigDecimal.ZERO:
totalOutput.divide(totalInput, 4, RoundingMode.HALF_UP);
//nullable
Map.Entry<BigDecimal, Integer> drawMultipleEntry = judgeConfig.getProductionRatioMultipleMap().ceilingEntry(totalProductionRatio);
int drawMultiple = drawMultipleEntry == null ? 0: drawMultipleEntry.getValue();
log.info("[lucky24] extra randomExtra uid {} partitionId {} totalInput {} totalOutput {} totalProductionRatio {} drawMultipleEntry {} drawMultiple {}",
senderUid, partitionId, totalInput, totalOutput, totalProductionRatio, drawMultipleEntry, drawMultiple);
long afterMultiple = drawMultiple; long afterMultiple = drawMultiple;
@@ -132,8 +159,8 @@ public class Lucky24ExtraService {
userMetaService.updateExtraUserMeta(senderUid, partitionId, everyoneGoldNum, winGoldNum); userMetaService.updateExtraUserMeta(senderUid, partitionId, everyoneGoldNum, winGoldNum);
log.info("[lucky24] extra uid {} partitionId {} receiverUid {} random {} drawMultiple {} preWinGoldNum {} afterMultiple {} winGoldNum {}", log.info("[lucky24] extra uid {} partitionId {} receiverUid {} drawMultiple {} preWinGoldNum {} afterMultiple {} winGoldNum {}",
senderUid, partitionId, receiverUid, random, drawMultiple, preWinGoldNum, afterMultiple, winGoldNum); senderUid, partitionId, receiverUid, drawMultiple, preWinGoldNum, afterMultiple, winGoldNum);
return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null, return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null,
receiverUid, Lucky24PoolTypeEnum.EXTRA_POOL.getType(), null, receiverUid, Lucky24PoolTypeEnum.EXTRA_POOL.getType(), null,

View File

@@ -25,7 +25,7 @@ import java.util.Set;
@Service @Service
public class Lucky24UserMetaService { public class Lucky24UserMetaService {
private static final String TIMES_KEY = "times"; public static final String TIMES_KEY = "times";
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";
@@ -181,36 +181,25 @@ public class Lucky24UserMetaService {
long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId()); long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
String today = String.valueOf(todayStartTimeLong); String today = String.valueOf(todayStartTimeLong);
String todayInputKey = String.join("_", today, INPUT_KEY); String todayTimesKey = String.join("_", today, TIMES_KEY);
long todayInput = userMetaMap.addAndGet(todayInputKey, input).longValue(); long todayTimes = userMetaMap.addAndGet(todayTimesKey, 1L).longValue();
String todayOutputKey = String.join("_", today, OUTPUT_KEY);
long todayOutput = userMetaMap.addAndGet(todayOutputKey, output).longValue();
long userMetaToday = userMetaMap.computeIfAbsent(TODAY, k->todayStartTimeLong).longValue(); long userMetaToday = userMetaMap.computeIfAbsent(TODAY, k->todayStartTimeLong).longValue();
if (userMetaToday < todayStartTimeLong if (userMetaToday < todayStartTimeLong
&& userMetaMap.replace(TODAY, userMetaToday, todayStartTimeLong)){ && userMetaMap.replace(TODAY, userMetaToday, todayStartTimeLong)){
// 清理昨天 // 清理昨天
String oldToday = String.valueOf(userMetaToday); String oldToday = String.valueOf(userMetaToday);
String oldTodayTimesKey = String.join("_", oldToday, TIMES_KEY);
String oldTodayInputKey = String.join("_", oldToday, INPUT_KEY); String oldTodayInputKey = String.join("_", oldToday, INPUT_KEY);
String oldTodayOutputKey = String.join("_", oldToday, OUTPUT_KEY); String oldTodayOutputKey = String.join("_", oldToday, OUTPUT_KEY);
userMetaMap.fastRemove(oldTodayInputKey, oldTodayOutputKey);
// 清理额外线计数器 String oldTodayExtraPoolKey = String.join("_", oldToday, EXTRA_POOL_COUNT);
String todayExtraPoolKey = String.join("_", EXTRA_POOL_COUNT, today);
String yesterday = DateTimeUtil.getDateTimeByZoneId(partitionEnum.getZoneId()).minusDays(1L).format(DateTimeUtil.dateFormatter);
String yesterdayExtraPoolKey = String.join("_", EXTRA_POOL_COUNT, yesterday);
String extraPoolKeyPattern = EXTRA_POOL_COUNT + "*"; userMetaMap.fastRemove(oldTodayTimesKey, oldTodayInputKey, oldTodayOutputKey, oldTodayExtraPoolKey);
Set<String> extraPoolKeySet = userMetaMap.keySet(extraPoolKeyPattern);
for (String extraPoolKey : extraPoolKeySet){
if (!extraPoolKey.equals(todayExtraPoolKey) && !extraPoolKey.equals(yesterdayExtraPoolKey)){
userMetaMap.fastRemove(extraPoolKey);
}
}
} }
log.info("[Lucky24] updateUserMeta uid {} times {} today {} todayInput {} todayOutput {}", log.info("[Lucky24] updateUserMeta uid {} times {} today {} todayTimes {}",
senderUid, times, todayStartTimeLong, todayInput, todayOutput); senderUid, times, todayStartTimeLong, todayTimes);
} }
public RMap<String, Number> getUserMeta(Long uid) { public RMap<String, Number> getUserMeta(Long uid) {