From 4fb7d8af0471a7054fee52bec7db514c54288fc0 Mon Sep 17 00:00:00 2001 From: hy001 Date: Thu, 23 Apr 2026 13:46:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=88=BF=E9=97=B4=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=E8=BF=87=E6=BB=A4=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E4=B8=AA=E4=BA=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/user/UserPhotoWallRestController.java | 68 ++++++ .../room/query/RoomVoiceDiscoverQryExe.java | 221 +++++++----------- .../room/query/SearchRegionRoomQryExe.java | 93 ++++---- .../command/team/TeamUserApplyJoinExe.java | 147 ++++++------ .../common/room/RoomRegionFilterCommon.java | 80 +++++++ .../scheduler/LuckyGiftRewardStreamTask.java | 28 +-- .../query/RoomVoiceDiscoverQryExeTest.java | 122 ++++++++++ .../query/SearchRegionRoomQryExeTest.java | 74 ++++++ .../room/RoomRegionFilterCommonTest.java | 61 +++++ .../LuckyGiftRewardStreamTaskTest.java | 14 +- .../service/live/ActiveVoiceRoomService.java | 23 +- .../live/RoomProfileManagerService.java | 16 +- .../live/impl/ActiveVoiceRoomServiceImpl.java | 33 ++- .../impl/RoomProfileManagerServiceImpl.java | 42 +++- .../impl/ActiveVoiceRoomServiceImplTest.java | 50 ++++ .../RoomProfileManagerServiceImplTest.java | 32 ++- .../rc-service-wallet/wallet-start/pom.xml | 26 ++- .../driver/NacosDriverURLProvider.java | 18 ++ .../driver/NacosDriverURLProviderTest.java | 45 ++++ 19 files changed, 876 insertions(+), 317 deletions(-) create mode 100644 rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserPhotoWallRestController.java create mode 100644 rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/room/RoomRegionFilterCommon.java create mode 100644 rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExeTest.java create mode 100644 rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExeTest.java create mode 100644 rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/room/RoomRegionFilterCommonTest.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImplTest.java create mode 100644 rc-service/rc-service-wallet/wallet-start/src/test/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProviderTest.java diff --git a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserPhotoWallRestController.java b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserPhotoWallRestController.java new file mode 100644 index 00000000..595e623c --- /dev/null +++ b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserPhotoWallRestController.java @@ -0,0 +1,68 @@ +package com.red.circle.console.adapter.app.user; + +import com.red.circle.console.app.service.app.user.UserPhotoWallService; +import com.red.circle.console.infra.annotations.OpsOperationReqLog; +import com.red.circle.framework.core.dto.PageCommand; +import com.red.circle.framework.dto.PageResult; +import com.red.circle.framework.web.controller.BaseController; +import com.red.circle.other.inner.model.cmd.user.PhotoWallApprovalTableQryCmd; +import com.red.circle.other.inner.model.dto.user.UserPhotoWallDTO; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 用户照片墙前端控制器. + */ +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/user/photo/wall", produces = MediaType.APPLICATION_JSON_VALUE) +public class UserPhotoWallRestController extends BaseController { + + private final UserPhotoWallService userPhotoWallService; + + /** + * 获取用户正常照片墙. + */ + @GetMapping("/normal/{userId}") + public List listUserPhotoWallNormal(@PathVariable("userId") Long userId) { + return userPhotoWallService.listUserPhotoWallNormal(userId); + } + + /** + * 获取用户全部照片墙. + */ + @GetMapping("/all/{userId}") + public List listUserPhotoWall(@PathVariable("userId") Long userId) { + return userPhotoWallService.listUserPhotoWall(userId); + } + + /** + * 用户照片墙分页. + */ + @GetMapping("/page") + public PageResult pageUserPhotoWall(PageCommand query) { + return userPhotoWallService.pageUserPhotoWall(query); + } + + /** + * 用户照片墙审批分页. + */ + @GetMapping("/page/all") + public PageResult pageAllUserPhotoWall(PhotoWallApprovalTableQryCmd query) { + return userPhotoWallService.pageUserPhotoWall(query); + } + + /** + * 删除用户照片墙. + */ + @OpsOperationReqLog("删除用户照片墙") + @GetMapping("/del/{id}") + public void deletePhotoWall(@PathVariable("id") Long id) { + userPhotoWallService.deleteById(id); + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExe.java index 706c731c..656375f7 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExe.java @@ -5,21 +5,23 @@ import com.google.common.collect.Sets; import com.google.gson.Gson; import com.red.circle.common.business.dto.cmd.AppExtCommand; import com.red.circle.common.business.enums.SVIPLevelEnum; -import com.red.circle.live.inner.endpoint.LiveMicClient; -import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; -import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; -import com.red.circle.other.app.service.game.GameLudoService; -import com.red.circle.other.app.service.room.RocketStatusCacheService; -import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.live.inner.endpoint.LiveMicClient; +import com.red.circle.other.app.common.room.RoomRegionFilterCommon; +import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; +import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; +import com.red.circle.other.app.service.game.GameLudoService; +import com.red.circle.other.app.service.room.RocketStatusCacheService; +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.ability.RegionConfig; import com.red.circle.other.domain.gateway.user.ability.UserSVipGateway; import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.rocket.RocketStatus; import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport; import com.red.circle.other.infra.database.cache.service.user.RegionRoomCacheService; -import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; -import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; -import com.red.circle.other.infra.database.mongo.entity.live.RoomUserBlacklist; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; +import com.red.circle.other.infra.database.mongo.entity.live.RoomUserBlacklist; import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; import com.red.circle.other.infra.database.mongo.service.live.RoomUserBlacklistService; @@ -51,10 +53,11 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class RoomVoiceDiscoverQryExe { - private final UserSVipGateway userSVipGateway; - private final UserRegionGateway userRegionGateway; - private final RoomVoiceProfileCommon roomVoiceProfileCommon; - private final ActiveVoiceRoomService activeVoiceRoomService; + private final UserSVipGateway userSVipGateway; + private final UserRegionGateway userRegionGateway; + private final RoomRegionFilterCommon roomRegionFilterCommon; + private final RoomVoiceProfileCommon roomVoiceProfileCommon; + private final ActiveVoiceRoomService activeVoiceRoomService; private final RoomUserBlacklistService roomUserBlacklistService; private final LatestMobileDeviceService latestMobileDeviceService; private final RoomProfileManagerService roomProfileManagerService; @@ -66,18 +69,14 @@ public class RoomVoiceDiscoverQryExe { private final UserProfileGateway userProfileGateway; private final CountryCodeAliasSupport countryCodeAliasSupport; - private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*"; - - // 区域隔离常量 - 与ActiveVoiceRoomServiceImpl保持一致 - private static final Set TR_REGIONS = Set.of("TR"); - private static final Set SA_REGIONS = Set.of("BD","SA", "IN", "PK"); - private static final Set AR_REGIONS = Set.of("AE", "AR", "BH", "DJ", "DZ", "EG", "EY", "ER", "IL", "IQ", "JO", "KM", "KW", "LB", "LY", "MA", "MR", "OM", "PS", "QA", "SD", "SO", "SS", "SY", "TD", "TN", "YE"); - private static final Set OTHER_REGIONS = Set.of("OTHER", "PH", "ID", "GH", "IR", "AF", "NG", "DE", "MYS", "TM", "AZ", "NP"); - - public List execute(AppExtCommand cmd) { - String region = cmd.isAllRegion() ? "ALL" : userRegionGateway.getRegionCode(cmd.requiredReqUserId()); - UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId()); - + private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*"; + private static final int DISCOVERY_LIMIT = 50; + + public List execute(AppExtCommand cmd) { + RegionConfig currentRegion = roomRegionFilterCommon.requireRegionConfig(cmd.requiredReqUserId()); + String region = currentRegion.getRegionCode(); + UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId()); + String key = cmd.requireReqSysOrigin() + region; String regionsRoom = regionRoomCacheService.getRegionsRoom(key); if (Objects.nonNull(regionsRoom)) { @@ -92,29 +91,49 @@ public class RoomVoiceDiscoverQryExe { } return availableRooms; } - - String userCountryCode = userProfile != null ? userProfile.getCountryCode() : null; - List activeVoiceRooms = activeVoiceRoomService.listDiscover(cmd.requireReqSysOrigin(), cmd.isAllRegion(), region, userCountryCode, 50); - - // 查询没人的房间缓存,加入进去 - List emptyRooms = getEmptyRoomsFromCache(); - if (CollectionUtils.isNotEmpty(emptyRooms)) { - List matchedEmptyRooms = filterEmptyRoomsByRegion(emptyRooms, cmd.requireReqSysOrigin(), cmd.isAllRegion(), region, userCountryCode); - if (CollectionUtils.isNotEmpty(matchedEmptyRooms)) { - List allRooms = new ArrayList<>(activeVoiceRooms); - Set existingRoomIds = activeVoiceRooms.stream() + + List activeVoiceRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion( + activeVoiceRoomService.listByRegion(cmd.requireReqSysOrigin(), region, DISCOVERY_LIMIT), + region + ); + + // 查询没人的房间缓存,加入进去 + List emptyRooms = getEmptyRoomsFromCache(); + if (CollectionUtils.isNotEmpty(emptyRooms)) { + List matchedEmptyRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion( + emptyRooms.stream() + .filter(room -> Objects.equals(cmd.requireReqSysOrigin(), room.getSysOrigin())) + .toList(), + region + ); + if (CollectionUtils.isNotEmpty(matchedEmptyRooms)) { + List allRooms = new ArrayList<>(activeVoiceRooms); + Set existingRoomIds = activeVoiceRooms.stream() .map(ActiveVoiceRoom::getId) .collect(Collectors.toSet()); matchedEmptyRooms.stream() .filter(room -> !existingRoomIds.contains(room.getId())) .forEach(allRooms::add); - activeVoiceRooms = allRooms; - } - } - List roomList = roomVoiceProfileCommon.toListRoomVoiceProfileCO(activeVoiceRooms); - - if (CollectionUtils.isEmpty(roomList)) { - return CollectionUtils.newArrayList(); + activeVoiceRooms = allRooms; + } + } + List roomList = roomVoiceProfileCommon.toListRoomVoiceProfileCO(activeVoiceRooms); + if (roomList.size() < DISCOVERY_LIMIT) { + List fallbackRoomManagers = roomRegionFilterCommon.filterRoomProfileManagersByRegion( + roomProfileManagerService.listSelectiveLimitByCountryCodes( + cmd.requireReqSysOrigin(), + roomRegionFilterCommon.listCountryCodes(currentRegion), + roomList.stream().map(RoomVoiceProfileCO::getId).toList(), + DISCOVERY_LIMIT + ), + region + ); + roomList = mergeRoomList(roomList, roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers), + DISCOVERY_LIMIT); + } + + if (CollectionUtils.isEmpty(roomList)) { + return CollectionUtils.newArrayList(); } Map svipLevelEnumMap = userSVipGateway.checkSVipIdentity( roomList.stream().map(RoomVoiceProfileCO::getUserId).collect( @@ -129,9 +148,25 @@ public class RoomVoiceDiscoverQryExe { fillRocketStatus(roomList); // 填充游戏图标 fillGameIcon(roomList); - - return roomList; - } + + return roomList; + } + + private List mergeRoomList(List currentRooms, + List fallbackRooms, int limit) { + if (CollectionUtils.isEmpty(fallbackRooms)) { + return currentRooms.stream().limit(limit).toList(); + } + List mergedRooms = new ArrayList<>(currentRooms); + Set existingRoomIds = currentRooms.stream() + .map(RoomVoiceProfileCO::getId) + .collect(Collectors.toSet()); + fallbackRooms.stream() + .filter(room -> !existingRoomIds.contains(room.getId())) + .limit(Math.max(limit - mergedRooms.size(), 0)) + .forEach(mergedRooms::add); + return mergedRooms.stream().limit(limit).toList(); + } private void fillGameIcon(List roomList) { if (CollectionUtils.isEmpty(roomList)) { @@ -244,7 +279,7 @@ public class RoomVoiceDiscoverQryExe { } - private List getRoomListLaneCodes(List roomList, String langeCodes, Integer limit, Long userId) { + private List getRoomListLaneCodes(List roomList, String langeCodes, Integer limit, Long userId) { Set roomUserIds = roomList.stream() .filter(room -> Objects.equals(langeCodes, latestMobileDeviceService.getLatestLanguage(room.getUserId()))) .map(ActiveVoiceRoom::getId) @@ -257,97 +292,7 @@ public class RoomVoiceDiscoverQryExe { } else { return filteredRooms.subList(0, limit); } - } - - private List filterEmptyRoomsByRegion(List emptyRooms, String sysOrigin, boolean isAllRegion, String region, String userCountryCode) { - return emptyRooms.stream() - .filter(room -> Objects.equals(sysOrigin, room.getSysOrigin()) && - (isAllRegion || shouldShowRoom(region, room.getRegion())) && - (!SA_REGIONS.contains(region) || userCountryCode == null - || countryCodeAliasSupport.matches(userCountryCode, room.getCountryCode()))) - .collect(Collectors.toList()); - } - - - /** - * 判断是否应该显示该房间 - */ - private boolean shouldShowRoom(String userRegion, String roomRegion) { - // 获取用户所属区域组 - Set userRegionGroup = getRegionGroup(userRegion); - Set roomRegionGroup = getRegionGroup(roomRegion); - - // 如果用户在特殊区域组,只显示同组房间 - if (userRegionGroup != null) { - return userRegionGroup.contains(roomRegion); - } - - // 如果用户不在特殊区域组,排除所有特殊区域组房间 - return roomRegionGroup == null; - } - - /** - * 获取区域所属的特殊区域组(如果不属于任何特殊组则返回null) - */ - private Set getRegionGroup(String region) { - if (SA_REGIONS.contains(region)) { - return SA_REGIONS; - } - if (AR_REGIONS.contains(region)) { - return AR_REGIONS; - } - if (OTHER_REGIONS.contains(region)) { - return OTHER_REGIONS; - } - if (TR_REGIONS.contains(region)) { - return TR_REGIONS; - } - return null; - } - - - /** - * 获取需要排除的区域列表 - */ - private List getRegions(String region) { - List excludeRegions = new ArrayList<>(); - - if (SA_REGIONS.contains(region)) { - excludeRegions.addAll(AR_REGIONS); - excludeRegions.addAll(OTHER_REGIONS); - } else if (AR_REGIONS.contains(region)) { - excludeRegions.addAll(SA_REGIONS); - excludeRegions.addAll(OTHER_REGIONS); - } else if (OTHER_REGIONS.contains(region)) { - excludeRegions.addAll(SA_REGIONS); - excludeRegions.addAll(AR_REGIONS); - } else { - excludeRegions.addAll(SA_REGIONS); - excludeRegions.addAll(AR_REGIONS); - excludeRegions.addAll(OTHER_REGIONS); - } - - return excludeRegions; - } - - /** - * 获取特殊区域组列表 - */ - private List getSpecialRegions(String region) { - if (SA_REGIONS.contains(region)) { - return new ArrayList<>(SA_REGIONS); - } - - if (AR_REGIONS.contains(region)) { - return new ArrayList<>(AR_REGIONS); - } - - if (OTHER_REGIONS.contains(region)) { - return new ArrayList<>(OTHER_REGIONS); - } - - return new ArrayList<>(); - } + } /** * 从缓存中获取没人的房间 diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExe.java index 327cf2ad..5d2fc29b 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExe.java @@ -1,18 +1,21 @@ package com.red.circle.other.app.command.room.query; -import com.red.circle.framework.mybatis.constant.PageConstant; -import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; -import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; -import com.red.circle.other.app.dto.cmd.room.RegionSearchRoomQryCmd; -import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; -import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; -import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; -import com.red.circle.tool.core.collection.CollectionUtils; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.stream.Collectors; +import com.red.circle.framework.mybatis.constant.PageConstant; +import com.red.circle.other.app.common.room.RoomRegionFilterCommon; +import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; +import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; +import com.red.circle.other.app.dto.cmd.room.RegionSearchRoomQryCmd; +import com.red.circle.other.domain.model.user.ability.RegionConfig; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; +import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; +import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; +import com.red.circle.tool.core.collection.CollectionUtils; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; /** * 获取区域房间. @@ -23,33 +26,43 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class SearchRegionRoomQryExe { - private final ActiveVoiceRoomService activeVoiceRoomService; - private final RoomVoiceProfileCommon roomVoiceProfileCommon; - private final RoomProfileManagerService roomProfileManagerService; - - public List execute(RegionSearchRoomQryCmd cmd) { - - List activeVoiceRooms = activeVoiceRoomService - .listSearchCountryRoom(cmd.getReqSysOrigin().getOrigin(), cmd.getCountryCode(), - PageConstant.MAX_LIMIT_SIZE); - - if (CollectionUtils.isNotEmpty(activeVoiceRooms) && activeVoiceRooms.size() >= 100) { - return assemblyRoomVoiceProfile(activeVoiceRooms); - } - - if (CollectionUtils.isEmpty(activeVoiceRooms)) { - return roomVoiceProfileCommon.listRoomVoiceProfilesV2( - roomProfileManagerService.listSelectiveLimit100(cmd.requireReqSysOrigin(), - cmd.getCountryCode(), null)); - } - - List roomVoiceProfiles = assemblyRoomVoiceProfile(activeVoiceRooms); - roomVoiceProfiles.addAll(roomVoiceProfileCommon.listRoomVoiceProfilesV2( - roomProfileManagerService.listSelectiveLimit100(cmd.requireReqSysOrigin(), - cmd.getCountryCode(), activeVoiceRooms - .stream().map(ActiveVoiceRoom::getId).collect(Collectors.toList())))); - return roomVoiceProfiles; - } + private final ActiveVoiceRoomService activeVoiceRoomService; + private final RoomRegionFilterCommon roomRegionFilterCommon; + private final RoomVoiceProfileCommon roomVoiceProfileCommon; + private final RoomProfileManagerService roomProfileManagerService; + + public List execute(RegionSearchRoomQryCmd cmd) { + RegionConfig currentRegion = roomRegionFilterCommon.requireRegionConfig(cmd.requiredReqUserId()); + String currentRegionCode = currentRegion.getRegionCode(); + + List activeVoiceRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion( + activeVoiceRoomService.listByRegion(cmd.requireReqSysOrigin(), currentRegionCode, + PageConstant.MAX_LIMIT_SIZE), + currentRegionCode + ); + + if (CollectionUtils.isNotEmpty(activeVoiceRooms) && activeVoiceRooms.size() >= 100) { + return assemblyRoomVoiceProfile(activeVoiceRooms); + } + + List fallbackRoomManagers = roomRegionFilterCommon.filterRoomProfileManagersByRegion( + roomProfileManagerService.listSelectiveLimitByCountryCodes( + cmd.requireReqSysOrigin(), + roomRegionFilterCommon.listCountryCodes(currentRegion), + activeVoiceRooms.stream().map(ActiveVoiceRoom::getId).collect(Collectors.toList()), + PageConstant.MAX_LIMIT_SIZE + ), + currentRegionCode + ); + + if (CollectionUtils.isEmpty(activeVoiceRooms)) { + return roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers); + } + + List roomVoiceProfiles = new ArrayList<>(assemblyRoomVoiceProfile(activeVoiceRooms)); + roomVoiceProfiles.addAll(roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers)); + return roomVoiceProfiles.stream().limit(PageConstant.MAX_LIMIT_SIZE).toList(); + } private List assemblyRoomVoiceProfile( List activeVoiceRooms) { diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/TeamUserApplyJoinExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/TeamUserApplyJoinExe.java index 9a878ea8..d869ea94 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/TeamUserApplyJoinExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/TeamUserApplyJoinExe.java @@ -1,31 +1,27 @@ package com.red.circle.other.app.command.team; -import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; -import com.red.circle.common.business.enums.OperationUserOrigin; -import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; -import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd; -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.app.convertor.user.UserProfileAppConvertor; -import com.red.circle.other.app.dto.cmd.team.DbInviteAgentCmd; -import com.red.circle.other.app.dto.cmd.team.DbLeaderInviteBdCmd; -import com.red.circle.other.app.dto.cmd.team.TeamUserApplyCmd; -import com.red.circle.other.domain.gateway.user.UserProfileGateway; -import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway; -import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; -import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService; +import com.red.circle.common.business.enums.OperationUserOrigin; +import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; +import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd; +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.app.convertor.user.UserProfileAppConvertor; +import com.red.circle.other.app.dto.cmd.team.TeamUserApplyCmd; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway; +import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; +import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamApplicationProcess; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile; -import com.red.circle.other.infra.database.mongo.service.team.team.TeamApplicationProcessService; -import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; -import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService; -import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService; -import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService; -import com.red.circle.other.inner.asserts.user.UserErrorCode; -import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; -import com.red.circle.other.inner.enums.config.EnumConfigKey; -import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamApplicationProcessService; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService; +import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService; +import com.red.circle.other.inner.asserts.user.UserErrorCode; +import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; +import com.red.circle.other.inner.enums.config.EnumConfigKey; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.asserts.team.TeamErrorCode; import com.red.circle.other.inner.model.dto.agency.agency.TeamStatus; import com.red.circle.other.inner.enums.team.TeamAppProcessReason; @@ -39,11 +35,11 @@ import com.red.circle.tool.core.text.StringUtils; import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; 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.springframework.stereotype.Component; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; /** * 用户申请加入团队. @@ -52,19 +48,21 @@ import org.springframework.stereotype.Component; */ @Component @RequiredArgsConstructor -public class TeamUserApplyJoinExe { - - private final WalletGoldClient walletGoldClient; - private final UserRegionGateway userRegionGateway; - private final UserProfileGateway userProfileGateway; - private final TeamMemberService teamMemberService; - private final TeamProfileService teamProfileService; - private final UserProfileAppConvertor userProfileAppConvertor; - private final LatestMobileDeviceService latestMobileDeviceService; - private final TeamApplicationProcessService teamApplicationProcessService; - private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; - private final RegisterDeviceGateway registerDeviceGateway; - private final EnumConfigCacheService enumConfigCacheService; +public class TeamUserApplyJoinExe { + + private static final String TEAM_APPLY_MESSAGE_PATH = "/message"; + + private final WalletGoldClient walletGoldClient; + private final UserRegionGateway userRegionGateway; + private final UserProfileGateway userProfileGateway; + private final TeamMemberService teamMemberService; + private final TeamProfileService teamProfileService; + private final OfficialNoticeClient officialNoticeClient; + private final UserProfileAppConvertor userProfileAppConvertor; + private final LatestMobileDeviceService latestMobileDeviceService; + private final TeamApplicationProcessService teamApplicationProcessService; + private final RegisterDeviceGateway registerDeviceGateway; + private final EnumConfigCacheService enumConfigCacheService; public String execute(TeamUserApplyCmd cmd) { @@ -167,40 +165,31 @@ public class TeamUserApplyJoinExe { return teamIds.contains(cmd.getTeamId()); } - private void sendMessage(TeamUserApplyCmd cmd, TeamProfile teamProfile) { - UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO( - userProfileGateway.getByUserId(cmd.requiredReqUserId()) - ); - ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile); - String h5Url = enumConfigCacheService.getValue(EnumConfigKey.H5_DOMAIN_BASE_URL_V2, - cmd.requireReqSysOrigin()); - - if (StringUtils.isBlank(h5Url)) { - return; - } - String path = null; - if (Objects.equals(cmd.requireReqSysOriginEnum(), SysOriginPlatformEnum.TARAB)) { - path = "/team-tarab"; - } - if (Objects.equals(cmd.requireReqSysOriginEnum(), SysOriginPlatformEnum.YOLO)) { - cmd.setUrl("/anchor_agent_yolo"); - if (StringUtils.isNotBlank(cmd.getUrl())){ - path = cmd.getUrl(); - } - } - if (StringUtils.isBlank(path)) { - return; - } - /*officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder() - .toAccount(teamProfile.getOwnUserId()) - .noticeType(OfficialNoticeTypeEnum.USER_SEND_APPLY_TEAM) - .templateParam(MapBuilder.builder() - .put("userProfile", String.format("%s(%s)", userProfile.getUserNickname(), - userProfile.actualAccount())) - .build()) - .expand(h5Url + path) - .build() - );*/ - } - -} + private void sendMessage(TeamUserApplyCmd cmd, TeamProfile teamProfile) { + UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO( + userProfileGateway.getByUserId(cmd.requiredReqUserId()) + ); + ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile); + String h5Url = enumConfigCacheService.getValue(EnumConfigKey.H5_DOMAIN_BASE_URL_V2, + cmd.requireReqSysOrigin()); + String expand = null; + if (StringUtils.isNotBlank(h5Url)) { + expand = (h5Url.endsWith("/") ? h5Url.substring(0, h5Url.length() - 1) : h5Url) + + TEAM_APPLY_MESSAGE_PATH; + } + + NoticeExtTemplateTypeCmd.NoticeExtTemplateTypeCmdBuilder builder = + NoticeExtTemplateTypeCmd.builder() + .toAccount(teamProfile.getOwnUserId()) + .noticeType(OfficialNoticeTypeEnum.USER_SEND_APPLY_TEAM) + .templateParam(MapBuilder.builder() + .put("userProfile", String.format("%s(%s)", userProfile.getUserNickname(), + userProfile.actualAccount())) + .build()); + if (StringUtils.isNotBlank(expand)) { + builder.expand(expand); + } + officialNoticeClient.send(builder.build()); + } + +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/room/RoomRegionFilterCommon.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/room/RoomRegionFilterCommon.java new file mode 100644 index 00000000..32cda572 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/room/RoomRegionFilterCommon.java @@ -0,0 +1,80 @@ +package com.red.circle.other.app.common.room; + +import com.google.common.collect.Lists; +import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; +import com.red.circle.other.domain.model.user.ability.RegionConfig; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.tool.core.text.StringUtils; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 房间区域过滤公共能力. + */ +@Service +@RequiredArgsConstructor +public class RoomRegionFilterCommon { + + private final UserRegionGateway userRegionGateway; + + public RegionConfig requireRegionConfig(Long userId) { + RegionConfig regionConfig = userRegionGateway.getRegionConfigByUserId(userId); + if (Objects.nonNull(regionConfig) && StringUtils.isNotBlank(regionConfig.getRegionCode())) { + return regionConfig; + } + return new RegionConfig().setRegionCode(userRegionGateway.getRegionCode(userId)); + } + + public List listCountryCodes(RegionConfig regionConfig) { + if (Objects.isNull(regionConfig) || StringUtils.isBlank(regionConfig.getCountryCodes())) { + return Lists.newArrayList(); + } + return java.util.Arrays.stream(regionConfig.getCountryCodes().split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .distinct() + .toList(); + } + + public List filterActiveVoiceRoomsByRegion( + List activeVoiceRooms, String regionCode) { + return filterByRegionCode(activeVoiceRooms, ActiveVoiceRoom::getUserId, regionCode); + } + + public List filterRoomProfileManagersByRegion( + List roomProfileManagers, String regionCode) { + return filterByRegionCode(roomProfileManagers, RoomProfileManager::getUserId, regionCode); + } + + private List filterByRegionCode(List source, Function userIdGetter, + String regionCode) { + if (CollectionUtils.isEmpty(source) || StringUtils.isBlank(regionCode)) { + return Lists.newArrayList(); + } + + Set userIds = source.stream() + .map(userIdGetter) + .filter(Objects::nonNull) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + if (CollectionUtils.isEmpty(userIds)) { + return Lists.newArrayList(); + } + + Map regionCodeMap = userRegionGateway.mapRegionCode(userIds); + if (CollectionUtils.isEmpty(regionCodeMap)) { + return Lists.newArrayList(); + } + + return source.stream() + .filter(item -> StringUtils.equalsIgnoreCase(regionCode, regionCodeMap.get(userIdGetter.apply(item)))) + .toList(); + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java index 7d3756b6..f665e497 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java @@ -18,7 +18,8 @@ import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.ReadOffset; import org.springframework.data.redis.connection.stream.StreamOffset; import org.springframework.data.redis.connection.stream.StreamReadOptions; -import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StreamOperations; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -31,7 +32,7 @@ public class LuckyGiftRewardStreamTask { private static final String LAST_ID_KEY_PREFIX = "lucky_gift:reward:last-id:"; private static final String PROCESSED_KEY_PREFIX = "lucky_gift:reward:processed:"; - private final RedisTemplate redisTemplate; + private final StringRedisTemplate stringRedisTemplate; private final RedisService redisService; private final LuckyGiftRewardSettlementService luckyGiftRewardSettlementService; @@ -59,7 +60,8 @@ public class LuckyGiftRewardStreamTask { StreamReadOptions readOptions = StreamReadOptions.empty() .count(Objects.requireNonNullElse(batchSize, 20L)) .block(Duration.ofMillis(Objects.requireNonNullElse(readBlockMillis, 200L))); - List> records = redisTemplate.opsForStream().read( + StreamOperations streamOperations = streamOperations(); + List> records = streamOperations.read( readOptions, StreamOffset.create(streamKey, ReadOffset.from(lastId)) ); @@ -67,7 +69,7 @@ public class LuckyGiftRewardStreamTask { return; } - for (MapRecord record : records) { + for (MapRecord record : records) { processRecord(record); redisService.setString(lastIdKey(), record.getId().getValue()); } @@ -78,9 +80,9 @@ public class LuckyGiftRewardStreamTask { } } - private void processRecord(MapRecord record) { - Map values = record.getValue(); - String payload = getValueAsString(values, "payload"); + private void processRecord(MapRecord record) { + Map values = record.getValue(); + String payload = values.get("payload"); if (StringUtils.isBlank(payload)) { throw new IllegalStateException("lucky gift reward payload is blank, recordId=" + record.getId().getValue()); @@ -93,7 +95,7 @@ public class LuckyGiftRewardStreamTask { } if (StringUtils.isBlank(event.getEventId())) { - event.setEventId(getValueAsString(values, "eventId")); + event.setEventId(values.get("eventId")); } if (StringUtils.isBlank(event.getEventId()) && StringUtils.isNotBlank(event.getBusinessId())) { event.setEventId("LUCKY_GIFT_REWARD:" + event.getBusinessId()); @@ -108,12 +110,12 @@ public class LuckyGiftRewardStreamTask { redisService.setString(processedKey, "1", 7, TimeUnit.DAYS); } - private String getValueAsString(Map values, String key) { - Object value = values.get(key); - return value == null ? null : String.valueOf(value); - } - private String lastIdKey() { return LAST_ID_KEY_PREFIX + streamKey; } + + @SuppressWarnings("unchecked") + private StreamOperations streamOperations() { + return (StreamOperations) (StreamOperations) stringRedisTemplate.opsForStream(); + } } diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExeTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExeTest.java new file mode 100644 index 00000000..2eaf4016 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/RoomVoiceDiscoverQryExeTest.java @@ -0,0 +1,122 @@ +package com.red.circle.other.app.command.room.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import com.red.circle.common.business.enums.SVIPLevelEnum; +import com.red.circle.framework.core.dto.ReqSysOrigin; +import com.red.circle.live.inner.endpoint.LiveMicClient; +import com.red.circle.other.app.common.room.RoomRegionFilterCommon; +import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; +import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; +import com.red.circle.other.app.service.game.GameLudoService; +import com.red.circle.other.app.service.room.RocketStatusCacheService; +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.gateway.user.ability.UserSVipGateway; +import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.domain.model.user.ability.RegionConfig; +import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport; +import com.red.circle.other.infra.database.cache.service.user.RegionRoomCacheService; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; +import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; +import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; +import com.red.circle.other.infra.database.mongo.service.live.RoomUserBlacklistService; +import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.springframework.data.redis.core.RedisTemplate; + +class RoomVoiceDiscoverQryExeTest { + + @Test + void execute_shouldIgnoreAllRegionAndOnlyReturnCurrentRegionRooms() { + UserSVipGateway userSVipGateway = mock(UserSVipGateway.class); + UserRegionGateway userRegionGateway = mock(UserRegionGateway.class); + RoomRegionFilterCommon roomRegionFilterCommon = mock(RoomRegionFilterCommon.class); + RoomVoiceProfileCommon roomVoiceProfileCommon = mock(RoomVoiceProfileCommon.class); + ActiveVoiceRoomService activeVoiceRoomService = mock(ActiveVoiceRoomService.class); + RoomUserBlacklistService roomUserBlacklistService = mock(RoomUserBlacklistService.class); + LatestMobileDeviceService latestMobileDeviceService = mock(LatestMobileDeviceService.class); + RoomProfileManagerService roomProfileManagerService = mock(RoomProfileManagerService.class); + RegionRoomCacheService regionRoomCacheService = mock(RegionRoomCacheService.class); + LiveMicClient liveMicClient = mock(LiveMicClient.class); + RedisTemplate redisTemplate = mock(RedisTemplate.class); + RocketStatusCacheService rocketStatusCacheService = mock(RocketStatusCacheService.class); + GameLudoService gameLudoService = mock(GameLudoService.class); + UserProfileGateway userProfileGateway = mock(UserProfileGateway.class); + CountryCodeAliasSupport countryCodeAliasSupport = mock(CountryCodeAliasSupport.class); + RoomVoiceDiscoverQryExe exe = new RoomVoiceDiscoverQryExe( + userSVipGateway, + userRegionGateway, + roomRegionFilterCommon, + roomVoiceProfileCommon, + activeVoiceRoomService, + roomUserBlacklistService, + latestMobileDeviceService, + roomProfileManagerService, + regionRoomCacheService, + liveMicClient, + redisTemplate, + rocketStatusCacheService, + gameLudoService, + userProfileGateway, + countryCodeAliasSupport + ); + + AppExtCommand cmd = new AppExtCommand(); + cmd.setReqUserId(1L); + cmd.setReqSysOrigin(ReqSysOrigin.of("YOLO", "YOLO")); + cmd.setAllRegion(true); + + RegionConfig currentRegion = new RegionConfig().setRegionCode("AE").setCountryCodes("AE,SA"); + UserProfile userProfile = new UserProfile(); + ActiveVoiceRoom activeVoiceRoom = new ActiveVoiceRoom().setId(101L).setUserId(201L); + RoomProfileManager fallbackRoomManager = new RoomProfileManager(); + fallbackRoomManager.setId(102L); + fallbackRoomManager.setUserId(202L); + + RoomVoiceProfileCO activeRoomCo = new RoomVoiceProfileCO().setId(101L).setUserId(201L); + RoomVoiceProfileCO fallbackRoomCo = new RoomVoiceProfileCO().setId(102L).setUserId(202L); + + when(roomRegionFilterCommon.requireRegionConfig(1L)).thenReturn(currentRegion); + when(userProfileGateway.getByUserId(1L)).thenReturn(userProfile); + when(regionRoomCacheService.getRegionsRoom("YOLOAE")).thenReturn(null); + when(activeVoiceRoomService.listByRegion("YOLO", "AE", 50)).thenReturn(List.of(activeVoiceRoom)); + when(roomRegionFilterCommon.filterActiveVoiceRoomsByRegion(List.of(activeVoiceRoom), "AE")) + .thenReturn(List.of(activeVoiceRoom)); + when(redisTemplate.keys("empty_room_cache:*")).thenReturn(Set.of()); + when(roomVoiceProfileCommon.toListRoomVoiceProfileCO(List.of(activeVoiceRoom))).thenReturn( + List.of(activeRoomCo) + ); + when(roomRegionFilterCommon.listCountryCodes(currentRegion)).thenReturn(List.of("AE", "SA")); + when(roomProfileManagerService.listSelectiveLimitByCountryCodes( + "YOLO", List.of("AE", "SA"), List.of(101L), 50 + )).thenReturn(List.of(fallbackRoomManager)); + when(roomRegionFilterCommon.filterRoomProfileManagersByRegion( + List.of(fallbackRoomManager), "AE" + )).thenReturn(List.of(fallbackRoomManager)); + when(roomVoiceProfileCommon.listRoomVoiceProfilesV2(List.of(fallbackRoomManager))).thenReturn( + List.of(fallbackRoomCo) + ); + when(userSVipGateway.checkSVipIdentity(anySet())).thenReturn( + Map.of(201L, SVIPLevelEnum.NONE, 202L, SVIPLevelEnum.NONE) + ); + when(rocketStatusCacheService.batchGetRocketStatus(List.of(101L, 102L))).thenReturn(Map.of()); + when(gameLudoService.listGameCoverByRoomIds(List.of(101L, 102L))).thenReturn(Map.of()); + + List roomList = exe.execute(cmd); + + assertEquals(List.of(activeRoomCo, fallbackRoomCo), roomList); + verify(activeVoiceRoomService).listByRegion("YOLO", "AE", 50); + verify(activeVoiceRoomService, never()).listDiscover("YOLO", true, "AE", null, 50); + } +} diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExeTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExeTest.java new file mode 100644 index 00000000..28fdc812 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/room/query/SearchRegionRoomQryExeTest.java @@ -0,0 +1,74 @@ +package com.red.circle.other.app.command.room.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.red.circle.framework.core.dto.ReqSysOrigin; +import com.red.circle.framework.mybatis.constant.PageConstant; +import com.red.circle.other.app.common.room.RoomRegionFilterCommon; +import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; +import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; +import com.red.circle.other.app.dto.cmd.room.RegionSearchRoomQryCmd; +import com.red.circle.other.domain.model.user.ability.RegionConfig; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; +import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; +import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; +import java.util.List; +import org.junit.jupiter.api.Test; + +class SearchRegionRoomQryExeTest { + + @Test + void execute_shouldUseCurrentUserRegionInsteadOfCountryCode() { + ActiveVoiceRoomService activeVoiceRoomService = mock(ActiveVoiceRoomService.class); + RoomRegionFilterCommon roomRegionFilterCommon = mock(RoomRegionFilterCommon.class); + RoomVoiceProfileCommon roomVoiceProfileCommon = mock(RoomVoiceProfileCommon.class); + RoomProfileManagerService roomProfileManagerService = mock(RoomProfileManagerService.class); + SearchRegionRoomQryExe exe = new SearchRegionRoomQryExe( + activeVoiceRoomService, + roomRegionFilterCommon, + roomVoiceProfileCommon, + roomProfileManagerService + ); + + RegionSearchRoomQryCmd cmd = new RegionSearchRoomQryCmd(); + cmd.setReqUserId(1L); + cmd.setReqSysOrigin(ReqSysOrigin.of("YOLO", "YOLO")); + cmd.setCountryCode("PK"); + + RegionConfig currentRegion = new RegionConfig().setRegionCode("AE").setCountryCodes("AE,SA"); + ActiveVoiceRoom activeVoiceRoom = new ActiveVoiceRoom().setId(101L).setUserId(201L); + RoomProfileManager fallbackRoomManager = new RoomProfileManager(); + fallbackRoomManager.setId(102L); + fallbackRoomManager.setUserId(202L); + + RoomVoiceProfileCO activeRoomCo = new RoomVoiceProfileCO().setId(101L).setUserId(201L); + RoomVoiceProfileCO fallbackRoomCo = new RoomVoiceProfileCO().setId(102L).setUserId(202L); + + when(roomRegionFilterCommon.requireRegionConfig(1L)).thenReturn(currentRegion); + when(activeVoiceRoomService.listByRegion("YOLO", "AE", PageConstant.MAX_LIMIT_SIZE)).thenReturn( + List.of(activeVoiceRoom) + ); + when(roomRegionFilterCommon.filterActiveVoiceRoomsByRegion(List.of(activeVoiceRoom), "AE")) + .thenReturn(List.of(activeVoiceRoom)); + when(roomRegionFilterCommon.listCountryCodes(currentRegion)).thenReturn(List.of("AE", "SA")); + when(roomProfileManagerService.listSelectiveLimitByCountryCodes( + "YOLO", List.of("AE", "SA"), List.of(101L), PageConstant.MAX_LIMIT_SIZE + )).thenReturn(List.of(fallbackRoomManager)); + when(roomRegionFilterCommon.filterRoomProfileManagersByRegion( + List.of(fallbackRoomManager), "AE" + )).thenReturn(List.of(fallbackRoomManager)); + when(roomVoiceProfileCommon.toListRoomVoiceProfileCO(List.of(activeVoiceRoom))).thenReturn( + List.of(activeRoomCo) + ); + when(roomVoiceProfileCommon.listRoomVoiceProfilesV2(List.of(fallbackRoomManager))).thenReturn( + List.of(fallbackRoomCo) + ); + + List roomList = exe.execute(cmd); + + assertEquals(List.of(activeRoomCo, fallbackRoomCo), roomList); + } +} diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/room/RoomRegionFilterCommonTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/room/RoomRegionFilterCommonTest.java new file mode 100644 index 00000000..5669463d --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/room/RoomRegionFilterCommonTest.java @@ -0,0 +1,61 @@ +package com.red.circle.other.app.common.room; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class RoomRegionFilterCommonTest { + + @Test + void filterActiveVoiceRoomsByRegion_shouldKeepOnlyCurrentRegionRooms() { + UserRegionGateway userRegionGateway = mock(UserRegionGateway.class); + RoomRegionFilterCommon roomRegionFilterCommon = new RoomRegionFilterCommon(userRegionGateway); + + ActiveVoiceRoom currentRegionRoom = new ActiveVoiceRoom().setId(1001L).setUserId(2001L); + ActiveVoiceRoom otherRegionRoom = new ActiveVoiceRoom().setId(1002L).setUserId(2002L); + + when(userRegionGateway.mapRegionCode(Set.of(2001L, 2002L))).thenReturn( + Map.of(2001L, "AE", 2002L, "SA") + ); + + List filteredRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion( + List.of(currentRegionRoom, otherRegionRoom), + "AE" + ); + + assertEquals(List.of(currentRegionRoom), filteredRooms); + } + + @Test + void filterRoomProfileManagersByRegion_shouldKeepOnlyCurrentRegionRooms() { + UserRegionGateway userRegionGateway = mock(UserRegionGateway.class); + RoomRegionFilterCommon roomRegionFilterCommon = new RoomRegionFilterCommon(userRegionGateway); + + RoomProfileManager currentRegionRoom = new RoomProfileManager(); + currentRegionRoom.setId(1001L); + currentRegionRoom.setUserId(2001L); + + RoomProfileManager otherRegionRoom = new RoomProfileManager(); + otherRegionRoom.setId(1002L); + otherRegionRoom.setUserId(2002L); + + when(userRegionGateway.mapRegionCode(Set.of(2001L, 2002L))).thenReturn( + Map.of(2001L, "AE", 2002L, "SA") + ); + + List filteredRooms = roomRegionFilterCommon.filterRoomProfileManagersByRegion( + List.of(currentRegionRoom, otherRegionRoom), + "AE" + ); + + assertEquals(List.of(currentRegionRoom), filteredRooms); + } +} diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java index f2963b0c..87a2169a 100644 --- a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java @@ -18,22 +18,22 @@ import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.StreamOffset; import org.springframework.data.redis.connection.stream.StreamReadOptions; import org.springframework.data.redis.connection.stream.StreamRecords; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StreamOperations; +import org.springframework.data.redis.core.StringRedisTemplate; class LuckyGiftRewardStreamTaskTest { - private RedisTemplate redisTemplate; + private StringRedisTemplate stringRedisTemplate; private RedisService redisService; private LuckyGiftRewardSettlementService settlementService; private LuckyGiftRewardStreamTask task; @BeforeEach void setUp() throws Exception { - redisTemplate = mock(RedisTemplate.class); + stringRedisTemplate = mock(StringRedisTemplate.class); redisService = mock(RedisService.class); settlementService = mock(LuckyGiftRewardSettlementService.class); - task = new LuckyGiftRewardStreamTask(redisTemplate, redisService, settlementService); + task = new LuckyGiftRewardStreamTask(stringRedisTemplate, redisService, settlementService); setField(task, "streamKey", "lucky-gift:reward"); setField(task, "batchSize", 20L); setField(task, "readBlockMillis", 50L); @@ -43,7 +43,7 @@ class LuckyGiftRewardStreamTaskTest { @SuppressWarnings({"rawtypes", "unchecked"}) void consume_shouldSettleEventAndAdvanceStreamOffset() throws Exception { StreamOperations streamOperations = mock(StreamOperations.class); - when(redisTemplate.opsForStream()).thenReturn(streamOperations); + when(stringRedisTemplate.opsForStream()).thenReturn(streamOperations); when(redisService.lock("lucky_gift:reward:consumer:lock", 30)).thenReturn(true); when(redisService.getString("lucky_gift:reward:last-id:lucky-gift:reward")).thenReturn(null); when(redisService.getString("lucky_gift:reward:processed:LUCKY_GIFT_REWARD:biz-1")) @@ -58,10 +58,8 @@ class LuckyGiftRewardStreamTaskTest { + "\"sysOrigin\":\"LIKEI\",\"roomId\":1001,\"sendUserId\":2001," + "\"results\":[{\"orderSeed\":\"order-1\",\"acceptUserId\":3001,\"rewardNum\":80}]}" )); - MapRecord record = - (MapRecord) (MapRecord) sourceRecord; when(streamOperations.read(any(StreamReadOptions.class), any(StreamOffset.class))) - .thenReturn(List.of(record)); + .thenReturn(List.of(sourceRecord)); task.consume(); diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/ActiveVoiceRoomService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/ActiveVoiceRoomService.java index 093a125c..f8751779 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/ActiveVoiceRoomService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/ActiveVoiceRoomService.java @@ -62,15 +62,20 @@ public interface ActiveVoiceRoomService { */ List listOps(AppActiveVoiceRoomQryCmd query); - /** - * 获取发现页数据. - */ - List listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit); - - /** - * 获取区域房间. - */ - List listSearchCountryRoom(String sysOrigin, String countryCode, Integer limit); + /** + * 获取发现页数据. + */ + List listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit); + + /** + * 获取指定区域房间. + */ + List listByRegion(String sysOrigin, String region, Integer limit); + + /** + * 获取区域房间. + */ + List listSearchCountryRoom(String sysOrigin, String countryCode, Integer limit); /** * 获取家族在线房间. diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/RoomProfileManagerService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/RoomProfileManagerService.java index ce297470..18c6df4c 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/RoomProfileManagerService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/RoomProfileManagerService.java @@ -105,11 +105,17 @@ public interface RoomProfileManagerService { List listSelectiveLimit100(String sysOrigin, String countryCode, List excludeIds); - /** - * 获取平台房间信息. - */ - List listSelectiveLimit100(String sysOrigin, List countryCodes, - List excludeIds); + /** + * 获取平台房间信息. + */ + List listSelectiveLimit100(String sysOrigin, List countryCodes, + List excludeIds); + + /** + * 按国家列表获取平台房间信息. + */ + List listSelectiveLimitByCountryCodes(String sysOrigin, + List countryCodes, List excludeIds, Integer limit); /** * 今天创建的房间. diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImpl.java index 880ee829..deed2ad3 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImpl.java @@ -135,7 +135,7 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService { @Override - public List listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit) { + public List listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit) { // 区域隔离 Set userRegionGroup = getRegionGroup(region); Set excludeRegions = userRegionGroup == null ? ALL_SPECIAL_REGIONS : getExcludeRegionsForGroup(userRegionGroup); @@ -186,13 +186,30 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService { ); // 执行聚合操作并返回结果 - return mongoTemplate.aggregate(aggregation, ActiveVoiceRoom.class, ActiveVoiceRoom.class) - .getMappedResults(); - } - - /** - * 获取区域所属的特殊区域组(如果不属于任何特殊组则返回null) - */ + return mongoTemplate.aggregate(aggregation, ActiveVoiceRoom.class, ActiveVoiceRoom.class) + .getMappedResults(); + } + + @Override + public List listByRegion(String sysOrigin, String region, Integer limit) { + Query query = Query.query( + Criteria.where("sysOrigin").is(sysOrigin) + .and("region").is(region) + ).with(Sort.by( + Sort.Order.desc("fixedWeights"), + Sort.Order.desc("weights"), + Sort.Order.desc("onlineQuantity") + )); + + if (Objects.nonNull(limit)) { + query.limit(limit); + } + return mongoTemplate.find(query, ActiveVoiceRoom.class); + } + + /** + * 获取区域所属的特殊区域组(如果不属于任何特殊组则返回null) + */ private Set getRegionGroup(String region) { if (SA_REGIONS.contains(region)) { return SA_REGIONS; diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImpl.java index 383dfdc1..4145379f 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImpl.java @@ -288,8 +288,8 @@ public class RoomProfileManagerServiceImpl implements RoomProfileManagerService } @Override - public List listSelectiveLimit100(String sysOrigin, - List countryCodes, List excludeIds) { + public List listSelectiveLimit100(String sysOrigin, + List countryCodes, List excludeIds) { Criteria criteria = Criteria.where("sysOrigin").in(sysOrigin); if (CollectionUtils.isNotEmpty(countryCodes)) { @@ -301,10 +301,40 @@ public class RoomProfileManagerServiceImpl implements RoomProfileManagerService } criteria.and("activeTime").gte(LocalDateTime.now().minusDays(7)); return filterExpireTimeDecorate( - mongoTemplate.find(Query.query(criteria) - .limit(100), RoomProfileManager.class) - ); - } + mongoTemplate.find(Query.query(criteria) + .limit(100), RoomProfileManager.class) + ); + } + + @Override + public List listSelectiveLimitByCountryCodes(String sysOrigin, + List countryCodes, List excludeIds, Integer limit) { + + Criteria criteria = Criteria.where("sysOrigin").in(sysOrigin) + .and("del").is(Boolean.FALSE); + if (CollectionUtils.isNotEmpty(countryCodes)) { + criteria.and("countryCode").in(countryCodeAliasSupport.expandCodes(countryCodes)); + } + + if (CollectionUtils.isNotEmpty(excludeIds)) { + criteria.and("id").nin(excludeIds); + } + + Query query = Query.query(criteria) + .with(Sort.by( + Sort.Order.desc("activeTime"), + Sort.Order.desc("updateTime"), + Sort.Order.desc("createTime"), + Sort.Order.desc("id") + )); + if (Objects.nonNull(limit)) { + query.limit(limit); + } + + return filterExpireTimeDecorate( + mongoTemplate.find(query, RoomProfileManager.class) + ); + } @Override public List listNowCreateRoom(String sysOrigin, Integer limit) { diff --git a/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImplTest.java b/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImplTest.java new file mode 100644 index 00000000..8cba96d2 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/ActiveVoiceRoomServiceImplTest.java @@ -0,0 +1,50 @@ +package com.red.circle.other.infra.database.mongo.service.live.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport; +import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; +import java.util.List; +import org.bson.Document; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; + +class ActiveVoiceRoomServiceImplTest { + + private MongoTemplate mongoTemplate; + private ActiveVoiceRoomServiceImpl service; + + @BeforeEach + void setUp() { + mongoTemplate = mock(MongoTemplate.class); + CountryCodeAliasSupport countryCodeAliasSupport = mock(CountryCodeAliasSupport.class); + service = new ActiveVoiceRoomServiceImpl(mongoTemplate, countryCodeAliasSupport); + } + + @Test + void listByRegion_shouldQueryCurrentRegionRoomsWithDiscoverySort() { + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Query.class); + when(mongoTemplate.find(queryCaptor.capture(), eq(ActiveVoiceRoom.class))).thenReturn(List.of()); + + service.listByRegion("YOLO", "AE", 50); + + Query query = queryCaptor.getValue(); + Document queryObject = query.getQueryObject(); + assertEquals("YOLO", queryObject.getString("sysOrigin")); + assertEquals("AE", queryObject.getString("region")); + assertEquals(50, query.getLimit()); + + Document sortObject = query.getSortObject(); + assertEquals(-1, sortObject.getInteger("fixedWeights")); + assertEquals(-1, sortObject.getInteger("weights")); + assertEquals(-1, sortObject.getInteger("onlineQuantity")); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImplTest.java b/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImplTest.java index cf037526..4f0f38fd 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImplTest.java +++ b/rc-service/rc-service-other/other-infrastructure/src/test/java/com/red/circle/other/infra/database/mongo/service/live/impl/RoomProfileManagerServiceImplTest.java @@ -14,6 +14,7 @@ import com.red.circle.other.infra.database.cache.service.other.RoomManagerCacheS import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile; import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; +import java.util.Collection; import java.util.List; import java.util.Set; import org.junit.jupiter.api.BeforeEach; @@ -27,6 +28,7 @@ import org.springframework.data.redis.core.RedisTemplate; class RoomProfileManagerServiceImplTest { private MongoTemplate mongoTemplate; + private CountryCodeAliasSupport countryCodeAliasSupport; private RoomProfileManagerServiceImpl service; @BeforeEach @@ -35,7 +37,7 @@ class RoomProfileManagerServiceImplTest { RedisTemplate redisTemplate = mock(RedisTemplate.class); ActiveVoiceRoomService activeVoiceRoomService = mock(ActiveVoiceRoomService.class); RoomManagerCacheService roomManagerCacheService = mock(RoomManagerCacheService.class); - CountryCodeAliasSupport countryCodeAliasSupport = mock(CountryCodeAliasSupport.class); + countryCodeAliasSupport = mock(CountryCodeAliasSupport.class); service = new RoomProfileManagerServiceImpl( mongoTemplate, redisTemplate, @@ -101,4 +103,32 @@ class RoomProfileManagerServiceImplTest { verify(mongoTemplate).updateFirst(queryCaptor.capture(), any(), eq(RoomProfileManager.class)); assertEquals(99L, queryCaptor.getValue().getQueryObject().getLong("id")); } + + @Test + void listSelectiveLimitByCountryCodes_shouldQueryIncludedCountriesWithRecentSort() { + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Query.class); + when(mongoTemplate.find(queryCaptor.capture(), eq(RoomProfileManager.class))).thenReturn( + List.of() + ); + when(countryCodeAliasSupport.expandCodes(List.of("AE"))).thenReturn(Set.of("AE", "AB")); + + service.listSelectiveLimitByCountryCodes("YOLO", List.of("AE"), List.of(88L), 50); + + Query query = queryCaptor.getValue(); + Document queryObject = query.getQueryObject(); + assertEquals("YOLO", ((Document) queryObject.get("sysOrigin")).get("$in", java.util.List.class).get(0)); + assertEquals(Boolean.FALSE, queryObject.getBoolean("del")); + assertEquals( + Set.of("AE", "AB"), + Set.copyOf((Collection) ((Document) queryObject.get("countryCode")).get("$in")) + ); + assertEquals(List.of(88L), ((Document) queryObject.get("id")).get("$nin")); + assertEquals(50, query.getLimit()); + + Document sortObject = query.getSortObject(); + assertEquals(-1, sortObject.getInteger("activeTime")); + assertEquals(-1, sortObject.getInteger("updateTime")); + assertEquals(-1, sortObject.getInteger("createTime")); + assertEquals(-1, sortObject.getInteger("id")); + } } diff --git a/rc-service/rc-service-wallet/wallet-start/pom.xml b/rc-service/rc-service-wallet/wallet-start/pom.xml index 10346415..3dd83547 100644 --- a/rc-service/rc-service-wallet/wallet-start/pom.xml +++ b/rc-service/rc-service-wallet/wallet-start/pom.xml @@ -30,16 +30,22 @@ ${shardingsphere-jdbc-core.version} - - com.google.protobuf - protobuf-java - ${protobuf-java.version} - - - - com.tencentcloudapi - tencentcloud-sdk-java - ${tencentcloud.version} + + com.google.protobuf + protobuf-java + ${protobuf-java.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.tencentcloudapi + tencentcloud-sdk-java + ${tencentcloud.version} diff --git a/rc-service/rc-service-wallet/wallet-start/src/main/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProvider.java b/rc-service/rc-service-wallet/wallet-start/src/main/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProvider.java index e4cfcadb..8317283f 100644 --- a/rc-service/rc-service-wallet/wallet-start/src/main/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProvider.java +++ b/rc-service/rc-service-wallet/wallet-start/src/main/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProvider.java @@ -17,10 +17,13 @@ import java.util.Properties; import org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereDriverURLProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.PropertyPlaceholderHelper; public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider { private static final Logger LOGGER = LoggerFactory.getLogger(NacosDriverURLProvider.class); + private static final PropertyPlaceholderHelper PLACEHOLDER_HELPER = + new PropertyPlaceholderHelper("${", "}", ":", true); private static final String NACOS_TYPE = "nacos:"; @@ -77,6 +80,7 @@ public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider { throw new RuntimeException( "Empty Nacos sharding config: dataId=" + shardingConfigFilename + ", group=" + group); } + content = resolvePlaceholders(content); LOGGER.info(content); return content.getBytes(StandardCharsets.UTF_8); } catch (IOException ex) { @@ -190,4 +194,18 @@ public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider { } return url.substring(nacosIndex + NACOS_TYPE.length()).split("\\?")[0]; } + + String resolvePlaceholders(String content) { + if (!hasText(content)) { + return content; + } + return PLACEHOLDER_HELPER.replacePlaceholders(content, placeholderName -> { + String systemProperty = System.getProperty(placeholderName); + if (hasText(systemProperty)) { + return systemProperty; + } + String envValue = System.getenv(placeholderName); + return hasText(envValue) ? envValue : null; + }); + } } diff --git a/rc-service/rc-service-wallet/wallet-start/src/test/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProviderTest.java b/rc-service/rc-service-wallet/wallet-start/src/test/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProviderTest.java new file mode 100644 index 00000000..871b2ed7 --- /dev/null +++ b/rc-service/rc-service-wallet/wallet-start/src/test/java/com/red/circle/framework/shardingsphere/driver/NacosDriverURLProviderTest.java @@ -0,0 +1,45 @@ +package com.red.circle.framework.shardingsphere.driver; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +class NacosDriverURLProviderTest { + + @AfterEach + void tearDown() { + System.clearProperty("LIKEI_WALLET_MYSQL_JDBC_URL"); + System.clearProperty("LIKEI_MYSQL_USERNAME"); + System.clearProperty("LIKEI_MYSQL_PASSWORD"); + } + + @Test + void resolvePlaceholders_shouldReplaceConfiguredPropertiesAndKeepShardingExpression() { + System.setProperty("LIKEI_WALLET_MYSQL_JDBC_URL", "jdbc:p6spy:mysql://mysql:3306/likei_wallet"); + System.setProperty("LIKEI_MYSQL_USERNAME", "root"); + System.setProperty("LIKEI_MYSQL_PASSWORD", "123456"); + + String content = """ + dataSources: + wallet: + jdbcUrl: ${LIKEI_WALLET_MYSQL_JDBC_URL} + username: ${LIKEI_MYSQL_USERNAME} + password: ${LIKEI_MYSQL_PASSWORD} + rules: + - !SHARDING + tables: + wallet_gold_asset_record: + actualDataNodes: wallet.wallet_gold_asset_record_${1..12} + """; + + String resolved = new NacosDriverURLProvider().resolvePlaceholders(content); + + assertTrue(resolved.contains("jdbcUrl: jdbc:p6spy:mysql://mysql:3306/likei_wallet")); + assertTrue(resolved.contains("username: root")); + assertTrue(resolved.contains("password: 123456")); + assertTrue(resolved.contains("wallet.wallet_gold_asset_record_${1..12}")); + assertEquals(1, resolved.split("wallet.wallet_gold_asset_record_\\$\\{1\\.\\.12}", -1).length - 1); + } +}