diff --git a/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/BadgeOperateEvent.java b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/BadgeOperateEvent.java new file mode 100644 index 00000000..3068b827 --- /dev/null +++ b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/BadgeOperateEvent.java @@ -0,0 +1,50 @@ +package com.red.circle.mq.business.model.event; + +import java.io.Serial; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 徽章操作事件消息. + * + * @author system + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class BadgeOperateEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id. + */ + private Long userId; + + /** + * 徽章Key. + */ + private String badgeKey; + + /** + * 操作类型: WEAR-佩戴, REMOVE-移除. + */ + private String operationType; + + /** + * 操作类型枚举. + */ + public enum OperationType { + /** + * 佩戴徽章. + */ + WEAR, + /** + * 移除徽章. + */ + REMOVE + } +} diff --git a/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/constant/QueueNameConstant.java b/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/constant/QueueNameConstant.java index 7548c7be..f0b36854 100644 --- a/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/constant/QueueNameConstant.java +++ b/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/constant/QueueNameConstant.java @@ -62,6 +62,11 @@ public interface QueueNameConstant { */ String BADGE_CHECK = "BADGE_CHECK"; + /** + * 徽章操作. + */ + String BADGE_OPERATE = "BADGE_OPERATE"; + /** * 红包游戏. */ diff --git a/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/producer/UserMqMessageService.java b/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/producer/UserMqMessageService.java index 1b53b0fc..d2e2210d 100644 --- a/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/producer/UserMqMessageService.java +++ b/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/producer/UserMqMessageService.java @@ -4,6 +4,7 @@ import com.red.circle.component.mq.MessageEvent; 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.user.AccountBeautifulNumberEvent; import com.red.circle.mq.business.model.event.user.CpApplyEvent; import com.red.circle.mq.business.model.event.user.LoginLoggerEvent; @@ -12,6 +13,7 @@ import com.red.circle.mq.business.model.event.user.UserHeartbeatEvent; import com.red.circle.mq.business.model.event.user.UserOnlineStatusEvent; 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.LoginRegisterLoggerSink; import com.red.circle.mq.rocket.business.streams.OnlineStatusUploadSink; @@ -123,5 +125,24 @@ public class UserMqMessageService { .build()); } + /** + * 发送徽章操作事件. + * + * @param userId 用户ID + * @param badgeKey 徽章Key + * @param operationType 操作类型: WEAR-佩戴, REMOVE-移除 + */ + public void sendBadgeOperateEvent(Long userId, String badgeKey, String operationType) { + senderService.sendEventMessage(MessageEvent.builder() + .consumeId(IdWorkerUtils.getIdStr()) + .topic(FixedTopic.RC_DEFAULT_APP_ORDINARY) + .tag(BadgeOperateSink.TAG) + .body(new BadgeOperateEvent() + .setUserId(userId) + .setBadgeKey(badgeKey) + .setOperationType(operationType) + ) + .build()); + } } diff --git a/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/streams/BadgeOperateSink.java b/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/streams/BadgeOperateSink.java new file mode 100644 index 00000000..290e5480 --- /dev/null +++ b/rc-common-business/common-business-mq/business-rocketmq/src/main/java/com/red/circle/mq/rocket/business/streams/BadgeOperateSink.java @@ -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 BadgeOperateSink { + + String INPUT = QueueNameConstant.BADGE_OPERATE; + String TAG = "badge_operate_v1"; + +} diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/material/BadgeKeyEnum.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/material/BadgeKeyEnum.java index 753999db..b3ba1c94 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/material/BadgeKeyEnum.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/enums/material/BadgeKeyEnum.java @@ -27,33 +27,49 @@ public enum BadgeKeyEnum { */ FORTUNE_ROOM, - /** - * 红包徽章. - */ - LUCK_BAG, - - /** - * 签到徽章. - */ - CHECK_IN, - /** * 充值徽章 */ RECHARGE, /** - * TODO 需要重构. - * 管理员徽章. + * 经理徽章 + */ + MANAGER, + + /** + * 充值代理徽章 + */ + RECHARGE_AGENCY, + + /** + * 代理徽章 + */ + AGENCY, + + /** + * 主播徽章 + */ + HOST, + + /** + * 管理员徽章 + */ + ADMIN, + + /** + * BD领导徽章 + */ + BD_LEADER, + + /** + * 超级管理员徽章 */ - @Deprecated SUPER_ADMIN, /** - * TODO 需要重构. - * 管理员. + * BD徽章 */ - @Deprecated - ADMIN + BD, } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/BadgeOperateListener.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/BadgeOperateListener.java new file mode 100644 index 00000000..ff754195 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/BadgeOperateListener.java @@ -0,0 +1,145 @@ +package com.red.circle.other.app.listener; + +import com.google.common.collect.Sets; +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.mq.business.model.event.BadgeOperateEvent; +import com.red.circle.mq.rocket.business.streams.BadgeOperateSink; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.database.rds.entity.badge.BadgeBackpack; +import com.red.circle.other.infra.database.rds.entity.badge.BadgeConfig; +import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService; +import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum; +import com.red.circle.other.inner.model.dto.user.props.UserUseBadgeDTO; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.tool.core.date.TimestampUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 徽章操作处理. + * + * @author system + */ +@Slf4j +@RocketMqMessageListener(groupId = BadgeOperateSink.INPUT, tag = BadgeOperateSink.TAG) +@RequiredArgsConstructor +public class BadgeOperateListener implements MessageListener { + + private final BadgeConfigService badgeConfigService; + private final BadgeBackpackService badgeBackpackService; + private final MessageEventProcess messageEventProcess; + private final UserProfileGateway userProfileGateway; + + @Override + public Action consume(ConsumerMessage message) { + return messageEventProcess.consume( + MessageEventProcessDescribe.builder() + .logTag("徽章操作处理") + .consumeMsgTimeoutMinute(60) + .repeatConsumeMinute(1) + .repeatConsumeTag("BadgeOperate") + .checkConsumeTag(BadgeOperateSink.TAG) + .message(message) + .build(), + BadgeOperateEvent.class, + this::handleBadgeOperate); + } + + private void handleBadgeOperate(BadgeOperateEvent event) { + Long userId = event.getUserId(); + String badgeKey = event.getBadgeKey(); + String operationType = event.getOperationType(); + + if (userId == null || badgeKey == null || operationType == null) { + log.warn("徽章操作事件参数不完整, userId: {}, badgeKey: {}, operationType: {}", + userId, badgeKey, operationType); + return; + } + + // 根据badgeKey查询徽章配置 + BadgeConfig badgeConfig = badgeConfigService.getByBadgeKey(badgeKey); + if (badgeConfig == null) { + log.warn("徽章配置不存在, badgeKey: {}", badgeKey); + return; + } + + Long badgeId = badgeConfig.getId(); + + // 根据操作类型执行对应逻辑 + if (BadgeOperateEvent.OperationType.WEAR.name().equals(operationType)) { + handleWearBadge(userId, badgeId); + } else if (BadgeOperateEvent.OperationType.REMOVE.name().equals(operationType)) { + handleRemoveBadge(userId, badgeId); + } else { + log.warn("未知的操作类型, operationType: {}", operationType); + } + } + + /** + * 佩戴徽章. + */ + private void handleWearBadge(Long userId, Long badgeId) { + // 检查用户是否已经拥有该徽章(幂等处理) + BadgeBackpack existBadge = badgeBackpackService.query() + .eq(BadgeBackpack::getUserId, userId) + .eq(BadgeBackpack::getBadgeId, badgeId) + .getOne(); + + if (existBadge != null) { + log.warn("用户已拥有该徽章, 跳过发放, userId: {}, badgeId: {}", userId, badgeId); + return; + } + + BadgeBackpack badgeBackpack = badgeBackpackService.getByUserIdByBadgeId(userId, badgeId); + if (Objects.isNull(badgeBackpack)) { + badgeBackpackService.save(buildSaveUserBadgeBackpack(badgeId, userId)); + userProfileGateway.removeCacheAll(userId); + } + + } + + /** + * 移除徽章. + */ + 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 wearBadge = userProfile.getWearBadge(); + if (CollectionUtils.isEmpty(wearBadge)) { + return; + } + //过滤需要删除的 wearBadge id + Set 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) { + return new BadgeBackpack() + .setUserId(userId) + .setBadgeId(badgeId) + .setExpireTime(TimestampUtils.now()) + .setExpireType(BadgeBackpackExpireTypeEnum.PERMANENT.name()) + .setUseProps(Boolean.TRUE); + } + + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/BadgeConfigService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/BadgeConfigService.java index c3f4cebb..916604d7 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/BadgeConfigService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/BadgeConfigService.java @@ -23,13 +23,6 @@ import java.util.Set; */ public interface BadgeConfigService extends BaseService { - /** - * 获取管理员徽章id. - * - * @return ignore - */ - Long getAdminBadge(); - List listBadgeByType(SysBadgeConfigTypeEnum type); /** @@ -125,4 +118,12 @@ public interface BadgeConfigService extends BaseService { * @return */ List getBadgeConfigList(BadgeConfigTypeEnum type, String s); + + /** + * 根据徽章Key查询徽章配置. + * + * @param badgeKey 徽章Key + * @return 徽章配置 + */ + BadgeConfig getByBadgeKey(String badgeKey); } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/impl/BadgeConfigServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/impl/BadgeConfigServiceImpl.java index 835db74e..2df07069 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/impl/BadgeConfigServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/badge/impl/BadgeConfigServiceImpl.java @@ -34,19 +34,6 @@ import org.springframework.stereotype.Service; public class BadgeConfigServiceImpl extends BaseServiceImpl implements BadgeConfigService { - @Override - public Long getAdminBadge() { - return Optional.ofNullable(query().select(BadgeConfig::getId) - .eq(BadgeConfig::getType, BadgeConfigTypeEnum.ADMINISTRATOR) - .eq(BadgeConfig::getBadgeKey, BadgeKeyEnum.ADMIN) - .eq(BadgeConfig::getDel, Boolean.FALSE) - .orderByAsc(BadgeConfig::getBadgeLevel) - .last(PageConstant.LIMIT_ONE) - .getOne()) - .map(BadgeConfig::getId) - .orElse(null); - } - @Override public List listBadgeByType(SysBadgeConfigTypeEnum type) { @@ -204,4 +191,13 @@ public class BadgeConfigServiceImpl extends BaseServiceImpl getBadgeConfigList(BadgeConfigTypeEnum type, String sysOrgin) { return baseDAO.getBadgeConfigList(type,sysOrgin); } + + @Override + public BadgeConfig getByBadgeKey(String badgeKey) { + return query() + .eq(BadgeConfig::getBadgeKey, badgeKey) + .eq(BadgeConfig::getDel, Boolean.FALSE) + .last(PageConstant.LIMIT_ONE) + .getOne(); + } }