From 88b20f9ca34586d6832a3be0d95120c8ec4cf035 Mon Sep 17 00:00:00 2001 From: zhx Date: Mon, 20 Jul 2026 16:15:29 +0800 Subject: [PATCH] fix: discard rocket gift overflow between levels --- .../app/manager/RocketEnergyAggregator.java | 49 ++++++++++++------ .../other/domain/rocket/RocketStatus.java | 51 +++++-------------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketEnergyAggregator.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketEnergyAggregator.java index 77c4f0c5..297e009a 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketEnergyAggregator.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketEnergyAggregator.java @@ -222,14 +222,17 @@ public class RocketEnergyAggregator { return false; } - // 处理能量增加 - processRoomEnergyBatch(roomId, batch); + // 处理能量增加;火箭满能时只消费到触发礼物,后续独立礼物留给下一级。 + int processedCount = processRoomEnergyBatch(roomId, batch); + if (processedCount <= 0) { + return false; + } // 处理成功后才删除Redis中的数据(使用 LTRIM) - redisTemplate.opsForList().trim(queueKey, batch.size(), -1); + redisTemplate.opsForList().trim(queueKey, processedCount, -1); log.info("成功处理房间{}能量, 处理数量={}, 剩余={}", - roomId, batch.size(), queueSize - batch.size()); + roomId, processedCount, queueSize - processedCount); return true; @@ -242,7 +245,7 @@ public class RocketEnergyAggregator { /** * 处理单个房间的能量批次 */ - private void processRoomEnergyBatch(Long roomId, List batch) { + private int processRoomEnergyBatch(Long roomId, List batch) { String riyadhToday = ZonedDateTimeAsiaRiyadhUtils.nowFormat("yyyy-MM-dd"); // 查询当前火箭状态 @@ -264,7 +267,7 @@ public class RocketEnergyAggregator { // 校验状态 if (!rocketStatus.canAddEnergy()) { log.warn("火箭不可充能, roomId={}, status={}", roomId, rocketStatus.getStatus()); - return; + return 0; } // 记录增加前的状态 @@ -272,23 +275,38 @@ public class RocketEnergyAggregator { long maxEnergy = rocketStatus.getMaxEnergy(); // 批量增加能量 - long totalEnergy = 0L; + long requestedEnergy = 0L; + long acceptedEnergy = 0L; + int processedCount = 0; 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) { long beforeEnergy = rocketStatus.getCurrentEnergy(); + long giftEnergy = cmd.getGiftGoldValue(); - // 增加能量 - rocketStatus.addEnergy(cmd.getUserId(), cmd.getGiftGoldValue()); - totalEnergy += cmd.getGiftGoldValue(); + // 领域对象会把单笔礼物截断到当前火箭的剩余能量。 + rocketStatus.addEnergy(cmd.getUserId(), giftEnergy); + requestedEnergy += giftEnergy; + processedCount++; long afterEnergy = rocketStatus.getCurrentEnergy(); + acceptedEnergy += afterEnergy - beforeEnergy; // 记录触发满能量的用户 if (beforeEnergy < maxEnergy && afterEnergy >= maxEnergy && triggerUserId == null) { triggerUserId = cmd.getUserId(); - log.info("找到触发升级的用户: userId={}, beforeEnergy={}, afterEnergy={}, maxEnergy={}", - triggerUserId, beforeEnergy, afterEnergy, maxEnergy); + long discardedEnergy = Math.max(0L, giftEnergy - (afterEnergy - beforeEnergy)); + log.info("找到触发升级的用户: userId={}, beforeEnergy={}, afterEnergy={}, maxEnergy={}, 单笔溢出丢弃={}", + triggerUserId, beforeEnergy, afterEnergy, maxEnergy, discardedEnergy); + // 当前礼物之后的队列记录是独立送礼,留到下一级火箭再处理。 + break; } } @@ -300,8 +318,8 @@ public class RocketEnergyAggregator { throw e; } - log.info("批量增加火箭能量: roomId={}, 批次数量={}, 总能量={}, 更新前={}, 更新后={}/{}", - roomId, batch.size(), totalEnergy, energyBeforeAdd, + log.info("批量增加火箭能量: roomId={}, 读取数量={}, 处理数量={}, 请求能量={}, 计入能量={}, 更新前={}, 更新后={}/{}", + roomId, batch.size(), processedCount, requestedEnergy, acceptedEnergy, energyBeforeAdd, rocketStatus.getCurrentEnergy(), rocketStatus.getMaxEnergy()); // 推送更新消息(节流) @@ -320,6 +338,7 @@ public class RocketEnergyAggregator { // 异步触发发射 rocketLaunchManager.launchAsync(roomId, triggerUserId); } + return processedCount; } /** @@ -424,4 +443,4 @@ public class RocketEnergyAggregator { return status; } -} \ No newline at end of file +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketStatus.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketStatus.java index bf5cb2aa..3a412f49 100644 --- a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketStatus.java +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketStatus.java @@ -108,7 +108,13 @@ public class RocketStatus { * 增加能量 */ 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); if (contributor == null) { @@ -119,46 +125,20 @@ public class RocketStatus { contributor.setFirstContributeTime(LocalDateTime.now()); this.contributors.add(contributor); } - contributor.addEnergy(energyValue); + contributor.addEnergy(acceptedEnergy); contributor.setLastContributeTime(LocalDateTime.now()); recalculateContributionRate(); this.updatedAt = LocalDateTime.now(); } - // RocketStatus.java - - /** - * 计算能发射的最高等级 + * 判断当前等级是否可以发射 * @return 能发射的等级,如果能量不足返回null */ public RocketLevel calculateLaunchLevel(RocketConfigGateway configGateway) { - long currentEnergy = this.currentEnergy; - - // 如果当前已经是最高级,不能再发射 - if (this.level.isMaxLevel()) { - return this.level; - } - - // 查询所有启用的配置 - List 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; + // 发射等级只能是正在充能的当前等级,禁止单笔大额礼物直接跨级。 + return isFullEnergy() ? this.level : null; } /** @@ -187,14 +167,11 @@ public class RocketStatus { return; } - // 保留当前能量值 - long preservedEnergy = this.currentEnergy; - // 升级到目标等级 RocketLevel oldLevel = this.level; this.level = targetLevel; this.maxEnergy = targetConfig.getMaxEnergy(); - this.currentEnergy = preservedEnergy; // 完全保留当前能量 + this.currentEnergy = 0L; this.status = RocketStatusEnum.CHARGING; // 清空贡献者(新一轮) @@ -202,8 +179,8 @@ public class RocketStatus { this.launchTime = null; this.claimExpireTime = null; - log.info("火箭升级成功, {}级 -> {}级, 保留能量={}, 新目标={}", - oldLevel.getLevel(), targetLevel.getLevel(), preservedEnergy, this.maxEnergy); + log.info("火箭升级成功, {}级 -> {}级, 新等级能量从0开始, 新目标={}", + oldLevel.getLevel(), targetLevel.getLevel(), this.maxEnergy); } /**