Commit bb5ac45be8f517c2e780aebd920a7218a432afe5

Authored by YunaiV
1 parent 8996b947

✨ feat(im): 优化 JDK8 的兼容性

yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java
... ... @@ -91,5 +91,6 @@ public interface ErrorCodeConstants {
91 91 ErrorCode RTC_GROUP_REQUIRED = new ErrorCode(1_040_900_007, "群聊通话必须指定群编号");
92 92 ErrorCode RTC_INVITE_BUSY = new ErrorCode(1_040_900_008, "通话发起繁忙,请稍后再试");
93 93 ErrorCode RTC_GROUP_CALL_ACTIVE = new ErrorCode(1_040_900_009, "该群已有进行中通话,请通过胶囊条加入");
  94 + ErrorCode RTC_GROUP_INVITEE_OVER_LIMIT = new ErrorCode(1_040_900_010, "群通话邀请人数超过最大值");
94 95  
95 96 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallStatusEnum.java
1 1 package cn.iocoder.yudao.module.im.enums.rtc;
2 2  
  3 +import cn.hutool.core.collection.ListUtil;
3 4 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 5 import lombok.Getter;
5 6 import lombok.RequiredArgsConstructor;
... ... @@ -28,7 +29,7 @@ public enum ImRtcCallStatusEnum implements ArrayValuable<Integer> {
28 29 /**
29 30 * 活跃状态集合;通话未结束(CREATED / RUNNING)
30 31 */
31   - public static final List<Integer> ACTIVE_STATUSES = List.of(CREATED.getStatus(), RUNNING.getStatus());
  32 + public static final List<Integer> ACTIVE_STATUSES = ListUtil.of(CREATED.getStatus(), RUNNING.getStatus());
32 33  
33 34 /**
34 35 * 状态值
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcParticipantStatusEnum.java
1 1 package cn.iocoder.yudao.module.im.enums.rtc;
2 2  
  3 +import cn.hutool.core.collection.ListUtil;
3 4 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 5 import lombok.Getter;
5 6 import lombok.RequiredArgsConstructor;
... ... @@ -35,7 +36,7 @@ public enum ImRtcParticipantStatusEnum implements ArrayValuable&lt;Integer&gt; {
35 36 /**
36 37 * 活跃状态集合;尚未离开通话(INVITING / JOINED)
37 38 */
38   - public static final List<Integer> ACTIVE_STATUSES = List.of(INVITING.getStatus(), JOINED.getStatus());
  39 + public static final List<Integer> ACTIVE_STATUSES = ListUtil.of(INVITING.getStatus(), JOINED.getStatus());
39 40  
40 41 /**
41 42 * 状态值
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.im.framework.rtc.core;
3 3 import cn.hutool.core.codec.Base64;
4 4 import cn.hutool.core.collection.CollUtil;
5 5 import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.map.MapUtil;
6 7 import cn.hutool.core.util.StrUtil;
7 8 import cn.hutool.crypto.digest.DigestUtil;
8 9 import cn.hutool.http.HttpRequest;
... ... @@ -214,7 +215,7 @@ public class LiveKitClient {
214 215 .setIssuer(cfg.getApiKey())
215 216 .setNotBefore(new Date(nowSec * 1000))
216 217 .setExpiresAt(new Date((nowSec + ADMIN_TOKEN_TTL.getSeconds()) * 1000))
217   - .setPayload("video", Map.of("roomAdmin", true))
  218 + .setPayload("video", MapUtil.of("roomAdmin", true))
218 219 .setSigner(JWTSignerUtil.hs256(cfg.getApiSecret().getBytes()))
219 220 .sign();
220 221 }
... ... @@ -232,7 +233,7 @@ public class LiveKitClient {
232 233 return HttpRequest.post(HttpUtils.wsUrlToHttp(imProperties.getRtc().getLivekitUrl()) + path)
233 234 .header("Authorization", "Bearer " + token)
234 235 .header("Content-Type", "application/json")
235   - .body(JSONUtil.toJsonStr(Map.of("room", room)))
  236 + .body(JSONUtil.toJsonStr(MapUtil.of("room", room)))
236 237 .timeout(SERVER_API_TIMEOUT_MS)
237 238 .execute();
238 239 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallService.java
... ... @@ -43,7 +43,7 @@ public interface ImRtcCallService {
43 43 void inviteMoreCall(Long userId, ImRtcCallInviteMoreReqVO reqVO);
44 44  
45 45 /**
46   - * 接听通话:私聊状态切换 INVITING → RUNNING;群通话直接加入已有 RUNNING 房间
  46 + * 接听通话:参与者 INVITING → JOINED;主表 CREATED → RUNNING(首次有非发起人接通时)
47 47 *
48 48 * @return 通话主表
49 49 */
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java
... ... @@ -41,7 +41,6 @@ import org.springframework.validation.annotation.Validated;
41 41 import java.time.Duration;
42 42 import java.time.LocalDateTime;
43 43 import java.util.*;
44   -import java.util.stream.Collectors;
45 44  
46 45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
47 46 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
... ... @@ -181,10 +180,11 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
181 180 }
182 181 rtcParticipantMapper.insertBatch(participants);
183 182  
184   - // 3.1 推送通知:RTC_CALL(INVITE) 给每个被邀请人
  183 + // 3.1 推送通知:RTC_CALL(INVITE) 给每个被邀请人;批量取用户避免 N 次 RPC
185 184 AdminUserRespDTO inviterUser = adminUserApi.getUser(inviterId);
  185 + Map<Long, AdminUserRespDTO> inviteeMap = adminUserApi.getUserMap(invitees);
186 186 for (Long inviteeId : invitees) {
187   - pushCallInviteNotification(call, inviterUser, inviteeId);
  187 + pushCallInviteNotification(call, inviterUser, inviteeId, inviteeMap.get(inviteeId));
188 188 }
189 189 // 3.2 群通话再向全群广播 RTC_CALL_START 入聊天流
190 190 if (ImConversationTypeEnum.isGroup(reqVO.getScene())) {
... ... @@ -268,10 +268,11 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
268 268 }
269 269 rtcParticipantMapper.insertBatch(participants);
270 270  
271   - // 3. 推送通知:RTC_CALL(INVITE) 给每个新邀请人
  271 + // 3. 推送通知:RTC_CALL(INVITE) 给每个新邀请人;批量取用户避免 N 次 RPC
272 272 AdminUserRespDTO inviter = adminUserApi.getUser(userId);
  273 + Map<Long, AdminUserRespDTO> inviteeMap = adminUserApi.getUserMap(incomingUserIds);
273 274 for (Long inviteeId : incomingUserIds) {
274   - pushCallInviteNotification(call, inviter, inviteeId);
  275 + pushCallInviteNotification(call, inviter, inviteeId, inviteeMap.get(inviteeId));
275 276 }
276 277 }
277 278  
... ... @@ -438,19 +439,23 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
438 439 return signToken(userId, displayName, room);
439 440 }
440 441  
441   - // TODO DONE @AI:兼容 JDK8 —— 项目 Spring Boot 3 已要求 JDK17+,switch arrow case 可直接用
442 442 @Override
443 443 public void handleLiveKitEvent(LiveKitWebhookEventDTO event) {
444 444 if (event == null || event.getEvent() == null) {
445 445 return;
446 446 }
447 447 switch (event.getEvent()) {
448   - case LiveKitWebhookEventDTO.EVENT_PARTICIPANT_JOINED -> handleParticipantJoined(event);
449   - case LiveKitWebhookEventDTO.EVENT_PARTICIPANT_LEFT -> handleParticipantLeft(event);
450   - case LiveKitWebhookEventDTO.EVENT_ROOM_FINISHED -> handleRoomFinished(event);
451   - default -> {
452   - /* 其它事件忽略;track_published 等业务态由 LiveKit 自身分发驱动 */
453   - }
  448 + case LiveKitWebhookEventDTO.EVENT_PARTICIPANT_JOINED:
  449 + handleParticipantJoined(event);
  450 + break;
  451 + case LiveKitWebhookEventDTO.EVENT_PARTICIPANT_LEFT:
  452 + handleParticipantLeft(event);
  453 + break;
  454 + case LiveKitWebhookEventDTO.EVENT_ROOM_FINISHED:
  455 + handleRoomFinished(event);
  456 + break;
  457 + default:
  458 + // 其它事件忽略;track_published 等业务态由 LiveKit 自身分发驱动
454 459 }
455 460 }
456 461  
... ... @@ -611,7 +616,7 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
611 616 if (reqVO.getPeerUserId() == null) {
612 617 throw exception(RTC_PRIVATE_INVITEE_REQUIRED);
613 618 }
614   - if (Objects.equals(userId, reqVO.getPeerUserId())) {
  619 + if (ObjUtil.equal(userId, reqVO.getPeerUserId())) {
615 620 throw exception(RTC_INVITE_SELF);
616 621 }
617 622 friendService.validateFriend(userId, reqVO.getPeerUserId());
... ... @@ -629,7 +634,7 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
629 634 }
630 635  
631 636 /**
632   - * 解析被邀请池:私聊为 peerUserId 单元素;群聊优先用前端选中的子集,否则取群活跃成员;超量截断
  637 + * 解析被邀请池:私聊为 peerUserId 单元素;群聊优先用前端选中的子集,否则取群活跃成员;超量直接抛错
633 638 * <p>
634 639 * 群聊场景:被邀请人必须是该群活跃成员;防止恶意客户端塞任意 userId 进群房间
635 640 */
... ... @@ -653,12 +658,10 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
653 658 }
654 659 // 不论来源,发起人本人不进被邀请池
655 660 initial.remove(inviterId);
656   - // 超出最大同时在房成员数;截断为 max-1 个,避免邀请整个 500 人大群
657   - // TODO @AI:超过数量,直接抛出异常,不用裁剪,不然很奇怪;
  661 + // 超量直接抛错;前端按 max-1 限制选人
