增加房间区域过滤,修复后台个人信息

This commit is contained in:
hy001 2026-04-23 13:46:26 +08:00
parent 1fd196094d
commit 4fb7d8af04
19 changed files with 876 additions and 317 deletions

View File

@ -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<UserPhotoWallDTO> listUserPhotoWallNormal(@PathVariable("userId") Long userId) {
return userPhotoWallService.listUserPhotoWallNormal(userId);
}
/**
* 获取用户全部照片墙.
*/
@GetMapping("/all/{userId}")
public List<UserPhotoWallDTO> listUserPhotoWall(@PathVariable("userId") Long userId) {
return userPhotoWallService.listUserPhotoWall(userId);
}
/**
* 用户照片墙分页.
*/
@GetMapping("/page")
public PageResult<UserPhotoWallDTO> pageUserPhotoWall(PageCommand query) {
return userPhotoWallService.pageUserPhotoWall(query);
}
/**
* 用户照片墙审批分页.
*/
@GetMapping("/page/all")
public PageResult<UserPhotoWallDTO> pageAllUserPhotoWall(PhotoWallApprovalTableQryCmd query) {
return userPhotoWallService.pageUserPhotoWall(query);
}
/**
* 删除用户照片墙.
*/
@OpsOperationReqLog("删除用户照片墙")
@GetMapping("/del/{id}")
public void deletePhotoWall(@PathVariable("id") Long id) {
userPhotoWallService.deleteById(id);
}
}

View File

