fix: discard rocket gift overflow between levels

This commit is contained in:
zhx 2026-07-20 16:15:29 +08:00
parent a5e85e8b71
commit 88b20f9ca3
2 changed files with 48 additions and 52 deletions

View File

@ -222,14 +222,17 @@ public class RocketEnergyAggregator {
return false; return false;
} }
// 处理能量增加 // 处理能量增加火箭满能时只消费到触发礼物后续独立礼物留给下一级
processRoomEnergyBatch(roomId, batch); int processedCount = processRoomEnergyBatch(roomId, batch);
if (processedCount <= 0) {
return false;
}
// 处理成功后才删除Redis中的数据使用 LTRIM // 处理成功后才删除Redis中的数据使用 LTRIM
redisTemplate.opsForList().trim(queueKey, batch.size(), -1); redisTemplate.opsForList().trim(queueKey, processedCount, -1);
log.info("成功处理房间{}能量, 处理数量={}, 剩余={}", log.info("成功处理房间{}能量, 处理数量={}, 剩余={}",
roomId, batch.size(), queueSize - batch.size()); roomId, processedCount, queueSize - processedCount);
return true; return true;
@ -242,7 +245,7 @@ public class RocketEnergyAggregator {
/** /**
* 处理单个房间的能量批次 * 处理单个房间的能量批次
*/ */
private void processRoomEnergyBatch(Long roomId, List<RocketEnergyAddCmd> batch) { private int processRoomEnergyBatch(Long roomId, List<RocketEnergyAddCmd> batch) {
String riyadhToday = ZonedDateTimeAsiaRiyadhUtils.nowFormat("yyyy-MM-dd"); String riyadhToday = ZonedDateTimeAsiaRiyadhUtils.nowFormat("yyyy-MM-dd");
// 查询当前火箭状态 // 查询当前火箭状态
@ -264,7 +267,7 @@ public class RocketEnergyAggregator {
// 校验状态 // 校验状态
if (!rocketStatus.canAddEnergy()) { if (!rocketStatus.canAddEnergy()) {
log.warn("火箭不可充能, roomId={}, status={}", roomId, rocketStatus.getStatus()); log.warn("火箭不可充能, roomId={}, status={}", roomId, rocketStatus.getStatus());
return; return 0;
} }
// 记录增加前的状态 // 记录增加前的状态
@ -272,23 +275,38 @@ public class RocketEnergyAggregator {
long maxEnergy = rocketStatus.getMaxEnergy(); long maxEnergy = rocketStatus.getMaxEnergy();
// 批量增加能量 // 批量增加能量
long totalEnergy = 0L; long requestedEnergy = 0L;
long acceptedEnergy = 0L;
int processedCount = 0;
Long triggerUserId = null; Long triggerUserId = null;
// 历史上可能存在已满能但异步发射未完成的状态此时不消费新礼物
if (rocketStatus.isFullEnergy()) {
Long retryTriggerUserId = batch.isEmpty() ? null : batch.get(0).getUserId();
rocketLaunchManager.launchAsync(roomId, retryTriggerUserId);
return 0;
}
for (RocketEnergyAddCmd cmd : batch) { for (RocketEnergyAddCmd cmd : batch) {
long beforeEnergy = rocketStatus.getCurrentEnergy(); long beforeEnergy = rocketStatus.getCurrentEnergy();
long giftEnergy = cmd.getGiftGoldValue();
// 增加能量 // 领域对象会把单笔礼物截断到当前火箭的剩余能量
rocketStatus.addEnergy(cmd.getUserId(), cmd.getGiftGoldValue()); rocketStatus.addEnergy(cmd.getUserId(), giftEnergy);
totalEnergy += cmd.getGiftGoldValue(); requestedEnergy += giftEnergy;
processedCount++;
long afterEnergy = rocketStatus.getCurrentEnergy(); long afterEnergy = rocketStatus.getCurrentEnergy();
acceptedEnergy += afterEnergy - beforeEnergy;
// 记录触发满能量的用户 // 记录触发满能量的用户
if (beforeEnergy < maxEnergy && afterEnergy >= maxEnergy && triggerUserId == null) { if (beforeEnergy < maxEnergy && afterEnergy >= maxEnergy && triggerUserId == null) {
triggerUserId = cmd.getUserId(); triggerUserId = cmd.getUserId();
log.info("找到触发升级的用户: userId={}, beforeEnergy={}, afterEnergy={}, maxEnergy={}", long discardedEnergy = Math.max(0L, giftEnergy - (afterEnergy - beforeEnergy));
triggerUserId, beforeEnergy, afterEnergy, maxEnergy); log.info("找到触发升级的用户: userId={}, beforeEnergy={}, afterEnergy={}, maxEnergy={}, 单笔溢出丢弃={}",
triggerUserId, beforeEnergy, afterEnergy, maxEnergy, discardedEnergy);
// 当前礼物之后的队列记录是独立送礼留到下一级火箭再处理
break;
} }
} }
@ -300,8 +318,8 @@ public class RocketEnergyAggregator {
throw e; throw e;
} }
log.info("批量增加火箭能量: roomId={}, 批次数量={}, 总能量={}, 更新前={}, 更新后={}/{}", log.info("批量增加火箭能量: roomId={}, 读取数量={}, 处理数量={}, 请求能量={}, 计入能量={}, 更新前={}, 更新后={}/{}",
roomId, batch.size(), totalEnergy, energyBeforeAdd, roomId, batch.size(), processedCount, requestedEnergy, acceptedEnergy, energyBeforeAdd,
rocketStatus.getCurrentEnergy(), rocketStatus.getMaxEnergy()); rocketStatus.getCurrentEnergy(), rocketStatus.getMaxEnergy());
// 推送更新消息节流 // 推送更新消息节流
@ -320,6 +338,7 @@ public class RocketEnergyAggregator {
// 异步触发发射 // 异步触发发射
rocketLaunchManager.launchAsync(roomId, triggerUserId); rocketLaunchManager.launchAsync(roomId, triggerUserId);
} }
return processedCount;
} }
/** /**

View File

@ -108,7 +108,13 @@ public class RocketStatus {
* 增加能量 * 增加能量
*/ */
public void addEnergy(Long userId,Long energyValue) { public void addEnergy(Long userId,Long energyValue) {
this.currentEnergy += energyValue; if (energyValue == null || energyValue <= 0 || this.currentEnergy >= this.maxEnergy) {
return;
}
// 单笔礼物只补满当前火箭超出当前档位的能量不进入下一级
long acceptedEnergy = Math.min(energyValue, this.maxEnergy - this.currentEnergy);
this.currentEnergy += acceptedEnergy;
RocketContributor contributor = findContributor(userId); RocketContributor contributor = findContributor(userId);
if (contributor == null) { if (contributor == null) {
@ -119,46 +125,20 @@ public class RocketStatus {
contributor.setFirstContributeTime(LocalDateTime.now()); contributor.setFirstContributeTime(LocalDateTime.now());
this.contributors.add(contributor); this.contributors.add(contributor);
} }
contributor.addEnergy(energyValue); contributor.addEnergy(acceptedEnergy);
contributor.setLastContributeTime(LocalDateTime.now()); contributor.setLastContributeTime(LocalDateTime.now());
recalculateContributionRate(); recalculateContributionRate();
this.updatedAt = LocalDateTime.now(); this.updatedAt = LocalDateTime.now();
} }
// RocketStatus.java
/** /**
* 计算能发射的最高等级 * 判断当前等级是否可以发射
* @return 能发射的等级如果能量不足返回null * @return 能发射的等级如果能量不足返回null
*/ */
public RocketLevel calculateLaunchLevel(RocketConfigGateway configGateway) { public RocketLevel calculateLaunchLevel(RocketConfigGateway configGateway) {
long currentEnergy = this.currentEnergy; // 发射等级只能是正在充能的当前等级禁止单笔大额礼物直接跨级
return isFullEnergy() ? this.level : null;
// 如果当前已经是最高级不能再发射
if (this.level.isMaxLevel()) {
return this.level;
}
// 查询所有启用的配置
List<RocketConfig> configs = configGateway.findAllEnabled();
if (configs == null || configs.isEmpty()) {
log.warn("未找到启用的火箭配置");
return null;
}
// 按等级从高到低排序
configs.sort((a, b) -> b.getLevel().compareTo(a.getLevel()));
// 从最高等级开始查找找到能量满足发射条件的最高等级
for (RocketConfig config : configs) {
if (currentEnergy >= config.getMaxEnergy()) {
return RocketLevel.of(config.getLevel());
}
}
return null;
} }
/** /**
@ -187,14 +167,11 @@ public class RocketStatus {
return; return;
} }
// 保留当前能量值
long preservedEnergy = this.currentEnergy;
// 升级到目标等级 // 升级到目标等级
RocketLevel oldLevel = this.level; RocketLevel oldLevel = this.level;
this.level = targetLevel; this.level = targetLevel;
this.maxEnergy = targetConfig.getMaxEnergy(); this.maxEnergy = targetConfig.getMaxEnergy();
this.currentEnergy = preservedEnergy; // 完全保留当前能量 this.currentEnergy = 0L;
this.status = RocketStatusEnum.CHARGING; this.status = RocketStatusEnum.CHARGING;
// 清空贡献者新一轮 // 清空贡献者新一轮
@ -202,8 +179,8 @@ public class RocketStatus {
this.launchTime = null; this.launchTime = null;
this.claimExpireTime = null; this.claimExpireTime = null;
log.info("火箭升级成功, {}级 -> {}级, 保留能量={}, 新目标={}", log.info("火箭升级成功, {}级 -> {}级, 新等级能量从0开始, 新目标={}",
oldLevel.getLevel(), targetLevel.getLevel(), preservedEnergy, this.maxEnergy); oldLevel.getLevel(), targetLevel.getLevel(), this.maxEnergy);
} }
/** /**