运营数据
This commit is contained in:
@@ -166,7 +166,8 @@ import { minimizeCloudLawMeeting } from '@/utils/cloudLawMiniProgram'
|
||||
import {
|
||||
notifyCloudLawBackendEnd,
|
||||
notifyCloudLawParentCallEnded,
|
||||
isCloudLawPcIframe
|
||||
isCloudLawPcIframe,
|
||||
notifyNativeMiniProgramHangup
|
||||
} from '@/utils/cloudLawVoiceCall'
|
||||
import bgImage from '@/static/bg.png'
|
||||
import hangupIcon from '@/static/cacel.png'
|
||||
@@ -1017,6 +1018,10 @@ const hangup = async () => {
|
||||
userInitiatedHangup.value = true
|
||||
leaving.value = true
|
||||
stopCommandPolling()
|
||||
if (!isLawRole.value) {
|
||||
// 优先触发小程序原生 /end(web-view 跨域直连云律 API 不可靠)
|
||||
notifyNativeMiniProgramHangup()
|
||||
}
|
||||
try {
|
||||
// 先通知云律后端结束本次通话:
|
||||
// - 用户端:/api/applet/voiceConnection/{callId}/end
|
||||
|
||||
@@ -242,7 +242,8 @@ import {
|
||||
isCloudLawLawRoleInMeeting,
|
||||
isCloudLawPcIframe,
|
||||
notifyCloudLawBackendEnd,
|
||||
notifyCloudLawParentCallEnded
|
||||
notifyCloudLawParentCallEnded,
|
||||
queryCloudLawCallStatus
|
||||
} from '@/utils/cloudLawVoiceCall'
|
||||
|
||||
const meetingStore = useMeetingStore()
|
||||
@@ -843,6 +844,82 @@ const syncViewportHeight = () => {
|
||||
// #endif
|
||||
}
|
||||
|
||||
let cloudLawStatusPollTimer = null
|
||||
let cloudLawParticipantStopWatch = null
|
||||
let cloudLawSoloCheckTimer = null
|
||||
let cloudLawConsultEnding = false
|
||||
const hadRemoteParticipant = ref(false)
|
||||
|
||||
const getMyUserId = () => userStore.userInfo?.id || userStore.userInfo?.user_id
|
||||
|
||||
const countRemoteActiveParticipants = () => {
|
||||
const myUid = getMyUserId()
|
||||
return meetingStore.activeParticipants.filter(p => String(p.user_id) !== String(myUid)).length
|
||||
}
|
||||
|
||||
const stopCloudLawStatusPoll = () => {
|
||||
if (cloudLawStatusPollTimer) {
|
||||
clearInterval(cloudLawStatusPollTimer)
|
||||
cloudLawStatusPollTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleCloudLawConsultEnded = async (tip = '用户已退出咨询') => {
|
||||
if (cloudLawConsultEnding || !isCloudLawLawRoleInMeeting(cloudLawRole.value)) return
|
||||
cloudLawConsultEnding = true
|
||||
stopCloudLawStatusPoll()
|
||||
if (cloudLawSoloCheckTimer) {
|
||||
clearTimeout(cloudLawSoloCheckTimer)
|
||||
cloudLawSoloCheckTimer = null
|
||||
}
|
||||
try {
|
||||
if (cloudLawCallId.value) {
|
||||
await notifyCloudLawBackendEnd(cloudLawCallId.value, 'law', cloudLawApiBase.value)
|
||||
notifyCloudLawParentCallEnded(cloudLawCallId.value)
|
||||
}
|
||||
if (meetingStore.isInMeeting) {
|
||||
await meetingStore.leave().catch(() => {})
|
||||
}
|
||||
uni.showToast({ title: tip, icon: 'none' })
|
||||
if (!isCloudLawPcIframe()) {
|
||||
setTimeout(() => redirectHome(true), 800)
|
||||
}
|
||||
} finally {
|
||||
cloudLawConsultEnding = false
|
||||
}
|
||||
}
|
||||
|
||||
const pollCloudLawCallStatusOnce = async () => {
|
||||
if (!isCloudLawLawRoleInMeeting(cloudLawRole.value) || !cloudLawCallId.value || cloudLawConsultEnding) return
|
||||
if (meetingStore.localState === MEETING_LOCAL_STATE_ENDED || meetingStore.localState === MEETING_LOCAL_STATE_LEAVING) return
|
||||
const data = await queryCloudLawCallStatus(cloudLawCallId.value, 'law', cloudLawApiBase.value)
|
||||
const status = String(data?.status || data?.callStatus || '').toLowerCase()
|
||||
if (['ended', 'cancelled', 'failed'].includes(status)) {
|
||||
await handleCloudLawConsultEnded('咨询已结束')
|
||||
}
|
||||
}
|
||||
|
||||
const startCloudLawStatusPoll = () => {
|
||||
if (!isCloudLawLawRoleInMeeting(cloudLawRole.value) || !cloudLawCallId.value) return
|
||||
stopCloudLawStatusPoll()
|
||||
pollCloudLawCallStatusOnce()
|
||||
cloudLawStatusPollTimer = setInterval(pollCloudLawCallStatusOnce, 1000)
|
||||
}
|
||||
|
||||
const scheduleCloudLawSoloCheck = () => {
|
||||
if (cloudLawSoloCheckTimer) {
|
||||
clearTimeout(cloudLawSoloCheckTimer)
|
||||
}
|
||||
cloudLawSoloCheckTimer = setTimeout(async () => {
|
||||
cloudLawSoloCheckTimer = null
|
||||
if (!hadRemoteParticipant.value || cloudLawConsultEnding) return
|
||||
if (meetingStore.localState !== MEETING_LOCAL_STATE_CONNECTED) return
|
||||
if (countRemoteActiveParticipants() === 0) {
|
||||
await handleCloudLawConsultEnded('用户已退出咨询')
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// ============ 生命周期 ============
|
||||
|
||||
let stateStopWatch = null
|
||||
@@ -919,6 +996,25 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 云律律师端:轮询通话状态 + 对方离会后自动收尾(用户挂断不一定能推到 room.ended)
|
||||
if (isCloudLawLawRoleInMeeting(cloudLawRole.value)) {
|
||||
startCloudLawStatusPoll()
|
||||
cloudLawParticipantStopWatch = watch(
|
||||
() => meetingStore.activeParticipants,
|
||||
() => {
|
||||
const remoteCount = countRemoteActiveParticipants()
|
||||
if (remoteCount > 0) {
|
||||
hadRemoteParticipant.value = true
|
||||
return
|
||||
}
|
||||
if (hadRemoteParticipant.value && remoteCount === 0) {
|
||||
scheduleCloudLawSoloCheck()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
}
|
||||
|
||||
// #ifdef H5
|
||||
if (showCloudLawBack.value) {
|
||||
try {
|
||||
@@ -941,6 +1037,15 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
if (viewportCleanup) { viewportCleanup(); viewportCleanup = null }
|
||||
if (stateStopWatch) { stateStopWatch(); stateStopWatch = null }
|
||||
stopCloudLawStatusPoll()
|
||||
if (cloudLawParticipantStopWatch) {
|
||||
cloudLawParticipantStopWatch()
|
||||
cloudLawParticipantStopWatch = null
|
||||
}
|
||||
if (cloudLawSoloCheckTimer) {
|
||||
clearTimeout(cloudLawSoloCheckTimer)
|
||||
cloudLawSoloCheckTimer = null
|
||||
}
|
||||
// #ifdef H5
|
||||
if (popstateHandler) {
|
||||
window.removeEventListener('popstate', popstateHandler)
|
||||
|
||||
@@ -70,3 +70,54 @@ export function isCloudLawLawRoleInMeeting(role) {
|
||||
export function isCloudLawPcIframe() {
|
||||
return typeof window !== 'undefined' && !!(window.parent && window.parent !== window)
|
||||
}
|
||||
|
||||
/** 查询云律通话状态(律师端 /api/law/voiceCall,用户端 /api/applet/voiceConnection) */
|
||||
export function queryCloudLawCallStatus(callId, role = 'law', apiBase = '', token = '') {
|
||||
if (!callId) return Promise.resolve(null)
|
||||
const endBase = String(role).toLowerCase() === 'law' ? '/api/law/voiceCall' : '/api/applet/voiceConnection'
|
||||
const url = resolveCloudLawApiUrl(`${endBase}/${encodeURIComponent(callId)}/status`, apiBase)
|
||||
const header = { 'Content-Type': 'application/json' }
|
||||
if (token) {
|
||||
header.token = token.startsWith('Bearer') ? token : `Bearer ${token}`
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
uni.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
header,
|
||||
timeout: 8000,
|
||||
success: (res) => {
|
||||
const body = res?.data || {}
|
||||
const payload = body.data !== undefined ? body.data : body
|
||||
resolve(payload && typeof payload === 'object' ? payload : null)
|
||||
},
|
||||
fail: () => resolve(null)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const CLOUD_LAW_HANGUP_QUERY = 'cloudLawHangup'
|
||||
|
||||
/** 小程序 web-view 内挂断:改 URL 触发原生页 @load,立即调 /end(不依赖 web-view 销毁) */
|
||||
export function notifyNativeMiniProgramHangup() {
|
||||
// #ifdef H5
|
||||
if (typeof window === 'undefined') return false
|
||||
try {
|
||||
const mp = window.wx && window.wx.miniProgram
|
||||
if (!mp) return false
|
||||
const href = String(window.location.href || '')
|
||||
if (href.includes(`${CLOUD_LAW_HANGUP_QUERY}=1`)) return true
|
||||
const url = new URL(href)
|
||||
url.searchParams.set(CLOUD_LAW_HANGUP_QUERY, '1')
|
||||
window.location.replace(url.toString())
|
||||
return true
|
||||
} catch (e) {
|
||||
console.warn('[cloudLawVoiceCall] native hangup bridge failed', e)
|
||||
}
|
||||
// #endif
|
||||
return false
|
||||
}
|
||||
|
||||
export function isCloudLawHangupBridgeUrl(url) {
|
||||
return String(url || '').includes(`${CLOUD_LAW_HANGUP_QUERY}=1`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user