进房间异步自动领取奖励
This commit is contained in:
parent
ca77bcf74c
commit
3e360af785
@ -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<PropsActivityRewardConfigInfoDTO> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -50,6 +50,9 @@ public class RocketHistoryDocument {
|
||||
@Field("claim_stats")
|
||||
private ClaimStats claimStats;
|
||||
|
||||
@Field("claimedCount")
|
||||
private Integer claimedCount;
|
||||
|
||||
@Field("launch_time")
|
||||
private LocalDateTime launchTime;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user