import 'package:get/get.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter/material.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../im/im_manager.dart'; import '../../im/chat_presence_manager.dart'; import '../../model/home/marriage_data.dart'; import '../../model/live/gift_product_model.dart'; import '../../network/network_service.dart'; import '../../widget/live/live_recharge_popup.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import 'conversation_controller.dart'; import '../mine/rose_controller.dart'; class ChatController extends GetxController { final String userId; // 用户信息 final Rx userInfo = Rx(null); // 外部传入的用户数据模型(用于在获取不到环信用户信息时使用) MarriageData? _userData; // 外部传入的用户信息(用于在获取不到环信用户信息时使用) String? _externalNickName; String? _externalAvatarUrl; // 用户在线状态(从接口获取) final RxBool isUserOnline = RxBool(false); // 消息列表 final RxList messages = RxList([]); // 加载更多的游标 String? _cursor; // 视频发送状态 final RxBool isSendingVideo = RxBool(false); final RxString sendingStatus = RxString(''); var isDialogShowing = false.obs; // 语音发送状态(防止重复发送) final RxBool isSendingVoice = RxBool(false); // 礼物产品列表 final RxList giftProducts = [].obs; // 需要显示508错误提示的消息ID集合(临时状态,不持久化) final Set _roseErrorMessageIds = {}; // 需要显示敏感词错误提示的消息ID集合(临时状态,不持久化) final Set _sensitiveWordMessageIds = {}; // 网络服务 final NetworkService _networkService = NetworkService(); /// 检查消息是否需要显示508错误提示 bool shouldShowRoseError(String messageId) { return _roseErrorMessageIds.contains(messageId); } /// 添加需要显示508错误提示的消息ID void addRoseErrorMessageId(String messageId) { _roseErrorMessageIds.add(messageId); update(); } /// 检查消息是否需要显示敏感词错误提示 bool shouldShowSensitiveWordError(String messageId) { return _sensitiveWordMessageIds.contains(messageId); } /// 添加需要显示敏感词错误提示的消息ID void addSensitiveWordMessageId(String messageId) { _sensitiveWordMessageIds.add(messageId); update(); } 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); // 如果有外部传入的用户信息,先同步到 ConversationController 的缓存中 if (_userData != null) { _syncUserInfoToConversationController(); } else { // 如果没有外部传入的用户信息,尝试从 ConversationController 的缓存中获取 _loadUserInfoFromConversationCache(); } // 初始化时获取用户信息和消息列表 fetchUserInfo(); fetchMessages().then((_) { // 加载消息后,标记所有消息为已读 markAllMessagesAsRead(); }); // 加载礼物产品列表 loadGiftProducts(); // 订阅对方用户在线状态(实时接收状态变化) subscribeUserPresence(); // 如果有外部传入的用户信息,触发 UI 更新 if (_externalNickName != null || _externalAvatarUrl != null) { update(); } } void setDialogDismiss(bool flag){ isDialogShowing.value = flag; } /// 订阅用户在线状态(实时接收状态变化) Future subscribeUserPresence() async { try { await ChatPresenceManager().subscribeUserPresence( userId: userId, onStatusChanged: (isOnline) { // 状态变化回调 isUserOnline.value = isOnline; if (Get.isLogEnable) { Get.log('✅ [ChatController] 用户在线状态更新: userId=$userId, isOnline=$isOnline'); } update(); }, ); } catch (e) { if (Get.isLogEnable) { Get.log('❌ [ChatController] 订阅用户在线状态异常: $e'); } // 获取异常时,默认为离线状态 isUserOnline.value = false; update(); } } /// 取消订阅用户在线状态 Future unsubscribeUserPresence() async { try { await ChatPresenceManager().unsubscribeUserPresence(userId); } catch (e) { if (Get.isLogEnable) { Get.log('❌ [ChatController] 取消订阅用户在线状态异常: $e'); } } } /// 从 ConversationController 的缓存中加载用户信息 void _loadUserInfoFromConversationCache() { try { if (Get.isRegistered()) { final conversationController = Get.find(); // 尝试从缓存中获取用户信息 conversationController.loadContact(userId).then((extendedInfo) { if (extendedInfo != null && (extendedInfo.nickName != null || extendedInfo.avatarUrl != null)) { // 如果从缓存中获取到了用户信息,设置到外部信息中 _externalNickName = extendedInfo.nickName; _externalAvatarUrl = extendedInfo.avatarUrl; // 触发 UI 更新 update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] 从会话缓存加载用户信息: userId=$userId, nickName=${extendedInfo.nickName}'); } } }); } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 从会话缓存加载用户信息失败: $e'); } } } /// 将用户信息同步到 ConversationController 的缓存中 /// 注意:用户信息会通过消息扩展字段自动传递,这里只是提前缓存以确保聊天列表能立即显示 void _syncUserInfoToConversationController() { try { if (_userData == null) return; final nickName = _userData!.nickName; final avatarUrl = _userData!.profilePhoto.trim().replaceAll('`', ''); // 移除照片URL中的反引号 // 更新到 ConversationController 的缓存(确保聊天列表能立即显示) if (Get.isRegistered()) { final conversationController = Get.find(); // 创建 ExtendedUserInfo 并缓存 final extendedInfo = ExtendedUserInfo( userId: userId, nickName: nickName, avatarUrl: avatarUrl, ); // 直接更新缓存 conversationController.cacheUserInfo(userId, extendedInfo); if (Get.isLogEnable) { Get.log('✅ [ChatController] 已将用户信息同步到会话列表缓存: userId=$userId, nickName=$nickName'); Get.log('ℹ️ [ChatController] 用户信息将通过消息扩展字段自动传递给对方'); } } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 同步用户信息失败: $e'); } } } @override void onClose() { // 取消订阅用户在线状态 unsubscribeUserPresence(); // 注销 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 { final hxNickName = userInfo.value?.nickName; // 只有当环信用户信息的昵称不为空且不为空字符串时,才使用它 if (hxNickName != null && hxNickName.isNotEmpty) { return hxNickName; } // 否则回退到外部传入的昵称 return _externalNickName; } /// 获取用户头像(优先使用环信用户信息,如果没有或为空则使用外部传入的) String? get userAvatarUrl { final hxAvatarUrl = userInfo.value?.avatarUrl; // 只有当环信用户信息的头像不为空且不为空字符串时,才使用它 if (hxAvatarUrl != null && hxAvatarUrl.isNotEmpty) { return hxAvatarUrl; } // 否则回退到外部传入的头像 return _externalAvatarUrl; } /// 发送消息 Future sendMessage(String content) async { EMMessage? tempMessage; try { if (Get.isLogEnable) { Get.log('📤 [ChatController] 准备发送消息: content=$content, targetUserId=$userId'); } else { print('📤 [ChatController] 准备发送消息: content=$content, targetUserId=$userId'); } // 检查目标用户是否存在于IM系统中 final userExists = await IMManager.instance.checkUserExists(userId); if (!userExists) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 警告:目标用户可能不存在于IM系统中: $userId'); } else { print('⚠️ [ChatController] 警告:目标用户可能不存在于IM系统中: $userId'); } // 继续发送,但记录警告 } // 先创建消息对象(即使发送失败也要显示在列表中) tempMessage = EMMessage.createTxtSendMessage( targetId: userId, content: content, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 尝试发送消息 final message = await IMManager.instance.sendTextMessage(content, userId); if (message != null) { if (Get.isLogEnable) { Get.log('✅ [ChatController] SDK返回消息: msgId=${message.msgId}, status=${message.status}'); } else { print('✅ [ChatController] SDK返回消息: msgId=${message.msgId}, status=${message.status}'); } // 发送成功,替换临时消息 // 注意:SDK 可能会改变消息ID(onMessageIdChanged),所以需要根据原始消息ID或内容来匹配 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { if (Get.isLogEnable) { Get.log('✅ [ChatController] 找到临时消息,替换: index=$index, oldMsgId=${tempMessage.msgId}, newMsgId=${message.msgId}'); } else { print('✅ [ChatController] 找到临时消息,替换: index=$index, oldMsgId=${tempMessage.msgId}, newMsgId=${message.msgId}'); } messages[index] = message; } else { // 如果找不到原始消息(可能ID改变了),尝试根据内容匹配 final contentIndex = messages.indexWhere((msg) => msg.body.type == MessageType.TXT && (msg.body as EMTextMessageBody).content == content && msg.direction == MessageDirection.SEND ); if (contentIndex != -1) { if (Get.isLogEnable) { Get.log('✅ [ChatController] 根据内容找到消息,替换: index=$contentIndex'); } else { print('✅ [ChatController] 根据内容找到消息,替换: index=$contentIndex'); } messages[contentIndex] = message; } else { // 如果还是找不到,直接添加新消息 if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 未找到匹配的消息,添加新消息'); } else { print('⚠️ [ChatController] 未找到匹配的消息,添加新消息'); } messages.add(message); } } update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] UI已更新,消息状态: ${message.status}'); } else { print('✅ [ChatController] UI已更新,消息状态: ${message.status}'); } // 更新会话列表 _refreshConversationList(); // 如果消息状态仍然是 PROGRESS,说明 SDK 的状态更新是异步的 // 等待一段时间后再次检查并更新状态 // 注意:即使 sendMessage 返回成功,SDK 可能随后报错(如日志中的 custom internal error),所以需要持续检查状态 if (message.status == MessageStatus.PROGRESS) { if (Get.isLogEnable) { Get.log('⏳ [ChatController] 消息状态仍为PROGRESS,将延迟检查状态'); } else { print('⏳ [ChatController] 消息状态仍为PROGRESS,将延迟检查状态'); } // 延迟一小段时间后检查状态(给 SDK 时间更新状态) // 由于 SDK 可能在发送后报错,我们需要更频繁地检查状态 // 注意:SDK 可能会改变消息ID(onMessageIdChanged),所以需要从最新消息列表中查找 Future.delayed(Duration(milliseconds: 500), () { _checkMessageStatusAndUpdate(message.msgId); }); // 再延迟一段时间后再次检查(捕获可能的失败回调) Future.delayed(Duration(milliseconds: 1500), () { _checkMessageStatusAndUpdate(message.msgId); }); // 再延迟一段时间后最后一次检查 Future.delayed(Duration(milliseconds: 3000), () { _checkMessageStatusAndUpdate(message.msgId); }); } else if (message.status == MessageStatus.FAIL) { // 如果状态已经是 FAIL,立即更新UI update(); if (Get.isLogEnable) { Get.log('❌ [ChatController] 消息发送失败,状态: ${message.status}'); } else { print('❌ [ChatController] 消息发送失败,状态: ${message.status}'); } } else { // 状态不是 PROGRESS,说明已经更新完成 if (Get.isLogEnable) { Get.log('✅ [ChatController] 消息发送成功,状态: ${message.status}'); } else { print('✅ [ChatController] 消息发送成功,状态: ${message.status}'); } } return true; } else { // 发送失败,更新消息状态 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { // 注意:EMMessage对象的状态是只读的,需要通过SDK的onError回调来更新 // 这里先更新UI,等待onError回调更新实际的消息对象 update(); // 刷新会话列表,确保聊天列表能显示失败状态 _refreshConversationList(); if (Get.isLogEnable) { Get.log('❌ [ChatController] 消息发送失败,等待onError回调更新状态: msgId=${tempMessage.msgId}'); } } 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 { EMMessage? tempMessage; try { if (Get.isLogEnable) { Get.log('📤 [ChatController] 准备发送图片消息: path=$imagePath, targetUserId=$userId'); } else { print('📤 [ChatController] 准备发送图片消息: path=$imagePath, targetUserId=$userId'); } // 先创建消息对象(即使发送失败也要显示在列表中) tempMessage = EMMessage.createImageSendMessage( targetId: userId, filePath: imagePath, sendOriginalImage: false, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 尝试发送消息 final message = await IMManager.instance.sendImageMessage( imagePath, userId, ); if (message != null) { if (Get.isLogEnable) { Get.log('✅ [ChatController] SDK返回图片消息: msgId=${message.msgId}, status=${message.status}'); } else { print('✅ [ChatController] SDK返回图片消息: msgId=${message.msgId}, status=${message.status}'); } // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { messages[index] = message; } else { // 如果找不到原始消息(可能ID改变了),尝试根据内容匹配 final contentIndex = messages.indexWhere((msg) => msg.body.type == MessageType.IMAGE && msg.direction == MessageDirection.SEND ); if (contentIndex != -1) { messages[contentIndex] = message; } else { messages.add(message); } } update(); // 更新会话列表 _refreshConversationList(); // 如果消息状态仍然是 PROGRESS,延迟检查状态 if (message.status == MessageStatus.PROGRESS) { Future.delayed(Duration(milliseconds: 500), () { _checkMessageStatusAndUpdate(message.msgId); }); Future.delayed(Duration(milliseconds: 1500), () { _checkMessageStatusAndUpdate(message.msgId); }); Future.delayed(Duration(milliseconds: 3000), () { _checkMessageStatusAndUpdate(message.msgId); }); } return true; } else { // 发送失败,更新消息状态 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { 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 { // 防止重复发送 if (isSendingVoice.value) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 语音消息正在发送中,忽略重复发送请求'); } else { print('⚠️ [ChatController] 语音消息正在发送中,忽略重复发送请求'); } return false; } EMMessage? tempMessage; try { isSendingVoice.value = true; if (Get.isLogEnable) { Get.log('📤 [ChatController] 准备发送语音消息: path=$filePath, duration=$seconds, targetUserId=$userId'); } else { print('📤 [ChatController] 准备发送语音消息: path=$filePath, duration=$seconds, targetUserId=$userId'); } // 先创建消息对象(即使发送失败也要显示在列表中) 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) { if (Get.isLogEnable) { Get.log('✅ [ChatController] SDK返回语音消息: msgId=${message.msgId}, status=${message.status}'); } else { print('✅ [ChatController] SDK返回语音消息: msgId=${message.msgId}, status=${message.status}'); } // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { messages[index] = message; } else { // 如果找不到原始消息(可能ID改变了),尝试根据内容匹配 final contentIndex = messages.indexWhere((msg) => msg.body.type == MessageType.VOICE && msg.direction == MessageDirection.SEND ); if (contentIndex != -1) { messages[contentIndex] = message; } else { messages.add(message); } } update(); // 更新会话列表 _refreshConversationList(); // 如果消息状态仍然是 PROGRESS,延迟检查状态 if (message.status == MessageStatus.PROGRESS) { Future.delayed(Duration(milliseconds: 500), () { _checkMessageStatusAndUpdate(message.msgId); }); Future.delayed(Duration(milliseconds: 1500), () { _checkMessageStatusAndUpdate(message.msgId); }); Future.delayed(Duration(milliseconds: 3000), () { _checkMessageStatusAndUpdate(message.msgId); }); } isSendingVoice.value = false; return true; } else { // 发送失败,更新消息状态 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { update(); } isSendingVoice.value = false; SmartDialog.showToast('语音发送失败,请点击重发'); return false; } } catch (e) { isSendingVoice.value = false; if (Get.isLogEnable) { Get.log('发送语音消息失败: $e'); } SmartDialog.showToast('语音发送失败: $e'); return false; } } /// 发送视频消息 Future sendVideoMessage(String filePath, int duration) async { EMMessage? tempMessage; try { if (Get.isLogEnable) { Get.log('🎬 [ChatController] 准备发送视频消息: path=$filePath, duration=$duration, targetUserId=$userId'); } else { print('🎬 [ChatController] 准备发送视频消息: path=$filePath, duration=$duration, targetUserId=$userId'); } // 先创建消息对象(即使发送失败也要显示在列表中) 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) { if (Get.isLogEnable) { Get.log('✅ [ChatController] SDK返回视频消息: msgId=${message.msgId}, status=${message.status}'); } else { print('✅ [ChatController] SDK返回视频消息: msgId=${message.msgId}, status=${message.status}'); } // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { messages[index] = message; } else { // 如果找不到原始消息(可能ID改变了),尝试根据内容匹配 final contentIndex = messages.indexWhere((msg) => msg.body.type == MessageType.VIDEO && msg.direction == MessageDirection.SEND ); if (contentIndex != -1) { messages[contentIndex] = message; } else { messages.add(message); } } update(); // 更新会话列表 _refreshConversationList(); // 如果消息状态仍然是 PROGRESS,延迟检查状态 if (message.status == MessageStatus.PROGRESS) { Future.delayed(Duration(milliseconds: 500), () { _checkMessageStatusAndUpdate(message.msgId); }); Future.delayed(Duration(milliseconds: 1500), () { _checkMessageStatusAndUpdate(message.msgId); }); Future.delayed(Duration(milliseconds: 3000), () { _checkMessageStatusAndUpdate(message.msgId); }); } return true; } else { // 发送失败,更新消息状态 final index = messages.indexWhere((msg) => msg.msgId == tempMessage!.msgId); if (index != -1) { 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) { // 从新消息的 attributes 中恢复错误码状态 for (var msg in newMessages) { if (msg.status == MessageStatus.FAIL && msg.direction == MessageDirection.SEND) { try { final errorCode = msg.attributes?['errorCode'] as String?; if (errorCode == 'E0002') { _roseErrorMessageIds.add(msg.msgId); } else if (errorCode == 'E0001') { _sensitiveWordMessageIds.add(msg.msgId); } } catch (e) { // 忽略错误 } } } 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 { // 刷新时替换整个列表,但需要去重(处理重发消息的情况) // 对于相同内容的消息,只保留最新的(msgId更大的) // 重新进入页面时,清空临时错误提示状态,然后从消息 attributes 中恢复 _roseErrorMessageIds.clear(); _sensitiveWordMessageIds.clear(); final Map contentToMessage = {}; for (var msg in validMessages) { // 生成消息的唯一标识(基于内容和时间戳) String contentKey; if (msg.body.type == MessageType.TXT) { final textBody = msg.body as EMTextMessageBody; contentKey = '${msg.direction}_${msg.serverTime}_${textBody.content}'; } else { // 对于其他类型的消息,使用时间戳和类型作为key(处理重发时msgId改变的情况) contentKey = '${msg.direction}_${msg.serverTime}_${msg.body.type}'; } // 如果已存在相同内容的消息,比较msgId,保留更大的(更新的) if (contentToMessage.containsKey(contentKey)) { final existingMsg = contentToMessage[contentKey]!; // 比较msgId,保留更大的(通常更新的消息ID更大) if (msg.msgId.compareTo(existingMsg.msgId) > 0) { contentToMessage[contentKey] = msg; } } else { contentToMessage[contentKey] = msg; } } // 将去重后的消息列表转换为列表并排序 final deduplicatedMessages = contentToMessage.values.toList(); // 按时间戳排序(从旧到新) deduplicatedMessages.sort((a, b) => a.serverTime.compareTo(b.serverTime)); // 从消息 attributes 中恢复错误码状态 for (var msg in deduplicatedMessages) { if (msg.status == MessageStatus.FAIL && msg.direction == MessageDirection.SEND) { try { final errorCode = msg.attributes?['errorCode'] as String?; if (errorCode == 'E0002') { _roseErrorMessageIds.add(msg.msgId); } else if (errorCode == 'E0001') { _sensitiveWordMessageIds.add(msg.msgId); } } catch (e) { // 忽略错误 } } } messages.assignAll(deduplicatedMessages); // 更新游标为最旧的消息ID(列表开头) if (deduplicatedMessages.isNotEmpty) { _cursor = deduplicatedMessages.first.msgId; } else { _cursor = null; } if (Get.isLogEnable) { Get.log('刷新消息成功,去重前: ${validMessages.length} 条,去重后: ${deduplicatedMessages.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' 时有效 /// [channelId] RTC频道ID Future sendCallMessage({ required String callType, // 'voice' 或 'video' 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(); } if (channelId != null && channelId.isNotEmpty) { callParams['channelId'] = channelId; } // 先创建自定义消息对象(即使发送失败也要显示在列表中) final tempMessage = EMMessage.createCustomSendMessage( targetId: userId, event: 'call', params: callParams, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 发送自定义消息 try { final message = await IMManager.instance.sendCustomMessage(userId, 'call', callParams); 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(); if (Get.isLogEnable) { Get.log('发送通话消息失败: message为null'); } SmartDialog.showToast('通话消息发送失败'); return false; } } 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}'); } // 找到原消息的索引 final oldIndex = messages.indexWhere((msg) => msg.msgId == failedMessage.msgId); if (oldIndex == -1) { if (Get.isLogEnable) { Get.log('❌ [ChatController] 未找到要重发的消息: ${failedMessage.msgId}'); } SmartDialog.showToast('未找到要重发的消息'); return false; } // 先删除SDK中的旧消息(避免重新加载时出现重复) try { final conversationId = failedMessage.conversationId ?? userId; await IMManager.instance.deleteMessage(conversationId, failedMessage.msgId); if (Get.isLogEnable) { Get.log('✅ [ChatController] 已删除SDK中的旧消息: ${failedMessage.msgId}'); } } catch (e) { // 如果删除失败,记录日志但继续重发流程 if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 删除SDK中的旧消息失败: $e,继续重发'); } } // 调用IMManager的重发方法 final newMessage = await IMManager.instance.resendMessage(failedMessage); if (newMessage != null) { if (Get.isLogEnable) { Get.log('✅ [ChatController] SDK返回重发消息: msgId=${newMessage.msgId}, status=${newMessage.status}'); } // 删除旧消息,添加新消息(在相同位置) messages.removeAt(oldIndex); messages.insert(oldIndex, newMessage); update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] 消息重发成功,已替换旧消息'); } // 更新会话列表 _refreshConversationList(); // 如果消息状态仍然是 PROGRESS,设置状态检查和监听(与sendMessage逻辑一致) if (newMessage.status == MessageStatus.PROGRESS) { if (Get.isLogEnable) { Get.log('⏳ [ChatController] 重发消息状态仍为PROGRESS,将延迟检查状态'); } // 延迟检查状态(与sendMessage逻辑一致) Future.delayed(Duration(milliseconds: 500), () { _checkMessageStatusAndUpdate(newMessage.msgId); }); Future.delayed(Duration(milliseconds: 1500), () { _checkMessageStatusAndUpdate(newMessage.msgId); }); Future.delayed(Duration(milliseconds: 3000), () { _checkMessageStatusAndUpdate(newMessage.msgId); }); } else if (newMessage.status == MessageStatus.FAIL) { // 如果状态已经是 FAIL,立即更新UI update(); if (Get.isLogEnable) { Get.log('❌ [ChatController] 重发消息失败,状态: ${newMessage.status}'); } SmartDialog.showToast('消息重发失败,请重试'); } else { // 状态不是 PROGRESS,说明已经更新完成 if (Get.isLogEnable) { Get.log('✅ [ChatController] 重发消息成功,状态: ${newMessage.status}'); } } return true; } else { if (Get.isLogEnable) { Get.log('❌ [ChatController] 消息重发失败,SDK返回null'); } SmartDialog.showToast('消息重发失败,请重试'); 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'); } } } /// 刷新玫瑰余额 void _refreshRoseBalance() { try { // 尝试获取 RoseController 并刷新玫瑰余额 if (Get.isRegistered()) { final roseController = Get.find(); roseController.getRoseNum(); } } catch (e) { // RoseController 可能未注册,忽略错误 if (Get.isLogEnable) { Get.log('刷新玫瑰余额失败: $e'); } } } /// 检查消息状态并更新(用于处理发送后状态可能变化的情况) void _checkMessageStatusAndUpdate(String messageId) async { final currentIndex = messages.indexWhere((msg) => msg.msgId == messageId); if (currentIndex != -1) { final currentMessage = messages[currentIndex]; // 如果状态仍然是 PROGRESS,尝试从 SDK 重新获取消息状态 if (currentMessage.status == MessageStatus.PROGRESS) { // 尝试从 SDK 重新获取消息的最新状态 try { // 先尝试通过原始ID获取 var latestMessage = await IMManager.instance.getMessageById(userId, messageId); // 如果获取失败或找不到,可能是消息ID改变了,尝试刷新消息列表 if (latestMessage == null) { await _refreshMessageFromSDK(messageId); // 刷新后再次尝试获取 latestMessage = await IMManager.instance.getMessageById(userId, messageId); } if (latestMessage != null) { // 如果从 SDK 获取到的消息状态已更新,替换列表中的消息 if (latestMessage.status != MessageStatus.PROGRESS) { messages[currentIndex] = latestMessage; // 强制刷新UI update(); if (Get.isLogEnable) { if (latestMessage.status == MessageStatus.FAIL) { Get.log('❌ [ChatController] 从SDK获取到消息失败状态,已更新UI'); } else { Get.log('✅ [ChatController] 从SDK获取到消息最新状态: ${latestMessage.status},已更新UI'); } } return; } else { // 状态仍然是 PROGRESS,但消息对象可能已更新(SDK内部状态可能已变化) // 更新消息对象以确保使用最新的消息数据 messages[currentIndex] = latestMessage; update(); } } else { // 如果通过ID找不到,尝试通过刷新消息列表来匹配 await _refreshMessageFromSDK(messageId); } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 从SDK获取消息状态失败: $e'); } } // 如果从 SDK 获取失败或状态仍然是 PROGRESS,继续轮询检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: 6); } else { // 状态已更新,刷新UI update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] 消息状态已自动更新: ${currentMessage.status}'); } // 如果状态是 FAIL,显示提示 if (currentMessage.status == MessageStatus.FAIL) { if (Get.isLogEnable) { Get.log('❌ [ChatController] 消息发送失败,请点击重发'); } } } } else { // 如果找不到消息(可能ID改变了),尝试从 SDK 获取最新消息列表 if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 找不到消息ID: $messageId,尝试从SDK获取最新消息'); } // 刷新消息列表以获取最新状态(处理消息ID可能改变的情况) await _refreshMessageFromSDK(messageId); } } /// 检查消息是否在会话中(用于验证消息是否真的发送成功) Future _checkMessageInConversation(String messageId) async { try { // 先找到当前消息,用于匹配内容和时间戳 final currentIndex = messages.indexWhere((msg) => msg.msgId == messageId); if (currentIndex == -1) { return; } final currentMessage = messages[currentIndex]; // 从会话中获取最新消息(如果会话不存在,尝试创建) final conversationMessages = await IMManager.instance.getMessages( userId, pageSize: 20, // 检查最近20条消息 createIfNeed: true, // 如果会话不存在,尝试创建 ); // 过滤掉null消息 final validMessages = conversationMessages.whereType().toList(); if (Get.isLogEnable) { Get.log('🔍 [ChatController] 检查会话消息,共 ${validMessages.length} 条,查找消息ID: $messageId'); // 打印会话中的消息详情,用于调试 for (var msg in validMessages) { Get.log(' - 消息ID: ${msg.msgId}, 方向: ${msg.direction}, 状态: ${msg.status}, 时间: ${msg.serverTime}'); if (msg.body.type == MessageType.TXT) { final body = msg.body as EMTextMessageBody; Get.log(' 内容: ${body.content}'); } } } // 先尝试通过消息ID查找 EMMessage? foundMessage; try { foundMessage = validMessages.firstWhere( (msg) => msg.msgId == messageId, ); } catch (e) { // 未找到,继续尝试通过内容和时间戳匹配 } // 如果通过ID找不到,尝试通过内容和时间戳匹配 if (foundMessage == null) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 在会话中未找到消息ID: $messageId,尝试通过内容和时间戳匹配'); } // 通过内容和时间戳匹配 for (var msg in validMessages) { // 只匹配发送方向的消息 if (msg.direction != MessageDirection.SEND) { continue; } // 匹配条件:相同的内容、相同的时间戳(允许一定误差) // 放宽时间戳匹配条件到30秒,因为消息可能延迟保存 final msgTime = msg.serverTime ?? 0; final currentTime = currentMessage.serverTime ?? 0; final timeDiff = (msgTime - currentTime).abs(); if (timeDiff < 30000) { // 30秒内的误差 // 进一步检查消息类型和内容是否相同 if (msg.body.type == currentMessage.body.type) { bool isMatch = false; if (msg.body.type == MessageType.TXT) { // 文本消息:比较内容 final msgBody = msg.body as EMTextMessageBody; final currentBody = currentMessage.body as EMTextMessageBody; isMatch = msgBody.content == currentBody.content; if (!isMatch && Get.isLogEnable) { Get.log(' - 文本内容不匹配: 会话消息="${msgBody.content}", 原始消息="${currentBody.content}"'); } } else if (msg.body.type == MessageType.IMAGE) { // 图片消息:比较本地路径或远程URL final msgBody = msg.body as EMImageMessageBody; final currentBody = currentMessage.body as EMImageMessageBody; // 如果都有本地路径,比较路径;否则比较远程URL if (msgBody.localPath != null && currentBody.localPath != null) { isMatch = msgBody.localPath == currentBody.localPath; } else if (msgBody.remotePath != null && currentBody.remotePath != null) { isMatch = msgBody.remotePath == currentBody.remotePath; } else { // 如果路径都为空,通过时间戳匹配(同一时间发送的图片) isMatch = true; } if (!isMatch && Get.isLogEnable) { Get.log(' - 图片路径不匹配: 会话消息路径="${msgBody.localPath ?? msgBody.remotePath}", 原始消息路径="${currentBody.localPath ?? currentBody.remotePath}"'); } } else if (msg.body.type == MessageType.VOICE) { // 语音消息:比较时长和本地路径 final msgBody = msg.body as EMVoiceMessageBody; final currentBody = currentMessage.body as EMVoiceMessageBody; if (msgBody.localPath != null && currentBody.localPath != null) { isMatch = msgBody.localPath == currentBody.localPath; } else { // 如果路径都为空,比较时长(同一时间发送的语音,时长应该相同) isMatch = msgBody.duration == currentBody.duration; } if (!isMatch && Get.isLogEnable) { Get.log(' - 语音不匹配: 会话消息时长=${msgBody.duration}, 原始消息时长=${currentBody.duration}'); } } else if (msg.body.type == MessageType.VIDEO) { // 视频消息:比较时长和本地路径 final msgBody = msg.body as EMVideoMessageBody; final currentBody = currentMessage.body as EMVideoMessageBody; if (msgBody.localPath != null && currentBody.localPath != null) { isMatch = msgBody.localPath == currentBody.localPath; } else { // 如果路径都为空,比较时长(同一时间发送的视频,时长应该相同) isMatch = msgBody.duration == currentBody.duration; } if (!isMatch && Get.isLogEnable) { Get.log(' - 视频不匹配: 会话消息时长=${msgBody.duration}, 原始消息时长=${currentBody.duration}'); } } else { // 其他类型消息:仅通过时间戳匹配 isMatch = true; } if (isMatch) { foundMessage = msg; if (Get.isLogEnable) { Get.log('✅ [ChatController] 通过内容和时间戳匹配到消息: 原ID=$messageId, 新ID=${msg.msgId}, 类型=${msg.body.type}, 时间差: ${timeDiff}ms'); } break; } } else { if (Get.isLogEnable) { Get.log('🔍 [ChatController] 消息类型不匹配: 会话消息类型=${msg.body.type}, 原始消息类型=${currentMessage.body.type}'); } } } else { if (Get.isLogEnable) { Get.log(' - 时间戳不匹配: 会话消息时间=${msg.serverTime}, 原始消息时间=${currentMessage.serverTime}, 时间差=${timeDiff}ms'); } } } } if (foundMessage != null) { // 如果找到了消息,更新消息状态 messages[currentIndex] = foundMessage; update(); if (Get.isLogEnable) { if (foundMessage.status == MessageStatus.FAIL) { Get.log('❌ [ChatController] 从会话中找到消息,状态为失败'); } else if (foundMessage.status == MessageStatus.SUCCESS) { Get.log('✅ [ChatController] 从会话中找到消息,状态为成功,消息ID已更新: ${foundMessage.msgId}'); } else { Get.log('⏳ [ChatController] 从会话中找到消息,状态仍为: ${foundMessage.status}'); } } // 找到消息后,返回true表示已找到,不需要继续查找 return; } else { // 如果找不到消息,说明消息可能没有真正发送成功 if (Get.isLogEnable) { Get.log('❌ [ChatController] 在会话中未找到消息,可能发送失败。会话消息数量: ${validMessages.length}'); } } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 检查会话消息失败: $e'); } } } /// 从 SDK 刷新消息列表,查找指定消息ID的消息(处理消息ID可能改变的情况) Future _refreshMessageFromSDK(String originalMessageId) async { try { // 从 SDK 获取最新消息列表 final List fetchedMessages = await IMManager.instance.getMessages( userId, pageSize: 50, // 获取最近50条消息 startMsgId: null, ); // 过滤掉null消息 final List validMessages = fetchedMessages.whereType().toList(); // 查找对应的消息(可能是原始ID,也可能是新的ID) // 由于消息ID可能改变,我们通过消息内容和时间戳来匹配 final currentIndex = messages.indexWhere((msg) => msg.msgId == originalMessageId); if (currentIndex != -1) { final originalMessage = messages[currentIndex]; // 尝试通过时间戳和内容匹配找到对应的消息 EMMessage? matchedMessage; for (var msg in validMessages) { // 匹配条件:相同的内容、相同的时间戳(允许一定误差)、发送方向相同 if (msg.direction == originalMessage.direction && msg.serverTime == originalMessage.serverTime) { // 进一步检查内容是否相同 if (msg.body.type == originalMessage.body.type) { if (msg.body.type == MessageType.TXT) { final msgBody = msg.body as EMTextMessageBody; final originalBody = originalMessage.body as EMTextMessageBody; if (msgBody.content == originalBody.content) { matchedMessage = msg; break; } } else { // 对于其他类型的消息,如果时间戳和方向匹配,就认为是同一条消息 matchedMessage = msg; break; } } } } if (matchedMessage != null) { // 找到匹配的消息,更新列表 messages[currentIndex] = matchedMessage; update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] 从SDK刷新消息成功,新消息ID: ${matchedMessage.msgId},状态: ${matchedMessage.status}'); } } else { // 如果找不到匹配的消息,尝试直接通过消息ID查找(可能ID没有改变) final directMatch = validMessages.firstWhere( (msg) => msg.msgId == originalMessageId, orElse: () => originalMessage, ); if (directMatch.msgId == originalMessageId) { messages[currentIndex] = directMatch; update(); if (Get.isLogEnable) { Get.log('✅ [ChatController] 通过消息ID找到消息,状态: ${directMatch.status}'); } } } } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 从SDK刷新消息失败: $e'); } // 刷新消息列表以获取最新状态 try { await fetchMessages(loadMore: false); update(); } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 刷新消息列表失败: $e'); } } } } /// 轮询检查消息状态,直到状态不再是 PROGRESS void _checkMessageStatusUntilComplete(String messageId, {int maxAttempts = 6, int attempt = 0}) async { if (attempt >= maxAttempts) { // 超过最大尝试次数,尝试从 SDK 最后一次获取消息状态 // 由于消息ID可能改变,使用刷新消息列表的方式 try { await _refreshMessageFromSDK(messageId); // 如果刷新后仍然找不到,尝试直接通过ID获取 final index = messages.indexWhere((msg) => msg.msgId == messageId); if (index != -1) { final latestMessage = await IMManager.instance.getMessageById(userId, messageId); if (latestMessage != null) { messages[index] = latestMessage; update(); if (Get.isLogEnable) { if (latestMessage.status == MessageStatus.FAIL) { Get.log('❌ [ChatController] 从SDK获取到消息失败状态(最终检查)'); } else if (latestMessage.status == MessageStatus.PROGRESS) { Get.log('⚠️ [ChatController] 消息状态检查超时,状态仍为 PROGRESS,可能发送失败'); } else { Get.log('✅ [ChatController] 从SDK获取到消息最新状态: ${latestMessage.status}'); } } return; } } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 最终检查时从SDK获取消息状态失败: $e'); } } // 如果从 SDK 获取失败,检查内存中的消息状态 final index = messages.indexWhere((msg) => msg.msgId == messageId); if (index != -1) { final message = messages[index]; // 刷新UI,显示当前状态(可能是 PROGRESS、SUCCESS 或 FAIL) update(); if (Get.isLogEnable) { if (message.status == MessageStatus.PROGRESS) { Get.log('⚠️ [ChatController] 消息状态检查超时,状态仍为 PROGRESS,可能发送失败'); } else { Get.log('✅ [ChatController] 消息状态检查完成,最终状态: ${message.status}'); } } // 如果状态仍然是 PROGRESS,可能是发送失败但没有更新状态 // 这种情况下,用户可以通过重发按钮重新发送 } return; } Future.delayed(Duration(milliseconds: 500), () async { // 每次检查时都尝试从 SDK 获取最新状态 // 注意:SDK 可能会改变消息ID(onMessageIdChanged),所以需要处理这种情况 try { // 先尝试通过原始ID获取 var latestMessage = await IMManager.instance.getMessageById(userId, messageId); // 如果获取失败或找不到,可能是消息ID改变了,尝试刷新消息列表 if (latestMessage == null) { await _refreshMessageFromSDK(messageId); // 刷新后再次尝试获取 latestMessage = await IMManager.instance.getMessageById(userId, messageId); } // 如果仍然找不到,尝试从会话中检查 if (latestMessage == null) { await _checkMessageInConversation(messageId); } if (latestMessage != null) { final index = messages.indexWhere((msg) => msg.msgId == messageId); if (index != -1) { // 更新消息对象(无论状态如何,都更新以确保使用最新的消息数据) messages[index] = latestMessage; update(); // 如果从 SDK 获取到的消息状态已更新,停止检查 if (latestMessage.status != MessageStatus.PROGRESS) { if (Get.isLogEnable) { if (latestMessage.status == MessageStatus.FAIL) { Get.log('❌ [ChatController] 从SDK获取到消息失败状态'); } else { Get.log('✅ [ChatController] 从SDK获取到消息最新状态: ${latestMessage.status}'); } } return; } else { // 状态仍然是 PROGRESS,先检查会话中的消息 // 如果从会话中找到消息并更新了状态,_checkMessageInConversation 会返回,不会继续执行 await _checkMessageInConversation(messageId); // 检查消息状态是否已更新(可能通过 _checkMessageInConversation 更新了) final updatedIndex = messages.indexWhere((msg) => msg.msgId == messageId); if (updatedIndex != -1) { final updatedMessage = messages[updatedIndex]; // 如果状态已更新为成功或失败,停止检查 if (updatedMessage.status != MessageStatus.PROGRESS) { if (Get.isLogEnable) { Get.log('✅ [ChatController] 消息状态已通过会话检查更新: ${updatedMessage.status}'); } return; } } // 继续检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: maxAttempts, attempt: attempt + 1); } } else { // 如果本地找不到消息(可能ID改变了),尝试刷新消息列表 await _refreshMessageFromSDK(messageId); // 继续检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: maxAttempts, attempt: attempt + 1); } } else { // 如果从 SDK 获取失败,使用本地消息状态继续检查 final index = messages.indexWhere((msg) => msg.msgId == messageId); if (index != -1) { final message = messages[index]; if (message.status != MessageStatus.PROGRESS) { // 状态已更新,刷新UI update(); if (Get.isLogEnable) { if (message.status == MessageStatus.FAIL) { Get.log('❌ [ChatController] 消息发送失败,状态: ${message.status}'); } else { Get.log('✅ [ChatController] 消息状态已更新: ${message.status}'); } } return; } else { // 状态仍然是 PROGRESS,继续检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: maxAttempts, attempt: attempt + 1); } } else { // 如果本地找不到消息,尝试刷新消息列表 await _refreshMessageFromSDK(messageId); // 继续检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: maxAttempts, attempt: attempt + 1); } } } catch (e) { if (Get.isLogEnable) { Get.log('⚠️ [ChatController] 从SDK获取消息状态失败: $e'); } // 继续检查 _checkMessageStatusUntilComplete(messageId, maxAttempts: maxAttempts, attempt: attempt + 1); } }); } /// 加载礼物产品列表 Future loadGiftProducts() async { try { final response = await _networkService.rtcApi.listGiftProduct(); final base = response.data; if (base.isSuccess && base.data != null) { giftProducts.assignAll(base.data!); if (Get.isLogEnable) { Get.log('✅ 礼物产品列表加载成功,共 ${giftProducts.length} 个'); } } else { if (Get.isLogEnable) { Get.log('❌ 加载礼物产品列表失败: ${base.message}'); } } } catch (e) { if (Get.isLogEnable) { Get.log('❌ 加载礼物产品列表异常: $e'); } } } /// 发送礼物(聊天界面) Future sendGift({ required GiftProductModel gift, int quantity = 1, }) async { try { // 准备请求参数(IM 聊天送礼接口) final requestData = { 'toUserId': int.tryParse(userId) ?? 0, 'productSpecId': int.tryParse(gift.productSpecId) ?? 0, 'quantity': quantity, }; // 调用 IM 送礼接口 final response = await _networkService.rtcApi.costImGift( requestData, ); if (response.data.isSuccess && !response.data.data['success']) { final code = response.data.data['code']; if (code == 'E0002') { // 玫瑰不足,显示 toast 并弹出充值弹框 SmartDialog.showToast('玫瑰不足请充值'); SmartDialog.show( alignment: Alignment.bottomCenter, maskColor: Get.context != null ? TDTheme.of(Get.context!).fontGyColor2 : Colors.black.withOpacity(0.5), builder: (_) => const LiveRechargePopup(), ); } else { SmartDialog.showToast('发送礼物失败'); } return false; } // 发送成功,创建礼物消息参数(需要转换为 Map) final giftParams = { 'giftProductId': gift.productId, 'giftProductTitle': gift.productTitle, 'giftMainPic': gift.mainPic, 'giftPrice': gift.unitSellingPrice.toString(), 'quantity': quantity.toString(), }; // 先创建自定义消息对象(即使发送失败也要显示在列表中) final tempMessage = EMMessage.createCustomSendMessage( targetId: userId, event: 'gift', params: giftParams, ); // 将消息添加到列表末尾(显示发送中状态) messages.add(tempMessage); update(); // 发送自定义消息 final message = await IMManager.instance.sendCustomMessage(userId, 'gift', giftParams); if (message != null) { // 发送成功,替换临时消息 final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId); if (index != -1) { messages[index] = message; } update(); // 更新会话列表 _refreshConversationList(); // 刷新玫瑰余额 _refreshRoseBalance(); if (Get.isLogEnable) { Get.log('✅ 礼物消息发送成功: ${gift.productTitle}'); } return true; } else { // 发送失败,更新消息状态 final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId); if (index != -1) { update(); } SmartDialog.showToast('礼物消息发送失败,请点击重发'); return false; } } catch (e) { if (Get.isLogEnable) { Get.log('❌ 发送礼物失败: $e'); } SmartDialog.showToast('发送礼物失败: $e'); return false; } } }