fix: enforce atyou room region country visibility

This commit is contained in:
zhx 2026-06-22 10:50:18 +08:00
parent 8c2b965e6d
commit 73adb48c1c
6 changed files with 240 additions and 48 deletions

View File

@ -1,15 +1,16 @@
package com.red.circle.other.app.command.room.query;
import com.red.circle.other.app.common.room.RoomRegionCountryCommon;
import com.red.circle.other.app.common.room.RoomRegionCountryCommon.RegionCountryScope;
import com.red.circle.other.app.dto.clientobject.room.CountryCO;
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.UserProfile;
import com.red.circle.other.infra.constant.RegionConstants;
import com.red.circle.other.infra.database.rds.entity.sys.SysCountryCode;
import com.red.circle.other.infra.database.rds.service.sys.SysCountryCodeService;
import com.red.circle.tool.core.text.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@ -23,42 +24,39 @@ import org.springframework.stereotype.Component;
public class RoomCountryListQryExe {
private final SysCountryCodeService sysCountryCodeService;
private final UserRegionGateway userRegionGateway;
private final UserProfileGateway userProfileGateway;
private final RoomRegionCountryCommon roomRegionCountryCommon;
public List<CountryCO> execute(Long userId) {
List<SysCountryCode> all = sysCountryCodeService.listOpenCountry();
String userRegion = userRegionGateway.getRegionCode(userId);
UserProfile userProfile = userProfileGateway.getByUserId(userId);
String userCountryCode = userProfile != null ? userProfile.getCountryCode() : null;
boolean isArRegion = RegionConstants.AR.equals(userRegion);
String sysOrigin = userProfile == null ? null : userProfile.getOriginSys();
String userCountryCode = userProfile == null
? null
: roomRegionCountryCommon.normalizeCountryCode(userProfile.getCountryCode());
RegionCountryScope regionScope = roomRegionCountryCommon.resolve(userId, sysOrigin);
Set<String> visibleCountryCodes = regionScope.getCountryCodes();
List<CountryCO> current = new ArrayList<>();
List<CountryCO> arList = new ArrayList<>();
List<CountryCO> otherList = new ArrayList<>();
List<CountryCO> others = new ArrayList<>();
for (SysCountryCode entity : all) {
String countryCode = roomRegionCountryCommon.normalizeCountryCode(entity.getAlphaTwo());
if (!visibleCountryCodes.contains(countryCode)) {
continue;
}
CountryCO co = toCountryCO(entity);
if (StringUtils.isNotBlank(userCountryCode) && userCountryCode.equals(entity.getAlphaTwo())) {
if (StringUtils.isNotBlank(userCountryCode) && userCountryCode.equals(countryCode)) {
current.add(co);
} else if (RegionConstants.AR_REGIONS.contains(entity.getAlphaTwo())) {
arList.add(co);
} else {
otherList.add(co);
others.add(co);
}
}
List<CountryCO> result = new ArrayList<>();
result.addAll(current);
if (isArRegion) {
result.addAll(arList);
result.addAll(otherList);
} else {
result.addAll(otherList);
result.addAll(arList);
}
result.addAll(others);
return result;
}

View File

@ -6,6 +6,8 @@ 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.RoomRegionCountryCommon;
import com.red.circle.other.app.common.room.RoomRegionCountryCommon.RegionCountryScope;
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;
@ -67,27 +69,35 @@ public class RoomVoiceDiscoverQryExe {
private final GameLudoService gameLudoService;
private final UserProfileGateway userProfileGateway;
private final MicUserCacheService micUserCacheService;
private final RoomRegionCountryCommon roomRegionCountryCommon;
private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*";
public List<RoomVoiceProfileCO> execute(AppExtCommand cmd) {
cmd.setAllRegion(false);
String region = cmd.isAllRegion() ? "ALL" : userRegionGateway.getRegionCode(cmd.requiredReqUserId());
RegionCountryScope regionScope = roomRegionCountryCommon.resolve(
cmd.requiredReqUserId(), cmd.requireReqSysOrigin());
String region = regionScope.getRegionCode();
UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId());
String key = cmd.requireReqSysOrigin() + region;
String key = cmd.requireReqSysOrigin() + region + ":" + String.join(",", regionScope.getCountryCodes());
String regionsRoom = regionRoomCacheService.getRegionsRoom(key);
if (Objects.nonNull(regionsRoom)) {
List<RoomVoiceProfileCO> roomList = JSON.parseArray(regionsRoom, RoomVoiceProfileCO.class);
return roomList;
}
List<ActiveVoiceRoom> activeVoiceRooms = activeVoiceRoomService.listDiscover(cmd.requireReqSysOrigin(), cmd.isAllRegion(), region, 50);
String userCountryCode = userProfile == null
? ""
: roomRegionCountryCommon.normalizeCountryCode(userProfile.getCountryCode());
List<ActiveVoiceRoom> activeVoiceRooms = activeVoiceRoomService.listDiscoverByCountryCodes(
cmd.requireReqSysOrigin(), region, userCountryCode, regionScope.getCountryCodes(), 50);
// 查询没人的房间缓存加入进去
List<ActiveVoiceRoom> emptyRooms = getEmptyRoomsFromCache();
if (CollectionUtils.isNotEmpty(emptyRooms)) {
List<ActiveVoiceRoom> matchedEmptyRooms = filterEmptyRoomsByRegion(emptyRooms, cmd.requireReqSysOrigin(), cmd.isAllRegion(), region);
List<ActiveVoiceRoom> matchedEmptyRooms = filterEmptyRoomsByRegion(
emptyRooms, cmd.requireReqSysOrigin(), regionScope.getCountryCodes());
if (CollectionUtils.isNotEmpty(matchedEmptyRooms)) {
List<ActiveVoiceRoom> allRooms = new ArrayList<>(activeVoiceRooms);
Set<Long> existingRoomIds = activeVoiceRooms.stream()
@ -282,20 +292,13 @@ public class RoomVoiceDiscoverQryExe {
}
}
private List<ActiveVoiceRoom> filterEmptyRoomsByRegion(List<ActiveVoiceRoom> emptyRooms, String sysOrigin, boolean isAllRegion, String region) {
private List<ActiveVoiceRoom> filterEmptyRoomsByRegion(
List<ActiveVoiceRoom> emptyRooms, String sysOrigin, Set<String> countryCodes) {
return emptyRooms.stream()
.filter(room -> Objects.equals(sysOrigin, room.getSysOrigin()))
.filter(room -> {
if (isAllRegion) {
return true;
}
if (RegionConstant.isArabicRegionByCode(region)) {
return RegionConstant.isArabicRegionByCode(room.getRegion());
} else {
return !RegionConstant.isArabicRegionByCode(room.getRegion());
}
})
.collect(Collectors.toList());
.filter(room -> Objects.equals(sysOrigin, room.getSysOrigin()))
.filter(room -> countryCodes.contains(
roomRegionCountryCommon.normalizeCountryCode(room.getCountryCode())))
.collect(Collectors.toList());
}

View File

@ -1,21 +1,18 @@
package com.red.circle.other.app.command.room.query;
import com.red.circle.common.business.enums.SVIPLevelEnum;
import com.red.circle.other.app.common.room.RoomRegionCountryCommon;
import com.red.circle.other.app.common.room.RoomRegionCountryCommon.RegionCountryScope;
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.RoomExploreQryCmd;
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.ability.UserRegionGateway;
import com.red.circle.other.domain.gateway.user.ability.UserSVipGateway;
import com.red.circle.other.domain.rocket.RocketStatus;
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.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@ -31,20 +28,20 @@ public class RoomVoiceExploreQryExe {
private static final int EXPLORE_LIMIT = 100;
private final UserRegionGateway userRegionGateway;
private final UserSVipGateway userSVipGateway;
private final ActiveVoiceRoomService activeVoiceRoomService;
private final RoomVoiceProfileCommon roomVoiceProfileCommon;
private final RocketStatusCacheService rocketStatusCacheService;
private final GameLudoService gameLudoService;
private final RoomRegionCountryCommon roomRegionCountryCommon;
public List<RoomVoiceProfileCO> execute(RoomExploreQryCmd cmd) {
String userRegion = StringUtils.isNotBlank(cmd.getCountryCode())
? null
: userRegionGateway.getRegionCode(cmd.requiredReqUserId());
RegionCountryScope regionScope = roomRegionCountryCommon.resolve(
cmd.requiredReqUserId(), cmd.requireReqSysOrigin());
String countryCode = roomRegionCountryCommon.normalizeCountryCode(cmd.getCountryCode());
List<ActiveVoiceRoom> rooms = activeVoiceRoomService.listExplore(
cmd.requireReqSysOrigin(), userRegion, cmd.getCountryCode(), EXPLORE_LIMIT);
cmd.requireReqSysOrigin(), regionScope.getRegionCode(), countryCode,
regionScope.getCountryCodes(), EXPLORE_LIMIT);
List<RoomVoiceProfileCO> roomList = roomVoiceProfileCommon.toListRoomVoiceProfileCO(rooms);
if (CollectionUtils.isEmpty(roomList)) {

View File

@ -0,0 +1,94 @@
package com.red.circle.other.app.common.room;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.infra.database.mongo.entity.user.region.SysRegionConfig;
import com.red.circle.other.infra.database.mongo.service.user.region.SysRegionConfigService;
import com.red.circle.tool.core.text.StringUtils;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 房间区域国家范围.
*/
@Component
@RequiredArgsConstructor
public class RoomRegionCountryCommon {
private static final Pattern COUNTRY_CODE_PATTERN = Pattern.compile("[A-Za-z0-9_]+");
private final UserRegionGateway userRegionGateway;
private final SysRegionConfigService sysRegionConfigService;
public RegionCountryScope resolve(Long userId, String sysOrigin) {
String regionCode = userRegionGateway.getRegionCode(userId);
String regionId = userRegionGateway.getRegionId(userId);
SysRegionConfig regionConfig = null;
if (StringUtils.isNotBlank(regionId)) {
regionConfig = sysRegionConfigService.getById(regionId);
}
if (regionConfig == null && StringUtils.isNotBlank(regionCode)
&& StringUtils.isNotBlank(sysOrigin)) {
regionConfig = sysRegionConfigService.getByRegionCode(regionCode, sysOrigin);
}
return new RegionCountryScope(
regionId,
regionCode,
parseCountryCodes(regionConfig == null ? null : regionConfig.getCountryCodes())
);
}
public Set<String> parseCountryCodes(String countryCodes) {
if (StringUtils.isBlank(countryCodes)) {
return Collections.emptySet();
}
Set<String> result = new LinkedHashSet<>();
Matcher matcher = COUNTRY_CODE_PATTERN.matcher(countryCodes);
while (matcher.find()) {
String code = normalizeCountryCode(matcher.group());
if (StringUtils.isNotBlank(code)) {
result.add(code);
}
}
return Collections.unmodifiableSet(result);
}
public String normalizeCountryCode(String countryCode) {
return StringUtils.isBlank(countryCode)
? ""
: countryCode.trim().toUpperCase(Locale.ROOT);
}
public static class RegionCountryScope {
private final String regionId;
private final String regionCode;
private final Set<String> countryCodes;
private RegionCountryScope(String regionId, String regionCode, Set<String> countryCodes) {
this.regionId = regionId;
this.regionCode = regionCode;
this.countryCodes = countryCodes;
}
public String getRegionId() {
return regionId;
}
public String getRegionCode() {
return regionCode;
}
public Set<String> getCountryCodes() {
return countryCodes;
}
}
}

View File

@ -4,6 +4,7 @@ import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom;
import com.red.circle.other.inner.model.cmd.live.AppActiveVoiceRoomQryCmd;
import java.util.List;
import java.util.Set;
/**
* 活跃语音房间.
@ -67,6 +68,12 @@ public interface ActiveVoiceRoomService {
*/
List<ActiveVoiceRoom> listDiscover(String sysOrigin, boolean isAllRegion, String region, Integer limit);
/**
* 按后台区域配置的国家范围获取发现页数据.
*/
List<ActiveVoiceRoom> listDiscoverByCountryCodes(
String sysOrigin, String region, String countryCode, Set<String> countryCodes, Integer limit);
/**
* 获取区域房间.
*/
@ -96,4 +103,11 @@ public interface ActiveVoiceRoomService {
*/
List<ActiveVoiceRoom> listExplore(String sysOrigin, String userRegion, String countryCode, Integer limit);
/**
* explore 页查询.
* 按后台区域配置的国家范围过滤同区域排前.
*/
List<ActiveVoiceRoom> listExplore(
String sysOrigin, String userRegion, String countryCode, Set<String> countryCodes, Integer limit);
}

View File

@ -138,6 +138,47 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
return mongoTemplate.find(query, ActiveVoiceRoom.class);
}
@Override
public List<ActiveVoiceRoom> listDiscoverByCountryCodes(
String sysOrigin, String region, String countryCode, Set<String> countryCodes, Integer limit) {
Set<String> safeCountryCodes = safeCountryCodes(countryCodes);
if (safeCountryCodes.isEmpty()) {
return Collections.emptyList();
}
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
.and("countryCode").in(safeCountryCodes);
log.info("首页房间按后台区域国家过滤 region:{}, countries:{}", region, safeCountryCodes);
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.project("id", "timeId", "sysOrigin", "userId", "roomAccount",
"familyProfile", "runGame", "superVipLevel", "hot", "fixedWeights",
"region", "countryCode", "countryName", "onlineQuantity", "weights",
"expiredTime", "createTime", "updateTime")
.and(ConditionalOperators.when(Criteria.where("countryCode").is(countryCode))
.then(1).otherwise(0)).as("countryMatchScore")
.and(ConditionalOperators.when(Criteria.where("region").is(region))
.then(1).otherwise(0)).as("regionMatchScore")
.and(ConditionalOperators.when(Criteria.where("region").is(region))
.then("$fixedWeights").otherwise(0)).as("fixedWeightsScore")
.and("weights").as("weights"),
Aggregation.sort(Sort.by(
Sort.Order.desc("countryMatchScore"),
Sort.Order.desc("regionMatchScore"),
Sort.Order.desc("fixedWeightsScore"),
Sort.Order.desc("weights"),
Sort.Order.desc("onlineQuantity")
)),
Aggregation.skip(0),
Aggregation.limit(limit)
);
return mongoTemplate.aggregate(aggregation, ActiveVoiceRoom.class, ActiveVoiceRoom.class)
.getMappedResults();
}
@Override
public List<ActiveVoiceRoom> listSearchCountryRoom(String sysOrigin, String countryCode,
@ -234,6 +275,47 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
}
return criteria;
}
@Override
public List<ActiveVoiceRoom> listExplore(
String sysOrigin, String userRegion, String countryCode, Set<String> countryCodes, Integer limit) {
Set<String> safeCountryCodes = safeCountryCodes(countryCodes);
if (safeCountryCodes.isEmpty()) {
return Collections.emptyList();
}
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin);
if (StringUtils.isNotBlank(countryCode)) {
if (!safeCountryCodes.contains(countryCode)) {
return Collections.emptyList();
}
criteria.and("countryCode").is(countryCode);
} else {
criteria.and("countryCode").in(safeCountryCodes);
}
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.project("id", "timeId", "sysOrigin", "userId", "roomAccount", "familyProfile",
"runGame", "superVipLevel", "hot", "fixedWeights", "region", "countryCode",
"countryName", "onlineQuantity", "weights", "expiredTime", "createTime", "updateTime")
.and(ConditionalOperators.when(Criteria.where("region").is(userRegion))
.then(1).otherwise(0)).as("regionMatchScore")
.and(ConditionalOperators.when(Criteria.where("region").is(userRegion))
.then("$fixedWeights").otherwise(0)).as("fixedWeightsScore"),
Aggregation.sort(Sort.by(
Sort.Order.desc("regionMatchScore"),
Sort.Order.desc("fixedWeightsScore"),
Sort.Order.desc("weights"),
Sort.Order.desc("onlineQuantity")
)),
Aggregation.limit(limit)
);
return mongoTemplate.aggregate(aggregation, ActiveVoiceRoom.class, ActiveVoiceRoom.class)
.getMappedResults();
}
@Override
public List<ActiveVoiceRoom> listExplore(String sysOrigin, String userRegion, String countryCode, Integer limit) {
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin);
@ -272,6 +354,10 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
return mongoTemplate.aggregate(aggregation, ActiveVoiceRoom.class, ActiveVoiceRoom.class).getMappedResults();
}
private Set<String> safeCountryCodes(Set<String> countryCodes) {
return countryCodes == null ? Collections.emptySet() : countryCodes;
}
@Override
public List<ActiveVoiceRoom> listGetActiveRooms(List<Long> roomIds, String sysOrigin,Integer onlineQuantity) {
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)