From 3e360af785fdfb8d12c4b7c0cefc9a43af54c8cd Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Thu, 13 Nov 2025 14:23:56 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E6=88=BF=E9=97=B4=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E9=A2=86=E5=8F=96=E5=A5=96=E5=8A=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rocket/RocketRewardClaimCmdExe.java | 87 +++++++++++-------- .../app/command/room/RoomEnterCmdExe.java | 9 ++ .../other/app/config/RocketAsyncConfig.java | 58 +++++++++++-- .../app/manager/RocketLaunchManager.java | 2 +- .../mongo/document/RocketHistoryDocument.java | 3 + 5 files changed, 111 insertions(+), 48 deletions(-) diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java index 0dba0c50..e71b5e24 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/rocket/RocketRewardClaimCmdExe.java @@ -26,6 +26,7 @@ import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; import com.red.circle.wallet.inner.model.enums.GoldOrigin; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import java.math.BigDecimal; @@ -58,7 +59,7 @@ public class RocketRewardClaimCmdExe { private final Random random = new Random(); /** - * 执行奖励领取 + * 执行奖励领取(同步) */ public RocketRewardCO execute(RocketRewardClaimCmd cmd) { Long userId = cmd.getReqUserId(); @@ -67,6 +68,31 @@ public class RocketRewardClaimCmdExe { return distributedLockUtil.executeWithLock(lockKey, 5L, () -> doClaim(cmd), "Reward collection operations are too frequent. Please try again later"); } + /** + * 异步自动领取奖励(进房触发) + * 无返回值,静默领取 + */ + @Async("rocketRewardExecutor") + public void executeAsync(Long roomId, Long userId) { + try { + log.debug("异步自动领取火箭奖励: roomId={}, userId={}", roomId, userId); + + RocketRewardClaimCmd cmd = new RocketRewardClaimCmd(); + cmd.setRoomId(roomId); + cmd.setReqUserId(userId); + + RocketRewardCO result = execute(cmd); + + if (result != null) { + log.info("异步自动领取成功: roomId={}, userId={}, reward={}", + roomId, userId, result.getRewardType()); + } + } catch (Exception e) { + // 静默失败,不影响进房 + log.error("异步自动领取火箭奖励失败: roomId={}, userId={}", roomId, userId, e); + } + } + /** * 执行领取逻辑 */ @@ -78,46 +104,54 @@ public class RocketRewardClaimCmdExe { // 1. 查询今天最新的火箭发射记录 RocketHistory latestHistory = rocketHistoryGateway.findLatestByRoomIdAndDate(roomId, today); if (latestHistory == null) { - throw new RuntimeException("There are no rockets launched today, and there are no rewards to receive"); + return null; } Integer rocketLevel = latestHistory.getLevel(); + String historyId = latestHistory.getId(); - // 2. 检查是否已领取(每天每个房间每个等级只能领一次) - boolean hasClaimed = rocketRewardLogGateway.existsByRocketAndUser(latestHistory.getId(), userId); + // 2. 检查是否已领取 + boolean hasClaimed = rocketRewardLogGateway.existsByRocketAndUser(historyId, userId); if (hasClaimed) { - throw new RuntimeException("This class of rocket has been collected today. Please wait for the next stage or come back tomorrow."); + return null; } // 3. 查询火箭配置 RocketConfig config = rocketConfigGateway.findByLevel(rocketLevel); if (config == null) { - throw new RuntimeException("火箭配置不存在"); + return null; } - // 4. MongoDB原子递增检查 + // 4. 检查库存(查询,不递增) int rewardQuantity = config.getRewardQuantity() != null ? config.getRewardQuantity() : 20; - boolean success = rocketHistoryGateway.tryIncrementClaimCount(latestHistory.getId(), rewardQuantity); - if (!success) { - throw new RuntimeException("The rewards have been collected, please come earlier next time"); + int currentClaimed = latestHistory.getClaimedCount() != null ? latestHistory.getClaimedCount() : 0; + if (currentClaimed >= rewardQuantity) { + return null; } // 5. 解析奖励配置 List rewardList = parseRewardConfig(config.getRewardConfig().toJSONString()); if (rewardList == null || rewardList.isEmpty()) { - throw new RuntimeException("奖励配置为空"); + return null; } // 6. 随机选择一个奖励 PropsActivityRewardConfigInfoDTO selectedReward = rewardList.get(random.nextInt(rewardList.size())); - // 7. 发放奖励 + // 7. 发放奖励(先发放) String walletBizNo = RocketRewardLog.buildBizNo(roomId.toString(), userId); grantReward(userId, selectedReward, walletBizNo); // 8. 保存领取记录 saveRewardLog(roomId, userId, latestHistory, selectedReward, walletBizNo); + // 9. 递增领取人数(发放成功后再递增) + try { + rocketHistoryGateway.incrementClaimCount(historyId); + } catch (Exception e) { + log.error("递增领取人数失败: historyId={}", historyId, e); + } + // 10. 构建返回结果 RocketRewardCO rewardCO = new RocketRewardCO(); rewardCO.setRewardType(selectedReward.getType()); @@ -146,7 +180,7 @@ public class RocketRewardClaimCmdExe { } /** - * 发放奖励(手动实现) + * 发放奖励 */ private void grantReward(Long userId, PropsActivityRewardConfigInfoDTO reward, String bizNo) { try { @@ -154,19 +188,16 @@ public class RocketRewardClaimCmdExe { switch (rewardType) { case "GOLD" -> { - // 发放金币 Long goldAmount = Long.parseLong(reward.getContent()); grantGold(userId, goldAmount, bizNo); } case "PROPS" -> { - // 发放道具(头饰/座驾等) Long propId = Long.parseLong(reward.getContent()); Integer quantity = reward.getQuantity() != null ? reward.getQuantity() : 1; - String detailType = reward.getDetailType(); // AVATAR_FRAME / RIDE + String detailType = reward.getDetailType(); grantProp(userId, propId, quantity, detailType, bizNo); } case "BADGE" -> { - // 发放徽章 Long badgeId = Long.parseLong(reward.getContent()); Integer quantity = reward.getQuantity() != null ? reward.getQuantity() : 1; grantBadge(userId, badgeId, quantity, bizNo); @@ -187,7 +218,6 @@ public class RocketRewardClaimCmdExe { */ private void grantGold(Long userId, Long goldAmount, String bizNo) { try { - // 防止配错导致发送大量金币 BigDecimal amount = BigDecimal.valueOf(goldAmount); walletGoldClient.changeBalance( GoldReceiptCmd.builder() @@ -201,7 +231,6 @@ public class RocketRewardClaimCmdExe { .remark("") .build() ); - } catch (Exception e) { log.error("金币发放失败: userId={}, amount={}", userId, goldAmount, e); throw new RuntimeException("金币发放失败"); @@ -209,11 +238,10 @@ public class RocketRewardClaimCmdExe { } /** - * 发放道具(头饰/座驾等) + * 发放道具 */ private void grantProp(Long userId, Long propId, Integer quantity, String detailType, String bizNo) { try { - GivePropsBackpackCmd cmd = new GivePropsBackpackCmd() .setAcceptUserId(userId) .setPropsId(propId) @@ -226,7 +254,6 @@ public class RocketRewardClaimCmdExe { .setOpUser(userId); propsBackpackClient.giveProps(cmd); - // 发送通知 propsBackpackClient.givePropsNotify(userId, detailType, quantity, propId); } catch (Exception e) { log.error("道具发放失败: userId={}, propId={}", userId, propId, e); @@ -239,8 +266,6 @@ public class RocketRewardClaimCmdExe { */ private void grantBadge(Long userId, Long badgeId, Integer quantity, String bizNo) { try { - - // 徽章赠送天数大于1000天则为永久赠送 BadgeBackpackExpireTypeEnum expireType = quantity >= 1000 ? BadgeBackpackExpireTypeEnum.PERMANENT : BadgeBackpackExpireTypeEnum.TEMPORARY; @@ -253,7 +278,6 @@ public class RocketRewardClaimCmdExe { .setBadgeType(SysBadgeConfigTypeEnum.ACTIVITY) ); - // 发送通知 propsBackpackClient.givePropsNotify(userId, SysBadgeConfigTypeEnum.ACTIVITY.name(), quantity, badgeId); } catch (Exception e) { log.error("徽章发放失败: userId={}, badgeId={}", userId, badgeId, e); @@ -298,17 +322,4 @@ public class RocketRewardClaimCmdExe { int quantity = reward.getQuantity() == null ? 0 : reward.getQuantity(); return Long.parseLong(Integer.toString(quantity)); } - - /** - * 查找贡献者(备用) - */ - private RocketContributor findContributor(RocketStatus rocketStatus, Long userId) { - if (rocketStatus.getContributors() == null) { - return null; - } - return rocketStatus.getContributors().stream() - .filter(c -> c.getUserId().equals(userId)) - .findFirst() - .orElse(null); - } } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java index 1ba673e0..1e8045bd 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java @@ -8,6 +8,7 @@ import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.framework.core.response.ResponseErrorCode; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.rocket.business.producer.TaskMqMessage; +import com.red.circle.other.app.command.rocket.RocketRewardClaimCmdExe; import com.red.circle.other.app.convertor.live.RoomProfileAppConvertor; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; import com.red.circle.other.app.dto.clientobject.room.EntryRoomEntrantsCO; @@ -82,6 +83,7 @@ public class RoomEnterCmdExe { private final UserAgoraTokenCacheService userAgoraTokenCacheService; private final TaskMqMessage taskMqMessage; private final AdministratorAuthService administratorAuthService; + private final RocketRewardClaimCmdExe rocketRewardClaimCmdExe; public EntryRoomResponseCO execute(RoomEntryCmd cmd) { //checkEntryUserId(cmd); @@ -124,6 +126,13 @@ public class RoomEnterCmdExe { log.error("进房间添加足迹异常:{}", e.getMessage()); } + // 自动领取火箭奖励 + try { + rocketRewardClaimCmdExe.executeAsync(cmd.getRoomId(), cmd.getReqUserId()); + } catch (Exception e) { + log.error("触发自动领取火箭奖励失败: roomId={}, userId={}", cmd.getRoomId(), cmd.getReqUserId(), e); + } + return new EntryRoomResponseCO() .setRoomProfile( new EntryRoomProfileCO() diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/config/RocketAsyncConfig.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/config/RocketAsyncConfig.java index 024db71b..45cb6de9 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/config/RocketAsyncConfig.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/config/RocketAsyncConfig.java @@ -23,25 +23,26 @@ import java.util.concurrent.ThreadPoolExecutor; public class RocketAsyncConfig { /** - * 火箭任务线程池 + * 火箭发射专用线程池 + * 并发场景:100个活跃房间,活动期间可能有20-30个房间同时触发发射 */ - @Bean("rocketTaskExecutor") - public Executor rocketTaskExecutor() { + @Bean("rocketLaunchExecutor") + public Executor rocketLaunchExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数 - executor.setCorePoolSize(5); + executor.setCorePoolSize(10); // 最大线程数 - executor.setMaxPoolSize(10); + executor.setMaxPoolSize(30); // 队列容量 - executor.setQueueCapacity(100); + executor.setQueueCapacity(50); // 线程名前缀 - executor.setThreadNamePrefix("rocket-task-"); + executor.setThreadNamePrefix("rocket-launch-"); - // 拒绝策略:调用者运行 + // 拒绝策略:发射任务重要,使用调用者运行策略保证执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 线程空闲时间(秒) @@ -53,7 +54,46 @@ public class RocketAsyncConfig { executor.initialize(); - log.info("火箭任务线程池初始化完成"); + log.info("火箭发射线程池初始化完成: core={}, max={}, queue={}", + executor.getCorePoolSize(), executor.getMaxPoolSize(), executor.getQueueCapacity()); + + return executor; + } + + /** + * 火箭领奖专用线程池 + * 使用场景:用户进房自动领奖 + */ + @Bean("rocketRewardExecutor") + public Executor rocketRewardExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + // 线程数多一些,应对200-500活跃用户 + executor.setCorePoolSize(15); + + // 最大线程数 + executor.setMaxPoolSize(30); + + // 大容量队列,缓冲进房峰值请求 + executor.setQueueCapacity(500); + + // 线程名前缀 + executor.setThreadNamePrefix("rocket-reward-"); + + // 拒绝策略:领奖可以丢弃,避免阻塞进房接口 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); + + // 线程空闲时间(秒) + executor.setKeepAliveSeconds(60); + + // 关闭时等待任务完成 + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAwaitTerminationSeconds(60); + + executor.initialize(); + + log.info("火箭领奖线程池初始化完成: core={}, max={}, queue={}", + executor.getCorePoolSize(), executor.getMaxPoolSize(), executor.getQueueCapacity()); return executor; } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketLaunchManager.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketLaunchManager.java index 7433106a..f8dc9397 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketLaunchManager.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/RocketLaunchManager.java @@ -25,7 +25,7 @@ public class RocketLaunchManager { * @param roomId 房间ID * @param triggerUserId 触发用户ID */ - @Async("rocketTaskExecutor") + @Async("rocketLaunchExecutor") public void launchAsync(Long roomId, Long triggerUserId) { try { log.info("异步执行火箭发射: roomId={}, 触发用户={}", roomId, triggerUserId); diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java index 73c951f5..bbb6592b 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/document/RocketHistoryDocument.java @@ -50,6 +50,9 @@ public class RocketHistoryDocument { @Field("claim_stats") private ClaimStats claimStats; + @Field("claimedCount") + private Integer claimedCount; + @Field("launch_time") private LocalDateTime launchTime;