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.
26 KiB
26 KiB
核心组件交接文档
本文档详细说明了项目中5个核心控制器/管理器的功能、使用方法和注意事项。
目录
- RoomController - 直播房间控制器
- IMManager - 即时通讯管理器
- RTCManager - 实时音视频管理器
- RTMManager - 实时消息管理器
- ChatController - 聊天控制器
RoomController - 直播房间控制器
文件位置
lib/controller/discover/room_controller.dart
功能概述
负责管理直播房间相关的所有功能,包括:
- 创建/加入直播频道
- 管理直播角色(主持人、男嘉宾、女嘉宾、观众)
- 处理直播间聊天消息
- 管理礼物赠送
- 管理连麦功能
- 管理玫瑰余额
核心属性
// 当前角色
CurrentRole currentRole = CurrentRole.normalUser;
// 是否正在直播
var isLive = false.obs;
// 当前频道信息
final Rxn<RtcChannelData> rtcChannel = Rxn<RtcChannelData>();
final Rxn<RtcChannelDetail> rtcChannelDetail = Rxn<RtcChannelDetail>();
// 聊天消息列表
final RxList<LiveChatMessage> chatMessages = <LiveChatMessage>[].obs;
// 礼物产品列表
final RxList<GiftProductModel> giftProducts = <GiftProductModel>[].obs;
// 玫瑰数量
final RxInt roseCount = 0.obs;
主要方法
1. 创建直播频道
Future<void> createRtcChannel() async
- 功能: 创建新的直播频道,主持人角色
- 前置条件:
- 不能正在直播中
- 不能正在通话中(会检查 CallController)
- 需要摄像头和麦克风权限
- 流程:
- 检查权限
- 调用 API 创建频道
- 设置角色为 broadcaster
- 加入 RTC 频道
- 跳转到直播页面
2. 加入直播频道
Future<void> joinChannel(String channelName) async
- 功能: 作为观众加入他人的直播频道
- 参数:
channelName- 频道ID - 前置条件: 不能正在通话中
3. 上麦(加入聊天)
Future<void> joinChat(CurrentRole role) async
- 功能: 上麦参与直播聊天
- 参数:
role- 角色类型(男嘉宾/女嘉宾/普通观众) - 权限要求:
- 男/女嘉宾:需要摄像头和麦克风权限
- 普通观众:只需要麦克风权限
- 流程:
- 检查权限
- 调用连接 RTC 频道接口
- 根据角色发布音视频流
- 刷新连麦卡片数据
4. 下麦(离开聊天)
Future<void> leaveChat() async
- 功能: 下麦,停止发布音视频流
- 流程:
- 调用断开 RTC 频道连接接口
- 停止发布音视频
- 更新频道详情(清空对应角色信息)
5. 离开频道
Future<void> leaveChannel() async
- 功能: 完全离开直播频道
- 流程:
- 如果是主持人:销毁频道并发送结束直播消息
- 如果是嘉宾:断开连接
- 清理所有状态
6. 发送礼物
Future<void> sendGift({
required GiftProductModel gift,
required int targetUserId,
required int type,
}) async
- 功能: 在直播间赠送礼物
- 参数:
gift: 礼物产品模型targetUserId: 目标用户IDtype: 类型(1=送礼,2=添加好友)
- 流程:
- 调用消费接口
- 检查玫瑰余额(不足时弹出充值弹框)
- 刷新玫瑰数量
- 添加到 SVGA 动画播放队列
- 发送 RTM 消息通知其他用户
- 在公屏显示礼物消息
7. 发送公屏消息
Future<void> sendChatMessage(String content) async
- 功能: 在直播间发送文字消息到公屏
8. 获取虚拟账户(玫瑰余额)
Future<void> getVirtualAccount() async
- 功能: 刷新用户的玫瑰余额
- 使用场景: 充值成功后、消费后需要刷新余额时调用
9. 获取频道详情
Future<void> fetchRtcChannelDetail(String channelName) async
- 功能: 获取直播频道的详细信息(主持人、嘉宾信息等)
使用示例
// 获取 RoomController 实例
final roomController = Get.find<RoomController>();
// 创建直播频道
await roomController.createRtcChannel();
// 监听消息列表
Obx(() => ListView.builder(
itemCount: roomController.chatMessages.length,
itemBuilder: (context, index) {
final message = roomController.chatMessages[index];
return Text(message.content);
},
));
// 发送礼物
await roomController.sendGift(
gift: giftProduct,
targetUserId: targetUserId,
type: 1,
);
注意事项
-
生命周期管理:
- 在
onInit()中注册消息监听和加载礼物列表 - 在
onClose()中移除监听和清空消息列表
- 在
-
权限检查:
- 创建频道和上麦前都会检查权限
- 权限被拒绝时会提示用户去设置中开启
-
状态同步:
- 使用响应式变量(
.obs)管理状态 - UI 通过
Obx或GetBuilder监听状态变化
- 使用响应式变量(
-
消息去重:
- 消息列表会自动去重(基于 userId + content + timestamp)
- 最多保留 300 条消息
-
与 CallController 的互斥:
- 创建/加入频道前会检查是否正在通话
- 确保直播和通话不会同时进行
IMManager - 即时通讯管理器
文件位置
lib/im/im_manager.dart
功能概述
负责管理环信 IM SDK 的初始化和消息处理,包括:
- IM SDK 初始化和登录
- 消息接收和分发
- 通话消息处理
- 用户在线状态管理
- 消息通知管理
- ChatController 注册管理
核心属性
// 初始化状态
bool _initialized = false;
bool _loggedIn = false;
// 活跃的 ChatController 实例
final Map<String, ChatController> _activeChatControllers = {};
// Presence 状态变化回调
final Map<String, Function(bool)> _presenceCallbacks = {};
// 消息通知队列
final List<_NotificationMessage> _notificationQueue = [];
主要方法
1. 初始化 IM
Future<void> ensureInitialized({required String appKey}) async
- 功能: 初始化环信 IM SDK
- 参数:
appKey- 环信 AppKey(格式:orgname#appname) - 特点:
- 支持并发调用,只初始化一次
- 使用 Completer 确保并发安全
- 流程:
- 创建 EMOptions
- 调用 SDK 初始化
- 注册监听器
- 初始化本地通知服务
2. 登录
Future<bool> login({
required String userId,
required String token,
}) async
- 功能: 使用用户ID和Token登录环信
- 参数:
userId: 用户IDtoken: 环信Token(从服务器获取)
- 返回: 登录是否成功
3. 登出
Future<void> logout() async
- 功能: 登出环信,清理状态
4. 注册 ChatController
void registerChatController(ChatController controller)
- 功能: 注册 ChatController,用于接收该用户的消息
- 使用场景: ChatController 初始化时调用
5. 注销 ChatController
void unregisterChatController(String userId)
- 功能: 注销 ChatController
- 使用场景: ChatController 销毁时调用
6. 获取用户信息
Future<EMUserInfo?> getUserInfo(String userId) async
- 功能: 从环信服务器获取用户信息
7. 发送消息
Future<EMMessage?> sendMessage({
required String toUserId,
required String content,
MessageType type = MessageType.TXT,
}) async
- 功能: 发送文本消息
- 注意: 实际发送消息通常通过 ChatController,这个方法主要用于特殊场景
消息处理流程
消息接收流程
- 消息监听器接收:
onMessagesReceived回调 - 消息分发:
- 查找对应的 ChatController
- 如果找到,添加到对应 Controller 的消息列表
- 如果未找到,显示通知弹框
- 通话消息特殊处理:
- 检测通话消息(event == 'call')
- 显示视频通话邀请弹框
- 播放来电铃声
- 消息状态更新:
- 监听
onMessageContentChanged - 处理通话状态变化(calling、rejected、cancelled等)
- 监听
通话消息处理
- 接收方:
- 显示
VideoCallInviteDialog弹框 - 播放来电铃声
- 点击弹框跳转到
VideoCallPage(isInitiator: false)
- 显示
- 状态更新:
- 监听消息内容变化
- 通知
CallController处理状态变化
使用示例
// 初始化 IM
await IMManager.instance.ensureInitialized(
appKey: 'your_app_key',
);
// 登录
final success = await IMManager.instance.login(
userId: 'user123',
token: 'hx_token',
);
// 获取用户信息
final userInfo = await IMManager.instance.getUserInfo('user123');
// 注册 ChatController(在 ChatController.onInit 中)
IMManager.instance.registerChatController(this);
// 注销 ChatController(在 ChatController.onClose 中)
IMManager.instance.unregisterChatController(userId);
注意事项
-
初始化时机:
- 在应用启动时初始化(
main.dart或MyApp的initState) - 确保只初始化一次
- 在应用启动时初始化(
-
登录时机:
- 用户登录成功后调用
- Token 从服务器获取
-
消息分发:
- 消息会自动分发到对应的 ChatController
- 如果没有对应的 ChatController,会显示通知
-
通话消息:
- 通话消息会触发特殊的 UI 处理(弹框、铃声)
- 需要确保 CallController 已初始化
-
连接状态:
- 监听连接状态变化
- 断开连接时会自动重连
- 被其他设备踢下线时会处理登出逻辑
-
线程安全:
- 使用 Completer 确保并发初始化安全
- 消息处理使用
Future.microtask确保在主线程执行
RTCManager - 实时音视频管理器
文件位置
lib/rtc/rtc_manager.dart
功能概述
负责管理声网 Agora RTC SDK,提供音视频通话和直播功能:
- RTC Engine 初始化
- 加入/离开频道
- 音视频流管理(发布/订阅)
- 远端用户管理
- 事件回调处理
核心属性
// RTC Engine 实例
RtcEngine? _engine;
// 频道状态
bool _isInChannel = false;
String? _currentChannelId;
int? _currentUid;
// 客户端角色
ClientRoleType _clientRole = ClientRoleType.clientRoleBroadcaster;
// 远端用户列表
final List<int> _remoteUserIds = <int>[];
// 状态通知器
final ValueNotifier<bool> channelJoinedNotifier = ValueNotifier<bool>(false);
final ValueNotifier<List<int>> remoteUsersNotifier = ValueNotifier<List<int>>([]);
// RTC 类型
RTCType type = RTCType.live; // live 或 call
主要方法
1. 初始化 RTC Engine
Future<bool> initialize({
required String appId,
ChannelProfileType channelProfile = ChannelProfileType.channelProfileLiveBroadcasting,
}) async
- 功能: 初始化声网 RTC Engine
- 参数:
appId: 声网 App IDchannelProfile: 频道场景类型(默认直播模式)
- 流程:
- 创建 RTC Engine
- 初始化 Engine
- 设置客户端角色
- 配置视频编码
- 启用视频
- 注册事件处理器
2. 加入频道
Future<void> joinChannel({
required String token,
required String channelId,
required int uid,
required ClientRoleType role,
required RTCType rtcType,
}) async
- 功能: 加入 RTC 频道
- 参数:
token: RTC TokenchannelId: 频道IDuid: 用户IDrole: 客户端角色(主播/观众)rtcType: RTC 类型(直播/通话)
- 流程:
- 设置 RTC 类型
- 设置客户端角色
- 加入频道
- 触发
onJoinChannelSuccess回调
3. 离开频道
Future<void> leaveChannel() async
- 功能: 离开当前频道
- 流程:
- 取消订阅所有远端用户
- 离开频道
- 清理状态
4. 发布视频
Future<void> publishVideo(CurrentRole role) async
- 功能: 发布本地视频流(用于直播上麦)
- 参数:
role- 角色类型
5. 发布音频
Future<void> publishAudio() async
- 功能: 发布本地音频流(用于观众上麦)
6. 停止发布
Future<void> unpublish(CurrentRole role) async
- 功能: 停止发布音视频流(下麦)
7. 启用/禁用视频
Future<void> enableVideo() async
Future<void> disableVideo() async
- 功能: 控制本地视频的开启/关闭
8. 静音/取消静音
Future<void> muteLocalAudio(bool muted) async
- 功能: 控制本地音频的静音状态
9. 设置扬声器
Future<void> setEnableSpeakerphone(bool enabled) async
- 功能: 控制扬声器开关
事件回调
频道事件
onJoinChannelSuccess: 加入频道成功onLeaveChannel: 离开频道onConnectionStateChanged: 连接状态变化onConnectionLost: 连接丢失
用户事件
onUserJoined: 远端用户加入onUserOffline: 远端用户离开onUserMuteAudio: 用户静音状态变化onUserMuteVideo: 用户视频状态变化onFirstRemoteVideoDecoded: 首帧远端视频解码完成onVideoSizeChanged: 视频尺寸变化
使用示例
// 初始化 RTC
await RTCManager.instance.initialize(
appId: 'your_app_id',
);
// 加入频道(直播)
await RTCManager.instance.joinChannel(
token: rtcToken,
channelId: channelId,
uid: uid,
role: ClientRoleType.clientRoleBroadcaster,
rtcType: RTCType.live,
);
// 监听频道加入状态
RTCManager.instance.channelJoinedNotifier.addListener(() {
if (RTCManager.instance.channelJoinedNotifier.value) {
print('已加入频道');
}
});
// 监听远端用户
RTCManager.instance.remoteUsersNotifier.addListener(() {
final remoteUsers = RTCManager.instance.remoteUsersNotifier.value;
print('远端用户数: ${remoteUsers.length}');
});
// 发布视频(上麦)
await RTCManager.instance.publishVideo(CurrentRole.maleAudience);
// 离开频道
await RTCManager.instance.leaveChannel();
注意事项
-
初始化时机:
- 在应用启动时初始化(
main.dart) - 只需要初始化一次
- 在应用启动时初始化(
-
RTC 类型:
RTCType.live: 直播场景RTCType.call: 通话场景- 类型会影响事件处理逻辑
-
角色管理:
clientRoleBroadcaster: 主播角色,可以发布音视频clientRoleAudience: 观众角色,只能订阅
-
状态通知:
- 使用
ValueNotifier提供状态通知 - UI 可以通过监听器响应状态变化
- 使用
-
与 RoomController 的交互:
- 加入频道成功后会调用
RoomController.fetchRtcChannelDetail - 只有直播类型才会执行 RoomController 的逻辑
- 加入频道成功后会调用
-
与 CallController 的交互:
- 通话场景下,CallController 会设置
remoteUid - 用于显示远端视频
- 通话场景下,CallController 会设置
-
资源清理:
- 离开频道时会自动清理订阅
- 应用退出时需要调用
dispose()释放资源
RTMManager - 实时消息管理器
文件位置
lib/rtc/rtm_manager.dart
功能概述
负责管理声网 Agora RTM SDK,提供实时消息功能:
- RTM Client 初始化
- 频道订阅/取消订阅
- 频道消息发布
- 事件监听(消息、Presence、Topic等)
核心属性
// RTM Client 实例
RtmClient? _client;
// 初始化状态
bool _isInitialized = false;
bool _isLoggedIn = false;
// 当前 AppId 和 UserId
String? _currentAppId;
String? _currentUserId;
// StreamChannel 映射
final Map<String, StreamChannel> _streamChannels = {};
// 事件回调
void Function(MessageEvent event)? onMessageEvent;
void Function(PresenceEvent event)? onPresenceEvent;
void Function(TopicEvent event)? onTopicEvent;
// ... 其他事件回调
主要方法
1. 初始化 RTM
Future<bool> initialize({
required String appId,
required String userId,
RtmConfig? config,
}) async
- 功能: 初始化 RTM Client
- 参数:
appId: 声网 App IDuserId: 用户IDconfig: RTM 配置(可选)
- 流程:
- 创建 RTM Client
- 注册客户端监听器
- 获取 RTM Token
- 自动登录
2. 登录
Future<bool> login(String token) async
- 功能: 使用 Token 登录 RTM
- 参数:
token- RTM Token(从服务器获取或本地生成) - 注意: 初始化时会自动调用登录
3. 订阅频道
Future<bool> subscribe(String channelName) async
- 功能: 订阅频道,接收频道消息
- 参数:
channelName- 频道名称 - 使用场景: 加入直播频道时调用
4. 取消订阅
Future<bool> unsubscribe(String channelName) async
- 功能: 取消订阅频道
- 使用场景: 离开直播频道时调用
5. 发布频道消息
Future<bool> publishChannelMessage({
required String channelName,
required String message,
RtmChannelType channelType = RtmChannelType.message,
String? customType,
bool storeInHistory = false,
}) async
- 功能: 在频道中发布消息
- 参数:
channelName: 频道名称message: 消息内容(通常是 JSON 字符串)channelType: 频道类型(默认 message)customType: 自定义类型(可选)storeInHistory: 是否存储到历史记录
- 使用场景:
- 发送直播间聊天消息
- 发送礼物消息
- 发送系统消息(加入/离开房间等)
6. 登出
Future<void> logout() async
- 功能: 登出 RTM
7. 刷新 Token
Future<bool> renewToken(String token) async
- 功能: 刷新 RTM Token(Token 过期时调用)
事件监听
消息事件
onMessageEvent = (MessageEvent event) {
// 处理频道消息
final channelName = event.channelName;
final message = event.message;
// 解析消息并处理
};
Presence 事件
onPresenceEvent = (PresenceEvent event) {
// 处理用户上线/下线事件
};
使用示例
// 初始化 RTM
await RTMManager.instance.initialize(
appId: 'your_app_id',
userId: 'user123',
);
// 订阅频道
await RTMManager.instance.subscribe('channel_123');
// 发布消息
await RTMManager.instance.publishChannelMessage(
channelName: 'channel_123',
message: json.encode({
'type': 'chat',
'content': 'Hello',
'userId': 'user123',
}),
);
// 监听消息事件
RTMManager.instance.onMessageEvent = (event) {
final message = json.decode(event.message);
print('收到消息: ${message['content']}');
};
// 取消订阅
await RTMManager.instance.unsubscribe('channel_123');
注意事项
-
初始化时机:
- 在应用启动时初始化(
main_page.dart的initRTM()) - 需要用户ID
- 在应用启动时初始化(
-
Token 管理:
- 初始化时会自动获取 Token 并登录
- Token 过期时需要调用
renewToken()刷新
-
频道订阅:
- 加入直播频道时需要订阅对应的 RTM 频道
- 离开时需要取消订阅
-
消息格式:
- 通常使用 JSON 格式
- 包含
type字段区分消息类型
-
事件处理:
- 通过回调函数处理事件
- 需要在初始化后设置回调
-
与 RTCManager 的配合:
- RTC 负责音视频传输
- RTM 负责消息传输
- 两者配合实现完整的直播功能
ChatController - 聊天控制器
文件位置
lib/controller/message/chat_controller.dart
功能概述
负责管理一对一聊天功能,包括:
- 消息发送和接收
- 消息列表管理
- 用户信息管理
- 在线状态订阅
- 礼物发送
- 图片/视频/语音消息处理
核心属性
// 用户ID
final String userId;
// 用户信息
final Rx<EMUserInfo?> userInfo = Rx<EMUserInfo?>(null);
// 用户在线状态
final RxBool isUserOnline = RxBool(false);
// 消息列表
final RxList<EMMessage> messages = RxList<EMMessage>([]);
// 加载更多的游标
String? _cursor;
// 礼物产品列表
final RxList<GiftProductModel> giftProducts = <GiftProductModel>[].obs;
主要方法
1. 获取消息列表
Future<void> fetchMessages({bool loadMore = false}) async
- 功能: 获取聊天消息列表
- 参数:
loadMore- 是否加载更多(分页) - 流程:
- 调用环信 SDK 获取消息
- 添加到消息列表
- 更新游标用于分页
2. 发送文本消息
Future<void> sendMessage(String content) async
- 功能: 发送文本消息
- 参数:
content- 消息内容 - 流程:
- 创建文本消息
- 发送消息
- 添加到本地消息列表
- 标记为已读
3. 发送图片消息
Future<void> sendImageMessage(String imagePath) async
- 功能: 发送图片消息
- 参数:
imagePath- 图片本地路径 - 流程:
- 上传图片到 OSS
- 创建图片消息
- 发送消息
4. 发送语音消息
Future<void> sendVoiceMessage(String filePath, int seconds) async
- 功能: 发送语音消息
- 参数:
filePath: 语音文件路径seconds: 语音时长(秒)
- 流程:
- 上传语音文件到 OSS
- 创建语音消息
- 发送消息
5. 发送视频消息
Future<void> sendVideoMessage(String videoPath) async
- 功能: 发送视频消息
- 参数:
videoPath- 视频文件路径 - 流程:
- 生成视频缩略图
- 上传视频和缩略图到 OSS
- 创建视频消息
- 发送消息
6. 发送礼物
Future<void> sendGift({
required GiftProductModel gift,
required int quantity,
}) async
- 功能: 发送礼物消息
- 参数:
gift: 礼物产品模型quantity: 礼物数量
- 流程:
- 调用消费接口
- 检查玫瑰余额
- 创建自定义消息(礼物消息)
- 发送消息
7. 发送通话消息
Future<bool> sendCallMessage({
required String callType,
required String callStatus,
int? callDuration,
String? channelId,
int? uid,
}) async
- 功能: 发送通话消息(自定义消息)
- 参数:
callType: 通话类型(voice/video)callStatus: 通话状态(waitCalling/calling/rejected等)callDuration: 通话时长(秒)channelId: RTC 频道IDuid: RTC 用户ID
8. 标记消息已读
Future<void> markAllMessagesAsRead() async
- 功能: 将所有消息标记为已读
9. 订阅用户在线状态
Future<void> subscribeUserPresence() async
- 功能: 订阅对方用户的在线状态
- 回调: 状态变化时会更新
isUserOnline
10. 加载礼物产品列表
Future<void> loadGiftProducts() async
- 功能: 加载可用的礼物产品列表
使用示例
// 创建 ChatController(在 ChatPage 中)
final controller = Get.put(
ChatController(
userId: targetUserId,
userData: userData,
),
tag: 'chat_$targetUserId',
);
// 发送文本消息
await controller.sendMessage('Hello');
// 发送图片
await controller.sendImageMessage('/path/to/image.jpg');
// 发送语音
await controller.sendVoiceMessage('/path/to/voice.m4a', 10);
// 发送礼物
await controller.sendGift(
gift: giftProduct,
quantity: 1,
);
// 监听消息列表
Obx(() => ListView.builder(
itemCount: controller.messages.length,
itemBuilder: (context, index) {
final message = controller.messages[index];
return MessageItem(message: message);
},
));
// 监听在线状态
Obx(() => Text(
controller.isUserOnline.value ? '在线' : '离线',
));
注意事项
-
生命周期管理:
- 在
onInit()中注册到 IMManager - 在
onClose()中注销 - 使用 tag 区分不同用户的 Controller
- 在
-
消息接收:
- 消息通过 IMManager 自动分发到对应的 ChatController
- 需要先注册到 IMManager
-
用户信息:
- 优先使用环信用户信息
- 如果没有,使用外部传入的用户数据
- 会同步到 ConversationController 缓存
-
在线状态:
- 使用 ChatPresenceManager 订阅
- 状态变化会实时更新
-
错误处理:
- 发送失败会显示错误提示
- 玫瑰不足会显示充值弹框
- 敏感词会显示错误提示
-
消息去重:
- 环信 SDK 会自动处理消息去重
- 本地消息列表使用 RxList 管理
-
文件上传:
- 图片/视频/语音需要先上传到 OSS
- 上传成功后再发送消息
组件间关系
依赖关系图
IMManager (核心)
├── ChatController (一对一聊天)
│ └── 使用 IMManager 发送/接收消息
│
├── CallController (通话)
│ └── 使用 IMManager 发送通话消息
│
RTCManager (音视频)
├── RoomController (直播)
│ └── 使用 RTCManager 进行音视频传输
│
└── CallController (通话)
└── 使用 RTCManager 进行音视频传输
RTMManager (消息)
└── RoomController (直播)
└── 使用 RTMManager 发送直播间消息
交互流程
直播流程
-
创建直播:
RoomController.createRtcChannel()- →
RTCManager.joinChannel() - →
RTMManager.subscribe() - → 跳转到直播页面
-
发送消息:
RoomController.sendChatMessage()- →
RTMManager.publishChannelMessage() - → 其他用户通过 RTM 事件接收
-
发送礼物:
RoomController.sendGift()- → 调用消费接口
- →
RTMManager.publishChannelMessage() - → SVGA 动画播放
聊天流程
-
发送消息:
ChatController.sendMessage()- →
IMManager.sendMessage() - → 环信 SDK 发送
- → 对方通过 IMManager 接收
- → 分发到对应的 ChatController
-
接收消息:
- 环信 SDK 接收
- →
IMManager.onMessagesReceived - → 查找对应的 ChatController
- → 添加到消息列表
通话流程
-
发起通话:
CallController.initiateCall()- → 创建 RTC 频道
- →
RTCManager.joinChannel() - →
ChatController.sendCallMessage() - →
IMManager发送通话消息
-
接听通话:
IMManager接收通话消息- → 显示邀请弹框
- →
CallController.acceptCall() - →
RTCManager.joinChannel() - → 开始通话