From 0104ad51002999216d8589492eb830ca407f1c52 Mon Sep 17 00:00:00 2001 From: zhx Date: Tue, 14 Jul 2026 20:29:50 +0800 Subject: [PATCH] feat(console): add reward grant records --- .../sql/20260714_reward_grant_record_menu.sql | 119 ++++++++++++ .../sql/20260714_reward_grant_record_menu.sql | 119 ++++++++++++ ...oomContributionActivityCountClientApi.java | 10 + ...oomContributionRewardRecordPageQryCmd.java | 33 ++++ .../RoomContributionActivityCountDTO.java | 15 ++ .../wallet/api/WalletGoldClientApi.java | 8 + .../callback/WalletGoldClientCallback.java | 8 + .../RewardGrantRecordRestController.java | 30 +++ .../RewardGrantRecordServiceImpl.java | 181 ++++++++++++++++++ .../activity/RewardGrantRecordCO.java | 32 ++++ .../app/activity/RewardGrantRecordQryCmd.java | 30 +++ .../app/dto/enums/RewardGrantTypeEnum.java | 12 ++ .../activity/RewardGrantRecordService.java | 16 ++ .../RoomContributionActivityCountService.java | 7 + ...mContributionActivityCountServiceImpl.java | 13 ++ ...ntributionActivityCountClientEndpoint.java | 8 + ...ontributionActivityCountClientService.java | 7 + ...ibutionActivityCountClientServiceImpl.java | 9 + .../service/WalletGoldAssetRecordService.java | 6 + .../WalletGoldAssetRecordServiceImpl.java | 19 ++ .../wallet/WalletGoldClientEndpoint.java | 6 + .../wallet/WalletGoldClientService.java | 6 + .../impl/WalletGoldClientServiceImpl.java | 7 + 23 files changed, 701 insertions(+) create mode 100644 .deploy/prod-deploy/sql/20260714_reward_grant_record_menu.sql create mode 100644 .deploy/test-deploy/sql/20260714_reward_grant_record_menu.sql create mode 100644 rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/activity/RoomContributionRewardRecordPageQryCmd.java create mode 100644 rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/activity/RewardGrantRecordRestController.java create mode 100644 rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordServiceImpl.java create mode 100644 rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/activity/RewardGrantRecordCO.java create mode 100644 rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/cmd/app/activity/RewardGrantRecordQryCmd.java create mode 100644 rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/enums/RewardGrantTypeEnum.java create mode 100644 rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordService.java diff --git a/.deploy/prod-deploy/sql/20260714_reward_grant_record_menu.sql b/.deploy/prod-deploy/sql/20260714_reward_grant_record_menu.sql new file mode 100644 index 00000000..21be84b2 --- /dev/null +++ b/.deploy/prod-deploy/sql/20260714_reward_grant_record_menu.sql @@ -0,0 +1,119 @@ +-- Webconsole reward grant record menu and list API permission. +-- The menu follows the existing room contribution page and inherits only its roles. + +SET NAMES utf8mb4; + +INSERT INTO sys_resource (id, resource_name, mapping, method, auth_type, perm, update_time) +VALUES ( + 'activity:reward-grant-record:page', + '奖励发放记录列表', + '/reward-grant-record/page', + 'GET', + 3, + 'activity:reward-grant-record:page', + NOW() +) +ON DUPLICATE KEY UPDATE + resource_name = VALUES(resource_name), + mapping = VALUES(mapping), + method = VALUES(method), + auth_type = VALUES(auth_type), + perm = VALUES(perm), + update_time = NOW(); + +-- 关键逻辑:优先使用现有“房间支持活动”的父目录,确保新页面与活动记录同级。 +INSERT INTO sys_menu ( + parent_id, + menu_name, + path, + router, + menu_type, + icon, + alias, + sort, + status, + create_user, + update_user, + create_time, + update_time +) +SELECT + source_menu.parent_id, + '奖励发放记录', + 'activity/reward-grant-record/index', + 'activity/reward-grant-record', + 2, + 'form', + 'activity:reward-grant-record', + source_menu.sort - 1, + 0, + '0', + '0', + NOW(), + NOW() +FROM sys_menu source_menu +LEFT JOIN sys_menu existing ON existing.alias = 'activity:reward-grant-record' +WHERE ( + source_menu.router = 'activity/room/contribution' + OR source_menu.router = '/activity/room/contribution' + OR source_menu.path = 'activity/room-contribution-list/index' + OR source_menu.menu_name = '房间支持活动' + ) + AND source_menu.menu_type = 2 + AND existing.id IS NULL +LIMIT 1; + +-- 关键逻辑:迁移可重复执行,已存在菜单时仍同步页面路径和所属目录。 +UPDATE sys_menu reward_menu +JOIN ( + SELECT parent_id, sort + FROM sys_menu + WHERE ( + router = 'activity/room/contribution' + OR router = '/activity/room/contribution' + OR path = 'activity/room-contribution-list/index' + OR menu_name = '房间支持活动' + ) + AND menu_type = 2 + LIMIT 1 +) source_menu ON 1 = 1 +SET + reward_menu.parent_id = source_menu.parent_id, + reward_menu.menu_name = '奖励发放记录', + reward_menu.path = 'activity/reward-grant-record/index', + reward_menu.router = 'activity/reward-grant-record', + reward_menu.menu_type = 2, + reward_menu.icon = 'form', + reward_menu.sort = source_menu.sort - 1, + reward_menu.status = 0, + reward_menu.update_user = '0', + reward_menu.update_time = NOW() +WHERE reward_menu.alias = 'activity:reward-grant-record'; + +INSERT INTO sys_menu_resource (menu_id, resource_id) +SELECT menu.id, resource.id +FROM sys_menu menu +JOIN sys_resource resource ON resource.id = 'activity:reward-grant-record:page' +LEFT JOIN sys_menu_resource existing + ON existing.menu_id = menu.id + AND existing.resource_id = resource.id +WHERE menu.alias = 'activity:reward-grant-record' + AND existing.id IS NULL; + +-- 关键逻辑:仅继承已有房间活动记录的角色,避免扩大后台数据可见范围。 +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT DISTINCT source_role.role_id, reward_menu.id +FROM sys_role_menu source_role +JOIN sys_menu source_menu ON source_menu.id = source_role.menu_id +JOIN sys_menu reward_menu ON reward_menu.alias = 'activity:reward-grant-record' +LEFT JOIN sys_role_menu existing + ON existing.role_id = source_role.role_id + AND existing.menu_id = reward_menu.id +WHERE ( + source_menu.router = 'activity/room/contribution' + OR source_menu.router = '/activity/room/contribution' + OR source_menu.path = 'activity/room-contribution-list/index' + OR source_menu.menu_name = '房间支持活动' + ) + AND source_menu.menu_type = 2 + AND existing.id IS NULL; diff --git a/.deploy/test-deploy/sql/20260714_reward_grant_record_menu.sql b/.deploy/test-deploy/sql/20260714_reward_grant_record_menu.sql new file mode 100644 index 00000000..21be84b2 --- /dev/null +++ b/.deploy/test-deploy/sql/20260714_reward_grant_record_menu.sql @@ -0,0 +1,119 @@ +-- Webconsole reward grant record menu and list API permission. +-- The menu follows the existing room contribution page and inherits only its roles. + +SET NAMES utf8mb4; + +INSERT INTO sys_resource (id, resource_name, mapping, method, auth_type, perm, update_time) +VALUES ( + 'activity:reward-grant-record:page', + '奖励发放记录列表', + '/reward-grant-record/page', + 'GET', + 3, + 'activity:reward-grant-record:page', + NOW() +) +ON DUPLICATE KEY UPDATE + resource_name = VALUES(resource_name), + mapping = VALUES(mapping), + method = VALUES(method), + auth_type = VALUES(auth_type), + perm = VALUES(perm), + update_time = NOW(); + +-- 关键逻辑:优先使用现有“房间支持活动”的父目录,确保新页面与活动记录同级。 +INSERT INTO sys_menu ( + parent_id, + menu_name, + path, + router, + menu_type, + icon, + alias, + sort, + status, + create_user, + update_user, + create_time, + update_time +) +SELECT + source_menu.parent_id, + '奖励发放记录', + 'activity/reward-grant-record/index', + 'activity/reward-grant-record', + 2, + 'form', + 'activity:reward-grant-record', + source_menu.sort - 1, + 0, + '0', + '0', + NOW(), + NOW() +FROM sys_menu source_menu +LEFT JOIN sys_menu existing ON existing.alias = 'activity:reward-grant-record' +WHERE ( + source_menu.router = 'activity/room/contribution' + OR source_menu.router = '/activity/room/contribution' + OR source_menu.path = 'activity/room-contribution-list/index' + OR source_menu.menu_name = '房间支持活动' + ) + AND source_menu.menu_type = 2 + AND existing.id IS NULL +LIMIT 1; + +-- 关键逻辑:迁移可重复执行,已存在菜单时仍同步页面路径和所属目录。 +UPDATE sys_menu reward_menu +JOIN ( + SELECT parent_id, sort + FROM sys_menu + WHERE ( + router = 'activity/room/contribution' + OR router = '/activity/room/contribution' + OR path = 'activity/room-contribution-list/index' + OR menu_name = '房间支持活动' + ) + AND menu_type = 2 + LIMIT 1 +) source_menu ON 1 = 1 +SET + reward_menu.parent_id = source_menu.parent_id, + reward_menu.menu_name = '奖励发放记录', + reward_menu.path = 'activity/reward-grant-record/index', + reward_menu.router = 'activity/reward-grant-record', + reward_menu.menu_type = 2, + reward_menu.icon = 'form', + reward_menu.sort = source_menu.sort - 1, + reward_menu.status = 0, + reward_menu.update_user = '0', + reward_menu.update_time = NOW() +WHERE reward_menu.alias = 'activity:reward-grant-record'; + +INSERT INTO sys_menu_resource (menu_id, resource_id) +SELECT menu.id, resource.id +FROM sys_menu menu +JOIN sys_resource resource ON resource.id = 'activity:reward-grant-record:page' +LEFT JOIN sys_menu_resource existing + ON existing.menu_id = menu.id + AND existing.resource_id = resource.id +WHERE menu.alias = 'activity:reward-grant-record' + AND existing.id IS NULL; + +-- 关键逻辑:仅继承已有房间活动记录的角色,避免扩大后台数据可见范围。 +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT DISTINCT source_role.role_id, reward_menu.id +FROM sys_role_menu source_role +JOIN sys_menu source_menu ON source_menu.id = source_role.menu_id +JOIN sys_menu reward_menu ON reward_menu.alias = 'activity:reward-grant-record' +LEFT JOIN sys_role_menu existing + ON existing.role_id = source_role.role_id + AND existing.menu_id = reward_menu.id +WHERE ( + source_menu.router = 'activity/room/contribution' + OR source_menu.router = '/activity/room/contribution' + OR source_menu.path = 'activity/room-contribution-list/index' + OR source_menu.menu_name = '房间支持活动' + ) + AND source_menu.menu_type = 2 + AND existing.id IS NULL; diff --git a/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/activity/api/RoomContributionActivityCountClientApi.java b/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/activity/api/RoomContributionActivityCountClientApi.java index 8564b406..39b5b7fd 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/activity/api/RoomContributionActivityCountClientApi.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-api/src/main/java/com/red/circle/other/inner/endpoint/activity/api/RoomContributionActivityCountClientApi.java @@ -2,8 +2,11 @@ package com.red.circle.other.inner.endpoint.activity.api; import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.ResultResponse; +import com.red.circle.other.inner.model.cmd.activity.RoomContributionRewardRecordPageQryCmd; import com.red.circle.other.inner.model.dto.activity.RoomContributionActivityCountDTO; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; /** @@ -30,4 +33,11 @@ public interface RoomContributionActivityCountClientApi { @RequestParam("limitSize") Integer limitSize ); + /** + * 查询已成功直发给房主的周流水奖励记录. + */ + @PostMapping("/pageOwnerRewardSent") + ResultResponse> pageOwnerRewardSent( + @RequestBody RoomContributionRewardRecordPageQryCmd qryCmd); + } diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/activity/RoomContributionRewardRecordPageQryCmd.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/activity/RoomContributionRewardRecordPageQryCmd.java new file mode 100644 index 00000000..821b7fda --- /dev/null +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/cmd/activity/RoomContributionRewardRecordPageQryCmd.java @@ -0,0 +1,33 @@ +package com.red.circle.other.inner.model.cmd.activity; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Set; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 房间周流水奖励发放记录分页查询. + */ +@Data +@Accessors(chain = true) +public class RoomContributionRewardRecordPageQryCmd implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 房间ID集合,为空时查询全部房间. + */ + private Set roomIds; + + /** + * 当前页,从1开始. + */ + private Integer current; + + /** + * 每页数量. + */ + private Integer limitSize; +} diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/activity/RoomContributionActivityCountDTO.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/activity/RoomContributionActivityCountDTO.java index 350f28a1..123cab96 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/activity/RoomContributionActivityCountDTO.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/activity/RoomContributionActivityCountDTO.java @@ -60,6 +60,21 @@ public class RoomContributionActivityCountDTO extends DTO implements Serializabl */ private BigDecimal balance; + /** + * 房主奖励金币直发事件ID. + */ + private String ownerRewardCoinsEventId; + + /** + * 房主奖励金币直发时间. + */ + private Timestamp ownerRewardCoinsSentTime; + + /** + * 房主奖励比例补差事件ID. + */ + private String ownerRewardRatioSupplementEventId; + /** * 创建时间. */ diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/api/WalletGoldClientApi.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/api/WalletGoldClientApi.java index 6b365b71..7b564390 100644 --- a/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/api/WalletGoldClientApi.java +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/api/WalletGoldClientApi.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; /** * @author pengliang on 2023/5/26 @@ -101,6 +102,13 @@ public interface WalletGoldClientApi { @GetMapping("/existsEventId") ResultResponse existsEventId(@RequestParam("eventId") String eventId); + /** + * 按事件ID批量查询金币流水. + */ + @PostMapping("/listByEventIds") + ResultResponse> listByEventIds( + @RequestBody Set eventIds); + /** * 获取金币统计. diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/callback/WalletGoldClientCallback.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/callback/WalletGoldClientCallback.java index 9751b145..2eec0ac6 100644 --- a/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/callback/WalletGoldClientCallback.java +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/callback/WalletGoldClientCallback.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Component; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; /** * @author pengliang on 2024/2/23 @@ -93,6 +94,13 @@ public class WalletGoldClientCallback implements WalletGoldClient { return ResultResponse.failure(CommonErrorCode.REQUEST_LIMITING); } + @Override + public ResultResponse> listByEventIds(Set eventIds) { + log.warn("[listByEventIds] eventIds={}", JacksonUtils.toJson(eventIds)); + // 奖励记录必须展示真实钱包流水,熔断时明确失败,不能用空列表伪装成发放 0 金币。 + return ResultResponse.failure(CommonErrorCode.REQUEST_LIMITING); + } + @Override public ResultResponse> listWalletGoldCount(String sysOrigin, Integer groupNum) { diff --git a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/activity/RewardGrantRecordRestController.java b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/activity/RewardGrantRecordRestController.java new file mode 100644 index 00000000..c31a06fd --- /dev/null +++ b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/activity/RewardGrantRecordRestController.java @@ -0,0 +1,30 @@ +package com.red.circle.console.adapter.app.activity; + +import com.red.circle.console.app.dto.clienobject.activity.RewardGrantRecordCO; +import com.red.circle.console.app.dto.cmd.app.activity.RewardGrantRecordQryCmd; +import com.red.circle.console.app.service.app.activity.RewardGrantRecordService; +import com.red.circle.framework.dto.PageResult; +import com.red.circle.framework.web.controller.BaseController; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 奖励发放记录后台接口. + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/reward-grant-record") +public class RewardGrantRecordRestController extends BaseController { + + private final RewardGrantRecordService rewardGrantRecordService; + + /** + * 分页查询. + */ + @GetMapping("/page") + public PageResult page(RewardGrantRecordQryCmd qryCmd) { + return rewardGrantRecordService.pageQuery(qryCmd); + } +} diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordServiceImpl.java new file mode 100644 index 00000000..926e3094 --- /dev/null +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordServiceImpl.java @@ -0,0 +1,181 @@ +package com.red.circle.console.app.service.app.activity; + +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.console.app.dto.clienobject.activity.RewardGrantRecordCO; +import com.red.circle.console.app.dto.cmd.app.activity.RewardGrantRecordQryCmd; +import com.red.circle.console.app.dto.enums.RewardGrantTypeEnum; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.dto.PageResult; +import com.red.circle.other.inner.endpoint.activity.RoomContributionActivityCountClient; +import com.red.circle.other.inner.endpoint.live.RoomManagerClient; +import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; +import com.red.circle.other.inner.model.cmd.activity.RoomContributionRewardRecordPageQryCmd; +import com.red.circle.other.inner.model.dto.activity.RoomContributionActivityCountDTO; +import com.red.circle.other.inner.model.dto.live.RoomProfileDTO; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import com.red.circle.wallet.inner.model.dto.WalletGoldAssetRecordDTO; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/** + * 奖励发放记录实现. + */ +@Service +@RequiredArgsConstructor +public class RewardGrantRecordServiceImpl implements RewardGrantRecordService { + + private static final String OWNER_REWARD_EVENT_PREFIX = "RCWR:"; + private static final String OWNER_REWARD_SUPPLEMENT_EVENT_PREFIX = "RCWRS:"; + + private final UserProfileClient userProfileClient; + private final RoomManagerClient roomManagerClient; + private final RoomContributionActivityCountClient roomContributionActivityCountClient; + private final WalletGoldClient walletGoldClient; + + @Override + public PageResult pageQuery(RewardGrantRecordQryCmd qryCmd) { + RewardGrantTypeEnum rewardType = Objects.requireNonNullElse(qryCmd.getRewardType(), + RewardGrantTypeEnum.ROOM_WEEKLY_CONTRIBUTION); + if (rewardType != RewardGrantTypeEnum.ROOM_WEEKLY_CONTRIBUTION) { + return PageResult.newPageResult(qryCmd.getPageQuery().getLimit()); + } + + Set roomIds = resolveRoomIds(qryCmd.getUserAccount()); + if (StringUtils.isNotBlank(qryCmd.getUserAccount()) && CollectionUtils.isEmpty(roomIds)) { + return PageResult.newPageResult(qryCmd.getPageQuery().getLimit()); + } + + RoomContributionRewardRecordPageQryCmd innerQry = + new RoomContributionRewardRecordPageQryCmd() + .setRoomIds(roomIds) + .setCurrent(qryCmd.getPageQuery().getCursor()) + .setLimitSize(qryCmd.getPageQuery().getLimit()); + PageResult pageResult = ResponseAssert.requiredSuccess( + roomContributionActivityCountClient.pageOwnerRewardSent(innerQry)); + if (CollectionUtils.isEmpty(pageResult.getRecords())) { + return pageResult.emptyRecords(); + } + + Map roomProfileMap = loadRooms(pageResult.getRecords()); + Map userProfileMap = loadUsers(roomProfileMap); + WalletRewardData walletRewardData = loadWalletRewardData(pageResult.getRecords()); + + return pageResult.convert(record -> { + RoomProfileDTO room = roomProfileMap.get(record.getRoomId()); + Long userId = Objects.nonNull(room) ? room.getUserId() : null; + Set eventIds = rewardEventIds(record); + long rewardPenny = eventIds.stream() + .mapToLong(eventId -> walletRewardData.amountByEventId().getOrDefault(eventId, 0L)) + .sum(); + Timestamp grantTime = eventIds.stream() + .map(walletRewardData.timeByEventId()::get) + .filter(Objects::nonNull) + .max(Timestamp::compareTo) + .orElse(record.getOwnerRewardCoinsSentTime()); + + return new RewardGrantRecordCO() + .setRewardType(rewardType) + .setUserProfile(userProfileMap.get(userId)) + .setRoomProfile(room) + .setRoomContribution(record.getContributionValue()) + // 钱包以分存储,页面统一展示金币单位。 + .setRewardCoins(BigDecimal.valueOf(rewardPenny, 2)) + .setDateNumber(record.getDateNumber()) + .setGrantTime(grantTime); + }); + } + + private Set resolveRoomIds(String userAccount) { + if (StringUtils.isBlank(userAccount)) { + return null; + } + + // 平台内查询会先匹配短ID,未命中时继续匹配有效靓号,避免跨平台同号导致漏查。 + UserProfileDTO user = ResponseAssert.requiredSuccess(userProfileClient.getByAccount( + SysOriginPlatformEnum.ATYOU.name(), userAccount.trim())); + if (Objects.isNull(user) || Objects.isNull(user.getId())) { + return Collections.emptySet(); + } + + Map rooms = ResponseAssert.requiredSuccess( + roomManagerClient.mapProfileByUserIds(Set.of(user.getId()))); + return rooms.values().stream() + .filter(Objects::nonNull) + .map(RoomProfileDTO::getId) + .collect(Collectors.toSet()); + } + + private Map loadRooms( + Collection records) { + Set roomIds = records.stream() + .map(RoomContributionActivityCountDTO::getRoomId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + return ResponseAssert.requiredSuccess(roomManagerClient.mapProfileByRoomIds(roomIds)); + } + + private Map loadUsers(Map roomProfileMap) { + Set userIds = roomProfileMap.values().stream() + .filter(Objects::nonNull) + .map(RoomProfileDTO::getUserId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(userIds)) { + return Collections.emptyMap(); + } + return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds)); + } + + private WalletRewardData loadWalletRewardData( + Collection records) { + Set eventIds = records.stream() + .map(this::rewardEventIds) + .flatMap(Set::stream) + .collect(Collectors.toSet()); + List walletRecords = ResponseAssert.requiredSuccess( + walletGoldClient.listByEventIds(eventIds)); + + Map amountByEventId = new HashMap<>(); + Map timeByEventId = new HashMap<>(); + walletRecords.stream() + .filter(Objects::nonNull) + .filter(record -> Objects.equals(record.getType(), 0)) + .forEach(record -> { + amountByEventId.merge(record.getEventId(), + Objects.requireNonNullElse(record.getPennyAmount(), 0L), Long::sum); + if (Objects.nonNull(record.getCreateTime())) { + timeByEventId.merge(record.getEventId(), record.getCreateTime(), + (left, right) -> left.after(right) ? left : right); + } + }); + return new WalletRewardData(amountByEventId, timeByEventId); + } + + private Set rewardEventIds(RoomContributionActivityCountDTO record) { + Set eventIds = new HashSet<>(); + eventIds.add(StringUtils.defaultIfBlank(record.getOwnerRewardCoinsEventId(), + OWNER_REWARD_EVENT_PREFIX + record.getId())); + // 兼容补差字段上线前已发放的数据,派生事件ID后查询实际钱包流水。 + eventIds.add(StringUtils.defaultIfBlank(record.getOwnerRewardRatioSupplementEventId(), + OWNER_REWARD_SUPPLEMENT_EVENT_PREFIX + record.getId())); + return eventIds; + } + + private record WalletRewardData(Map amountByEventId, + Map timeByEventId) { + } +} diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/activity/RewardGrantRecordCO.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/activity/RewardGrantRecordCO.java new file mode 100644 index 00000000..e537b378 --- /dev/null +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/clienobject/activity/RewardGrantRecordCO.java @@ -0,0 +1,32 @@ +package com.red.circle.console.app.dto.clienobject.activity; + +import com.red.circle.console.app.dto.enums.RewardGrantTypeEnum; +import com.red.circle.framework.dto.ClientObject; +import com.red.circle.other.inner.model.dto.live.RoomProfileDTO; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import java.io.Serial; +import java.math.BigDecimal; +import java.sql.Timestamp; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 奖励发放记录. + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class RewardGrantRecordCO extends ClientObject { + + @Serial + private static final long serialVersionUID = 1L; + + private RewardGrantTypeEnum rewardType; + private UserProfileDTO userProfile; + private RoomProfileDTO roomProfile; + private BigDecimal roomContribution; + private BigDecimal rewardCoins; + private Integer dateNumber; + private Timestamp grantTime; +} diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/cmd/app/activity/RewardGrantRecordQryCmd.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/cmd/app/activity/RewardGrantRecordQryCmd.java new file mode 100644 index 00000000..4010b341 --- /dev/null +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/cmd/app/activity/RewardGrantRecordQryCmd.java @@ -0,0 +1,30 @@ +package com.red.circle.console.app.dto.cmd.app.activity; + +import com.red.circle.console.app.dto.enums.RewardGrantTypeEnum; +import com.red.circle.framework.core.dto.PageCommand; +import java.io.Serial; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 奖励发放记录分页查询. + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class RewardGrantRecordQryCmd extends PageCommand { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 奖励类型. + */ + private RewardGrantTypeEnum rewardType = RewardGrantTypeEnum.ROOM_WEEKLY_CONTRIBUTION; + + /** + * 用户短ID或靓号. + */ + private String userAccount; +} diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/enums/RewardGrantTypeEnum.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/enums/RewardGrantTypeEnum.java new file mode 100644 index 00000000..1b0e26a7 --- /dev/null +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/dto/enums/RewardGrantTypeEnum.java @@ -0,0 +1,12 @@ +package com.red.circle.console.app.dto.enums; + +/** + * 后台奖励发放记录类型. + */ +public enum RewardGrantTypeEnum { + + /** + * 房间周流水奖励. + */ + ROOM_WEEKLY_CONTRIBUTION +} diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordService.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordService.java new file mode 100644 index 00000000..a8f40f8f --- /dev/null +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/activity/RewardGrantRecordService.java @@ -0,0 +1,16 @@ +package com.red.circle.console.app.service.app.activity; + +import com.red.circle.console.app.dto.clienobject.activity.RewardGrantRecordCO; +import com.red.circle.console.app.dto.cmd.app.activity.RewardGrantRecordQryCmd; +import com.red.circle.framework.dto.PageResult; + +/** + * 奖励发放记录. + */ +public interface RewardGrantRecordService { + + /** + * 分页查询奖励发放记录. + */ + PageResult pageQuery(RewardGrantRecordQryCmd qryCmd); +} 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 cec34f43..360e7c63 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 @@ -6,6 +6,7 @@ import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributio import java.math.BigDecimal; import java.util.List; import java.util.Optional; +import java.util.Set; import org.apache.commons.lang3.tuple.ImmutablePair; /** @@ -85,6 +86,12 @@ public interface RoomContributionActivityCountService { PageResult pageReceived(long pageNumber, long pageSize, Long roomId); + /** + * 已成功直发给房主的周流水奖励分页记录. + */ + PageResult pageOwnerRewardSent(long pageNumber, long pageSize, + Set roomIds); + /** * * @param roomId 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 232ee854..d4c76e60 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 @@ -19,6 +19,7 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -273,6 +274,18 @@ public class RoomContributionActivityCountServiceImpl implements (int) pageNumber, Sort.Order.desc("createTime")); } + @Override + public PageResult pageOwnerRewardSent(long pageNumber, + long pageSize, Set roomIds) { + Criteria criteria = Criteria.where("ownerRewardCoinsSent").is(Boolean.TRUE); + if (CollectionUtils.isNotEmpty(roomIds)) { + // 账号筛选先解析成房间ID,避免在奖励集合中做跨集合或模糊查询。 + criteria.and("roomId").in(roomIds); + } + return mongoPageHelper.query(new Query(criteria), RoomContributionActivityCount.class, + (int) pageSize, (int) pageNumber, Sort.Order.desc("ownerRewardCoinsSentTime")); + } + @Override public List listRoomContributionByPH(Long roomId,BigDecimal number) { diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/activity/RoomContributionActivityCountClientEndpoint.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/activity/RoomContributionActivityCountClientEndpoint.java index 0ec5ad20..ccfa64ea 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/activity/RoomContributionActivityCountClientEndpoint.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/endpoint/activity/RoomContributionActivityCountClientEndpoint.java @@ -4,6 +4,7 @@ import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.ResultResponse; import com.red.circle.other.app.inner.service.activity.RoomContributionActivityCountClientService; import com.red.circle.other.inner.endpoint.activity.api.RoomContributionActivityCountClientApi; +import com.red.circle.other.inner.model.cmd.activity.RoomContributionRewardRecordPageQryCmd; import com.red.circle.other.inner.model.dto.activity.RoomContributionActivityCountDTO; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -33,4 +34,11 @@ public class RoomContributionActivityCountClientEndpoint implements ); } + @Override + public ResultResponse> pageOwnerRewardSent( + RoomContributionRewardRecordPageQryCmd qryCmd) { + return ResultResponse.success( + roomContributionActivityCountClientService.pageOwnerRewardSent(qryCmd)); + } + } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/RoomContributionActivityCountClientService.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/RoomContributionActivityCountClientService.java index fdc67058..710df70e 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/RoomContributionActivityCountClientService.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/RoomContributionActivityCountClientService.java @@ -1,6 +1,7 @@ package com.red.circle.other.app.inner.service.activity; import com.red.circle.framework.dto.PageResult; +import com.red.circle.other.inner.model.cmd.activity.RoomContributionRewardRecordPageQryCmd; import com.red.circle.other.inner.model.dto.activity.RoomContributionActivityCountDTO; /** @@ -22,4 +23,10 @@ public interface RoomContributionActivityCountClientService { PageResult pageReceived(Long roomId, Integer current, Integer limitSize); + /** + * 已成功直发给房主的周流水奖励记录. + */ + PageResult pageOwnerRewardSent( + RoomContributionRewardRecordPageQryCmd qryCmd); + } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/impl/RoomContributionActivityCountClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/impl/RoomContributionActivityCountClientServiceImpl.java index 6ce70c9b..6bb014f7 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/impl/RoomContributionActivityCountClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/activity/impl/RoomContributionActivityCountClientServiceImpl.java @@ -4,6 +4,7 @@ import com.red.circle.framework.dto.PageResult; import com.red.circle.other.app.inner.convertor.activity.RoomContributionActivityCountInnerConvertor; import com.red.circle.other.app.inner.service.activity.RoomContributionActivityCountClientService; import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService; +import com.red.circle.other.inner.model.cmd.activity.RoomContributionRewardRecordPageQryCmd; import com.red.circle.other.inner.model.dto.activity.RoomContributionActivityCountDTO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -29,4 +30,12 @@ public class RoomContributionActivityCountClientServiceImpl implements ); } + @Override + public PageResult pageOwnerRewardSent( + RoomContributionRewardRecordPageQryCmd qryCmd) { + return roomContributionActivityCountInnerConvertor.toPageRoomContributionActivityCountDTO( + roomContributionActivityCountService.pageOwnerRewardSent(qryCmd.getCurrent(), + qryCmd.getLimitSize(), qryCmd.getRoomIds())); + } + } diff --git a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/WalletGoldAssetRecordService.java b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/WalletGoldAssetRecordService.java index d5225a9d..b26fc13c 100644 --- a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/WalletGoldAssetRecordService.java +++ b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/WalletGoldAssetRecordService.java @@ -3,6 +3,7 @@ package com.red.circle.wallet.infra.database.rds.service; import com.red.circle.framework.mybatis.service.BaseService; import com.red.circle.wallet.infra.database.rds.entity.WalletGoldAssetRecord; import java.util.List; +import java.util.Set; /** * 用户金币资产流水 服务. @@ -21,6 +22,11 @@ public interface WalletGoldAssetRecordService extends BaseService listByEventIds(Set eventIds); + /** * 获取本月指定数量资产记录. */ diff --git a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/impl/WalletGoldAssetRecordServiceImpl.java b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/impl/WalletGoldAssetRecordServiceImpl.java index 1ae259f1..9b600cfd 100644 --- a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/impl/WalletGoldAssetRecordServiceImpl.java +++ b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/rds/service/impl/WalletGoldAssetRecordServiceImpl.java @@ -10,6 +10,7 @@ import java.util.Date; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import org.springframework.stereotype.Service; /** @@ -22,6 +23,8 @@ public class WalletGoldAssetRecordServiceImpl extends BaseServiceImpl implements WalletGoldAssetRecordService { + private static final int MAX_EVENT_ID_QUERY_SIZE = 200; + @Override public boolean existsRecent(Long id) { return Optional.ofNullable( @@ -44,6 +47,22 @@ public class WalletGoldAssetRecordServiceImpl extends .orElse(Boolean.FALSE); } + @Override + public List listByEventIds(Set eventIds) { + if (eventIds == null || eventIds.isEmpty()) { + return List.of(); + } + if (eventIds.size() > MAX_EVENT_ID_QUERY_SIZE) { + throw new IllegalArgumentException("eventIds size must not exceed " + + MAX_EVENT_ID_QUERY_SIZE); + } + // event_id在每个月分片表都有索引;限制批量大小,避免跨月分片广播成大范围查询。 + return query().in(WalletGoldAssetRecord::getEventId, eventIds) + .eq(WalletGoldAssetRecord::getType, 0) + .last(PageConstant.formatLimitStrict(eventIds.size())) + .list(); + } + @Override public List listThisMonth(String sysOrigin, Long userId, Integer type, Long lastId, diff --git a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/WalletGoldClientEndpoint.java b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/WalletGoldClientEndpoint.java index c5ff2fb0..e56150ad 100644 --- a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/WalletGoldClientEndpoint.java +++ b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/WalletGoldClientEndpoint.java @@ -14,6 +14,7 @@ import com.red.circle.wallet.inner.service.wallet.WalletGoldClientService; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; @@ -99,6 +100,11 @@ public class WalletGoldClientEndpoint implements WalletGoldClientApi { return ResultResponse.success(walletGoldClientService.existsEventId(eventId)); } + @Override + public ResultResponse> listByEventIds(Set eventIds) { + return ResultResponse.success(walletGoldClientService.listByEventIds(eventIds)); + } + @Override public ResultResponse> listWalletGoldCount(String sysOrigin, Integer groupNum) { diff --git a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/WalletGoldClientService.java b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/WalletGoldClientService.java index 11100eb4..d0d36b38 100644 --- a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/WalletGoldClientService.java +++ b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/WalletGoldClientService.java @@ -9,6 +9,7 @@ import com.red.circle.wallet.inner.model.cmd.UserGoldRunningWaterBackQryCmd; import com.red.circle.wallet.inner.model.dto.*; import java.util.List; +import java.util.Set; /** * @author pengliang on 2023/5/26 @@ -65,6 +66,11 @@ public interface WalletGoldClientService { */ Boolean existsEventId(String eventId); + /** + * 按事件ID批量查询金币流水. + */ + List listByEventIds(Set eventIds); + /** * 获取最近一个月流水信息(app). */ diff --git a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/WalletGoldClientServiceImpl.java b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/WalletGoldClientServiceImpl.java index b29cafc4..483a7911 100644 --- a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/WalletGoldClientServiceImpl.java +++ b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/WalletGoldClientServiceImpl.java @@ -367,6 +367,13 @@ public class WalletGoldClientServiceImpl implements WalletGoldClientService { return walletGoldAssetRecordService.existsRecentEventId(eventId); } + @Override + public List listByEventIds(Set eventIds) { + return walletGoldAssetRecordService.listByEventIds(eventIds).stream() + .map(walletInnerConvertor::toWalletGoldAssetRecordDTO) + .toList(); + } + @Override public List listThisMonth(String sysOrigin, Long userId, Integer type, Long lastId) {