火箭升级完善
This commit is contained in:
parent
c76526d3c0
commit
f91f550e8e
@ -131,4 +131,14 @@ public enum GroupMessageTypeEnum {
|
||||
*/
|
||||
GAME_BAISHUN_WIN,
|
||||
|
||||
/**
|
||||
* 火箭能力值更新
|
||||
*/
|
||||
ROCKET_ENERGY_UPDATE,
|
||||
|
||||
/**
|
||||
* 火箭发射
|
||||
*/
|
||||
ROCKET_ENERGY_LAUNCH,
|
||||
|
||||
}
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
package com.red.circle.other.app.command.rocket;
|
||||
|
||||
import com.red.circle.other.app.dto.cmd.RocketEnergyAddCmd;
|
||||
import com.red.circle.other.app.util.DistributedLockUtil;
|
||||
import com.red.circle.other.domain.gateway.RocketStatusGateway;
|
||||
import com.red.circle.other.domain.rocket.RocketStatus;
|
||||
import com.red.circle.other.infra.database.cache.key.RocketKeys;
|
||||
import com.red.circle.other.app.manager.RocketEnergyAggregator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 火箭能量增加命令执行器
|
||||
* 优化版:使用异步聚合,避免频繁锁竞争和推送
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
@ -22,89 +18,16 @@ import java.time.LocalDate;
|
||||
@RequiredArgsConstructor
|
||||
public class RocketEnergyAddCmdExe {
|
||||
|
||||
private final RocketStatusGateway rocketStatusGateway;
|
||||
private final DistributedLockUtil distributedLockUtil;
|
||||
private final RocketLaunchCmdExe rocketLaunchCmdExe;
|
||||
private final RocketEnergyAggregator rocketEnergyAggregator;
|
||||
|
||||
/**
|
||||
* 幸运礼物加成比例
|
||||
*/
|
||||
private static final double LUCKY_BONUS_RATE = 0.04;
|
||||
|
||||
/**
|
||||
* 执行能量增加
|
||||
* 执行能量增加(异步聚合)
|
||||
*/
|
||||
public void execute(RocketEnergyAddCmd cmd) {
|
||||
// 构建锁Key
|
||||
String lockKey = RocketKeys.LOCK.getKey(cmd.getRoomId());
|
||||
// 直接提交到聚合器,不加锁
|
||||
rocketEnergyAggregator.addEnergy(cmd);
|
||||
|
||||
// 使用分布式锁执行
|
||||
distributedLockUtil.executeWithLock(lockKey, 5L, () -> {
|
||||
doAddEnergy(cmd);
|
||||
return null;
|
||||
}, "火箭能量增加操作过于频繁,请稍后重试");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行能量增加逻辑
|
||||
*/
|
||||
private void doAddEnergy(RocketEnergyAddCmd cmd) {
|
||||
// 1. 查询当前火箭状态
|
||||
String today = LocalDate.now().toString();
|
||||
RocketStatus rocketStatus = rocketStatusGateway.findByRoomIdAndDate(cmd.getRoomId(), today);
|
||||
|
||||
if (rocketStatus == null) {
|
||||
// 初始化火箭
|
||||
rocketStatus = RocketStatus.init(cmd.getRoomId());
|
||||
log.info("初始化房间火箭, roomId={}", cmd.getRoomId());
|
||||
}
|
||||
|
||||
// 2. 校验状态
|
||||
if (!rocketStatus.canAddEnergy()) {
|
||||
log.warn("火箭不可充能, roomId={}, status={}", cmd.getRoomId(), rocketStatus.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 计算能量值
|
||||
long energyValue = calculateEnergy(cmd);
|
||||
|
||||
// 4. 增加能量
|
||||
rocketStatus.addEnergy(
|
||||
cmd.getUserId(),
|
||||
cmd.getUserName(),
|
||||
cmd.getUserAvatar(),
|
||||
energyValue
|
||||
);
|
||||
|
||||
// 5. 保存状态
|
||||
rocketStatusGateway.save(rocketStatus);
|
||||
|
||||
log.info("火箭能量增加成功, roomId={}, userId={}, energy={}, currentEnergy={}, level={}",
|
||||
cmd.getRoomId(), cmd.getUserId(), energyValue,
|
||||
rocketStatus.getCurrentEnergy(), rocketStatus.getLevel().getLevel());
|
||||
|
||||
// 6. 检查是否需要发射
|
||||
if (rocketStatus.isFullEnergy()) {
|
||||
log.info("火箭能量已满, 准备发射, roomId={}, level={}, finalEnergy={}",
|
||||
cmd.getRoomId(), rocketStatus.getLevel().getLevel(), rocketStatus.getCurrentEnergy());
|
||||
|
||||
// 触发发射
|
||||
rocketLaunchCmdExe.execute(cmd.getRoomId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算能量值
|
||||
*/
|
||||
private long calculateEnergy(RocketEnergyAddCmd cmd) {
|
||||
long baseEnergy = cmd.getGiftGoldValue();
|
||||
|
||||
if (Boolean.TRUE.equals(cmd.getIsLucky())) {
|
||||
// 幸运礼物额外增加4%
|
||||
long luckyBonus = (long) (baseEnergy * LUCKY_BONUS_RATE);
|
||||
return baseEnergy + luckyBonus;
|
||||
}
|
||||
|
||||
return baseEnergy;
|
||||
log.debug("提交火箭能量增加任务: roomId={}, userId={}, energy={}",
|
||||
cmd.getRoomId(), cmd.getUserId(), cmd.getGiftGoldValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,12 +4,14 @@ import com.red.circle.other.app.convertor.RocketConvertor;
|
||||
import com.red.circle.other.app.dto.clientobject.RocketStatusCO;
|
||||
import com.red.circle.other.app.manager.RocketImPushManager;
|
||||
import com.red.circle.other.app.util.DistributedLockUtil;
|
||||
import com.red.circle.other.domain.gateway.RocketConfigGateway;
|
||||
import com.red.circle.other.domain.gateway.RocketHistoryGateway;
|
||||
import com.red.circle.other.domain.gateway.RocketStatusGateway;
|
||||
import com.red.circle.other.domain.rocket.RocketHistory;
|
||||
import com.red.circle.other.domain.rocket.RocketStatus;
|
||||
import com.red.circle.other.domain.rocket.RocketStatusEnum;
|
||||
import com.red.circle.other.infra.database.cache.key.RocketKeys;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -18,6 +20,7 @@ import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 火箭发射命令执行器
|
||||
* 优化版:发射后自动升级到下一级火箭
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
@ -31,16 +34,16 @@ public class RocketLaunchCmdExe {
|
||||
private final RocketHistoryGateway rocketHistoryGateway;
|
||||
private final DistributedLockUtil distributedLockUtil;
|
||||
private final RocketImPushManager rocketImPushManager;
|
||||
private final RocketConfigGateway rocketConfigGateway;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
|
||||
/**
|
||||
* 执行火箭发射
|
||||
*/
|
||||
public void execute(Long roomId) {
|
||||
// 构建锁Key
|
||||
String lockKey = RocketKeys.LOCK.getKey(roomId);
|
||||
|
||||
// 使用分布式锁执行
|
||||
distributedLockUtil.executeWithLock(lockKey, 5L, () -> {
|
||||
distributedLockUtil.executeWithLock(lockKey, 10L, () -> {
|
||||
doLaunch(roomId);
|
||||
return null;
|
||||
}, "火箭发射操作过于频繁,请稍后重试");
|
||||
@ -50,7 +53,7 @@ public class RocketLaunchCmdExe {
|
||||
* 执行发射逻辑
|
||||
*/
|
||||
private void doLaunch(Long roomId) {
|
||||
// 1. 查询当前火箭状态
|
||||
// 1. 查询当前火箭状态(需要完整数据,包含贡献者)
|
||||
String today = LocalDate.now().toString();
|
||||
RocketStatus rocketStatus = rocketStatusGateway.findByRoomIdAndDate(roomId, today);
|
||||
|
||||
@ -72,37 +75,53 @@ public class RocketLaunchCmdExe {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 发射火箭
|
||||
rocketStatus.launch();
|
||||
// 4. 记录发射前的等级(用于日志和历史)
|
||||
int launchedLevel = rocketStatus.getLevel().getLevel();
|
||||
long finalEnergy = rocketStatus.getCurrentEnergy();
|
||||
int contributorCount = rocketStatus.getContributors() != null ? rocketStatus.getContributors().size() : 0;
|
||||
|
||||
// 5. 立即进入领取状态
|
||||
rocketStatus.startClaiming();
|
||||
|
||||
// 6. 保存状态
|
||||
rocketStatusGateway.save(rocketStatus);
|
||||
|
||||
// 7. 保存历史记录
|
||||
// 5. 保存历史记录(发射前保存,保留贡献者信息)
|
||||
try {
|
||||
RocketHistory history = RocketHistory.fromRocketStatus(rocketStatus);
|
||||
String historyId = rocketHistoryGateway.save(history);
|
||||
log.info("保存火箭历史记录成功: historyId={}, roomId={}", historyId, roomId);
|
||||
log.info("保存火箭历史记录成功: historyId={}, roomId={}, level={}", historyId, roomId, launchedLevel);
|
||||
} catch (Exception e) {
|
||||
log.error("保存火箭历史记录失败: roomId={}", roomId, e);
|
||||
}
|
||||
|
||||
// 8. 推送发射动画消息
|
||||
// 6. 推送发射动画消息(发射前推送,包含贡献者信息)
|
||||
try {
|
||||
RocketStatusCO statusCO = RocketConvertor.toStatusCO(rocketStatus);
|
||||
rocketImPushManager.pushRocketLaunch(roomId, statusCO);
|
||||
rocketImPushManager.pushRocketLaunch( statusCO);
|
||||
} catch (Exception e) {
|
||||
log.error("推送火箭发射消息失败: roomId={}", roomId, e);
|
||||
}
|
||||
|
||||
log.info("火箭发射成功, roomId={}, level={}, finalEnergy={}, contributors={}, launchTime={}",
|
||||
// 7. 发射并自动升级到下一级
|
||||
rocketStatus.launchAndUpgrade(rocketConfigGateway);
|
||||
|
||||
// 8. 保存新状态
|
||||
rocketStatusGateway.save(rocketStatus);
|
||||
|
||||
log.info("火箭发射并升级成功: roomId={}, 发射等级={}, 最终能量={}, 贡献者数={}, 升级到={}",
|
||||
roomId,
|
||||
rocketStatus.getLevel().getLevel(),
|
||||
rocketStatus.getCurrentEnergy(),
|
||||
rocketStatus.getContributors() != null ? rocketStatus.getContributors().size() : 0,
|
||||
rocketStatus.getLaunchTime());
|
||||
launchedLevel,
|
||||
finalEnergy,
|
||||
contributorCount,
|
||||
rocketStatus.getLevel().getLevel());
|
||||
|
||||
// 9. 如果升级成功,推送升级消息
|
||||
if (rocketStatus.getLevel().getLevel() > launchedLevel) {
|
||||
try {
|
||||
RocketStatusCO newStatusCO = RocketConvertor.toStatusCO(rocketStatus);
|
||||
String roomAccount = roomProfileManagerService.getRoomAccount(roomId);
|
||||
rocketImPushManager.pushEnergyUpdate(roomAccount, newStatusCO);
|
||||
|
||||
String upgradeMsg = launchedLevel == 3 ? "重置为一级火箭" : "升级到" + rocketStatus.getLevel().getName();
|
||||
log.info("推送火箭升级消息: roomId={}, {}", roomId, upgradeMsg);
|
||||
} catch (Exception e) {
|
||||
log.error("推送火箭升级消息失败: roomId={}", roomId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
package com.red.circle.other.app.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 火箭异步任务配置
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class RocketAsyncConfig {
|
||||
|
||||
/**
|
||||
* 火箭任务线程池
|
||||
*/
|
||||
@Bean("rocketTaskExecutor")
|
||||
public Executor rocketTaskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
|
||||
// 核心线程数
|
||||
executor.setCorePoolSize(5);
|
||||
|
||||
// 最大线程数
|
||||
executor.setMaxPoolSize(10);
|
||||
|
||||
// 队列容量
|
||||
executor.setQueueCapacity(100);
|
||||
|
||||
// 线程名前缀
|
||||
executor.setThreadNamePrefix("rocket-task-");
|
||||
|
||||
// 拒绝策略:调用者运行
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
|
||||
// 线程空闲时间(秒)
|
||||
executor.setKeepAliveSeconds(60);
|
||||
|
||||
// 关闭时等待任务完成
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
|
||||
executor.initialize();
|
||||
|
||||
log.info("火箭任务线程池初始化完成");
|
||||
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@ -400,7 +400,7 @@ public class GiveGiftsListener implements MessageListener {
|
||||
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.RANK_COUNT, runningWater);
|
||||
|
||||
// 钻石统计相关
|
||||
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.DIAMOND_COUNT, runningWater);
|
||||
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater);
|
||||
}
|
||||
|
||||
private Pair<GiftGiveRunningWater,Boolean> saveRunningWater(GiveAwayGiftBatchEvent event) {
|
||||
|
||||
@ -1,14 +1,28 @@
|
||||
package com.red.circle.other.app.listener.gift.strategy;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.red.circle.mq.business.model.event.gift.OfflineProcessGiftEvent;
|
||||
import com.red.circle.other.app.service.RocketService;
|
||||
import com.red.circle.other.app.command.rocket.RocketEnergyAddCmdExe;
|
||||
import com.red.circle.other.app.common.gift.GameLuckyGiftCommon;
|
||||
import com.red.circle.other.app.dto.cmd.RocketEnergyAddCmd;
|
||||
import com.red.circle.other.infra.database.cache.service.other.RoomManagerCacheService;
|
||||
import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser;
|
||||
import com.red.circle.other.infra.database.mongo.entity.gift.GiftGiveRunningWater;
|
||||
import com.red.circle.other.infra.database.mongo.entity.gift.GiftValue;
|
||||
import com.red.circle.other.infra.database.mongo.service.gift.GiftGiveRunningWaterService;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.other.inner.enums.material.GiftCurrencyType;
|
||||
import com.red.circle.other.inner.enums.material.GiftTabEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 火箭统计相关
|
||||
* 火箭统计相关 - 礼物送出时增加火箭能量
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
@ -16,14 +30,100 @@ import org.springframework.stereotype.Service;
|
||||
@Slf4j
|
||||
@Service("ROCKET_COUNT_LISTENER")
|
||||
@RequiredArgsConstructor
|
||||
public class GiftSendRocketListener implements GiftStrategy {
|
||||
|
||||
private final RocketService rocketService;
|
||||
public class GiftSendRocketListener implements GiftStrategy {
|
||||
|
||||
private final RocketEnergyAddCmdExe rocketEnergyAddCmdExe;
|
||||
private final GiftGiveRunningWaterService giftGiveRunningWaterService;
|
||||
private final GameLuckyGiftCommon gameLuckyGiftCommon;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
|
||||
@Override
|
||||
public void processor(OfflineProcessGiftEvent event) {
|
||||
log.info("【火箭能量增加】接收礼物事件: {}", JSON.toJSONString(event));
|
||||
|
||||
// 1. 查询礼物流水
|
||||
GiftGiveRunningWater runningWater = giftGiveRunningWaterService.getByIdPrimary(
|
||||
event.getRunningWaterId());
|
||||
|
||||
if (Objects.isNull(runningWater)) {
|
||||
log.warn("【火箭能量增加】没有找到流水信息,忽略处理");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 只处理金币礼物
|
||||
if (!GiftCurrencyType.GOLD.eq(runningWater.getGiftValue().getCurrencyType())) {
|
||||
log.debug("【火箭能量增加】非金币礼物,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
giftGiveRunningWaterService.addLog(runningWater.getId(), "开始:火箭能量增加");
|
||||
|
||||
try {
|
||||
// 3. 获取礼物信息
|
||||
GiftValue giftValue = runningWater.getGiftValue();
|
||||
|
||||
// 4. 判断是否为幸运礼物
|
||||
boolean isLuckyGift = isLuckyGift(giftValue);
|
||||
|
||||
// 5. 获取幸运礼物比例
|
||||
BigDecimal luckyGiftRatio = BigDecimal.ZERO;
|
||||
if (isLuckyGift) {
|
||||
luckyGiftRatio = gameLuckyGiftCommon.getLuckyGiftShareRatio(runningWater.getSysOrigin());
|
||||
log.info("【火箭能量增加】幸运礼物比例:{}", luckyGiftRatio);
|
||||
}
|
||||
|
||||
// 6. 计算实际金币价值(考虑幸运礼物加成)
|
||||
Long actualGoldValue = getActualAmount(isLuckyGift, giftValue.getActualAmount(), luckyGiftRatio);
|
||||
|
||||
// 7. 只处理在房间内送出的礼物
|
||||
String originId = runningWater.getOriginId();
|
||||
if (originId == null || !originId.matches("\\d+")) {
|
||||
log.debug("【火箭能量增加】非房间场景,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
Long roomId = Long.parseLong(originId);
|
||||
|
||||
// 8. 获取送礼用户信息
|
||||
Long userId = runningWater.getUserId();
|
||||
|
||||
// 9. 构建命令并执行
|
||||
RocketEnergyAddCmd cmd = new RocketEnergyAddCmd();
|
||||
cmd.setRoomId(roomId);
|
||||
cmd.setUserId(userId);
|
||||
cmd.setGiftGoldValue(actualGoldValue);
|
||||
cmd.setIsLucky(isLuckyGift);
|
||||
|
||||
rocketEnergyAddCmdExe.execute(cmd);
|
||||
|
||||
log.info("【火箭能量增加】处理成功: roomId={}, userId={}, goldValue={}, isLucky={}",
|
||||
roomId, userId, actualGoldValue, isLuckyGift);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("【火箭能量增加】处理异常: runningWaterId={}", runningWater.getId(), e);
|
||||
} finally {
|
||||
giftGiveRunningWaterService.addLog(runningWater.getId(), "结束:火箭能量增加");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为幸运礼物
|
||||
*/
|
||||
private boolean isLuckyGift(GiftValue giftValue) {
|
||||
return giftValue.getGiftType() != null &&
|
||||
giftValue.getGiftType().contains(GiftTabEnum.LUCKY_GIFT.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算实际金额(考虑幸运礼物加成)
|
||||
*/
|
||||
private Long getActualAmount(boolean isLuckyGift, BigDecimal amount, BigDecimal luckyGiftRatio) {
|
||||
if (isLuckyGift && luckyGiftRatio != null && luckyGiftRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 幸运礼物按比例计算
|
||||
return amount.multiply(luckyGiftRatio)
|
||||
.setScale(0, RoundingMode.DOWN)
|
||||
.longValue();
|
||||
}
|
||||
return amount.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,265 @@
|
||||
package com.red.circle.other.app.manager;
|
||||
|
||||
import com.red.circle.other.app.convertor.RocketConvertor;
|
||||
import com.red.circle.other.app.dto.clientobject.RocketStatusCO;
|
||||
import com.red.circle.other.app.dto.cmd.RocketEnergyAddCmd;
|
||||
import com.red.circle.other.app.util.DistributedLockUtil;
|
||||
import com.red.circle.other.domain.gateway.RocketConfigGateway;
|
||||
import com.red.circle.other.domain.gateway.RocketStatusGateway;
|
||||
import com.red.circle.other.domain.rocket.RocketConfig;
|
||||
import com.red.circle.other.domain.rocket.RocketStatus;
|
||||
import com.red.circle.other.infra.database.cache.key.RocketKeys;
|
||||
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.other.infra.gateway.RocketStatusGatewayImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 火箭能量聚合器
|
||||
* 职责:
|
||||
* 1. 接收能量增加请求,缓存到内存
|
||||
* 2. 定时批量刷新到数据库
|
||||
* 3. 定时推送能量更新消息
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RocketEnergyAggregator {
|
||||
|
||||
private final RocketStatusGateway rocketStatusGateway;
|
||||
private final RocketStatusGatewayImpl rocketStatusGatewayImpl;
|
||||
private final DistributedLockUtil distributedLockUtil;
|
||||
private final RocketConfigGateway rocketConfigGateway;
|
||||
private final RocketImPushManager rocketImPushManager;
|
||||
private final RocketLaunchManager rocketLaunchManager;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
|
||||
/**
|
||||
* 房间能量聚合缓存
|
||||
* Key: roomId
|
||||
* Value: 该房间待聚合的能量列表
|
||||
*/
|
||||
private final Map<Long, List<RocketEnergyAddCmd>> energyBuffer = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 房间级别的细粒度锁
|
||||
* Key: roomId
|
||||
* Value: 该房间的锁
|
||||
*/
|
||||
private final Map<Long, ReentrantLock> roomLocks = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 推送间隔(毫秒)
|
||||
*/
|
||||
private static final long PUSH_INTERVAL = 3000L; // 3秒推送一次
|
||||
|
||||
/**
|
||||
* 批量刷新间隔(毫秒)
|
||||
*/
|
||||
private static final long FLUSH_INTERVAL = 5000L; // 5秒刷新一次
|
||||
|
||||
/**
|
||||
* 上次推送时间
|
||||
* Key: roomId
|
||||
* Value: 上次推送的时间戳
|
||||
*/
|
||||
private final Map<Long, Long> lastPushTime = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 接收能量增加请求(无锁,直接缓存)
|
||||
*/
|
||||
public void addEnergy(RocketEnergyAddCmd cmd) {
|
||||
energyBuffer.computeIfAbsent(cmd.getRoomId(), k -> new ArrayList<>()).add(cmd);
|
||||
|
||||
log.debug("缓存火箭能量: roomId={}, userId={}, energy={}, bufferSize={}",
|
||||
cmd.getRoomId(), cmd.getUserId(), cmd.getGiftGoldValue(),
|
||||
energyBuffer.get(cmd.getRoomId()).size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时批量刷新到数据库(每5秒)
|
||||
*/
|
||||
@Scheduled(fixedDelay = FLUSH_INTERVAL)
|
||||
public void flushToDatabase() {
|
||||
if (energyBuffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("开始批量刷新火箭能量, 待处理房间数={}", energyBuffer.size());
|
||||
|
||||
// 遍历所有房间
|
||||
energyBuffer.forEach((roomId, cmdList) -> {
|
||||
if (cmdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取该房间的细粒度锁
|
||||
ReentrantLock roomLock = roomLocks.computeIfAbsent(roomId, k -> new ReentrantLock());
|
||||
|
||||
if (roomLock.tryLock()) {
|
||||
try {
|
||||
// 取出当前批次的数据
|
||||
List<RocketEnergyAddCmd> batch = new ArrayList<>(cmdList);
|
||||
cmdList.clear();
|
||||
|
||||
// 处理这个房间的能量增加
|
||||
processRoomEnergy(roomId, batch);
|
||||
|
||||
} finally {
|
||||
roomLock.unlock();
|
||||
}
|
||||
} else {
|
||||
log.debug("房间{}正在处理中,跳过本次刷新", roomId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("刷新房间{}火箭能量失败", roomId, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个房间的能量增加
|
||||
*/
|
||||
private void processRoomEnergy(Long roomId, List<RocketEnergyAddCmd> cmdList) {
|
||||
if (cmdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String lockKey = RocketKeys.LOCK.getKey(roomId);
|
||||
|
||||
// 使用Redis分布式锁,避免多实例冲突
|
||||
distributedLockUtil.executeWithLock(lockKey, 10L, () -> {
|
||||
|
||||
// 1. 查询当前火箭状态(不包含贡献者列表,性能优化)
|
||||
String today = LocalDate.now().toString();
|
||||
RocketStatus rocketStatus = rocketStatusGatewayImpl
|
||||
.findByRoomIdAndDateWithoutContributors(roomId, today);
|
||||
|
||||
if (rocketStatus == null) {
|
||||
// 查询一级火箭配置
|
||||
RocketConfig level1Config = rocketConfigGateway.findByLevel(1);
|
||||
if (level1Config == null || level1Config.getStatus() != 1) {
|
||||
log.error("一级火箭配置不存在或未启用");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用配置初始化
|
||||
rocketStatus = RocketStatus.init(roomId, level1Config);
|
||||
log.info("初始化房间火箭, roomId={}, maxEnergy={}", roomId, level1Config.getMaxEnergy());
|
||||
}
|
||||
|
||||
// 2. 检查状态
|
||||
if (!rocketStatus.canAddEnergy()) {
|
||||
log.warn("火箭不可充能, roomId={}, status={}", roomId, rocketStatus.getStatus());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 批量增加能量
|
||||
long totalEnergy = 0L;
|
||||
for (RocketEnergyAddCmd cmd : cmdList) {
|
||||
rocketStatus.addEnergy(
|
||||
cmd.getUserId(),
|
||||
cmd.getGiftGoldValue()
|
||||
);
|
||||
totalEnergy += cmd.getGiftGoldValue();
|
||||
}
|
||||
|
||||
// 4. 保存状态(一次性保存)
|
||||
rocketStatusGateway.save(rocketStatus);
|
||||
|
||||
log.info("批量增加火箭能量: roomId={}, 批次数量={}, 总能量={}, 当前能量={}/{}",
|
||||
roomId, cmdList.size(), totalEnergy,
|
||||
rocketStatus.getCurrentEnergy(), rocketStatus.getMaxEnergy());
|
||||
|
||||
// 5. 推送更新消息(节流)
|
||||
String roomAccount = roomProfileManagerService.getRoomAccount(roomId);
|
||||
pushEnergyUpdate(roomId, roomAccount, rocketStatus);
|
||||
|
||||
// 6. 检查是否需要发射
|
||||
if (rocketStatus.isFullEnergy()) {
|
||||
log.info("火箭能量已满, 准备发射, roomId={}, level={}",
|
||||
roomId, rocketStatus.getLevel().getLevel());
|
||||
|
||||
// 异步触发发射
|
||||
rocketLaunchManager.launchAsync(roomId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, "火箭能量刷新操作中,请稍后");
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送能量更新(节流:3秒推送一次)
|
||||
*/
|
||||
private void pushEnergyUpdate(Long roomId, String roomAccount, RocketStatus rocketStatus) {
|
||||
long now = System.currentTimeMillis();
|
||||
Long lastPush = lastPushTime.get(roomId);
|
||||
|
||||
// 节流:距离上次推送不足3秒,跳过
|
||||
if (lastPush != null && (now - lastPush) < PUSH_INTERVAL) {
|
||||
log.debug("推送节流:距离上次推送{}ms,跳过本次推送", now - lastPush);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 查询火箭状态
|
||||
RocketStatus fullStatus = rocketStatusGatewayImpl
|
||||
.findByRoomIdAndDateWithoutContributors(roomId, rocketStatus.getDate());
|
||||
|
||||
if (fullStatus != null) {
|
||||
RocketStatusCO statusCO = RocketConvertor.toStatusCO(fullStatus);
|
||||
rocketImPushManager.pushEnergyUpdate(roomAccount, statusCO);
|
||||
|
||||
// 更新推送时间
|
||||
lastPushTime.put(roomId, now);
|
||||
|
||||
log.info("推送火箭能量更新: roomId={}, energy={}/{}",
|
||||
roomId, fullStatus.getCurrentEnergy(), fullStatus.getMaxEnergy());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("推送火箭能量更新失败: roomId={}", roomId, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时清理空闲房间的锁(每小时)
|
||||
*/
|
||||
@Scheduled(fixedRate = 3600000)
|
||||
public void cleanupIdleRooms() {
|
||||
// 清理buffer中的空列表
|
||||
energyBuffer.entrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
|
||||
// 清理不在buffer中的锁
|
||||
roomLocks.keySet().removeIf(roomId -> !energyBuffer.containsKey(roomId));
|
||||
|
||||
// 清理推送时间
|
||||
lastPushTime.keySet().removeIf(roomId -> !energyBuffer.containsKey(roomId));
|
||||
|
||||
log.info("清理空闲房间资源, 当前活跃房间数={}", energyBuffer.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用关闭时强制刷新
|
||||
*/
|
||||
@javax.annotation.PreDestroy
|
||||
public void shutdown() {
|
||||
log.info("应用关闭,强制刷新所有待处理能量");
|
||||
flushToDatabase();
|
||||
}
|
||||
}
|
||||
@ -31,45 +31,39 @@ public class RocketImPushManager {
|
||||
/**
|
||||
* 推送能量更新消息
|
||||
*/
|
||||
public void pushEnergyUpdate(Long roomId, RocketStatusCO status) {
|
||||
public void pushEnergyUpdate(String roomAccount, RocketStatusCO status) {
|
||||
try {
|
||||
// 构建消息
|
||||
ImMessage message = buildEnergyUpdateMessage(status);
|
||||
|
||||
imGroupClient.sendCustomMessage(String.valueOf(roomId),
|
||||
imGroupClient.sendCustomMessage(roomAccount,
|
||||
CustomGroupMsgBodyCmd.builder()
|
||||
.type(GroupMessageTypeEnum.GAME_LUCKY_GIFT)
|
||||
.data(message)
|
||||
.type(GroupMessageTypeEnum.ROCKET_ENERGY_UPDATE)
|
||||
.data(status)
|
||||
.build());
|
||||
|
||||
log.info("推送火箭能量更新消息成功: roomId={}, level={}, energy={}/{}",
|
||||
roomId, status.getLevel(), status.getCurrentEnergy(), status.getMaxEnergy());
|
||||
roomAccount, status.getLevel(), status.getCurrentEnergy(), status.getMaxEnergy());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("推送火箭能量更新消息失败: roomId={}", roomId, e);
|
||||
log.error("推送火箭能量更新消息失败: roomId={}", roomAccount, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送火箭发射消息
|
||||
*/
|
||||
public void pushRocketLaunch(Long roomId, RocketStatusCO status) {
|
||||
public void pushRocketLaunch(RocketStatusCO status) {
|
||||
try {
|
||||
// 构建消息
|
||||
ImMessage message = buildLaunchMessage(status);
|
||||
|
||||
imGroupClient.sendMessageBroadcast(
|
||||
BroadcastGroupMsgBodyCmd.builder()
|
||||
.toPlatform(SysOriginPlatformEnum.LIKEI)
|
||||
.type(GroupMessageTypeEnum.GAME_LUCKY_GIFT)
|
||||
.data(message)
|
||||
.type(GroupMessageTypeEnum.ROCKET_ENERGY_LAUNCH)
|
||||
.data(status)
|
||||
.build()
|
||||
);
|
||||
|
||||
log.info("推送火箭发射消息成功: roomId={}, level={}", roomId, status.getLevel());
|
||||
log.info("推送火箭发射消息成功: roomId={}, level={}", status.getRoomId(), status.getLevel());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("推送火箭发射消息失败: roomId={}", roomId, e);
|
||||
log.error("推送火箭发射消息失败: roomId={}", status.getRoomId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,8 +149,7 @@ public class RocketImPushManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* IM消息对象(示例)
|
||||
* TODO: 替换为实际的IM消息类
|
||||
* IM消息对象
|
||||
*/
|
||||
private static class ImMessage {
|
||||
private String type;
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package com.red.circle.other.app.manager;
|
||||
|
||||
import com.red.circle.other.app.command.rocket.RocketLaunchCmdExe;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 火箭发射管理器
|
||||
* 职责:异步处理火箭发射
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RocketLaunchManager {
|
||||
|
||||
private final RocketLaunchCmdExe rocketLaunchCmdExe;
|
||||
|
||||
/**
|
||||
* 异步发射火箭
|
||||
*/
|
||||
@Async("rocketTaskExecutor")
|
||||
public void launchAsync(Long roomId) {
|
||||
try {
|
||||
log.info("异步执行火箭发射: roomId={}", roomId);
|
||||
rocketLaunchCmdExe.execute(roomId);
|
||||
} catch (Exception e) {
|
||||
log.error("异步火箭发射失败: roomId={}", roomId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
package com.red.circle.other.app.scheduler;
|
||||
|
||||
import com.red.circle.other.domain.gateway.RocketStatusGateway;
|
||||
import com.red.circle.other.domain.rocket.RocketStatus;
|
||||
import com.red.circle.other.domain.rocket.RocketStatusEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 火箭领取过期检查任务
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RocketClaimExpireTask {
|
||||
|
||||
private final RocketStatusGateway rocketStatusGateway;
|
||||
|
||||
/**
|
||||
* 每分钟检查一次过期状态
|
||||
*/
|
||||
@Scheduled(fixedRate = 60000)
|
||||
public void checkExpire() {
|
||||
try {
|
||||
String today = LocalDate.now().toString();
|
||||
|
||||
// 查询所有处于充能状态的火箭 (包含CLAIMING状态)
|
||||
List<RocketStatus> allRockets = rocketStatusGateway.findAllCharging(today);
|
||||
|
||||
for (RocketStatus rocketStatus : allRockets) {
|
||||
// 检查是否过期
|
||||
if (rocketStatus.getStatus() == RocketStatusEnum.CLAIMING && rocketStatus.isExpired()) {
|
||||
log.info("火箭领取已过期, 开始重置, roomId={}, claimExpireTime={}",
|
||||
rocketStatus.getRoomId(), rocketStatus.getClaimExpireTime());
|
||||
|
||||
// TODO: 保存历史记录
|
||||
|
||||
// 重置火箭
|
||||
rocketStatus.reset();
|
||||
rocketStatusGateway.save(rocketStatus);
|
||||
|
||||
log.info("过期火箭重置成功, roomId={}", rocketStatus.getRoomId());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("火箭过期检查任务执行失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,6 +55,25 @@ public enum RocketLevel {
|
||||
throw new IllegalArgumentException("不支持的火箭等级: " + level);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一级火箭
|
||||
* @return 下一级火箭,如果已是最高级则返回null
|
||||
*/
|
||||
public RocketLevel getNextLevel() {
|
||||
return switch (this) {
|
||||
case LEVEL_1 -> LEVEL_2;
|
||||
case LEVEL_2 -> LEVEL_3;
|
||||
case LEVEL_3 -> null; // 三级是最高级,返回null表示需要重置
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是最高级
|
||||
*/
|
||||
public boolean isMaxLevel() {
|
||||
return this == LEVEL_3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据能量值判断等级
|
||||
*/
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.red.circle.other.domain.rocket;
|
||||
|
||||
import com.red.circle.other.domain.gateway.RocketConfigGateway;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
@ -17,6 +19,7 @@ import java.util.List;
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class RocketStatus {
|
||||
|
||||
/**
|
||||
@ -87,13 +90,13 @@ public class RocketStatus {
|
||||
/**
|
||||
* 初始化火箭
|
||||
*/
|
||||
public static RocketStatus init(Long roomId) {
|
||||
public static RocketStatus init(Long roomId, RocketConfig config) {
|
||||
RocketStatus rocketStatus = new RocketStatus();
|
||||
rocketStatus.setRoomId(roomId);
|
||||
rocketStatus.setDate(LocalDate.now().toString());
|
||||
rocketStatus.setLevel(RocketLevel.LEVEL_1);
|
||||
rocketStatus.setLevel(RocketLevel.of(config.getLevel()));
|
||||
rocketStatus.setCurrentEnergy(0L);
|
||||
rocketStatus.setMaxEnergy(RocketLevel.LEVEL_1.getMaxEnergy());
|
||||
rocketStatus.setMaxEnergy(config.getMaxEnergy());
|
||||
rocketStatus.setStatus(RocketStatusEnum.CHARGING);
|
||||
rocketStatus.setContributors(new ArrayList<>());
|
||||
rocketStatus.setClaimCount(0);
|
||||
@ -101,52 +104,100 @@ public class RocketStatus {
|
||||
rocketStatus.setUpdatedAt(LocalDateTime.now());
|
||||
return rocketStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加能量
|
||||
*/
|
||||
public void addEnergy(Long userId, String userName, String userAvatar, Long energyValue) {
|
||||
// 增加总能量
|
||||
public void addEnergy(Long userId,Long energyValue) {
|
||||
this.currentEnergy += energyValue;
|
||||
|
||||
// 更新贡献者
|
||||
RocketContributor contributor = findContributor(userId);
|
||||
if (contributor == null) {
|
||||
contributor = new RocketContributor();
|
||||
contributor.setUserId(userId);
|
||||
contributor.setUserName(userName);
|
||||
contributor.setUserAvatar(userAvatar);
|
||||
contributor.setEnergy(0L);
|
||||
contributor.setGiftCount(0);
|
||||
contributor.setFirstContributeTime(LocalDateTime.now());
|
||||
this.contributors.add(contributor);
|
||||
}
|
||||
contributor.addEnergy(energyValue);
|
||||
contributor.setLastContributeTime(LocalDateTime.now());
|
||||
|
||||
// 重新计算贡献率
|
||||
recalculateContributionRate();
|
||||
|
||||
// 检查等级提升
|
||||
checkAndUpgradeLevel();
|
||||
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// RocketStatus.java
|
||||
|
||||
/**
|
||||
* 发射火箭
|
||||
* 升级到下一级火箭(使用配置)
|
||||
*/
|
||||
public void launch() {
|
||||
private void upgradeToNextLevel(RocketConfigGateway configGateway) {
|
||||
RocketLevel nextLevel = this.level.getNextLevel();
|
||||
|
||||
if (nextLevel != null) {
|
||||
// 查询下一级配置
|
||||
RocketConfig nextConfig = configGateway.findByLevel(nextLevel.getLevel());
|
||||
if (nextConfig == null || nextConfig.getStatus() != 1) {
|
||||
log.warn("下一级火箭配置不存在或未启用, level={}", nextLevel.getLevel());
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算溢出能量(当前能量 - 当前等级最大能量)
|
||||
long overflowEnergy = this.currentEnergy - this.maxEnergy;
|
||||
if (overflowEnergy < 0) {
|
||||
overflowEnergy = 0L;
|
||||
}
|
||||
|
||||
// 升级到下一级
|
||||
this.level = nextLevel;
|
||||
this.maxEnergy = nextConfig.getMaxEnergy();
|
||||
this.currentEnergy = overflowEnergy; // 保留溢出能量
|
||||
this.status = RocketStatusEnum.CHARGING;
|
||||
|
||||
// 清空贡献者(新一轮)
|
||||
this.contributors = new ArrayList<>();
|
||||
this.launchTime = null;
|
||||
this.claimExpireTime = null;
|
||||
|
||||
log.info("火箭升级成功, 升至{}级, 溢出能量={}, 新目标={}",
|
||||
nextLevel.getLevel(), overflowEnergy, this.maxEnergy);
|
||||
} else {
|
||||
// 已经是最高级,重置为一级
|
||||
// reset(configGateway);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射火箭并自动升级(需要传入配置网关)
|
||||
*/
|
||||
public void launchAndUpgrade(RocketConfigGateway configGateway) {
|
||||
this.status = RocketStatusEnum.LAUNCHED;
|
||||
this.launchTime = LocalDateTime.now();
|
||||
// 领取过期时间:发射后1分钟
|
||||
this.claimExpireTime = LocalDateTime.now().plusMinutes(1);
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
|
||||
// 升级到下一级
|
||||
upgradeToNextLevel(configGateway);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始领取
|
||||
* 重置状态(使用一级配置)
|
||||
*/
|
||||
public void startClaiming() {
|
||||
this.status = RocketStatusEnum.CLAIMING;
|
||||
public void reset(RocketConfigGateway configGateway) {
|
||||
// 查询一级配置
|
||||
RocketConfig level1Config = configGateway.findByLevel(1);
|
||||
if (level1Config == null) {
|
||||
throw new RuntimeException("一级火箭配置不存在");
|
||||
}
|
||||
|
||||
this.date = LocalDate.now().toString();
|
||||
this.level = RocketLevel.LEVEL_1;
|
||||
this.currentEnergy = 0L;
|
||||
this.maxEnergy = level1Config.getMaxEnergy();
|
||||
this.status = RocketStatusEnum.CHARGING;
|
||||
this.contributors = new ArrayList<>();
|
||||
this.launchTime = null;
|
||||
this.claimExpireTime = null;
|
||||
this.claimCount = 0;
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@ -162,7 +213,7 @@ public class RocketStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置状态
|
||||
* 重置状态(三级发射后重置为一级)
|
||||
*/
|
||||
public void reset() {
|
||||
this.date = LocalDate.now().toString();
|
||||
@ -192,12 +243,10 @@ public class RocketStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以领取
|
||||
* 是否可以领取(已移除CLAIMING状态)
|
||||
*/
|
||||
public boolean canClaim() {
|
||||
return this.status == RocketStatusEnum.CLAIMING
|
||||
&& this.claimExpireTime != null
|
||||
&& LocalDateTime.now().isBefore(this.claimExpireTime);
|
||||
return false; // 暂时隐藏领取功能
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,18 +298,6 @@ public class RocketStatus {
|
||||
contributor.setContributionRate(rate);
|
||||
});
|
||||
|
||||
// 按贡献能量排序
|
||||
this.contributors.sort(Comparator.comparing(RocketContributor::getEnergy).reversed());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并升级等级
|
||||
*/
|
||||
private void checkAndUpgradeLevel() {
|
||||
RocketLevel newLevel = RocketLevel.determineLevel(this.currentEnergy);
|
||||
if (newLevel != this.level) {
|
||||
this.level = newLevel;
|
||||
this.maxEnergy = newLevel.getMaxEnergy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,14 +19,9 @@ public enum RocketStatusEnum {
|
||||
CHARGING(1, "充能中"),
|
||||
|
||||
/**
|
||||
* 已发射
|
||||
* 已发射(等待下一级)
|
||||
*/
|
||||
LAUNCHED(2, "已发射"),
|
||||
|
||||
/**
|
||||
* 领取中
|
||||
*/
|
||||
CLAIMING(3, "领取中");
|
||||
LAUNCHED(2, "已发射");
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
|
||||
@ -27,21 +27,21 @@ public interface RocketStatusRepository extends MongoRepository<RocketStatusDocu
|
||||
* 根据房间ID和日期查询(排除贡献者列表,性能优化)
|
||||
* 用于:仅需要火箭状态,不需要贡献者详情的场景
|
||||
*/
|
||||
@Query(value = "{ 'room_id': ?0, 'date': ?1 }", fields = "{ 'contributors': 0 }")
|
||||
@Query(value = "{ 'roomId': ?0, 'date': ?1 }", fields = "{ 'contributors': 0 }")
|
||||
Optional<RocketStatusDocument> findByRoomIdAndDateWithoutContributors(Long roomId, String date);
|
||||
|
||||
/**
|
||||
* 根据房间ID和日期查询(只包含Top N贡献者)
|
||||
* 用于:前端展示Top榜单的场景
|
||||
*/
|
||||
@Query(value = "{ 'room_id': ?0, 'date': ?1 }",
|
||||
@Query(value = "{ 'roomId': ?0, 'date': ?1 }",
|
||||
fields = "{ 'contributors': { $slice: ?2 } }")
|
||||
Optional<RocketStatusDocument> findByRoomIdAndDateWithTopContributors(Long roomId, String date, Integer topN);
|
||||
|
||||
/**
|
||||
* 根据房间ID列表和日期查询(排除贡献者列表)
|
||||
*/
|
||||
@Query(value = "{ 'room_id': { $in: ?0 }, 'date': ?1 }", fields = "{ 'contributors': 0 }")
|
||||
@Query(value = "{ 'roomId': { $in: ?0 }, 'date': ?1 }", fields = "{ 'contributors': 0 }")
|
||||
List<RocketStatusDocument> findByRoomIdInAndDateWithoutContributors(List<Long> roomIds, String date);
|
||||
|
||||
/**
|
||||
|
||||
@ -17,6 +17,8 @@ import java.util.Set;
|
||||
*/
|
||||
public interface RoomProfileManagerService {
|
||||
|
||||
String getRoomAccount(Long roomId);
|
||||
|
||||
/**
|
||||
* 用户是否存在房间.
|
||||
*/
|
||||
|
||||
@ -26,14 +26,18 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@ -43,9 +47,58 @@ import org.springframework.stereotype.Service;
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RoomProfileManagerServiceImpl implements RoomProfileManagerService {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
private final MongoTemplate mongoTemplate;
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
/**
|
||||
* Redis缓存Key前缀
|
||||
*/
|
||||
private static final String ROOM_ACCOUNT_CACHE_KEY = "rocket:room:account:";
|
||||
|
||||
/**
|
||||
* Redis缓存过期时间(1小时)
|
||||
*/
|
||||
private static final long ROOM_ACCOUNT_CACHE_EXPIRE = 3600L;
|
||||
|
||||
/**
|
||||
* 获取房间Account(带Redis缓存)
|
||||
*
|
||||
* @param roomId 房间ID
|
||||
* @return 房间Account
|
||||
*/
|
||||
@Override
|
||||
public String getRoomAccount(Long roomId) {
|
||||
if (roomId == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 1. 先查Redis缓存
|
||||
String cacheKey = ROOM_ACCOUNT_CACHE_KEY + roomId;
|
||||
String account = redisTemplate.opsForValue().get(cacheKey);
|
||||
if (account != null) {
|
||||
return account;
|
||||
}
|
||||
|
||||
// 2. 查MongoDB
|
||||
try {
|
||||
RoomProfile roomProfile = getById(roomId);
|
||||
if (roomProfile != null && roomProfile.getRoomAccount() != null) {
|
||||
account = roomProfile.getRoomAccount();
|
||||
|
||||
// 写入Redis缓存
|
||||
redisTemplate.opsForValue().set(cacheKey, account, ROOM_ACCOUNT_CACHE_EXPIRE, TimeUnit.SECONDS);
|
||||
|
||||
return account;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("查询房间Account失败: roomId={}", roomId, e);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsUserRoom(Long userId) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user