Commit 1eda319ea09583b5e799cb1864e067bc8f00bb61

Authored by YunaiV
1 parent dc081cfd

✨ feat(im): 通话事件接入会话列表预览(私聊补 START 入消息流);文案统一「语音通话」

yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java
... ... @@ -101,7 +101,8 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> {
101 101 /**
102 102 * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流)
103 103 * 对应自己的类:ImRtcCallStartNotification
104   - * 场景:群通话开始;入 im_group_message + 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了{voice/video}通话」
  104 + * 场景:通话开始;群聊入 im_group_message 全群广播,前端渲染聊天 tip「{inviterNickname} 发起了语音通话」;
  105 + * 私聊入 im_private_message 定向给被叫,仅用于会话列表预览展示「[语音通话]」(不渲染聊天 tip)
105 106 * <p>
106 107 * 与 RTC_CALL_END(1611) 两段式配对:START 一定先于 END 入库(START 在 invite 接口事务里、END 在 cancel/leave 接口事务里,自然按请求顺序串行)
107 108 */
... ... @@ -109,9 +110,9 @@ public enum ImMessageTypeEnum implements ArrayValuable&lt;Integer&gt; {
109 110 /**
110 111 * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流)
111 112 * 对应自己的类:ImRtcCallEndNotification
112   - * 场景:通话结束;入 im_private_message / im_group_message;私聊渲染准气泡,群聊渲染 tip「{voice/video}通话已结束 [时长 X]」
  113 + * 场景:通话结束;入 im_private_message / im_group_message;私聊渲染准气泡,群聊渲染 tip「语音通话已经结束」
113 114 * <p>
114   - * 群通话两段式配对前导是 RTC_CALL_START(1610);私聊场景没有 START,单 END 准气泡承载完整生命周期
  115 + * 与 RTC_CALL_START(1610) 两段式配对
115 116 */
116 117 RTC_CALL_END(1611, "通话结束", true, false),
117 118  
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java
... ... @@ -234,10 +234,8 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
234 234 for (Long inviteeId : invitees) {
235 235 pushCallInviteNotification(call, inviterUser, inviteeId, inviteeMap.get(inviteeId), invitees);
236 236 }
237   - // 3.2 群通话再向全群广播 RTC_CALL_START 入聊天流
238   - if (ImConversationTypeEnum.isGroup(reqVO.getConversationType())) {
239   - pushCallStartNotification(call, inviterUser);
240   - }
  237 + // 3.2 向消息流写入 RTC_CALL_START;群聊全群广播,私聊定向给被叫,作为会话列表预览的依据
  238 + pushCallStartNotification(call, inviterUser, invitees);
241 239 return call;
242 240 }
243 241  
... ... @@ -891,16 +889,18 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
891 889 }
892 890  
893 891 /**
894   - * RTC_CALL_START:仅群通话场景;走 imGroupMessageService.send,入 im_group_message 全群广播;与 RTC_CALL_END 两段式配对
  892 + * RTC_CALL_START:群聊走 imGroupMessageService.send 全群广播;私聊走 imPrivateMessageService.send 定向给被叫
  893 + * <p>
  894 + * 私聊 peer 直接复用 createCall 解析好的 invitees,避免再查参与表;用于会话列表预览展示「[语音通话]」
895 895 *
896   - * @param call 通话主表
897   - * @param inviter 发起人;可空
  896 + * @param call 通话主表
  897 + * @param inviter 发起人;可空
  898 + * @param invitees 本次邀请池;私聊场景取首个作为 peer
898 899 */
899   - private void pushCallStartNotification(ImRtcCallDO call, AdminUserRespDTO inviter) {
  900 + private void pushCallStartNotification(ImRtcCallDO call, AdminUserRespDTO inviter, Set<Long> invitees) {
900 901 ImRtcCallStartNotification payload = ImRtcCallStartNotification.of(call, inviter);
901   - ImGroupMessageSendDTO dto = new ImGroupMessageSendDTO().setGroupId(call.getGroupId())
902   - .setType(ImMessageTypeEnum.RTC_CALL_START.getType()).setContent(payload);
903   - groupMessageService.sendGroupMessage(call.getInviterUserId(), dto);
  902 + Long peerUserId = ImConversationTypeEnum.isGroup(call.getConversationType()) ? null : CollUtil.getFirst(invitees);
  903 + pushCallChatMessage(call, ImMessageTypeEnum.RTC_CALL_START, payload, peerUserId);
904 904 }
905 905  
906 906 /**
... ... @@ -917,23 +917,37 @@ public class ImRtcCallServiceImpl implements ImRtcCallService {
917 917 Long durationSeconds) {
918 918 AdminUserRespDTO operator = operatorId != null ? adminUserApi.getUser(operatorId) : null;
919 919 ImRtcCallEndNotification payload = ImRtcCallEndNotification.of(call, reason, durationSeconds, operatorId, operator);
920   - Long senderId = call.getInviterUserId();
  920 + Long peerUserId = null;
  921 + if (!ImConversationTypeEnum.isGroup(call.getConversationType())) {
  922 + ImRtcParticipantDO peer = CollUtil.findOne(
  923 + rtcParticipantMapper.selectListByRoom(call.getRoom()),
  924 + p -> ObjUtil.notEqual(p.getUserId(), call.getInviterUserId()));
  925 + peerUserId = peer != null ? peer.getUserId() : null;
  926 + }
  927 + pushCallChatMessage(call, ImMessageTypeEnum.RTC_CALL_END, payload, peerUserId);
  928 + }
921 929  
922   - // 群聊:发起人总是 sender;receiver = 全群;走 imGroupMessageService.send 全群广播
  930 + /**
  931 + * RTC 通话事件入消息流:群聊走 groupMessageService 全群广播,私聊走 privateMessageService 定向给 peer
  932 + * <p>
  933 + * senderId 固定取 inviterUserId,让前端按「谁发起」决定气泡左右;私聊 peer 缺失时降级为发给自己作兜底
  934 + *
  935 + * @param call 通话主表
  936 + * @param type 消息类型;RTC_CALL_START / RTC_CALL_END
  937 + * @param payload 推送 payload
  938 + * @param peerUserId 私聊对端用户编号;群聊忽略,私聊缺失时回退为 senderId
  939 + */
  940 + private void pushCallChatMessage(ImRtcCallDO call, ImMessageTypeEnum type, Object payload, Long peerUserId) {
  941 + Long senderId = call.getInviterUserId();
923 942 if (ImConversationTypeEnum.isGroup(call.getConversationType())) {
924 943 ImGroupMessageSendDTO dto = new ImGroupMessageSendDTO().setGroupId(call.getGroupId())
925   - .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload);
  944 + .setType(type.getType()).setContent(payload);
926 945 groupMessageService.sendGroupMessage(senderId, dto);
927 946 return;
928 947 }
929   -
930   - // 私聊:发起人总是 sender;receiver = 通话另一方
931   - ImRtcParticipantDO peer = CollUtil.findOne(
932   - rtcParticipantMapper.selectListByRoom(call.getRoom()),
933   - p -> ObjUtil.notEqual(p.getUserId(), senderId));
934   - Long receiverId = peer != null ? peer.getUserId() : senderId;
  948 + Long receiverId = peerUserId != null ? peerUserId : senderId;
935 949 ImPrivateMessageSendDTO dto = new ImPrivateMessageSendDTO().setReceiverId(receiverId)
936   - .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload);
  950 + .setType(type.getType()).setContent(payload);
