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 09affbef..f351ef80 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 @@ -53,6 +53,7 @@ public class RocketRewardClaimCmdExe { private final WalletGoldClient walletGoldClient; private final PropsBackpackClient propsBackpackClient; private final BadgeBackpackClient badgeBackpackClient; + private final RocketHistoryGateway rocketHistoryGateway; private final Random random = new Random(); @@ -63,7 +64,7 @@ public class RocketRewardClaimCmdExe { Long userId = cmd.getReqUserId(); String lockKey = RocketKeys.LOCK.getKey(cmd.getRoomId(), "claim", userId); - return distributedLockUtil.executeWithLock(lockKey, 5L, () -> doClaim(cmd), "奖励领取操作过于频繁,请稍后重试"); + return distributedLockUtil.executeWithLock(lockKey, 5L, () -> doClaim(cmd), "Reward collection operations are too frequent. Please try again later"); } /** @@ -73,59 +74,52 @@ public class RocketRewardClaimCmdExe { Long userId = cmd.getReqUserId(); Long roomId = cmd.getRoomId(); String today = LocalDate.now().toString(); - - // 1. 查询火箭状态 - RocketStatus rocketStatus = rocketStatusGateway.findByRoomIdAndDate(roomId, today); - if (rocketStatus == null) { - throw new RuntimeException("火箭状态不存在"); + + // 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"); } - Integer rocketLevel = rocketStatus.getLevel().getLevel(); + Integer rocketLevel = latestHistory.getLevel(); - // 2. 校验是否已发射 - if (rocketStatus.getStatus() != RocketStatusEnum.LAUNCHED) { - throw new RuntimeException("火箭尚未发射,无法领取奖励"); - } - - // 3. 检查是否已领取(每天每个房间每个等级只能领一次) - boolean hasClaimed = rocketRewardLogGateway.existsByRoomAndUserAndDateAndLevel(roomId, userId, today, rocketLevel); + // 2. 检查是否已领取(每天每个房间每个等级只能领一次) + boolean hasClaimed = rocketRewardLogGateway.existsByRocketAndUser(latestHistory.getId(), userId); if (hasClaimed) { - throw new RuntimeException("今日该等级火箭已领取,请等待下一级或明天再来"); + throw new RuntimeException("This class of rocket has been collected today. Please wait for the next stage or come back tomorrow."); } - // 4. 查询火箭配置 + // 3. 查询火箭配置 RocketConfig config = rocketConfigGateway.findByLevel(rocketLevel); if (config == null) { throw new RuntimeException("火箭配置不存在"); } - // 5. 检查奖励数量是否已达上限 - Integer rewardQuantity = config.getRewardQuantity() != null ? config.getRewardQuantity() : 20; - if (rocketStatus.getClaimCount() >= rewardQuantity) { - throw new RuntimeException("奖励已被领完,请下次早点来"); + // 4. MongoDB原子递增检查 + 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"); } - // 6. 解析奖励配置 + // 5. 解析奖励配置 List rewardList = parseRewardConfig(config.getRewardConfig().toJSONString()); if (rewardList == null || rewardList.isEmpty()) { throw new RuntimeException("奖励配置为空"); } - // 7. 随机选择一个奖励 + // 6. 随机选择一个奖励 PropsActivityRewardConfigInfoDTO selectedReward = rewardList.get(random.nextInt(rewardList.size())); - // 8. 发放奖励 + // 7. 发放奖励 String walletBizNo = RocketRewardLog.buildBizNo(roomId.toString(), userId); grantReward(userId, selectedReward, walletBizNo); - // 9. 保存领取记录 - saveRewardLog(roomId, userId, today, rocketLevel, selectedReward, walletBizNo); + // 8. 保存领取记录 + saveRewardLog(roomId, userId, latestHistory, selectedReward, walletBizNo); - // 10. 更新领取人数 - rocketStatus.incrementClaimCount(); - rocketStatusGateway.save(rocketStatus); - // 11. 推送领取成功消息 + // 9. 推送领取成功消息 try { String userName = "用户" + userId; Long rewardAmount = getRewardAmount(selectedReward); @@ -134,7 +128,7 @@ public class RocketRewardClaimCmdExe { log.error("推送奖励领取消息失败: roomId={}, userId={}", roomId, userId, e); } - // 12. 构建返回结果 + // 10. 构建返回结果 RocketRewardCO rewardCO = new RocketRewardCO(); rewardCO.setRewardType(selectedReward.getType()); rewardCO.setRewardItemId(selectedReward.getId()); @@ -218,7 +212,6 @@ public class RocketRewardClaimCmdExe { .build() ); - log.info("金币发放成功: userId={}, amount={}", userId, goldAmount); } catch (Exception e) { log.error("金币发放失败: userId={}, amount={}", userId, goldAmount, e); throw new RuntimeException("金币发放失败"); @@ -243,8 +236,8 @@ public class RocketRewardClaimCmdExe { .setOpUser(userId); propsBackpackClient.giveProps(cmd); - log.info("道具发放成功(TODO): userId={}, propId={}, quantity={}, type={}", - userId, propId, quantity, detailType); + // 发送通知 + propsBackpackClient.givePropsNotify(userId, detailType, quantity, propId); } catch (Exception e) { log.error("道具发放失败: userId={}, propId={}", userId, propId, e); throw new RuntimeException("道具发放失败"); @@ -270,7 +263,8 @@ public class RocketRewardClaimCmdExe { .setBadgeType(SysBadgeConfigTypeEnum.ACTIVITY) ); - log.info("徽章发放成功(TODO): userId={}, badgeId={}", userId, badgeId); + // 发送通知 + propsBackpackClient.givePropsNotify(userId, SysBadgeConfigTypeEnum.ACTIVITY.name(), quantity, badgeId); } catch (Exception e) { log.error("徽章发放失败: userId={}, badgeId={}", userId, badgeId, e); throw new RuntimeException("徽章发放失败"); @@ -280,12 +274,13 @@ public class RocketRewardClaimCmdExe { /** * 保存领取记录 */ - private void saveRewardLog(Long roomId, Long userId, String date, Integer level, + private void saveRewardLog(Long roomId, Long userId, RocketHistory rocketHistory, PropsActivityRewardConfigInfoDTO reward, String walletBizNo) { RocketRewardLog log = new RocketRewardLog(); + log.setRocketHistoryId(rocketHistory.getId()); log.setRoomId(roomId); log.setUserId(userId); - log.setRocketLevel(level); + log.setRocketLevel(rocketHistory.getLevel()); log.setIsContributor(0); log.setContributionEnergy(0L); log.setContributionRate(null); diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java index 53af87b8..21996fbd 100644 --- a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/RocketHistoryGateway.java @@ -18,6 +18,22 @@ public interface RocketHistoryGateway { */ String save(RocketHistory history); + /** + * 查询今天最新的火箭发射记录(按发射时间倒序) + */ + RocketHistory findLatestByRoomIdAndDate(Long roomId, String date); + + /** + * 尝试原子递增领取人数(带上限检查) + * @return 是否成功递增 + */ + boolean tryIncrementClaimCount(String historyId, int maxCount); + + /** + * 增加领取人数 + */ + void incrementClaimCount(String historyId); + /** * 根据ID查询 */ diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java index e6f6bb54..6797c75a 100644 --- a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/rocket/RocketHistory.java @@ -26,6 +26,10 @@ public class RocketHistory { private Integer totalContributors; private RewardPool rewardPool; private ClaimStats claimStats; + /** + * 已领取人数 + */ + private Integer claimedCount; private LocalDateTime launchTime; private LocalDateTime claimStartTime; private LocalDateTime claimEndTime; diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java index 02a98b31..10a388ca 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/RocketConfigConvertor.java @@ -54,7 +54,7 @@ public class RocketConfigConvertor { } else { config.setNormalRewardConfig(new JSONArray()); } - + config.setRewardQuantity(entity.getRewardQuantity()); config.setStatus(entity.getStatus()); // config.setCreatedAt(entity.getCreatedAt()); // config.setUpdatedAt(entity.getUpdatedAt()); @@ -91,7 +91,8 @@ public class RocketConfigConvertor { } else { entity.setNormalRewardConfig(null); } - + + entity.setRewardQuantity(config.getRewardQuantity()); entity.setStatus(config.getStatus()); // createdAt 和 updatedAt 由数据库自动管理 diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java index 98e658ce..8e87d511 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/repository/RocketHistoryRepository.java @@ -2,6 +2,7 @@ package com.red.circle.other.infra.database.mongo.repository; import com.red.circle.other.infra.database.mongo.document.RocketHistoryDocument; import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; @@ -21,6 +22,11 @@ public interface RocketHistoryRepository extends MongoRepository findByRoomIdOrderByLaunchTimeDesc(Long roomId); + /** + * 查询今天最新的火箭发射记录(按发射时间倒序) + */ + RocketHistoryDocument findFirstByRoomIdAndDateOrderByLaunchTimeDesc(Long roomId, String date); + /** * 根据房间ID和日期查询 */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java index c355e4a1..77db3c82 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/RocketHistoryGatewayImpl.java @@ -1,5 +1,6 @@ package com.red.circle.other.infra.gateway; +import com.mongodb.client.result.UpdateResult; import com.red.circle.other.domain.gateway.RocketHistoryGateway; import com.red.circle.other.domain.rocket.RocketHistory; import com.red.circle.other.infra.convertor.RocketHistoryConvertor; @@ -41,6 +42,40 @@ public class RocketHistoryGatewayImpl implements RocketHistoryGateway { return saved.getId(); } + @Override + public RocketHistory findLatestByRoomIdAndDate(Long roomId, String date) { + RocketHistoryDocument document = rocketHistoryRepository.findFirstByRoomIdAndDateOrderByLaunchTimeDesc(roomId, date); + if (document == null) { + return null; + } + return rocketHistoryConvertor.toDomain(document); + } + + @Override + public boolean tryIncrementClaimCount(String historyId, int maxCount) { + Query query = new Query(); + query.addCriteria(Criteria.where("_id").is(historyId)); + query.addCriteria(Criteria.where("claimedCount").lt(maxCount)); // 小于上限才更新 + + Update update = new Update(); + update.inc("claimedCount", 1); + update.set("updatedAt", LocalDateTime.now()); + + UpdateResult result = mongoTemplate.updateFirst(query, update, RocketHistoryDocument.class); + + return result.getModifiedCount() > 0; // 更新成功返回true + } + + @Override + public void incrementClaimCount(String historyId) { + Query query = new Query(Criteria.where("_id").is(historyId)); + Update update = new Update(); + update.inc("claimedCount", 1); + update.set("updatedAt", LocalDateTime.now()); + + mongoTemplate.updateFirst(query, update, RocketHistoryDocument.class); + } + @Override public RocketHistory findById(String id) { return rocketHistoryRepository.findById(id) diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigDAO.xml b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigDAO.xml index 3deaa508..9fafe8e0 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigDAO.xml +++ b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/RocketConfigDAO.xml @@ -15,7 +15,7 @@ - id, level, max_energy, reward_config, contributor_reward_rate, + id, level, max_energy, reward_config,reward_quantity, contributor_reward_rate, normal_reward_config, status, created_at, updated_at