幸运24-额外
This commit is contained in:
@@ -1355,6 +1355,8 @@ public enum RedisKey {
|
||||
lock_lucky_24_message, // 消费送礼物消息锁
|
||||
lucky_24_user_10w_stat, // 消费送礼物消息锁
|
||||
|
||||
lucky_24_extra_stock,
|
||||
|
||||
family_diamond_settlement,
|
||||
|
||||
room_avatar_under_review,//头像:审核中的任务
|
||||
|
@@ -22,6 +22,7 @@ public enum Lucky24PoolTypeEnum {
|
||||
*/
|
||||
NORMAL_POOL(2, "普通奖池"),
|
||||
BLACK_POOL(3, "黑名单奖池"),
|
||||
EXTRA_POOL(4, "额外奖池"),
|
||||
;
|
||||
|
||||
/**
|
||||
|
@@ -5,6 +5,7 @@ import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
public class Lucky24GiftConfig {
|
||||
@@ -24,6 +25,8 @@ public class Lucky24GiftConfig {
|
||||
private BigDecimal totalInputOffset_K1;
|
||||
private BigDecimal totalInputOffset_K2;
|
||||
|
||||
private Lucky24ExtraPoolConfig extraPoolConfig;
|
||||
|
||||
private Integer poolSize = 500;
|
||||
private Integer newUserPoolCount = 0;
|
||||
|
||||
@@ -48,4 +51,17 @@ public class Lucky24GiftConfig {
|
||||
return ratioPartitionMap.getOrDefault(partitionId, this);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Lucky24ExtraPoolConfig{
|
||||
private Boolean isOpen;
|
||||
private BigDecimal storeRatio;
|
||||
private Integer startHour;
|
||||
private Integer endHour;
|
||||
private Integer inputThreshold;
|
||||
private Set<String> userRechargeLevels;
|
||||
private BigDecimal todayProductionRatio;
|
||||
private Long todayDiff;
|
||||
private Integer twoDayCountLimit;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -16,4 +16,13 @@ public class SuperLuckyGiftIncomeAllot {
|
||||
private BigDecimal roomOwnerTotalIncome;
|
||||
private BigDecimal remainValue;
|
||||
|
||||
private BigDecimal extraStore;
|
||||
|
||||
public SuperLuckyGiftIncomeAllot(BigDecimal totalValue, BigDecimal receiveTotalIncome, Map<Long, BigDecimal> receiveIncomeMap, BigDecimal roomOwnerTotalIncome, BigDecimal remainValue) {
|
||||
this.totalValue = totalValue;
|
||||
this.receiveTotalIncome = receiveTotalIncome;
|
||||
this.receiveIncomeMap = receiveIncomeMap;
|
||||
this.roomOwnerTotalIncome = roomOwnerTotalIncome;
|
||||
this.remainValue = remainValue;
|
||||
}
|
||||
}
|
||||
|
@@ -57,6 +57,8 @@ public class Lucky24GiftSendService {
|
||||
private RocketMQService rocketMQService;
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
@Autowired
|
||||
private Lucky24ExtraService extraService;
|
||||
|
||||
public void draw(long senderUid, Integer partitionId, Room room, List<Long> receiverList,
|
||||
Gift gift, int everyGiftNum, Date sendGiftTime) {
|
||||
@@ -72,12 +74,43 @@ public class Lucky24GiftSendService {
|
||||
log.info("[lucky24] uid {}, partitionId {}, addStockGoldNum {}, afterStock {}",
|
||||
senderUid, partitionId, incomeAllot.getRemainValue(), afterStock);
|
||||
|
||||
Map<Long, Lucky24Record> recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
|
||||
// 增加额外库存
|
||||
if (null != incomeAllot.getExtraStore()){
|
||||
BigDecimal extraAfterStock = extraService.addStock(partitionId, incomeAllot.getExtraStore());
|
||||
log.info("[lucky24] extraStockAdd uid {}, partitionId {}, addStockGoldNum {}, afterStock {}",
|
||||
senderUid, partitionId, incomeAllot.getExtraStore(), extraAfterStock);
|
||||
}
|
||||
|
||||
Map<Long, Lucky24Record> recordMap = draw(config, partitionConfig, senderUid, partitionId,
|
||||
gift, everyGiftNum, everyoneGoldNum,
|
||||
receiverList, room, sendGiftTime, null != incomeAllot.getExtraStore());
|
||||
log.info("[lucky24] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordMap));
|
||||
|
||||
sendMq(recordMap);
|
||||
}
|
||||
|
||||
private Map<Long, Lucky24Record> draw(Lucky24GiftConfig config, Lucky24GiftConfig partitionConfig, long senderUid, Integer partitionId,
|
||||
Gift gift, int everyGiftNum, long everyoneGoldNum,
|
||||
List<Long> receiverList, Room room, Date sendGiftTime, boolean extraSwitch) {
|
||||
if (!extraSwitch){
|
||||
return draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
|
||||
}
|
||||
|
||||
Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig = partitionConfig.getExtraPoolConfig();
|
||||
Long extraLucker = extraService.selectExtraLucker(extraPoolConfig, senderUid, partitionId, everyoneGoldNum, receiverList);
|
||||
if (null == extraLucker){
|
||||
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);
|
||||
|
||||
List<Long> receiverUidList = new ArrayList<>(receiverList);
|
||||
receiverUidList.remove(extraLucker);
|
||||
Map<Long, Lucky24Record> recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverUidList, room, sendGiftTime);
|
||||
recordMap.put(extraLucker, extraRecord);
|
||||
return recordMap;
|
||||
}
|
||||
|
||||
public Map<Long, Lucky24Record> draw(Lucky24GiftConfig config, Lucky24GiftConfig partitionConfig, Long senderUid, int partitionId,
|
||||
Gift gift, int everyGiftNum, long everyoneGoldNum,
|
||||
List<Long> receiverList, Room room, Date sendGiftTime) {
|
||||
|
@@ -0,0 +1,144 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.constant.Lucky24PoolTypeEnum;
|
||||
import com.accompany.business.dto.lucky.Lucky24GiftConfig;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.common.utils.RandomUtil;
|
||||
import com.accompany.core.enumeration.PartitionEnum;
|
||||
import com.accompany.core.model.Room;
|
||||
import com.accompany.payment.service.UserRechargeLevelService;
|
||||
import com.accompany.sharding.model.Lucky24Record;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RMap;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky24ExtraService {
|
||||
|
||||
@Autowired
|
||||
private Lucky24ExtraStockService stockService;
|
||||
@Autowired
|
||||
private UserRechargeLevelService userRechargeLevelService;
|
||||
@Autowired
|
||||
private Lucky24UserMetaService userMetaService;
|
||||
@Autowired
|
||||
private Lucky24RobotMsgService robotMsgService;
|
||||
@Autowired
|
||||
private Lucky24SettlementService settlementService;
|
||||
@Autowired
|
||||
private Lucky24RecordService recordService;
|
||||
|
||||
public BigDecimal addStock(Integer partitionId, BigDecimal addScore) {
|
||||
return stockService.addStock(partitionId, addScore);
|
||||
}
|
||||
|
||||
public Long selectExtraLucker(Lucky24GiftConfig.Lucky24ExtraPoolConfig extraPoolConfig, long senderUid, Integer partitionId, long everyoneGoldNum, List<Long> receiverList) {
|
||||
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId);
|
||||
ZonedDateTime zdt = DateTimeUtil.getDateTimeByZoneId(partitionEnum.getZoneId());
|
||||
if (zdt.getHour() < extraPoolConfig.getStartHour() || zdt.getHour() > extraPoolConfig.getEndHour()){
|
||||
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);
|
||||
Map<String, Number> userMetaSnapshot = userMetaMap.readAllMap();
|
||||
long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
|
||||
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 = todayOutput > 0L ? BigDecimal.valueOf(todayInput).divide(BigDecimal.valueOf(todayOutput), 4, RoundingMode.HALF_UP) : BigDecimal.ZERO;
|
||||
if (todayProductionRatio.compareTo(extraPoolConfig.getTodayProductionRatio()) >= 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
long todayDiff = todayInput - todayOutput;
|
||||
if (todayDiff < extraPoolConfig.getTodayDiff()){
|
||||
return null;
|
||||
}
|
||||
|
||||
String yesterday = String.valueOf(zdt.minusDays(1L).toInstant().toEpochMilli());
|
||||
String lastTwoDayKey = String.join("_", Lucky24UserMetaService.EXTRA_POOL_COUNT, yesterday);
|
||||
int lastTwoDayCount = userMetaSnapshot.getOrDefault(lastTwoDayKey, 0).intValue();
|
||||
if (lastTwoDayCount >= extraPoolConfig.getTwoDayCountLimit()){
|
||||
return null;
|
||||
}
|
||||
|
||||
int expectedValue = lastTwoDayCount + 1;
|
||||
int result = userMetaMap.compute(lastTwoDayKey, (key, currentVal) -> {
|
||||
if (currentVal == null || currentVal.intValue() == lastTwoDayCount) {
|
||||
return expectedValue;
|
||||
}
|
||||
return currentVal; // 如果当前值不等于期望值,则返回原值,不做修改
|
||||
}).intValue();
|
||||
if (result != expectedValue){
|
||||
//cas 失败了
|
||||
return null;
|
||||
}
|
||||
|
||||
String todayKey = String.join("_", Lucky24UserMetaService.EXTRA_POOL_COUNT, today);
|
||||
int todayAfter = userMetaMap.addAndGet(todayKey, 1).intValue();
|
||||
|
||||
//todo log
|
||||
|
||||
|
||||
return receiverList.get(0);
|
||||
}
|
||||
|
||||
public Lucky24Record randomExtraRecord(Lucky24GiftConfig config, long senderUid, Integer partitionId, Long receiverUid,
|
||||
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;
|
||||
|
||||
long afterMultiple = drawMultiple;
|
||||
|
||||
// 平台库存
|
||||
BigDecimal preWinGoldNum = BigDecimal.valueOf(afterMultiple * everyoneGoldNum);
|
||||
if (!judgeStock(partitionId, preWinGoldNum, senderUid, receiverUid)){
|
||||
afterMultiple = 0L;
|
||||
}
|
||||
long winGoldNum = afterMultiple * everyoneGoldNum;
|
||||
|
||||
if (winGoldNum > 0L){
|
||||
settlementService.sendReward(config, senderUid, room, gift, winGoldNum, afterMultiple);
|
||||
}
|
||||
|
||||
//todo log
|
||||
|
||||
return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null,
|
||||
receiverUid, Lucky24PoolTypeEnum.EXTRA_POOL.getType(),
|
||||
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("[lucky24] extraStock sender {} receiver {} 产出大于库存 winGoldNum {} afterStock {}",
|
||||
senderUid, receiverUid, winGoldNum, afterStock);
|
||||
afterStock = stockService.addStock(partitionId, winGoldNum);
|
||||
robotMsgService.pushStockNotEnough(partitionId, afterStock);
|
||||
}
|
||||
return enough;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky24ExtraStockService implements StockService {
|
||||
|
||||
@Autowired
|
||||
protected RedissonClient redissonClient;
|
||||
|
||||
@Override
|
||||
public RedissonClient getRedissonClient() {
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisKey getRedisKey() {
|
||||
return RedisKey.lucky_24_extra_stock;
|
||||
}
|
||||
}
|
@@ -46,10 +46,17 @@ public class Lucky24IncomeAllotService implements LuckyGiftIncomeAllotService {
|
||||
|
||||
// 平台抽成
|
||||
BigDecimal platformGoldNum = totalValue.multiply(config.getPlatformRatio());
|
||||
// 增加库存
|
||||
BigDecimal addStockGoldNum = totalValue.subtract(platformGoldNum);
|
||||
|
||||
return new SuperLuckyGiftIncomeAllot(totalValue, receiverTotalIncome, receiverIncomeMap, BigDecimal.ZERO, addStockGoldNum);
|
||||
// 额外抽成
|
||||
BigDecimal extraRatio = null == config.getExtraPoolConfig() || Boolean.TRUE.equals(config.getExtraPoolConfig().getIsOpen())?
|
||||
BigDecimal.ZERO: config.getExtraPoolConfig().getTodayProductionRatio();
|
||||
BigDecimal extraGoldNum = extraRatio.equals(BigDecimal.ZERO)? BigDecimal.ZERO:
|
||||
totalValue.multiply(config.getExtraPoolConfig().getStoreRatio());
|
||||
// 增加库存
|
||||
BigDecimal addStockGoldNum = totalValue.subtract(platformGoldNum).subtract(extraGoldNum);
|
||||
|
||||
return new SuperLuckyGiftIncomeAllot(totalValue, receiverTotalIncome, receiverIncomeMap, BigDecimal.ZERO, addStockGoldNum,
|
||||
extraGoldNum.equals(BigDecimal.ZERO)? null: extraGoldNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -33,6 +33,25 @@ public class Lucky24RecordService extends ServiceImpl<Lucky24RecordMapper, Lucky
|
||||
@Resource(name = "bizExecutor")
|
||||
private ThreadPoolExecutor bizExecutor;
|
||||
|
||||
public Lucky24Record buildRecord(long senderUid, int partitionId, Gift gift, int giftNum, Long roomUid, long receiverUid, Integer poolType,
|
||||
long drawMultiple, long afterMultiple, Date sendGiftTime) {
|
||||
Lucky24Record record = new Lucky24Record();
|
||||
record.setUid(senderUid);
|
||||
record.setPartitionId(partitionId);
|
||||
record.setReceiverUid(receiverUid);
|
||||
record.setRoomUid(roomUid);
|
||||
record.setGiftId(gift.getGiftId());
|
||||
record.setGiftNum(giftNum);
|
||||
record.setGiftGoldPrice(gift.getGoldPrice());
|
||||
record.setPoolType(poolType);
|
||||
record.setIsSupplement(false);
|
||||
record.setDrawMultiple((int) drawMultiple);
|
||||
record.setAfterMultiple((int) afterMultiple);
|
||||
record.setWinGoldNum(afterMultiple * gift.getGoldPrice() * giftNum);
|
||||
record.setCreateTime(sendGiftTime);
|
||||
return record;
|
||||
}
|
||||
|
||||
public Lucky24Record buildRecord(long senderUid, int partitionId, Gift gift, int giftNum, Long roomUid, long receiverUid, Integer poolId,
|
||||
boolean isSupplement, long drawMultiple, long afterMultiple, Date sendGiftTime) {
|
||||
Lucky24Record record = new Lucky24Record();
|
||||
|
@@ -17,6 +17,7 @@ import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -28,6 +29,8 @@ public class Lucky24UserMetaService {
|
||||
|
||||
private static final String TODAY = "today";
|
||||
|
||||
public static final String EXTRA_POOL_COUNT = "extra_pool_count";
|
||||
|
||||
private static final int HISTORY_QUEUE_SIZE = 40000;
|
||||
|
||||
@Autowired
|
||||
@@ -125,6 +128,12 @@ public class Lucky24UserMetaService {
|
||||
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId);
|
||||
long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
|
||||
|
||||
String today = String.valueOf(todayStartTimeLong);
|
||||
String todayInputKey = String.join("_", today, INPUT_KEY);
|
||||
long todayInput = userMetaMap.addAndGet(todayInputKey, input).longValue();
|
||||
String todayOutputKey = String.join("_", today, OUTPUT_KEY);
|
||||
long todayOutput = userMetaMap.addAndGet(todayOutputKey, output).longValue();
|
||||
|
||||
long userMetaToday = userMetaMap.computeIfAbsent(TODAY, k->todayStartTimeLong).longValue();
|
||||
if (userMetaToday < todayStartTimeLong
|
||||
&& userMetaMap.replace(TODAY, userMetaToday, todayStartTimeLong)){
|
||||
@@ -132,14 +141,21 @@ public class Lucky24UserMetaService {
|
||||
String oldToday = String.valueOf(userMetaToday);
|
||||
String oldTodayInputKey = String.join("_", oldToday, INPUT_KEY);
|
||||
String oldTodayOutputKey = String.join("_", oldToday, OUTPUT_KEY);
|
||||
userMetaMap.fastRemoveAsync(oldTodayInputKey, oldTodayOutputKey);
|
||||
}
|
||||
userMetaMap.fastRemove(oldTodayInputKey, oldTodayOutputKey);
|
||||
|
||||
String today = String.valueOf(todayStartTimeLong);
|
||||
String todayInputKey = String.join("_", today, INPUT_KEY);
|
||||
long todayInput = userMetaMap.addAndGet(todayInputKey, input).longValue();
|
||||
String todayOutputKey = String.join("_", today, OUTPUT_KEY);
|
||||
long todayOutput = userMetaMap.addAndGet(todayOutputKey, output).longValue();
|
||||
// 清理额外线计数器
|
||||
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 + "*";
|
||||
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 {}",
|
||||
senderUid, times, todayStartTimeLong, todayInput, todayOutput);
|
||||
|
Reference in New Issue
Block a user