From b1cb74f23cbb0456c75a56d90440dd3e951e1de4 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Tue, 30 Dec 2025 21:29:30 +0800 Subject: [PATCH] =?UTF-8?q?refactor(call):=20=E9=87=8D=E6=9E=84=E9=80=9A?= =?UTF-8?q?=E8=AF=9D=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=A7=86=E9=A2=91=E9=80=9A=E8=AF=9D=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正音频和视频通话类型参数,音频改为type:2,视频改为type:3 - 在通话信息中添加uid字段并传递给通话控制器 - 移除通话状态更新相关代码,不再修改消息状态 - 从IMManager中移除modifyMessage方法实现 - 重构视频通话页面背景构建逻辑,优化远端用户UID监听机制 - 添加key确保remoteUid变化时视频视图正确重建 --- lib/controller/message/call_controller.dart | 131 +++----------------- lib/controller/message/chat_controller.dart | 2 + lib/im/im_manager.dart | 92 -------------- lib/pages/message/video_call_page.dart | 116 ++++++++--------- 4 files changed, 75 insertions(+), 266 deletions(-) diff --git a/lib/controller/message/call_controller.dart b/lib/controller/message/call_controller.dart index 51be975..f2c47f7 100644 --- a/lib/controller/message/call_controller.dart +++ b/lib/controller/message/call_controller.dart @@ -205,12 +205,12 @@ class CallController extends GetxController { /// 创建音频通话频道 Future createAudioChannel() { - return createOneOnOneRtcChannel(type: 1); + return createOneOnOneRtcChannel(type: 2); } /// 创建视频通话频道 Future createVideoChannel() { - return createOneOnOneRtcChannel(type: 2); + return createOneOnOneRtcChannel(type: 3); } /// 发起通话 @@ -266,6 +266,7 @@ class CallController extends GetxController { callStatus: 'waitCalling', // 初始状态为未接听,等待对方响应 channelId: channelData.channelId, // 传递频道ID chatController: chatController, + uid: channelData.uid, ); startCallAudio(); @@ -339,15 +340,7 @@ class CallController extends GetxController { // 开始计时 _startCallTimer(); - // 更新通话消息状态为通话中 - await _updateCallMessageStatus( - message: message, - callStatus: 'calling', - callDuration: 0, - chatController: chatController, - ); - - // 从通话信息中获取 channelId + // 从通话信息中获取 channelId 和 uid final channelId = callInfo['channelId'] as String?; if (channelId == null || channelId.isEmpty) { print('❌ [CallController] channelId 为空,无法加入 RTC 频道'); @@ -373,6 +366,13 @@ class CallController extends GetxController { await joinChannel(channelId); print('✅ [CallController] 已加入 RTC 频道: $channelId'); + // 加入 RTC 频道后,从消息中获取发起方的 uid,设置为远端用户 UID + final initiatorUid = callInfo['uid'] as int?; + if (initiatorUid != null) { + remoteUid.value = initiatorUid; + print('📞 [CallController] 从消息中获取到发起方 UID: $initiatorUid,已设置 remoteUid'); + } + return true; } @@ -388,13 +388,6 @@ class CallController extends GetxController { // 停止播放来电铃声(已拒绝) stopCallAudio(); - // 更新通话消息状态为已拒绝 - await _updateCallMessageStatus( - message: message, - callStatus: 'rejected', - chatController: chatController, - ); - // 清理通话会话 currentCall.value = null; @@ -413,15 +406,6 @@ class CallController extends GetxController { }) async { print('📞 [CallController] 取消通话'); - // 如果有消息,更新通话消息状态为已取消 - if (message != null) { - await _updateCallMessageStatus( - message: message, - callStatus: 'cancelled', - chatController: chatController, - ); - } - // 停止播放来电铃声(已取消) stopCallAudio(); @@ -453,16 +437,6 @@ class CallController extends GetxController { // 停止计时 _stopCallTimer(); - // 如果有消息,更新通话消息状态为通话中(显示时长) - if (message != null) { - await _updateCallMessageStatus( - message: message, - callStatus: 'calling', - callDuration: callDuration, - chatController: chatController, - ); - } - // 清理通话会话和远端用户UID currentCall.value = null; remoteUid.value = null; @@ -502,6 +476,7 @@ class CallController extends GetxController { required String callType, required String callStatus, int? callDuration, + int? uid, String? channelId, ChatController? chatController, }) async { @@ -511,89 +486,10 @@ class CallController extends GetxController { callStatus: callStatus, callDuration: callDuration, channelId: channelId, + uid: uid, ); } - /// 更新通话消息状态(使用modifyMessage修改现有消息) - Future _updateCallMessageStatus({ - required EMMessage message, - required String callStatus, - int? callDuration, - ChatController? chatController, - }) async { - // 解析现有通话信息 - final callInfo = _parseCallInfo(message); - if (callInfo == null) { - return false; - } - - final callType = callInfo['callType'] as String? ?? 'voice'; - final messageId = message.msgId; - - if (messageId.isEmpty) { - print('❌ [CallController] 消息ID为空,无法修改消息'); - return false; - } - - // 如果是自定义消息,使用modifyMessage修改 - if (message.body.type == MessageType.CUSTOM) { - // 构建新的参数 - final callParams = { - 'callType': callType, - 'callStatus': callStatus, - }; - if (callDuration != null) { - callParams['callDuration'] = callDuration.toString(); - } - - // 创建新的消息体 - final customBody = EMCustomMessageBody(event: 'call', params: callParams); - - // 使用modifyMessage修改消息 - final success = await IMManager.instance.modifyMessage( - messageId: messageId, - msgBody: customBody, - attributes: null, // 不修改扩展属性 - ); - - if (success) { - print( - '✅ [CallController] 消息修改成功: messageId=$messageId, callStatus=$callStatus', - ); - - // 如果提供了chatController,更新本地消息列表 - if (chatController != null) { - // 更新消息体中的参数 - final index = chatController.messages.indexWhere( - (msg) => msg.msgId == messageId, - ); - if (index != -1) { - final updatedMessage = chatController.messages[index]; - if (updatedMessage.body.type == MessageType.CUSTOM) { - final customBody = updatedMessage.body as EMCustomMessageBody; - // 创建新的参数Map并更新 - final updatedParams = Map.from( - customBody.params ?? {}, - ); - updatedParams['callType'] = callType; - updatedParams['callStatus'] = callStatus; - if (callDuration != null) { - updatedParams['callDuration'] = callDuration.toString(); - } - // 注意:EMCustomMessageBody的params可能是只读的,这里可能需要重新创建消息 - // 暂时先通知UI更新,实际的消息体更新会在收到onMessageContentChanged回调时处理 - chatController.update(); - } - } - } - } - - return success; - } - // 如果不是自定义消息,返回失败 - return false; - } - /// 从自定义消息中解析通话信息 Map? _parseCallInfo(EMMessage message) { if (message.body.type == MessageType.CUSTOM) { @@ -607,6 +503,7 @@ class CallController extends GetxController { ? int.tryParse(params['callDuration']!) : null, 'channelId': params['channelId'], + 'uid': params['uid'] != null ? int.tryParse(params['uid']!) : null, }; } } diff --git a/lib/controller/message/chat_controller.dart b/lib/controller/message/chat_controller.dart index 9144ff7..601cbee 100644 --- a/lib/controller/message/chat_controller.dart +++ b/lib/controller/message/chat_controller.dart @@ -1059,12 +1059,14 @@ class ChatController extends GetxController { required String callStatus, // 'calling', 'missed', 'cancelled', 'rejected' int? callDuration, // 通话时长(秒) String? channelId, // RTC频道ID + int? uid }) async { try { // 构建通话消息参数(需要转换为 Map) final callParams = { 'callType': callType, 'callStatus': callStatus, + 'uid': uid.toString(), }; if (callDuration != null) { callParams['callDuration'] = callDuration.toString(); diff --git a/lib/im/im_manager.dart b/lib/im/im_manager.dart index e49ebfb..9ede731 100644 --- a/lib/im/im_manager.dart +++ b/lib/im/im_manager.dart @@ -1856,98 +1856,6 @@ class IMManager { } } - /// 修改消息(可同时修改消息体和消息扩展属性) - /// [messageId] 要修改的消息ID - /// [msgBody] 新的消息体(可选,如果为null则不修改消息体) - /// [attributes] 新的消息扩展属性(可选,如果为null则不修改扩展属性) - Future modifyMessage({ - required String messageId, - EMMessageBody? msgBody, - Map? attributes, - }) async { - try { - if (messageId.isEmpty) { - if (Get.isLogEnable) { - Get.log('❌ [IMManager] 消息ID为空,无法修改'); - } - return false; - } - - // 如果既没有提供消息体也没有提供扩展属性,则无法修改 - if (msgBody == null && attributes == null) { - if (Get.isLogEnable) { - Get.log('❌ [IMManager] 消息体和扩展属性都为空,无法修改'); - } - return false; - } - - // 调用SDK的修改消息方法 - await EMClient.getInstance.chatManager.modifyMessage( - messageId: messageId, - msgBody: msgBody, - attributes: attributes, - ); - - // 刷新会话列表 - _refreshConversationList(); - - // 通知对应的 ChatController 更新消息 - _notifyMessageModified(messageId, msgBody, attributes); - - return true; - } catch (e) { - if (Get.isLogEnable) { - Get.log('❌ [IMManager] 消息修改失败: messageId=$messageId, 错误: $e'); - } - return false; - } - } - - /// 通知 ChatController 消息已被修改 - void _notifyMessageModified( - String messageId, - EMMessageBody? msgBody, - Map? attributes, - ) { - try { - // 遍历所有活跃的 ChatController,查找包含该消息的控制器 - for (var entry in _activeChatControllers.entries) { - final controller = entry.value; - final index = controller.messages.indexWhere((msg) => msg.msgId == messageId); - if (index != -1) { - // 找到消息,更新它 - final message = controller.messages[index]; - - // 如果提供了新的消息体,更新消息体 - if (msgBody != null) { - // 注意:EMMessage 的 body 是只读的,需要通过重新获取消息来更新 - // 这里我们更新消息的扩展属性,消息体需要通过重新获取消息来更新 - if (Get.isLogEnable) { - Get.log('⚠️ [IMManager] 消息体修改需要重新获取消息: messageId=$messageId'); - } - } - - // 如果提供了新的扩展属性,更新扩展属性 - if (attributes != null) { - message.attributes ??= {}; - message.attributes!.addAll(attributes); - } - - // 通知UI更新 - controller.update(); - - if (Get.isLogEnable) { - Get.log('✅ [IMManager] 已通知ChatController更新消息: userId=${entry.key}, messageId=$messageId'); - } - } - } - } catch (e) { - if (Get.isLogEnable) { - Get.log('⚠️ [IMManager] 通知ChatController消息修改失败: $e'); - } - } - } - /// 撤回消息 Future recallMessage(EMMessage message) async { try { diff --git a/lib/pages/message/video_call_page.dart b/lib/pages/message/video_call_page.dart index 55c50fa..a382333 100644 --- a/lib/pages/message/video_call_page.dart +++ b/lib/pages/message/video_call_page.dart @@ -293,70 +293,72 @@ class _VideoCallPageState extends State { /// 构建背景 Widget _buildBackground() { - final callSession = _callController.currentCall.value; - final isVideoCall = callSession != null && callSession.callType == CallType.video; - - // 使用 Obx 监听远端用户 UID 的变化 - if (isVideoCall) { - return Obx(() { - // 同时监听 CallController.remoteUid 和 RTCManager.remoteUsersNotifier - var remoteUid = _callController.remoteUid.value; - final remoteUsers = _rtcManager.remoteUsersNotifier.value; // 触发 Obx 监听 - - print('📞 [VideoCallPage] Obx 重建,CallController.remoteUid: ${_callController.remoteUid.value}, remoteUsers: $remoteUsers, isVideoCall: $isVideoCall'); - - // 如果 remoteUid 为空,尝试从 RTCManager 的远端用户列表中获取 - if (remoteUid == null && remoteUsers.isNotEmpty) { - remoteUid = remoteUsers.first; - // 同步到 CallController - _callController.remoteUid.value = remoteUid; - print('📞 [VideoCallPage] 从 RTCManager.remoteUsersNotifier 获取到 remoteUid: $remoteUid'); - } - - // 如果远端用户已加入,显示远端视频视图(对方画面) - if (remoteUid != null) { - final engine = _rtcManager.engine; - print('📞 [VideoCallPage] remoteUid 不为 null: $remoteUid, engine: ${engine != null}'); - if (engine != null) { - print('📞 [VideoCallPage] 显示远端视频视图,UID:$remoteUid'); - final remoteVideoViewController = VideoViewController( - rtcEngine: engine, - canvas: VideoCanvas(uid: remoteUid), - ); - return SizedBox( - width: double.infinity, - height: 1.sh, - child: AgoraVideoView( - controller: remoteVideoViewController, - ), - ); - } else { - print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频'); - } - } else { - print('⚠️ [VideoCallPage] remoteUid 为 null,无法显示远端视频'); - } - - // 如果没有远端视频,显示本地视频视图(自己的画面) - if (_localVideoViewController != null) { - print('📞 [VideoCallPage] 显示本地视频视图'); + // 使用 Obx 监听通话状态和远端用户 UID 的变化 + return Obx(() { + // 在 Obx 中访问响应式变量,确保建立监听关系 + final callSession = _callController.currentCall.value; + final isVideoCall = callSession != null && callSession.callType == CallType.video; + final remoteUid = _callController.remoteUid.value; + final remoteUsers = _rtcManager.remoteUsersNotifier.value; + + print('📞 [VideoCallPage] _buildBackground Obx 重建,isVideoCall: $isVideoCall, remoteUid: $remoteUid, remoteUsers: $remoteUsers'); + + // 如果不是视频通话,显示模糊的头像背景 + if (!isVideoCall) { + return _buildAvatarBackground(); + } + + // 如果 remoteUid 为空,尝试从 RTCManager 的远端用户列表中获取 + if (remoteUid == null && remoteUsers.isNotEmpty) { + _callController.remoteUid.value = remoteUsers.first; + print('📞 [VideoCallPage] 从 RTCManager.remoteUsersNotifier 获取到 remoteUid: ${remoteUsers.first}'); + // Obx 会自动重建,所以这里不需要手动返回 + } + + // 再次获取 remoteUid(可能刚刚被设置) + final currentRemoteUid = _callController.remoteUid.value; + + // 如果远端用户已加入,显示远端视频视图(对方画面) + if (currentRemoteUid != null) { + final engine = _rtcManager.engine; + print('📞 [VideoCallPage] currentRemoteUid 不为 null: $currentRemoteUid, engine: ${engine != null}'); + if (engine != null) { + print('📞 [VideoCallPage] 显示远端视频视图,UID:$currentRemoteUid'); + final remoteVideoViewController = VideoViewController( + rtcEngine: engine, + canvas: VideoCanvas(uid: currentRemoteUid), + ); return SizedBox( width: double.infinity, height: 1.sh, + key: ValueKey('remote_video_$currentRemoteUid'), // 使用 key 确保 remoteUid 变化时重建 child: AgoraVideoView( - controller: _localVideoViewController!, + controller: remoteVideoViewController, ), ); + } else { + print('⚠️ [VideoCallPage] engine 为 null,无法显示远端视频'); } - - // 如果本地视频也没有,显示模糊的头像背景 - print('📞 [VideoCallPage] 显示头像背景'); - return _buildAvatarBackground(); - }); - } - - // 非视频通话,显示模糊的头像背景 - return _buildAvatarBackground(); + } else { + print('⚠️ [VideoCallPage] currentRemoteUid 为 null,无法显示远端视频'); + } + + // 如果没有远端视频,显示本地视频视图(自己的画面) + if (_localVideoViewController != null) { + print('📞 [VideoCallPage] 显示本地视频视图'); + return SizedBox( + width: double.infinity, + height: 1.sh, + child: AgoraVideoView( + controller: _localVideoViewController!, + ), + ); + } + + // 如果本地视频也没有,显示模糊的头像背景 + print('📞 [VideoCallPage] 显示头像背景'); + return _buildAvatarBackground(); + }); } /// 构建头像背景