diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryRecordQryExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryRecordQryExe.java index 6480a605..63f6b635 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryRecordQryExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/activity/LotteryRecordQryExe.java @@ -124,6 +124,7 @@ public class LotteryRecordQryExe { prizeCO.setPrizeValue(prize.getPrizeValue()); prizeCO.setPrizeImage(prize.getPrizeImage()); prizeCO.setPrizeLevel(prize.getPrizeLevel()); + prizeCO.setDays(prize.getDays()); co.setPrize(prizeCO); } } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/CleanExpiredDismissingCpTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CleanExpiredDismissingCpTask.java similarity index 98% rename from rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/CleanExpiredDismissingCpTask.java rename to rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CleanExpiredDismissingCpTask.java index eba57da3..136623dd 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/CleanExpiredDismissingCpTask.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CleanExpiredDismissingCpTask.java @@ -1,4 +1,4 @@ -package com.red.circle.other.app.scheduler; +package com.red.circle.other.app.scheduler.cp; import com.google.common.collect.Sets; import com.red.circle.other.domain.gateway.user.UserProfileGateway; diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CpRankingBadgeTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CpRankingBadgeTask.java new file mode 100644 index 00000000..27e6147c --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CpRankingBadgeTask.java @@ -0,0 +1,171 @@ +package com.red.circle.other.app.scheduler.cp; + +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.component.redis.annotation.TaskCacheLock; +import com.red.circle.mq.business.model.event.BadgeOperateEvent; +import com.red.circle.mq.rocket.business.producer.UserMqMessageService; +import com.red.circle.other.infra.database.mongo.entity.ranking.RankingRewardSnapshot; +import com.red.circle.other.infra.database.mongo.entity.user.count.WeekCpValueCount; +import com.red.circle.other.infra.database.mongo.service.user.count.WeekCpValueCountService; +import com.red.circle.other.infra.database.rds.entity.badge.BadgeBackpack; +import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService; +import com.red.circle.other.inner.enums.material.BadgeBackpackExpireType; +import com.red.circle.other.inner.enums.material.BadgeKeyEnum; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.tool.core.date.TimestampUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CpRankingBadgeTask { + + private final WeekCpValueCountService weekCpValueCountService; + private final BadgeBackpackService badgeBackpackService; + private final UserMqMessageService userMqMessageService; + + // CP排名徽章ID数组:[第一名, 第二名, 第三名] + private final static Long[] CP_BADGE_IDS = new Long[]{ + 2012070776778235906L, // 第一名 + 2012070846403682305L, // 第二名 + 2012070905480708098L // 第三名 + }; + + /** + * 每30秒发放和收回CP排名徽章 + * 支持用户同时在多对CP中,持有多个排名徽章 + */ + @Scheduled(cron = "0/20 * * * * *", zone = "Asia/Riyadh") + @TaskCacheLock(key = "CP_RANKING_BADGE_TASK", expireSecond = 20) + public void sendWeekRankingReward() { + try { + // 1. 获取本周前三名CP对 + List thisWeekTop3 = weekCpValueCountService.listThisWeekTop(SysOriginPlatformEnum.LIKEI.name(), 3); + if (CollectionUtils.isEmpty(thisWeekTop3)) { + log.warn("CP排名徽章任务:本周前三名数据为空"); + return; + } + + // 2. 查询所有持有CP排名徽章的用户 + List allBackpackList = badgeBackpackService.query() + .in(BadgeBackpack::getBadgeId, Arrays.asList(CP_BADGE_IDS)) + .list() + .stream() + .filter(CpRankingBadgeTask::filterAvailable) + .collect(Collectors.toList()); + + // 3. 构建"用户应得徽章集合":Map> + // 一个用户可能在多对CP中,所以可以有多个徽章 + Map> userShouldHaveBadgesMap = new HashMap<>(); + + for (int rank = 0; rank < thisWeekTop3.size() && rank < 3; rank++) { + WeekCpValueCount rankingData = thisWeekTop3.get(rank); + Long badgeId = CP_BADGE_IDS[rank]; + + // 解析该CP对中的两个用户 + ImmutablePair userPair = weekCpValueCountService.parseUserIdPair(rankingData.getId()); + Long userOne = userPair.getLeft(); + Long userTwo = userPair.getRight(); + + // 给两个用户都添加这个徽章到应得集合 + userShouldHaveBadgesMap.computeIfAbsent(userOne, k -> new HashSet<>()).add(badgeId); + userShouldHaveBadgesMap.computeIfAbsent(userTwo, k -> new HashSet<>()).add(badgeId); + } + + // 4. 构建"用户当前持有徽章集合":Map> + Map> userCurrentBadgesMap = new HashMap<>(); + for (BadgeBackpack backpack : allBackpackList) { + userCurrentBadgesMap.computeIfAbsent(backpack.getUserId(), k -> new HashSet<>()) + .add(backpack.getBadgeId()); + } + + // 5. 发放缺失的徽章 + for (Map.Entry> entry : userShouldHaveBadgesMap.entrySet()) { + Long userId = entry.getKey(); + Set shouldHaveBadges = entry.getValue(); + Set currentBadges = userCurrentBadgesMap.getOrDefault(userId, Collections.emptySet()); + + // 找出缺失的徽章(应得但当前没有的) + for (Long badgeId : shouldHaveBadges) { + if (!currentBadges.contains(badgeId)) { + grantBadge(userId, badgeId); + int rank = Arrays.asList(CP_BADGE_IDS).indexOf(badgeId) + 1; + log.info("CP排名徽章发放:rank={}, userId={}, badgeId={}", rank, userId, badgeId); + } + } + } + + // 6. 收回多余的徽章 + for (Map.Entry> entry : userCurrentBadgesMap.entrySet()) { + Long userId = entry.getKey(); + Set currentBadges = entry.getValue(); + Set shouldHaveBadges = userShouldHaveBadgesMap.getOrDefault(userId, Collections.emptySet()); + + // 找出多余的徽章(当前有但不应得的) + for (Long badgeId : currentBadges) { + if (!shouldHaveBadges.contains(badgeId)) { + revokeBadge(userId, badgeId); + int rank = Arrays.asList(CP_BADGE_IDS).indexOf(badgeId) + 1; + log.info("CP排名徽章收回:rank={}, userId={}, badgeId={} (用户不在该排名CP中)", + rank, userId, badgeId); + } + } + } + + log.info("CP排名徽章任务执行完成:处理{}对CP,涉及{}个用户(可能重复)", + thisWeekTop3.size(), userShouldHaveBadgesMap.size()); + + } catch (Exception e) { + log.error("CP排名徽章任务执行异常", e); + } + } + + /** + * 检查用户是否已持有该徽章 + */ + private boolean hasUserBadge(List backpackList, Long userId, Long badgeId) { + return backpackList.stream() + .anyMatch(b -> Objects.equals(b.getUserId(), userId) + && Objects.equals(b.getBadgeId(), badgeId)); + } + + private static boolean filterAvailable(BadgeBackpack badgeBackpack) { + return Objects.equals(badgeBackpack.getExpireType(), + BadgeBackpackExpireType.PERMANENT.name()) + || badgeBackpack.getExpireTime().after(TimestampUtils.now()); + } + + /** + * 发放徽章 + */ + private void grantBadge(Long userId, Long badgeId) { + BadgeOperateEvent event = new BadgeOperateEvent() + .setUserId(userId) + .setBadgeId(badgeId) + .setDays(1L) + .setOperationType(BadgeOperateEvent.OperationType.WEAR.name()); + + userMqMessageService.sendBadgeOperateEvent(event); + } + + /** + * 收回徽章 + */ + private void revokeBadge(Long userId, Long badgeId) { + BadgeOperateEvent event = new BadgeOperateEvent() + .setUserId(userId) + .setBadgeId(badgeId) + .setDays(1L) + .setOperationType(BadgeOperateEvent.OperationType.REMOVE.name()); + + userMqMessageService.sendBadgeOperateEvent(event); + } + +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/CpRankingRewardTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CpRankingRewardTask.java similarity index 97% rename from rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/CpRankingRewardTask.java rename to rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CpRankingRewardTask.java index 4e061612..2a44808d 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/CpRankingRewardTask.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/cp/CpRankingRewardTask.java @@ -1,10 +1,9 @@ -package com.red.circle.other.app.scheduler; +package com.red.circle.other.app.scheduler.cp; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.component.redis.annotation.TaskCacheLock; import com.red.circle.other.app.manager.activity.award.CpRankingRewardManager; import com.red.circle.other.infra.utils.SeasonUtils; -import com.red.circle.tool.core.date.LocalDateTimeUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; diff --git a/rc-service/rc-service-other/other-start/src/test/java/DailTaskTest.java b/rc-service/rc-service-other/other-start/src/test/java/DailTaskTest.java index 77ea6ef9..b6a09597 100644 --- a/rc-service/rc-service-other/other-start/src/test/java/DailTaskTest.java +++ b/rc-service/rc-service-other/other-start/src/test/java/DailTaskTest.java @@ -1,12 +1,9 @@ import com.red.circle.OtherServiceApplication; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.common.business.enums.SendPropsOrigin; -import com.red.circle.other.app.scheduler.CleanExpiredDismissingCpTask; -import com.red.circle.other.app.scheduler.DailyTask; import com.red.circle.other.app.scheduler.RankingActivityRewardTask; import com.red.circle.other.app.scheduler.RocketStatusSyncTask; -import com.red.circle.other.app.scheduler.activity.GameKingRewardTask; -import com.red.circle.other.app.service.room.RocketStatusCacheService; +import com.red.circle.other.app.scheduler.cp.CleanExpiredDismissingCpTask; import com.red.circle.other.inner.endpoint.activity.PropsActivityClient; import com.red.circle.other.inner.model.cmd.activity.SendActivityRewardCmd; import org.junit.Test;