From bd5f8305dfe5721f988330d9c554ad7c888d739f Mon Sep 17 00:00:00 2001 From: zhx Date: Mon, 13 Jul 2026 22:58:09 +0800 Subject: [PATCH] feat: update room weekly reward ratio payout --- .../enums/message/OfficialNoticeTypeEnum.java | 5 + .../RoomContributionWeeklyRewardTask.java | 210 +++++++++++++++++- .../RoomContributionWeeklyRewardTaskTest.java | 90 +++++++- .../RoomContributionActivityCount.java | 30 +++ .../RoomContributionActivityCountService.java | 21 ++ ...mContributionActivityCountServiceImpl.java | 88 +++++++- 6 files changed, 429 insertions(+), 15 deletions(-) diff --git a/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java b/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java index b466e710..abb97d20 100644 --- a/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java +++ b/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java @@ -267,6 +267,11 @@ public enum OfficialNoticeTypeEnum { */ COIN_SELLER_COINS_RECEIVED, + /** + * 房间流水奖励金币. + */ + ROOM_CONTRIBUTION_REWARD, + ; diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTask.java index 74d6dbbd..7c187a53 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTask.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTask.java @@ -2,8 +2,12 @@ package com.red.circle.other.app.scheduler; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.component.redis.annotation.TaskCacheLock; +import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; +import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateCustomizeCmd; +import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; import com.red.circle.framework.dto.ResultResponse; import com.red.circle.other.app.enums.RoomRewardLevelEnum; +import com.red.circle.other.infra.constant.RegionConstants; import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount; import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService; @@ -15,9 +19,12 @@ import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO; import com.red.circle.wallet.inner.model.enums.GoldOrigin; import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -25,6 +32,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -42,10 +51,13 @@ public class RoomContributionWeeklyRewardTask { * wallet_gold_asset_record.event_id is varchar(50), keep the event id short. */ private static final String OWNER_REWARD_EVENT_PREFIX = "RCWR:"; - private static final BigDecimal DEFAULT_MIN_CONTRIBUTION = BigDecimal.valueOf(100000); - private static final BigDecimal TARAB_MIN_CONTRIBUTION = BigDecimal.valueOf(150000); + private static final String OWNER_REWARD_SUPPLEMENT_EVENT_PREFIX = "RCWRS:"; + private static final BigDecimal MIN_RATIO_REWARD_CONTRIBUTION = BigDecimal.valueOf(5000000); + private static final BigDecimal DEFAULT_REWARD_RATIO = new BigDecimal("0.10"); + private static final BigDecimal LOW_REWARD_RATIO = new BigDecimal("0.05"); private final WalletGoldClient walletGoldClient; + private final OfficialNoticeClient officialNoticeClient; private final RoomProfileManagerService roomProfileManagerService; private final RoomContributionActivityCountService roomContributionActivityCountService; @@ -55,6 +67,18 @@ public class RoomContributionWeeklyRewardTask { @Value("${activity.room-contribution.weekly-reward.sys-origins:ATYOU}") private String enabledSysOrigins; + @Value("${activity.room-contribution.weekly-reward.ratio-supplement-enabled:true}") + private Boolean ratioSupplementEnabled; + + @Value("${activity.room-contribution.weekly-reward.ratio-supplement-start-date-number:0}") + private Integer ratioSupplementStartDateNumber; + + @Value("${activity.room-contribution.weekly-reward.ratio-supplement-end-date-number:0}") + private Integer ratioSupplementEndDateNumber; + + @Value("${activity.room-contribution.weekly-reward.run-repair-on-startup:false}") + private Boolean runRepairOnStartup; + /** * 每周一沙特时间 00:08 发送上周房间周流水奖励. */ @@ -76,6 +100,20 @@ public class RoomContributionWeeklyRewardTask { return; } sendLastWeekRoomContributionRewards("repair"); + sendOwnerRewardRatioSupplements("repair"); + } + + /** + * 线上临时补偿入口:配置打开后服务启动执行一次 repair,执行完应关闭配置。 + */ + @EventListener(ApplicationReadyEvent.class) + @TaskCacheLock(key = "ROOM_CONTRIBUTION_WEEKLY_REWARD_STARTUP_REPAIR", expireSecond = 600) + public void repairLastWeekRoomContributionRewardsOnStartup() { + if (!Boolean.TRUE.equals(runRepairOnStartup)) { + return; + } + log.warn("[RoomContributionWeeklyOwnerReward] startup repair enabled"); + repairLastWeekRoomContributionRewards(); } private void sendLastWeekRoomContributionRewards(String trigger) { @@ -132,9 +170,8 @@ public class RoomContributionWeeklyRewardTask { return false; } - RoomRewardLevelEnum level = RoomRewardLevelEnum - .getLevelByAmount(data.getContributionValue().longValue()); - if (Objects.isNull(level)) { + Long rewardCoins = calculateRatioRewardCoins(room, sysOrigin, data.getContributionValue()); + if (rewardCoins <= 0) { return false; } @@ -147,11 +184,16 @@ public class RoomContributionWeeklyRewardTask { try { if (!ownerRewardEventExists(eventId)) { - addOwnerGold(room.getUserId(), sysOrigin, level.getRewardCoins(), eventId); + addOwnerGold(room.getUserId(), sysOrigin, rewardCoins, eventId); } + sendOwnerRewardNotice(room.getUserId(), rewardCoins); if (!roomContributionActivityCountService.markOwnerRewardCoinsSent(data.getId(), eventId)) { log.warn("[RoomContributionWeeklyOwnerReward] mark sent skipped roomId={} ownerId={} dataId={} eventId={}", data.getRoomId(), room.getUserId(), data.getId(), eventId); + } else { + // 新发记录已经按比例全额发放,直接关闭补差入口,避免 repair 重复补发。 + roomContributionActivityCountService.markOwnerRewardRatioSupplementSent(data.getId(), + eventId); } } catch (Exception e) { roomContributionActivityCountService.resetOwnerRewardCoinsSending(data.getId(), eventId, @@ -159,16 +201,119 @@ public class RoomContributionWeeklyRewardTask { throw e; } - log.info("[RoomContributionWeeklyOwnerReward] sent roomId={} ownerId={} dataId={} level={} rewardCoins={} eventId={}", - data.getRoomId(), room.getUserId(), data.getId(), level.getLevel(), level.getRewardCoins(), + log.info("[RoomContributionWeeklyOwnerReward] sent roomId={} ownerId={} dataId={} rewardCoins={} eventId={}", + data.getRoomId(), room.getUserId(), data.getId(), rewardCoins, eventId); return true; } + private void sendOwnerRewardRatioSupplements(String trigger) { + if (!Boolean.TRUE.equals(ratioSupplementEnabled)) { + return; + } + Integer lastWeekDateNumber = getLastWeekDateNumber(); + Integer startDateNumber = Objects.nonNull(ratioSupplementStartDateNumber) + && ratioSupplementStartDateNumber > 0 ? ratioSupplementStartDateNumber : lastWeekDateNumber; + Integer endDateNumber = Objects.nonNull(ratioSupplementEndDateNumber) + && ratioSupplementEndDateNumber > 0 ? ratioSupplementEndDateNumber : lastWeekDateNumber; + + log.warn("[RoomContributionWeeklyOwnerRewardSupplement] start trigger={} startDateNumber={} endDateNumber={}", + trigger, startDateNumber, endDateNumber); + List list = + roomContributionActivityCountService.listOwnerRewardRatioSupplementData(startDateNumber, + endDateNumber); + if (CollectionUtils.isEmpty(list)) { + log.warn("[RoomContributionWeeklyOwnerRewardSupplement] no supplement data trigger={}", + trigger); + return; + } + + int success = 0; + int fail = 0; + for (RoomContributionActivityCount data : list) { + try { + if (sendSupplementReward(data)) { + success++; + } + } catch (Exception e) { + fail++; + log.error("[RoomContributionWeeklyOwnerRewardSupplement] send failed roomId={} dataId={} amount={}", + data.getRoomId(), data.getId(), data.getContributionValue(), e); + } + } + + log.warn("[RoomContributionWeeklyOwnerRewardSupplement] end trigger={} total={} success={} fail={}", + trigger, list.size(), success, fail); + } + + private boolean sendSupplementReward(RoomContributionActivityCount data) { + if (Objects.isNull(data) || Objects.isNull(data.getRoomId()) + || Objects.isNull(data.getContributionValue())) { + return false; + } + + RoomProfileManager room = roomProfileManagerService.getById(data.getRoomId()); + SysOriginPlatformEnum sysOrigin = getSysOrigin(room); + if (Objects.isNull(room) || Objects.isNull(room.getUserId()) || Objects.isNull(sysOrigin)) { + log.warn("[RoomContributionWeeklyOwnerRewardSupplement] skip invalid room roomId={} dataId={}", + data.getRoomId(), data.getId()); + return false; + } + if (!getEnabledSysOrigins().contains(sysOrigin) + || !meetsPlatformThreshold(sysOrigin, data.getContributionValue())) { + return false; + } + + // 已经按旧档位直发过的记录,只补“新比例规则 - 旧档位金额”的差额。 + Long shouldRewardCoins = calculateRatioRewardCoins(room, sysOrigin, + data.getContributionValue()); + Long oldLevelRewardCoins = calculateOldLevelRewardCoins(data.getContributionValue()); + Long supplementCoins = shouldRewardCoins - oldLevelRewardCoins; + String eventId = buildOwnerRewardSupplementEventId(data.getId()); + if (!roomContributionActivityCountService.markOwnerRewardRatioSupplementSending(data.getId(), + eventId)) { + log.warn("[RoomContributionWeeklyOwnerRewardSupplement] skip sending occupied roomId={} dataId={} eventId={}", + data.getRoomId(), data.getId(), eventId); + return false; + } + + try { + if (supplementCoins > 0 && !ownerRewardEventExists(eventId)) { + addOwnerGold(room.getUserId(), sysOrigin, supplementCoins, eventId); + } + if (supplementCoins > 0) { + sendOwnerRewardNotice(room.getUserId(), supplementCoins); + } + if (!roomContributionActivityCountService.markOwnerRewardRatioSupplementSent(data.getId(), + eventId)) { + log.warn("[RoomContributionWeeklyOwnerRewardSupplement] mark sent skipped roomId={} ownerId={} dataId={} eventId={}", + data.getRoomId(), room.getUserId(), data.getId(), eventId); + } + } catch (Exception e) { + roomContributionActivityCountService.resetOwnerRewardRatioSupplementSending(data.getId(), + eventId, e.getMessage()); + throw e; + } + + log.info("[RoomContributionWeeklyOwnerRewardSupplement] sent roomId={} ownerId={} dataId={} shouldRewardCoins={} oldLevelRewardCoins={} supplementCoins={} eventId={}", + data.getRoomId(), room.getUserId(), data.getId(), shouldRewardCoins, oldLevelRewardCoins, + Math.max(supplementCoins, 0L), eventId); + return true; + } + private String buildOwnerRewardEventId(String dataId) { return OWNER_REWARD_EVENT_PREFIX + dataId; } + private String buildOwnerRewardSupplementEventId(String dataId) { + return OWNER_REWARD_SUPPLEMENT_EVENT_PREFIX + dataId; + } + + private Integer getLastWeekDateNumber() { + return Integer.valueOf(com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils + .getLastWeekMonday().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); + } + private boolean ownerRewardEventExists(String eventId) { ResultResponse response = walletGoldClient.existsEventId(eventId); if (Objects.isNull(response) || !response.checkSuccess()) { @@ -194,6 +339,21 @@ public class RoomContributionWeeklyRewardTask { } } + private void sendOwnerRewardNotice(Long userId, Long rewardCoins) { + ResultResponse response = officialNoticeClient.send( + NoticeExtTemplateCustomizeCmd.builder() + .toAccount(userId) + .noticeType(OfficialNoticeTypeEnum.ROOM_CONTRIBUTION_REWARD) + .title("Room Rewards") + .content(String.format(Locale.ENGLISH, "You get room rewards %,d coins", + rewardCoins)) + .build() + ); + if (Objects.isNull(response) || !response.checkSuccess()) { + throw new IllegalStateException("send owner reward notice failed: " + userId); + } + } + private SysOriginPlatformEnum getSysOrigin(RoomProfileManager room) { if (Objects.isNull(room) || Objects.isNull(room.getSysOrigin())) { return null; @@ -206,9 +366,37 @@ public class RoomContributionWeeklyRewardTask { } private boolean meetsPlatformThreshold(SysOriginPlatformEnum sysOrigin, BigDecimal contribution) { - BigDecimal minContribution = Objects.equals(sysOrigin, SysOriginPlatformEnum.TARAB) - ? TARAB_MIN_CONTRIBUTION : DEFAULT_MIN_CONTRIBUTION; - return contribution.compareTo(minContribution) >= 0; + return Objects.nonNull(contribution) + && contribution.compareTo(MIN_RATIO_REWARD_CONTRIBUTION) >= 0; + } + + private Long calculateRatioRewardCoins(RoomProfileManager room, SysOriginPlatformEnum sysOrigin, + BigDecimal contribution) { + if (!meetsPlatformThreshold(sysOrigin, contribution)) { + return 0L; + } + // 新规则按房间流水比例发放;阿语区和土耳其按5%,其它地区按10%. + return contribution.multiply(getRewardRatio(room, sysOrigin)) + .setScale(0, RoundingMode.DOWN) + .longValue(); + } + + private Long calculateOldLevelRewardCoins(BigDecimal contribution) { + RoomRewardLevelEnum level = RoomRewardLevelEnum.getLevelByAmount(contribution.longValue()); + return Objects.nonNull(level) ? level.getRewardCoins() : 0L; + } + + private BigDecimal getRewardRatio(RoomProfileManager room, SysOriginPlatformEnum sysOrigin) { + if (Objects.equals(sysOrigin, SysOriginPlatformEnum.TARAB)) { + return LOW_REWARD_RATIO; + } + String countryCode = Objects.nonNull(room) && Objects.nonNull(room.getCountryCode()) + ? room.getCountryCode().trim().toUpperCase(Locale.ROOT) : ""; + if (RegionConstants.AR_REGIONS.contains(countryCode) + || RegionConstants.TR_REGIONS.contains(countryCode)) { + return LOW_REWARD_RATIO; + } + return DEFAULT_REWARD_RATIO; } private Set getEnabledSysOrigins() { diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTaskTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTaskTest.java index f14e3fa5..63ddcc54 100644 --- a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTaskTest.java +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/RoomContributionWeeklyRewardTaskTest.java @@ -8,6 +8,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; +import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateCustomizeCmd; import com.red.circle.framework.dto.ResultResponse; import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount; import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; @@ -32,12 +34,15 @@ class RoomContributionWeeklyRewardTaskTest { private static final String DATA_ID = "2071532202565136385_20260629"; private static final String EVENT_ID = "RCWR:" + DATA_ID; + private static final String SUPPLEMENT_EVENT_ID = "RCWRS:" + DATA_ID; private static final Long ROOM_ID = 2071532202565136385L; private static final Long OWNER_ID = 2071520380357021697L; @Mock private WalletGoldClient walletGoldClient; @Mock + private OfficialNoticeClient officialNoticeClient; + @Mock private RoomProfileManagerService roomProfileManagerService; @Mock private RoomContributionActivityCountService roomContributionActivityCountService; @@ -46,9 +51,13 @@ class RoomContributionWeeklyRewardTaskTest { @BeforeEach void setUp() { - task = new RoomContributionWeeklyRewardTask(walletGoldClient, roomProfileManagerService, - roomContributionActivityCountService); + task = new RoomContributionWeeklyRewardTask(walletGoldClient, officialNoticeClient, + roomProfileManagerService, roomContributionActivityCountService); ReflectionTestUtils.setField(task, "enabledSysOrigins", "ATYOU"); + ReflectionTestUtils.setField(task, "repairEnabled", Boolean.TRUE); + ReflectionTestUtils.setField(task, "ratioSupplementEnabled", Boolean.TRUE); + ReflectionTestUtils.setField(task, "ratioSupplementStartDateNumber", 20260629); + ReflectionTestUtils.setField(task, "ratioSupplementEndDateNumber", 20260629); } @Test @@ -61,8 +70,12 @@ class RoomContributionWeeklyRewardTaskTest { when(walletGoldClient.existsEventId(EVENT_ID)).thenReturn(ResultResponse.success(false)); when(walletGoldClient.changeBalance(any(GoldReceiptCmd.class))) .thenReturn(ResultResponse.success(new WalletReceiptResDTO())); + when(officialNoticeClient.send(any(NoticeExtTemplateCustomizeCmd.class))) + .thenReturn(ResultResponse.success()); when(roomContributionActivityCountService.markOwnerRewardCoinsSent(DATA_ID, EVENT_ID)) .thenReturn(true); + when(roomContributionActivityCountService.markOwnerRewardRatioSupplementSent(DATA_ID, EVENT_ID)) + .thenReturn(true); task.sendLastWeekRoomContributionRewards(); @@ -74,8 +87,15 @@ class RoomContributionWeeklyRewardTaskTest { assertEquals(EVENT_ID, cmd.getEventId()); assertTrue(cmd.getEventId().length() <= 50); assertEquals(GoldOrigin.ROOM_CONTRIBUTION_REWARD, cmd.getOrigin()); - assertEquals(25000000L, cmd.getAmount().getPennyAmount()); + assertEquals(207777000L, cmd.getAmount().getPennyAmount()); + + ArgumentCaptor noticeCaptor = + ArgumentCaptor.forClass(NoticeExtTemplateCustomizeCmd.class); + verify(officialNoticeClient).send(noticeCaptor.capture()); + assertEquals("You get room rewards 2,077,770 coins", noticeCaptor.getValue().getContent()); verify(roomContributionActivityCountService).markOwnerRewardCoinsSent(DATA_ID, EVENT_ID); + verify(roomContributionActivityCountService).markOwnerRewardRatioSupplementSent(DATA_ID, + EVENT_ID); } @Test @@ -86,15 +106,79 @@ class RoomContributionWeeklyRewardTaskTest { when(roomContributionActivityCountService.markOwnerRewardCoinsSending(DATA_ID, EVENT_ID)) .thenReturn(true); when(walletGoldClient.existsEventId(EVENT_ID)).thenReturn(ResultResponse.success(true)); + when(officialNoticeClient.send(any(NoticeExtTemplateCustomizeCmd.class))) + .thenReturn(ResultResponse.success()); when(roomContributionActivityCountService.markOwnerRewardCoinsSent(DATA_ID, EVENT_ID)) .thenReturn(true); + when(roomContributionActivityCountService.markOwnerRewardRatioSupplementSent(DATA_ID, EVENT_ID)) + .thenReturn(true); task.sendLastWeekRoomContributionRewards(); verify(walletGoldClient, never()).changeBalance(any(GoldReceiptCmd.class)); + verify(officialNoticeClient).send(any(NoticeExtTemplateCustomizeCmd.class)); verify(roomContributionActivityCountService).markOwnerRewardCoinsSent(DATA_ID, EVENT_ID); } + @Test + void shouldUseFivePercentForArabicOrTurkeyRooms() { + RoomProfileManager room = room(); + room.setCountryCode("AR"); + when(roomContributionActivityCountService.listLastWeekOwnerRewardUnsentData()) + .thenReturn(List.of(activityCount())); + when(roomProfileManagerService.getById(ROOM_ID)).thenReturn(room); + when(roomContributionActivityCountService.markOwnerRewardCoinsSending(DATA_ID, EVENT_ID)) + .thenReturn(true); + when(walletGoldClient.existsEventId(EVENT_ID)).thenReturn(ResultResponse.success(false)); + when(walletGoldClient.changeBalance(any(GoldReceiptCmd.class))) + .thenReturn(ResultResponse.success(new WalletReceiptResDTO())); + when(officialNoticeClient.send(any(NoticeExtTemplateCustomizeCmd.class))) + .thenReturn(ResultResponse.success()); + when(roomContributionActivityCountService.markOwnerRewardCoinsSent(DATA_ID, EVENT_ID)) + .thenReturn(true); + when(roomContributionActivityCountService.markOwnerRewardRatioSupplementSent(DATA_ID, EVENT_ID)) + .thenReturn(true); + + task.sendLastWeekRoomContributionRewards(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(GoldReceiptCmd.class); + verify(walletGoldClient).changeBalance(captor.capture()); + assertEquals(103888500L, captor.getValue().getAmount().getPennyAmount()); + } + + @Test + void shouldSupplementOldLevelRewardDifference() { + when(roomContributionActivityCountService.listLastWeekOwnerRewardUnsentData()) + .thenReturn(List.of()); + when(roomContributionActivityCountService.listOwnerRewardRatioSupplementData(20260629, + 20260629)).thenReturn(List.of(activityCount())); + when(roomProfileManagerService.getById(ROOM_ID)).thenReturn(room()); + when(roomContributionActivityCountService.markOwnerRewardRatioSupplementSending(DATA_ID, + SUPPLEMENT_EVENT_ID)).thenReturn(true); + when(walletGoldClient.existsEventId(SUPPLEMENT_EVENT_ID)).thenReturn(ResultResponse.success(false)); + when(walletGoldClient.changeBalance(any(GoldReceiptCmd.class))) + .thenReturn(ResultResponse.success(new WalletReceiptResDTO())); + when(officialNoticeClient.send(any(NoticeExtTemplateCustomizeCmd.class))) + .thenReturn(ResultResponse.success()); + when(roomContributionActivityCountService.markOwnerRewardRatioSupplementSent(DATA_ID, + SUPPLEMENT_EVENT_ID)).thenReturn(true); + + task.repairLastWeekRoomContributionRewards(); + + ArgumentCaptor goldCaptor = ArgumentCaptor.forClass(GoldReceiptCmd.class); + verify(walletGoldClient).changeBalance(goldCaptor.capture()); + GoldReceiptCmd cmd = goldCaptor.getValue(); + assertEquals(SUPPLEMENT_EVENT_ID, cmd.getEventId()); + assertEquals(182777000L, cmd.getAmount().getPennyAmount()); + + ArgumentCaptor noticeCaptor = + ArgumentCaptor.forClass(NoticeExtTemplateCustomizeCmd.class); + verify(officialNoticeClient).send(noticeCaptor.capture()); + assertEquals("You get room rewards 1,827,770 coins", noticeCaptor.getValue().getContent()); + verify(roomContributionActivityCountService).markOwnerRewardRatioSupplementSent(DATA_ID, + SUPPLEMENT_EVENT_ID); + } + private RoomContributionActivityCount activityCount() { RoomContributionActivityCount count = new RoomContributionActivityCount(); count.setId(DATA_ID); diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/activity/RoomContributionActivityCount.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/activity/RoomContributionActivityCount.java index 4d88ad95..8244a587 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/activity/RoomContributionActivityCount.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/activity/RoomContributionActivityCount.java @@ -117,6 +117,36 @@ public class RoomContributionActivityCount extends Convert implements Serializab */ String ownerRewardCoinsSendError; + /** + * 房主奖励金币比例规则补差是否已处理. + */ + Boolean ownerRewardRatioSupplementSent; + + /** + * 房主奖励金币比例规则补差是否处理中. + */ + Boolean ownerRewardRatioSupplementSending; + + /** + * 房主奖励金币比例规则补差事件ID. + */ + String ownerRewardRatioSupplementEventId; + + /** + * 房主奖励金币比例规则补差处理时间. + */ + Timestamp ownerRewardRatioSupplementSentTime; + + /** + * 房主奖励金币比例规则补差处理中时间. + */ + Timestamp ownerRewardRatioSupplementSendingTime; + + /** + * 房主奖励金币比例规则补差失败原因. + */ + String ownerRewardRatioSupplementSendError; + /** * 比例. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/RoomContributionActivityCountService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/RoomContributionActivityCountService.java index 55688741..cec34f43 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/RoomContributionActivityCountService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/RoomContributionActivityCountService.java @@ -133,6 +133,12 @@ public interface RoomContributionActivityCountService { */ List listLastWeekOwnerRewardUnsentData(); + /** + * 获取已按旧档位直发、但未按比例规则补差的房间贡献数据. + */ + List listOwnerRewardRatioSupplementData(Integer startDateNumber, + Integer endDateNumber); + /** * 抢占房主奖励金币直发记录. */ @@ -148,6 +154,21 @@ public interface RoomContributionActivityCountService { */ void resetOwnerRewardCoinsSending(String id, String eventId, String reason); + /** + * 抢占房主奖励金币比例补差记录. + */ + boolean markOwnerRewardRatioSupplementSending(String id, String eventId); + + /** + * 标记房主奖励金币比例补差已处理. + */ + boolean markOwnerRewardRatioSupplementSent(String id, String eventId); + + /** + * 释放房主奖励金币比例补差处理中状态,等待重试. + */ + void resetOwnerRewardRatioSupplementSending(String id, String eventId, String reason); + /** * 根据房间ID和日期查询数据 * diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/impl/RoomContributionActivityCountServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/impl/RoomContributionActivityCountServiceImpl.java index 8e0b9e4d..232ee854 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/impl/RoomContributionActivityCountServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/activity/impl/RoomContributionActivityCountServiceImpl.java @@ -46,6 +46,7 @@ public class RoomContributionActivityCountServiceImpl implements private final MongoPageHelper mongoPageHelper; private static final BigDecimal integralMin = BigDecimal.valueOf(100000); private static final BigDecimal otherIntegralMin = BigDecimal.valueOf(150000); + private static final BigDecimal ownerRewardMinContribution = BigDecimal.valueOf(5000000); private static final long REWARD_SENDING_EXPIRE_MILLIS = 2 * 60 * 1000L; private Criteria getUniquenessCriteria(Long roomId, Integer dateNumber) { @@ -363,7 +364,7 @@ public class RoomContributionActivityCountServiceImpl implements public List listLastWeekOwnerRewardUnsentData() { Criteria criteria = new Criteria().andOperator( Criteria.where("dateNumber").is(getLastWeekDateNumber()), - Criteria.where("contributionValue").gte(integralMin), + Criteria.where("contributionValue").gte(ownerRewardMinContribution), unsentOwnerRewardCriteria(), ownerRewardNotSendingOrExpiredCriteria() ); @@ -371,6 +372,25 @@ public class RoomContributionActivityCountServiceImpl implements return mongoTemplate.find(Query.query(criteria), RoomContributionActivityCount.class); } + @Override + public List listOwnerRewardRatioSupplementData( + Integer startDateNumber, Integer endDateNumber) { + Criteria dateCriteria = Criteria.where("dateNumber").gte(startDateNumber); + if (Objects.nonNull(endDateNumber)) { + dateCriteria.lte(endDateNumber); + } + + Criteria criteria = new Criteria().andOperator( + dateCriteria, + Criteria.where("contributionValue").gte(ownerRewardMinContribution), + Criteria.where("ownerRewardCoinsSent").is(Boolean.TRUE), + unsentOwnerRewardRatioSupplementCriteria(), + ownerRewardRatioSupplementNotSendingOrExpiredCriteria() + ); + + return mongoTemplate.find(Query.query(criteria), RoomContributionActivityCount.class); + } + @Override public boolean markOwnerRewardCoinsSending(String id, String eventId) { Query query = Query.query(new Criteria().andOperator( @@ -415,6 +435,56 @@ public class RoomContributionActivityCountServiceImpl implements mongoTemplate.updateFirst(query, update, RoomContributionActivityCount.class); } + @Override + public boolean markOwnerRewardRatioSupplementSending(String id, String eventId) { + Query query = Query.query(new Criteria().andOperator( + Criteria.where("_id").is(id), + unsentOwnerRewardRatioSupplementCriteria(), + ownerRewardRatioSupplementNotSendingOrExpiredCriteria() + )); + Update update = new Update() + .set("ownerRewardRatioSupplementSending", Boolean.TRUE) + .set("ownerRewardRatioSupplementEventId", eventId) + .set("ownerRewardRatioSupplementSendingTime", TimestampUtils.now()) + .unset("ownerRewardRatioSupplementSendError"); + UpdateResult result = mongoTemplate.updateFirst(query, update, + RoomContributionActivityCount.class); + return result.getModifiedCount() > 0; + } + + @Override + public boolean markOwnerRewardRatioSupplementSent(String id, String eventId) { + Query query = Query.query(new Criteria().andOperator( + Criteria.where("_id").is(id), + new Criteria().orOperator( + Criteria.where("ownerRewardRatioSupplementEventId").exists(false), + Criteria.where("ownerRewardRatioSupplementEventId").is(eventId) + ) + )); + Update update = new Update() + .set("ownerRewardRatioSupplementSent", Boolean.TRUE) + .set("ownerRewardRatioSupplementSending", Boolean.FALSE) + .set("ownerRewardRatioSupplementEventId", eventId) + .set("ownerRewardRatioSupplementSentTime", TimestampUtils.now()) + .unset("ownerRewardRatioSupplementSendError"); + UpdateResult result = mongoTemplate.updateFirst(query, update, + RoomContributionActivityCount.class); + return result.getModifiedCount() > 0; + } + + @Override + public void resetOwnerRewardRatioSupplementSending(String id, String eventId, String reason) { + Query query = Query.query(new Criteria().andOperator( + Criteria.where("_id").is(id), + Criteria.where("ownerRewardRatioSupplementEventId").is(eventId), + unsentOwnerRewardRatioSupplementCriteria() + )); + Update update = new Update() + .set("ownerRewardRatioSupplementSending", Boolean.FALSE) + .set("ownerRewardRatioSupplementSendError", limitErrorReason(reason)); + mongoTemplate.updateFirst(query, update, RoomContributionActivityCount.class); + } + @Override public Optional findByRoomIdAndDateNumber(Long roomId, Integer dateNumber) { Criteria criteria = Criteria.where("roomId").is(roomId) @@ -465,6 +535,22 @@ public class RoomContributionActivityCountServiceImpl implements ); } + private Criteria unsentOwnerRewardRatioSupplementCriteria() { + return new Criteria().orOperator( + Criteria.where("ownerRewardRatioSupplementSent").exists(false), + Criteria.where("ownerRewardRatioSupplementSent").ne(Boolean.TRUE) + ); + } + + private Criteria ownerRewardRatioSupplementNotSendingOrExpiredCriteria() { + return new Criteria().orOperator( + Criteria.where("ownerRewardRatioSupplementSending").exists(false), + Criteria.where("ownerRewardRatioSupplementSending").ne(Boolean.TRUE), + Criteria.where("ownerRewardRatioSupplementSendingTime") + .lt(new java.sql.Timestamp(System.currentTimeMillis() - REWARD_SENDING_EXPIRE_MILLIS)) + ); + } + private String limitErrorReason(String reason) { if (StringUtils.isBlank(reason) || reason.length() <= 500) { return reason;