diff --git a/nacos_config/rc-service-external/application.yml b/nacos_config/rc-service-external/application.yml index ab3a1d19..d2ae0c61 100644 --- a/nacos_config/rc-service-external/application.yml +++ b/nacos_config/rc-service-external/application.yml @@ -46,6 +46,8 @@ red-circle: endpoint: ${LIKEI_TRTC_ENDPOINT} region: ${LIKEI_TRTC_REGION:ap-singapore} sdkAppId: ${LIKEI_TRTC_SDK_APP_ID} + userSigKey: ${LIKEI_TRTC_USER_SIG_KEY:${LIKEI_TRTC_SECRET_KEY}} + userSigExpireSeconds: ${LIKEI_TRTC_USER_SIG_EXPIRE_SECOND:7200} tencet-im: appId: ${LIKEI_IM_APP_ID} key: ${LIKEI_IM_KEY} diff --git a/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcCredentialCO.java b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcCredentialCO.java new file mode 100644 index 00000000..6c4fb8b9 --- /dev/null +++ b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcCredentialCO.java @@ -0,0 +1,74 @@ +package com.red.circle.common.business.dto.rtc; + +import com.red.circle.framework.dto.ClientObject; +import java.io.Serial; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * RTC credential returned to compatible clients. + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class RtcCredentialCO extends ClientObject { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * AGORA or TRTC. + */ + private String rtcProvider; + + /** + * Legacy Agora token. + */ + private String agoraToken; + + /** + * Agora channel name, currently the business room id. + */ + private String agoraChannelName; + + /** + * Tencent TRTC sdkAppId. + */ + private Long trtcSdkAppId; + + /** + * TRTC user id. Use business userId string. + */ + private String trtcUserId; + + /** + * TRTC UserSig. + */ + private String trtcUserSig; + + /** + * Numeric TRTC room id when available. + */ + private Long trtcRoomId; + + /** + * String TRTC room id fallback. + */ + private String trtcStrRoomId; + + /** + * TRTC role: 20 anchor, 21 audience. + */ + private Integer trtcRole; + + /** + * UserSig lifetime in seconds. + */ + private Long trtcUserSigExpireSeconds; + + /** + * UserSig expire timestamp in milliseconds. + */ + private Long trtcUserSigExpireAt; +} diff --git a/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcProviderEnum.java b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcProviderEnum.java new file mode 100644 index 00000000..821484bf --- /dev/null +++ b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcProviderEnum.java @@ -0,0 +1,21 @@ +package com.red.circle.common.business.dto.rtc; + +/** + * RTC provider used by voice rooms. + */ +public enum RtcProviderEnum { + AGORA, + TRTC; + + public static RtcProviderEnum of(String provider) { + if (provider == null || provider.isBlank()) { + return null; + } + for (RtcProviderEnum value : values()) { + if (value.name().equalsIgnoreCase(provider.trim())) { + return value; + } + } + return null; + } +} diff --git a/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcVersionSupport.java b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcVersionSupport.java new file mode 100644 index 00000000..2d667063 --- /dev/null +++ b/rc-common-business/common-business-dto/src/main/java/com/red/circle/common/business/dto/rtc/RtcVersionSupport.java @@ -0,0 +1,66 @@ +package com.red.circle.common.business.dto.rtc; + +/** + * Version based RTC compatibility rules. + */ +public final class RtcVersionSupport { + + public static final String TRTC_MIN_VERSION = "1.4.0"; + public static final String ROOM_PROVIDER_KEY_PREFIX = "RTC:ROOM:PROVIDER:"; + public static final long ROOM_PROVIDER_TTL_SECONDS = 12 * 60 * 60L; + + private RtcVersionSupport() { + } + + public static RtcProviderEnum defaultProvider(String version) { + return compareVersion(version, TRTC_MIN_VERSION) <= 0 + ? RtcProviderEnum.AGORA + : RtcProviderEnum.TRTC; + } + + public static String roomProviderKey(Object roomId) { + return ROOM_PROVIDER_KEY_PREFIX + roomId; + } + + public static int compareVersion(String left, String right) { + int[] leftParts = parseVersion(left); + int[] rightParts = parseVersion(right); + int length = Math.max(leftParts.length, rightParts.length); + for (int i = 0; i < length; i++) { + int leftPart = i < leftParts.length ? leftParts[i] : 0; + int rightPart = i < rightParts.length ? rightParts[i] : 0; + if (leftPart != rightPart) { + return Integer.compare(leftPart, rightPart); + } + } + return 0; + } + + private static int[] parseVersion(String version) { + if (version == null || version.isBlank()) { + return new int[]{0}; + } + String normalized = version.trim().split("-", 2)[0]; + String[] parts = normalized.split("\\."); + int[] values = new int[parts.length]; + for (int i = 0; i < parts.length; i++) { + values[i] = parseVersionPart(parts[i]); + } + return values; + } + + private static int parseVersionPart(String part) { + if (part == null || part.isBlank()) { + return 0; + } + String digits = part.replaceAll("[^0-9].*$", ""); + if (digits.isBlank()) { + return 0; + } + try { + return Integer.parseInt(digits); + } catch (NumberFormatException ignored) { + return 0; + } + } +} diff --git a/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/TrtcClientApi.java b/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/TrtcClientApi.java index a8703a17..e4cdc43d 100644 --- a/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/TrtcClientApi.java +++ b/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/TrtcClientApi.java @@ -1,8 +1,9 @@ -package com.red.circle.external.inner.endpoint.message.api; - -import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; -import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; -import com.red.circle.framework.dto.ResultResponse; +package com.red.circle.external.inner.endpoint.message.api; + +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; +import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; +import com.red.circle.framework.dto.ResultResponse; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -50,10 +51,20 @@ public interface TrtcClientApi { @RequestParam("type") String channelName ); - @GetMapping("/createAgoraTokenGoUp") - ResultResponse createAgoraTokenGoUp( - @RequestParam("userId") Long userId, - @RequestParam("type") String channelName - ); - -} + @GetMapping("/createAgoraTokenGoUp") + ResultResponse createAgoraTokenGoUp( + @RequestParam("userId") Long userId, + @RequestParam("type") String channelName + ); + + /** + * 创建 TRTC 入房凭证. + */ + @GetMapping("/createTrtcCredential") + ResultResponse createTrtcCredential( + @RequestParam("userId") Long userId, + @RequestParam("roomIdentifier") String roomIdentifier, + @RequestParam(value = "publisher", required = false) Boolean publisher + ); + +} diff --git a/rc-service/rc-service-external/external-adapter/src/main/java/com/red/circle/external/adapter/app/imtrtc/ImRestController.java b/rc-service/rc-service-external/external-adapter/src/main/java/com/red/circle/external/adapter/app/imtrtc/ImRestController.java index 3ce1a849..5b52e65f 100644 --- a/rc-service/rc-service-external/external-adapter/src/main/java/com/red/circle/external/adapter/app/imtrtc/ImRestController.java +++ b/rc-service/rc-service-external/external-adapter/src/main/java/com/red/circle/external/adapter/app/imtrtc/ImRestController.java @@ -1,15 +1,21 @@ package com.red.circle.external.adapter.app.imtrtc; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.red.circle.auth.inner.endpoint.AuthClient; -import com.red.circle.common.business.dto.cmd.AppExtCommand; -import com.red.circle.component.instant.msg.agora.AgoraAuthentication; -import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.account.ImAccountManagerService; -import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.group.ImGroupAvManagerService; -import com.red.circle.component.instant.msg.tencet.service.endpoint.api.trtc.TencentTrtcService; -import com.red.circle.component.instant.msg.tencet.service.result.OnlineStatusResult; -import com.red.circle.component.instant.msg.tencet.service.result.OnlineStatusResult.OnlineStatus; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.red.circle.auth.inner.endpoint.AuthClient; +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.common.business.dto.rtc.RtcProviderEnum; +import com.red.circle.common.business.dto.rtc.RtcVersionSupport; +import com.red.circle.component.instant.msg.InstantMessageProperties; +import com.red.circle.component.instant.msg.agora.AgoraAuthentication; +import com.red.circle.component.instant.msg.tencet.props.TrtcTencetProperties; +import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.account.ImAccountManagerService; +import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.group.ImGroupAvManagerService; +import com.red.circle.component.instant.msg.tencet.service.endpoint.api.trtc.TencentTrtcService; +import com.red.circle.component.instant.msg.tencet.service.result.OnlineStatusResult; +import com.red.circle.component.instant.msg.tencet.service.result.OnlineStatusResult.OnlineStatus; +import com.red.circle.component.redis.service.RedisService; import com.red.circle.external.clienobject.TmpSigCO; import com.red.circle.external.cmd.imtrtc.ImRoomRemoveUserCmd; import com.red.circle.external.infra.database.redis.service.UserAgoraTokenCacheService; @@ -23,13 +29,15 @@ import com.red.circle.framework.web.controller.BaseController; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.regex.RegexConstant; import com.red.circle.tool.core.text.StringPool; -import com.red.circle.other.inner.asserts.user.UserErrorCode; -import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; -import com.red.circle.other.inner.model.dto.user.UserProfileDTO; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import com.red.circle.other.inner.asserts.user.UserErrorCode; +import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.tencentyun.TLSSigAPIv2; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import io.agora.media.RtcTokenBuilder2; import lombok.RequiredArgsConstructor; @@ -41,8 +49,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.util.StringUtils; /** * IM操作. @@ -62,14 +71,22 @@ public class ImRestController extends BaseController { private final UserProfileClient userProfileClient; private final TencentTrtcService tencentTrtcService; private final AgoraAuthentication agoraAuthentication; - private final ImGroupAvManagerService imGroupAvManagerService; - private final ImAccountManagerService imAccountManagerService; - private final UserAgoraTokenCacheService userAgoraTokenCacheService; - - @Value("${rtc.appId}") - private String appId; - @Value("${rtc.certificate}") - private String certificate; + private final ImGroupAvManagerService imGroupAvManagerService; + private final ImAccountManagerService imAccountManagerService; + private final UserAgoraTokenCacheService userAgoraTokenCacheService; + private final RedisService redisService; + private final InstantMessageProperties instantMessageProperties; + + @Value("${rtc.appId}") + private String appId; + @Value("${rtc.certificate}") + private String certificate; + + @Value("${red-circle.instant-message.tencet-trtc.userSigExpireSeconds:7200}") + private long trtcUserSigExpireSeconds; + + @Value("${red-circle.instant-message.tencet-trtc.userSigKey:${red-circle.instant-message.tencet-trtc.secretKey:}}") + private String trtcUserSigKey; /** * 认证签名(腾讯). @@ -127,13 +144,16 @@ public class ImRestController extends BaseController { * @eo.method get * @eo.request-type formdata */ - @GetMapping("/tmp/agora/sig") - public TmpSigCO userSig(Long userId, String channelName,Boolean isPublisher) { - log.warn("/tmp/agora/sig {}, {}, {}", userId, channelName, isPublisher); - if (isPublisher == null){ - isPublisher = false; - } - String token; + @GetMapping("/tmp/agora/sig") + public TmpSigCO userSig(AppExtCommand cmd, Long userId, String channelName, Boolean isPublisher) { + log.warn("/tmp/agora/sig {}, {}, {}", userId, channelName, isPublisher); + if (isPublisher == null){ + isPublisher = false; + } + RtcProviderEnum provider = resolveRoomProvider(channelName, currentVersion(cmd)); + ResponseAssert.isTrue(CommonErrorCode.UPGRADE_VERSION, + !Objects.equals(RtcProviderEnum.TRTC, provider)); + String token; if (isPublisher){ token = userAgoraTokenCacheService.getUserAgoraToken(userId, Long.valueOf(channelName)); } else { @@ -156,36 +176,160 @@ public class ImRestController extends BaseController { if (Objects.nonNull(json)) { return getTmpSigByUserCredential(userId, token, json, gson); - } else { - return new TmpSigCO() - .setRtcToken(token) - .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody()); - } - - } - - /** - * 根据用户信息获取用户Servicetoken + } else { + RtcCredentialCO credential = createAgoraCredential(channelName, token); + return new TmpSigCO() + .setRtcToken(token) + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setRtcCredential(credential) + .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody()); + } + + } + + /** + * RTC 凭证,支持按客户端版本和房间级 provider 在 Agora/TRTC 间分流. + * + * @param userId 用户id + * @param roomId 业务房间id,作为房间级 provider 缓存 key + * @param channelName 历史 Agora channel,可为空 + * @param roomAccount TRTC 房间号,优先使用 + * @param isPublisher 是否主播/上麦 + */ + @GetMapping("/tmp/rtc/sig") + public TmpSigCO rtcSig( + AppExtCommand cmd, + @RequestParam("userId") Long userId, + @RequestParam(value = "roomId", required = false) Long roomId, + @RequestParam(value = "channelName", required = false) String channelName, + @RequestParam(value = "roomAccount", required = false) String roomAccount, + @RequestParam(value = "isPublisher", required = false) Boolean isPublisher) { + String providerRoomKey = Objects.nonNull(roomId) ? roomId.toString() : channelName; + ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + StringUtils.hasText(providerRoomKey)); + + RtcProviderEnum provider = resolveRoomProvider(providerRoomKey, currentVersion(cmd)); + boolean publisher = Boolean.TRUE.equals(isPublisher); + if (Objects.equals(RtcProviderEnum.AGORA, provider)) { + String agoraChannelName = StringUtils.hasText(channelName) ? channelName : providerRoomKey; + String token = publisher + ? createChannelPublisherToken(agoraChannelName, userId) + : createChannelSubscriberToken(agoraChannelName, userId); + RtcCredentialCO credential = createAgoraCredential(agoraChannelName, token); + return new TmpSigCO() + .setRtcToken(token) + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setRtcCredential(credential) + .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody()); + } + + String trtcRoomIdentifier = firstText(roomAccount, channelName, providerRoomKey); + RtcCredentialCO credential = createTrtcCredential(userId, trtcRoomIdentifier, publisher); + return new TmpSigCO() + .setRtcToken(credential.getTrtcUserSig()) + .setRtcProvider(RtcProviderEnum.TRTC.name()) + .setRtcCredential(credential) + .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody()); + } + + /** + * 根据用户信息获取用户Servicetoken * * @param userId * @param token * @param json * @param gson - * @return - */ - private TmpSigCO getTmpSigByUserCredential(Long userId, String token, String json, Gson gson) { - try { - UserCredential userCredential = gson.fromJson(json, UserCredential.class); - return new TmpSigCO() - .setRtcToken(token) - .setNewServiceToken(userCredential.token()); - } catch (JsonSyntaxException e) { - log.error("/tmp/agora/sig类型转换错误", e); - return new TmpSigCO() - .setRtcToken(token) - .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody()); - } - } + * @return + */ + private TmpSigCO getTmpSigByUserCredential(Long userId, String token, String json, Gson gson) { + RtcCredentialCO credential = createAgoraCredential(null, token); + try { + UserCredential userCredential = gson.fromJson(json, UserCredential.class); + return new TmpSigCO() + .setRtcToken(token) + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setRtcCredential(credential) + .setNewServiceToken(userCredential.token()); + } catch (JsonSyntaxException e) { + log.error("/tmp/agora/sig类型转换错误", e); + return new TmpSigCO() + .setRtcToken(token) + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setRtcCredential(credential) + .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody()); + } + } + + private RtcProviderEnum resolveRoomProvider(String roomKey, String version) { + if (!StringUtils.hasText(roomKey)) { + return RtcVersionSupport.defaultProvider(version); + } + String cacheKey = RtcVersionSupport.roomProviderKey(roomKey.trim()); + RtcProviderEnum cached = RtcProviderEnum.of(redisService.getString(cacheKey)); + if (Objects.nonNull(cached)) { + return cached; + } + RtcProviderEnum provider = RtcVersionSupport.defaultProvider(version); + redisService.setIfAbsent(cacheKey, provider.name(), + RtcVersionSupport.ROOM_PROVIDER_TTL_SECONDS, TimeUnit.SECONDS); + RtcProviderEnum resolved = RtcProviderEnum.of(redisService.getString(cacheKey)); + return Objects.nonNull(resolved) ? resolved : provider; + } + + private String currentVersion(AppExtCommand cmd) { + return Optional.ofNullable(cmd) + .map(AppExtCommand::getReqAppIntel) + .map(reqAppIntel -> reqAppIntel.getVersion()) + .orElse(null); + } + + private RtcCredentialCO createAgoraCredential(String channelName, String token) { + return new RtcCredentialCO() + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setAgoraChannelName(channelName) + .setAgoraToken(token); + } + + private RtcCredentialCO createTrtcCredential(Long userId, String roomIdentifier, + boolean publisher) { + TrtcTencetProperties trtcProperties = instantMessageProperties.getTencetTrtc(); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, trtcProperties); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, trtcProperties.getSdkAppId()); + ResponseAssert.isTrue(CommonErrorCode.CONFIGURATION_ERROR, StringUtils.hasText(trtcUserSigKey)); + + String trtcUserId = userId.toString(); + String userSig = new TLSSigAPIv2(trtcProperties.getSdkAppId(), trtcUserSigKey) + .genUserSig(trtcUserId, trtcUserSigExpireSeconds); + RtcCredentialCO credential = new RtcCredentialCO() + .setRtcProvider(RtcProviderEnum.TRTC.name()) + .setTrtcSdkAppId(trtcProperties.getSdkAppId()) + .setTrtcUserId(trtcUserId) + .setTrtcUserSig(userSig) + .setTrtcRole(publisher ? 20 : 21) + .setTrtcUserSigExpireSeconds(trtcUserSigExpireSeconds) + .setTrtcUserSigExpireAt( + System.currentTimeMillis() + trtcUserSigExpireSeconds * 1000L); + applyTrtcRoomIdentifier(credential, roomIdentifier); + return credential; + } + + private void applyTrtcRoomIdentifier(RtcCredentialCO credential, String roomIdentifier) { + String normalizedRoomIdentifier = roomIdentifier.trim(); + try { + credential.setTrtcRoomId(Long.valueOf(normalizedRoomIdentifier)); + } catch (NumberFormatException ignored) { + credential.setTrtcStrRoomId(normalizedRoomIdentifier); + } + } + + private String firstText(String... values) { + for (String value : values) { + if (StringUtils.hasText(value)) { + return value.trim(); + } + } + return ""; + } /** diff --git a/rc-service/rc-service-external/external-client/src/main/java/com/red/circle/external/clienobject/TmpSigCO.java b/rc-service/rc-service-external/external-client/src/main/java/com/red/circle/external/clienobject/TmpSigCO.java index b2ef8d50..9a54b3ad 100644 --- a/rc-service/rc-service-external/external-client/src/main/java/com/red/circle/external/clienobject/TmpSigCO.java +++ b/rc-service/rc-service-external/external-client/src/main/java/com/red/circle/external/clienobject/TmpSigCO.java @@ -1,9 +1,10 @@ -package com.red.circle.external.clienobject; - -import com.red.circle.framework.dto.ClientObject; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; +package com.red.circle.external.clienobject; + +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.framework.dto.ClientObject; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; /** * 临时签名(兼容服务). @@ -20,8 +21,18 @@ public class TmpSigCO extends ClientObject { */ private String rtcToken; - /** - * 新服务token. - */ - private String newServiceToken; -} + /** + * 新服务token. + */ + private String newServiceToken; + + /** + * RTC provider: AGORA or TRTC. + */ + private String rtcProvider; + + /** + * Provider specific credential. + */ + private RtcCredentialCO rtcCredential; +} diff --git a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/TrtcClientEndpoint.java b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/TrtcClientEndpoint.java index e95d35a2..ffddc066 100644 --- a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/TrtcClientEndpoint.java +++ b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/TrtcClientEndpoint.java @@ -1,8 +1,9 @@ -package com.red.circle.external.inner.endpoint.message; - -import com.red.circle.external.inner.endpoint.message.api.TrtcClientApi; -import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; -import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; +package com.red.circle.external.inner.endpoint.message; + +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.external.inner.endpoint.message.api.TrtcClientApi; +import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; +import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; import com.red.circle.external.inner.service.message.TrtcClientService; import com.red.circle.framework.dto.ResultResponse; import lombok.RequiredArgsConstructor; @@ -45,8 +46,15 @@ public class TrtcClientEndpoint implements TrtcClientApi { } @Override - public ResultResponse createAgoraTokenGoUp(Long userId, String channelName) { - return ResultResponse.success(trtcClientService.createAgoraTokenGoUp(userId, channelName)); - } - -} + public ResultResponse createAgoraTokenGoUp(Long userId, String channelName) { + return ResultResponse.success(trtcClientService.createAgoraTokenGoUp(userId, channelName)); + } + + @Override + public ResultResponse createTrtcCredential(Long userId, String roomIdentifier, + Boolean publisher) { + return ResultResponse.success( + trtcClientService.createTrtcCredential(userId, roomIdentifier, publisher)); + } + +} diff --git a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/TrtcClientService.java b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/TrtcClientService.java index a5c3f5f0..0208e495 100644 --- a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/TrtcClientService.java +++ b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/TrtcClientService.java @@ -1,7 +1,8 @@ -package com.red.circle.external.inner.service.message; - -import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; -import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; +package com.red.circle.external.inner.service.message; + +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; +import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; /** * trtc 服务. @@ -33,8 +34,13 @@ public interface TrtcClientService { /** * 创建声网令牌. */ - String createAgoraToken(Long userId, String channelName); - - String createAgoraTokenGoUp(Long userId, String channelName); - -} + String createAgoraToken(Long userId, String channelName); + + String createAgoraTokenGoUp(Long userId, String channelName); + + /** + * 创建 TRTC 入房凭证. + */ + RtcCredentialCO createTrtcCredential(Long userId, String roomIdentifier, Boolean publisher); + +} diff --git a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/TrtcClientServiceImpl.java b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/TrtcClientServiceImpl.java index 7c6eb016..b7720127 100644 --- a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/TrtcClientServiceImpl.java +++ b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/TrtcClientServiceImpl.java @@ -1,17 +1,23 @@ -package com.red.circle.external.inner.service.message.impl; - -import com.red.circle.component.instant.msg.agora.AgoraAuthentication; -import com.red.circle.component.instant.msg.agora.AgoraProperties; -import com.red.circle.component.instant.msg.tencet.service.endpoint.api.trtc.TencentTrtcService; -import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; -import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; -import com.red.circle.external.inner.service.message.TrtcClientService; -import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; -import com.red.circle.other.inner.model.dto.user.UserProfileDTO; -import io.agora.media.RtcTokenBuilder2; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; +package com.red.circle.external.inner.service.message.impl; + +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.common.business.dto.rtc.RtcProviderEnum; +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.TencentTrtcService; +import com.red.circle.external.inner.model.cmd.message.TrtcRemoveUserCmd; +import com.red.circle.external.inner.model.cmd.message.TrtcUserBlockedCmd; +import com.red.circle.external.inner.service.message.TrtcClientService; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.tencentyun.TLSSigAPIv2; +import io.agora.media.RtcTokenBuilder2; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; /** * trtc 服务. @@ -22,17 +28,22 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class TrtcClientServiceImpl implements TrtcClientService { - private final UserProfileClient userProfileClient; - private final TencentTrtcService tencentTrtcService; -// private final AgoraAuthentication agoraAuthentication; -// private final AgoraProperties agoraProperties; - - @Value("${rtc.appId}") - private String appId; - - @Value("${rtc.certificate}") - private String certificate; - + private final UserProfileClient userProfileClient; + private final TencentTrtcService tencentTrtcService; + private final InstantMessageProperties instantMessageProperties; + + @Value("${rtc.appId}") + private String appId; + + @Value("${rtc.certificate}") + private String certificate; + + @Value("${red-circle.instant-message.tencet-trtc.userSigExpireSeconds:7200}") + private long trtcUserSigExpireSeconds; + + @Value("${red-circle.instant-message.tencet-trtc.userSigKey:${red-circle.instant-message.tencet-trtc.secretKey:}}") + private String trtcUserSigKey; + @Override public boolean removeUser(TrtcRemoveUserCmd cmd) { return tencentTrtcService.removeUser(cmd.getRoomId(), @@ -61,10 +72,50 @@ public class TrtcClientServiceImpl implements TrtcClientService { } @Override - public String createAgoraTokenGoUp(Long userId, String channelName) { - UserProfileDTO userProfile = userProfileClient.getByUserId(userId).getBody(); - RtcTokenBuilder2 var3 = new RtcTokenBuilder2(); - return var3.buildTokenWithUid(appId, certificate, channelName, Integer.parseInt(userProfile.getAccount()), RtcTokenBuilder2.Role.ROLE_PUBLISHER, 21600, 21600); - } - -} + public String createAgoraTokenGoUp(Long userId, String channelName) { + UserProfileDTO userProfile = userProfileClient.getByUserId(userId).getBody(); + RtcTokenBuilder2 var3 = new RtcTokenBuilder2(); + return var3.buildTokenWithUid(appId, certificate, channelName, Integer.parseInt(userProfile.getAccount()), RtcTokenBuilder2.Role.ROLE_PUBLISHER, 21600, 21600); + } + + @Override + public RtcCredentialCO createTrtcCredential(Long userId, String roomIdentifier, + Boolean publisher) { + ResponseAssert.notNull(CommonErrorCode.REQUIRED_FIELDS_CANNOT_BE_IGNORED, userId); + ResponseAssert.isTrue(CommonErrorCode.REQUIRED_FIELDS_CANNOT_BE_IGNORED, + StringUtils.hasText(roomIdentifier)); + + TrtcTencetProperties trtcProperties = instantMessageProperties.getTencetTrtc(); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, trtcProperties); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, trtcProperties.getSdkAppId()); + ResponseAssert.isTrue(CommonErrorCode.CONFIGURATION_ERROR, StringUtils.hasText(trtcUserSigKey)); + + String trtcUserId = userId.toString(); + String userSig = new TLSSigAPIv2( + trtcProperties.getSdkAppId(), + trtcUserSigKey + ).genUserSig(trtcUserId, trtcUserSigExpireSeconds); + + RtcCredentialCO credential = new RtcCredentialCO() + .setRtcProvider(RtcProviderEnum.TRTC.name()) + .setTrtcSdkAppId(trtcProperties.getSdkAppId()) + .setTrtcUserId(trtcUserId) + .setTrtcUserSig(userSig) + .setTrtcRole(Boolean.TRUE.equals(publisher) ? 20 : 21) + .setTrtcUserSigExpireSeconds(trtcUserSigExpireSeconds) + .setTrtcUserSigExpireAt( + System.currentTimeMillis() + trtcUserSigExpireSeconds * 1000L); + applyTrtcRoomIdentifier(credential, roomIdentifier); + return credential; + } + + private void applyTrtcRoomIdentifier(RtcCredentialCO credential, String roomIdentifier) { + String normalizedRoomIdentifier = roomIdentifier.trim(); + try { + credential.setTrtcRoomId(Long.valueOf(normalizedRoomIdentifier)); + } catch (NumberFormatException ignored) { + credential.setTrtcStrRoomId(normalizedRoomIdentifier); + } + } + +} diff --git a/rc-service/rc-service-live/live-application/src/main/java/com/red/circle/live/app/command/mic/LiveMicGoUpCmdExe.java b/rc-service/rc-service-live/live-application/src/main/java/com/red/circle/live/app/command/mic/LiveMicGoUpCmdExe.java index 0a270e97..27bcd05e 100644 --- a/rc-service/rc-service-live/live-application/src/main/java/com/red/circle/live/app/command/mic/LiveMicGoUpCmdExe.java +++ b/rc-service/rc-service-live/live-application/src/main/java/com/red/circle/live/app/command/mic/LiveMicGoUpCmdExe.java @@ -1,6 +1,9 @@ -package com.red.circle.live.app.command.mic; - -import com.red.circle.component.redis.service.RedisService; +package com.red.circle.live.app.command.mic; + +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.common.business.dto.rtc.RtcProviderEnum; +import com.red.circle.common.business.dto.rtc.RtcVersionSupport; +import com.red.circle.component.redis.service.RedisService; import com.red.circle.external.inner.endpoint.message.TrtcClient; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.response.CommonErrorCode; @@ -29,7 +32,8 @@ import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import java.util.Objects; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; @@ -52,16 +56,25 @@ public class LiveMicGoUpCmdExe { private final RedisService redisService; private final RoomDailyTaskClient roomDailyTaskClient; private final RoomManagerClient roomManagerClient; - private final RedisTemplate redisTemplate; - private final RoomMemberClient roomMemberClient; - + private final RedisTemplate redisTemplate; + private final RoomMemberClient roomMemberClient; + public LiveMicrophoneCO execute(MicUpDownCmd cmd) { boolean targetMicAlreadySelf = false; boolean goUpCompleted = false; + RtcProviderEnum rtcProvider = null; try { checkPermissions(cmd); targetMicAlreadySelf = checkTargetMicAvailableOrSelf(cmd); - String roomToken = createPublisherToken(cmd); + rtcProvider = resolveRoomProvider(cmd); + String roomToken = null; + RtcCredentialCO rtcCredential; + if (Objects.equals(RtcProviderEnum.AGORA, rtcProvider)) { + roomToken = createPublisherToken(cmd); + rtcCredential = createAgoraCredential(cmd, roomToken); + } else { + rtcCredential = createTrtcCredential(cmd, true); + } LiveMicrophoneNoticeDTO noticeDTO = liveMicrophoneGateway.goUp( cmd.getRoomId(), @@ -70,16 +83,22 @@ public class LiveMicGoUpCmdExe { ); goUpCompleted = true; - cachePublisherToken(cmd, roomToken); + if (Objects.equals(RtcProviderEnum.AGORA, rtcProvider)) { + cachePublisherToken(cmd, roomToken); + } LiveMicrophoneCO notice = returnMicrophoneCO(cmd, noticeDTO); - notice.setRoomToken(roomToken); + notice.setRoomToken(roomToken) + .setRtcProvider(rtcProvider.name()) + .setRtcCredential(rtcCredential); // 房主邀请成员上麦 handleRoomDailyTask(cmd); return notice; } catch (RuntimeException e) { if (goUpCompleted) { - rollbackPublisherToken(cmd); + if (Objects.equals(RtcProviderEnum.AGORA, rtcProvider)) { + rollbackPublisherToken(cmd); + } if (!targetMicAlreadySelf) { rollbackMicSeat(cmd, e); } @@ -103,6 +122,60 @@ public class LiveMicGoUpCmdExe { userAgoraTokenCacheService.save(roomToken, cmd.requiredReqUserId(), cmd.getRoomId()); } + private RtcProviderEnum resolveRoomProvider(MicUpDownCmd cmd) { + if (Objects.isNull(redisService)) { + return RtcVersionSupport.defaultProvider(currentVersion(cmd)); + } + String cacheKey = RtcVersionSupport.roomProviderKey(cmd.getRoomId()); + RtcProviderEnum requestedProvider = RtcVersionSupport.defaultProvider(currentVersion(cmd)); + RtcProviderEnum cachedProvider = RtcProviderEnum.of(redisService.getString(cacheKey)); + if (Objects.nonNull(cachedProvider)) { + ResponseAssert.isTrue(CommonErrorCode.UPGRADE_VERSION, + !Objects.equals(RtcProviderEnum.AGORA, requestedProvider) + || !Objects.equals(RtcProviderEnum.TRTC, cachedProvider)); + return cachedProvider; + } + redisService.setIfAbsent(cacheKey, requestedProvider.name(), + RtcVersionSupport.ROOM_PROVIDER_TTL_SECONDS, TimeUnit.SECONDS); + RtcProviderEnum resolvedProvider = RtcProviderEnum.of(redisService.getString(cacheKey)); + return Objects.nonNull(resolvedProvider) ? resolvedProvider : requestedProvider; + } + + private String currentVersion(MicUpDownCmd cmd) { + return Optional.ofNullable(cmd) + .map(MicUpDownCmd::getReqAppIntel) + .map(reqAppIntel -> reqAppIntel.getVersion()) + .orElse(null); + } + + private RtcCredentialCO createAgoraCredential(MicUpDownCmd cmd, String token) { + return new RtcCredentialCO() + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setAgoraChannelName(cmd.getRoomId().toString()) + .setAgoraToken(token); + } + + private RtcCredentialCO createTrtcCredential(MicUpDownCmd cmd, boolean publisher) { + ResultResponse response = trtcClient.createTrtcCredential( + cmd.requiredReqUserId(), + resolveTrtcRoomIdentifier(cmd), + publisher + ); + RtcCredentialCO credential = Objects.nonNull(response) ? response.getBody() : null; + ResponseAssert.notNull(CommonErrorCode.THIRD_PARTY_SERVER_ERROR, credential); + return credential; + } + + private String resolveTrtcRoomIdentifier(MicUpDownCmd cmd) { + ResultResponse response = roomManagerClient.getById(cmd.getRoomId()); + RoomProfileDTO roomProfile = Objects.nonNull(response) ? response.getBody() : null; + if (Objects.nonNull(roomProfile) && Objects.nonNull(roomProfile.getRoomAccount()) + && !roomProfile.getRoomAccount().isBlank()) { + return roomProfile.getRoomAccount(); + } + return cmd.getRoomId().toString(); + } + private boolean checkTargetMicAvailableOrSelf(MicUpDownCmd cmd) { boolean targetMicAlreadySelf = liveMicCacheService.checkEqSelf( cmd.getRoomId(), diff --git a/rc-service/rc-service-live/live-client/src/main/java/com/red/circle/live/app/dto/clientobject/LiveMicrophoneCO.java b/rc-service/rc-service-live/live-client/src/main/java/com/red/circle/live/app/dto/clientobject/LiveMicrophoneCO.java index 5062d63e..dfb80909 100644 --- a/rc-service/rc-service-live/live-client/src/main/java/com/red/circle/live/app/dto/clientobject/LiveMicrophoneCO.java +++ b/rc-service/rc-service-live/live-client/src/main/java/com/red/circle/live/app/dto/clientobject/LiveMicrophoneCO.java @@ -1,10 +1,11 @@ -package com.red.circle.live.app.dto.clientobject; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.red.circle.framework.dto.ClientObject; -import java.io.Serial; -import lombok.Data; +package com.red.circle.live.app.dto.clientobject; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.framework.dto.ClientObject; +import java.io.Serial; +import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -47,9 +48,19 @@ public class LiveMicrophoneCO extends ClientObject { */ private LiveMicUserCO user; - /** - * 上麦token信息 - */ - private String roomToken; - -} + /** + * 上麦token信息 + */ + private String roomToken; + + /** + * RTC provider: AGORA or TRTC. + */ + private String rtcProvider; + + /** + * RTC credential for clients that support provider negotiation. + */ + private RtcCredentialCO rtcCredential; + +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java index 3b98aaa8..1593f197 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/room/RoomEnterCmdExe.java @@ -1,6 +1,9 @@ -package com.red.circle.other.app.command.room; - +package com.red.circle.other.app.command.room; + import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.common.business.dto.rtc.RtcProviderEnum; +import com.red.circle.common.business.dto.rtc.RtcVersionSupport; import com.red.circle.external.inner.endpoint.message.ImGroupClient; import com.red.circle.external.inner.endpoint.message.TrtcClient; import com.red.circle.external.inner.model.cmd.message.CreateGroupCmd; @@ -121,13 +124,21 @@ public class RoomEnterCmdExe { ); ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, ownUserProfile); - //String token = createAgoraToken(cmd); - String token = userAgoraTokenCacheService.getUserAgoraToken(cmd.getReqUserId(), cmd.getRoomId()); - if (Objects.isNull(token)) { - token = trtcClient.createAgoraToken(cmd.getReqUserId(), cmd.getRoomId().toString()).getBody(); - log.warn("进房间token:{}", token); - userAgoraTokenCacheService.save(token, cmd.getReqUserId(), cmd.getRoomId()); - } + RtcProviderEnum rtcProvider = resolveRoomProvider(cmd); + String token = null; + RtcCredentialCO rtcCredential; + if (Objects.equals(RtcProviderEnum.AGORA, rtcProvider)) { + token = userAgoraTokenCacheService.getUserAgoraToken(cmd.getReqUserId(), cmd.getRoomId()); + if (Objects.isNull(token)) { + token = trtcClient.createAgoraToken(cmd.getReqUserId(), cmd.getRoomId().toString()) + .getBody(); + log.warn("进房间token:{}", token); + userAgoraTokenCacheService.save(token, cmd.getReqUserId(), cmd.getRoomId()); + } + rtcCredential = createAgoraCredential(cmd, token); + } else { + rtcCredential = createTrtcCredential(cmd, manager, false); + } try { // 无法操作,错误归属 @@ -159,10 +170,12 @@ public class RoomEnterCmdExe { .setEntrants( new EntryRoomEntrantsCO() .setRoles(roomManagerCacheService.getRoomUserRoles(cmd.getReqUserId(), - cmd.getRoomId())) - .setLevel(getUserLevel(cmd.requireReqSysOriginEnum(), cmd.getReqUserId())) - .setNobleVipAbility(listUserPropsVipActualEquity(cmd)) - .setRoomToken(token) + cmd.getRoomId())) + .setLevel(getUserLevel(cmd.requireReqSysOriginEnum(), cmd.getReqUserId())) + .setNobleVipAbility(listUserPropsVipActualEquity(cmd)) + .setRoomToken(token) + .setRtcProvider(rtcProvider.name()) + .setRtcCredential(rtcCredential) ); } @@ -210,9 +223,9 @@ public class RoomEnterCmdExe { * @param cmd * @return */ - private String createAgoraToken(RoomEntryCmd cmd) { - ExecutorService executor = Executors.newSingleThreadExecutor(); - AtomicReference agoraToken = new AtomicReference<>("BaHrIypJdFWqwWtoZLToRucRyoGrVgYCONBFvGynmlJsevFYsWBfEIcaWUUGijQBjycTtmSvkmBINoSltJMmgiPeBnkaWErixaxg"); + private String createAgoraToken(RoomEntryCmd cmd) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + AtomicReference agoraToken = new AtomicReference<>("BaHrIypJdFWqwWtoZLToRucRyoGrVgYCONBFvGynmlJsevFYsWBfEIcaWUUGijQBjycTtmSvkmBINoSltJMmgiPeBnkaWErixaxg"); Future future = executor.submit(() -> { agoraToken.set(trtcClient.createAgoraToken(cmd.getReqUserId(), cmd.getRoomId().toString()).getBody()); }); @@ -227,13 +240,63 @@ public class RoomEnterCmdExe { log.error("上麦获取声网token其他异常: {}", e.getMessage()); } - executor.shutdown(); - return agoraToken.get(); - } - - private List listUserPropsVipActualEquity(RoomEntryCmd cmd) { - return userProfileGateway.listUserActualEquityEnum(cmd.requiredReqUserId()); - } + executor.shutdown(); + return agoraToken.get(); + } + + private RtcProviderEnum resolveRoomProvider(RoomEntryCmd cmd) { + String cacheKey = RtcVersionSupport.roomProviderKey(cmd.getRoomId()); + RtcProviderEnum requestedProvider = RtcVersionSupport.defaultProvider(currentVersion(cmd)); + RtcProviderEnum cachedProvider = RtcProviderEnum.of(redisService.getString(cacheKey)); + if (Objects.nonNull(cachedProvider)) { + ResponseAssert.isTrue(CommonErrorCode.UPGRADE_VERSION, + !Objects.equals(RtcProviderEnum.AGORA, requestedProvider) + || !Objects.equals(RtcProviderEnum.TRTC, cachedProvider)); + return cachedProvider; + } + redisService.setIfAbsent(cacheKey, requestedProvider.name(), + RtcVersionSupport.ROOM_PROVIDER_TTL_SECONDS, TimeUnit.SECONDS); + RtcProviderEnum resolvedProvider = RtcProviderEnum.of(redisService.getString(cacheKey)); + return Objects.nonNull(resolvedProvider) ? resolvedProvider : requestedProvider; + } + + private String currentVersion(RoomEntryCmd cmd) { + return Optional.ofNullable(cmd) + .map(RoomEntryCmd::getReqAppIntel) + .map(reqAppIntel -> reqAppIntel.getVersion()) + .orElse(null); + } + + private RtcCredentialCO createAgoraCredential(RoomEntryCmd cmd, String token) { + return new RtcCredentialCO() + .setRtcProvider(RtcProviderEnum.AGORA.name()) + .setAgoraChannelName(cmd.getRoomId().toString()) + .setAgoraToken(token); + } + + private RtcCredentialCO createTrtcCredential(RoomEntryCmd cmd, RoomProfileManager manager, + boolean publisher) { + ResultResponse response = trtcClient.createTrtcCredential( + cmd.getReqUserId(), + resolveTrtcRoomIdentifier(cmd, manager), + publisher + ); + RtcCredentialCO credential = Objects.nonNull(response) ? response.getBody() : null; + ResponseAssert.notNull(CommonErrorCode.THIRD_PARTY_SERVER_ERROR, credential); + return credential; + } + + private String resolveTrtcRoomIdentifier(RoomEntryCmd cmd, RoomProfileManager manager) { + if (Objects.nonNull(manager) && Objects.nonNull(manager.getRoomAccount()) + && !manager.getRoomAccount().isBlank()) { + return manager.getRoomAccount(); + } + return cmd.getRoomId().toString(); + } + + private List listUserPropsVipActualEquity(RoomEntryCmd cmd) { + return userProfileGateway.listUserActualEquityEnum(cmd.requiredReqUserId()); + } private WealthAndCharmLevelDTO getUserLevel(SysOriginPlatformEnum sysOrigin, Long userId) { return Optional.ofNullable( diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/room/EntryRoomEntrantsCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/room/EntryRoomEntrantsCO.java index b85104b8..9be2a0dc 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/room/EntryRoomEntrantsCO.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/room/EntryRoomEntrantsCO.java @@ -1,10 +1,11 @@ -package com.red.circle.other.app.dto.clientobject.room; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.red.circle.framework.dto.ClientObject; -import com.red.circle.other.inner.enums.live.RoomUserRolesEnum; -import com.red.circle.other.inner.enums.material.PropsVipActualEquityEnum; -import com.red.circle.other.inner.model.dto.user.WealthAndCharmLevelDTO; +package com.red.circle.other.app.dto.clientobject.room; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.red.circle.common.business.dto.rtc.RtcCredentialCO; +import com.red.circle.framework.dto.ClientObject; +import com.red.circle.other.inner.enums.live.RoomUserRolesEnum; +import com.red.circle.other.inner.enums.material.PropsVipActualEquityEnum; +import com.red.circle.other.inner.model.dto.user.WealthAndCharmLevelDTO; import java.util.List; import lombok.Data; import lombok.EqualsAndHashCode; @@ -36,9 +37,19 @@ public class EntryRoomEntrantsCO extends ClientObject { */ private List nobleVipAbility; - /** - * 进入房间token信息 - */ - private String roomToken; - -} + /** + * 进入房间token信息 + */ + private String roomToken; + + /** + * RTC provider: AGORA or TRTC. + */ + private String rtcProvider; + + /** + * RTC credential for clients that support provider negotiation. + */ + private RtcCredentialCO rtcCredential; + +}