You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1805 lines
68 KiB

import 'package:get/get.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'dart:convert';
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 'package:im_flutter_sdk/im_flutter_sdk.dart';
import 'conversation_controller.dart';
class ChatController extends GetxController {
final String userId;
// 用户信息
final Rx<EMUserInfo?> userInfo = Rx<EMUserInfo?>(null);
// 外部传入的用户数据模型(用于在获取不到环信用户信息时使用)
MarriageData? _userData;
// 外部传入的用户信息(用于在获取不到环信用户信息时使用)
String? _externalNickName;
String? _externalAvatarUrl;
// 用户在线状态(从接口获取)
final RxBool isUserOnline = RxBool(false);
// 消息列表
final RxList<EMMessage> messages = RxList<EMMessage>([]);
// 加载更多的游标
String? _cursor;
// 视频发送状态
final RxBool isSendingVideo = RxBool(false);
final RxString sendingStatus = RxString('');
// 语音发送状态(防止重复发送)
final RxBool isSendingVoice = RxBool(false);
// 礼物产品列表
final RxList<GiftProductModel> giftProducts = <GiftProductModel>[].obs;
// 需要显示508错误提示的消息ID集合(临时状态,不持久化)
final Set<String> _roseErrorMessageIds = <String>{};
// 需要显示敏感词错误提示的消息ID集合(临时状态,不持久化)
final Set<String> _sensitiveWordMessageIds = <String>{};
// 网络服务
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();
}
}
/// 订阅用户在线状态(实时接收状态变化)
Future<void> 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<void> unsubscribeUserPresence() async {
try {
await ChatPresenceManager().unsubscribeUserPresence(userId);
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [ChatController] 取消订阅用户在线状态异常: $e');
}
}
}
/// 从 ConversationController 的缓存中加载用户信息
void _loadUserInfoFromConversationCache() {
try {
if (Get.isRegistered<ConversationController>()) {
final conversationController = Get.find<ConversationController>();
// 尝试从缓存中获取用户信息
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<ConversationController>()) {
final conversationController = Get.find<ConversationController>();
// 创建 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<void> 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<bool> 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<bool> 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<bool> 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<bool> 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<void> fetchMessages({bool loadMore = false}) async {
try {
// 如果加载更多但没有游标,说明已经加载完所有消息
if (loadMore && _cursor == null) {
if (Get.isLogEnable) {
Get.log('没有更多消息了');
}
return;
}
final List<EMMessage?> fetchedMessages = await IMManager.instance
.getMessages(
userId,
pageSize: 20,
startMsgId: loadMore ? _cursor : null,
);
// 过滤掉null消息
final List<EMMessage> validMessages = fetchedMessages
.whereType<EMMessage>()
.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<String, EMMessage> 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<void> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> sendCallMessage({
required String callType, // 'voice' 或 'video'
required String callStatus, // 'calling', 'missed', 'cancelled', 'rejected'
int? callDuration, // 通话时长(秒)
}) async {
try {
// 构建通话信息
final callInfoMap = <String, dynamic>{
'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<bool> 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<ConversationController>()) {
final conversationController = Get.find<ConversationController>();
conversationController.refreshConversations();
}
} catch (e) {
// ConversationController 可能未注册,忽略错误
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<void> _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<EMMessage>().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<void> _refreshMessageFromSDK(String originalMessageId) async {
try {
// 从 SDK 获取最新消息列表
final List<EMMessage?> fetchedMessages = await IMManager.instance.getMessages(
userId,
pageSize: 50, // 获取最近50条消息
startMsgId: null,
);
// 过滤掉null消息
final List<EMMessage> validMessages = fetchedMessages.whereType<EMMessage>().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<void> 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<bool> 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']) {
SmartDialog.showToast(
response.data.data['code'] == 'E0002' ? '玫瑰不足请充值' : '发送礼物失败',
);
return false;
}
// 发送成功,创建礼物消息参数(需要转换为 Map<String, String>)
final giftParams = <String, String>{
'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();
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;
}
}
}