幸运25-poolJudge

This commit is contained in:
khalil
2025-05-05 16:17:39 +08:00
parent f3f6f995d8
commit 017ea7bd69
5 changed files with 211 additions and 12 deletions

View File

@@ -9,21 +9,34 @@ import java.util.Map;
@Data
public class Lucky25GiftConfig {
private BigDecimal platformRatio;
private BigDecimal receiverRatio;
//n
private BigDecimal productionRatio_N;
private Map<Integer, Lucky25GiftConfig> ratioPartitionMap;
private BigDecimal platformRatio;
private BigDecimal receiverRatio;
//
private Integer poolSize = 500;
private Integer newUserPoolCount = 0;
//n
private BigDecimal productionRatio_N;
private BigDecimal todayInput;
private BigDecimal todayProductionRatio;
//
private Long highRechargeThreshold_M;
//
private Long judgeInputThreshold;
private BigDecimal highPoolEntreProductionRatio;
private BigDecimal highPoolQuitProductionRatio;
private BigDecimal lowPoolEntreProductionRatio;
private BigDecimal lowPoolQuitProductionRatio;
//
private Integer specialTipMulti;
private Long allRoomChatToastValue;

View File

@@ -1,5 +1,6 @@
package com.accompany.business.service.gift;
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.Gift;
@@ -31,6 +32,8 @@ public class Lucky25DrawService {
@Autowired
private Lucky25HighRechargePoolService highRechargePoolService;
@Autowired
private Lucky25PoolJudgeService poolJudgeService;
@Autowired
private Lucky25RecordService recordService;
@Autowired
private Lucky25SettlementService settlementService;
@@ -46,9 +49,10 @@ public class Lucky25DrawService {
long totalGoldNum = everyoneGoldNum * receiverList.size();
RMap<String, Number> userMetaMap = userMetaService.getUserMeta(senderUid);
long afterTotalInput = userMetaMap.addAndGet(Lucky25UserMetaService.INPUT_KEY, totalGoldNum).longValue();
long beforeTotalInput = afterTotalInput - totalGoldNum;
Lucky25Result highRechargeResult = drawHighRechargeUserPool(config, senderUid, partitionId,
totalGoldNum, afterTotalInput);
beforeTotalInput, afterTotalInput);
if (null != highRechargeResult){
Long receiver = receiverList.remove(0);
highRechargeResult.setReceiverUid(receiver);
@@ -83,6 +87,10 @@ public class Lucky25DrawService {
}
// todo 比较
if (null != config.getJudgeInputThreshold() && beforeTotalInput > config.getJudgeInputThreshold()){
int curPoolType = userMetaMap.getOrDefault(Lucky25UserMetaService.POOL_TYPE, Lucky25PoolTypeEnum.NEW_USER_POOL.getType()).intValue();
poolJudgeService.judge(config, senderUid, partitionId, curPoolType);
}
// todo 原数组
@@ -93,13 +101,15 @@ public class Lucky25DrawService {
.collect(Collectors.toList());
}
private Lucky25Result drawHighRechargeUserPool(Lucky25GiftConfig config, long uid, int partitionId, long totalGoldNum, long afterInput) {
private Lucky25Result drawHighRechargeUserPool(Lucky25GiftConfig config, long uid, int partitionId, long beforeInput, long afterInput) {
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
if (null == partitionConfig || null == partitionConfig.getHighRechargeThreshold_M()){
return null;
}
long beforeInput = afterInput - totalGoldNum;
if (beforeInput <= 0L){
return null;
}
long beforeThreshold = beforeInput / partitionConfig.getHighRechargeThreshold_M();
long afterThreshold = afterInput / partitionConfig.getHighRechargeThreshold_M();
if (beforeThreshold >= afterThreshold){

View File

@@ -0,0 +1,77 @@
package com.accompany.business.service.gift;
import com.accompany.business.constant.Lucky25PoolTypeEnum;
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
import com.accompany.business.service.lucky.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Slf4j
@Service
public class Lucky25PoolJudgeService {
@Autowired
private Lucky25UserMetaService userMetaService;
@Autowired
private Lucky25PoolService poolService;
public void judge(Lucky25GiftConfig config, Long senderUid, int partitionId, int curPoolType) {
if (curPoolType == Lucky25PoolTypeEnum.NEW_USER_POOL.getType()
|| curPoolType == Lucky25PoolTypeEnum.BLACK_POOL.getType()){
return;
}
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
BigDecimal judgeProductionRatio = userMetaService.getUserJudgeProductionRatio(senderUid, config.getJudgeInputThreshold());
if (curPoolType == Lucky25PoolTypeEnum.NORMAL_POOL.getType()){
if (null != partitionConfig.getHighPoolEntreProductionRatio() && judgeProductionRatio.compareTo(partitionConfig.getHighPoolEntreProductionRatio()) < 0){
if (!userMetaService.casCurPoolType(senderUid, curPoolType, Lucky25PoolTypeEnum.HIGH_POOL.getType())){
//todo log failure
return;
}
poolService.delPoolAndGenPool(config, senderUid, Lucky25PoolTypeEnum.HIGH_POOL.getType());
return;
} else if (null != partitionConfig.getLowPoolEntreProductionRatio() && judgeProductionRatio.compareTo(partitionConfig.getLowPoolEntreProductionRatio()) > 0) {
if (!userMetaService.casCurPoolType(senderUid, curPoolType, Lucky25PoolTypeEnum.LOW_POOL.getType())){
//todo log failure
return;
}
poolService.delPoolAndGenPool(config, senderUid, Lucky25PoolTypeEnum.LOW_POOL.getType());
return;
}
//todo log
} else if (curPoolType == Lucky25PoolTypeEnum.HIGH_POOL.getType()) {
if (null != partitionConfig.getHighPoolQuitProductionRatio() && judgeProductionRatio.compareTo(partitionConfig.getHighPoolQuitProductionRatio()) > 0){
if (!userMetaService.casCurPoolType(senderUid, curPoolType, Lucky25PoolTypeEnum.NEW_USER_POOL.getType())){
//todo log failure
return;
}
poolService.delPoolAndGenPool(config, senderUid, Lucky25PoolTypeEnum.NEW_USER_POOL.getType(), 4);
return;
}
//todo log
} else if (curPoolType == Lucky25PoolTypeEnum.LOW_POOL.getType()) {
if (null != partitionConfig.getLowPoolQuitProductionRatio() && judgeProductionRatio.compareTo(partitionConfig.getLowPoolQuitProductionRatio()) < 0){
if (!userMetaService.casCurPoolType(senderUid, curPoolType, Lucky25PoolTypeEnum.HIGH_POOL.getType())){
//todo log failure
return;
}
poolService.delPoolAndGenPool(config, senderUid, Lucky25PoolTypeEnum.NEW_USER_POOL.getType(), 4);
return;
}
//todo log
}
}
}

View File

@@ -50,6 +50,57 @@ public class Lucky25PoolService {
throw new ServiceException(BusiStatus.SERVERBUSY);
}
public void delPoolAndGenPool(Lucky25GiftConfig config, Long uid, int poolType){
delPoolAndGenPool(config, uid, poolType, 1);
}
public void delPoolAndGenPool(Lucky25GiftConfig config, Long uid, int poolType, int genNum){
RQueue<Lucky25Result> userPool = getUserPool(uid);
userPool.clear();
genRandomPoolByType(config, uid, userPool, poolType, genNum);
}
private void genRandomPoolByType(Lucky25GiftConfig config, Long uid, RQueue<Lucky25Result> userPool,
Integer pooType, int genNum) {
boolean locked = false;
RLock lock = redissonClient.getLock(RedisKey.lucky_25_user_lock.getKey(uid.toString()));
try {
locked = lock.tryLock(5,3, TimeUnit.SECONDS);
if (!userPool.isEmpty()){
log.info("[lucky25] genRandomPoolByType uid {} 已生成pool无需再生成", uid);
return;
}
if (!locked) {
throw new ServiceException(BusiStatus.SERVERBUSY);
}
Lucky25PoolTypeEnum poolType = Lucky25PoolTypeEnum.get(pooType);
List<Lucky25Pool> poolList = poolMapper.selectList(null);
if (CollectionUtils.isEmpty(poolList)){
throw new ServiceException(BusiStatus.SEIZE_TREASURE_POOL_CONFIG_ERROR);
}
for (int i = 0; i < genNum; i++) {
int randomIndex = RandomUtil.randomByRange(0, poolList.size());
Lucky25Pool randomPool = poolList.get(randomIndex);
log.info("[lucky25] genRandomPoolByType selectPool type {}, index {}, pooList {}, randomIndex {}, expect {}",
poolType, i, JSON.toJSONString(poolList.stream().map(Lucky25Pool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
buildPool(config, uid, userPool, randomPool);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if (locked) {
lock.unlock();
}
}
}
private void genPool(Lucky25GiftConfig config, Long uid, int partitionId, RQueue<Lucky25Result> userPool) {
boolean locked = false;
RLock lock = redissonClient.getLock(RedisKey.lucky_25_user_lock.getKey(uid.toString()));

View File

@@ -16,6 +16,7 @@ import java.math.RoundingMode;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
@@ -25,13 +26,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";
public static final String POOL_TYPE = "pool_type";
private static final String TODAY = "today";
public static final String ADMIN_1000_NUM_KEY = "admin_1000_num";
private static final BigDecimal HISTORY_QUEUE_INPUT_MAX = BigDecimal.valueOf(100000L);
private final int HISTORY_MIN_SIZE = 2000;
@Autowired
private RedissonClient redissonClient;
@@ -40,6 +41,53 @@ public class Lucky25UserMetaService {
return getUserMeta(uid).getOrDefault(TIMES_KEY, 0L).longValue();
}
public boolean casCurPoolType(long uid, Integer beforePoolType, Integer afterPoolType){
Number result = getUserMeta(uid).computeIfPresent(POOL_TYPE, (key, value)-> value.equals(beforePoolType)? afterPoolType: value);
boolean success = !Objects.equals(result, beforePoolType);
if (success){
//todo log
} else {
//todo log failure
}
return success;
}
public BigDecimal getUserJudgeProductionRatio(Long uid, Long judgeInputThreshold) {
RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid);
List<Lucky25Result> historyResult = historyQueue.readAll();
int historySize = historyResult.size();
int index = 0;
long historyInput = 0L;
long historyOutput = 0L;
for (Lucky25Result result : historyResult){
index++;
historyInput += result.getInput();
historyOutput += result.getOutput();
if (historyInput >= judgeInputThreshold){
break;
}
}
BigDecimal historyInputB = BigDecimal.valueOf(historyInput);
BigDecimal historyOutputB = BigDecimal.valueOf(historyOutput);
BigDecimal productionRatio = historyOutputB.compareTo(BigDecimal.ZERO) > 0 ?
historyOutputB.divide(historyInputB,4, RoundingMode.HALF_UP): BigDecimal.ZERO;
log.info("[lucky25] userJudgeProductionRation uid {}, historyInput {}, historyOutput {}, num {}, productionRatio {}",
uid, historyInput, historyOutput, index, productionRatio);
if (index >= historySize || HISTORY_MIN_SIZE >= historySize){
return productionRatio;
}
int len = Math.max(index, HISTORY_MIN_SIZE-1);
historyQueue.trimAsync(0, len);
return productionRatio;
}
public BigDecimal getUserLast2000ProductionRatio(Long uid) {
RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid);
//保留2000条记录
@@ -51,7 +99,7 @@ public class Lucky25UserMetaService {
BigDecimal historyOutputB = BigDecimal.valueOf(historyOutput);
BigDecimal productionRatio = historyOutputB.compareTo(BigDecimal.ZERO) > 0 ?
historyOutputB.divide(historyInputB,2, RoundingMode.HALF_UP): BigDecimal.ZERO;
historyOutputB.divide(historyInputB,4, RoundingMode.HALF_UP): BigDecimal.ZERO;
log.info("[lucky25] buildPool last2000 uid {}, historyInput {}, historyOutput {}, num {}, productionRatio {}",
uid, historyInput, historyOutput, num, productionRatio);
return productionRatio;
@@ -61,7 +109,7 @@ public class Lucky25UserMetaService {
RMap<String, Number> userMetaMap = getUserMeta(uid);
BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(OUTPUT_KEY, 0L).longValue());
BigDecimal input = BigDecimal.valueOf(userMetaMap.getOrDefault(INPUT_KEY, 0L).longValue());
BigDecimal productionRatio = BigDecimal.ZERO.equals(output)? BigDecimal.ZERO: output.divide(input,2,RoundingMode.HALF_UP);
BigDecimal productionRatio = BigDecimal.ZERO.equals(output)? BigDecimal.ZERO: output.divide(input,4,RoundingMode.HALF_UP);
log.info("[lucky25] buildPool total uid {}, totalOutput {}, totalInput {}, productionRatio {}",
uid, output, input, productionRatio);
return productionRatio;