新增发送平台红包

This commit is contained in:
tianfeng 2025-12-02 11:51:25 +08:00
parent 375f4ef2c2
commit 3b37929c03
9 changed files with 79 additions and 219 deletions

View File

@ -128,7 +128,23 @@ public enum RoomErrorCode implements IResponseErrorCode {
/**
* 房间已关闭.
*/
ROOM_CLOSE(9114, "Room close");
ROOM_CLOSE(9114, "Room close"),
/**
* 不是房间拥有者
*/
NOT_ROOM_OWNER(9115, "Not the owner of the room"),
/**
* 房间贡献奖励不存在
*/
ROOM_CONTRIBUTION_NOT_FOUND(9116, "There is no room contribution reward"),
/**
* 房间贡献奖励未达标
*/
ROOM_CONTRIBUTION_NOT_COMPLETED(9117, "The room reward has not been completed"),
;
private final int code;
private final String message;

View File

@ -45,21 +45,6 @@ public class RoomContributionActivityRestController extends BaseController {
return roomContributionActivityService.getRoomContribution(cmd);
}
/**
* 领取奖励已废弃请使用 /room-reward/receive.
*
* @deprecated 请使用新接口 /room-reward/receive
* @eo.name 领取奖励已废弃
* @eo.url /receive
* @eo.method post
* @eo.request-type json
*/
@Deprecated
@PostMapping("/receive")
public void receive(AppRoomIdCmd cmd) {
roomContributionActivityService.receive(cmd);
}
/**
* 查询房间奖励信息.
*

View File

@ -8,6 +8,7 @@ import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributio
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.inner.asserts.ActivityErrorCode;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
@ -54,7 +55,7 @@ public class ReceiveRoomRewardCmdExe {
// 2. 校验用户是否是房主
Long userRoomId = roomProfileManagerService.getRoomId(userId);
ResponseAssert.isTrue(
CommonErrorCode.DATA_ERROR,
RoomErrorCode.NOT_ROOM_OWNER,
Objects.equals(userRoomId, Long.parseLong(roomId))
);

View File

@ -4,15 +4,17 @@ import com.red.circle.other.app.dto.clientobject.activity.room.RoomRewardInfoCO;
import com.red.circle.other.app.enums.RoomRewardLevelEnum;
import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount;
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Optional;
/**
@ -50,18 +52,11 @@ public class RoomRewardInfoQryExe {
/**
* 计算到下周一沙特时间0点的倒计时
*/
private RoomRewardInfoCO.CountdownCO calculateCountdown() {
private Timestamp calculateCountdown() {
ZonedDateTime now = ZonedDateTimeAsiaRiyadhUtils.now();
ZonedDateTime nextMondayZoned = now.plusDays(7 - (now.getDayOfWeek().getValue() - 1));
Duration duration = Duration.between(now, nextMondayZoned);
RoomRewardInfoCO.CountdownCO countdown = new RoomRewardInfoCO.CountdownCO();
countdown.setDays(duration.toDays());
countdown.setHours(duration.toHours());
countdown.setMinutes(duration.toMinutes());
countdown.setSeconds(duration.getSeconds());
return countdown;
ZonedDateTime nextMonday = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
.withHour(0).withMinute(0).withSecond(0).withNano(0);
return Timestamp.from(nextMonday.toInstant());
}
/**

View File

@ -6,10 +6,12 @@ import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
import com.red.circle.external.inner.model.cmd.message.CustomGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.convertor.RoomRedPacketAppConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.RoomRedPacketCO;
import com.red.circle.other.app.dto.cmd.SendRoomRedPacketCmd;
import com.red.circle.other.app.enums.RoomRewardLevelEnum;
import com.red.circle.other.app.util.OfficialNoticeUtils;
import com.red.circle.other.domain.gateway.RoomRedPacketGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
@ -19,9 +21,13 @@ import com.red.circle.other.domain.redpacket.RoomRedPacketSourceType;
import com.red.circle.other.domain.redpacket.RoomRedPacketStatus;
import com.red.circle.other.domain.redpacket.RoomRedPacketType;
import com.red.circle.other.infra.database.cache.service.other.RoomRedPacketCacheService;
import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount;
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.inner.asserts.OtherErrorCode;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
@ -36,6 +42,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
@ -54,6 +61,7 @@ public class SendRoomRedPacketCmdExe {
private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor;
private final RoomProfileManagerService roomProfileManagerService;
private final RoomContributionActivityCountService roomContributionActivityCountService;
/**
* 执行发红包用户红包
@ -115,7 +123,7 @@ public class SendRoomRedPacketCmdExe {
// 参数校验
validateParams(cmd);
String packetId = IdWorkerUtils.getIdStr();
// 计算过期时间
@ -126,6 +134,8 @@ public class SendRoomRedPacketCmdExe {
RoomRedPacket redPacket = buildRedPacket(cmd, systemUserId, packetId, expireTime, RoomRedPacketSourceType.PLATFORM);
roomRedPacketGateway.save(redPacket);
// 查询上周数据
deductRoomContribution(cmd.getRoomId(), cmd.getTotalAmount());
// 预生成随机金额列表拼手气红包
List<Long> amounts = generateRandomAmounts(cmd.getTotalAmount(), cmd.getTotalCount());
@ -143,7 +153,7 @@ public class SendRoomRedPacketCmdExe {
);
// 发送飘窗通知平台红包
sendMessage(cmd, packetId);
// sendMessage(cmd, packetId);
log.info("发平台红包成功 packetId={} roomId={} amount={} count={} ",
packetId, cmd.getRoomId(), cmd.getTotalAmount(), cmd.getTotalCount());
@ -151,6 +161,29 @@ public class SendRoomRedPacketCmdExe {
return RoomRedPacketAppConvertor.toCO(redPacket);
}
private void deductRoomContribution(Long roomId, Long totalAmount) {
String lastWeekDateNumber = ZonedDateTimeAsiaRiyadhUtils.getLastWeekMonday().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
RoomContributionActivityCount lastWeek = findByRoomIdAndDateNumber(roomId, Integer.parseInt(lastWeekDateNumber));
ResponseAssert.notNull(RoomErrorCode.ROOM_CONTRIBUTION_NOT_FOUND, lastWeek);
Long amount = lastWeek.getContributionValue().longValue();
RoomRewardLevelEnum level = RoomRewardLevelEnum.getLevelByAmount(amount);
ResponseAssert.notNull(RoomErrorCode.ROOM_CONTRIBUTION_NOT_COMPLETED, level);
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, Objects.equals(totalAmount, level.getRewardCoins()));
boolean updated = roomContributionActivityCountService.updateRewardCoinsSent(lastWeek.getId());
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE, updated);
}
/**
* 根据房间ID和日期查询数据
*/
private RoomContributionActivityCount findByRoomIdAndDateNumber(Long roomId, Integer dateNumber) {
return roomContributionActivityCountService
.findByRoomIdAndDateNumber(roomId, dateNumber)
.orElse(null);
}
/**
* 发送消息通知
*/

View File

@ -1,142 +0,0 @@
package com.red.circle.other.app.scheduler;
import com.red.circle.component.redis.annotation.TaskCacheLock;
import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount;
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
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 lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 房间贡献周奖励定时任务
* <p>
* 沙特时间每周一 0 点执行
* </p>
*
* @author Claude
* @since 2025-10-28
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class RoomContributionWeeklyRewardTask {
private final RoomContributionActivityCountService roomContributionActivityCountService;
private final WalletGoldClient walletGoldClient;
private final RoomProfileManagerService roomProfileManagerService;
/**
* 金币奖励比例上周贡献金额的比例
*/
private static final BigDecimal REWARD_RATIO = new BigDecimal("0.05");
/**
* 沙特时间每周一 0 点执行
* cron:
* Asia/Riyadh 时区UTC+3
*/
@Scheduled(cron = "0 0 0 * * MON", zone = "Asia/Riyadh")
@TaskCacheLock(key = "ROOM_CONTRIBUTION_REWARD_TASK", expireSecond = 86400)
public void execute() {
log.info("【房间贡献周奖励】定时任务开始执行...");
try {
// 校验沙特时间 2025年10月10号之后包含才执行
int currentDate = ZonedDateTimeAsiaRiyadhUtils.nowDateToInt();
if (currentDate < 20251010) {
log.info("【房间贡献周奖励】当前日期: {}, 未到活动开始时间(20251010),跳过执行", currentDate);
return;
}
// 1. 获取所有房间上周数据
List<RoomContributionActivityCount> lastWeekDataList =
roomContributionActivityCountService.getAllRoomsLastWeekData();
if (CollectionUtils.isEmpty(lastWeekDataList)) {
log.info("【房间贡献周奖励】没有需要发放的奖励数据");
return;
}
log.info("【房间贡献周奖励】待发放奖励房间数: {}", lastWeekDataList.size());
int successCount = 0;
int failCount = 0;
// 2. 遍历发放奖励
for (RoomContributionActivityCount data : lastWeekDataList) {
try {
// 校验如果已领取跳过
if (Boolean.TRUE.equals(data.getReceived())) {
log.info("【房间贡献周奖励】数据已领取,跳过发放, roomId: {}, recordId: {}",
data.getRoomId(), data.getId());
continue;
}
// 计算奖励金币 = 上周贡献金额 × 比例
BigDecimal rewardAmount = data.getContributionValue()
.multiply(REWARD_RATIO)
.setScale(2, RoundingMode.DOWN);
// 如果奖励金额 <= 0跳过
if (rewardAmount.compareTo(BigDecimal.ZERO) <= 0) {
log.warn("【房间贡献周奖励】奖励金额为0跳过发放, roomId: {}, recordId: {}",
data.getRoomId(), data.getId());
continue;
}
// 3. 发送金币奖励需要获取房主ID
// 注意这里假设从其他地方获取房主ID你需要根据实际情况调整
Long roomOwnerId = getRoomOwnerId(data.getRoomId());
if (roomOwnerId == null) {
log.warn("【房间贡献周奖励】无法获取房主ID, roomId: {}", data.getRoomId());
failCount++;
continue;
}
walletGoldClient.changeBalance(GoldReceiptCmd.builder()
.appIncome()
.eventId(data.getRoomId())
.userId(roomOwnerId)
.origin(GoldOrigin.ROOM_CONTRIBUTION_REWARD)
.amount(rewardAmount)
.build());
// 4. 修改数据状态为已领取
roomContributionActivityCountService.updateReceivedByRoomId(data.getRoomId(), REWARD_RATIO);
successCount++;
log.info("【房间贡献周奖励】发放成功, roomId: {}, roomOwnerId: {}, rewardAmount: {}",
data.getRoomId(), roomOwnerId, rewardAmount);
} catch (Exception e) {
failCount++;
log.error("【房间贡献周奖励】发放失败, roomId: {}, error: ",
data.getRoomId(), e);
}
}
log.info("【房间贡献周奖励】定时任务执行完成, 成功: {}, 失败: {}", successCount, failCount);
} catch (Exception e) {
log.error("【房间贡献周奖励】定时任务执行异常: ", e);
}
}
/**
* 获取房主ID
*/
private Long getRoomOwnerId(Long roomId) {
return roomProfileManagerService.getHomeownerId(roomId);
}
}

View File

@ -2,6 +2,8 @@ package com.red.circle.other.app.dto.clientobject.activity.room;
import lombok.Data;
import java.sql.Timestamp;
/**
* 房间奖励信息
*
@ -14,7 +16,7 @@ public class RoomRewardInfoCO {
/**
* 倒计时信息
*/
private CountdownCO countdown;
private Timestamp countdown;
/**
* 当前周进度
@ -26,32 +28,6 @@ public class RoomRewardInfoCO {
*/
private LastWeekProgressCO lastWeekProgress;
/**
* 倒计时对象
*/
@Data
public static class CountdownCO {
/**
* 剩余天数
*/
private Long days;
/**
* 剩余小时
*/
private Long hours;
/**
* 剩余分钟
*/
private Long minutes;
/**
* 剩余秒数
*/
private Long seconds;
}
/**
* 当前周进度
*/

View File

@ -92,13 +92,10 @@ public interface RoomContributionActivityCountService {
*/
List<RoomContributionActivityCount> listRoomContributionByPH(Long roomId,BigDecimal number);
/**
* 获取房间上周贡献数据未领取状态
*
* @param roomId 房间ID
* @return 上周贡献数据
*/
RoomContributionActivityCount getLastWeekData(Long roomId);
/**
* 更新奖励金币状态为已发放
*/
boolean updateRewardCoinsSent(String id);
/**
* 获取所有房间上周贡献数据未领取状态

View File

@ -1,6 +1,7 @@
package com.red.circle.other.infra.database.mongo.service.activity.impl;
import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.mongodb.page.MongoPageHelper;
import com.red.circle.framework.dto.PageResult;
@ -280,19 +281,17 @@ public class RoomContributionActivityCountServiceImpl implements
RoomContributionActivityCount.class);
}
@Override
public RoomContributionActivityCount getLastWeekData(Long roomId) {
// 获取上周的 dateNumber当前周一 - 7
Integer lastWeekDateNumber = ZonedDateTimeAsiaRiyadhUtils.getThisMonday() - 7;
Criteria criteria = Criteria.where("roomId").is(roomId)
.and("dateNumber").is(lastWeekDateNumber)
.and("received").is(Boolean.FALSE);
return mongoTemplate.findOne(Query.query(criteria), RoomContributionActivityCount.class);
}
@Override
public boolean updateRewardCoinsSent(String id) {
Query query = new Query(Criteria.where("_id").is(id));
Update update = new Update().set("rewardCoinsSent", true);
UpdateResult result = mongoTemplate.updateFirst(query, update, RoomContributionActivityCount.class);
@Override
// 检查是否有文档被修改
return result.getModifiedCount() > 0;
}
@Override
public List<RoomContributionActivityCount> getAllRoomsLastWeekData() {
// 获取上周的 dateNumber
Integer lastWeekDateNumber = ZonedDateTimeAsiaRiyadhUtils.getThisMonday() - 7;