import 'package:get/get.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'dart:convert'; import '../../im/im_manager.dart'; import '../../model/home/marriage_data.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import 'conversation_controller.dart'; class ChatController extends GetxController { final String userId; // 用户信息 final Rx userInfo = Rx(null); // 外部传入的用户数据模型(用于在获取不到环信用户信息时使用) MarriageData? _userData; // 外部传入的用户信息(用于在获取不到环信用户信息时使用) String? _externalNickName; String? _externalAvatarUrl; // 消息列表 final RxList messages = RxList([]); // 加载更多的游标 String? _cursor; // 视频发送状态 final RxBool isSendingVideo = RxBool(false); final RxString sendingStatus = RxString(''); ChatController({ required this.userId, MarriageData? userData, }) { // 如果构造函数传入了用户数据模型,保存并使用 if (userData != null) { _userData = userData; _externalNickName = userData.nickName; _externalAvatarUrl = userData.profilePhoto; } } /// 获取用户数据模型 MarriageData? get userData => _userData; @override void onInit() { super.onInit(); // 注册到 IMManager,以便接收消息时能通知到此 Controller IMManager.instance.registerChatController(this); // 初始化时获取用户信息和消息列表 fetchUserInfo(); fetchMessages(); // 如果有外部传入的用户信息,触发 UI 更新 if (_externalNickName != null || _externalAvatarUrl != null) { update(); } } @override void onClose() { // 注销 ChatController IMManager.instance.unregisterChatController(userId); super.onClose(); } /// 获取用户信息 Future fetchUserInfo() async { try { final EMUserInfo? info = await IMManager.instance.getUserInfo(userId); if (info != null) { userInfo.value = info; update(); // 通知 GetBuilder 更新 UI if (Get.isLogEnable) { Get.log('获取用户信息成功: ${info.nickName}'); } } else { // 如果没有获取到用户信息,但 userInfo 已经有值(从外部设置的),则不覆盖 if (userInfo.value == null) { if (Get.isLogEnable) { Get.log('未找到用户信息: $userId,使用外部传入的信息'); } // 即使没有从环信获取到用户信息,也触发 UI 更新,因为外部可能传入了信息 update(); } } } catch (e) { if (Get.isLogEnable) { Get.log('获取用户信息失败: $e,使用外部传入的信息'); } // 即使获取失败,也触发 UI 更新 update(); } } /// 从外部设置用户信息(用于从其他页面传递用户数据) void setUserInfoFromExternal({String? nickName, String? avatarUrl}) { try { // 保存外部传入的用户信息 _externalNickName = nickName; _externalAvatarUrl = avatarUrl; // 如果当前没有用户信息,尝试从服务器获取 if (userInfo.value == null) { fetchUserInfo().then((_) { // 如果获取后还是没有用户信息,触发 UI 更新以使用外部传入的信息 if (userInfo.value == null) { update(); } }); } // 触发 UI 更新,使外部传入的信息生效 update(); if (Get.isLogEnable) { Get.log('设置外部用户信息: nickName=$nickName, avatarUrl=$avatarUrl'); } } catch (e) { if (Get.isLogEnable) { Get.log('设置用户信息失败: $e'); } } } /// 获取用户昵称(优先使用环信用户信息,如果没有则使用外部传入的) String? get userNickName { return userInfo.value?.nickName ?? _externalNickName; } /// 获取用户头像(优先使用环信用户信息,如果没有则使用外部传入的) String? get userAvatarUrl { return userInfo.value?.avatarUrl ?? _externalAvatarUrl; } /// 发送消息 Future sendMessage(String content) async { EMMessage? tempMessage; try { // 先创建消息对象(即使发送失败也要显示在列表中) tempMessage = EMMessage.createTxtSendMessage( targetId: userId, content: content, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 尝试发送消息 final message = await IMManager.instance.sendTextMessage(content, userId); if (message != null) { // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { messages[index] = message; } update(); // 更新会话列表 _refreshConversationList(); // 如果消息状态仍然是 PROGRESS,说明 SDK 的状态更新是异步的 // 等待一段时间后再次检查并更新状态 if (message.status == MessageStatus.PROGRESS) { // 轮询检查消息状态,直到状态不再是 PROGRESS 或超过3秒 _checkMessageStatusUntilComplete(message.msgId, maxAttempts: 6); } return true; } else { // 发送失败,更新消息状态 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { // 等待一下让 SDK 更新状态为 FAIL Future.delayed(Duration(milliseconds: 300), () { update(); }); } update(); SmartDialog.showToast('消息发送失败,请点击重发'); return false; } } catch (e) { if (Get.isLogEnable) { Get.log('发送消息失败: $e'); } // 发送异常,更新消息状态 if (tempMessage != null) { final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { update(); } } SmartDialog.showToast('消息发送失败: $e'); return false; } } /// 发送图片消息 Future sendImageMessage(String imagePath) async { try { // 先创建消息对象(即使发送失败也要显示在列表中) final tempMessage = EMMessage.createImageSendMessage( targetId: userId, filePath: imagePath, sendOriginalImage: false, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 尝试发送消息 final message = await IMManager.instance.sendImageMessage( imagePath, userId, ); if (message != null) { // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId); if (index != -1) { messages[index] = message; } update(); // 更新会话列表 _refreshConversationList(); return true; } else { // 发送失败,消息状态会自动变为FAIL update(); SmartDialog.showToast('图片发送失败,请点击重发'); return false; } } catch (e) { if (Get.isLogEnable) { Get.log('发送图片消息失败: $e'); } SmartDialog.showToast('图片发送失败: $e'); return false; } } /// 发送语音消息 Future sendVoiceMessage(String filePath, int seconds) async { try { // 先创建消息对象(即使发送失败也要显示在列表中) final tempMessage = EMMessage.createVoiceSendMessage( targetId: userId, filePath: filePath, duration: seconds, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 尝试发送消息 final message = await IMManager.instance.sendVoiceMessage( filePath, userId, seconds, ); if (message != null) { // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId); if (index != -1) { messages[index] = message; } update(); // 更新会话列表 _refreshConversationList(); return true; } else { // 发送失败,消息状态会自动变为FAIL update(); SmartDialog.showToast('语音发送失败,请点击重发'); return false; } } catch (e) { if (Get.isLogEnable) { Get.log('发送语音消息失败: $e'); } SmartDialog.showToast('语音发送失败: $e'); return false; } } /// 发送视频消息 Future sendVideoMessage(String filePath, int duration) async { try { print('🎬 [ChatController] 准备发送视频消息'); print('视频路径: $filePath'); print('视频时长: $duration 秒'); // 先创建消息对象(即使发送失败也要显示在列表中) final tempMessage = EMMessage.createVideoSendMessage( targetId: userId, filePath: filePath, duration: duration, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 尝试发送消息(后台上传,消息状态会自动更新) final message = await IMManager.instance.sendVideoMessage( filePath, userId, duration, ); if (message != null) { print('✅ [ChatController] 视频消息发送成功'); // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId); if (index != -1) { messages[index] = message; } update(); // 更新会话列表 _refreshConversationList(); return true; } else { // 发送失败,消息状态会自动变为FAIL update(); SmartDialog.showToast('视频发送失败,请点击重发'); return false; } } catch (e) { if (Get.isLogEnable) { Get.log('发送视频消息失败: $e'); } SmartDialog.showToast('视频发送失败: $e'); return false; } } /// 获取消息列表 Future fetchMessages({bool loadMore = false}) async { try { // 如果加载更多但没有游标,说明已经加载完所有消息 if (loadMore && _cursor == null) { if (Get.isLogEnable) { Get.log('没有更多消息了'); } return; } final List fetchedMessages = await IMManager.instance .getMessages( userId, pageSize: 20, startMsgId: loadMore ? _cursor : null, ); // 过滤掉null消息 final List validMessages = fetchedMessages .whereType() .toList(); if (loadMore) { // 加载更多时,需要去重并添加到列表开头(更旧的消息) final existingMsgIds = messages.map((msg) => msg.msgId).toSet(); final newMessages = validMessages .where((msg) => !existingMsgIds.contains(msg.msgId)) .toList(); if (newMessages.isNotEmpty) { messages.insertAll(0, newMessages); // 更新游标为最旧的消息ID(列表开头) _cursor = newMessages.first.msgId; if (Get.isLogEnable) { Get.log('加载更多消息成功,新增: ${newMessages.length} 条,总数量: ${messages.length}'); } } else { // 没有新消息,说明已经加载完所有消息 _cursor = null; if (Get.isLogEnable) { Get.log('没有更多消息了'); } } } else { // 刷新时替换整个列表 messages.assignAll(validMessages); // 更新游标为最旧的消息ID(列表开头) if (validMessages.isNotEmpty) { _cursor = validMessages.first.msgId; } else { _cursor = null; } if (Get.isLogEnable) { Get.log('刷新消息成功,数量: ${validMessages.length}'); } } // 通知UI更新 update(); } catch (e) { if (Get.isLogEnable) { Get.log('获取消息失败: $e'); } } } /// 加载更多消息 Future loadMoreMessages() async { if (_cursor != null) { await fetchMessages(loadMore: true); } } /// 检查是否还有更多消息可以加载 bool hasMoreMessages() { return _cursor != null; } /// 添加接收到的消息 void addReceivedMessage(EMMessage message) { // 检查消息是否已存在(避免重复添加) if (!messages.any((msg) => msg.msgId == message.msgId)) { // 将新消息添加到列表末尾 messages.add(message); update(); // 更新会话列表 _refreshConversationList(); if (Get.isLogEnable) { Get.log('收到新消息并添加到列表: ${message.msgId}'); } } } /// 撤回消息 Future recallMessage(EMMessage message) async { try { final success = await IMManager.instance.recallMessage(message); if (success) { // 从消息列表中移除或更新消息 final index = messages.indexWhere((msg) => msg.msgId == message.msgId); if (index != -1) { // 可以保留消息但标记为已撤回,或者直接移除 // 这里选择移除消息 messages.removeAt(index); update(); } // 更新会话列表 _refreshConversationList(); SmartDialog.showToast('消息已撤回'); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('撤回消息失败: $e'); } SmartDialog.showToast('撤回消息失败'); return false; } } /// 删除消息 Future deleteMessage(EMMessage message, {bool deleteRemote = false}) async { try { final conversationId = message.conversationId ?? userId; final messageId = message.msgId; if (messageId.isEmpty) { if (Get.isLogEnable) { Get.log('消息ID为空,无法删除'); } return false; } final success = await IMManager.instance.deleteMessage(conversationId, messageId, deleteRemote: deleteRemote); if (success) { // 从消息列表中移除 final index = messages.indexWhere((msg) => msg.msgId == message.msgId); if (index != -1) { messages.removeAt(index); update(); } // 更新会话列表 _refreshConversationList(); SmartDialog.showToast('消息已删除'); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('删除消息失败: $e'); } SmartDialog.showToast('删除消息失败'); return false; } } /// 标记消息为已读 Future markMessageAsRead(String messageId) async { try { final success = await IMManager.instance.markMessageAsRead(userId, messageId); if (success) { // 更新消息的已读状态 final index = messages.indexWhere((msg) => msg.msgId == messageId); if (index != -1) { update(); } // 更新会话列表 _refreshConversationList(); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('标记消息为已读失败: $e'); } return false; } } /// 标记会话所有消息为已读 Future markAllMessagesAsRead() async { try { final success = await IMManager.instance.markAllMessagesAsRead(userId); if (success) { // 更新所有消息的已读状态 update(); // 更新会话列表 _refreshConversationList(); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('标记所有消息为已读失败: $e'); } return false; } } /// 处理消息撤回通知 void handleMessageRecalled(EMMessage message) { try { // 从消息列表中移除或更新消息 final index = messages.indexWhere((msg) => msg.msgId == message.msgId); if (index != -1) { messages.removeAt(index); update(); } // 更新会话列表 _refreshConversationList(); if (Get.isLogEnable) { Get.log('处理消息撤回: ${message.msgId}'); } } catch (e) { if (Get.isLogEnable) { Get.log('处理消息撤回失败: $e'); } } } /// 更新消息状态 void updateMessageStatus(EMMessage message, MessageStatus status) { try { final index = messages.indexWhere((msg) => msg.msgId == message.msgId); if (index != -1) { // 更新消息状态 update(); if (Get.isLogEnable) { Get.log('更新消息状态: ${message.msgId}, status=$status'); } } } catch (e) { if (Get.isLogEnable) { Get.log('更新消息状态失败: $e'); } } } /// 检查消息是否发送失败 bool isMessageFailed(EMMessage message) { return message.status == MessageStatus.FAIL; } /// 发送通话消息 /// [callType] 通话类型:'voice' 语音通话,'video' 视频通话 /// [callStatus] 通话状态:'calling' 通话中,'missed' 未接听,'cancelled' 已取消,'rejected' 已拒绝 /// [callDuration] 通话时长(秒),仅在 callStatus 为 'calling' 时有效 Future sendCallMessage({ required String callType, // 'voice' 或 'video' required String callStatus, // 'calling', 'missed', 'cancelled', 'rejected' int? callDuration, // 通话时长(秒) }) async { try { // 构建通话信息 final callInfoMap = { 'callType': callType, 'callStatus': callStatus, }; if (callDuration != null) { callInfoMap['callDuration'] = callDuration; } final callInfoJson = jsonEncode(callInfoMap); // 创建文本消息,内容格式为 [CALL:] + JSON final content = '[CALL:]$callInfoJson'; // 先创建消息对象(即使发送失败也要显示在列表中) final tempMessage = EMMessage.createTxtSendMessage( targetId: userId, content: content, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 创建消息并发送 final messageToSend = EMMessage.createTxtSendMessage( targetId: userId, content: content, ); // 直接发送消息 try { final sentMessage = await EMClient.getInstance.chatManager.sendMessage(messageToSend); // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId); if (index != -1) { messages[index] = sentMessage; } update(); // 更新会话列表 _refreshConversationList(); return true; } catch (e) { // 发送失败,消息状态会自动变为FAIL update(); if (Get.isLogEnable) { Get.log('发送通话消息失败: $e'); } SmartDialog.showToast('通话消息发送失败: $e'); return false; } } catch (e) { if (Get.isLogEnable) { Get.log('发送通话消息失败: $e'); } SmartDialog.showToast('通话消息发送失败: $e'); return false; } } /// 重发消息 Future resendMessage(EMMessage failedMessage) async { try { if (Get.isLogEnable) { Get.log('🔄 [ChatController] 开始重发消息: ${failedMessage.msgId}'); } // 调用IMManager的重发方法 final newMessage = await IMManager.instance.resendMessage(failedMessage); if (newMessage != null) { // 找到原消息并替换 final index = messages.indexWhere((msg) => msg.msgId == failedMessage.msgId); if (index != -1) { messages[index] = newMessage; update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] 消息重发成功'); } // 更新会话列表 _refreshConversationList(); SmartDialog.showToast('消息已重发'); return true; } } else { if (Get.isLogEnable) { Get.log('❌ [ChatController] 消息重发失败'); } SmartDialog.showToast('消息重发失败,请重试'); return false; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [ChatController] 重发消息异常: $e'); } SmartDialog.showToast('重发消息失败: $e'); return false; } } /// 刷新会话列表 void _refreshConversationList() { try { // 尝试获取 ConversationController 并刷新会话列表 if (Get.isRegistered()) { final conversationController = Get.find(); conversationController.refreshConversations(); } } catch (e) { // ConversationController 可能未注册,忽略错误 if (Get.isLogEnable) { Get.log('刷新会话列表失败: $e'); } } } /// 轮询检查消息状态,直到状态不再是 PROGRESS void _checkMessageStatusUntilComplete(String messageId, {int maxAttempts = 6, int attempt = 0}) { if (attempt >= maxAttempts) { // 超过最大尝试次数,停止检查 return; } Future.delayed(Duration(milliseconds: 500), () { final index = messages.indexWhere((msg) => msg.msgId == messageId); if (index != -1) { final message = messages[index]; if (message.status != MessageStatus.PROGRESS) { // 状态已更新,刷新UI update(); return; } else { // 状态仍然是 PROGRESS,继续检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: maxAttempts, attempt: attempt + 1); } } }); } }