Compare commits

...

7 Commits

Author SHA1 Message Date
hy001
41305adb9d fix external trtc region config 2026-05-12 13:44:13 +08:00
hy001
f735b0a9a3 白名单 2026-05-12 13:19:00 +08:00
hy001
e0c4b72d4e fix: reduce live microphone log volume 2026-05-12 11:57:24 +08:00
hy001
93c39f77cf fix: tolerate lingxian callback signing variants 2026-05-12 11:19:40 +08:00
hy001
3a01a490ab 修复灵仙 2026-05-11 22:50:49 +08:00
hy001
1ac5e6c62d Merge branch 'main' of gitea.haiyihy.com:hy/chatapp3-java 2026-05-11 18:58:59 +08:00
ZuoZuo
354f11150f Send task center events through MQ 2026-05-10 02:05:22 +08:00
16 changed files with 404 additions and 41 deletions

View File

@ -40,6 +40,7 @@ red-circle:
secretId: ${LIKEI_TRTC_SECRET_ID}
secretKey: ${LIKEI_TRTC_SECRET_KEY}
endpoint: ${LIKEI_TRTC_ENDPOINT}
region: ${LIKEI_TRTC_REGION:ap-singapore}
sdkAppId: ${LIKEI_TRTC_SDK_APP_ID}
tencet-im:
appId: ${LIKEI_IM_APP_ID}

View File