937 951 privateMessageService.sendPrivateMessage(senderId, dto);
938 952 }
939 953  
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallEndNotification.java
... ... @@ -11,10 +11,10 @@ import lombok.Data;
11 11 * 入 im_private_message / im_group_message;接收方关闭通话窗 + 渲染聊天 tip
12 12 * <p>
13 13 * 文案分场景:
14   - * 群聊:「{voice/video}通话已结束 [时长 X]」;发起人信息走配对的 {@link ImRtcCallStartNotification}
  14 + * 群聊:「语音通话已经结束」;发起人信息走配对的 {@link ImRtcCallStartNotification}
15 15 * 私聊:仿微信准气泡,按 endReason × selfSend 视角转换文案(HANGUP / CANCEL / REJECT / BUSY / ERROR)
16 16 * <p>
17   - * 与 {@link ImRtcCallStartNotification} 两段式配对(仅群通话):START 在 invite 事务里 INSERT,END 在 cancel / leave 事务里 INSERT;
  17 + * 与 {@link ImRtcCallStartNotification} 两段式配对:START 在 invite 事务里 INSERT,END 在 cancel / leave 事务里 INSERT;
18 18 * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序
19 19 *
20 20 * @author 芋道源码
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallStartNotification.java
... ... @@ -7,13 +7,13 @@ import lombok.Data;
7 7 /**
8 8 * RTC_CALL_START 通话开始通知
9 9 * <p>
10   - * 仅群通话场景;入 im_group_message + 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了{voice/video}通话」
  10 + * 群聊入 im_group_message 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了语音通话」
  11 + * <p>
  12 + * 私聊入 im_private_message 定向给被叫;前端不渲染聊天 tip,仅用于会话列表预览展示「[语音通话]」(刷新后仍可见)
11 13 * <p>
12 14 * 与 {@link ImRtcCallEndNotification} 两段式配对:
13 15 * START 在 invite 接口事务里 INSERT,END 在 cancel / leave 接口事务里 INSERT,
14 16 * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序;后续如果合并到同一事务里 push,需要额外保证 START 先于 END
15   - * <p>
16   - * 私聊场景没有 START(单 END 准气泡足够),本通知不会用于 conversationType=PRIVATE
17 17 *
18 18 * @author 芋道源码
19 19 */
... ...