多抽逻辑也增加抽不到美元奖励逻辑
This commit is contained in:
parent
c20883bef4
commit
06604ca428
@ -21,7 +21,11 @@ import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketRec
|
|||||||
import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketService;
|
import com.red.circle.other.infra.database.rds.service.activity.LotteryTicketService;
|
||||||
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.UserActivityRechargeService;
|
||||||
|
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;
|
||||||
|
import com.red.circle.other.inner.asserts.OtherErrorCode;
|
||||||
import com.red.circle.other.inner.asserts.lottery.LotteryErrorCode;
|
import com.red.circle.other.inner.asserts.lottery.LotteryErrorCode;
|
||||||
import com.red.circle.tool.core.date.TimestampUtils;
|
import com.red.circle.tool.core.date.TimestampUtils;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -59,6 +63,9 @@ public class LotteryMultiDrawExe {
|
|||||||
private final LotteryUserProgressService lotteryUserProgressService;
|
private final LotteryUserProgressService lotteryUserProgressService;
|
||||||
private final LotteryPrizeGrantService lotteryPrizeGrantService;
|
private final LotteryPrizeGrantService lotteryPrizeGrantService;
|
||||||
private final RedisService redisService;
|
private final RedisService redisService;
|
||||||
|
private final LatestMobileDeviceService latestMobileDeviceService;
|
||||||
|
private final LotteryDeviceRecordService lotteryDeviceRecordService;
|
||||||
|
private final UserActivityRechargeService userActivityRechargeService;
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public LotteryMultiDrawResultCO execute(LotteryMultiDrawCmd cmd) {
|
public LotteryMultiDrawResultCO execute(LotteryMultiDrawCmd cmd) {
|
||||||
@ -70,27 +77,33 @@ public class LotteryMultiDrawExe {
|
|||||||
// 1. 校验活动
|
// 1. 校验活动
|
||||||
LotteryActivity activity = validateActivity(activityId, now);
|
LotteryActivity activity = validateActivity(activityId, now);
|
||||||
|
|
||||||
// 2. 校验是否启用连抽
|
// 2. 校验设备唯一性(防止小号薅羊毛)
|
||||||
|
checkDeviceUnique(activityId, userId);
|
||||||
|
|
||||||
|
// 3. 校验是否启用连抽
|
||||||
ResponseAssert.isTrue(LotteryErrorCode.MULTI_DRAW_NOT_ENABLED,
|
ResponseAssert.isTrue(LotteryErrorCode.MULTI_DRAW_NOT_ENABLED,
|
||||||
activity.getEnableMultiDraw() != null && activity.getEnableMultiDraw() == 1);
|
activity.getEnableMultiDraw() != null && activity.getEnableMultiDraw() == 1);
|
||||||
|
|
||||||
// 3. 校验抽奖次数限制(需要剩余足够次数)
|
// 4. 查询用户充值金额(用于过滤美元奖励)
|
||||||
|
BigDecimal userRechargeAmount = getUserRechargeAmount(userId, activityId);
|
||||||
|
|
||||||
|
// 5. 校验抽奖次数限制(需要剩余足够次数)
|
||||||
validateDrawCount(userId, activityId, activity, drawCount);
|
validateDrawCount(userId, activityId, activity, drawCount);
|
||||||
|
|
||||||
// 4. 校验并扣除抽奖券(连抽消耗更少)
|
// 6. 校验并扣除抽奖券(连抽消耗更少)
|
||||||
Integer ticketCost = activity.getMultiDrawTicketCost() != null
|
Integer ticketCost = activity.getMultiDrawTicketCost() != null
|
||||||
? activity.getMultiDrawTicketCost()
|
? activity.getMultiDrawTicketCost()
|
||||||
: drawCount * activity.getTicketCost();
|
: drawCount * activity.getTicketCost();
|
||||||
|
|
||||||
deductTicket(userId, ticketCost);
|
deductTicket(userId, ticketCost);
|
||||||
|
|
||||||
// 5. 获取或创建用户进度(用于动态概率)
|
// 7. 获取或创建用户进度(用于动态概率)
|
||||||
LotteryUserProgress progress = getOrCreateUserProgress(userId, activityId);
|
LotteryUserProgress progress = getOrCreateUserProgress(userId, activityId);
|
||||||
|
|
||||||
// 6. 生成批次号
|
// 8. 生成批次号
|
||||||
String batchNo = generateBatchNo();
|
String batchNo = generateBatchNo();
|
||||||
|
|
||||||
// 7. 执行连抽
|
// 9. 执行连抽
|
||||||
List<LotteryDrawResultCO> results = new ArrayList<>();
|
List<LotteryDrawResultCO> results = new ArrayList<>();
|
||||||
boolean guaranteeTriggered = false;
|
boolean guaranteeTriggered = false;
|
||||||
BigDecimal batchTotalAmount = BigDecimal.ZERO;
|
BigDecimal batchTotalAmount = BigDecimal.ZERO;
|
||||||
@ -101,7 +114,7 @@ public class LotteryMultiDrawExe {
|
|||||||
LotteryUserProgress currentProgress = lotteryUserProgressService.getById(progress.getId());
|
LotteryUserProgress currentProgress = lotteryUserProgressService.getById(progress.getId());
|
||||||
|
|
||||||
// 使用动态概率抽奖
|
// 使用动态概率抽奖
|
||||||
LotteryPrize prize = drawPrizeWithDynamicProbability(activityId, activity, currentProgress);
|
LotteryPrize prize = drawPrizeWithDynamicProbability(activityId, activity, currentProgress, userRechargeAmount);
|
||||||
|
|
||||||
// 扣减库存
|
// 扣减库存
|
||||||
if (prize != null) {
|
if (prize != null) {
|
||||||
@ -138,7 +151,7 @@ public class LotteryMultiDrawExe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. 检查连抽保底
|
// 10. 检查连抽保底
|
||||||
BigDecimal guaranteeMinAmount = activity.getMultiDrawGuaranteeMinAmount() != null
|
BigDecimal guaranteeMinAmount = activity.getMultiDrawGuaranteeMinAmount() != null
|
||||||
? activity.getMultiDrawGuaranteeMinAmount()
|
? activity.getMultiDrawGuaranteeMinAmount()
|
||||||
: BigDecimal.ZERO;
|
: BigDecimal.ZERO;
|
||||||
@ -151,13 +164,64 @@ public class LotteryMultiDrawExe {
|
|||||||
userId, activityId, batchTotalAmount);
|
userId, activityId, batchTotalAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. 更新用户抽奖次数(+drawCount次)
|
// 11. 更新用户抽奖次数(+drawCount次)
|
||||||
updateUserDrawCount(userId, activityId, drawCount);
|
updateUserDrawCount(userId, activityId, drawCount);
|
||||||
|
|
||||||
// 10. 组装返回结果
|
// 12. 记录设备信息(首次抽奖时)
|
||||||
|
recordDeviceAfterDraw(activityId, userId);
|
||||||
|
|
||||||
|
// 13. 组装返回结果
|
||||||
return buildMultiDrawResult(batchNo, results, guaranteeTriggered);
|
return buildMultiDrawResult(batchNo, results, guaranteeTriggered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验设备唯一性(防止小号薅羊毛)
|
||||||
|
*/
|
||||||
|
private void checkDeviceUnique(Long activityId, Long userId) {
|
||||||
|
String fingerprint = latestMobileDeviceService.getEnhancedDeviceFingerprintByUserId(userId);
|
||||||
|
|
||||||
|
if (com.red.circle.tool.core.text.StringUtils.isBlank(fingerprint)) {
|
||||||
|
log.warn("Failed to get device fingerprint in multi-draw, userId: {}", userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
com.red.circle.other.infra.database.rds.entity.activity.LotteryDeviceRecord existingRecord = lotteryDeviceRecordService
|
||||||
|
.getByActivityAndFingerprint(activityId, fingerprint);
|
||||||
|
|
||||||
|
if (existingRecord != null && !existingRecord.getUserId().equals(userId)) {
|
||||||
|
log.warn("Duplicate device detected in multi-draw, activityId: {}, currentUser: {}, boundUser: {}, fingerprint: {}",
|
||||||
|
activityId, userId, existingRecord.getUserId(), fingerprint);
|
||||||
|
ResponseAssert.isTrue(OtherErrorCode.DEVICE_ALREADY_USED, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录设备信息(抽奖成功后)
|
||||||
|
*/
|
||||||
|
private void recordDeviceAfterDraw(Long activityId, Long userId) {
|
||||||
|
try {
|
||||||
|
String fingerprint = latestMobileDeviceService.getEnhancedDeviceFingerprintByUserId(userId);
|
||||||
|
if (com.red.circle.tool.core.text.StringUtils.isNotBlank(fingerprint)) {
|
||||||
|
lotteryDeviceRecordService.recordDevice(activityId, userId, fingerprint);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to record device in multi-draw, activityId: {}, userId: {}", activityId, userId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户充值金额
|
||||||
|
*/
|
||||||
|
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 in multi-draw, userId: {}, activityId: {}", userId, activityId, e);
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private LotteryActivity validateActivity(Long activityId, Timestamp now) {
|
private LotteryActivity validateActivity(Long activityId, Timestamp now) {
|
||||||
LotteryActivity activity = lotteryActivityService.getById(activityId);
|
LotteryActivity activity = lotteryActivityService.getById(activityId);
|
||||||
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_NOT_FOUND, activity != null);
|
ResponseAssert.isTrue(LotteryErrorCode.ACTIVITY_NOT_FOUND, activity != null);
|
||||||
@ -203,9 +267,10 @@ public class LotteryMultiDrawExe {
|
|||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 drawPrizeWithMultiProbability(activityId, progress.getUserId());
|
return drawPrizeWithMultiProbability(activityId, progress.getUserId(), userRechargeAmount);
|
||||||
}
|
}
|
||||||
String stage = determinePrizePool(progress, activity);
|
String stage = determinePrizePool(progress, activity);
|
||||||
log.debug("Multi-draw stage: {}, userId: {}, drawCount: {}, totalAmount: {}",
|
log.debug("Multi-draw stage: {}, userId: {}, drawCount: {}, totalAmount: {}",
|
||||||
@ -215,6 +280,9 @@ public class LotteryMultiDrawExe {
|
|||||||
// 过滤用户已中过的BADGE类型奖品
|
// 过滤用户已中过的BADGE类型奖品
|
||||||
prizes = filterAlreadyWonBadgePrizes(prizes, progress.getUserId());
|
prizes = filterAlreadyWonBadgePrizes(prizes, progress.getUserId());
|
||||||
|
|
||||||
|
// 过滤美元奖励(充值金额为0的用户不能抽到)
|
||||||
|
prizes = filterMoneyPrizes(prizes, userRechargeAmount);
|
||||||
|
|
||||||
prizes = filterOverThresholdPrizes(prizes, progress, activity.getWithdrawThreshold());
|
prizes = filterOverThresholdPrizes(prizes, progress, activity.getWithdrawThreshold());
|
||||||
if (prizes.isEmpty()) {
|
if (prizes.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@ -316,7 +384,7 @@ public class LotteryMultiDrawExe {
|
|||||||
return prizes.get(prizes.size() - 1);
|
return prizes.get(prizes.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LotteryPrize drawPrizeWithMultiProbability(Long activityId, Long userId) {
|
private LotteryPrize drawPrizeWithMultiProbability(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)
|
||||||
@ -329,6 +397,9 @@ public class LotteryMultiDrawExe {
|
|||||||
// 过滤用户已中过的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;
|
||||||
}
|
}
|
||||||
@ -554,6 +625,37 @@ public class LotteryMultiDrawExe {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤美元奖励(充值金额为0的用户不能抽到)
|
||||||
|
*/
|
||||||
|
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 in multi-draw, filtering MONEY prize. userRechargeAmount: {}, prizeId: {}, prizeName: {}",
|
||||||
|
userRechargeAmount, prize.getId(), prize.getPrizeName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
log.info("Filter MONEY prizes completed in multi-draw. userRechargeAmount: {}, beforeCount: {}, afterCount: {}",
|
||||||
|
userRechargeAmount, prizes.size(), filtered.size());
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记用户已中BADGE类型奖品(永久记录).
|
* 标记用户已中BADGE类型奖品(永久记录).
|
||||||
*
|
*
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user