@ -7,4 +7,9 @@ task:
base-url: ${TASK_CENTER_GO_URL:${LIKEI_GATEWAY_ROUTE_GO_URI:http://golang:2900}}
internal-token: ${GAME_INTERNAL_CALLBACK_SECRET:}
timeout-millis: ${TASK_CENTER_GO_TIMEOUT_MILLIS:3000}
event:
mq:
enabled: ${TASK_CENTER_EVENT_MQ_ENABLED:false}
topic: ${TASK_CENTER_EVENT_MQ_TOPIC:RC_DEFAULT_APP_ORDINARY}
tag: ${TASK_CENTER_EVENT_MQ_TAG:task_center_event}

View File

@ -79,3 +79,8 @@ task:
base-url: ${TASK_CENTER_GO_URL:${LIKEI_GATEWAY_ROUTE_GO_URI:http://golang:2900}}
internal-token: ${GAME_INTERNAL_CALLBACK_SECRET:}
timeout-millis: ${TASK_CENTER_GO_TIMEOUT_MILLIS:3000}
event:
mq:
enabled: ${TASK_CENTER_EVENT_MQ_ENABLED:false}
topic: ${TASK_CENTER_EVENT_MQ_TOPIC:RC_DEFAULT_APP_ORDINARY}
tag: ${TASK_CENTER_EVENT_MQ_TAG:task_center_event}

View File

@ -122,6 +122,11 @@ task:
base-url: ${TASK_CENTER_GO_URL:${LIKEI_GATEWAY_ROUTE_GO_URI:http://golang:2900}}
internal-token: ${GAME_INTERNAL_CALLBACK_SECRET:}
timeout-millis: ${TASK_CENTER_GO_TIMEOUT_MILLIS:3000}
event:
mq:
enabled: ${TASK_CENTER_EVENT_MQ_ENABLED:false}
topic: ${TASK_CENTER_EVENT_MQ_TOPIC:RC_DEFAULT_APP_ORDINARY}
tag: ${TASK_CENTER_EVENT_MQ_TAG:task_center_event}
voice-room:
region-broadcast:

View File

@ -18,3 +18,8 @@ task:
base-url: ${TASK_CENTER_GO_URL:${LIKEI_GATEWAY_ROUTE_GO_URI:http://golang:2900}}
internal-token: ${GAME_INTERNAL_CALLBACK_SECRET:}
timeout-millis: ${TASK_CENTER_GO_TIMEOUT_MILLIS:3000}
event:
mq:
enabled: ${TASK_CENTER_EVENT_MQ_ENABLED:false}
topic: ${TASK_CENTER_EVENT_MQ_TOPIC:RC_DEFAULT_APP_ORDINARY}
tag: ${TASK_CENTER_EVENT_MQ_TAG:task_center_event}

View File

@ -0,0 +1,38 @@
package com.red.circle.external.infra.config;
import com.red.circle.component.instant.msg.InstantMessageProperties;
import com.red.circle.component.instant.msg.tencet.props.TrtcTencetProperties;
import com.red.circle.component.instant.msg.tencet.service.endpoint.api.trtc.HyTrtcClient;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.util.StringUtils;
@Configuration
public class TencentTrtcClientRegionConfiguration {
private static final String DEFAULT_TRTC_REGION = "ap-singapore";
@Bean
@Primary
public HyTrtcClient regionAwareHyTrtcClient(
InstantMessageProperties instantMessageProperties,
@Value("${red-circle.instant-message.tencet-trtc.region:" + DEFAULT_TRTC_REGION + "}")
String region) {
TrtcTencetProperties trtcProperties = instantMessageProperties.getTencetTrtc();
Credential credential = new Credential(
trtcProperties.getSecretId(),
trtcProperties.getSecretKey()
);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint(trtcProperties.getEndpoint());
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
String resolvedRegion = StringUtils.hasText(region) ? region.trim() : DEFAULT_TRTC_REGION;
return new HyTrtcClient(credential, resolvedRegion, clientProfile);
}
}

View File

@ -1,7 +1,9 @@
package com.red.circle.live.infra.client;
import com.alibaba.fastjson.JSON;
import com.red.circle.component.mq.MessageEvent;
import com.red.circle.live.domain.constant.ActiveSecondConstant;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
@ -14,6 +16,7 @@ import java.util.Objects;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -23,6 +26,8 @@ public class TaskCenterGoClient {
private static final String EVENT_TYPE_MIC_DURATION_SECONDS = "MIC_DURATION_SECONDS";
private final ObjectProvider<MessageSenderService> messageSenderServiceProvider;
@Value("${task.center.go.enabled:true}")
private boolean enabled;
@ -35,6 +40,19 @@ public class TaskCenterGoClient {
@Value("${task.center.go.timeout-millis:3000}")
private Integer timeoutMillis;
@Value("${task.center.event.mq.enabled:false}")
private boolean mqEnabled;
@Value("${task.center.event.mq.topic:RC_DEFAULT_APP_ORDINARY}")
private String mqTopic;
@Value("${task.center.event.mq.tag:task_center_event}")
private String mqTag;
public TaskCenterGoClient(ObjectProvider<MessageSenderService> messageSenderServiceProvider) {
this.messageSenderServiceProvider = messageSenderServiceProvider;
}
public void reportMicDuration(String sysOrigin,
Long roomId,
Integer micIndex,
@ -81,6 +99,9 @@ public class TaskCenterGoClient {
}
private void reportEvent(TaskCenterEventRequest request) {
if (mqEnabled) {
publishMqEvent(request);
}
if (!enabled) {
return;
}
@ -134,6 +155,24 @@ public class TaskCenterGoClient {
}
}
private void publishMqEvent(TaskCenterEventRequest request) {
try {
MessageSenderService senderService = messageSenderServiceProvider.getIfAvailable();
if (Objects.isNull(senderService)) {
log.warn("Task center mq sender missing, skip event report. eventId={}", request.getEventId());
return;
}
senderService.sendEventMessage(MessageEvent.builder()
.consumeId(request.getEventId())
.topicString(defaultIfBlank(mqTopic, "RC_DEFAULT_APP_ORDINARY"))
.tag(defaultIfBlank(mqTag, "task_center_event"))
.body(request)
.build());
} catch (Exception e) {
log.warn("Task center mq report error. eventId={}", request.getEventId(), e);
}
}
private String readResponse(HttpURLConnection connection) {
try (InputStream inputStream = connection.getErrorStream() != null
? connection.getErrorStream()
@ -166,6 +205,11 @@ public class TaskCenterGoClient {
return value;
}
private String defaultIfBlank(String value, String fallback) {
String normalized = Objects.toString(value, "").trim();
return normalized.isEmpty() ? fallback : normalized;
}
@Data
@Accessors(chain = true)
private static class TaskCenterEventRequest {

View File

@ -595,13 +595,14 @@ public class LiveMicrophoneGatewayImpl implements LiveMicrophoneGateway {
List<LiveMicSettingSeat> liveMicSettingSeats = initMicSetting(roomId);
if (CollectionUtils.isEmpty(liveMicSettingSeats)) {
return CollectionUtils.newArrayList();
}
Map<Integer, LiveMicrophone> liveMicrophoneMap = mapLiveMicrophone(roomId);
log.info("在线取麦克风列表信息 -Map: {}", liveMicrophoneMap);
return liveMicSettingSeats.stream().map(seat -> new LiveMicrophoneDTO()
.setRoomId(roomId)
.setMicIndex(seat.getMicIndex())
}
Map<Integer, LiveMicrophone> liveMicrophoneMap = mapLiveMicrophone(roomId);
log.debug("list live microphone map summary, roomId={}, micCount={}", roomId,
liveMicrophoneMap.size());
return liveMicSettingSeats.stream().map(seat -> new LiveMicrophoneDTO()
.setRoomId(roomId)
.setMicIndex(seat.getMicIndex())
.setMicLock(seat.getMicLock())
.setMicMute(seat.getMicMute())
.setUser(liveMicInfraConvertor.toLiveMicUserDTO(
@ -655,24 +656,26 @@ public class LiveMicrophoneGatewayImpl implements LiveMicrophoneGateway {
public void initMicSetting(Long roomId, Integer micSize) {
init(roomId, micSize);
}
private Map<Integer, LiveMicrophone> mapLiveMicrophone(Long roomId) {
List<LiveMicrophone> liveMicrophones = liveMicCacheService.listLiveMicrophone(roomId);
log.info("在线取麦克风列表信息 -列表: {}", liveMicrophones);
//获取房间用户心动值
Map<Long, Long> liveHeartbeatMap = getLiveHeartbeatMap(liveHeartbeatClient.listLiveHeartbeat(roomId).getBody());
if (CollectionUtils.isEmpty(liveMicrophones)) {
private Map<Integer, LiveMicrophone> mapLiveMicrophone(Long roomId) {
List<LiveMicrophone> liveMicrophones = liveMicCacheService.listLiveMicrophone(roomId);
log.debug("list live microphone cache summary, roomId={}, micCount={}", roomId,
liveMicrophones.size());
//获取房间用户心动值
Map<Long, Long> liveHeartbeatMap = getLiveHeartbeatMap(liveHeartbeatClient.listLiveHeartbeat(roomId).getBody());
if (CollectionUtils.isEmpty(liveMicrophones)) {
return CollectionUtils.newHashMap();
}
List<LiveMicUser> liveMicUsers = liveMicrophones.stream().map(LiveMicrophone::getUser).toList();
List<Long> userIds = liveMicUsers.stream().map(LiveMicUser::getId).toList();
updateLiveMicrophoneUserInfo(liveMicrophones, roomId, userIds);
Map<Long, Long> cpUserIdMap = userCpClient.getCpUserIdByUserIds(userIds).getBody();
log.info("用户ID集合={}, 结果集cpUserIdMap = {}", userIds, cpUserIdMap);
return liveMicrophones.stream()
.peek(liveMicrophone -> {
LiveMicUser user = liveMicrophone.getUser();
List<Long> userIds = liveMicUsers.stream().map(LiveMicUser::getId).toList();
updateLiveMicrophoneUserInfo(liveMicrophones, roomId, userIds);
Map<Long, Long> cpUserIdMap = userCpClient.getCpUserIdByUserIds(userIds).getBody();
log.debug("list live microphone cp user summary, roomId={}, userCount={}, cpUserCount={}",
roomId, userIds.size(), Objects.nonNull(cpUserIdMap) ? cpUserIdMap.size() : 0);
return liveMicrophones.stream()
.peek(liveMicrophone -> {
LiveMicUser user = liveMicrophone.getUser();
user.setHeartbeatVal(Objects.nonNull(liveHeartbeatMap.get(user.getId()))? liveHeartbeatMap.get(user.getId()):0L);
user.setCpUserId(Objects.nonNull(cpUserIdMap.get(user.getId()))? cpUserIdMap.get(user.getId()):0L);
liveMicrophone.setUser(user);

View File

@ -1,6 +1,8 @@
package com.red.circle.order.app.common.task;
import com.alibaba.fastjson.JSON;
import com.red.circle.component.mq.MessageEvent;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
@ -15,6 +17,7 @@ import java.util.Objects;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -24,6 +27,8 @@ public class TaskCenterGoClient {
private static final String EVENT_TYPE_RECHARGE_GOLD = "RECHARGE_GOLD";
private final ObjectProvider<MessageSenderService> messageSenderServiceProvider;
@Value("${task.center.go.enabled:true}")
private boolean enabled;
@ -36,6 +41,19 @@ public class TaskCenterGoClient {
@Value("${task.center.go.timeout-millis:3000}")
private Integer timeoutMillis;
@Value("${task.center.event.mq.enabled:false}")
private boolean mqEnabled;
@Value("${task.center.event.mq.topic:RC_DEFAULT_APP_ORDINARY}")
private String mqTopic;
@Value("${task.center.event.mq.tag:task_center_event}")
private String mqTag;
public TaskCenterGoClient(ObjectProvider<MessageSenderService> messageSenderServiceProvider) {
this.messageSenderServiceProvider = messageSenderServiceProvider;
}
public void reportRechargeGold(String sysOrigin,
Long userId,
Long purchaseHistoryId,
@ -68,6 +86,9 @@ public class TaskCenterGoClient {
}
private void reportEvent(TaskCenterEventRequest request) {
if (mqEnabled) {
publishMqEvent(request);
}
if (!enabled) {
return;
}
@ -109,6 +130,24 @@ public class TaskCenterGoClient {
}
}
private void publishMqEvent(TaskCenterEventRequest request) {
try {
MessageSenderService senderService = messageSenderServiceProvider.getIfAvailable();
if (Objects.isNull(senderService)) {
log.warn("Task center mq sender missing, skip event report. eventId={}", request.getEventId());
return;
}
senderService.sendEventMessage(MessageEvent.builder()
.consumeId(request.getEventId())
.topicString(defaultIfBlank(mqTopic, "RC_DEFAULT_APP_ORDINARY"))
.tag(defaultIfBlank(mqTag, "task_center_event"))
.body(request)
.build());
} catch (Exception e) {
log.warn("Task center mq report error. eventId={}", request.getEventId(), e);
}
}
private String readResponse(HttpURLConnection connection) {
try (InputStream inputStream = connection.getErrorStream() != null
? connection.getErrorStream()
@ -129,6 +168,11 @@ public class TaskCenterGoClient {
return value;
}
private String defaultIfBlank(String value, String fallback) {
String normalized = Objects.toString(value, "").trim();
return normalized.isEmpty() ? fallback : normalized;
}
@Data
@Accessors(chain = true)
private static class TaskCenterEventRequest {

View File

@ -51,12 +51,14 @@ import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.tool.crypto.SecurityUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -102,10 +104,12 @@ public class GameHkysRestController {
return new HkysResponse<HkysUserProfileResponse>()
.setErrorCode(HyksErrorEnum.PARAM_ERROR.getErrorCode());
}
String sign = SecurityUtils.md5(Stream.of(cmd.getGameId(), cmd.getUid(), cmd.getToken(), cmd.getRoomId(), gameHkysProperties.getSignKey()).filter(Objects::nonNull).map(String::valueOf).collect(Collectors.joining("")));
if (!sign.equals(cmd.getSign())) {
return new HkysResponse<HkysUserProfileResponse>().setErrorCode(HyksErrorEnum.SIGN_VERIFY_FAIL.getErrorCode());
}
if (!verifyLingxianUserInfoSign(cmd)) {
log.warn("lingxian getUserInfo sign verify failed, uid={}, gameId={}, roomId={}, sysOrigin={}, tokenLength={}",
cmd.getUid(), cmd.getGameId(), cmd.getRoomId(), resolveSysOriginQuietly(cmd.getToken()),
cmd.getToken() == null ? 0 : cmd.getToken().length());
return new HkysResponse<HkysUserProfileResponse>().setErrorCode(HyksErrorEnum.SIGN_VERIFY_FAIL.getErrorCode());
}
HkysUserProfileQuery query = new HkysUserProfileQuery();
query.setGameId(cmd.getGameId());
@ -123,12 +127,12 @@ public class GameHkysRestController {
|| Objects.isNull(cmd.getType()) || Objects.isNull(cmd.getCoin())) {
return new HkysResponse<JSONObject>().setErrorCode(HyksErrorEnum.PARAM_ERROR.getErrorCode());
}
String sign = SecurityUtils.md5(Stream.of(cmd.getOrderId(), cmd.getGameId(), cmd.getRoundId(), cmd.getUid(),
cmd.getCoin(), cmd.getType(), cmd.getToken(), cmd.getWinId(), cmd.getRoomId(), gameHkysProperties.getSignKey())
.filter(Objects::nonNull).map(String::valueOf).collect(Collectors.joining("")));
if (!sign.equals(cmd.getSign())) {
return new HkysResponse<JSONObject>().setErrorCode(HyksErrorEnum.SIGN_VERIFY_FAIL.getErrorCode());
}
if (!verifyLingxianUpdateBalanceSign(cmd)) {
log.warn("lingxian updateBalance sign verify failed, uid={}, gameId={}, orderId={}, roomId={}, sysOrigin={}, tokenLength={}",
cmd.getUid(), cmd.getGameId(), cmd.getOrderId(), cmd.getRoomId(), resolveSysOriginQuietly(cmd.getToken()),
cmd.getToken() == null ? 0 : cmd.getToken().length());
return new HkysResponse<JSONObject>().setErrorCode(HyksErrorEnum.SIGN_VERIFY_FAIL.getErrorCode());
}
HkysUserCoinUpdate param = new HkysUserCoinUpdate();
param.setOrderId(cmd.getOrderId());
@ -150,7 +154,7 @@ public class GameHkysRestController {
//游玩一句游戏结束 每日任务 灵仙
Long userId = DataTypeUtils.toLong(cmd.getUid());
String sysOrigin = UserCredential.parseToken(cmd.getToken()).getSysOrigin();
String sysOrigin = parseSysOrigin(cmd.getToken());
BigDecimal cost = enumConfigCacheService
.getValueBigDecimal(EnumConfigKey.GAME_BROADCAST_AMOUNT, sysOrigin) ;
@ -223,6 +227,88 @@ public class GameHkysRestController {
);
}
private String resolveLingxianSignKey(String token) {
List<String> keys = lingxianSignKeys(token);
return keys.isEmpty() ? "" : keys.get(0);
}
private boolean verifyLingxianUserInfoSign(GameLxwlUserInfoCmd cmd) {
for (String token : lingxianTokenCandidates(cmd.getToken())) {
for (String key : lingxianSignKeys(token)) {
String sign = SecurityUtils.md5(Stream.of(cmd.getGameId(), cmd.getUid(), token, cmd.getRoomId(), key)
.filter(Objects::nonNull).map(String::valueOf).collect(Collectors.joining("")));
if (sign.equalsIgnoreCase(cmd.getSign())) {
return true;
}
}
}
return false;
}
private boolean verifyLingxianUpdateBalanceSign(GameLxwlUpdateBalanceCmd cmd) {
for (String token : lingxianTokenCandidates(cmd.getToken())) {
for (String key : lingxianSignKeys(token)) {
String sign = SecurityUtils.md5(Stream.of(cmd.getOrderId(), cmd.getGameId(), cmd.getRoundId(), cmd.getUid(),
cmd.getCoin(), cmd.getType(), token, cmd.getWinId(), cmd.getRoomId(), key)
.filter(Objects::nonNull).map(String::valueOf).collect(Collectors.joining("")));
if (sign.equalsIgnoreCase(cmd.getSign())) {
return true;
}
}
}
return false;
}
private List<String> lingxianSignKeys(String token) {
List<String> keys = new ArrayList<>();
addIfNotBlank(keys, gameListConfigService.getActiveLingxianAppKey(resolveSysOriginQuietly(token)));
addIfNotBlank(keys, gameHkysProperties.getSignKey());
return keys;
}
private List<String> lingxianTokenCandidates(String token) {
List<String> tokens = new ArrayList<>();
if (StringUtils.isBlank(token)) {
tokens.add(null);
return tokens;
}
addTokenCandidate(tokens, token);
addTokenCandidate(tokens, SecurityUtils.urlDecode(token));
return tokens;
}
private void addTokenCandidate(List<String> tokens, String token) {
if (StringUtils.isBlank(token)) {
return;
}
addIfNotBlank(tokens, token);
addIfNotBlank(tokens, trimTokenPadding(token));
}
private String trimTokenPadding(String token) {
return token == null ? "" : token.trim().replaceAll("=+$", "");
}
private void addIfNotBlank(List<String> values, String value) {
if (StringUtils.isBlank(value) || values.contains(value)) {
return;
}
values.add(value);
}
private String resolveSysOriginQuietly(String token) {
try {
return parseSysOrigin(token);
} catch (Exception e) {
log.warn("resolve lingxian sysOrigin failed");
return "";
}
}
private String parseSysOrigin(String token) {
return UserCredential.parseToken(SecurityUtils.urlDecode(token)).getSysOrigin();
}
private void reportTaskCenterGameEvents(String sysOrigin,
Long userId,
String provider,

View File

@ -23,8 +23,9 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class RoomRegionFilterCommon {
// account 1001041, 1001545
// account 1001035, 1001041, 1001545
private static final Set<Long> ALL_REGION_ROOM_WHITELIST_USER_IDS = Set.of(
2044358825183604738L,
2044700023064686594L,
2047206645188063233L
);

View File

@ -3,6 +3,8 @@ package com.red.circle.other.app.common.task;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.red.circle.component.mq.MessageEvent;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.HashMap;
@ -11,6 +13,7 @@ import java.util.Objects;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -21,6 +24,8 @@ public class TaskCenterGoClient {
private static final String EVENT_TYPE_GAME_CONSUME_GOLD = "GAME_CONSUME_GOLD";
private static final String EVENT_TYPE_GIFT_CONSUME_GOLD = "GIFT_CONSUME_GOLD";
private final ObjectProvider<MessageSenderService> messageSenderServiceProvider;
@Value("${task.center.go.enabled:true}")
private boolean enabled;
@ -33,6 +38,19 @@ public class TaskCenterGoClient {
@Value("${task.center.go.timeout-millis:3000}")
private Integer timeoutMillis;
@Value("${task.center.event.mq.enabled:false}")
private boolean mqEnabled;
@Value("${task.center.event.mq.topic:RC_DEFAULT_APP_ORDINARY}")
private String mqTopic;
@Value("${task.center.event.mq.tag:task_center_event}")
private String mqTag;
public TaskCenterGoClient(ObjectProvider<MessageSenderService> messageSenderServiceProvider) {
this.messageSenderServiceProvider = messageSenderServiceProvider;
}
public void reportGameConsumeGold(String sysOrigin,
Long userId,
String provider,
@ -101,6 +119,9 @@ public class TaskCenterGoClient {
}
private void reportEvent(TaskCenterEventRequest request) {
if (mqEnabled) {
publishMqEvent(request);
}
if (!enabled) {
return;
}
@ -127,6 +148,24 @@ public class TaskCenterGoClient {
}
}
private void publishMqEvent(TaskCenterEventRequest request) {
try {
MessageSenderService senderService = messageSenderServiceProvider.getIfAvailable();
if (Objects.isNull(senderService)) {
log.warn("Task center mq sender missing, skip event report. eventId={}", request.getEventId());
return;
}
senderService.sendEventMessage(MessageEvent.builder()
.consumeId(request.getEventId())
.topicString(defaultIfBlank(mqTopic, "RC_DEFAULT_APP_ORDINARY"))
.tag(defaultIfBlank(mqTag, "task_center_event"))
.body(request)
.build());
} catch (Exception e) {
log.warn("Task center mq report error. eventId={}", request.getEventId(), e);
}
}
private String trimRightSlash(String value) {
while (value.endsWith("/")) {
value = value.substring(0, value.length() - 1);
@ -134,6 +173,11 @@ public class TaskCenterGoClient {
return value;
}
private String defaultIfBlank(String value, String fallback) {
String normalized = Objects.toString(value, "").trim();
return normalized.isEmpty() ? fallback : normalized;
}
@Data
@Accessors(chain = true)
private static class TaskCenterEventRequest {

View File

@ -1,6 +1,8 @@
package com.red.circle.other.app.common.room;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -14,6 +16,16 @@ import org.junit.jupiter.api.Test;
class RoomRegionFilterCommonTest {
@Test
void canViewAllRegions_shouldAllowConfiguredWhiteListUser() {
UserRegionGateway userRegionGateway = mock(UserRegionGateway.class);
RoomRegionFilterCommon roomRegionFilterCommon = new RoomRegionFilterCommon(userRegionGateway);
assertTrue(roomRegionFilterCommon.canViewAllRegions(2044358825183604738L));
assertFalse(roomRegionFilterCommon.canViewAllRegions(1001035L));
assertFalse(roomRegionFilterCommon.canViewAllRegions(null));
}
@Test
void filterActiveVoiceRoomsByRegion_shouldKeepOnlyCurrentRegionRooms() {
UserRegionGateway userRegionGateway = mock(UserRegionGateway.class);

View File

@ -46,10 +46,15 @@ public interface GameListConfigService extends BaseService<GameListConfig> {
* 获取灵仙当前启用的配置档案.
*/
String getActiveLingxianProfile(String sysOrigin);
/**
* 查询游戏信息
*/
/**
* 获取灵仙当前启用配置的 appKey.
*/
String getActiveLingxianAppKey(String sysOrigin);
/**
* 查询游戏信息
*/
GameListConfig getByGameCode(String gameCode, String sysOrigin);
/**

View File

@ -213,6 +213,27 @@ public class GameListConfigServiceImpl extends
}
}
@Override
public String getActiveLingxianAppKey(String sysOrigin) {
if (StringUtils.isBlank(sysOrigin)) {
return "";
}
try {
return Optional.ofNullable(jdbcTemplate.queryForObject("""
SELECT app_key
FROM lingxian_provider_config
WHERE sys_origin = ?
AND active = 1
ORDER BY update_time DESC
LIMIT 1
""", String.class, sysOrigin))
.map(String::trim)
.orElse("");
} catch (Exception e) {
return "";
}
}
@Override
public GameListConfig getByGameCode(String gameCode, String sysOrigin) {
return query()

View File

@ -1,6 +1,8 @@
package com.red.circle.wallet.app.common.task;
import com.alibaba.fastjson.JSON;
import com.red.circle.component.mq.MessageEvent;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
@ -15,6 +17,7 @@ import java.util.Objects;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -24,6 +27,8 @@ public class TaskCenterGoClient {
private static final String EVENT_TYPE_RECHARGE_GOLD = "RECHARGE_GOLD";
private final ObjectProvider<MessageSenderService> messageSenderServiceProvider;
@Value("${task.center.go.enabled:true}")
private boolean enabled;
@ -36,6 +41,19 @@ public class TaskCenterGoClient {
@Value("${task.center.go.timeout-millis:3000}")
private Integer timeoutMillis;
@Value("${task.center.event.mq.enabled:false}")
private boolean mqEnabled;
@Value("${task.center.event.mq.topic:RC_DEFAULT_APP_ORDINARY}")
private String mqTopic;
@Value("${task.center.event.mq.tag:task_center_event}")
private String mqTag;
public TaskCenterGoClient(ObjectProvider<MessageSenderService> messageSenderServiceProvider) {
this.messageSenderServiceProvider = messageSenderServiceProvider;
}
public void reportShippingAgentRechargeGold(String sysOrigin,
Long userId,
Long runningWaterId,
@ -99,6 +117,9 @@ public class TaskCenterGoClient {
}
private void reportEvent(TaskCenterEventRequest request) {
if (mqEnabled) {
publishMqEvent(request);
}
if (!enabled) {
return;
}
@ -140,6 +161,24 @@ public class TaskCenterGoClient {
}
}
private void publishMqEvent(TaskCenterEventRequest request) {
try {
MessageSenderService senderService = messageSenderServiceProvider.getIfAvailable();
if (Objects.isNull(senderService)) {
log.warn("Task center mq sender missing, skip event report. eventId={}", request.getEventId());
return;
}
senderService.sendEventMessage(MessageEvent.builder()
.consumeId(request.getEventId())
.topicString(defaultIfBlank(mqTopic, "RC_DEFAULT_APP_ORDINARY"))
.tag(defaultIfBlank(mqTag, "task_center_event"))
.body(request)
.build());
} catch (Exception e) {
log.warn("Task center mq report error. eventId={}", request.getEventId(), e);
}
}
private String readResponse(HttpURLConnection connection) {
try (InputStream inputStream = connection.getErrorStream() != null
? connection.getErrorStream()
@ -160,6 +199,11 @@ public class TaskCenterGoClient {
return value;
}
private String defaultIfBlank(String value, String fallback) {
String normalized = Objects.toString(value, "").trim();
return normalized.isEmpty() ? fallback : normalized;
}
@Data
@Accessors(chain = true)
private static class TaskCenterEventRequest {