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.
239 lines
7.2 KiB
239 lines
7.2 KiB
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
|
import 'package:dating_touchme_app/model/live/live_chat_message.dart';
|
|
import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
|
|
import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart';
|
|
import 'package:dating_touchme_app/network/network_service.dart';
|
|
import 'package:dating_touchme_app/rtc/rtc_manager.dart';
|
|
import 'package:dating_touchme_app/service/live_chat_message_service.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
// 当前角色
|
|
enum CurrentRole{
|
|
broadcaster,//主持
|
|
maleAudience,//男嘉宾
|
|
femaleAudience,//女嘉宾
|
|
audience,//观众
|
|
normalUser, //普通用户
|
|
}
|
|
|
|
/// 直播房间相关控制器
|
|
class RoomController extends GetxController {
|
|
RoomController({NetworkService? networkService})
|
|
: _networkService = networkService ?? Get.find<NetworkService>();
|
|
|
|
final NetworkService _networkService;
|
|
CurrentRole currentRole = CurrentRole.normalUser;
|
|
bool isLive = false;
|
|
/// 当前频道信息
|
|
final Rxn<RtcChannelData> rtcChannel = Rxn<RtcChannelData>();
|
|
final Rxn<RtcChannelDetail> rtcChannelDetail = Rxn<RtcChannelDetail>();
|
|
|
|
/// 聊天消息列表
|
|
final RxList<LiveChatMessage> chatMessages = <LiveChatMessage>[].obs;
|
|
|
|
/// 消息服务实例
|
|
final LiveChatMessageService _messageService =
|
|
LiveChatMessageService.instance;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
// 注册消息监听
|
|
_registerMessageListener();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
super.onClose();
|
|
// 移除消息监听
|
|
_messageService.unregisterMessageListener();
|
|
}
|
|
|
|
/// 注册消息监听
|
|
void _registerMessageListener() {
|
|
_messageService.registerMessageListener(
|
|
onMessageReceived: (message) {
|
|
_addMessage(message);
|
|
},
|
|
onMessageError: (error) {
|
|
print('❌ 消息处理错误: $error');
|
|
},
|
|
);
|
|
}
|
|
|
|
/// 添加消息到列表(带去重和数量限制)
|
|
void _addMessage(LiveChatMessage message) {
|
|
// 去重:检查是否已存在相同的消息(基于 userId + content + timestamp)
|
|
final exists = chatMessages.any(
|
|
(m) =>
|
|
m.userId == message.userId &&
|
|
m.content == message.content &&
|
|
(m.timestamp - message.timestamp).abs() < 1000,
|
|
); // 1秒内的相同消息视为重复
|
|
|
|
if (exists) {
|
|
print('⚠️ 消息已存在,跳过添加');
|
|
return;
|
|
}
|
|
|
|
chatMessages.add(message);
|
|
print('✅ 消息已添加到列表,当前消息数: ${chatMessages.length}');
|
|
|
|
// 限制消息数量,最多保留100条
|
|
if (chatMessages.length > 300) {
|
|
chatMessages.removeAt(0);
|
|
print('📝 消息列表已满,移除最旧的消息');
|
|
}
|
|
}
|
|
|
|
/// 调用接口创建 RTC 频道
|
|
Future<void> createRtcChannel() async {
|
|
final granted = await _ensureRtcPermissions();
|
|
if (!granted) return;
|
|
|
|
try {
|
|
final response = await _networkService.rtcApi.createRtcChannel();
|
|
final base = response.data;
|
|
if (base.isSuccess && base.data != null) {
|
|
rtcChannel.value = base.data;
|
|
currentRole = CurrentRole.broadcaster;
|
|
isLive = true;
|
|
await _joinRtcChannel(
|
|
base.data!.token,
|
|
base.data!.channelId,
|
|
base.data!.uid,
|
|
ClientRoleType.clientRoleBroadcaster,
|
|
);
|
|
} else {
|
|
final message = base.message.isNotEmpty ? base.message : '创建频道失败';
|
|
SmartDialog.showToast(message);
|
|
}
|
|
} catch (e) {
|
|
SmartDialog.showToast('创建频道异常:$e');
|
|
}
|
|
}
|
|
|
|
Future<void> joinChannel(String channelName) async {
|
|
try {
|
|
final response = await _networkService.rtcApi.getSwRtcToken(channelName);
|
|
final base = response.data;
|
|
if (base.isSuccess && base.data != null) {
|
|
rtcChannel.value = base.data;
|
|
currentRole = CurrentRole.normalUser;
|
|
await _joinRtcChannel(
|
|
base.data!.token,
|
|
channelName,
|
|
base.data!.uid,
|
|
ClientRoleType.clientRoleAudience,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
SmartDialog.showToast('加入频道异常:$e');
|
|
}
|
|
}
|
|
|
|
Future<void> _joinRtcChannel(
|
|
String token,
|
|
String channelName,
|
|
int uid,
|
|
ClientRoleType roleType,
|
|
) async {
|
|
try {
|
|
await _fetchRtcChannelDetail(channelName);
|
|
await RTCManager.instance.joinChannel(
|
|
token: token,
|
|
channelId: channelName,
|
|
uid: uid,
|
|
role: roleType,
|
|
);
|
|
} catch (e) {
|
|
SmartDialog.showToast('加入频道失败:$e');
|
|
}
|
|
}
|
|
|
|
Future<void> joinChat(CurrentRole role) async {
|
|
final data = {
|
|
'channelId': rtcChannel.value?.channelId,
|
|
'seatNumber': role == CurrentRole.maleAudience ? 1 : 2,
|
|
'isMicrophoneOn': role != CurrentRole.normalUser ? true : false,
|
|
'isVideoOn': role == CurrentRole.maleAudience || role == CurrentRole.femaleAudience ? true : false,
|
|
};
|
|
final response = await _networkService.rtcApi.connectRtcChannel(data);
|
|
if (!response.data.isSuccess) {
|
|
SmartDialog.showToast(response.data.message);
|
|
return;
|
|
}
|
|
currentRole = role;
|
|
if(role == CurrentRole.maleAudience || role == CurrentRole.femaleAudience){
|
|
await RTCManager.instance.publishVideo(role);
|
|
}else{
|
|
await RTCManager.instance.publishAudio();
|
|
}
|
|
isLive = true;
|
|
}
|
|
|
|
Future<void> leaveChat() async {
|
|
final data = {
|
|
'channelId': rtcChannel.value?.channelId
|
|
};
|
|
final response = await _networkService.rtcApi.disconnectRtcChannel(data);
|
|
if(response.data.isSuccess){
|
|
await RTCManager.instance.unpublish();
|
|
}
|
|
}
|
|
|
|
Future<void> _fetchRtcChannelDetail(String channelName) async {
|
|
try {
|
|
final response = await _networkService.rtcApi.getRtcChannelDetail(
|
|
channelName,
|
|
);
|
|
final base = response.data;
|
|
if (base.isSuccess && base.data != null) {
|
|
rtcChannelDetail.value = base.data;
|
|
}
|
|
} catch (e) {
|
|
print('获取 RTC 频道详情失败:$e');
|
|
}
|
|
}
|
|
|
|
/// 发送公屏消息
|
|
Future<void> sendChatMessage(String content) async {
|
|
final channelName =
|
|
rtcChannel.value?.channelId ?? RTCManager.instance.currentChannelId;
|
|
|
|
final result = await _messageService.sendMessage(
|
|
content: content,
|
|
channelName: channelName,
|
|
);
|
|
|
|
// 如果发送成功,立即添加到本地列表(优化体验,避免等待 RTM 回调)
|
|
if (result.success && result.message != null) {
|
|
_addMessage(result.message!);
|
|
}
|
|
}
|
|
|
|
Future<bool> _ensureRtcPermissions() async {
|
|
final statuses = await [Permission.camera, Permission.microphone].request();
|
|
final allGranted = statuses.values.every((status) => status.isGranted);
|
|
if (allGranted) {
|
|
return true;
|
|
}
|
|
|
|
final permanentlyDenied = statuses.values.any(
|
|
(status) => status.isPermanentlyDenied,
|
|
);
|
|
if (permanentlyDenied) {
|
|
SmartDialog.showToast('请在系统设置中开启摄像头和麦克风权限');
|
|
await openAppSettings();
|
|
} else {
|
|
SmartDialog.showToast('请允许摄像头和麦克风权限以进入房间');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Future<void> leaveChannel() async {
|
|
await RTCManager.instance.leaveChannel();
|
|
}
|
|
}
|