From b1b04030af944087dd2a836b26a87765ca1f2386 Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Fri, 7 Nov 2025 17:49:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B4=BB=E5=8A=A8=E5=8F=91?= =?UTF-8?q?=E6=94=BE=E9=81=93=E5=85=B7=E5=88=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/PropsActivityRewardEnum.java | 5 + .../award/WeeklyRewardsSentManager.java | 108 +++++++++++++++ .../scheduler/RankingActivityRewardTask.java | 52 ++++++++ .../infra/common/props/PropsSendCommon.java | 123 +++++++++++++++++- .../service/sys/ActivityConfigService.java | 2 + .../sys/impl/ActivityConfigServiceImpl.java | 18 +++ .../src/test/java/DailTaskTest.java | 8 ++ 7 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RankingActivityRewardTask.java diff --git a/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/enums/PropsActivityRewardEnum.java b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/enums/PropsActivityRewardEnum.java index bb969539..a07e15ba 100644 --- a/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/enums/PropsActivityRewardEnum.java +++ b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/enums/PropsActivityRewardEnum.java @@ -16,6 +16,11 @@ public enum PropsActivityRewardEnum { */ PROPS, + /** + * 道具券 + */ + PROP_COUPON, + /** * 金币 */ diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/activity/award/WeeklyRewardsSentManager.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/activity/award/WeeklyRewardsSentManager.java index 9628b939..200aade5 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/activity/award/WeeklyRewardsSentManager.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/manager/activity/award/WeeklyRewardsSentManager.java @@ -14,10 +14,13 @@ import com.red.circle.other.app.dto.clientobject.activity.ActivityResourceCO; import com.red.circle.other.app.dto.clientobject.gift.GiftConfigCO; import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway; import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; +import com.red.circle.other.domain.ranking.RankingActivityType; +import com.red.circle.other.domain.ranking.RankingCycleType; import com.red.circle.other.infra.common.activity.PropsActivitySendCommon; import com.red.circle.other.infra.common.activity.send.SendRewardGroup; import com.red.circle.other.infra.database.cache.service.user.CacheEnumConfigManagerService; import com.red.circle.other.infra.database.mongo.entity.activity.AgentActivityCount; +import com.red.circle.other.infra.database.mongo.entity.activity.RankingActivityRecord; import com.red.circle.other.infra.database.mongo.entity.activity.WeekStarGiftCount; import com.red.circle.other.infra.database.mongo.entity.user.count.WeekCpValueCount; import com.red.circle.other.infra.database.mongo.entity.user.count.WeekFriendshipCardCount; @@ -37,6 +40,7 @@ import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRul import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; import com.red.circle.other.infra.database.rds.service.sys.WeekStarGiftService; import com.red.circle.other.infra.database.rds.service.sys.WeekStarGroupService; +import com.red.circle.other.infra.gateway.RankingActivityGateway; import com.red.circle.other.infra.utils.DateFormatEnum; import com.red.circle.other.infra.utils.ZonedDateTimeUtils; import com.red.circle.other.inner.endpoint.activity.PropsActivityClient; @@ -60,6 +64,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutableTriple; +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; import java.math.BigDecimal; @@ -97,6 +102,7 @@ public class WeeklyRewardsSentManager { private final SysActivityConfigClient sysActivityConfigClient; private final WeekKingQueenClient weekKingQueenClient; private final PropsActivityClient propsActivityClient; + private final RankingActivityGateway rankingActivityGateway; /** * 每周一发送奖励业务. @@ -185,13 +191,111 @@ public class WeeklyRewardsSentManager { } } + + public void sendLastRankingActivity(Long templateId) { + try { + // 1. 获取活动配置信息 + ActivityConfigDTO activityConfigDTO = getActivityConfig(templateId); + // 国王皇后活动单独发放。 + long kingQueenTemplateId = getKingQueenTemplateId(); + if (activityConfigDTO == null || activityConfigDTO.getTemplateId() == kingQueenTemplateId) { + log.warn("未找到有效的活动配置"); + return; + } + + // 2. 检查奖励是否已发放 + if (Boolean.TRUE.equals(activityConfigDTO.getAwardStatus())) { + log.info("奖励已发放,跳过处理"); + return; + } + + Long activityId = activityConfigDTO.getId(); + String lastWeekDate = getLastWeekDate(); + + String sysOrigin = SysOriginPlatformEnum.LIKEI.name(); + // 获取枚举 + RankingActivityType activityType = RankingActivityType.HEROES_DAY; + RankingCycleType cycleType = RankingCycleType.WEEKLY; + // 4. 获取奖励配置并更新 + List kingRewardList = activityConfigDTO.getButOneRewards(); + List records = rankingActivityGateway.getRankingList( + sysOrigin, + activityType, + cycleType, + lastWeekDate, + null, + 3 + ); + + // 发放活动奖励 + sendRankingActivityRewards(SysOriginPlatformEnum.LIKEI, activityId, records, kingRewardList); + + + log.info("上周{}活动奖励发放完成, 活动ID: {}", activityConfigDTO.getRemark(), activityId); + + } catch (Exception e) { + log.error("发送活动奖励异常, 错误信息: {}", e.getMessage(), e); + } + } + + /** + * 发放活动奖励 + */ + private void sendRankingActivityRewards(SysOriginPlatformEnum sysOriginPlatformEnum, Long activityId, + List topUsers, + List rewardsDTOList) { + if (CollectionUtils.isEmpty(topUsers) || CollectionUtils.isEmpty(rewardsDTOList)) { + log.warn("活动奖励为空, 跳过国王奖励发放"); + return; + } + + for (int i = 0; i < Math.min(topUsers.size(), rewardsDTOList.size()); i++) { + try { + RankingActivityRecord user = topUsers.get(i); + + // 判断金额是否达到100K,如果没有则跳过不发奖励 + if (user.getQuantity() == null || user.getQuantity() < 100000) { + log.warn("活动奖励 - 排名: {}, 用户ID: {}, 金额: {} 未达到100K要求,跳过发奖励", + i + 1, user.getUserId(), user.getQuantity()); + continue; + } + + ActivityConfigRewardsDTO reward = rewardsDTOList.get(i); + + // 发送奖励 + sendRewardToUser(sysOriginPlatformEnum, activityId, user.getUserId(), reward.getResourceGroupId()); + + log.info("活动奖励发放成功 - 排名: {}, 用户ID: {}, 奖励组ID: {}", + i + 1, user.getUserId(), reward.getResourceGroupId()); + } catch (Exception e) { + log.error("活动奖励发放失败 - 排名: {}, 错误信息: {}", i + 1, e.getMessage(), e); + } + } + } + /** * 获取活动配置信息 */ + private ActivityConfigDTO getActivityConfig(Long templateId) { + try { + ActivityConfigBackQryCmd query = new ActivityConfigBackQryCmd(); + query.setSysOrigin(SysOriginPlatformEnum.LIKEI.name()); + query.setTemplateId(templateId); + return ResponseAssert.requiredSuccess(sysActivityConfigClient.effectiveActivityConf(query)); + } catch (Exception e) { + log.error("[KingQueen] 获取活动配置失败, 错误信息: {}", e.getMessage(), e); + return null; + } + } + + /** + * 获取国王皇后活动配置信息 + */ private ActivityConfigDTO getActivityConfig(String sysOrigin) { try { ActivityConfigBackQryCmd query = new ActivityConfigBackQryCmd(); query.setSysOrigin(sysOrigin); + query.setTemplateId(getKingQueenTemplateId()); return ResponseAssert.requiredSuccess(sysActivityConfigClient.effectiveActivityConf(query)); } catch (Exception e) { log.error("[KingQueen] 获取活动配置失败, 系统平台: {}, 错误信息: {}", sysOrigin, e.getMessage(), e); @@ -199,6 +303,10 @@ public class WeeklyRewardsSentManager { } } + private static long getKingQueenTemplateId() { + return 1966020557831610370L; + } + /** * 获取排行榜数据 */ diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RankingActivityRewardTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RankingActivityRewardTask.java new file mode 100644 index 00000000..e2ffc577 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RankingActivityRewardTask.java @@ -0,0 +1,52 @@ +package com.red.circle.other.app.scheduler; + + +import com.red.circle.component.redis.annotation.TaskCacheLock; +import com.red.circle.other.app.manager.activity.award.WeeklyRewardsSentManager; +import com.red.circle.other.infra.database.mongo.entity.sys.ActivityConfig; +import com.red.circle.other.infra.database.mongo.service.sys.ActivityConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * rankingActivity 活动奖励发放 + */ + +@Slf4j +@Component +@RequiredArgsConstructor +public class RankingActivityRewardTask { + + private final WeeklyRewardsSentManager weeklyRewardsSentManager; + private final ActivityConfigService activityConfigService; + + /** + * 每周一 0点0分执行. + */ +// @Scheduled(cron = "0 0 0 ? * MON", zone = "Asia/Riyadh") + @TaskCacheLock(key = "RANKING_ACTIVITY_REWARD_TASK", expireSecond = 86400) + public void sendRankingActivityReward() { + long startTime = System.currentTimeMillis(); + log.warn("========== rankingActivity 活动奖励发放定时任务开始 =========="); + + try { + + // 查询上周结束的活动 + List lastWeekActivities = + activityConfigService.listRecentlyEndedActivities("LIKEI", 10); + lastWeekActivities.forEach(activityConfig -> { + weeklyRewardsSentManager.sendLastRankingActivity(activityConfig.getTemplateId()); + }); + + log.warn("========== rankingActivity 活动奖励发放定时任务完成,耗时:{} ms ==========", + System.currentTimeMillis() - startTime); + } catch (Exception e) { + log.error("========== rankingActivity 活动奖励发放定时任务异常 ==========", e); + } + } + +} \ No newline at end of file diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/props/PropsSendCommon.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/props/PropsSendCommon.java index c354a281..98cf6dd0 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/props/PropsSendCommon.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/props/PropsSendCommon.java @@ -11,12 +11,17 @@ import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExt import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.other.domain.gateway.PropCouponGateway; import com.red.circle.other.domain.gateway.props.PropsNobleVipGateway; import com.red.circle.other.domain.gateway.props.PropsStoreGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.model.user.ability.RegionConfig; +import com.red.circle.other.domain.propcoupon.PropCoupon; +import com.red.circle.other.domain.propcoupon.PropCouponSource; +import com.red.circle.other.domain.propcoupon.PropCouponStatus; +import com.red.circle.other.domain.propcoupon.PropCouponType; import com.red.circle.other.infra.convertor.material.PropsMaterialInfraConvertor; import com.red.circle.other.infra.database.cache.service.other.EmojiCacheService; import com.red.circle.other.infra.database.mongo.entity.live.RoomSetting; @@ -34,13 +39,17 @@ import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordSe import com.red.circle.other.infra.database.rds.service.props.PropsVipActualEquityService; import com.red.circle.other.infra.database.rds.service.user.user.ConsumptionLevelService; import com.red.circle.other.infra.utils.I18nUtils; +import com.red.circle.other.inner.asserts.OtherErrorCode; import com.red.circle.other.inner.enums.material.NobleVipEnum; import com.red.circle.other.inner.enums.material.PropsCommodityType; import com.red.circle.other.inner.model.cmd.material.PrizeDescribe; import com.red.circle.other.inner.model.cmd.material.PrizeDescribeRewardCmd; +import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd; +import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO; import com.red.circle.other.inner.model.dto.material.props.NobleVipAbility; import com.red.circle.other.inner.model.dto.material.props.ProductProps; import com.red.circle.other.inner.model.dto.material.props.PropsResources; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.num.NumConstant; @@ -54,10 +63,8 @@ import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; import com.red.circle.wallet.inner.model.enums.GoldOrigin; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.time.LocalDateTime; +import java.util.*; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -88,7 +95,7 @@ public class PropsSendCommon { private final PropsMaterialInfraConvertor propsMaterialInfraConvertor; private final PropsVipActualEquityService propsVipActualEquityService; private final ConsumptionLevelService consumptionLevelService; - private final UserRegionGateway userRegionGateway; + private final PropCouponGateway propCouponGateway; private final OfficialNoticeClient officialNoticeClient; /** @@ -152,6 +159,10 @@ public class PropsSendCommon { List exps = filterPrize(cmd, PropsActivityRewardEnum.CUSTOMIZE); sendUserExp(cmd.getAcceptUserId(), exps); + // 发放道具券 + List coupons = filterPrize(cmd, PropsActivityRewardEnum.PROP_COUPON); + sendPropCoupon(cmd.getAcceptUserId(), coupons); + saveSendLog(cmd); } @@ -165,6 +176,37 @@ public class PropsSendCommon { }); } + private void sendPropCoupon(Long acceptUserId, List coupons) { + coupons.forEach(prizeDescribe -> { + String content = prizeDescribe.getContent(); + + PropsSourceRecord propsSourceRecord = propsSourceRecordService.getById(Long.parseLong(content)); + if (propsSourceRecord != null) { + PropCouponType couponType = PropCouponType.getByName(propsSourceRecord.getType()); + if (Objects.nonNull(couponType)) { + PropCouponGrantCmd grantCmd = new PropCouponGrantCmd(); + grantCmd.setPropId(propsSourceRecord.getId().toString()); + grantCmd.setPropIcon(propsSourceRecord.getCover()); + grantCmd.setPropName(propsSourceRecord.getName()); + grantCmd.setCouponType(couponType.getCode()); + grantCmd.setValidDays(prizeDescribe.getQuantity()); + + List propCoupons = generateCoupons(grantCmd, couponType, Collections.singleton(acceptUserId)); + // 5. 批量插入券记录 + propCouponGateway.batchSave(propCoupons); + + officialNoticeClient.send( + NoticeExtTemplateCustomizeCmd.builder() + .toAccount(acceptUserId) + .noticeType(OfficialNoticeTypeEnum.RECEIVE_PROP_COUPON) + .content("You have received a Coupon. Please check it.") + .build() + ); + } + } + }); + } + public List filterPrize(PrizeDescribeRewardCmd cmd, PropsActivityRewardEnum type) { return cmd.getPrizes().stream() .filter(prizeDescribe -> type.eq(prizeDescribe.getType())) @@ -429,5 +471,76 @@ public class PropsSendCommon { ); } + /** + * 批量生成券 + */ + private List generateCoupons(PropCouponGrantCmd cmd, PropCouponType couponType, Set userIds) { + List propCoupons = new ArrayList<>(); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime expireTime = now.plusDays(cmd.getValidDays()); + + // 转换贵族等级名称为VIP等级 + String displayPropName = convertNobleNameToVipLevel(cmd.getPropName()); + for (Long userId : userIds) { + PropCoupon propCoupon = new PropCoupon(); + propCoupon.setCouponNo(generateCouponNo()); + propCoupon.setUserId(userId); + propCoupon.setCouponType(couponType); + propCoupon.setPropId(Long.parseLong(cmd.getPropId())); + propCoupon.setPropIcon(cmd.getPropIcon()); + propCoupon.setPropName(displayPropName); + propCoupon.setValidDays(cmd.getValidDays()); + propCoupon.setStatus(PropCouponStatus.UNUSED); + propCoupon.setSource(PropCouponSource.ADMIN_GRANT); + propCoupon.setExpireTime(expireTime); + propCoupon.setCreateTime(now); + propCoupon.setUpdateTime(now); + + propCoupons.add(propCoupon); + } + + return propCoupons; + } + + + /** + * 转换贵族等级名称为VIP等级 + * + * @param propName 道具名称 + * @return VIP等级名称 + */ + private String convertNobleNameToVipLevel(String propName) { + if (propName == null) { + return propName; + } + + // 贵族等级映射 + switch (propName.toUpperCase()) { + case "VISCOUNT": + return "VIP 1"; + case "EARL": + return "VIP 2"; + case "MARQUIS": + return "VIP 3"; + case "DUKE": + return "VIP 4"; + case "KING": + return "VIP 5"; + default: + return ""; + } + } + + /** + * 生成券编号(唯一) + */ + private String generateCouponNo() { + // 使用雪花算法生成唯一ID + long id = IdWorkerUtils.getId(); + // 格式: COUPON + 时间戳 + ID + return String.format("COUPON%d", id); + } + + } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/ActivityConfigService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/ActivityConfigService.java index 775d7b69..e392fdcb 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/ActivityConfigService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/ActivityConfigService.java @@ -68,4 +68,6 @@ public interface ActivityConfigService { * 查询有效i的活动配置 */ ActivityConfig getEffectiveActivityConf(ActivityConfigBackQryCmd query); + + List listRecentlyEndedActivities(String sysOrigin, Integer limit); } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/impl/ActivityConfigServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/impl/ActivityConfigServiceImpl.java index a4ee7865..4dd652d5 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/impl/ActivityConfigServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/sys/impl/ActivityConfigServiceImpl.java @@ -9,10 +9,12 @@ import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.text.StringUtils; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.time.DateUtils; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; @@ -237,4 +239,20 @@ public class ActivityConfigServiceImpl implements ActivityConfigService { , ActivityConfig.class); } + @Override + public List listRecentlyEndedActivities(String sysOrigin, Integer limit) { + Date now = TimestampUtils.now(); + + Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin) + .and("showcase").is(true) + .and("startTime").lte(now); + + return mongoTemplate.find( + Query.query(criteria) + .with(Sort.by(Sort.Order.desc("id"))) + .limit(limit != null ? limit : 100), + ActivityConfig.class + ); + } + } 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 0102c953..587f005f 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,5 +1,6 @@ import com.red.circle.OtherServiceApplication; import com.red.circle.other.app.scheduler.DailyTask; +import com.red.circle.other.app.scheduler.RankingActivityRewardTask; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,10 +13,17 @@ public class DailTaskTest { @Autowired private DailyTask dailyTask; + @Autowired + private RankingActivityRewardTask rankingActivityRewardTask; @Test public void DailTaskTest() { dailyTask. nobleDailyGoldRewardTask(); } + @Test + public void DailTaskTest2() { + rankingActivityRewardTask.sendRankingActivityReward(); + } + }