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.
290 lines
8.8 KiB
290 lines
8.8 KiB
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
import 'package:agora_rtm/agora_rtm.dart';
|
|
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
|
|
import 'package:dating_touchme_app/controller/global.dart';
|
|
import 'package:dating_touchme_app/controller/overlay_controller.dart';
|
|
import 'package:dating_touchme_app/model/live/live_chat_message.dart';
|
|
import 'package:dating_touchme_app/pages/discover/live_end_page.dart';
|
|
import 'package:dating_touchme_app/rtc/rtc_manager.dart';
|
|
import 'package:dating_touchme_app/rtc/rtm_manager.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
|
|
/// 直播间聊天消息服务
|
|
/// 负责消息的发送、接收、解析和管理
|
|
class LiveChatMessageService {
|
|
// 单例模式
|
|
static final LiveChatMessageService _instance = LiveChatMessageService._internal();
|
|
factory LiveChatMessageService() => _instance;
|
|
static LiveChatMessageService get instance => _instance;
|
|
|
|
final GetStorage _storage = GetStorage();
|
|
bool _isListenerRegistered = false;
|
|
|
|
// 消息监听回调
|
|
Function(LiveChatMessage message)? onMessageReceived;
|
|
Function(String error)? onMessageError;
|
|
|
|
LiveChatMessageService._internal();
|
|
|
|
/// 注册消息监听器
|
|
void registerMessageListener({
|
|
Function(LiveChatMessage message)? onMessageReceived,
|
|
Function(String error)? onMessageError,
|
|
}) {
|
|
if (_isListenerRegistered) {
|
|
print('⚠️ 消息监听器已注册,跳过重复注册');
|
|
return;
|
|
}
|
|
|
|
this.onMessageReceived = onMessageReceived;
|
|
this.onMessageError = onMessageError;
|
|
|
|
RTMManager.instance.onMessageEvent = (MessageEvent event) {
|
|
_handleIncomingMessage(event);
|
|
};
|
|
|
|
_isListenerRegistered = true;
|
|
print('✅ 消息监听器注册完成');
|
|
}
|
|
|
|
/// 移除消息监听器
|
|
void unregisterMessageListener() {
|
|
RTMManager.instance.onMessageEvent = null;
|
|
_isListenerRegistered = false;
|
|
onMessageReceived = null;
|
|
onMessageError = null;
|
|
print('✅ 消息监听器已移除');
|
|
}
|
|
|
|
/// 处理接收到的消息
|
|
void _handleIncomingMessage(MessageEvent event) async{
|
|
try {
|
|
// 解析消息内容
|
|
final messageText = _parseMessageContent(event.message);
|
|
final messageData = json.decode(messageText) as Map<String, dynamic>;
|
|
print('📥 收到消息: $messageData');
|
|
|
|
// 处理结束直播消息
|
|
if (messageData['type'] == 'end_live') {
|
|
RoomController controller = Get.find<RoomController>();
|
|
await controller.leaveChannel();
|
|
_handleEndLiveMessage();
|
|
return;
|
|
}
|
|
|
|
// 只处理聊天消息类型
|
|
if (messageData['type'] == 'chat_message') {
|
|
final chatMessage = LiveChatMessage.fromJson(messageData);
|
|
onMessageReceived?.call(chatMessage);
|
|
}else{
|
|
RoomController controller = Get.find<RoomController>();
|
|
await controller.receiveRTCMessage(messageData);
|
|
}
|
|
} catch (e, stackTrace) {
|
|
final error = '解析RTM消息失败: $e';
|
|
print('❌ $error');
|
|
print('❌ 堆栈信息: $stackTrace');
|
|
onMessageError?.call(error);
|
|
}
|
|
}
|
|
|
|
/// 处理结束直播消息
|
|
void _handleEndLiveMessage() {
|
|
try {
|
|
// 检查 overlay 是否显示,如果显示则关闭
|
|
if (Get.isRegistered<OverlayController>()) {
|
|
final overlayController = Get.find<OverlayController>();
|
|
if (overlayController.showOverlay.value) {
|
|
overlayController.hide();
|
|
print('✅ 已关闭 overlay 小窗');
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 检查当前是否在 LiveRoomPage,如果是则跳转到 live_end_page
|
|
final currentRoute = Get.currentRoute;
|
|
if (currentRoute == '/LiveRoomPage' || currentRoute.contains('LiveRoomPage')) {
|
|
Get.off(() => const LiveEndPage());
|
|
print('✅ 已跳转到直播结束页面');
|
|
}
|
|
} catch (e) {
|
|
print('❌ 处理结束直播消息失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 解析消息内容(支持 String 和 Uint8List)
|
|
String _parseMessageContent(dynamic message) {
|
|
if (message is String) {
|
|
return message;
|
|
} else if (message is Uint8List) {
|
|
return utf8.decode(message);
|
|
} else {
|
|
return message.toString();
|
|
}
|
|
}
|
|
|
|
/// 构建消息数据
|
|
Map<String, dynamic> _buildMessageData(String content) {
|
|
final userId = _storage.read('userId') ?? GlobalData().userId ?? '';
|
|
final userData = GlobalData().userData;
|
|
final userName = userData?.nickName ?? '用户';
|
|
final avatar = userData?.profilePhoto;
|
|
|
|
return {
|
|
'type': 'chat_message',
|
|
'userId': userId,
|
|
'userName': userName,
|
|
'avatar': avatar,
|
|
'content': content.trim(),
|
|
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
|
};
|
|
}
|
|
|
|
/// 验证消息内容
|
|
String? _validateMessage(String content) {
|
|
final trimmed = content.trim();
|
|
if (trimmed.isEmpty) {
|
|
return '消息内容不能为空';
|
|
}
|
|
if (trimmed.length > 500) {
|
|
return '消息内容不能超过500个字符';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// 检查发送前置条件
|
|
String? _checkSendPreconditions(String channelName) {
|
|
if (!RTMManager.instance.isInitialized) {
|
|
return 'RTM 未初始化,无法发送消息';
|
|
}
|
|
if (!RTMManager.instance.isLoggedIn) {
|
|
return 'RTM 未登录,无法发送消息';
|
|
}
|
|
if (channelName.isEmpty) {
|
|
return '未加入频道,无法发送消息';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// 发送聊天消息
|
|
///
|
|
/// [content] 消息内容
|
|
/// [channelName] 频道名称,如果为空则从 RTCManager 获取
|
|
/// [showToast] 是否显示 Toast 提示,默认为 true
|
|
///
|
|
/// 返回发送结果,true 表示成功,false 表示失败
|
|
Future<MessageSendResult> sendMessage({
|
|
required String content,
|
|
String? channelName,
|
|
bool showToast = true,
|
|
}) async {
|
|
// 验证消息内容
|
|
final validationError = _validateMessage(content);
|
|
if (validationError != null) {
|
|
if (showToast) {
|
|
SmartDialog.showToast(validationError);
|
|
}
|
|
return MessageSendResult.failure(validationError);
|
|
}
|
|
|
|
// 获取频道名称
|
|
final targetChannelName = channelName ??
|
|
RTCManager.instance.currentChannelId ?? '';
|
|
|
|
// 检查前置条件
|
|
final preconditionError = _checkSendPreconditions(targetChannelName);
|
|
if (preconditionError != null) {
|
|
print('❌ $preconditionError');
|
|
if (showToast) {
|
|
SmartDialog.showToast(preconditionError);
|
|
}
|
|
return MessageSendResult.failure(preconditionError);
|
|
}
|
|
|
|
try {
|
|
// 构建消息数据
|
|
final messageData = _buildMessageData(content);
|
|
final messageJson = json.encode(messageData);
|
|
|
|
print('📤 发送消息到频道: $targetChannelName');
|
|
print('📤 消息内容: $messageJson');
|
|
|
|
// 通过 RTM 发送消息
|
|
final success = await RTMManager.instance.publishChannelMessage(
|
|
channelName: targetChannelName,
|
|
message: messageJson,
|
|
);
|
|
|
|
if (success) {
|
|
print('✅ 消息发送成功');
|
|
// 创建消息对象(用于立即显示)
|
|
final chatMessage = LiveChatMessage(
|
|
userId: messageData['userId'] as String,
|
|
userName: messageData['userName'] as String,
|
|
avatar: messageData['avatar'] as String?,
|
|
content: messageData['content'] as String,
|
|
timestamp: messageData['timestamp'] as int,
|
|
);
|
|
return MessageSendResult.success(chatMessage);
|
|
} else {
|
|
final error = '消息发送失败,请重试';
|
|
print('❌ $error');
|
|
if (showToast) {
|
|
SmartDialog.showToast(error);
|
|
}
|
|
return MessageSendResult.failure(error);
|
|
}
|
|
} catch (e, stackTrace) {
|
|
final error = '发送消息异常: $e';
|
|
print('❌ $error');
|
|
print('❌ 堆栈信息: $stackTrace');
|
|
if (showToast) {
|
|
SmartDialog.showToast(error);
|
|
}
|
|
return MessageSendResult.failure(error);
|
|
}
|
|
}
|
|
|
|
/// 创建消息对象(用于本地显示)
|
|
LiveChatMessage createLocalMessage(String content) {
|
|
final messageData = _buildMessageData(content);
|
|
return LiveChatMessage(
|
|
userId: messageData['userId'] as String,
|
|
userName: messageData['userName'] as String,
|
|
avatar: messageData['avatar'] as String?,
|
|
content: messageData['content'] as String,
|
|
timestamp: messageData['timestamp'] as int,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 消息发送结果
|
|
class MessageSendResult {
|
|
final bool success;
|
|
final LiveChatMessage? message;
|
|
final String? error;
|
|
|
|
MessageSendResult._({
|
|
required this.success,
|
|
this.message,
|
|
this.error,
|
|
});
|
|
|
|
factory MessageSendResult.success(LiveChatMessage message) {
|
|
return MessageSendResult._(
|
|
success: true,
|
|
message: message,
|
|
);
|
|
}
|
|
|
|
factory MessageSendResult.failure(String error) {
|
|
return MessageSendResult._(
|
|
success: false,
|
|
error: error,
|
|
);
|
|
}
|
|
}
|
|
|