发红包优化
This commit is contained in:
@@ -78,6 +78,7 @@ public class RedPacketServiceImpl implements IRedPacketService {
|
|||||||
rp.setCreateTime(LocalDateTime.now());
|
rp.setCreateTime(LocalDateTime.now());
|
||||||
|
|
||||||
// 判断红包类型
|
// 判断红包类型
|
||||||
|
Integer targetDigit = null;
|
||||||
if (roundId != null) {
|
if (roundId != null) {
|
||||||
rp.setType(TYPE_GAME);
|
rp.setType(TYPE_GAME);
|
||||||
} else {
|
} else {
|
||||||
@@ -98,25 +99,28 @@ public class RedPacketServiceImpl implements IRedPacketService {
|
|||||||
round.setFirstRpId(rp.getId());
|
round.setFirstRpId(rp.getId());
|
||||||
gameRoundMapper.updateById(round);
|
gameRoundMapper.updateById(round);
|
||||||
|
|
||||||
// 如果有目标尾数,预计算份额金额
|
// 获取局的目标尾数(可能为null)
|
||||||
if (round.getTargetDigit() != null) {
|
targetDigit = round.getTargetDigit();
|
||||||
List<BigDecimal> shareAmounts = preCalculateShareAmounts(rp, round.getTargetDigit());
|
|
||||||
try {
|
|
||||||
rp.setShareAmounts(objectMapper.writeValueAsString(shareAmounts));
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.error("序列化shareAmounts失败: {}", shareAmounts, e);
|
|
||||||
throw new RuntimeException("系统错误");
|
|
||||||
}
|
|
||||||
rp.setShareTakenCount(0);
|
|
||||||
redPacketMapper.updateById(rp);
|
|
||||||
log.info("预分配红包份额: rpId={}, targetDigit={}, amounts={}", rp.getId(), round.getTargetDigit(), shareAmounts);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 已有红包,不再绑定本局,作为普通游戏红包发放(不计算尾数)
|
// 已有红包,不再绑定本局,作为普通游戏红包发放(不计算尾数)
|
||||||
log.info("本局已有红包,当前红包不绑定: rpId={}, roundId={}", rp.getId(), roundId);
|
log.info("本局已有红包,当前红包不绑定: rpId={}, roundId={}", rp.getId(), roundId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 无论红包类型(TYPE_GAME/TYPE_RANDOM),都预先生成份额金额
|
||||||
|
// 游戏红包有targetDigit时控制手气王尾数,无targetDigit或非游戏红包按纯随机份额
|
||||||
|
List<BigDecimal> shareAmounts = preCalculateShareAmounts(rp, targetDigit);
|
||||||
|
try {
|
||||||
|
rp.setShareAmounts(objectMapper.writeValueAsString(shareAmounts));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("序列化shareAmounts失败: {}", shareAmounts, e);
|
||||||
|
throw new RuntimeException("系统错误");
|
||||||
|
}
|
||||||
|
rp.setShareTakenCount(0);
|
||||||
|
redPacketMapper.updateById(rp);
|
||||||
|
log.info("预分配红包份额: rpId={}, type={}, targetDigit={}, amounts={}",
|
||||||
|
rp.getId(), rp.getType(), targetDigit, shareAmounts);
|
||||||
|
|
||||||
// 审计日志
|
// 审计日志
|
||||||
auditService.log(groupId, roundId, userId, "RP_SEND",
|
auditService.log(groupId, roundId, userId, "RP_SEND",
|
||||||
String.format("{\"rpId\":%d,\"amount\":%s,\"count\":%d}",
|
String.format("{\"rpId\":%d,\"amount\":%s,\"count\":%d}",
|
||||||
@@ -290,10 +294,11 @@ public class RedPacketServiceImpl implements IRedPacketService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预计算红包份额金额,确保手气王尾数正确
|
* 预计算红包份额金额
|
||||||
* 手气王金额确保以目标尾数结尾
|
* targetDigit 非空时确保手气王金额尾数=targetDigit
|
||||||
|
* targetDigit 为空时按纯随机份额处理
|
||||||
*/
|
*/
|
||||||
private List<BigDecimal> preCalculateShareAmounts(RedPacket rp, int targetDigit) {
|
private List<BigDecimal> preCalculateShareAmounts(RedPacket rp, Integer targetDigit) {
|
||||||
List<BigDecimal> amounts = new ArrayList<>();
|
List<BigDecimal> amounts = new ArrayList<>();
|
||||||
int count = rp.getTotalCount();
|
int count = rp.getTotalCount();
|
||||||
BigDecimal total = rp.getTotalAmount();
|
BigDecimal total = rp.getTotalAmount();
|
||||||
@@ -306,17 +311,8 @@ public class RedPacketServiceImpl implements IRedPacketService {
|
|||||||
|
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
|
|
||||||
// 计算手气王金额(较大金额,尾数必须=targetDigit)
|
// 计算手气王金额
|
||||||
// 手气王比例:总额的30%~50%,确保手气王金额足够大
|
BigDecimal luckyAmount = calculateLuckyAmount(total, targetDigit, random);
|
||||||
double ratio = 0.3 + random.nextDouble() * 0.2;
|
|
||||||
BigDecimal luckyAmountBase = total.multiply(BigDecimal.valueOf(ratio));
|
|
||||||
|
|
||||||
// 确保尾数 = targetDigit
|
|
||||||
int lastDigit = luckyAmountBase.remainder(BigDecimal.TEN).intValue();
|
|
||||||
int diff = (targetDigit - lastDigit + 10) % 10;
|
|
||||||
BigDecimal luckyAmount = luckyAmountBase.add(BigDecimal.valueOf(diff))
|
|
||||||
.setScale(2, RoundingMode.HALF_UP);
|
|
||||||
|
|
||||||
// 确保手气王金额不超过总额
|
// 确保手气王金额不超过总额
|
||||||
if (luckyAmount.compareTo(total) > 0) {
|
if (luckyAmount.compareTo(total) > 0) {
|
||||||
luckyAmount = total;
|
luckyAmount = total;
|
||||||
@@ -346,29 +342,55 @@ public class RedPacketServiceImpl implements IRedPacketService {
|
|||||||
// 最后一个拿剩余
|
// 最后一个拿剩余
|
||||||
amounts.add(remainingForOthers.setScale(2, RoundingMode.HALF_UP));
|
amounts.add(remainingForOthers.setScale(2, RoundingMode.HALF_UP));
|
||||||
|
|
||||||
// 打乱顺序(让领取顺序随机,但手气王金额已确定尾数)
|
// 打乱顺序(让领取顺序随机)
|
||||||
Collections.shuffle(amounts);
|
Collections.shuffle(amounts);
|
||||||
|
|
||||||
// 确保手气王金额尾数正确(如果打乱后手气王不在列表中)
|
// 如果指定了目标尾数,确保列表中至少有1个金额的尾数=targetDigit
|
||||||
boolean hasCorrectDigit = false;
|
if (targetDigit != null) {
|
||||||
for (BigDecimal amt : amounts) {
|
ensureLuckyDigit(amounts, targetDigit);
|
||||||
if (amt.remainder(BigDecimal.TEN).intValue() == targetDigit) {
|
|
||||||
hasCorrectDigit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasCorrectDigit) {
|
|
||||||
// 重新找一个尾数匹配的作为手气王
|
|
||||||
for (int i = 0; i < amounts.size(); i++) {
|
|
||||||
BigDecimal amt = amounts.get(i);
|
|
||||||
int digit = amt.remainder(BigDecimal.TEN).intValue();
|
|
||||||
if (digit == targetDigit && i != 0) {
|
|
||||||
Collections.swap(amounts, i, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return amounts;
|
return amounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算手气王金额
|
||||||
|
* 比例为总额的30%~50%,有目标尾数时确保尾数=targetDigit
|
||||||
|
*/
|
||||||
|
private BigDecimal calculateLuckyAmount(BigDecimal total, Integer targetDigit, Random random) {
|
||||||
|
// 手气王比例:总额的30%~50%
|
||||||
|
double ratio = 0.3 + random.nextDouble() * 0.2;
|
||||||
|
BigDecimal luckyAmountBase = total.multiply(BigDecimal.valueOf(ratio));
|
||||||
|
|
||||||
|
// 未指定目标尾数时按基础金额返回
|
||||||
|
if (targetDigit == null) {
|
||||||
|
return luckyAmountBase.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指定了目标尾数,调整金额使尾数=targetDigit
|
||||||
|
int lastDigit = luckyAmountBase.remainder(BigDecimal.TEN).intValue();
|
||||||
|
int diff = (targetDigit - lastDigit + 10) % 10;
|
||||||
|
return luckyAmountBase.add(BigDecimal.valueOf(diff))
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保份额列表中至少有1个金额的尾数=targetDigit
|
||||||
|
* 若没有则从列表中找一个尾数匹配的金额,换到首位作为手气王
|
||||||
|
*/
|
||||||
|
private void ensureLuckyDigit(List<BigDecimal> amounts, int targetDigit) {
|
||||||
|
for (BigDecimal amt : amounts) {
|
||||||
|
if (amt.remainder(BigDecimal.TEN).intValue() == targetDigit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 没有尾数匹配的,找一个换到首位
|
||||||
|
for (int i = 1; i < amounts.size(); i++) {
|
||||||
|
BigDecimal amt = amounts.get(i);
|
||||||
|
if (amt.remainder(BigDecimal.TEN).intValue() == targetDigit) {
|
||||||
|
Collections.swap(amounts, i, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user