会中呼叫(Web)

最近更新时间:2025-12-29 17:09:52

我的收藏
会中呼叫功能允许会议内的用户(邀请者)向房间外的其他用户(被邀请者)发送实时入会邀请。该功能基于 Atomicx State Hooks 实现,能够帮助开发者快速构建具有主动邀约能力的互动场景。

适用场景

协同办公:在进行远程会议时,若涉及跨部门决策,可一键呼叫外部专家快速入会,缩短沟通链路。
直播教学:老师在授课过程中,若发现部分学生缺席,可直接邀请其加入课堂。
互联网医疗会诊:首诊医生在病历分析过程中,通过呼叫功能邀请上级医生进入虚拟诊室进行多方会诊,或者医生呼叫在等候室的患者进行问诊。

前提条件

在接入功能前,请确保满足以下条件:
邀请者 (User A):用户已经通过 useLoginState 完成登录鉴权,参考 接入概览,并已作为一个房间的房主或成员在房间内,参考 房间周期
被邀请者 (User B):用户已经通过 useLoginState 完成登录鉴权,参考 接入概览,以便接收信令事件。
环境依赖:项目已正确安装并引入 tuikit-atomicx-vue3

实现会中呼叫功能

实现会中呼叫的核心流程如下:
会中呼叫时序图
会中呼叫时序图


步骤1:发起呼叫(邀请者)

邀请者调用 callUserToRoom 接口发起邀请。请确保邀请者当前已在房间内(即 currentRoom 不为空),否则无法发起呼叫。
import { useRoomState } from 'tuikit-atomicx-vue3/room';

const { currentRoom, callUserToRoom } = useRoomState();

const inviteUser = async () => {
// 会中呼叫必须关联一个已存在的 roomId,因此需先校验当前用户的进房状态
if (!currentRoom.value) {
console.warn('请先进入房间再发起邀请');
return;
}

try {
const resultMap = await callUserToRoom({
roomId: currentRoom.value.roomId, // 被邀请者将要加入的房间 ID
userIdList: ['user1', 'user2'], // 目标用户 ID 数组,支持单人或多人
timeout: 60, // 邀请有效时长(秒),超时后双方会触发 onCallTimeout
// extensionInfo 可用于传递业务自定义数据,例如邀请类型、附加消息等
extensionInfo: JSON.stringify({ type: 'emergency', priority: 'high' })
});
// resultMap 返回每个 userId 对应的状态,例如 Success、AlreadyInRoom (已在房) 或 AlreadyInCalling (通话中)
console.log('邀请发送完成,详情状态映射:', resultMap);
} catch (error) {
console.error('呼叫请求发送失败,请检查网络或登录状态:', error);
}
};

步骤2:监听呼叫事件(被邀请者)

被邀请者需要监听 onCallReceived 事件。建议在例如 App.vue等长驻组件中实现,以确保无论用户在哪个页面都能响应。
import { ref, onMounted, onUnmounted } from 'vue';
import { useRoomState, RoomEvent } from 'tuikit-atomicx-vue3/room';

const {
subscribeEvent,
unsubscribeEvent,
acceptCall,
rejectCall,
joinRoom,
currentRoom
} = useRoomState();

const showInviteDialog = ref(false); // 控制 UI 弹窗显隐
const currentCallInfo = ref(null); // 缓存收到的邀请上下文

const handleCallReceived = async ({ roomInfo, call, extensionInfo }) => {
// 冲突处理:如果用户当前已经在会议中,根据业务策略通常应直接拒绝新邀请,防止干扰
if (currentRoom.value?.roomId) {
await rejectCall({ roomId: roomInfo.roomId });
return;
}

// 记录邀请详情,用于在 UI 上展示邀请者名称或业务透传数据
currentCallInfo.value = { roomInfo, call };
showInviteDialog.value = true;
};

// 接受邀请逻辑
const onUserAccept = async () => {
if (!currentCallInfo.value) return;
const { roomId } = currentCallInfo.value.roomInfo;

try {
// 关键流程 A:响应信令。告知邀请者已接受,使其能更新 UI(例如显示“对方已接听”)
await acceptCall({ roomId });
// 关键流程 B:执行进房。只有调用 joinRoom 才会真正开启音视频推拉流链路
await joinRoom({ roomId });
} catch (error) {
console.error('接受呼叫后的进房操作失败:', error);
} finally {
closeDialog();
}
};

// 拒绝邀请逻辑
const onUserReject = async () => {
if (currentCallInfo.value) {
// 告知邀请者已拒绝,邀请者会收到 onCallRejected 事件
await rejectCall({ roomId: currentCallInfo.value.roomInfo.roomId });
}
closeDialog();
};

const closeDialog = () => {
showInviteDialog.value = false;
currentCallInfo.value = null;
};

// 事件全生命周期管理:确保在组件挂载时监听,销毁时移除,防止事件溢出
onMounted(() => {
subscribeEvent(RoomEvent.onCallReceived, handleCallReceived);
// 监听导致邀请结束的各种异常/同步事件,确保 UI 弹窗能及时闭合
subscribeEvent(RoomEvent.onCallCancelled, closeDialog); // 邀请者取消了呼叫
subscribeEvent(RoomEvent.onCallTimeout, closeDialog); // 呼叫超时未响应
subscribeEvent(RoomEvent.onCallHandledByOtherDevice, closeDialog); // 用户在其他设备已处理该呼叫
});

