Commit 1eda319ea09583b5e799cb1864e067bc8f00bb61
1 parent
dc081cfd
✨ feat(im): 通话事件接入会话列表预览(私聊补 START 入消息流);文案统一「语音通话」
Showing
4 changed files
with
44 additions
and
29 deletions
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,7 +101,8 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> { | ||
| 101 | /** | 101 | /** |
| 102 | * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流) | 102 | * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流) |
| 103 | * 对应自己的类:ImRtcCallStartNotification | 103 | * 对应自己的类:ImRtcCallStartNotification |
| 104 | - * 场景:群通话开始;入 im_group_message + 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了{voice/video}通话」 | 104 | + * 场景:通话开始;群聊入 im_group_message 全群广播,前端渲染聊天 tip「{inviterNickname} 发起了语音通话」; |
| 105 | + * 私聊入 im_private_message 定向给被叫,仅用于会话列表预览展示「[语音通话]」(不渲染聊天 tip) | ||
| 105 | * <p> | 106 | * <p> |
| 106 | * 与 RTC_CALL_END(1611) 两段式配对:START 一定先于 END 入库(START 在 invite 接口事务里、END 在 cancel/leave 接口事务里,自然按请求顺序串行) | 107 | * 与 RTC_CALL_END(1611) 两段式配对:START 一定先于 END 入库(START 在 invite 接口事务里、END 在 cancel/leave 接口事务里,自然按请求顺序串行) |
| 107 | */ | 108 | */ |
| @@ -109,9 +110,9 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> { | @@ -109,9 +110,9 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> { | ||
| 109 | /** | 110 | /** |
| 110 | * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流) | 111 | * 对应 OpenIM:无(自有扩展,OpenIM 通话事件不入消息流) |
| 111 | * 对应自己的类:ImRtcCallEndNotification | 112 | * 对应自己的类:ImRtcCallEndNotification |
| 112 | - * 场景:通话结束;入 im_private_message / im_group_message;私聊渲染准气泡,群聊渲染 tip「{voice/video}通话已结束 [时长 X]」 | 113 | + * 场景:通话结束;入 im_private_message / im_group_message;私聊渲染准气泡,群聊渲染 tip「语音通话已经结束」 |
| 113 | * <p> | 114 | * <p> |
| 114 | - * 群通话两段式配对前导是 RTC_CALL_START(1610);私聊场景没有 START,单 END 准气泡承载完整生命周期 | 115 | + * 与 RTC_CALL_START(1610) 两段式配对 |
| 115 | */ | 116 | */ |
| 116 | RTC_CALL_END(1611, "通话结束", true, false), | 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,10 +234,8 @@ public class ImRtcCallServiceImpl implements ImRtcCallService { | ||
| 234 | for (Long inviteeId : invitees) { | 234 | for (Long inviteeId : invitees) { |
| 235 | pushCallInviteNotification(call, inviterUser, inviteeId, inviteeMap.get(inviteeId), invitees); | 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 | return call; | 239 | return call; |
| 242 | } | 240 | } |
| 243 | 241 | ||
| @@ -891,16 +889,18 @@ public class ImRtcCallServiceImpl implements ImRtcCallService { | @@ -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 | ImRtcCallStartNotification payload = ImRtcCallStartNotification.of(call, inviter); | 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,23 +917,37 @@ public class ImRtcCallServiceImpl implements ImRtcCallService { | ||
| 917 | Long durationSeconds) { | 917 | Long durationSeconds) { |
| 918 | AdminUserRespDTO operator = operatorId != null ? adminUserApi.getUser(operatorId) : null; | 918 | AdminUserRespDTO operator = operatorId != null ? adminUserApi.getUser(operatorId) : null; |
| 919 | ImRtcCallEndNotification payload = ImRtcCallEndNotification.of(call, reason, durationSeconds, operatorId, operator); | 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 | if (ImConversationTypeEnum.isGroup(call.getConversationType())) { | 942 | if (ImConversationTypeEnum.isGroup(call.getConversationType())) { |
| 924 | ImGroupMessageSendDTO dto = new ImGroupMessageSendDTO().setGroupId(call.getGroupId()) | 943 | ImGroupMessageSendDTO dto = new ImGroupMessageSendDTO().setGroupId(call.getGroupId()) |
| 925 | - .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload); | 944 | + .setType(type.getType()).setContent(payload); |
| 926 | groupMessageService.sendGroupMessage(senderId, dto); | 945 | groupMessageService.sendGroupMessage(senderId, dto); |
| 927 | return; | 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 | ImPrivateMessageSendDTO dto = new ImPrivateMessageSendDTO().setReceiverId(receiverId) | 949 | ImPrivateMessageSendDTO dto = new ImPrivateMessageSendDTO().setReceiverId(receiverId) |
| 936 | - .setType(ImMessageTypeEnum.RTC_CALL_END.getType()).setContent(payload); | 950 | + .setType(type.getType()).setContent(payload); |
| 937 | privateMessageService.sendPrivateMessage(senderId, dto); | 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,10 +11,10 @@ import lombok.Data; | ||
| 11 | * 入 im_private_message / im_group_message;接收方关闭通话窗 + 渲染聊天 tip | 11 | * 入 im_private_message / im_group_message;接收方关闭通话窗 + 渲染聊天 tip |
| 12 | * <p> | 12 | * <p> |
| 13 | * 文案分场景: | 13 | * 文案分场景: |
| 14 | - * 群聊:「{voice/video}通话已结束 [时长 X]」;发起人信息走配对的 {@link ImRtcCallStartNotification} | 14 | + * 群聊:「语音通话已经结束」;发起人信息走配对的 {@link ImRtcCallStartNotification} |
| 15 | * 私聊:仿微信准气泡,按 endReason × selfSend 视角转换文案(HANGUP / CANCEL / REJECT / BUSY / ERROR) | 15 | * 私聊:仿微信准气泡,按 endReason × selfSend 视角转换文案(HANGUP / CANCEL / REJECT / BUSY / ERROR) |
| 16 | * <p> | 16 | * <p> |
| 17 | - * 与 {@link ImRtcCallStartNotification} 两段式配对(仅群通话):START 在 invite 事务里 INSERT,END 在 cancel / leave 事务里 INSERT; | 17 | + * 与 {@link ImRtcCallStartNotification} 两段式配对:START 在 invite 事务里 INSERT,END 在 cancel / leave 事务里 INSERT; |
| 18 | * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序 | 18 | * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序 |
| 19 | * | 19 | * |
| 20 | * @author 芋道源码 | 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,13 +7,13 @@ import lombok.Data; | ||
| 7 | /** | 7 | /** |
| 8 | * RTC_CALL_START 通话开始通知 | 8 | * RTC_CALL_START 通话开始通知 |
| 9 | * <p> | 9 | * <p> |
| 10 | - * 仅群通话场景;入 im_group_message + 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了{voice/video}通话」 | 10 | + * 群聊入 im_group_message 全群广播;前端渲染聊天 tip「{inviterNickname} 发起了语音通话」 |
| 11 | + * <p> | ||
| 12 | + * 私聊入 im_private_message 定向给被叫;前端不渲染聊天 tip,仅用于会话列表预览展示「[语音通话]」(刷新后仍可见) | ||
| 11 | * <p> | 13 | * <p> |
| 12 | * 与 {@link ImRtcCallEndNotification} 两段式配对: | 14 | * 与 {@link ImRtcCallEndNotification} 两段式配对: |
| 13 | * START 在 invite 接口事务里 INSERT,END 在 cancel / leave 接口事务里 INSERT, | 15 | * START 在 invite 接口事务里 INSERT,END 在 cancel / leave 接口事务里 INSERT, |
| 14 | * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序;后续如果合并到同一事务里 push,需要额外保证 START 先于 END | 16 | * 两段位于不同请求 / 事务,自增 id 保证聊天流顺序;后续如果合并到同一事务里 push,需要额外保证 START 先于 END |
| 15 | - * <p> | ||
| 16 | - * 私聊场景没有 START(单 END 准气泡足够),本通知不会用于 conversationType=PRIVATE | ||
| 17 | * | 17 | * |
| 18 | * @author 芋道源码 | 18 | * @author 芋道源码 |
| 19 | */ | 19 | */ |