From 5b682d090d449a63911f1f357d71a793da03fca2 Mon Sep 17 00:00:00 2001 From: Jolie <> Date: Wed, 17 Dec 2025 00:52:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live/live_room_guest_list_dialog.dart | 295 +++++++++++++++--- 1 file changed, 257 insertions(+), 38 deletions(-) diff --git a/lib/widget/live/live_room_guest_list_dialog.dart b/lib/widget/live/live_room_guest_list_dialog.dart index 88b0c94..4fbd570 100644 --- a/lib/widget/live/live_room_guest_list_dialog.dart +++ b/lib/widget/live/live_room_guest_list_dialog.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:dating_touchme_app/controller/discover/room_controller.dart'; +import 'package:dating_touchme_app/controller/message/conversation_controller.dart'; import 'package:dating_touchme_app/im/im_manager.dart'; import 'package:dating_touchme_app/network/network_service.dart'; import 'package:dating_touchme_app/rtc/rtc_manager.dart'; @@ -9,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:im_flutter_sdk/im_flutter_sdk.dart'; /// 嘉宾列表对话框 class LiveRoomGuestListDialog extends StatefulWidget { @@ -41,6 +43,7 @@ class _LiveRoomGuestListDialogState extends State { try { // 获取会话列表 final conversations = await IMManager.instance.getConversations(); + if (conversations.isEmpty) { setState(() { _guestList = []; @@ -53,15 +56,119 @@ class _LiveRoomGuestListDialogState extends State { final List> guestList = []; // 遍历每个会话,获取联系人信息 - for (final conversation in conversations) { + for (int i = 0; i < conversations.length; i++) { + final conversation = conversations[i]; try { final userId = conversation.id; - if (userId.isEmpty) continue; + + if (userId.isEmpty) { + continue; + } // 获取环信用户信息 final contactsMap = await IMManager.instance.getContacts(userId); final emUserInfo = contactsMap[userId]; - Get.log('获取会话列表: ${emUserInfo?.gender}'); + + // 尝试从 ConversationController 获取用户信息(会自动从缓存、消息扩展字段、环信等多个数据源获取) + ExtendedUserInfo? cachedUserInfo; + + // 首先尝试从 ConversationController 获取(如果已注册) + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + // 使用 loadContact 方法,它会自动从多个数据源获取用户信息: + // 1. 缓存 + // 2. 会话历史消息的扩展字段 + // 3. 环信用户信息 + cachedUserInfo = await conversationController.loadContact(userId); + } catch (e) { + // 忽略错误,继续从其他数据源获取 + } + } + + // 如果还没有获取到用户信息(无论 ConversationController 是否注册),都尝试直接从会话历史消息中提取 + final hasNickName = cachedUserInfo?.nickName != null && cachedUserInfo!.nickName!.isNotEmpty; + final hasAvatar = cachedUserInfo?.avatarUrl != null && cachedUserInfo!.avatarUrl!.isNotEmpty; + + if (!hasNickName || !hasAvatar) { + try { + // 直接从会话历史消息中提取用户信息 + final conversation = await EMClient.getInstance.chatManager.getConversation( + userId, + type: EMConversationType.Chat, + createIfNeed: false, + ); + if (conversation != null) { + final messages = await conversation.loadMessages(loadCount: 50); + + for (var message in messages) { + Map? attributes; + try { + attributes = message.attributes; + } catch (e) { + continue; + } + + if (attributes == null || attributes.isEmpty) { + continue; + } + + if (message.direction == MessageDirection.RECEIVE) { + final fromUserId = message.from; + if (fromUserId != null && fromUserId == userId) { + 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 && nickName.isNotEmpty) || (avatarUrl != null && avatarUrl.isNotEmpty)) { + cachedUserInfo = ExtendedUserInfo( + userId: userId, + nickName: nickName ?? cachedUserInfo?.nickName, + avatarUrl: avatarUrl ?? cachedUserInfo?.avatarUrl, + ); + // 如果 ConversationController 已注册,缓存起来 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + conversationController.cacheUserInfo(userId, cachedUserInfo); + } catch (e) { + // 忽略缓存失败 + } + } + break; + } + } + } else if (message.direction == MessageDirection.SEND) { + final toUserId = message.to; + if (toUserId != null && toUserId == userId) { + final nickName = attributes['receiver_nickName'] as String?; + final avatarUrl = attributes['receiver_avatarUrl'] as String?; + + if ((nickName != null && nickName.isNotEmpty) || (avatarUrl != null && avatarUrl.isNotEmpty)) { + cachedUserInfo = ExtendedUserInfo( + userId: userId, + nickName: nickName ?? cachedUserInfo?.nickName, + avatarUrl: avatarUrl ?? cachedUserInfo?.avatarUrl, + ); + // 如果 ConversationController 已注册,缓存起来 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + conversationController.cacheUserInfo(userId, cachedUserInfo); + } catch (e) { + // 忽略缓存失败 + } + } + break; + } + } + } + } + } + } catch (e) { + // 忽略错误,继续使用其他数据源 + } + } + // 获取用户详细信息(包含性别) final response = await networkService.userApi.getBaseUserInfo(userId); @@ -73,14 +180,49 @@ class _LiveRoomGuestListDialogState extends State { int? age; String? cityName; int? vipLevel; + String? extAvatarUrl; // 从扩展信息中获取头像 + String? extNickName; // 从扩展信息中获取昵称 + + // 首先尝试从环信用户信息的 gender 字段获取性别 + // 环信的 gender 可能是 "1"(男)或 "2"(女),"0" 表示未设置 + // 需要转换为我们的 genderCode(0-男, 1-女) + try { + final emGender = emUserInfo?.gender; + if (emGender != null) { + final emGenderStr = emGender.toString().trim(); + + // 环信: "1"=男, "2"=女, "0"=未设置 -> 我们的: 0=男, 1=女 + if (emGenderStr == '1') { + genderCode = 0; // 男性 + } else if (emGenderStr == '2') { + genderCode = 1; // 女性 + } else if (emGenderStr != '0' && emGenderStr.isNotEmpty) { + // 尝试直接解析为数字 + final emGenderInt = int.tryParse(emGenderStr); + if (emGenderInt != null) { + if (emGenderInt == 1) { + genderCode = 0; // 男性 + } else if (emGenderInt == 2) { + genderCode = 1; // 女性 + } else if (emGenderInt != 0) { + // 其他值,尝试转换 + genderCode = emGenderInt >= 2 ? 1 : (emGenderInt == 1 ? 0 : null); + } + } + } + } + } catch (e) { + // 忽略错误,继续从其他数据源获取 + } - if (emUserInfo?.ext != null) { + // 如果还没有获取到性别,尝试从扩展信息中获取 + if (genderCode == null && emUserInfo?.ext != null) { try { final extJson = json.decode(emUserInfo!.ext!); if (extJson is Map) { genderCode = extJson['genderCode'] is int ? extJson['genderCode'] as int - : int.tryParse(extJson['genderCode']?.toString() ?? '0'); + : int.tryParse(extJson['genderCode']?.toString() ?? ''); age = extJson['age'] is int ? extJson['age'] as int : int.tryParse(extJson['age']?.toString() ?? ''); @@ -88,26 +230,88 @@ class _LiveRoomGuestListDialogState extends State { vipLevel = extJson['vipLevel'] is int ? extJson['vipLevel'] as int : int.tryParse(extJson['vipLevel']?.toString() ?? ''); + extAvatarUrl = extJson['avatarUrl']?.toString() ?? + extJson['profilePhoto']?.toString(); + extNickName = extJson['nickName']?.toString(); } } catch (e) { - print('解析扩展信息失败: $e'); + // 忽略解析错误 } } - guestList.add({ + // 确定头像:优先使用环信头像,其次使用缓存头像,最后使用扩展信息中的头像 + // 注意:需要检查空字符串,不仅仅是 null + String avatarUrl = ''; + final emAvatar = emUserInfo?.avatarUrl?.trim(); + final cachedAvatar = cachedUserInfo?.avatarUrl?.trim(); + final extAvatar = extAvatarUrl?.trim(); + + if (emAvatar != null && emAvatar.isNotEmpty) { + avatarUrl = emAvatar; + } else if (cachedAvatar != null && cachedAvatar.isNotEmpty) { + avatarUrl = cachedAvatar; + } else if (extAvatar != null && extAvatar.isNotEmpty) { + avatarUrl = extAvatar; + } + + // 清理头像URL(移除反引号) + if (avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.replaceAll('`', ''); + } + + // 确定昵称:优先使用环信昵称,其次使用缓存昵称,再次使用扩展信息中的昵称,最后使用 API 返回的昵称 + // 注意:需要检查空字符串,不仅仅是 null + String name = ''; + final emNickName = emUserInfo?.nickName?.trim(); + final cachedNickName = cachedUserInfo?.nickName?.trim(); + final extNick = extNickName?.trim(); + final apiNickName = userBaseData.nickName.trim(); + + if (emNickName != null && emNickName.isNotEmpty) { + name = emNickName; + } else if (cachedNickName != null && cachedNickName.isNotEmpty) { + name = cachedNickName; + } else if (extNick != null && extNick.isNotEmpty) { + name = extNick; + } else if (apiNickName.isNotEmpty) { + name = apiNickName; + } + + // 如果昵称为空,使用 userId 作为备选 + if (name.isEmpty) { + name = userId; + } + + // 如果 genderCode 仍然为 null,尝试从环信的 gender 字段推断 + // 注意:如果环信的 gender=0 表示未设置,但如果用户有头像和昵称,可能是男性(因为只有男嘉宾有问题) + int? finalGenderCode = genderCode; + + if (finalGenderCode == null) { + // 如果所有数据源都没有性别信息,但用户有头像或昵称,可能是男性(因为只有男嘉宾有问题) + // 这里暂时设置为 0(男性),因为用户说只有男嘉宾有问题 + if (avatarUrl.isNotEmpty || name.isNotEmpty) { + finalGenderCode = 0; // 默认设置为男性 + } else { + finalGenderCode = -1; // 未知 + } + } + + final guestData = { 'userId': userId, - 'avatar': emUserInfo?.avatarUrl ?? '', - 'name': emUserInfo?.nickName ?? userBaseData.nickName, + 'avatar': avatarUrl, + 'name': name, 'age': age, 'location': cityName ?? '', 'vipLevel': vipLevel, - 'genderCode': genderCode ?? 0, // 0-男, 1-女 + 'genderCode': finalGenderCode, // 0-男, 1-女, -1-未知 'hasMaleGuest': false, // TODO: 根据实际业务逻辑判断 'hasFemaleGuest': false, // TODO: 根据实际业务逻辑判断 - }); + }; + + guestList.add(guestData); } } catch (e) { - print('获取用户信息失败: ${conversation.id}, $e'); + print('❌ [LiveRoomGuestListDialog] 获取用户信息异常: userId=${conversation.id}, error=$e'); } } @@ -116,7 +320,7 @@ class _LiveRoomGuestListDialogState extends State { _isLoading = false; }); } catch (e) { - print('获取联系人列表失败: $e'); + print('❌ [LiveRoomGuestListDialog] 获取联系人列表异常: $e'); setState(() { _guestList = []; _isLoading = false; @@ -197,14 +401,16 @@ class _LiveRoomGuestListDialogState extends State { } // 根据性别筛选联系人 - // genderCode: 0-男, 1-女 + // genderCode: 0-男, 1-女, -1-未知 final filteredList = _guestList.where((guest) { - final genderCode = guest['genderCode'] as int? ?? 0; + final genderCode = guest['genderCode'] as int? ?? -1; if (_selectedTab == 0) { // 女嘉宾标签页,显示女性(genderCode == 1) + // 注意:如果 genderCode 为 -1(未知),暂时不显示,避免错误分类 return genderCode == 1; } else { // 男嘉宾标签页,显示男性(genderCode == 0) + // 注意:如果 genderCode 为 -1(未知),暂时不显示,避免错误分类 return genderCode == 0; } }).toList(); @@ -228,6 +434,10 @@ class _LiveRoomGuestListDialogState extends State { } Widget _buildGuestItem(Map guest) { + final userId = guest['userId'] as String? ?? ''; + final avatar = guest['avatar'] as String? ?? ''; + final name = guest['name'] as String? ?? ''; + // 根据当前标签页判断是否已有对应性别的嘉宾 final hasGuest = _selectedTab == 0 ? (guest['hasMaleGuest'] as bool? ?? false) @@ -241,29 +451,38 @@ class _LiveRoomGuestListDialogState extends State { // 头像 ClipRRect( borderRadius: BorderRadius.circular(20.w), - child: CachedNetworkImage( - imageUrl: guest['avatar'] as String? ?? '', - width: 40.w, - height: 40.w, - fit: BoxFit.cover, - placeholder: (context, url) => Container( - width: 40.w, - height: 40.w, - color: Colors.grey[300], - child: Center( - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.grey[600], + child: avatar.isNotEmpty + ? CachedNetworkImage( + imageUrl: avatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + placeholder: (context, url) => Container( + width: 40.w, + height: 40.w, + color: Colors.grey[300], + child: Center( + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.grey[600], + ), + ), + ), + errorWidget: (context, url, error) { + return Container( + width: 40.w, + height: 40.w, + color: Colors.grey[300], + child: Icon(Icons.person, size: 24.w), + ); + }, + ) + : Container( + width: 40.w, + height: 40.w, + color: Colors.grey[300], + child: Icon(Icons.person, size: 24.w), ), - ), - ), - errorWidget: (context, url, error) => Container( - width: 40.w, - height: 40.w, - color: Colors.grey[300], - child: Icon(Icons.person, size: 24.w), - ), - ), ), SizedBox(width: 12.w), // 用户信息 @@ -274,7 +493,7 @@ class _LiveRoomGuestListDialogState extends State { Row( children: [ Text( - guest['name'] as String? ?? '', + name.isNotEmpty ? name : userId, style: TextStyle( fontSize: 15.w, fontWeight: FontWeight.w500,