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.
 
 
 
 
 

209 lines
5.8 KiB

import 'package:agora_rtm/agora_rtm.dart';
import 'package:agora_token_generator/agora_token_generator.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../model/rtc/rtc_channel_data.dart';
import '../network/network_service.dart';
import '../network/rtc_api.dart';
/// 声网 RTM 管理器,聚合常用的客户端与 StreamChannel 操作
class RTMManager {
RTMManager._internal();
static final RTMManager _instance = RTMManager._internal();
factory RTMManager() => _instance;
static RTMManager get instance => _instance;
RtmClient? _client;
bool _isInitialized = false;
bool _isLoggedIn = false;
String? _currentAppId;
String? _currentUserId;
late NetworkService _networkService;
final Map<String, StreamChannel> _streamChannels = {};
/// 监听 link state
void Function(LinkStateEvent event)? onLinkStateEvent;
/// 监听消息事件(频道 / 主题)
void Function(MessageEvent event)? onMessageEvent;
/// 监听 presence 事件
void Function(PresenceEvent event)? onPresenceEvent;
/// 监听 topic 事件
void Function(TopicEvent event)? onTopicEvent;
/// 监听 lock 事件
void Function(LockEvent event)? onLockEvent;
/// 监听 storage 事件
void Function(StorageEvent event)? onStorageEvent;
/// 监听 token 事件
void Function(TokenEvent event)? onTokenEvent;
/// 统一错误通知
void Function(RtmStatus status)? onOperationError;
/// 创建/初始化 RTM Client
Future<bool> initialize({
required String appId,
required String userId,
RtmConfig? config,
}) async {
if (_isInitialized &&
_client != null &&
_currentAppId == appId &&
_currentUserId == userId) {
return true;
}
_networkService = Get.find<NetworkService>();
await dispose();
final (status, client) = await RTM(appId, userId, config: config);
print('RTM初始化成功');
if (status.error) {
onOperationError?.call(status);
return false;
}
_client = client;
_currentAppId = appId;
_currentUserId = userId;
_isInitialized = true;
_registerClientListeners();
final response = await _networkService.rtcApi.getSwRtmToken();
// 处理响应
if (response.data.isSuccess) {
await login(response.data.data?.token ?? '');
} else {
SmartDialog.showToast(response.data.message);
}
return true;
}
/// 登录 RTM
Future<bool> login(String token) async {
print('RTM TOKEN:$token');
_ensureInitialized();
GetStorage storage = GetStorage();
String userId = storage.read('userId') ?? '';
print('RTM userId:$userId');
String tokens = RtmTokenBuilder.buildToken(
appId: '4c2ea9dcb4c5440593a418df0fdd512d',
appCertificate: '16f34b45181a4fae8acdb1a28762fcfa',
userId: userId,
tokenExpireSeconds: 3600,
);
print('RTM TOKEN:$tokens');
final (status, _) = await _client!.login(tokens);
final ok = _handleStatus(status);
print(ok ? 'RTM 登录成功' : 'RTM 登录失败');
if (ok) {
_isLoggedIn = true;
}
return ok;
}
/// 创建频道
Future<bool> subscribe(String channelName) async {
_ensureInitialized();
final (status, _) = await _client!.subscribe(channelName);
print(!status.error ? 'RTM 订阅成功' : 'RTM 订阅失败');
return _handleStatus(status);
}
/// 取消订阅频道
Future<bool> unsubscribe(String channelName) async {
_ensureInitialized();
final (status, _) = await _client!.unsubscribe(channelName);
return _handleStatus(status);
}
/// 登出 RTM
Future<void> logout() async {
if (!_isInitialized || _client == null || !_isLoggedIn) return;
final (status, _) = await _client!.logout();
_handleStatus(status);
_isLoggedIn = false;
}
/// 刷新 Token
Future<bool> renewToken(String token) async {
_ensureInitialized();
final (status, _) = await _client!.renewToken(token);
return _handleStatus(status);
}
/// 发布频道文本消息
Future<bool> publishChannelMessage({
required String channelName,
required String message,
RtmChannelType channelType = RtmChannelType.message,
String? customType,
bool storeInHistory = false,
}) async {
_ensureInitialized();
final (status, _) = await _client!.publish(
channelName,
message,
channelType: channelType,
customType: customType,
storeInHistory: storeInHistory,
);
return _handleStatus(status);
}
/// 释放 RTM Client
Future<void> dispose() async {
if (_client != null && _isLoggedIn) {
final (status, _) = await _client!.logout();
_handleStatus(status);
}
await _client?.release();
_client = null;
_isInitialized = false;
_isLoggedIn = false;
_currentAppId = null;
_currentUserId = null;
_streamChannels.clear();
}
bool get isInitialized => _isInitialized;
bool get isLoggedIn => _isLoggedIn;
String? get currentUserId => _currentUserId;
Iterable<String> get joinedStreamChannels => _streamChannels.keys;
void _registerClientListeners() {
if (_client == null) return;
_client!.addListener(
linkState: (event) => onLinkStateEvent?.call(event),
message: (event) => onMessageEvent?.call(event),
presence: (event) => onPresenceEvent?.call(event),
topic: (event) => onTopicEvent?.call(event),
lock: (event) => onLockEvent?.call(event),
storage: (event) => onStorageEvent?.call(event),
token: (event) => onTokenEvent?.call(event),
);
}
bool _handleStatus(RtmStatus status) {
if (status.error) {
onOperationError?.call(status);
return false;
}
return true;
}
void _ensureInitialized() {
if (!_isInitialized || _client == null) {
throw Exception('RTM Client 未初始化,请先调用 initialize');
}
}
}