新增活动发放道具券处理

This commit is contained in:
tianfeng 2025-11-07 17:49:25 +08:00
parent 71b40bbb50
commit b1b04030af
7 changed files with 311 additions and 5 deletions

View File

@ -16,6 +16,11 @@ public enum PropsActivityRewardEnum {
*/
PROPS,
/**
* 道具券
*/
PROP_COUPON,
/**
* 金币
*/

View File

@ -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<ActivityConfigRewardsDTO> kingRewardList = activityConfigDTO.getButOneRewards();
List<RankingActivityRecord> 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<RankingActivityRecord> topUsers,
List<ActivityConfigRewardsDTO> 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;
}
/**
* 获取排行榜数据
*/

View File

@ -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<ActivityConfig> 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);
}
}
}

View File

@ -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<PrizeDescribe> exps = filterPrize(cmd, PropsActivityRewardEnum.CUSTOMIZE);
sendUserExp(cmd.getAcceptUserId(), exps);
// 发放道具券
List<PrizeDescribe> 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<PrizeDescribe> 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<PropCoupon> 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<PrizeDescribe> filterPrize(PrizeDescribeRewardCmd cmd, PropsActivityRewardEnum type) {
return cmd.getPrizes().stream()
.filter(prizeDescribe -> type.eq(prizeDescribe.getType()))
@ -429,5 +471,76 @@ public class PropsSendCommon {
);
}
/**
* 批量生成券
*/
private List<PropCoupon> generateCoupons(PropCouponGrantCmd cmd, PropCouponType couponType, Set<Long> userIds) {
List<PropCoupon> 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);
}
}

View File

@ -68,4 +68,6 @@ public interface ActivityConfigService {
* 查询有效i的活动配置
*/
ActivityConfig getEffectiveActivityConf(ActivityConfigBackQryCmd query);
List<ActivityConfig> listRecentlyEndedActivities(String sysOrigin, Integer limit);
}

View File

@ -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<ActivityConfig> 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
);
}
}

View File

@ -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();
}
}