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.
 
 
 
 
 

500 lines
19 KiB

import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../im/im_manager.dart';
import '../../model/mine/user_base_data.dart';
// 扩展类用于存储用户信息(包括业务系统的信息)
class ExtendedUserInfo {
final String userId;
final String? nickName;
final String? avatarUrl;
ExtendedUserInfo({
required this.userId,
this.nickName,
this.avatarUrl,
});
// 从 EMUserInfo 创建
factory ExtendedUserInfo.fromEMUserInfo(EMUserInfo emUserInfo) {
return ExtendedUserInfo(
userId: emUserInfo.userId,
nickName: emUserInfo.nickName,
avatarUrl: emUserInfo.avatarUrl,
);
}
// 从 UserBaseData 创建
factory ExtendedUserInfo.fromUserBaseData(UserBaseData userBaseData, {String? avatarUrl}) {
return ExtendedUserInfo(
userId: userBaseData.userId,
nickName: userBaseData.nickName,
avatarUrl: avatarUrl,
);
}
}
class ConversationController extends GetxController {
// 会话列表数据
final conversations = <EMConversation>[].obs;
// 加载状态
final isLoading = false.obs;
// 错误消息
final errorMessage = ''.obs;
// 用户信息缓存(userId -> ExtendedUserInfo)
final Map<String, ExtendedUserInfo> _userInfoCache = {};
/// 缓存用户信息(公开方法,供 ChatController 调用)
void cacheUserInfo(String userId, ExtendedUserInfo userInfo) {
_userInfoCache[userId] = userInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 已缓存用户信息: userId=$userId, nickName=${userInfo.nickName}');
}
}
@override
void onInit() {
super.onInit();
// 初始化时检查 IM 登录状态,如果已登录则加载会话列表
_checkAndLoadConversations();
}
/// 检查 IM 登录状态并加载会话列表
Future<void> _checkAndLoadConversations() async {
// 如果已登录,直接加载
if (IMManager.instance.isLoggedIn) {
await loadConversations();
return;
}
// 如果未登录,等待登录完成(最多等待 10 秒)
if (Get.isLogEnable) {
Get.log('⏳ [ConversationController] IM 未登录,等待登录完成...');
}
int retryCount = 0;
const maxRetries = 20; // 最多重试 20 次(每次 500ms,总共 10 秒)
while (retryCount < maxRetries && !IMManager.instance.isLoggedIn) {
await Future.delayed(Duration(milliseconds: 500));
retryCount++;
}
if (IMManager.instance.isLoggedIn) {
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] IM 已登录,开始加载会话列表');
}
await loadConversations();
} else {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] IM 登录超时,显示错误提示');
}
errorMessage.value = 'IM 未登录,请稍后重试';
isLoading.value = false;
}
}
/// 加载会话列表
Future<void> loadConversations() async {
if (isLoading.value) return;
// 检查 IM 登录状态
if (!IMManager.instance.isLoggedIn) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] IM 未登录,无法加载会话列表');
}
errorMessage.value = 'IM 未登录,无法加载会话列表';
isLoading.value = false;
return;
}
try {
isLoading.value = true;
errorMessage.value = '';
// 从IMManager获取会话列表
final List<EMConversation> convList = await IMManager.instance
.getConversations();
// 更新会话列表
conversations.value = convList;
// 从所有会话的历史消息中提取用户信息并缓存(应用重启后恢复用户信息)
_extractUserInfoFromConversations(convList);
// 使用GetX日志系统
if (Get.isLogEnable) {
Get.log('Loaded ${convList.length} conversations');
}
} catch (e) {
// 使用GetX日志系统
if (Get.isLogEnable) {
Get.log('Failed to load conversations: $e');
}
errorMessage.value = '加载会话列表失败,请稍后重试';
} finally {
isLoading.value = false;
}
}
/// 从所有会话的历史消息中提取用户信息并缓存
/// 这样应用重启后也能恢复用户信息
/// 注意:只提取对方(接收到的消息)的用户信息,不提取自己的信息
Future<void> _extractUserInfoFromConversations(List<EMConversation> convList) async {
try {
for (var conversation in convList) {
try {
// conversation.id 是对方用户ID
final targetUserId = conversation.id;
// 如果缓存中已有该用户信息,跳过
if (_userInfoCache.containsKey(targetUserId)) continue;
// 获取会话的最新消息(最多获取最近20条)
final messages = await conversation.loadMessages(
loadCount: 20,
);
// 从消息中提取用户信息
// 接收消息:提取发送者信息(sender_ 前缀)
// 发送消息:提取接收者信息(receiver_ 前缀)
for (var message in messages) {
Map<String, dynamic>? attributes;
try {
attributes = message.attributes;
} catch (e) {
continue;
}
if (attributes == null || attributes.isEmpty) {
continue;
}
if (message.direction == MessageDirection.RECEIVE) {
// 接收到的消息:从扩展字段中提取发送者信息(sender_ 前缀)
final fromUserId = message.from;
if (fromUserId != null && fromUserId == targetUserId) {
// 优先使用 sender_ 前缀的字段(发送者信息)
final nickName = attributes['sender_nickName'] as String? ?? attributes['nickName'] as String?;
final avatarUrl = attributes['sender_avatarUrl'] as String? ?? attributes['avatarUrl'] as String?;
if (nickName != null || avatarUrl != null) {
final extendedInfo = ExtendedUserInfo(
userId: targetUserId,
nickName: nickName,
avatarUrl: avatarUrl,
);
_userInfoCache[targetUserId] = extendedInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从接收消息恢复对方用户信息: userId=$targetUserId, nickName=$nickName');
}
// 找到一个就足够了,跳出循环
break;
}
}
} else if (message.direction == MessageDirection.SEND) {
// 发送的消息:从扩展字段中提取接收者信息(receiver_ 前缀)
final toUserId = message.to;
if (toUserId != null && toUserId == targetUserId) {
// 使用 receiver_ 前缀的字段(接收者信息)
final nickName = attributes['receiver_nickName'] as String?;
final avatarUrl = attributes['receiver_avatarUrl'] as String?;
if (nickName != null || avatarUrl != null) {
final extendedInfo = ExtendedUserInfo(
userId: targetUserId,
nickName: nickName,
avatarUrl: avatarUrl,
);
_userInfoCache[targetUserId] = extendedInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从发送消息恢复对方用户信息: userId=$targetUserId, nickName=$nickName');
}
// 找到一个就足够了,跳出循环
break;
}
}
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 从会话提取用户信息失败: ${conversation.id}, 错误: $e');
}
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 批量提取用户信息失败: $e');
}
}
}
/// 刷新会话列表
Future<void> refreshConversations() async {
await loadConversations();
}
/// 清除会话列表和用户信息缓存(用于退出登录时)
void clearConversations() {
conversations.clear();
_userInfoCache.clear();
errorMessage.value = '';
isLoading.value = false;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 已清除会话列表和用户信息缓存');
}
}
/// 获取会话的最新消息
String getLastMessageContent(EMMessage? message) {
if(message?.body.type == MessageType.TXT){
final body = message?.body as EMTextMessageBody;
return body.content;
}else if(message?.body.type == MessageType.IMAGE){
return '[图片]';
}else if(message?.body.type == MessageType.VOICE){
return '[语音]';
}else if(message?.body.type == MessageType.VIDEO){
return '[视频]';
}else if(message?.body.type == MessageType.FILE){
return '[文件]';
}else if(message?.body.type == MessageType.LOCATION){
return '[位置]';
}else if(message?.body.type == MessageType.CUSTOM){
final body = message?.body as EMCustomMessageBody;
// 检查是否是分享房间类型
if(body.event == 'live_room_invite'){
return '[分享房间]';
}
return '[自定义消息]';
}
return '暂无消息';
}
/// 获取会话的未读消息数量
Future<int> getUnreadCount(EMConversation conversation) async {
try {
// 简化实现,返回0
return await conversation.unreadCount();
} catch (e) {
if (Get.isLogEnable) {
Get.log('Error getting unread count: $e');
}
return 0;
}
}
/// 格式化消息时间
String formatMessageTime(int timestamp) {
DateTime messageTime = DateTime.fromMillisecondsSinceEpoch(timestamp);
DateTime now = DateTime.now();
// 检查是否是今天
if (messageTime.year == now.year &&
messageTime.month == now.month &&
messageTime.day == now.day) {
// 今天显示时:分
return '${messageTime.hour.toString().padLeft(2, '0')}:${messageTime.minute.toString().padLeft(2, '0')}';
}
// 检查是否是昨天
DateTime yesterday = now.subtract(Duration(days: 1));
if (messageTime.year == yesterday.year &&
messageTime.month == yesterday.month &&
messageTime.day == yesterday.day) {
return '昨天';
}
// 其他日期显示月-日
return '${messageTime.month.toString().padLeft(2, '0')}-${messageTime.day.toString().padLeft(2, '0')}';
}
Future<ExtendedUserInfo?> loadContact(String userId) async{
try {
// 1. 先从缓存中查找(优先级最高)
if (_userInfoCache.containsKey(userId)) {
final cachedUser = _userInfoCache[userId]!;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从缓存获取到用户信息: $userId, nickName=${cachedUser.nickName}, avatarUrl=${cachedUser.avatarUrl}');
}
return cachedUser;
}
// 2. 尝试从会话的历史消息中提取用户信息(从消息扩展字段)
// 这是最重要的数据源,因为用户信息存储在消息扩展字段中
// 注意:conversation.id 是对方用户ID,应该从接收到的消息中提取对方的信息
try {
final conversation = await EMClient.getInstance.chatManager.getConversation(
userId,
type: EMConversationType.Chat,
createIfNeed: false,
);
if (conversation != null) {
// 获取最近的消息(最多20条),查找包含用户信息的消息
final messages = await conversation.loadMessages(
loadCount: 20,
);
// 从消息中查找用户信息(只查找接收到的消息,因为那是对方的用户信息)
// conversation.id 是对方用户ID,所以应该从接收到的消息中提取对方的信息
if (Get.isLogEnable) {
Get.log('🔍 [ConversationController] 开始从会话历史消息查找用户信息: userId=$userId, 消息数量=${messages.length}');
}
// 先打印所有消息的详细信息,用于调试
if (Get.isLogEnable) {
for (var msg in messages) {
final fromId = msg.from;
final toId = msg.to;
final direction = msg.direction;
Map<String, dynamic>? attrs;
try {
attrs = msg.attributes;
} catch (e) {
attrs = null;
}
Get.log(' 📨 消息: msgId=${msg.msgId}, 方向=$direction, from=$fromId, to=$toId, attributes=$attrs');
}
}
for (var message in messages) {
Map<String, dynamic>? attributes;
try {
attributes = message.attributes;
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 无法访问 message.attributes: $e');
}
continue;
}
if (attributes == null || attributes.isEmpty) {
continue;
}
if (message.direction == MessageDirection.RECEIVE) {
// 接收到的消息:从扩展字段中提取发送者信息(sender_ 前缀)
// conversation.id 是对方用户ID,应该从接收到的消息中提取发送者(对方)的信息
final fromUserId = message.from;
if (fromUserId != null && fromUserId == userId) {
// 优先使用 sender_ 前缀的字段(发送者信息)
final nickName = attributes['sender_nickName'] as String? ?? attributes['nickName'] as String?;
final avatarUrl = attributes['sender_avatarUrl'] as String? ?? attributes['avatarUrl'] as String?;
if (Get.isLogEnable) {
Get.log('🔍 [ConversationController] 从接收消息提取发送者信息: msgId=${message.msgId}, fromUserId=$fromUserId, nickName=$nickName, avatarUrl=$avatarUrl');
}
if (nickName != null || avatarUrl != null) {
final extendedInfo = ExtendedUserInfo(
userId: userId,
nickName: nickName,
avatarUrl: avatarUrl,
);
_userInfoCache[userId] = extendedInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从接收消息扩展字段获取到对方用户信息: userId=$userId, nickName=$nickName');
}
return extendedInfo;
}
}
} else if (message.direction == MessageDirection.SEND) {
// 发送的消息:从扩展字段中提取接收者信息(receiver_ 前缀)
// conversation.id 是对方用户ID,应该从发送的消息中提取接收者(对方)的信息
final toUserId = message.to;
if (toUserId != null && toUserId == userId) {
// 使用 receiver_ 前缀的字段(接收者信息)
final nickName = attributes['receiver_nickName'] as String?;
final avatarUrl = attributes['receiver_avatarUrl'] as String?;
if (Get.isLogEnable) {
Get.log('🔍 [ConversationController] 从发送消息提取接收者信息: msgId=${message.msgId}, toUserId=$toUserId, nickName=$nickName, avatarUrl=$avatarUrl');
}
if (nickName != null || avatarUrl != null) {
final extendedInfo = ExtendedUserInfo(
userId: userId,
nickName: nickName,
avatarUrl: avatarUrl,
);
_userInfoCache[userId] = extendedInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从发送消息扩展字段获取到对方用户信息: userId=$userId, nickName=$nickName');
}
return extendedInfo;
}
}
}
}
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 在会话历史消息中未找到用户信息: userId=$userId');
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 从会话消息提取用户信息失败: $e');
}
}
// 3. 如果从消息扩展字段获取不到,尝试从环信获取用户信息(作为备选)
try {
var data = await IMManager.instance.getContacts(userId);
var emUserInfo = data[userId];
if (emUserInfo != null && (emUserInfo.nickName?.isNotEmpty ?? false)) {
final extendedInfo = ExtendedUserInfo.fromEMUserInfo(emUserInfo);
_userInfoCache[userId] = extendedInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从环信获取到用户信息: $userId, nickName=${extendedInfo.nickName}');
}
return extendedInfo;
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 从环信获取用户信息失败: $e');
}
}
// 4. 如果都获取不到,返回一个基本的 ExtendedUserInfo(至少显示用户ID)
final fallbackInfo = ExtendedUserInfo(userId: userId);
_userInfoCache[userId] = fallbackInfo;
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 未从任何来源获取到用户信息,使用默认值: $userId');
}
return fallbackInfo;
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] 获取用户信息失败: $userId, 错误: $e');
}
// 即使出错也返回一个基本的 ExtendedUserInfo
final fallbackInfo = ExtendedUserInfo(userId: userId);
_userInfoCache[userId] = fallbackInfo;
return fallbackInfo;
}
}
Future<EMMessage?> lastMessage(EMConversation conversation) async{
return await conversation.latestMessage();
}
/// 删除会话
Future<bool> deleteConversation(String conversationId) async {
try {
final success = await IMManager.instance.deleteConversation(
conversationId,
);
if (success) {
conversations.removeWhere((element) => element.id == conversationId);
}
return success;
} catch (e) {
if (Get.isLogEnable) {
Get.log('删除会话失败: $e');
}
return false;
}
}
}