@ -5,21 +5,23 @@ import com.google.common.collect.Sets;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.red.circle.common.business.dto.cmd.AppExtCommand; import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.common.business.enums.SVIPLevelEnum; import com.red.circle.common.business.enums.SVIPLevelEnum;
import com.red.circle.live.inner.endpoint.LiveMicClient; import com.red.circle.live.inner.endpoint.LiveMicClient;
import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; import com.red.circle.other.app.common.room.RoomRegionFilterCommon;
import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; import com.red.circle.other.app.common.room.RoomVoiceProfileCommon;
import com.red.circle.other.app.service.game.GameLudoService; import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO;
import com.red.circle.other.app.service.room.RocketStatusCacheService; import com.red.circle.other.app.service.game.GameLudoService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; 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.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.gateway.user.ability.UserSVipGateway;
import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.rocket.RocketStatus; import com.red.circle.other.domain.rocket.RocketStatus;
import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport; 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.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.ActiveVoiceRoom;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; 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.RoomUserBlacklist;
import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; 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.RoomProfileManagerService;
import com.red.circle.other.infra.database.mongo.service.live.RoomUserBlacklistService; import com.red.circle.other.infra.database.mongo.service.live.RoomUserBlacklistService;
@ -51,10 +53,11 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class RoomVoiceDiscoverQryExe { public class RoomVoiceDiscoverQryExe {
private final UserSVipGateway userSVipGateway; private final UserSVipGateway userSVipGateway;
private final UserRegionGateway userRegionGateway; private final UserRegionGateway userRegionGateway;
private final RoomVoiceProfileCommon roomVoiceProfileCommon; private final RoomRegionFilterCommon roomRegionFilterCommon;
private final ActiveVoiceRoomService activeVoiceRoomService; private final RoomVoiceProfileCommon roomVoiceProfileCommon;
private final ActiveVoiceRoomService activeVoiceRoomService;
private final RoomUserBlacklistService roomUserBlacklistService; private final RoomUserBlacklistService roomUserBlacklistService;
private final LatestMobileDeviceService latestMobileDeviceService; private final LatestMobileDeviceService latestMobileDeviceService;
private final RoomProfileManagerService roomProfileManagerService; private final RoomProfileManagerService roomProfileManagerService;
@ -66,18 +69,14 @@ public class RoomVoiceDiscoverQryExe {
private final UserProfileGateway userProfileGateway; private final UserProfileGateway userProfileGateway;
private final CountryCodeAliasSupport countryCodeAliasSupport; private final CountryCodeAliasSupport countryCodeAliasSupport;
private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*"; private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*";
private static final int DISCOVERY_LIMIT = 50;
// 区域隔离常量 - 与ActiveVoiceRoomServiceImpl保持一致
private static final Set<String> TR_REGIONS = Set.of("TR"); public List<RoomVoiceProfileCO> execute(AppExtCommand cmd) {
private static final Set<String> SA_REGIONS = Set.of("BD","SA", "IN", "PK"); RegionConfig currentRegion = roomRegionFilterCommon.requireRegionConfig(cmd.requiredReqUserId());
private static final Set<String> 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"); String region = currentRegion.getRegionCode();
private static final Set<String> OTHER_REGIONS = Set.of("OTHER", "PH", "ID", "GH", "IR", "AF", "NG", "DE", "MYS", "TM", "AZ", "NP"); UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId());
public List<RoomVoiceProfileCO> execute(AppExtCommand cmd) {
String region = cmd.isAllRegion() ? "ALL" : userRegionGateway.getRegionCode(cmd.requiredReqUserId());
UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId());
String key = cmd.requireReqSysOrigin() + region; String key = cmd.requireReqSysOrigin() + region;
String regionsRoom = regionRoomCacheService.getRegionsRoom(key); String regionsRoom = regionRoomCacheService.getRegionsRoom(key);
if (Objects.nonNull(regionsRoom)) { if (Objects.nonNull(regionsRoom)) {
@ -92,29 +91,49 @@ public class RoomVoiceDiscoverQryExe {
} }
return availableRooms; return availableRooms;
} }
String userCountryCode = userProfile != null ? userProfile.getCountryCode() : null; List<ActiveVoiceRoom> activeVoiceRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion(
List<ActiveVoiceRoom> activeVoiceRooms = activeVoiceRoomService.listDiscover(cmd.requireReqSysOrigin(), cmd.isAllRegion(), region, userCountryCode, 50); activeVoiceRoomService.listByRegion(cmd.requireReqSysOrigin(), region, DISCOVERY_LIMIT),
region
// 查询没人的房间缓存加入进去 );
List<ActiveVoiceRoom> emptyRooms = getEmptyRoomsFromCache();
if (CollectionUtils.isNotEmpty(emptyRooms)) { // 查询没人的房间缓存加入进去
List<ActiveVoiceRoom> matchedEmptyRooms = filterEmptyRoomsByRegion(emptyRooms, cmd.requireReqSysOrigin(), cmd.isAllRegion(), region, userCountryCode); List<ActiveVoiceRoom> emptyRooms = getEmptyRoomsFromCache();
if (CollectionUtils.isNotEmpty(matchedEmptyRooms)) { if (CollectionUtils.isNotEmpty(emptyRooms)) {
List<ActiveVoiceRoom> allRooms = new ArrayList<>(activeVoiceRooms); List<ActiveVoiceRoom> matchedEmptyRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion(
Set<Long> existingRoomIds = activeVoiceRooms.stream() emptyRooms.stream()
.filter(room -> Objects.equals(cmd.requireReqSysOrigin(), room.getSysOrigin()))
.toList(),
region
);
if (CollectionUtils.isNotEmpty(matchedEmptyRooms)) {
List<ActiveVoiceRoom> allRooms = new ArrayList<>(activeVoiceRooms);
Set<Long> existingRoomIds = activeVoiceRooms.stream()
.map(ActiveVoiceRoom::getId) .map(ActiveVoiceRoom::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
matchedEmptyRooms.stream() matchedEmptyRooms.stream()
.filter(room -> !existingRoomIds.contains(room.getId())) .filter(room -> !existingRoomIds.contains(room.getId()))
.forEach(allRooms::add); .forEach(allRooms::add);
activeVoiceRooms = allRooms; activeVoiceRooms = allRooms;
} }
} }
List<RoomVoiceProfileCO> roomList = roomVoiceProfileCommon.toListRoomVoiceProfileCO(activeVoiceRooms); List<RoomVoiceProfileCO> roomList = roomVoiceProfileCommon.toListRoomVoiceProfileCO(activeVoiceRooms);
if (roomList.size() < DISCOVERY_LIMIT) {
if (CollectionUtils.isEmpty(roomList)) { List<RoomProfileManager> fallbackRoomManagers = roomRegionFilterCommon.filterRoomProfileManagersByRegion(
return CollectionUtils.newArrayList(); 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<Long, SVIPLevelEnum> svipLevelEnumMap = userSVipGateway.checkSVipIdentity( Map<Long, SVIPLevelEnum> svipLevelEnumMap = userSVipGateway.checkSVipIdentity(
roomList.stream().map(RoomVoiceProfileCO::getUserId).collect( roomList.stream().map(RoomVoiceProfileCO::getUserId).collect(
@ -129,9 +148,25 @@ public class RoomVoiceDiscoverQryExe {
fillRocketStatus(roomList); fillRocketStatus(roomList);
// 填充游戏图标 // 填充游戏图标
fillGameIcon(roomList); fillGameIcon(roomList);
return roomList; return roomList;
} }
private List<RoomVoiceProfileCO> mergeRoomList(List<RoomVoiceProfileCO> currentRooms,
List<RoomVoiceProfileCO> fallbackRooms, int limit) {
if (CollectionUtils.isEmpty(fallbackRooms)) {
return currentRooms.stream().limit(limit).toList();
}
List<RoomVoiceProfileCO> mergedRooms = new ArrayList<>(currentRooms);
Set<Long> 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<RoomVoiceProfileCO> roomList) { private void fillGameIcon(List<RoomVoiceProfileCO> roomList) {
if (CollectionUtils.isEmpty(roomList)) { if (CollectionUtils.isEmpty(roomList)) {
@ -244,7 +279,7 @@ public class RoomVoiceDiscoverQryExe {
} }
private List<ActiveVoiceRoom> getRoomListLaneCodes(List<ActiveVoiceRoom> roomList, String langeCodes, Integer limit, Long userId) { private List<ActiveVoiceRoom> getRoomListLaneCodes(List<ActiveVoiceRoom> roomList, String langeCodes, Integer limit, Long userId) {
Set<Long> roomUserIds = roomList.stream() Set<Long> roomUserIds = roomList.stream()
.filter(room -> Objects.equals(langeCodes, latestMobileDeviceService.getLatestLanguage(room.getUserId()))) .filter(room -> Objects.equals(langeCodes, latestMobileDeviceService.getLatestLanguage(room.getUserId())))
.map(ActiveVoiceRoom::getId) .map(ActiveVoiceRoom::getId)
@ -257,97 +292,7 @@ public class RoomVoiceDiscoverQryExe {
} else { } else {
return filteredRooms.subList(0, limit); return filteredRooms.subList(0, limit);
} }
} }
private List<ActiveVoiceRoom> filterEmptyRoomsByRegion(List<ActiveVoiceRoom> 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<String> userRegionGroup = getRegionGroup(userRegion);
Set<String> roomRegionGroup = getRegionGroup(roomRegion);
// 如果用户在特殊区域组只显示同组房间
if (userRegionGroup != null) {
return userRegionGroup.contains(roomRegion);
}
// 如果用户不在特殊区域组排除所有特殊区域组房间
return roomRegionGroup == null;
}
/**
* 获取区域所属的特殊区域组如果不属于任何特殊组则返回null
*/
private Set<String> 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<String> getRegions(String region) {
List<String> 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<String> 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<>();
}
/** /**
* 从缓存中获取没人的房间 * 从缓存中获取没人的房间

View File

@ -1,18 +1,21 @@
package com.red.circle.other.app.command.room.query; package com.red.circle.other.app.command.room.query;
import com.red.circle.framework.mybatis.constant.PageConstant; import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.app.common.room.RoomVoiceProfileCommon; import com.red.circle.other.app.common.room.RoomRegionFilterCommon;
import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO; import com.red.circle.other.app.common.room.RoomVoiceProfileCommon;
import com.red.circle.other.app.dto.cmd.room.RegionSearchRoomQryCmd; import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO;
import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom; import com.red.circle.other.app.dto.cmd.room.RegionSearchRoomQryCmd;
import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService; import com.red.circle.other.domain.model.user.ability.RegionConfig;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
import lombok.RequiredArgsConstructor; import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService;
import org.springframework.stereotype.Component; import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.tool.core.collection.CollectionUtils;
import java.util.List; import java.util.ArrayList;
import java.util.stream.Collectors; 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 @RequiredArgsConstructor
public class SearchRegionRoomQryExe { public class SearchRegionRoomQryExe {
private final ActiveVoiceRoomService activeVoiceRoomService; private final ActiveVoiceRoomService activeVoiceRoomService;
private final RoomVoiceProfileCommon roomVoiceProfileCommon; private final RoomRegionFilterCommon roomRegionFilterCommon;
private final RoomProfileManagerService roomProfileManagerService; private final RoomVoiceProfileCommon roomVoiceProfileCommon;
private final RoomProfileManagerService roomProfileManagerService;
public List<RoomVoiceProfileCO> execute(RegionSearchRoomQryCmd cmd) {
public List<RoomVoiceProfileCO> execute(RegionSearchRoomQryCmd cmd) {
List<ActiveVoiceRoom> activeVoiceRooms = activeVoiceRoomService RegionConfig currentRegion = roomRegionFilterCommon.requireRegionConfig(cmd.requiredReqUserId());
.listSearchCountryRoom(cmd.getReqSysOrigin().getOrigin(), cmd.getCountryCode(), String currentRegionCode = currentRegion.getRegionCode();
PageConstant.MAX_LIMIT_SIZE);
List<ActiveVoiceRoom> activeVoiceRooms = roomRegionFilterCommon.filterActiveVoiceRoomsByRegion(
if (CollectionUtils.isNotEmpty(activeVoiceRooms) && activeVoiceRooms.size() >= 100) { activeVoiceRoomService.listByRegion(cmd.requireReqSysOrigin(), currentRegionCode,
return assemblyRoomVoiceProfile(activeVoiceRooms); PageConstant.MAX_LIMIT_SIZE),
} currentRegionCode
);
if (CollectionUtils.isEmpty(activeVoiceRooms)) {
return roomVoiceProfileCommon.listRoomVoiceProfilesV2( if (CollectionUtils.isNotEmpty(activeVoiceRooms) && activeVoiceRooms.size() >= 100) {
roomProfileManagerService.listSelectiveLimit100(cmd.requireReqSysOrigin(), return assemblyRoomVoiceProfile(activeVoiceRooms);
cmd.getCountryCode(), null)); }
}
List<RoomProfileManager> fallbackRoomManagers = roomRegionFilterCommon.filterRoomProfileManagersByRegion(
List<RoomVoiceProfileCO> roomVoiceProfiles = assemblyRoomVoiceProfile(activeVoiceRooms); roomProfileManagerService.listSelectiveLimitByCountryCodes(
roomVoiceProfiles.addAll(roomVoiceProfileCommon.listRoomVoiceProfilesV2( cmd.requireReqSysOrigin(),
roomProfileManagerService.listSelectiveLimit100(cmd.requireReqSysOrigin(), roomRegionFilterCommon.listCountryCodes(currentRegion),
cmd.getCountryCode(), activeVoiceRooms activeVoiceRooms.stream().map(ActiveVoiceRoom::getId).collect(Collectors.toList()),
.stream().map(ActiveVoiceRoom::getId).collect(Collectors.toList())))); PageConstant.MAX_LIMIT_SIZE
return roomVoiceProfiles; ),
} currentRegionCode
);
if (CollectionUtils.isEmpty(activeVoiceRooms)) {
return roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers);
}
List<RoomVoiceProfileCO> roomVoiceProfiles = new ArrayList<>(assemblyRoomVoiceProfile(activeVoiceRooms));
roomVoiceProfiles.addAll(roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers));
return roomVoiceProfiles.stream().limit(PageConstant.MAX_LIMIT_SIZE).toList();
}
private List<RoomVoiceProfileCO> assemblyRoomVoiceProfile( private List<RoomVoiceProfileCO> assemblyRoomVoiceProfile(
List<ActiveVoiceRoom> activeVoiceRooms) { List<ActiveVoiceRoom> activeVoiceRooms) {

View File

@ -1,31 +1,27 @@
package com.red.circle.other.app.command.team; 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.common.business.enums.OperationUserOrigin; import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
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.cmd.message.notice.official.NoticeExtTemplateTypeCmd; import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; import com.red.circle.other.app.dto.cmd.team.TeamUserApplyCmd;
import com.red.circle.other.app.dto.cmd.team.DbInviteAgentCmd; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.app.dto.cmd.team.DbLeaderInviteBdCmd; import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
import com.red.circle.other.app.dto.cmd.team.TeamUserApplyCmd; import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
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.TeamApplicationProcess;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile; 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.TeamApplicationProcessService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; 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.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.infra.database.rds.service.user.device.LatestMobileDeviceService; import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.other.inner.asserts.user.UserRelationErrorCode;
import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; import com.red.circle.other.inner.enums.config.EnumConfigKey;
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.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.asserts.team.TeamErrorCode; 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.model.dto.agency.agency.TeamStatus;
import com.red.circle.other.inner.enums.team.TeamAppProcessReason; 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 com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* 用户申请加入团队. * 用户申请加入团队.
@ -52,19 +48,21 @@ import org.springframework.stereotype.Component;
*/ */
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class TeamUserApplyJoinExe { public class TeamUserApplyJoinExe {
private final WalletGoldClient walletGoldClient; private static final String TEAM_APPLY_MESSAGE_PATH = "/message";
private final UserRegionGateway userRegionGateway;
private final UserProfileGateway userProfileGateway; private final WalletGoldClient walletGoldClient;
private final TeamMemberService teamMemberService; private final UserRegionGateway userRegionGateway;
private final TeamProfileService teamProfileService; private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor; private final TeamMemberService teamMemberService;
private final LatestMobileDeviceService latestMobileDeviceService; private final TeamProfileService teamProfileService;
private final TeamApplicationProcessService teamApplicationProcessService; private final OfficialNoticeClient officialNoticeClient;
private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; private final UserProfileAppConvertor userProfileAppConvertor;
private final RegisterDeviceGateway registerDeviceGateway; private final LatestMobileDeviceService latestMobileDeviceService;
private final EnumConfigCacheService enumConfigCacheService; private final TeamApplicationProcessService teamApplicationProcessService;
private final RegisterDeviceGateway registerDeviceGateway;
private final EnumConfigCacheService enumConfigCacheService;
public String execute(TeamUserApplyCmd cmd) { public String execute(TeamUserApplyCmd cmd) {
@ -167,40 +165,31 @@ public class TeamUserApplyJoinExe {
return teamIds.contains(cmd.getTeamId()); return teamIds.contains(cmd.getTeamId());
} }
private void sendMessage(TeamUserApplyCmd cmd, TeamProfile teamProfile) { private void sendMessage(TeamUserApplyCmd cmd, TeamProfile teamProfile) {
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO( UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(cmd.requiredReqUserId()) userProfileGateway.getByUserId(cmd.requiredReqUserId())
); );
ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile); ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
String h5Url = enumConfigCacheService.getValue(EnumConfigKey.H5_DOMAIN_BASE_URL_V2, String h5Url = enumConfigCacheService.getValue(EnumConfigKey.H5_DOMAIN_BASE_URL_V2,
cmd.requireReqSysOrigin()); cmd.requireReqSysOrigin());
String expand = null;
if (StringUtils.isBlank(h5Url)) { if (StringUtils.isNotBlank(h5Url)) {
return; expand = (h5Url.endsWith("/") ? h5Url.substring(0, h5Url.length() - 1) : h5Url)
} + TEAM_APPLY_MESSAGE_PATH;
String path = null; }
if (Objects.equals(cmd.requireReqSysOriginEnum(), SysOriginPlatformEnum.TARAB)) {
path = "/team-tarab"; NoticeExtTemplateTypeCmd.NoticeExtTemplateTypeCmdBuilder builder =
} NoticeExtTemplateTypeCmd.builder()
if (Objects.equals(cmd.requireReqSysOriginEnum(), SysOriginPlatformEnum.YOLO)) { .toAccount(teamProfile.getOwnUserId())
cmd.setUrl("/anchor_agent_yolo"); .noticeType(OfficialNoticeTypeEnum.USER_SEND_APPLY_TEAM)
if (StringUtils.isNotBlank(cmd.getUrl())){ .templateParam(MapBuilder.builder()
path = cmd.getUrl(); .put("userProfile", String.format("%s(%s)", userProfile.getUserNickname(),
} userProfile.actualAccount()))
} .build());
if (StringUtils.isBlank(path)) { if (StringUtils.isNotBlank(expand)) {
return; builder.expand(expand);
} }
/*officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder() officialNoticeClient.send(builder.build());
.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()
);*/
}
}

View File

@ -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<String> 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<ActiveVoiceRoom> filterActiveVoiceRoomsByRegion(
List<ActiveVoiceRoom> activeVoiceRooms, String regionCode) {
return filterByRegionCode(activeVoiceRooms, ActiveVoiceRoom::getUserId, regionCode);
}
public List<RoomProfileManager> filterRoomProfileManagersByRegion(
List<RoomProfileManager> roomProfileManagers, String regionCode) {
return filterByRegionCode(roomProfileManagers, RoomProfileManager::getUserId, regionCode);
}
private <T> List<T> filterByRegionCode(List<T> source, Function<T, Long> userIdGetter,
String regionCode) {
if (CollectionUtils.isEmpty(source) || StringUtils.isBlank(regionCode)) {
return Lists.newArrayList();
}
Set<Long> userIds = source.stream()
.map(userIdGetter)
.filter(Objects::nonNull)
.collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
if (CollectionUtils.isEmpty(userIds)) {
return Lists.newArrayList();
}
Map<Long, String> 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();
}
}

View File

@ -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.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset; import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions; 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.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; 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 LAST_ID_KEY_PREFIX = "lucky_gift:reward:last-id:";
private static final String PROCESSED_KEY_PREFIX = "lucky_gift:reward:processed:"; private static final String PROCESSED_KEY_PREFIX = "lucky_gift:reward:processed:";
private final RedisTemplate<String, Object> redisTemplate; private final StringRedisTemplate stringRedisTemplate;
private final RedisService redisService; private final RedisService redisService;
private final LuckyGiftRewardSettlementService luckyGiftRewardSettlementService; private final LuckyGiftRewardSettlementService luckyGiftRewardSettlementService;
@ -59,7 +60,8 @@ public class LuckyGiftRewardStreamTask {
StreamReadOptions readOptions = StreamReadOptions.empty() StreamReadOptions readOptions = StreamReadOptions.empty()
.count(Objects.requireNonNullElse(batchSize, 20L)) .count(Objects.requireNonNullElse(batchSize, 20L))
.block(Duration.ofMillis(Objects.requireNonNullElse(readBlockMillis, 200L))); .block(Duration.ofMillis(Objects.requireNonNullElse(readBlockMillis, 200L)));
List<MapRecord<String, Object, Object>> records = redisTemplate.opsForStream().read( StreamOperations<String, String, String> streamOperations = streamOperations();
List<MapRecord<String, String, String>> records = streamOperations.read(
readOptions, readOptions,
StreamOffset.create(streamKey, ReadOffset.from(lastId)) StreamOffset.create(streamKey, ReadOffset.from(lastId))
); );
@ -67,7 +69,7 @@ public class LuckyGiftRewardStreamTask {
return; return;
} }
for (MapRecord<String, Object, Object> record : records) { for (MapRecord<String, String, String> record : records) {
processRecord(record); processRecord(record);
redisService.setString(lastIdKey(), record.getId().getValue()); redisService.setString(lastIdKey(), record.getId().getValue());
} }
@ -78,9 +80,9 @@ public class LuckyGiftRewardStreamTask {
} }
} }
private void processRecord(MapRecord<String, Object, Object> record) { private void processRecord(MapRecord<String, String, String> record) {
Map<Object, Object> values = record.getValue(); Map<String, String> values = record.getValue();
String payload = getValueAsString(values, "payload"); String payload = values.get("payload");
if (StringUtils.isBlank(payload)) { if (StringUtils.isBlank(payload)) {
throw new IllegalStateException("lucky gift reward payload is blank, recordId=" throw new IllegalStateException("lucky gift reward payload is blank, recordId="
+ record.getId().getValue()); + record.getId().getValue());
@ -93,7 +95,7 @@ public class LuckyGiftRewardStreamTask {
} }
if (StringUtils.isBlank(event.getEventId())) { if (StringUtils.isBlank(event.getEventId())) {
event.setEventId(getValueAsString(values, "eventId")); event.setEventId(values.get("eventId"));
} }
if (StringUtils.isBlank(event.getEventId()) && StringUtils.isNotBlank(event.getBusinessId())) { if (StringUtils.isBlank(event.getEventId()) && StringUtils.isNotBlank(event.getBusinessId())) {
event.setEventId("LUCKY_GIFT_REWARD:" + event.getBusinessId()); event.setEventId("LUCKY_GIFT_REWARD:" + event.getBusinessId());
@ -108,12 +110,12 @@ public class LuckyGiftRewardStreamTask {
redisService.setString(processedKey, "1", 7, TimeUnit.DAYS); redisService.setString(processedKey, "1", 7, TimeUnit.DAYS);
} }
private String getValueAsString(Map<Object, Object> values, String key) {
Object value = values.get(key);
return value == null ? null : String.valueOf(value);
}
private String lastIdKey() { private String lastIdKey() {
return LAST_ID_KEY_PREFIX + streamKey; return LAST_ID_KEY_PREFIX + streamKey;
} }
@SuppressWarnings("unchecked")
private StreamOperations<String, String, String> streamOperations() {
return (StreamOperations<String, String, String>) (StreamOperations<?, ?, ?>) stringRedisTemplate.opsForStream();
}
} }

View File

@ -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<String, Object> 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<RoomVoiceProfileCO> 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);
}
}

View File

@ -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<RoomVoiceProfileCO> roomList = exe.execute(cmd);
assertEquals(List.of(activeRoomCo, fallbackRoomCo), roomList);
}
}

View File

@ -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<ActiveVoiceRoom> 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<RoomProfileManager> filteredRooms = roomRegionFilterCommon.filterRoomProfileManagersByRegion(
List.of(currentRegionRoom, otherRegionRoom),
"AE"
);
assertEquals(List.of(currentRegionRoom), filteredRooms);
}
}

View File

@ -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.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions; import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.connection.stream.StreamRecords; 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.StreamOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
class LuckyGiftRewardStreamTaskTest { class LuckyGiftRewardStreamTaskTest {
private RedisTemplate<String, Object> redisTemplate; private StringRedisTemplate stringRedisTemplate;
private RedisService redisService; private RedisService redisService;
private LuckyGiftRewardSettlementService settlementService; private LuckyGiftRewardSettlementService settlementService;
private LuckyGiftRewardStreamTask task; private LuckyGiftRewardStreamTask task;
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
redisTemplate = mock(RedisTemplate.class); stringRedisTemplate = mock(StringRedisTemplate.class);
redisService = mock(RedisService.class); redisService = mock(RedisService.class);
settlementService = mock(LuckyGiftRewardSettlementService.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, "streamKey", "lucky-gift:reward");
setField(task, "batchSize", 20L); setField(task, "batchSize", 20L);
setField(task, "readBlockMillis", 50L); setField(task, "readBlockMillis", 50L);
@ -43,7 +43,7 @@ class LuckyGiftRewardStreamTaskTest {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
void consume_shouldSettleEventAndAdvanceStreamOffset() throws Exception { void consume_shouldSettleEventAndAdvanceStreamOffset() throws Exception {
StreamOperations streamOperations = mock(StreamOperations.class); 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.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:last-id:lucky-gift:reward")).thenReturn(null);
when(redisService.getString("lucky_gift:reward:processed:LUCKY_GIFT_REWARD:biz-1")) when(redisService.getString("lucky_gift:reward:processed:LUCKY_GIFT_REWARD:biz-1"))
@ -58,10 +58,8 @@ class LuckyGiftRewardStreamTaskTest {
+ "\"sysOrigin\":\"LIKEI\",\"roomId\":1001,\"sendUserId\":2001," + "\"sysOrigin\":\"LIKEI\",\"roomId\":1001,\"sendUserId\":2001,"
+ "\"results\":[{\"orderSeed\":\"order-1\",\"acceptUserId\":3001,\"rewardNum\":80}]}" + "\"results\":[{\"orderSeed\":\"order-1\",\"acceptUserId\":3001,\"rewardNum\":80}]}"
)); ));
MapRecord<String, Object, Object> record =
(MapRecord<String, Object, Object>) (MapRecord<?, ?, ?>) sourceRecord;
when(streamOperations.read(any(StreamReadOptions.class), any(StreamOffset.class))) when(streamOperations.read(any(StreamReadOptions.class), any(StreamOffset.class)))
.thenReturn(List.of(record)); .thenReturn(List.of(sourceRecord));
task.consume(); task.consume();

View File

@ -62,15 +62,20 @@ public interface ActiveVoiceRoomService {
*/ */
List<ActiveVoiceRoom> listOps(AppActiveVoiceRoomQryCmd query); List<ActiveVoiceRoom> listOps(AppActiveVoiceRoomQryCmd query);
/** /**
* 获取发现页数据. * 获取发现页数据.
*/ */
List<ActiveVoiceRoom> listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit); List<ActiveVoiceRoom> listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit);
/** /**
* 获取区域房间. * 获取指定区域房间.
*/ */
List<ActiveVoiceRoom> listSearchCountryRoom(String sysOrigin, String countryCode, Integer limit); List<ActiveVoiceRoom> listByRegion(String sysOrigin, String region, Integer limit);
/**
* 获取区域房间.
*/
List<ActiveVoiceRoom> listSearchCountryRoom(String sysOrigin, String countryCode, Integer limit);
/** /**
* 获取家族在线房间. * 获取家族在线房间.

View File

@ -105,11 +105,17 @@ public interface RoomProfileManagerService {
List<RoomProfileManager> listSelectiveLimit100(String sysOrigin, String countryCode, List<RoomProfileManager> listSelectiveLimit100(String sysOrigin, String countryCode,
List<Long> excludeIds); List<Long> excludeIds);
/** /**
* 获取平台房间信息. * 获取平台房间信息.
*/ */
List<RoomProfileManager> listSelectiveLimit100(String sysOrigin, List<String> countryCodes, List<RoomProfileManager> listSelectiveLimit100(String sysOrigin, List<String> countryCodes,
List<Long> excludeIds); List<Long> excludeIds);
/**
* 按国家列表获取平台房间信息.
*/
List<RoomProfileManager> listSelectiveLimitByCountryCodes(String sysOrigin,
List<String> countryCodes, List<Long> excludeIds, Integer limit);
/** /**
* 今天创建的房间. * 今天创建的房间.

View File

@ -135,7 +135,7 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
@Override @Override
public List<ActiveVoiceRoom> listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit) { public List<ActiveVoiceRoom> listDiscover(String sysOrigin, boolean isAllRegion, String region, String countryCode, Integer limit) {
// 区域隔离 // 区域隔离
Set<String> userRegionGroup = getRegionGroup(region); Set<String> userRegionGroup = getRegionGroup(region);
Set<String> excludeRegions = userRegionGroup == null ? ALL_SPECIAL_REGIONS : getExcludeRegionsForGroup(userRegionGroup); Set<String> 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) return mongoTemplate.aggregate(aggregation, ActiveVoiceRoom.class, ActiveVoiceRoom.class)
.getMappedResults(); .getMappedResults();
} }
/** @Override
* 获取区域所属的特殊区域组如果不属于任何特殊组则返回null public List<ActiveVoiceRoom> 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<String> getRegionGroup(String region) { private Set<String> getRegionGroup(String region) {
if (SA_REGIONS.contains(region)) { if (SA_REGIONS.contains(region)) {
return SA_REGIONS; return SA_REGIONS;

View File

@ -288,8 +288,8 @@ public class RoomProfileManagerServiceImpl implements RoomProfileManagerService
} }
@Override @Override
public List<RoomProfileManager> listSelectiveLimit100(String sysOrigin, public List<RoomProfileManager> listSelectiveLimit100(String sysOrigin,
List<String> countryCodes, List<Long> excludeIds) { List<String> countryCodes, List<Long> excludeIds) {
Criteria criteria = Criteria.where("sysOrigin").in(sysOrigin); Criteria criteria = Criteria.where("sysOrigin").in(sysOrigin);
if (CollectionUtils.isNotEmpty(countryCodes)) { if (CollectionUtils.isNotEmpty(countryCodes)) {
@ -301,10 +301,40 @@ public class RoomProfileManagerServiceImpl implements RoomProfileManagerService
} }
criteria.and("activeTime").gte(LocalDateTime.now().minusDays(7)); criteria.and("activeTime").gte(LocalDateTime.now().minusDays(7));
return filterExpireTimeDecorate( return filterExpireTimeDecorate(
mongoTemplate.find(Query.query(criteria) mongoTemplate.find(Query.query(criteria)
.limit(100), RoomProfileManager.class) .limit(100), RoomProfileManager.class)
); );
} }
@Override
public List<RoomProfileManager> listSelectiveLimitByCountryCodes(String sysOrigin,
List<String> countryCodes, List<Long> 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 @Override
public List<RoomProfileManager> listNowCreateRoom(String sysOrigin, Integer limit) { public List<RoomProfileManager> listNowCreateRoom(String sysOrigin, Integer limit) {

View File

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

View File

@ -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.RoomProfile;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager; 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.ActiveVoiceRoomService;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -27,6 +28,7 @@ import org.springframework.data.redis.core.RedisTemplate;
class RoomProfileManagerServiceImplTest { class RoomProfileManagerServiceImplTest {
private MongoTemplate mongoTemplate; private MongoTemplate mongoTemplate;
private CountryCodeAliasSupport countryCodeAliasSupport;
private RoomProfileManagerServiceImpl service; private RoomProfileManagerServiceImpl service;
@BeforeEach @BeforeEach
@ -35,7 +37,7 @@ class RoomProfileManagerServiceImplTest {
RedisTemplate<String, String> redisTemplate = mock(RedisTemplate.class); RedisTemplate<String, String> redisTemplate = mock(RedisTemplate.class);
ActiveVoiceRoomService activeVoiceRoomService = mock(ActiveVoiceRoomService.class); ActiveVoiceRoomService activeVoiceRoomService = mock(ActiveVoiceRoomService.class);
RoomManagerCacheService roomManagerCacheService = mock(RoomManagerCacheService.class); RoomManagerCacheService roomManagerCacheService = mock(RoomManagerCacheService.class);
CountryCodeAliasSupport countryCodeAliasSupport = mock(CountryCodeAliasSupport.class); countryCodeAliasSupport = mock(CountryCodeAliasSupport.class);
service = new RoomProfileManagerServiceImpl( service = new RoomProfileManagerServiceImpl(
mongoTemplate, mongoTemplate,
redisTemplate, redisTemplate,
@ -101,4 +103,32 @@ class RoomProfileManagerServiceImplTest {
verify(mongoTemplate).updateFirst(queryCaptor.capture(), any(), eq(RoomProfileManager.class)); verify(mongoTemplate).updateFirst(queryCaptor.capture(), any(), eq(RoomProfileManager.class));
assertEquals(99L, queryCaptor.getValue().getQueryObject().getLong("id")); assertEquals(99L, queryCaptor.getValue().getQueryObject().getLong("id"));
} }
@Test
void listSelectiveLimitByCountryCodes_shouldQueryIncludedCountriesWithRecentSort() {
ArgumentCaptor<Query> 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<String>) ((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"));
}
} }

View File

@ -30,16 +30,22 @@
<version>${shardingsphere-jdbc-core.version}</version> <version>${shardingsphere-jdbc-core.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>${protobuf-java.version}</version> <version>${protobuf-java.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencentcloudapi</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>tencentcloud-sdk-java</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<version>${tencentcloud.version}</version> <scope>test</scope>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>${tencentcloud.version}</version>
</dependency> </dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>--> <!-- <groupId>com.alibaba.cloud</groupId>-->

View File

@ -17,10 +17,13 @@ import java.util.Properties;
import org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereDriverURLProvider; import org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereDriverURLProvider;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.PropertyPlaceholderHelper;
public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider { public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosDriverURLProvider.class); 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:"; private static final String NACOS_TYPE = "nacos:";
@ -77,6 +80,7 @@ public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider {
throw new RuntimeException( throw new RuntimeException(
"Empty Nacos sharding config: dataId=" + shardingConfigFilename + ", group=" + group); "Empty Nacos sharding config: dataId=" + shardingConfigFilename + ", group=" + group);
} }
content = resolvePlaceholders(content);
LOGGER.info(content); LOGGER.info(content);
return content.getBytes(StandardCharsets.UTF_8); return content.getBytes(StandardCharsets.UTF_8);
} catch (IOException ex) { } catch (IOException ex) {
@ -190,4 +194,18 @@ public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider {
} }
return url.substring(nacosIndex + NACOS_TYPE.length()).split("\\?")[0]; 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;
});
}
} }

View File

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