✨ feat(im): 振铃超时 Job 单人粒度标 NO_ANSWER + 独立 NO_ANSWER 信令推送
✨ feat(im): 处理 RTC_CALL(NO_ANSWER) 信令;私聊气泡显示「未接听」
pull/881/head
parent
f58d1d88c8
commit
8329a6a885
|
|
@ -74,9 +74,9 @@ export const leaveCall = (room: string) => {
|
||||||
return request.post<boolean>({ url: '/im/rtc/leave', params: { room } })
|
return request.post<boolean>({ url: '/im/rtc/leave', params: { room } })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新签发 Token;客户端重连或 Token 过期续期
|
// 振铃超时检查;RUNNING 端 timer 兜底,触发后端立即扫描该 room 的超时 INVITING(接口静默)
|
||||||
export const refreshCallToken = (room: string) => {
|
export const noAnswerCallCheck = (room: string) => {
|
||||||
return request.get<ImRtcCallRespVO>({ url: '/im/rtc/refresh-token', params: { room } })
|
return request.post<boolean>({ url: '/im/rtc/no-answer-call-check', params: { room } })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询当前进行中的通话;目前仅群聊场景(胶囊条),返回 null 表示无活跃通话
|
// 查询当前进行中的通话;目前仅群聊场景(胶囊条),返回 null 表示无活跃通话
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { useIntervalFn } from '@vueuse/core'
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
import { useRtcStore } from '../../store/rtcStore'
|
import { useRtcStore } from '../../store/rtcStore'
|
||||||
import { useLiveKitRoom } from '../../composables/useLiveKitRoom'
|
import { useLiveKitRoom } from '../../composables/useLiveKitRoom'
|
||||||
|
|
@ -64,13 +65,15 @@ import {
|
||||||
rejectCall,
|
rejectCall,
|
||||||
acceptCall,
|
acceptCall,
|
||||||
leaveCall,
|
leaveCall,
|
||||||
inviteCall
|
inviteCall,
|
||||||
|
noAnswerCallCheck
|
||||||
} from '@/api/im/home/rtc'
|
} from '@/api/im/home/rtc'
|
||||||
import {
|
import {
|
||||||
ImRtcCallMediaType,
|
ImRtcCallMediaType,
|
||||||
ImRtcCallStage,
|
ImRtcCallStage,
|
||||||
ImConversationType
|
ImConversationType
|
||||||
} from '@/views/im/utils/constants'
|
} from '@/views/im/utils/constants'
|
||||||
|
import { RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS } from '@/views/im/utils/config'
|
||||||
import { getCurrentUserId } from '@/views/im/utils/storage'
|
import { getCurrentUserId } from '@/views/im/utils/storage'
|
||||||
import { getSenderAvatar, getSenderDisplayName } from '@/views/im/utils/user'
|
import { getSenderAvatar, getSenderDisplayName } from '@/views/im/utils/user'
|
||||||
import { Track } from 'livekit-client'
|
import { Track } from 'livekit-client'
|
||||||
|
|
@ -363,9 +366,39 @@ async function handleHangup() {
|
||||||
|
|
||||||
/** LiveKit Room 异常断开;多见于网络中断 */
|
/** LiveKit Room 异常断开;多见于网络中断 */
|
||||||
function handlePeerDisconnected() {
|
function handlePeerDisconnected() {
|
||||||
if (!rtcStore.isActive) return
|
if (!rtcStore.isActive) {
|
||||||
message.warning('通话已断开')
|
return
|
||||||
rtcStore.reset()
|
}
|
||||||
|
// 给 RTC_CALL_END WebSocket 推送一个小窗口;私聊超时 / 主动挂断等场景下,后端 endSession 会先推 RTC_CALL_END,
|
||||||
|
// 让前端按业务语义("对方未接听" / "已取消" 等)reset,避免错把业务断开 toast 成「通话已断开」
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!rtcStore.isActive) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.warning('通话已断开')
|
||||||
|
rtcStore.reset()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 振铃超时兜底 ====================
|
||||||
|
|
||||||
|
/** 通话存活期间(INVITING / INCOMING / RUNNING)周期性触发后端扫该 room 的超时 INVITING;保持 timer 是为了 inviteCall 追加新人后也能覆盖;阈值由后端配置决定,前端只负责 trigger */
|
||||||
|
const { resume: resumeNoAnswerTimer, pause: pauseNoAnswerTimer } = useIntervalFn(
|
||||||
|
triggerNoAnswerCallCheck, RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS, { immediate: false }
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
() => rtcStore.isActive,
|
||||||
|
(active) => (active ? resumeNoAnswerTimer() : pauseNoAnswerTimer()),
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 本地仍有 pending 才调;INVITING / RUNNING 取 call、INCOMING 取 incomingPayload;接口静默错误 fire-and-forget */
|
||||||
|
function triggerNoAnswerCallCheck() {
|
||||||
|
const source = rtcStore.call ?? rtcStore.incomingPayload
|
||||||
|
if (!source?.room || !source.inviteeIds?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
noAnswerCallCheck(source.room).catch(() => undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 设备控制 ====================
|
// ==================== 设备控制 ====================
|
||||||
|
|
|
||||||
|
|
@ -75,3 +75,11 @@ export const MESSAGE_MERGE_PREVIEW_LINES = 3
|
||||||
|
|
||||||
/** 最近转发会话 key 列表的最大保留数量(对齐微信 PC 横向头像区可见容量) */
|
/** 最近转发会话 key 列表的最大保留数量(对齐微信 PC 横向头像区可见容量) */
|
||||||
export const CONVERSATION_RECENT_FORWARD_MAX = 12
|
export const CONVERSATION_RECENT_FORWARD_MAX = 12
|
||||||
|
|
||||||
|
// ==================== 前端独有:RTC 通话兜底 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 振铃超时兜底;通话存活期间 timer 调用 noAnswerCallCheck 接口的间隔;
|
||||||
|
* 实际超时阈值由后端 yudao.im.rtc.invite-timeout-minutes 决定,前端仅决定触发频率
|
||||||
|
*/
|
||||||
|
export const RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS = 60 * 1000
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue