游戏王活动
This commit is contained in:
parent
5cfa816752
commit
2c28ec5135
@ -8,8 +8,6 @@ import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 徽章操作事件消息.
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@ -29,6 +27,16 @@ public class BadgeOperateEvent implements Serializable {
|
||||
*/
|
||||
private String badgeKey;
|
||||
|
||||
/**
|
||||
* 徽章ID.
|
||||
*/
|
||||
private Long badgeId;
|
||||
|
||||
/**
|
||||
* 天数.
|
||||
*/
|
||||
private Long days;
|
||||
|
||||
/**
|
||||
* 操作类型: WEAR-佩戴, REMOVE-移除.
|
||||
*/
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
package com.red.circle.mq.business.model.event;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 游戏王奖励事件.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class GameKingRewardEvent {
|
||||
|
||||
/**
|
||||
* 活动类型.
|
||||
*/
|
||||
private String activityType;
|
||||
|
||||
/**
|
||||
* 来源系统.
|
||||
*/
|
||||
private String origin;
|
||||
|
||||
/**
|
||||
* 周期Key.
|
||||
*/
|
||||
private String cycleKey;
|
||||
|
||||
/**
|
||||
* 模板ID.
|
||||
*/
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* 排名数据.
|
||||
*/
|
||||
private List<RankingUser> rankingUsers;
|
||||
|
||||
/**
|
||||
* 排名用户信息.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class RankingUser {
|
||||
/**
|
||||
* 用户ID.
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 排名.
|
||||
*/
|
||||
private Integer rank;
|
||||
|
||||
/**
|
||||
* 分数.
|
||||
*/
|
||||
private Long score;
|
||||
|
||||
}
|
||||
}
|
||||
@ -152,6 +152,11 @@ public interface QueueNameConstant {
|
||||
*/
|
||||
String USER_HEARTBEAT = "USER_HEARTBEAT";
|
||||
|
||||
/**
|
||||
* 游戏王奖励.
|
||||
*/
|
||||
String GAME_KING_REWARD = "GAME_KING_REWARD";
|
||||
|
||||
// =================================================后台管理=============================================================
|
||||
/**
|
||||
* 后台管理审批.
|
||||
|
||||
@ -5,6 +5,7 @@ import com.red.circle.component.mq.service.FixedTopic;
|
||||
import com.red.circle.component.mq.service.SenderService;
|
||||
import com.red.circle.mq.business.model.event.BadgeEvent;
|
||||
import com.red.circle.mq.business.model.event.BadgeOperateEvent;
|
||||
import com.red.circle.mq.business.model.event.GameKingRewardEvent;
|
||||
import com.red.circle.mq.business.model.event.user.AccountBeautifulNumberEvent;
|
||||
import com.red.circle.mq.business.model.event.user.CpApplyEvent;
|
||||
import com.red.circle.mq.business.model.event.user.LoginLoggerEvent;
|
||||
@ -15,6 +16,7 @@ import com.red.circle.mq.rocket.business.streams.AccountBeautifulNumberSink;
|
||||
import com.red.circle.mq.rocket.business.streams.BadgeCheckSink;
|
||||
import com.red.circle.mq.rocket.business.streams.BadgeOperateSink;
|
||||
import com.red.circle.mq.rocket.business.streams.CpApplySink;
|
||||
import com.red.circle.mq.rocket.business.streams.GameKingRewardSink;
|
||||
import com.red.circle.mq.rocket.business.streams.LoginRegisterLoggerSink;
|
||||
import com.red.circle.mq.rocket.business.streams.OnlineStatusUploadSink;
|
||||
import com.red.circle.mq.rocket.business.streams.UserCardApplySink;
|
||||
@ -26,8 +28,6 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 用户相关队列消息.
|
||||
*
|
||||
* @author pengliang on 2023/5/22
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@ -126,11 +126,7 @@ public class UserMqMessageService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送徽章操作事件.
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param badgeKey 徽章Key
|
||||
* @param operationType 操作类型: WEAR-佩戴, REMOVE-移除
|
||||
* 发送徽章操作事件 (旧版本兼容 - 通过badgeKey).
|
||||
*/
|
||||
public void sendBadgeOperateEvent(Long userId, String badgeKey, String operationType) {
|
||||
senderService.sendEventMessage(MessageEvent.builder()
|
||||
@ -145,4 +141,28 @@ public class UserMqMessageService {
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送徽章操作事件 (新版本 - 传完整事件对象).
|
||||
*/
|
||||
public void sendBadgeOperateEvent(BadgeOperateEvent event) {
|
||||
senderService.sendEventMessage(MessageEvent.builder()
|
||||
.consumeId(IdWorkerUtils.getIdStr())
|
||||
.topic(FixedTopic.RC_DEFAULT_APP_ORDINARY)
|
||||
.tag(BadgeOperateSink.TAG)
|
||||
.body(event)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送游戏王奖励事件.
|
||||
*/
|
||||
public void sendGameKingRewardEvent(GameKingRewardEvent event) {
|
||||
senderService.sendEventMessage(MessageEvent.builder()
|
||||
.consumeId(IdWorkerUtils.getIdStr())
|
||||
.topic(FixedTopic.RC_DEFAULT_APP_ORDINARY)
|
||||
.tag(GameKingRewardSink.TAG)
|
||||
.body(event)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package com.red.circle.mq.rocket.business.streams;
|
||||
|
||||
import com.red.circle.mq.rocket.business.constant.QueueNameConstant;
|
||||
|
||||
/**
|
||||
* 游戏王奖励.
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
public interface GameKingRewardSink {
|
||||
|
||||
String INPUT = QueueNameConstant.GAME_KING_REWARD;
|
||||
String TAG = "game_king_reward_v1";
|
||||
|
||||
}
|
||||
@ -3,18 +3,22 @@ package com.red.circle.other.adapter.app.activity;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.HistoricalTop1ListCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.RankingListCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.RankingUserVO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.UserRankInfoCO;
|
||||
import com.red.circle.other.app.dto.cmd.UserRankQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.HistoricalTop1QueryCmd;
|
||||
import com.red.circle.other.app.service.activity.RankingActivityService;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingListQueryCmd;
|
||||
import com.red.circle.other.domain.ranking.RankingActivityType;
|
||||
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 排行榜活动控制器
|
||||
*/
|
||||
@ -67,4 +71,22 @@ public class RankingActivityRestController extends BaseController {
|
||||
public HistoricalTop1ListCO getHistoricalTop1List(@RequestBody @Validated HistoricalTop1QueryCmd cmd) {
|
||||
return rankingActivityService.getHistoricalTop1List(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询排行榜前四名(带缓存和奖励发送)
|
||||
*
|
||||
* 用途:获取指定活动的前四名排行榜,数据缓存五分钟,查询DB时发送游戏王奖励消息
|
||||
*
|
||||
* @param cmd 查询命令
|
||||
* @return 排行榜前四名
|
||||
*/
|
||||
@PostMapping("/top-four-with-reward")
|
||||
public List<RankingUserVO> getTopFourRankingWithReward(@RequestBody RankingListQueryCmd cmd) {
|
||||
if (cmd.getCycleType() == null) {
|
||||
cmd.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
|
||||
}
|
||||
cmd.setActivityType(RankingActivityType.KING_GAMES.getCode());
|
||||
cmd.setTemplateId("2001207026499137537");
|
||||
return rankingActivityService.getTopFourRankingWithReward(cmd);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,295 @@
|
||||
package com.red.circle.other.app.command.activity;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.red.circle.common.business.enums.SendPropsOrigin;
|
||||
import com.red.circle.mq.business.model.event.BadgeOperateEvent;
|
||||
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
|
||||
import com.red.circle.other.app.dto.clientobject.sys.ActivityConfigVO;
|
||||
import com.red.circle.other.infra.database.mongo.entity.ranking.RankingRewardSnapshot;
|
||||
import com.red.circle.other.infra.database.mongo.service.ranking.RankingRewardSnapshotService;
|
||||
import com.red.circle.other.infra.database.rds.entity.badge.BadgeConfig;
|
||||
import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService;
|
||||
import com.red.circle.other.inner.endpoint.material.props.PropsBackpackClient;
|
||||
import com.red.circle.other.inner.enums.material.PropsCommodityType;
|
||||
import com.red.circle.other.inner.model.cmd.material.DeletePropsCmd;
|
||||
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
|
||||
import com.red.circle.other.inner.model.dto.material.PropsActivityRewardConfigInfoDTO;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 排名奖励发放执行器.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RankingRewardGrantCmdExe {
|
||||
|
||||
private final RankingRewardSnapshotService snapshotService;
|
||||
private final PropsBackpackClient propsBackpackClient;
|
||||
private final UserMqMessageService userMqMessageService;
|
||||
private final BadgeConfigService badgeConfigService;
|
||||
|
||||
/**
|
||||
* 处理排名奖励变化.
|
||||
*/
|
||||
public void execute(String activityType, String origin, String cycleKey,
|
||||
String templateId, List<RankingUserData> currentRankingUsers,
|
||||
ActivityConfigVO activityConfig) {
|
||||
|
||||
RankingRewardSnapshot lastSnapshot = snapshotService.getLatestSnapshot(activityType, origin, cycleKey);
|
||||
|
||||
Map<Integer, RankingUserData> currentRankMap = currentRankingUsers.stream()
|
||||
.collect(Collectors.toMap(RankingUserData::getRank, u -> u));
|
||||
|
||||
Map<Long, RankingRewardSnapshot.RankingUserSnapshot> lastUserMap = new HashMap<>();
|
||||
if (lastSnapshot != null && CollectionUtils.isNotEmpty(lastSnapshot.getRankingUsers())) {
|
||||
lastUserMap = lastSnapshot.getRankingUsers().stream()
|
||||
.collect(Collectors.toMap(RankingRewardSnapshot.RankingUserSnapshot::getUserId, u -> u));
|
||||
}
|
||||
|
||||
processRewardChanges(currentRankMap, lastUserMap, activityConfig);
|
||||
|
||||
saveNewSnapshot(activityType, origin, cycleKey, templateId, currentRankingUsers, activityConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理奖励变化.
|
||||
*/
|
||||
private void processRewardChanges(Map<Integer, RankingUserData> currentRankMap,
|
||||
Map<Long, RankingRewardSnapshot.RankingUserSnapshot> lastUserMap,
|
||||
ActivityConfigVO activityConfig) {
|
||||
|
||||
Set<Long> currentUserIds = currentRankMap.values().stream()
|
||||
.map(RankingUserData::getUserId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (RankingRewardSnapshot.RankingUserSnapshot lastUser : lastUserMap.values()) {
|
||||
if (!currentUserIds.contains(lastUser.getUserId())) {
|
||||
log.info("用户失去排名,收回奖励, userId: {}, rank: {}", lastUser.getUserId(), lastUser.getRank());
|
||||
revokeRewards(lastUser.getUserId(), lastUser.getRewards());
|
||||
}
|
||||
}
|
||||
|
||||
for (RankingUserData currentUser : currentRankMap.values()) {
|
||||
Long userId = currentUser.getUserId();
|
||||
Integer currentRank = currentUser.getRank();
|
||||
|
||||
RankingRewardSnapshot.RankingUserSnapshot lastUser = lastUserMap.get(userId);
|
||||
|
||||
if (lastUser == null) {
|
||||
log.info("用户新获得排名,发放奖励, userId: {}, rank: {}", userId, currentRank);
|
||||
List<RankingRewardSnapshot.RewardItem> rewards = getRewardsForRank(currentRank, activityConfig);
|
||||
grantRewards(userId, rewards);
|
||||
|
||||
} else if (!Objects.equals(lastUser.getRank(), currentRank)) {
|
||||
log.info("用户排名变化, userId: {}, 旧排名: {}, 新排名: {}",
|
||||
userId, lastUser.getRank(), currentRank);
|
||||
revokeRewards(userId, lastUser.getRewards());
|
||||
List<RankingRewardSnapshot.RewardItem> rewards = getRewardsForRank(currentRank, activityConfig);
|
||||
grantRewards(userId, rewards);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放奖励.
|
||||
*/
|
||||
private void grantRewards(Long userId, List<RankingRewardSnapshot.RewardItem> rewards) {
|
||||
if (CollectionUtils.isEmpty(rewards)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RankingRewardSnapshot.RewardItem reward : rewards) {
|
||||
try {
|
||||
if ("PROPS".equals(reward.getType())) {
|
||||
grantProps(userId, reward);
|
||||
} else if ("BADGE".equals(reward.getType())) {
|
||||
grantBadge(userId, reward);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发放奖励失败, userId: {}, reward: {}", userId, reward, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 收回奖励.
|
||||
*/
|
||||
private void revokeRewards(Long userId, List<RankingRewardSnapshot.RewardItem> rewards) {
|
||||
if (CollectionUtils.isEmpty(rewards)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RankingRewardSnapshot.RewardItem reward : rewards) {
|
||||
try {
|
||||
if ("BADGE".equals(reward.getType())) {
|
||||
revokeBadge(userId, reward);
|
||||
} else if ("PROPS".equals(reward.getType())) {
|
||||
revokeProps(userId, reward);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("收回奖励失败, userId: {}, reward: {}", userId, reward, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放道具.
|
||||
*/
|
||||
private void grantProps(Long userId, RankingRewardSnapshot.RewardItem reward) {
|
||||
GivePropsBackpackCmd cmd = new GivePropsBackpackCmd()
|
||||
.setAcceptUserId(userId)
|
||||
.setPropsId(Long.valueOf(reward.getContent()))
|
||||
.setType(reward.getDetailType())
|
||||
.setOrigin(SendPropsOrigin.ACTIVITY_REWARD.name())
|
||||
.setOriginDesc("游戏王排名奖励")
|
||||
.setDays(reward.getQuantity())
|
||||
.setUseProps(Boolean.TRUE)
|
||||
.setAllowGive(Boolean.FALSE)
|
||||
.setOpUser(0L);
|
||||
|
||||
propsBackpackClient.giveProps(cmd);
|
||||
propsBackpackClient.givePropsNotify(userId, reward.getDetailType(),
|
||||
reward.getQuantity(), Long.valueOf(reward.getContent()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 收回道具.
|
||||
*/
|
||||
private void revokeProps(Long userId, RankingRewardSnapshot.RewardItem reward) {
|
||||
DeletePropsCmd cmd = new DeletePropsCmd()
|
||||
.setUserIds(Sets.newHashSet(userId))
|
||||
.setPropsIds(Sets.newHashSet(Long.valueOf(reward.getContent())))
|
||||
.setPropsType(PropsCommodityType.valueOf(reward.getDetailType()));
|
||||
|
||||
propsBackpackClient.deleteProps(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放徽章.
|
||||
*/
|
||||
private void grantBadge(Long userId, RankingRewardSnapshot.RewardItem reward) {
|
||||
BadgeOperateEvent event = new BadgeOperateEvent()
|
||||
.setUserId(userId)
|
||||
.setBadgeId(Long.valueOf(reward.getContent()))
|
||||
.setDays(1L)
|
||||
.setOperationType(BadgeOperateEvent.OperationType.WEAR.name());
|
||||
|
||||
userMqMessageService.sendBadgeOperateEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收回徽章.
|
||||
*/
|
||||
private void revokeBadge(Long userId, RankingRewardSnapshot.RewardItem reward) {
|
||||
BadgeOperateEvent event = new BadgeOperateEvent()
|
||||
.setUserId(userId)
|
||||
.setBadgeId(Long.valueOf(reward.getContent()))
|
||||
.setDays(1L)
|
||||
.setOperationType(BadgeOperateEvent.OperationType.REMOVE.name());
|
||||
|
||||
userMqMessageService.sendBadgeOperateEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定排名的奖励列表.
|
||||
*/
|
||||
private List<RankingRewardSnapshot.RewardItem> getRewardsForRank(Integer rank, ActivityConfigVO activityConfig) {
|
||||
if (activityConfig == null || CollectionUtils.isEmpty(activityConfig.getButOneRewards())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String rankRange = getRankRange(rank);
|
||||
|
||||
return activityConfig.getButOneRewards().stream()
|
||||
.filter(r -> rankRange.equals(r.getRankRange()))
|
||||
.flatMap(r -> {
|
||||
if (CollectionUtils.isEmpty(r.getRewards())) {
|
||||
return java.util.stream.Stream.empty();
|
||||
}
|
||||
return r.getRewards().stream()
|
||||
.filter(this::isSupportedRewardType)
|
||||
.map(this::convertToRewardItem);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为支持的奖励类型.
|
||||
*/
|
||||
private boolean isSupportedRewardType(PropsActivityRewardConfigInfoDTO dto) {
|
||||
return ("PROPS".equals(dto.getType()) && "AVATAR_FRAME".equals(dto.getDetailType())) ||
|
||||
("BADGE".equals(dto.getType()) && "HONOR_ACTIVITY".equals(dto.getDetailType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为奖励项.
|
||||
*/
|
||||
private RankingRewardSnapshot.RewardItem convertToRewardItem(PropsActivityRewardConfigInfoDTO dto) {
|
||||
return new RankingRewardSnapshot.RewardItem()
|
||||
.setType(dto.getType())
|
||||
.setDetailType(dto.getDetailType())
|
||||
.setContent(dto.getContent())
|
||||
.setQuantity(dto.getQuantity())
|
||||
.setBadgeName(dto.getBadgeName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取排名范围.
|
||||
*/
|
||||
private String getRankRange(Integer rank) {
|
||||
if (rank == 1) return "1";
|
||||
if (rank == 2) return "2";
|
||||
if (rank == 3) return "3";
|
||||
if (rank <= 10) return "4-10";
|
||||
return "other";
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存新快照.
|
||||
*/
|
||||
private void saveNewSnapshot(String activityType, String origin, String cycleKey,
|
||||
String templateId, List<RankingUserData> currentRankingUsers,
|
||||
ActivityConfigVO activityConfig) {
|
||||
List<RankingRewardSnapshot.RankingUserSnapshot> snapshotUsers = currentRankingUsers.stream()
|
||||
.map(user -> {
|
||||
String rankRange = getRankRange(user.getRank());
|
||||
List<RankingRewardSnapshot.RewardItem> rewards = getRewardsForRank(user.getRank(), activityConfig);
|
||||
|
||||
return new RankingRewardSnapshot.RankingUserSnapshot()
|
||||
.setUserId(user.getUserId())
|
||||
.setRank(user.getRank())
|
||||
.setRankRange(rankRange)
|
||||
.setScore(user.getScore())
|
||||
.setRewards(rewards);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
RankingRewardSnapshot snapshot = new RankingRewardSnapshot()
|
||||
.setActivityType(activityType)
|
||||
.setOrigin(origin)
|
||||
.setCycleKey(cycleKey)
|
||||
.setTemplateId(templateId)
|
||||
.setSnapshotTime(LocalDateTime.now())
|
||||
.setRankingUsers(snapshotUsers);
|
||||
|
||||
snapshotService.save(snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排名用户数据.
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class RankingUserData {
|
||||
private Long userId;
|
||||
private Integer rank;
|
||||
private Long score;
|
||||
}
|
||||
}
|
||||
@ -31,8 +31,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 徽章操作处理.
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Slf4j
|
||||
@RocketMqMessageListener(groupId = BadgeOperateSink.INPUT, tag = BadgeOperateSink.TAG)
|
||||
@ -63,28 +61,35 @@ public class BadgeOperateListener implements MessageListener {
|
||||
private void handleBadgeOperate(BadgeOperateEvent event) {
|
||||
Long userId = event.getUserId();
|
||||
String badgeKey = event.getBadgeKey();
|
||||
Long badgeId = event.getBadgeId();
|
||||
String operationType = event.getOperationType();
|
||||
|
||||
if (userId == null || badgeKey == null || operationType == null) {
|
||||
log.warn("徽章操作事件参数不完整, userId: {}, badgeKey: {}, operationType: {}",
|
||||
userId, badgeKey, operationType);
|
||||
if (userId == null || operationType == null) {
|
||||
log.warn("徽章操作事件参数不完整, userId: {}, operationType: {}", userId, operationType);
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据badgeKey查询徽章配置
|
||||
BadgeConfig badgeConfig = badgeConfigService.getByBadgeKey(badgeKey);
|
||||
if (badgeConfig == null) {
|
||||
log.warn("徽章配置不存在, badgeKey: {}", badgeKey);
|
||||
return;
|
||||
Long finalBadgeId = badgeId;
|
||||
|
||||
// 如果传了badgeId则使用badgeId,否则通过badgeKey查询
|
||||
if (finalBadgeId == null) {
|
||||
if (badgeKey == null) {
|
||||
log.warn("徽章操作事件缺少badgeKey和badgeId, userId: {}", userId);
|
||||
return;
|
||||
}
|
||||
BadgeConfig badgeConfig = badgeConfigService.getByBadgeKey(badgeKey);
|
||||
if (badgeConfig == null) {
|
||||
log.warn("徽章配置不存在, badgeKey: {}", badgeKey);
|
||||
return;
|
||||
}
|
||||
finalBadgeId = badgeConfig.getId();
|
||||
}
|
||||
|
||||
Long badgeId = badgeConfig.getId();
|
||||
|
||||
// 根据操作类型执行对应逻辑
|
||||
if (BadgeOperateEvent.OperationType.WEAR.name().equals(operationType)) {
|
||||
handleWearBadge(userId, badgeId);
|
||||
handleWearBadge(userId, finalBadgeId, event.getDays());
|
||||
} else if (BadgeOperateEvent.OperationType.REMOVE.name().equals(operationType)) {
|
||||
handleRemoveBadge(userId, badgeId);
|
||||
handleRemoveBadge(userId, finalBadgeId);
|
||||
} else {
|
||||
log.warn("未知的操作类型, operationType: {}", operationType);
|
||||
}
|
||||
@ -93,11 +98,10 @@ public class BadgeOperateListener implements MessageListener {
|
||||
/**
|
||||
* 佩戴徽章.
|
||||
*/
|
||||
private void handleWearBadge(Long userId, Long badgeId) {
|
||||
private void handleWearBadge(Long userId, Long badgeId, Long days) {
|
||||
String lockKey = "badge:operate:wear:" + userId + ":" + badgeId;
|
||||
|
||||
distributedLockUtil.executeWithLock(lockKey, () -> {
|
||||
// 检查用户是否已经拥有该徽章(幂等处理)
|
||||
BadgeBackpack existBadge = badgeBackpackService.query()
|
||||
.eq(BadgeBackpack::getUserId, userId)
|
||||
.eq(BadgeBackpack::getBadgeId, badgeId)
|
||||
@ -110,7 +114,7 @@ public class BadgeOperateListener implements MessageListener {
|
||||
|
||||
BadgeBackpack badgeBackpack = badgeBackpackService.getByUserIdByBadgeId(userId, badgeId);
|
||||
if (Objects.isNull(badgeBackpack)) {
|
||||
badgeBackpackService.save(buildSaveUserBadgeBackpack(badgeId, userId));
|
||||
badgeBackpackService.save(buildSaveUserBadgeBackpack(badgeId, userId, days));
|
||||
userProfileGateway.removeCacheAll(userId);
|
||||
}
|
||||
return null;
|
||||
@ -121,33 +125,37 @@ public class BadgeOperateListener implements MessageListener {
|
||||
* 移除徽章.
|
||||
*/
|
||||
private void handleRemoveBadge(Long userId, Long badgeId) {
|
||||
badgeBackpackService.deleteBadges(userId, Sets.newHashSet(badgeId));
|
||||
userProfileGateway.removeCache(userId);
|
||||
UserProfile userProfile = userProfileGateway.getByUserId(userId);
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return;
|
||||
}
|
||||
List<UserUseBadgeDTO> wearBadge = userProfile.getWearBadge();
|
||||
List<UserUseBadgeDTO> honorBadge = userProfile.getWearHonor();
|
||||
wearBadge.addAll(honorBadge);
|
||||
if (CollectionUtils.isEmpty(wearBadge)) {
|
||||
return;
|
||||
}
|
||||
//过滤需要删除的 wearBadge id
|
||||
Set<Long> saveWear = wearBadge.stream().map(UserUseBadgeDTO::getId)
|
||||
.filter(id -> !badgeId.equals(id)).collect(Collectors.toSet());
|
||||
userProfileGateway.updateUserWearBadge(userId, userProfile.getOriginSys(),saveWear);
|
||||
badgeBackpackService.deleteBadges(userId, Sets.newHashSet(badgeId));
|
||||
userProfileGateway.removeCache(userId);
|
||||
UserProfile userProfile = userProfileGateway.getByUserId(userId);
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return;
|
||||
}
|
||||
List<UserUseBadgeDTO> wearBadge = userProfile.getWearBadge();
|
||||
List<UserUseBadgeDTO> honorBadge = userProfile.getWearHonor();
|
||||
wearBadge.addAll(honorBadge);
|
||||
if (CollectionUtils.isEmpty(wearBadge)) {
|
||||
return;
|
||||
}
|
||||
Set<Long> saveWear = wearBadge.stream().map(UserUseBadgeDTO::getId)
|
||||
.filter(id -> !badgeId.equals(id)).collect(Collectors.toSet());
|
||||
userProfileGateway.updateUserWearBadge(userId, userProfile.getOriginSys(), saveWear);
|
||||
}
|
||||
|
||||
private BadgeBackpack buildSaveUserBadgeBackpack(Long badgeId, Long userId, Long days) {
|
||||
BadgeBackpack badgeBackpack = new BadgeBackpack()
|
||||
.setUserId(userId)
|
||||
.setBadgeId(badgeId)
|
||||
.setUseProps(Boolean.TRUE);
|
||||
|
||||
private BadgeBackpack buildSaveUserBadgeBackpack(Long badgeId, Long userId) {
|
||||
return new BadgeBackpack()
|
||||
.setUserId(userId)
|
||||
.setBadgeId(badgeId)
|
||||
.setExpireTime(TimestampUtils.now())
|
||||
.setExpireType(BadgeBackpackExpireTypeEnum.PERMANENT.name())
|
||||
.setUseProps(Boolean.TRUE);
|
||||
if (days != null && days > 0) {
|
||||
badgeBackpack.setExpireTime(TimestampUtils.nowPlusDays(days.intValue()));
|
||||
badgeBackpack.setExpireType(BadgeBackpackExpireTypeEnum.TEMPORARY.name());
|
||||
} else {
|
||||
badgeBackpack.setExpireTime(TimestampUtils.now());
|
||||
badgeBackpack.setExpireType(BadgeBackpackExpireTypeEnum.PERMANENT.name());
|
||||
}
|
||||
|
||||
|
||||
return badgeBackpack;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
package com.red.circle.other.app.listener;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import com.red.circle.component.mq.MessageEventProcess;
|
||||
import com.red.circle.component.mq.MessageEventProcessDescribe;
|
||||
import com.red.circle.component.mq.config.RocketMqMessageListener;
|
||||
import com.red.circle.component.mq.service.Action;
|
||||
import com.red.circle.component.mq.service.ConsumerMessage;
|
||||
import com.red.circle.component.mq.service.MessageListener;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.mq.business.model.event.GameKingRewardEvent;
|
||||
import com.red.circle.mq.rocket.business.streams.GameKingRewardSink;
|
||||
import com.red.circle.other.app.command.activity.RankingRewardGrantCmdExe;
|
||||
import com.red.circle.other.app.dto.clientobject.sys.ActivityConfigVO;
|
||||
import com.red.circle.other.app.service.activity.WeekKingQueenService;
|
||||
import com.red.circle.other.app.util.DistributedLockUtil;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 游戏王奖励监听器.
|
||||
*/
|
||||
@Slf4j
|
||||
@RocketMqMessageListener(groupId = GameKingRewardSink.INPUT, tag = GameKingRewardSink.TAG)
|
||||
@RequiredArgsConstructor
|
||||
public class GameKingRewardListener implements MessageListener {
|
||||
|
||||
private final MessageEventProcess messageEventProcess;
|
||||
private final DistributedLockUtil distributedLockUtil;
|
||||
private final WeekKingQueenService weekKingQueenService;
|
||||
private final RankingRewardGrantCmdExe rankingRewardGrantCmdExe;
|
||||
|
||||
@Override
|
||||
public Action consume(ConsumerMessage message) {
|
||||
return messageEventProcess.consume(
|
||||
MessageEventProcessDescribe.builder()
|
||||
.logTag("游戏王奖励处理")
|
||||
.consumeMsgTimeoutMinute(60)
|
||||
.repeatConsumeMinute(1)
|
||||
.repeatConsumeTag("GameKingReward")
|
||||
.checkConsumeTag(GameKingRewardSink.TAG)
|
||||
.message(message)
|
||||
.build(),
|
||||
GameKingRewardEvent.class,
|
||||
this::handleGameKingReward);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理游戏王奖励事件.
|
||||
*/
|
||||
private void handleGameKingReward(GameKingRewardEvent event) {
|
||||
String activityType = event.getActivityType();
|
||||
String origin = event.getOrigin();
|
||||
String cycleKey = event.getCycleKey();
|
||||
String templateId = event.getTemplateId() == null ? "-1" : event.getTemplateId();
|
||||
|
||||
if (activityType == null || origin == null || cycleKey == null) {
|
||||
log.warn("游戏王奖励事件参数不完整, activityType: {}, origin: {}, cycleKey: {}",
|
||||
activityType, origin, cycleKey);
|
||||
return;
|
||||
}
|
||||
|
||||
String lockKey = "game_king_reward:" + activityType + ":" + origin + ":" + cycleKey;
|
||||
|
||||
distributedLockUtil.executeWithLock(lockKey, () -> {
|
||||
try {
|
||||
log.info("开始处理游戏王奖励, activityType: {}, origin: {}, cycleKey: {}, templateId: {}, rankingUsers: {}",
|
||||
activityType, origin, cycleKey, templateId,
|
||||
event.getRankingUsers() != null ? event.getRankingUsers().size() : 0);
|
||||
|
||||
if (CollectionUtils.isEmpty(event.getRankingUsers())) {
|
||||
log.warn("排名用户列表为空,跳过处理");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取活动配置
|
||||
AppExtCommand cmd = new AppExtCommand();
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
|
||||
ActivityConfigVO activityConfig = weekKingQueenService.getEffectiveActivity(cmd, templateId);
|
||||
if (activityConfig == null || activityConfig.getConfig() == null) {
|
||||
log.warn("活动配置不存在, templateId: {}", templateId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 转换排名数据
|
||||
List<RankingRewardGrantCmdExe.RankingUserData> rankingUsers = event.getRankingUsers().stream()
|
||||
.map(user -> {
|
||||
RankingRewardGrantCmdExe.RankingUserData data = new RankingRewardGrantCmdExe.RankingUserData();
|
||||
data.setUserId(user.getUserId());
|
||||
data.setRank(user.getRank());
|
||||
data.setScore(user.getScore());
|
||||
return data;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 执行奖励发放逻辑
|
||||
rankingRewardGrantCmdExe.execute(activityType, origin, cycleKey, templateId,
|
||||
rankingUsers, activityConfig);
|
||||
|
||||
log.info("游戏王奖励处理完成, activityType: {}, origin: {}, cycleKey: {}",
|
||||
activityType, origin, cycleKey);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理游戏王奖励失败, activityType: {}, origin: {}, cycleKey: {}",
|
||||
activityType, origin, cycleKey, e);
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,36 @@
|
||||
package com.red.circle.other.app.service.activity;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.mq.business.model.event.GameKingRewardEvent;
|
||||
import com.red.circle.other.app.command.activity.query.HistoricalTop1QryExe;
|
||||
import com.red.circle.other.app.command.activity.query.RankingDataAccumulateCmdExe;
|
||||
import com.red.circle.other.app.command.activity.query.RankingListQryExe;
|
||||
import com.red.circle.other.app.command.activity.query.UserRankQryExe;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.HistoricalTop1ListCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.RankingListCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.UserRankInfoCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.*;
|
||||
import com.red.circle.other.app.dto.cmd.UserRankQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.HistoricalTop1QueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingListQueryCmd;
|
||||
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.other.domain.ranking.RankingActivityType;
|
||||
import com.red.circle.other.domain.ranking.RankingCycleType;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 排行榜活动服务实现
|
||||
*
|
||||
* 排行榜活动服务实现.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -28,6 +41,12 @@ public class RankingActivityServiceImpl implements RankingActivityService {
|
||||
private final RankingListQryExe rankingListQryExe;
|
||||
private final UserRankQryExe userRankQryExe;
|
||||
private final HistoricalTop1QryExe historicalTop1QryExe;
|
||||
private final RedisService redisService;
|
||||
private final UserMqMessageService userMqMessageService;
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
|
||||
private static final String RANKING_TOP4_CACHE_KEY_PREFIX = "ranking_top4:";
|
||||
private static final int CACHE_EXPIRE_MINUTES = 5;
|
||||
|
||||
@Override
|
||||
public Boolean accumulateRankingData(RankingDataUpdateCmd cmd) {
|
||||
@ -48,4 +67,87 @@ public class RankingActivityServiceImpl implements RankingActivityService {
|
||||
public HistoricalTop1ListCO getHistoricalTop1List(HistoricalTop1QueryCmd cmd) {
|
||||
return historicalTop1QryExe.execute(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RankingUserVO> getTopFourRankingWithReward(RankingListQueryCmd cmd) {
|
||||
String cacheKey = buildCacheKey(cmd);
|
||||
|
||||
String cachedJson = redisService.getString(cacheKey);
|
||||
if (cachedJson != null) {
|
||||
return JSON.parseArray(cachedJson, RankingUserVO.class);
|
||||
}
|
||||
|
||||
RankingListQueryCmd top4Cmd = new RankingListQueryCmd();
|
||||
top4Cmd.setActivityType(cmd.getActivityType());
|
||||
top4Cmd.setCycleKey(cmd.getCycleKey());
|
||||
top4Cmd.setCycleType(RankingCycleType.WEEKLY.getCode());
|
||||
top4Cmd.setReqUserId(cmd.getReqUserId());
|
||||
top4Cmd.setReqSysOrigin(cmd.getReqSysOrigin());
|
||||
top4Cmd.setTopN(10);
|
||||
|
||||
RankingListCO result = rankingListQryExe.execute(top4Cmd);
|
||||
|
||||
List<RankingUserVO> rankingUsers = null;
|
||||
if (result != null && CollectionUtils.isNotEmpty(result.getRankingList())) {
|
||||
Set<Long> userIds = result.getRankingList().stream()
|
||||
.map(RankingActivityRecordCO::getUserId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(userIds);
|
||||
|
||||
rankingUsers = result.getRankingList().stream()
|
||||
.limit(4)
|
||||
.map(user -> {
|
||||
UserProfile profile = userProfileMap.get(user.getUserId());
|
||||
RankingUserVO vo = new RankingUserVO();
|
||||
vo.setUserId(user.getUserId());
|
||||
vo.setRank(user.getRank());
|
||||
vo.setQuantity(String.valueOf(user.getQuantity()));
|
||||
|
||||
if (profile != null) {
|
||||
vo.setAccount(profile.getAccount());
|
||||
vo.setNickname(profile.getUserNickname());
|
||||
vo.setAvatar(profile.getUserAvatar());
|
||||
}
|
||||
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
redisService.setString(cacheKey, JSON.toJSONString(rankingUsers), CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||
|
||||
sendGameKingRewardEvent(cmd, result);
|
||||
}
|
||||
|
||||
return rankingUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送游戏王奖励消息.
|
||||
*/
|
||||
private void sendGameKingRewardEvent(RankingListQueryCmd cmd, RankingListCO result) {
|
||||
if (CollectionUtils.isEmpty(result.getRankingList())) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameKingRewardEvent event = new GameKingRewardEvent()
|
||||
.setActivityType(RankingActivityType.getByCode(cmd.getActivityType()).name())
|
||||
.setOrigin(cmd.getReqSysOrigin().getOrigin())
|
||||
.setCycleKey(cmd.getCycleKey())
|
||||
.setTemplateId(cmd.getTemplateId())
|
||||
.setRankingUsers(result.getRankingList().stream()
|
||||
.map(user -> new GameKingRewardEvent.RankingUser()
|
||||
.setUserId(user.getUserId())
|
||||
.setRank(user.getRank()))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
userMqMessageService.sendGameKingRewardEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建缓存Key.
|
||||
*/
|
||||
private String buildCacheKey(RankingListQueryCmd cmd) {
|
||||
return RANKING_TOP4_CACHE_KEY_PREFIX + RankingActivityType.getByCode(cmd.getActivityType()).name() + ":" + cmd.getCycleKey();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
package com.red.circle.other.app.dto.clientobject.activity;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 排名用户VO.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class RankingUserVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID.
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 账号.
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 昵称.
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像.
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 排名.
|
||||
*/
|
||||
private Integer rank;
|
||||
|
||||
/**
|
||||
* 数量/分数.
|
||||
*/
|
||||
private String quantity;
|
||||
}
|
||||
@ -57,4 +57,9 @@ public class RankingListQueryCmd extends AppExtCommand {
|
||||
* 是否包含当前用户排名
|
||||
*/
|
||||
private Boolean includeCurrentUser = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板ID
|
||||
*/
|
||||
private String templateId;
|
||||
}
|
||||
|
||||
@ -2,12 +2,15 @@ package com.red.circle.other.app.service.activity;
|
||||
|
||||
import com.red.circle.other.app.dto.clientobject.activity.HistoricalTop1ListCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.RankingListCO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.RankingUserVO;
|
||||
import com.red.circle.other.app.dto.clientobject.activity.UserRankInfoCO;
|
||||
import com.red.circle.other.app.dto.cmd.UserRankQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.HistoricalTop1QueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingListQueryCmd;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 排行榜活动服务
|
||||
*/
|
||||
@ -44,4 +47,12 @@ public interface RankingActivityService {
|
||||
* @return 历史Top1列表(最近10周)
|
||||
*/
|
||||
HistoricalTop1ListCO getHistoricalTop1List(HistoricalTop1QueryCmd cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取排行榜前四名(带缓存和奖励发送)
|
||||
*
|
||||
* @param cmd 查询命令
|
||||
* @return 排行榜前四名
|
||||
*/
|
||||
List<RankingUserVO> getTopFourRankingWithReward(RankingListQueryCmd cmd);
|
||||
}
|
||||
|
||||
@ -44,6 +44,8 @@ public enum RankingActivityType {
|
||||
GREEDY_GAME(1068, "GreedyLion", "游戏中奖排行"),
|
||||
|
||||
LUCK_GIFT(9999, "LuckGift", "统计送幸运礼物排行"),
|
||||
|
||||
KING_GAMES(10, "游戏王活动", "统计游戏数据排行"),
|
||||
;
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
package com.red.circle.other.infra.database.mongo.entity.ranking;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.index.CompoundIndex;
|
||||
import org.springframework.data.mongodb.core.index.CompoundIndexes;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 排名奖励快照.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Document(collection = "ranking_reward_snapshot")
|
||||
@CompoundIndexes({
|
||||
@CompoundIndex(name = "idx_activity_cycle", def = "{'activityType': 1, 'cycleKey': 1, 'origin': 1}"),
|
||||
@CompoundIndex(name = "idx_snapshot_time", def = "{'snapshotTime': -1}")
|
||||
})
|
||||
public class RankingRewardSnapshot implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String activityType;
|
||||
|
||||
private String origin;
|
||||
|
||||
private String cycleKey;
|
||||
|
||||
private String templateId;
|
||||
|
||||
private LocalDateTime snapshotTime;
|
||||
|
||||
private List<RankingUserSnapshot> rankingUsers;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class RankingUserSnapshot implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private Integer rank;
|
||||
|
||||
private String rankRange;
|
||||
|
||||
private Long score;
|
||||
|
||||
private List<RewardItem> rewards;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class RewardItem implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String type;
|
||||
|
||||
private String detailType;
|
||||
|
||||
private String content;
|
||||
|
||||
private Integer quantity;
|
||||
|
||||
private String badgeName;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.ranking;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.ranking.RankingRewardSnapshot;
|
||||
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.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 排名奖励快照服务.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RankingRewardSnapshotService {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
/**
|
||||
* 保存快照.
|
||||
*/
|
||||
public void save(RankingRewardSnapshot snapshot) {
|
||||
mongoTemplate.save(snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新快照.
|
||||
*/
|
||||
public RankingRewardSnapshot getLatestSnapshot(String activityType, String origin, String cycleKey) {
|
||||
Query query = new Query();
|
||||
query.addCriteria(Criteria.where("activityType").is(activityType)
|
||||
.and("origin").is(origin)
|
||||
.and("cycleKey").is(cycleKey));
|
||||
query.with(org.springframework.data.domain.Sort.by(
|
||||
org.springframework.data.domain.Sort.Direction.DESC, "snapshotTime"));
|
||||
query.limit(1);
|
||||
|
||||
return mongoTemplate.findOne(query, RankingRewardSnapshot.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除旧快照.
|
||||
*/
|
||||
public void deleteOldSnapshots(String activityType, String origin, String cycleKey) {
|
||||
Query query = new Query();
|
||||
query.addCriteria(Criteria.where("activityType").is(activityType)
|
||||
.and("origin").is(origin)
|
||||
.and("cycleKey").is(cycleKey));
|
||||
|
||||
mongoTemplate.remove(query, RankingRewardSnapshot.class);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user