flutter/lib/IM/im_service.dart
2025-08-21 10:50:38 +08:00

675 lines
22 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:logger/logger.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/controller/tab_bar_controller.dart';
import 'package:loopin/IM/global_badge.dart';
import 'package:loopin/IM/im_core.dart';
import 'package:loopin/IM/im_friend_listeners.dart';
import 'package:loopin/IM/im_message_listeners.dart';
import 'package:loopin/IM/im_result.dart';
import 'package:loopin/IM/push_service.dart';
import 'package:loopin/models/conversation_view_model.dart';
import 'package:loopin/utils/wxsdk.dart';
import 'package:tencent_cloud_chat_sdk/enum/friend_application_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/friend_response_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_filter.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_operation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_operation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_conversation_manager.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_friendship_manager.dart';
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_message_manager.dart';
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
final logger = Logger();
/// IM 服务封装类
class ImService {
ImService._();
static final instance = ImService._();
/// 登录
Future<ImResult> login({
required String userID,
required String userSig,
}) async {
final res = await TencentImSDKPlugin.v2TIMManager.login(
userID: userID,
userSig: userSig,
);
final result = ImResult(
success: res.code == 0,
code: res.code,
desc: res.desc,
);
if (result.success) {
logger.i("IM 登录成功:$userID");
// 初始化push服务
PushService().initPush(
sdkAppId: 1600080789,
appKey: 'vkFpe55aYqfV7Sk5uGaoxhEstJ3tcI9dquk7JwG1GloDSLD2HeMWeQweWWXgNlhC',
);
// 初始化微信 SDK
await Wxsdk.init();
// 注册用户信息(基本信息+自定义信息)
Get.put(ImUserInfoController(), permanent: true);
// 登录成功后注册高级消息监听器
final messageService = ImMessageListenerService();
Get.put<ImMessageListenerService>(messageService, permanent: true);
await messageService.init();
// 注册关系链监听器
final friendListener = ImFriendListeners();
Get.put<ImFriendListeners>(friendListener, permanent: true);
friendListener.register();
/// 注册消息未读数监听器
Get.put(GlobalBadge(), permanent: true);
} else {
logger.i("IM 登录失败:${result.code} - ${result.desc}");
Get.snackbar(
'登录失败',
'${result.code} - ${result.desc}',
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
}
return result;
}
/// 登出
Future<ImResult> logout() async {
final res = await TencentImSDKPlugin.v2TIMManager.logout();
if (res.code == 0) {
/// 清理用户信息
Get.delete<ImUserInfoController>(force: true);
/// 移出消息监听器
Get.find<ImMessageListenerService>().onClose();
Get.delete<ImMessageListenerService>(force: true);
/// 移出关系链监听器
Get.find<ImFriendListeners>().unregister();
Get.delete<ImFriendListeners>(force: true);
/// 清理tabbar
Get.find<TabBarController>().badgeMap.clear();
/// 清理会话列表数据
Get.find<ChatController>().initChatData();
/// 移出未读消息监听器
Get.find<GlobalBadge>().onClose();
Get.delete<GlobalBadge>(force: true);
/// 移除推送服务
PushService.unInitPush();
/// 反初始化
ImCore.unInit();
}
return ImResult.wrapNoData(res);
}
/// 设置会话自定义属性
Future<ImResult<List<V2TimConversationOperationResult>>> setConversationCustomData({
required String customData,
required List<String> conversationIDList,
}) async {
final res = await TIMConversationManager.instance.setConversationCustomData(customData: customData, conversationIDList: conversationIDList);
return ImResult.wrap(res);
}
/// 获取符合过滤条件的未读消息总数
Future<ImResult<int>> getUnreadMessageCountByFilter({
required V2TimConversationFilter filter,
}) async {
final res = await TIMConversationManager.instance.getUnreadMessageCountByFilter(
filter: filter,
);
return ImResult.wrap(res);
}
/// 删除会话
Future<ImResult<void>> deleteConversation({
required String conversationID,
}) async {
final res = await TIMConversationManager.instance.deleteConversation(conversationID: conversationID);
return ImResult.wrapNoData(res);
}
/// 创建会话分组
Future<ImResult<List<V2TimConversationOperationResult>>> createConversationGroup({
required String groupName,
required List<String> conversationIDList,
}) async {
final res = await TIMConversationManager.instance.createConversationGroup(
groupName: groupName,
conversationIDList: conversationIDList,
);
return ImResult.wrap(res);
}
/// 获取会话分组列表
Future<ImResult<List<String>>> getConversationGroupList() async {
final res = await TIMConversationManager.instance.getConversationGroupList();
return ImResult.wrap(res);
}
/// 将会话添加到分组
Future<ImResult<List<V2TimConversationOperationResult>>> addConversationsToGroup({
required String groupName,
required List<String> conversationIDList,
}) async {
final res = await TIMConversationManager.instance.addConversationsToGroup(
groupName: groupName,
conversationIDList: conversationIDList,
);
return ImResult.wrap(res);
}
///将会话移除分组
Future<ImResult<List<V2TimConversationOperationResult>>> deleteConversationsFromGroup({
required String groupName,
required List<String> conversationIDList,
}) async {
final res = await TIMConversationManager.instance.deleteConversationsFromGroup(
groupName: groupName,
conversationIDList: conversationIDList,
);
return ImResult.wrap(res);
}
/// 高级查询会话列表
Future<ImResult<V2TimConversationResult>> getConversationListByFilter({
required V2TimConversationFilter filter,
required int nextSeq,
int count = 20,
}) async {
final res = await TIMConversationManager.instance.getConversationListByFilter(
filter: filter,
nextSeq: nextSeq,
count: count,
);
return ImResult.wrap(res);
}
/// 查询会话记录
Future<ImResult> getConversationList(String nextSeq, int count) async {
// final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getConversationList(nextSeq: nextSeq, count: count);
final res = await getConvData(nextSeq, count);
if (res.success == false) {
return ImResult(
success: false,
code: res.code,
desc: res.desc,
);
}
final convList = res.data?.conversationList ?? [];
final userIDList = <String>[];
final groupIDList = <String>[];
// 提前收集所有需要批量查询的 userID 和 groupID
for (var conv in convList) {
if ((conv.faceUrl == null || conv.faceUrl!.isEmpty)) {
if (conv.userID != null) {
userIDList.add(conv.userID!);
} else if (conv.groupID != null) {
groupIDList.add(conv.groupID!);
}
}
}
Map<String, String?> userFaceUrlMap = {};
Map<String, String?> groupFaceUrlMap = {};
String isCustomAdmin = '0';
if (userIDList.isNotEmpty) {
final userRes = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: userIDList);
if (userRes.code == 0) {
for (var user in userRes.data!) {
final userId = user.userID ?? '';
userFaceUrlMap[userId] = user.faceUrl;
// 读取管理员标识
final customInfo = user.customInfo;
if (customInfo != null) {
isCustomAdmin = customInfo['admin'] ?? '0';
}
}
}
}
if (groupIDList.isNotEmpty) {
final groupRes = await TencentImSDKPlugin.v2TIMManager.getGroupManager().getGroupsInfo(groupIDList: groupIDList);
if (groupRes.code == 0) {
for (var groupResult in groupRes.data!) {
final info = groupResult.groupInfo;
if (info != null) {
groupFaceUrlMap[info.groupID] = info.faceUrl;
}
}
}
}
final viewList = convList.map((conv) {
String? faceUrl = conv.faceUrl;
// 如果 faceUrl 本身为空,尝试从查到的 map 中取值
if (faceUrl == null || faceUrl.isEmpty) {
if (conv.userID != null) {
faceUrl = userFaceUrlMap[conv.userID!];
} else if (conv.groupID != null) {
faceUrl = groupFaceUrlMap[conv.groupID!];
}
}
return ConversationViewModel(conversation: conv, faceUrl: faceUrl, isCustomAdmin: isCustomAdmin);
}).toList();
// 筛选数据,过滤掉陌生人消息
viewList.removeWhere((conv) {
final special = conv.conversation.conversationGroupList ?? [];
return special.contains('noFriend');
});
ChatController chatcontroller = Get.find<ChatController>();
logger.e('新的分页内容:${res.data!.toLogString()},控制器中的:${chatcontroller.nextSeq.value}');
String newNextSeq = res.data?.nextSeq ?? '0';
bool isEnd = res.data?.isFinished ?? true;
if (isEnd) {
//没数据了,关闭拉取;
chatcontroller.isFinished.value = isEnd;
} else {
// 没拉完,记录游标
chatcontroller.nextSeq.value = newNextSeq;
}
// 更新分页
chatcontroller.nextSeq.value = res.data!.nextSeq!;
if (res.data!.isFinished == false) {
if (viewList.length < 20) {
// 递归补偿拉取
final nextRes = await getConversationList(
res.data!.nextSeq!,
count,
);
if (nextRes.success && nextRes.data != null) {
viewList.addAll(nextRes.data as List<ConversationViewModel>);
}
}
}
return ImResult(
success: res.code == 0,
code: res.code,
desc: res.desc,
data: viewList,
);
}
///获取所有会话数据
Future<ImResult<V2TimConversationResult>> getConvData(String nextSeq, int count) async {
final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getConversationList(nextSeq: nextSeq, count: count);
// for (var element in res.data!.conversationList) {
// logger.e('所有的会话数据:${element.toJson()}');
// }
return ImResult.wrap(res);
}
///获取指定会话
Future<ImResult> getConversation({
required String conversationID,
}) async {
final res = await TIMConversationManager.instance.getConversation(conversationID: conversationID); //c2c_conversationID,group_conversationID
return ImResult<V2TimConversation?>(
success: res.code == 0 && res.data != null,
code: res.code,
desc: res.desc,
data: res.data,
);
}
/// 获取消息
Future<ImResult<List<V2TimMessage>>> findMessages({
required List<String> messageIDList,
}) async {
final res = await TIMMessageManager.instance.findMessages(messageIDList: messageIDList);
return ImResult.wrap(res);
}
/// 修改消息
Future<ImResult<V2TimMessageChangeInfo>> modifyMessage({
required V2TimMessage message,
}) async {
final res = await TIMMessageManager.instance.modifyMessage(message: message);
return ImResult.wrap(res);
}
/// 获取聊天记录 如果是群聊传 groupID单聊传 userID二选一
Future<ImResult<List<V2TimMessage>>> getHistoryMessageList({
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
String? userID,
String? groupID,
int? lastMsgSeq,
int count = 10,
V2TimMessage? lastMsg,
List<int>? messageTypeList,
List<int>? messageSeqList,
int? timeBegin,
int? timePeriod,
}) async {
if ((userID == null || userID.isEmpty) && (groupID == null || groupID.isEmpty)) {
return ImResult(
success: false,
code: -1,
desc: 'userID 或 groupID 必须至少传入一个',
data: [],
);
}
final isC2C = userID != null && groupID == null;
final manager = TencentImSDKPlugin.v2TIMManager.getMessageManager();
V2TimValueCallback<List<V2TimMessage>> res;
if (!isC2C && lastMsg == null && lastMsgSeq != null) {
// 群聊
res = await manager.getHistoryMessageList(
getType: getType,
userID: userID,
groupID: groupID,
count: count,
lastMsgSeq: lastMsgSeq, //群聊用,包含最后一条msg
messageTypeList: messageTypeList,
messageSeqList: messageSeqList,
timeBegin: timeBegin,
timePeriod: timePeriod,
);
} else {
// 单聊
res = await manager.getHistoryMessageList(
getType: getType,
userID: userID,
groupID: groupID,
count: count,
lastMsg: lastMsg, // 单聊用不包含最后一条msg
messageTypeList: messageTypeList,
messageSeqList: messageSeqList,
timeBegin: timeBegin,
timePeriod: timePeriod,
);
}
return ImResult<List<V2TimMessage>>(
success: res.code == 0,
code: res.code,
desc: res.desc,
data: res.data ?? [],
);
}
///
/// 清理指定单聊会话的未读数
///
Future<ImResult> clearConversationUnreadCount({
required String conversationID,
// 0表示全部清除
int cleanTimestamp = 0,
int cleanSequence = 0,
}) async {
final res = await TIMConversationManager.instance.cleanConversationUnreadMessageCount(
conversationID: conversationID,
cleanTimestamp: cleanTimestamp, // 单聊生效
cleanSequence: cleanSequence, // 群聊生效
);
return ImResult.wrapNoData(res);
}
/// 获取自己的userId
Future<ImResult> selfUserId() async {
V2TimValueCallback<String> self = await TencentImSDKPlugin.v2TIMManager.getLoginUser();
String? userId = self.data;
return ImResult(
success: self.code == 0,
code: self.code,
desc: self.desc,
data: userId,
);
}
/// 查询当前登录用户的个人信息
Future<ImResult> selfInfo() async {
// 获取当前登录的用户 ID
final idRes = await selfUserId();
// 获取用户信息
V2TimValueCallback<List<V2TimUserFullInfo>> res = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: [idRes.data]);
return ImResult(
success: res.code == 0,
code: res.code,
desc: res.desc,
data: res.data?.isNotEmpty == true ? res.data!.first : null,
);
}
/// 查询其他人的信息
Future<ImResult> otherInfo(id) async {
// 获取用户信息
V2TimValueCallback<List<V2TimUserFullInfo>> res = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: [id]);
return ImResult(
success: res.code == 0,
code: res.code,
desc: res.desc,
data: res.data?.isNotEmpty == true ? res.data!.first : null,
);
}
/// 设置个人资料
Future<ImResult> setSelfInfo({
required V2TimUserFullInfo userFullInfo,
}) async {
final res = await TencentImSDKPlugin.v2TIMManager.setSelfInfo(
userFullInfo: userFullInfo,
);
return ImResult.wrapNoData(res);
}
/// 检查是否是好友(双向或单向)
Future<ImResult> isMyFriend(String userID, FriendTypeEnum checkType) async {
final res = await TIMFriendshipManager.instance.checkFriend(
userIDList: [userID],
checkType: checkType, //V2TIM_FRIEND_TYPE_BOTH V2TIM_FRIEND_TYPE_SINGLE
);
if (res.code == 0 && res.data != null && res.data!.isNotEmpty) {
final resultType = res.data!.first.resultType;
final isFriend = resultType == 3; //0=无, 1=单向, 2=我在对方列表3=双向
return ImResult(
success: true,
desc: res.desc,
code: res.code,
data: isFriend,
);
} else {
return ImResult(
success: false,
code: res.code,
desc: res.desc,
data: false,
);
}
}
/// 添加好友
Future<ImResult<V2TimFriendOperationResult>> addFriend({
required String userID,
String? remark,
String? friendGroup,
String? addWording,
String? addSource,
required FriendTypeEnum addType,
}) async {
final res = await TIMFriendshipManager.instance.addFriend(
userID: userID,
remark: remark,
friendGroup: friendGroup,
addWording: addWording,
addSource: addSource,
addType: addType,
);
return ImResult.wrap(res);
}
///接受好友申请
Future<ImResult<V2TimFriendOperationResult>> acceptFriendApplication({
required FriendResponseTypeEnum responseType,
required FriendApplicationTypeEnum type, // V2TIM_FRIEND_ACCEPT_AGREE同意添加单向好友V2TIM_FRIEND_ACCEPT_AGREE_AND_ADD同意并添加为双向好友
required String userID,
}) async {
final res = await TIMFriendshipManager.instance.acceptFriendApplication(
responseType: responseType,
type: type,
userID: userID,
);
return ImResult.wrap(res);
}
/// 拉黑
Future<ImResult<List<V2TimFriendOperationResult>>> addToBlackList({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.addToBlackList(userIDList: userIDList);
return ImResult.wrap(res);
}
/// 取消拉黑
Future<ImResult<List<V2TimFriendOperationResult>>> deleteFromBlackList({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.deleteFromBlackList(userIDList: userIDList);
return ImResult.wrap(res);
}
///获取好友列表
Future<ImResult<List<V2TimFriendInfo>>> getFriendList() async {
final res = await TIMFriendshipManager.instance.getFriendList();
return ImResult.wrap(res);
}
/// set好友备注
Future<ImResult> setFriendInfo({
required String userID,
String? friendRemark,
Map<String, String>? friendCustomInfo,
}) async {
late V2TimCallback res;
res = await TIMFriendshipManager.instance.setFriendInfo(
userID: userID,
friendRemark: friendRemark,
friendCustomInfo: friendCustomInfo,
);
return ImResult.wrapNoData(res);
}
/// 获取好友信息
Future<ImResult<List<V2TimFriendInfoResult>>> getFriendInfo({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.getFriendsInfo(userIDList: userIDList);
return ImResult.wrap(res);
}
///关注
Future<ImResult<List<V2TimFollowOperationResult>>> followUser({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.followUser(
userIDList: userIDList,
);
return ImResult.wrap(res);
}
///取关
Future<ImResult<List<V2TimFollowOperationResult>>> unfollowUser({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.unfollowUser(
userIDList: userIDList,
);
return ImResult.wrap(res);
}
/// check关注的类型
/// 0不是好友也没有关注
/// 1你关注了对方单向
/// 2对方关注了你单向
/// 3互相关注双向好友
Future<ImResult<List<V2TimFollowTypeCheckResult>>> checkFollowType({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.checkFollowType(
userIDList: userIDList,
);
return ImResult.wrap(res);
}
///获取指定用户的 关注/粉丝/互关 数量信息
Future<ImResult<List<V2TimFollowInfo>>> getUserFollowInfo({
required List<String> userIDList,
}) async {
final res = await TIMFriendshipManager.instance.getUserFollowInfo(userIDList: userIDList);
return ImResult.wrap(res);
}
/// 获取双向关注列表(互关好友)
/// [nextCursor] 分页游标,首次传空字符串
Future<ImResult<V2TimUserInfoResult>> getMutualFollowersList({
required String nextCursor,
}) async {
final res = await TIMFriendshipManager.instance.getMutualFollowersList(
nextCursor: nextCursor,
);
return ImResult.wrap(res);
}
/// 获取我的粉丝列表
/// [nextCursor] 分页游标,首次传空字符串
Future<ImResult<V2TimUserInfoResult>> getMyFollowersList({
required String nextCursor,
}) async {
final res = await TIMFriendshipManager.instance.getMyFollowersList(
nextCursor: nextCursor,
);
return ImResult.wrap(res);
}
}