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.
 
 
 
 
 

829 lines
31 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';
import '../mine/user_controller.dart';
import 'chat_controller.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,
);
}
}
// 筛选类型枚举
enum FilterType {
none, // 无筛选
lastChatTime, // 最后聊天时间
unread, // 未读消息
online, // 当前在线
}
class ConversationController extends GetxController {
// 会话列表数据
final conversations = <EMConversation>[].obs;
// 加载状态
final isLoading = false.obs;
// 错误消息
final errorMessage = ''.obs;
// 用户信息缓存(userId -> ExtendedUserInfo)
final Map<String, ExtendedUserInfo> _userInfoCache = {};
// 筛选类型
final filterType = FilterType.none.obs;
// 总未读数
final totalUnreadCount = 0.obs;
/// 缓存用户信息(公开方法,供 ChatController 调用)
void cacheUserInfo(String userId, ExtendedUserInfo userInfo) {
_userInfoCache[userId] = userInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 已缓存用户信息: userId=$userId, nickName=${userInfo.nickName}');
}
}
/// 从缓存获取用户信息(公开方法,供 UI 调用)
ExtendedUserInfo? getCachedUserInfo(String userId) {
return _userInfoCache[userId];
}
@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();
// 先提取用户信息并缓存,然后再更新会话列表
// 这样可以确保在列表渲染时,用户信息已经在缓存中了
await _extractUserInfoFromConversations(convList);
// 更新会话列表(在用户信息提取完成后)
conversations.value = convList;
// 计算总未读数
await _updateTotalUnreadCount();
// 使用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;
// 获取会话的最新消息(增加数量以提高找到用户信息的概率)
final messages = await conversation.loadMessages(
loadCount: 50, // 从20增加到50,提高找到用户信息的概率
);
if (Get.isLogEnable) {
Get.log('🔍 [ConversationController] 开始提取用户信息: userId=$targetUserId, 消息数量=${messages.length}');
}
// 从消息中提取用户信息
// 接收消息:提取发送者信息(sender_ 前缀)
// 发送消息:提取接收者信息(receiver_ 前缀)
// 遍历所有消息,找到最新的包含用户信息的消息
ExtendedUserInfo? foundUserInfo;
for (var message in messages) {
Map<String, dynamic>? attributes;
try {
attributes = message.attributes;
} catch (e) {
if (Get.isLogEnable) {
Get.log(' ⚠️ 无法访问消息 attributes: msgId=${message.msgId}, error=$e');
}
continue;
}
if (attributes == null || attributes.isEmpty) {
if (Get.isLogEnable) {
Get.log(' ⚠️ 消息 attributes 为空: msgId=${message.msgId}, direction=${message.direction}');
}
continue;
}
if (Get.isLogEnable) {
Get.log(' 📨 检查消息: msgId=${message.msgId}, direction=${message.direction}, from=${message.from}, to=${message.to}, attributes keys=${attributes.keys.toList()}');
}
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) {
// 如果找到用户信息,保存(但继续遍历以找到最新的)
foundUserInfo = ExtendedUserInfo(
userId: targetUserId,
nickName: nickName,
avatarUrl: avatarUrl,
);
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从接收消息找到用户信息: userId=$targetUserId, nickName=$nickName, msgId=${message.msgId}');
}
// 继续遍历,找到最新的消息(因为消息是按时间倒序的,第一个就是最新的)
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) {
// 如果找到用户信息,保存(但继续遍历以找到最新的)
foundUserInfo = ExtendedUserInfo(
userId: targetUserId,
nickName: nickName,
avatarUrl: avatarUrl,
);
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从发送消息找到用户信息: userId=$targetUserId, nickName=$nickName, msgId=${message.msgId}');
}
// 继续遍历,找到最新的消息(因为消息是按时间倒序的,第一个就是最新的)
break;
}
}
}
}
// 如果找到了用户信息,保存到缓存
if (foundUserInfo != null) {
_userInfoCache[targetUserId] = foundUserInfo;
}
// 如果从消息中提取不到用户信息,尝试从环信获取(作为备选)
if (!_userInfoCache.containsKey(targetUserId)) {
try {
var data = await IMManager.instance.getContacts(targetUserId);
var emUserInfo = data[targetUserId];
if (emUserInfo != null && (emUserInfo.nickName?.isNotEmpty ?? false)) {
final extendedInfo = ExtendedUserInfo.fromEMUserInfo(emUserInfo);
_userInfoCache[targetUserId] = extendedInfo;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从环信获取到用户信息: userId=$targetUserId, nickName=${extendedInfo.nickName}');
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 从环信获取用户信息失败: $e');
}
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 从会话提取用户信息失败: ${conversation.id}, 错误: $e');
}
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 批量提取用户信息失败: $e');
}
}
}
/// 刷新会话列表
Future<void> refreshConversations() async {
// 如果IM未登录,先尝试登录
if (!IMManager.instance.isLoggedIn) {
if (Get.isLogEnable) {
Get.log('🔄 [ConversationController] IM未登录,尝试重新登录...');
}
try {
// 尝试获取token并登录
if (Get.isRegistered<UserController>()) {
final userController = Get.find<UserController>();
final token = await userController.getHxUserToken();
if (token != null) {
// 等待登录完成(最多等待5秒)
int waitCount = 0;
const maxWait = 10; // 最多等待10次,每次500ms,总共5秒
while (waitCount < maxWait && !IMManager.instance.isLoggedIn) {
await Future.delayed(Duration(milliseconds: 500));
waitCount++;
}
if (IMManager.instance.isLoggedIn) {
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] IM登录成功,开始加载会话列表');
}
// 登录成功后加载会话列表
await loadConversations();
return;
} else {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] IM登录超时');
}
errorMessage.value = 'IM登录超时,请稍后重试';
isLoading.value = false;
return;
}
} else {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] 获取IM token失败');
}
errorMessage.value = '获取IM token失败,请稍后重试';
isLoading.value = false;
return;
}
} else {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] UserController未注册');
}
errorMessage.value = 'IM未登录,请稍后重试';
isLoading.value = false;
return;
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] 重试登录失败: $e');
}
errorMessage.value = '重试登录失败,请稍后重试';
isLoading.value = false;
return;
}
} else {
// 如果已登录,直接加载会话列表
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 == null) {
return '暂无消息';
}
// 检查消息是否发送失败(发送的消息且状态为FAIL)
if (message.direction == MessageDirection.SEND && message.status == MessageStatus.FAIL) {
return '[发送失败]';
}
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;
}
}
/// 更新总未读数(内部方法)
Future<void> _updateTotalUnreadCount() async {
try {
int total = 0;
for (var conversation in conversations) {
final unreadCount = await getUnreadCount(conversation);
total += unreadCount;
}
totalUnreadCount.value = total;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 总未读数已更新: $total');
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 更新总未读数失败: $e');
}
totalUnreadCount.value = 0;
}
}
/// 刷新总未读数(公开方法,供外部调用)
Future<void> refreshTotalUnreadCount() async {
await _updateTotalUnreadCount();
}
/// 格式化消息时间
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) {
// 获取最近的消息(增加数量以提高找到用户信息的概率)
final messages = await conversation.loadMessages(
loadCount: 50, // 从20增加到50,提高找到用户信息的概率
);
// 从消息中查找用户信息(只查找接收到的消息,因为那是对方的用户信息)
// 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{
try {
// 优先尝试从ChatController获取最后一条消息(如果ChatController还在内存中)
// 这样可以获取到失败状态的消息(SDK可能不会保存失败消息)
try {
final tag = 'chat_${conversation.id}';
if (Get.isRegistered<ChatController>(tag: tag)) {
final chatController = Get.find<ChatController>(tag: tag);
if (chatController.messages.isNotEmpty) {
// 获取最后一条消息(列表末尾,按时间戳排序后最后一条是最新的)
// 注意:messages列表是按时间戳从旧到新排序的,所以last是最新的
final lastMsg = chatController.messages.last;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 从ChatController获取最后一条消息: msgId=${lastMsg.msgId}, status=${lastMsg.status}, direction=${lastMsg.direction}, content=${lastMsg.body.type == MessageType.TXT ? (lastMsg.body as EMTextMessageBody).content : lastMsg.body.type}');
}
return lastMsg;
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 从ChatController获取最后一条消息失败: $e');
}
}
// 如果ChatController不存在或没有消息,从SDK获取
final sdkMessage = await conversation.latestMessage();
if (Get.isLogEnable) {
if (sdkMessage != null) {
Get.log('✅ [ConversationController] 从SDK获取最后一条消息: msgId=${sdkMessage.msgId}, status=${sdkMessage.status}, direction=${sdkMessage.direction}');
} else {
Get.log('⚠️ [ConversationController] SDK返回的最后一条消息为null');
}
}
return sdkMessage;
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 获取最后一条消息失败: $e');
}
return null;
}
}
/// 删除会话
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;
}
}
/// 设置筛选类型
void setFilterType(FilterType type) {
filterType.value = type;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 设置筛选类型: $type');
}
}
/// 获取筛选后的会话列表
Future<List<EMConversation>> getFilteredConversations() async {
List<EMConversation> filteredList = List.from(conversations);
switch (filterType.value) {
case FilterType.none:
// 无筛选,按默认顺序返回
break;
case FilterType.lastChatTime:
// 按最后聊天时间排序(最新的在前)
// 先获取所有消息的时间戳
final List<MapEntry<EMConversation, int>> conversationTimes = await Future.wait(
filteredList.map((conv) async {
final message = await lastMessage(conv);
final time = message?.serverTime ?? 0;
return MapEntry(conv, time);
}),
);
// 按时间戳排序(降序,最新的在前)
conversationTimes.sort((a, b) => b.value.compareTo(a.value));
filteredList = conversationTimes.map((entry) => entry.key).toList();
break;
case FilterType.unread:
// 只显示有未读消息的会话
final List<MapEntry<EMConversation, int>> conversationUnreads = await Future.wait(
filteredList.map((conv) async {
final unreadCount = await getUnreadCount(conv);
return MapEntry(conv, unreadCount);
}),
);
// 过滤出有未读消息的会话
final unreadConversations = conversationUnreads
.where((entry) => entry.value > 0)
.toList();
// 按未读数量排序(未读数多的在前)
unreadConversations.sort((a, b) => b.value.compareTo(a.value));
filteredList = unreadConversations.map((entry) => entry.key).toList();
break;
case FilterType.online:
// 只显示在线用户的会话
final List<EMConversation?> onlineConversations = await Future.wait(
filteredList.map((conv) async {
final isOnline = await _checkUserOnline(conv);
return isOnline ? conv : null;
}),
);
filteredList = onlineConversations
.whereType<EMConversation>()
.toList();
break;
}
return filteredList;
}
/// 检查用户是否在线
Future<bool> _checkUserOnline(EMConversation conversation) async {
try {
// 获取会话的最新消息
final message = await lastMessage(conversation);
if (message == null) {
return false;
}
// 从消息扩展字段中获取在线状态
Map<String, dynamic>? attributes;
try {
attributes = message.attributes;
} catch (e) {
return false;
}
if (attributes == null || attributes.isEmpty) {
return false;
}
// 检查在线状态字段
// 接收消息:检查 sender_isOnline
// 发送消息:检查 receiver_isOnline
String? isOnlineStr;
String? lastActiveTimeStr;
if (message.direction == MessageDirection.RECEIVE) {
isOnlineStr = attributes['sender_isOnline'] as String?;
lastActiveTimeStr = attributes['sender_lastActiveTime'] as String?;
} else {
isOnlineStr = attributes['receiver_isOnline'] as String?;
lastActiveTimeStr = attributes['receiver_lastActiveTime'] as String?;
}
if (isOnlineStr == 'true') {
// 进一步检查最后活跃时间(5分钟内认为在线)
if (lastActiveTimeStr != null) {
try {
final lastActiveTime = int.parse(lastActiveTimeStr);
final now = DateTime.now().millisecondsSinceEpoch;
final diff = now - lastActiveTime;
// 5分钟内认为在线(5 * 60 * 1000 毫秒)
return diff < 5 * 60 * 1000;
} catch (e) {
// 解析失败,使用 isOnline 字段
return true;
}
} else {
return true;
}
}
return false;
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 检查用户在线状态失败: $e');
}
return false;
}
}
}