新增未充值抽不到美元奖励逻辑
This commit is contained in:
parent
551498082d
commit
c20883bef4
@ -22,6 +22,7 @@ import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketSer
|
|||||||
import com.red.circle.other.infra.database.rds.service.activity.LotteryUserCountService;
|
import com.red.circle.other.infra.database.rds.service.activity.LotteryUserCountService;
|
||||||
import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService;
|
import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService;
|
||||||
import com.red.circle.other.infra.database.rds.service.activity.LotteryDeviceRecordService;
|
import com.red.circle.other.infra.database.rds.service.activity.LotteryDeviceRecordService;
|
||||||
|
import com.red.circle.other.infra.database.rds.service.activity.UserActivityRechargeService;
|
||||||
import com.red.circle.other.infra.database.rds.entity.activity.LotteryDeviceRecord;
|
import com.red.circle.other.infra.database.rds.entity.activity.LotteryDeviceRecord;
|
||||||
import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService;
|
import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService;
|
||||||
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
|
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
|
||||||
@ -66,6 +67,7 @@ public class LotteryDrawExe {
|
|||||||
private final RedisService redisService;
|
private final RedisService redisService;
|
||||||
private final LatestMobileDeviceService latestMobileDeviceService;
|
private final LatestMobileDeviceService latestMobileDeviceService;
|
||||||
private final LotteryDeviceRecordService lotteryDeviceRecordService;
|
private final LotteryDeviceRecordService lotteryDeviceRecordService;
|
||||||
|
private final UserActivityRechargeService userActivityRechargeService;
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public LotteryDrawResultCO execute(LotteryDrawCmd cmd) {
|
public LotteryDrawResultCO execute(LotteryDrawCmd cmd) {
|
||||||
@ -81,16 +83,20 @@ public class LotteryDrawExe {
|
|||||||
|
|
||||||
// 3. 校验抽奖次数限制
|
// 3. 校验抽奖次数限制
|
||||||
validateDrawCount(userId, activityId, activity);
|
validateDrawCount(userId, activityId, activity);
|
||||||
// 4. 校验并扣除抽奖券(如果需要)
|
|
||||||
|
// 4. 查询用户充值金额(用于过滤美元奖励)
|
||||||
|
BigDecimal userRechargeAmount = getUserRechargeAmount(userId, activityId);
|
||||||
|
|
||||||
|
// 5. 校验并扣除抽奖券(如果需要)
|
||||||
deductTicket(userId, activity.getTicketCost());
|
deductTicket(userId, activity.getTicketCost());
|
||||||
|
|
||||||
// 5. 获取或创建用户进度(用于动态概率)
|
// 6. 获取或创建用户进度(用于动态概率)
|
||||||
LotteryUserProgress progress = getOrCreateUserProgress(userId, activityId);
|
LotteryUserProgress progress = getOrCreateUserProgress(userId, activityId);
|
||||||
|
|
||||||
// 6. 执行抽奖算法(动态概率)
|
// 7. 执行抽奖算法(动态概率)
|
||||||
LotteryPrize prize = drawPrizeWithDynamicProbability(activityId, activity, progress);
|
LotteryPrize prize = drawPrizeWithDynamicProbability(activityId, activity, progress, userRechargeAmount);
|
||||||
|
|
||||||
// 7. 扣减奖品库存(使用悲观锁防止超卖)
|
// 8. 扣减奖品库存(使用悲观锁防止超卖)
|
||||||
if (prize != null) {
|
if (prize != null) {
|
||||||
boolean deductSuccess = deductPrizeStock(prize.getId());
|
boolean deductSuccess = deductPrizeStock(prize.getId());
|
||||||
if (!deductSuccess) {
|
if (!deductSuccess) {
|
||||||
@ -99,24 +105,24 @@ public class LotteryDrawExe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. 保存抽奖记录
|
// 9. 保存抽奖记录
|
||||||
LotteryRecord record = saveDrawRecord(userId, activityId, prize, now, null, 1);
|
LotteryRecord record = saveDrawRecord(userId, activityId, prize, now, null, 1);
|
||||||
|
|
||||||
// 9. 发放奖励
|
// 10. 发放奖励
|
||||||
if (prize != null) {
|
if (prize != null) {
|
||||||
lotteryPrizeGrantService.grantPrize(userId, prize, record.getRecordNo());
|
lotteryPrizeGrantService.grantPrize(userId, prize, record.getRecordNo());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. 更新用户抽奖次数
|
// 11. 更新用户抽奖次数
|
||||||
updateUserDrawCount(userId, activityId);
|
updateUserDrawCount(userId, activityId);
|
||||||
|
|
||||||
// 11. 更新用户进度(累加金额和次数)
|
// 12. 更新用户进度(累加金额和次数)
|
||||||
updateUserProgress(progress, prize);
|
updateUserProgress(progress, prize);
|
||||||
|
|
||||||
// 12. 记录设备信息(首次抽奖时)
|
// 13. 记录设备信息(首次抽奖时)
|
||||||
recordDeviceAfterDraw(activityId, userId);
|
recordDeviceAfterDraw(activityId, userId);
|
||||||
|
|
||||||
// 13. 组装返回结果
|
// 14. 组装返回结果
|
||||||
return convertToResultCO(record, prize);
|
return convertToResultCO(record, prize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,14 +221,28 @@ public class LotteryDrawExe {
|
|||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户充值金额.
|
||||||
|
*/
|
||||||
|
private BigDecimal getUserRechargeAmount(Long userId, Long activityId) {
|
||||||
|
try {
|
||||||
|
BigDecimal amount = userActivityRechargeService.getUserTotalAmount(userId, activityId);
|
||||||
|
return amount != null ? amount : BigDecimal.ZERO;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to get user recharge amount, userId: {}, activityId: {}", userId, activityId, e);
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态概率抽奖.
|
* 动态概率抽奖.
|
||||||
*/
|
*/
|
||||||
private LotteryPrize drawPrizeWithDynamicProbability(Long activityId, LotteryActivity activity, LotteryUserProgress progress) {
|
private LotteryPrize drawPrizeWithDynamicProbability(Long activityId, LotteryActivity activity,
|
||||||
|
LotteryUserProgress progress, BigDecimal userRechargeAmount) {
|
||||||
// 判断是否启用动态概率
|
// 判断是否启用动态概率
|
||||||
if (activity.getEnableDynamicProbability() == null || activity.getEnableDynamicProbability() != 1) {
|
if (activity.getEnableDynamicProbability() == null || activity.getEnableDynamicProbability() != 1) {
|
||||||
// 未启用动态概率,使用普通抽奖
|
// 未启用动态概率,使用普通抽奖
|
||||||
return drawPrizeNormal(activityId, progress.getUserId());
|
return drawPrizeNormal(activityId, progress.getUserId(), userRechargeAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 判断用户所处阶段
|
// 1. 判断用户所处阶段
|
||||||
@ -236,20 +256,23 @@ public class LotteryDrawExe {
|
|||||||
// 3. 过滤用户已中过的BADGE类型奖品
|
// 3. 过滤用户已中过的BADGE类型奖品
|
||||||
prizes = filterAlreadyWonBadgePrizes(prizes, progress.getUserId());
|
prizes = filterAlreadyWonBadgePrizes(prizes, progress.getUserId());
|
||||||
|
|
||||||
// 4. 过滤会超过提现门槛的大奖
|
// 4. 过滤美元奖励(充值金额为0的用户不能抽到)
|
||||||
|
prizes = filterMoneyPrizes(prizes, userRechargeAmount);
|
||||||
|
|
||||||
|
// 5. 过滤会超过提现门槛的大奖
|
||||||
prizes = filterOverThresholdPrizes(prizes, progress, activity.getWithdrawThreshold());
|
prizes = filterOverThresholdPrizes(prizes, progress, activity.getWithdrawThreshold());
|
||||||
|
|
||||||
if (prizes.isEmpty()) {
|
if (prizes.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 动态调整概率
|
// 6. 动态调整概率
|
||||||
adjustProbability(prizes, progress, activity.getWithdrawThreshold());
|
adjustProbability(prizes, progress, activity.getWithdrawThreshold());
|
||||||
|
|
||||||
// 6. 执行抽奖
|
// 7. 执行抽奖
|
||||||
LotteryPrize selectedPrize = randomSelectByProbability(prizes);
|
LotteryPrize selectedPrize = randomSelectByProbability(prizes);
|
||||||
|
|
||||||
// 7. 如果抽中了BADGE类型奖品,记录到Redis
|
// 8. 如果抽中了BADGE类型奖品,记录到Redis
|
||||||
if (selectedPrize != null && "BADGE".equals(selectedPrize.getPrizeType())) {
|
if (selectedPrize != null && "BADGE".equals(selectedPrize.getPrizeType())) {
|
||||||
markBadgePrizeAsWon(progress.getUserId(), selectedPrize.getId());
|
markBadgePrizeAsWon(progress.getUserId(), selectedPrize.getId());
|
||||||
}
|
}
|
||||||
@ -260,7 +283,7 @@ public class LotteryDrawExe {
|
|||||||
/**
|
/**
|
||||||
* 普通抽奖(不使用动态概率).
|
* 普通抽奖(不使用动态概率).
|
||||||
*/
|
*/
|
||||||
private LotteryPrize drawPrizeNormal(Long activityId, Long userId) {
|
private LotteryPrize drawPrizeNormal(Long activityId, Long userId, BigDecimal userRechargeAmount) {
|
||||||
List<LotteryPrize> prizes = lotteryPrizeService.query()
|
List<LotteryPrize> prizes = lotteryPrizeService.query()
|
||||||
.eq(LotteryPrize::getActivityId, activityId)
|
.eq(LotteryPrize::getActivityId, activityId)
|
||||||
.gt(LotteryPrize::getRemainingStock, 0)
|
.gt(LotteryPrize::getRemainingStock, 0)
|
||||||
@ -274,6 +297,9 @@ public class LotteryDrawExe {
|
|||||||
// 过滤用户已中过的BADGE类型奖品
|
// 过滤用户已中过的BADGE类型奖品
|
||||||
prizes = filterAlreadyWonBadgePrizes(prizes, userId);
|
prizes = filterAlreadyWonBadgePrizes(prizes, userId);
|
||||||
|
|
||||||
|
// 过滤美元奖励(充值金额为0的用户不能抽到)
|
||||||
|
prizes = filterMoneyPrizes(prizes, userRechargeAmount);
|
||||||
|
|
||||||
if (prizes.isEmpty()) {
|
if (prizes.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -621,6 +647,41 @@ public class LotteryDrawExe {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤美元奖励(充值金额为0的用户不能抽到).
|
||||||
|
*
|
||||||
|
* @param prizes 奖品列表
|
||||||
|
* @param userRechargeAmount 用户充值金额
|
||||||
|
* @return 过滤后的奖品列表
|
||||||
|
*/
|
||||||
|
private List<LotteryPrize> filterMoneyPrizes(List<LotteryPrize> prizes, BigDecimal userRechargeAmount) {
|
||||||
|
if (prizes == null || prizes.isEmpty()) {
|
||||||
|
return prizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果用户充值金额>0,不需要过滤
|
||||||
|
if (userRechargeAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
return prizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充值金额为0,过滤掉MONEY类型奖品
|
||||||
|
List<LotteryPrize> filtered = prizes.stream()
|
||||||
|
.filter(prize -> {
|
||||||
|
if ("MONEY".equals(prize.getPrizeType())) {
|
||||||
|
log.info("User has no recharge, filtering MONEY prize. userId: {}, prizeId: {}, prizeName: {}",
|
||||||
|
userRechargeAmount, prize.getId(), prize.getPrizeName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
log.info("Filter MONEY prizes completed. userRechargeAmount: {}, beforeCount: {}, afterCount: {}",
|
||||||
|
userRechargeAmount, prizes.size(), filtered.size());
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记用户已中BADGE类型奖品(永久记录).
|
* 标记用户已中BADGE类型奖品(永久记录).
|
||||||
*
|
*
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user