From 1eda319ea09583b5e799cb1864e067bc8f00bb61 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 17 May 2026 22:26:07 +0800 Subject: [PATCH] ✨ feat(im): 通话事件接入会话列表预览(私聊补 START 入消息流);文案统一「语音通话」 --- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java | 7 ++++--- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java | 56 +++++++++++++++++++++++++++++++++++--------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallEndNotification.java | 4 ++-- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallStartNotification.java | 6 +++--- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java index 15e9b2b..fb37fac 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java @@ -101,7 +101,8 @@ public enum ImMessageTypeEnum implements ArrayValuable { /** * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流) * 对应自己的类:ImRtcCallStartNotification - * 场景:群通话开始;入 im_group_message + 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了{voice/video}通话」 + * 场景:通话开始;群聊入 im_group_message 全群广播,前端渲染聊天 tip「{inviterNickname} 发起了语音通话」; + * 私聊入 im_private_message 定向给被叫,仅用于会话列表预览展示「[语音通话]」(不渲染聊天 tip) *

* 与 RTC_CALL_END(1611) 两段式配对:START 一定先于 END 入库(START 在 invite 接口事务里、END 在 cancel/leave 接口事务里,自然按请求顺序串行) */ @@ -109,9 +110,9 @@ public enum ImMessageTypeEnum implements ArrayValuable { /** * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流) * 对应自己的类:ImRtcCallEndNotification - * 场景:通话结束;入 im_private_message / im_group_message;私聊渲染准气泡,群聊渲染 tip「{voice/video}通话已结束 [时长 X]」 + * 场景:通话结束;入 im_private_message / im_group_message;私聊渲染准气泡,群聊渲染 tip「语音通话已经结束」 *

- * 群通话两段式配对前导是 RTC_CALL_START(1610);私聊场景没有 START,单 END 准气泡承载完整生命周期 + * 与 RTC_CALL_START(1610) 两段式配对 */ RTC_CALL_END(1611, "通话结束", true, false), diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java index 2bf3cf3..d34667d 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java @@ -234,10 +234,8 @@ public class ImRtcCallServiceImpl implements ImRtcCallService { for (Long inviteeId : invitees) { pushCallInviteNotification(call, inviterUser, inviteeId, inviteeMap.get(inviteeId), invitees); } - // 3.2 群通话再向全群广播 RTC_CALL_START 入聊天流 - if (ImConversationTypeEnum.isGroup(reqVO.getConversationType())) { - pushCallStartNotification(call, inviterUser); - } + // 3.2 向消息流写入 RTC_CALL_START;群聊全群广播,私聊定向给被叫,作为会话列表预览的依据 + pushCallStartNotification(call, inviterUser, invitees); return call; } @@ -891,16 +889,18 @@ public class ImRtcCallServiceImpl implements ImRtcCallService { } /** - * RTC_CALL_START:仅群通话场景;走 imGroupMessageService.send,入 im_group_message 全群广播;与 RTC_CALL_END 两段式配对 + * RTC_CALL_START:群聊走 imGroupMessageService.send 全群广播;私聊走 imPrivateMessageService.send 定向给被叫 + *

+ * 私聊 peer 直接复用 createCall 解析好的 invitees,避免再查参与表;用于会话列表预览展示「[语音通话]」 * - * @param call 通话主表 - * @param inviter 发起人;可空 + * @param call 通话主表 + * @param inviter 发起人;可空 + * @param invitees 本次邀请池;私聊场景取首个作为 peer */ - private void pushCallStartNotification(ImRtcCallDO call, AdminUserRespDTO inviter) { + private void pushCallStartNotification(ImRtcCallDO call, AdminUserRespDTO inviter, Set invitees) { ImRtcCallStartNotification payload = ImRtcCallStartNotification.of(call, inviter); - ImGroupMessageSendDTO dto = new ImGroupMessageSendDTO().setGroupId(call.getGroupId()) - .setType(ImMessageTypeEnum.RTC_CALL_START.getType()).setContent(payload); - groupMessageService.sendGroupMessage(call.getInviterUserId(), dto); + Long peerUserId = ImConversationTypeEnum.isGroup(call.getConversationType()) ? null : CollUtil.getFirst(invitees); + pushCallChatMessage(call, ImMessageTypeEnum.RTC_CALL_START, payload, peerUserId); } /** @@ -917,23 +917,37 @@ public class ImRtcCallServiceImpl implements ImRtcCallService { Long durationSeconds) { AdminUserRespDTO operator = operatorId != null ? adminUserApi.getUser(operatorId) : null; ImRtcCallEndNotification payload = ImRtcCallEndNotification.of(call, reason, durationSeconds, operatorId, operator); - Long senderId = call.getInviterUserId(); + Long peerUserId = null; + if (!ImConversationTypeEnum.isGroup(call.getConversationType())) { + ImRtcParticipantDO peer = CollUtil.findOne( + rtcParticipantMapper.selectListByRoom(call.getRoom()), + p -> ObjUtil.notEqual(p.getUserId(), call.getInviterUserId())); + peerUserId = peer != null ? peer.getUserId() : null; + } + pushCallChatMessage(call, ImMessageTypeEnum.RTC_CALL_END, payload, peerUserId); + } - // 群聊:发起人总是 sender;receiver = 全群;走 imGroupMessageService.send 全群广播 + /** + * RTC 通话事件入消息流:群聊走 groupMessageService 全群广播,私聊走 privateMessageService 定向给 peer + *

+ * senderId 固定取 inviterUserId,让前端按「谁发起」决定气泡左右;私聊 peer 缺失时降级为发给自己作兜底 + * + * @param call 通话主表 + * @param type 消息类型;RTC_CALL_START / RTC_CALL_END + * @param payload 推送 payload + * @param peerUserId 私聊对端用户编号;群聊忽略,私聊缺失时回退为 senderId + */ + private void pushCallChatMessage(ImRtcCallDO call, ImMessageTypeEnum type, Object payload, Long peerUserId) { + Long senderId = call.getInviterUserId(); if (ImConversationTypeEnum.isGroup(call.getConversationType())) { ImGroupMessageSendDTO dto = new ImGroupMessageSendDTO().setGroupId(call.getGroupId()) - .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload); + .setType(type.getType()).setContent(payload); groupMessageService.sendGroupMessage(senderId, dto); return; } - - // 私聊:发起人总是 sender;receiver = 通话另一方 - ImRtcParticipantDO peer = CollUtil.findOne( - rtcParticipantMapper.selectListByRoom(call.getRoom()), - p -> ObjUtil.notEqual(p.getUserId(), senderId)); - Long receiverId = peer != null ? peer.getUserId() : senderId; + Long receiverId = peerUserId != null ? peerUserId : senderId; ImPrivateMessageSendDTO dto = new ImPrivateMessageSendDTO().setReceiverId(receiverId) - .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload); + .setType(type.getType()).setContent(payload); privateMessageService.sendPrivateMessage(senderId, dto); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallEndNotification.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallEndNotification.java index 9676ac5..f0864e1 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallEndNotification.java +++ b/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; * 入 im_private_message / im_group_message;接收方关闭通话窗 + 渲染聊天 tip *

* 文案分场景: - * 群聊:「{voice/video}通话已结束 [时长 X]」;发起人信息走配对的 {@link ImRtcCallStartNotification} + * 群聊:「语音通话已经结束」;发起人信息走配对的 {@link ImRtcCallStartNotification} * 私聊:仿微信准气泡,按 endReason × selfSend 视角转换文案(HANGUP / CANCEL / REJECT / BUSY / ERROR) *

- * 与 {@link ImRtcCallStartNotification} 两段式配对(仅群通话):START 在 invite 事务里 INSERT,END 在 cancel / leave 事务里 INSERT; + * 与 {@link ImRtcCallStartNotification} 两段式配对:START 在 invite 事务里 INSERT,END 在 cancel / leave 事务里 INSERT; * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序 * * @author 芋道源码 diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallStartNotification.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallStartNotification.java index 965a8a2..b73346b 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/rtc/ImRtcCallStartNotification.java +++ b/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; /** * RTC_CALL_START 通话开始通知 *

- * 仅群通话场景;入 im_group_message + 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了{voice/video}通话」 + * 群聊入 im_group_message 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了语音通话」 + *

+ * 私聊入 im_private_message 定向给被叫;前端不渲染聊天 tip,仅用于会话列表预览展示「[语音通话]」(刷新后仍可见) *

* 与 {@link ImRtcCallEndNotification} 两段式配对: * START 在 invite 接口事务里 INSERT,END 在 cancel / leave 接口事务里 INSERT, * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序;后续如果合并到同一事务里 push,需要额外保证 START 先于 END - *

- * 私聊场景没有 START(单 END 准气泡足够),本通知不会用于 conversationType=PRIVATE * * @author 芋道源码 */ -- libgit2 0.21.4