onUnmounted(() => {
// 移除所有监听器,避免在组件销毁后继续执行 handleCallReceived 导致内存泄漏或报错
unsubscribeEvent(RoomEvent.onCallReceived, handleCallReceived);
unsubscribeEvent(RoomEvent.onCallCancelled, closeDialog);
unsubscribeEvent(RoomEvent.onCallTimeout, closeDialog);
unsubscribeEvent(RoomEvent.onCallHandledByOtherDevice, closeDialog);
});

实践教程:提升呼叫接通率

为了在业务中获得更高的呼叫接通率,除了基础功能的实现外,建议参考以下策略优化用户体验:

1. 增强呼叫感知(避免用户遗漏)

由于 Web 端特性,浏览器静音或页面处于后台运行状态可能导致用户遗漏呼叫。
视觉提醒:利用 Page Visibility API 检测页面可见性状态。若页面处于后台,可通过循环修改 document.title 实现标题栏闪烁提示(例如:“【新邀请】UserA 邀请您入会...”),以吸引用户注意。
听觉提醒:在 onCallReceived 事件触发时播放高优先级的提示铃声。注意:需遵循浏览器的 自动播放策略,大部分浏览器在缺乏用户交互(例如点击、触摸或按键)的情况下会限制音频自动播放。

2. 丰富邀请上下文(提升接听意愿)

缺乏明确意图的邀请容易被用户拒绝。建议充分利用 extensionInfo 字段传递关键业务信息,以提升用户的接听意愿。
传递业务上下文:利用 extensionInfo 透传如 reason(例如:“项目紧急评审”)、level(例如:“P0故障排查”)等业务字段,告知用户呼叫的紧迫性和重要性。

3. 多通道触达兜底(确保消息可达)

受限于 Web 端机制,浏览器进程结束将无法接收实时信令。建议采取以下替代方案以确保触达:
浏览器系统通知:引导用户授权 Web Notification API。只要浏览器进程未关闭(包括最小化或在后台标签页),即可触发操作系统级别的弹窗通知,有效提醒用户。
多通道触达:建议结合业务后端逻辑,在检测到用户不在线或超时未响应时,通过短信邮件企业微信等外部渠道发送入会链接,实现全方位触达。

4. 智能重试机制(解决“接受邀请后进房失败”)

在弱网或其他不稳定环境下,用户点击接受邀请后调用 joinRoom 可能会出现偶发性失败。
自动重试:建议在 onUserAccept 的异常处理逻辑(catch)中引入 2-3 次自动重试机制(推荐结合指数退避策略),以最大程度确保用户点击“接受”后能成功加入会议。

开发注意事项

1. 接受邀请不等于进房
acceptCall 仅仅是信令层面的回复(告知对方已接听)。用户必须在调用 acceptCall 后紧接着手动调用 await joinRoom({ roomId }) 才能真正进入音视频房间。
2. 生命周期管理
请务必在组件销毁周期(onUnmounted)调用 unsubscribeEvent。如果不清理监听器,当用户切换页面时,旧页面的监听器会残留在内存中,导致一次邀请弹出多个窗口或逻辑重复执行。
3. 超时设置
如果不设置 timeout 参数,在某些网络异常或程序崩溃情况下,邀请可能长期处于挂起状态,导致无法再次对该用户发起呼叫。建议设置为 30-60 秒。

常见问题

1. 如何在呼叫时带上自定义参数(如“来自XX的紧急会议”)?
使用 callUserToRoomextensionInfo 参数传递 JSON 字符串。被邀请者可以在 onCallReceived 事件的 extensionInfo 字段中解析该字符串获取业务信息。
2. 被邀请者如果不在线,上线后能收到邀请吗?
不可以。邀请事件是一个瞬时信令动作,SDK 目前不会补发离线期间收到的呼叫通知。
3. 可以取消已发出的邀请吗?
可以。调用 cancelCall({ roomId, userIdList }) 即可撤销。此时被邀请者会收到 onCallCancelled 事件,您需要监听此事件来关闭被邀请端的 UI 弹窗。
4. 接受后进房失败怎么办?
检查是否已登录 TUIRoomEngine,重试 joinRoom({ roomId }) 并捕获错误提示用户;必要时在 UI 上提供“重试”按钮。
5. 对方忙线或已在其他房间如何提示?
callUserToRoom 的返回值 resultMap 中若出现 AlreadyInRoom / AlreadyInCalling,应在邀请者 UI 提示“对方忙线/已在会中”,并决定是否稍后重试。
6. 超时/取消后弹窗不关闭?
确保监听 onCallTimeoutonCallCancelledonCallHandledByOtherDevice,统一在回调里关闭弹窗、清理当前邀请数据。
7. 多设备同时响铃如何处理?
在任意设备接受/拒绝后,其余设备会收到 onCallHandledByOtherDevice,需关闭其他端弹窗避免重复处理。
8. extensionInfo 该怎么约定?
建议使用 JSON 字符串并约定字段(例如 typepriorityfrom),在 handleCallReceived 时解析后展示友好文案(例如“来自XX的紧急会议”)。

示例项目

腾讯云在 GitHub 上提供了示例项目 atomicx-vite-vue3-ts ,您可以参考以实现完整的 RoomKit 功能。

API 文档

State/Component
功能描述
API 文档
useRoomState
房间状态管理(创建、加入、预约等)