658 662 int max = imProperties.getRtc().getGroupMaxParticipants();
659 663 if (initial.size() + 1 > max) {
660   - return initial.stream().limit(max - 1L)
661   - .collect(Collectors.toCollection(LinkedHashSet::new));
  664 + throw exception(RTC_GROUP_INVITEE_OVER_LIMIT);
662 665 }
663 666 return initial;
664 667 }
... ... @@ -671,7 +674,7 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
671 674 if (!ImRtcCallStatusEnum.isCreated(call.getStatus())) {
672 675 return;
673 676 }
674   - if (Objects.equals(call.getInviterUserId(), acceptorId)) {
  677 + if (ObjUtil.equal(call.getInviterUserId(), acceptorId)) {
675 678 return;
676 679 }
677 680  
... ... @@ -745,8 +748,12 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
745 748 /**
746 749 * RTC_CALL(INVITE):走 webSocketService 直推到被邀请人;persistent=false 不入消息流
747 750 */
748   - private void pushCallInviteNotification(ImRtcCallDO call, AdminUserRespDTO inviter, Long inviteeId) {
749   - String token = signToken(inviteeId, inviter == null ? null : inviter.getNickname(), call.getRoom());
  751 + private void pushCallInviteNotification(ImRtcCallDO call, AdminUserRespDTO inviter,
  752 + Long inviteeId, AdminUserRespDTO invitee) {
  753 + String displayName = invitee != null
  754 + ? StrUtil.blankToDefault(invitee.getNickname(), String.valueOf(inviteeId))
  755 + : String.valueOf(inviteeId);
  756 + String token = signToken(inviteeId, displayName, call.getRoom());
750 757 ImRtcCallNotification payload = ImRtcCallNotification.ofInvite(
751 758 call, inviter, imProperties.getRtc().getLivekitUrl(), token);
752 759 webSocketService.sendPrivateMessageAsync(inviteeId, ImPrivateMessageDTO.ofRtcNotification(
... ... @@ -831,7 +838,7 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
831 838 // 私聊:发起人总是 sender;receiver = 通话另一方
832 839 ImRtcParticipantDO peer = CollUtil.findOne(
833 840 rtcParticipantMapper.selectListByRoom(call.getRoom()),
834   - p -> !Objects.equals(p.getUserId(), senderId));
  841 + p -> ObjUtil.notEqual(p.getUserId(), senderId));
835 842 Long receiverId = peer != null ? peer.getUserId() : senderId;
836 843 ImPrivateMessageSendDTO dto = new ImPrivateMessageSendDTO().setReceiverId(receiverId)
837 844 .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload);
... ...