139 lines
5.1 KiB
TypeScript
139 lines
5.1 KiB
TypeScript
import dayjs from 'dayjs'
|
||
|
||
export type FriendlyParse = { main: string; sub?: string; tag?: string }
|
||
|
||
/** 业务时间:支持秒级时间戳或毫秒(字符串数字) */
|
||
export function formatBizTime(t: unknown): string | undefined {
|
||
if (t == null || t === '') return undefined
|
||
const n =
|
||
typeof t === 'string' && /^\d+$/.test(t.trim())
|
||
? parseInt(t.trim(), 10)
|
||
: Number(t)
|
||
if (!Number.isFinite(n) || n <= 0) return undefined
|
||
return n > 1e12
|
||
? dayjs(n).format('YYYY-MM-DD HH:mm:ss')
|
||
: dayjs.unix(n).format('YYYY-MM-DD HH:mm:ss')
|
||
}
|
||
|
||
export function parseRtcInner(inner: Record<string, unknown>): FriendlyParse {
|
||
const callType = inner.call_type ?? inner.callType ?? 2
|
||
const callTypeLabel =
|
||
callType === 2 ? '视频' : callType === 1 ? '语音通话' : '通话'
|
||
|
||
let cmd = String(inner.cmd || inner.action || '').toLowerCase()
|
||
if (!cmd) {
|
||
const at = Number(inner.actionType ?? inner.action_type ?? inner.call_action)
|
||
if (at === 1) cmd = 'invite'
|
||
else if (at === 2) cmd = 'cancel'
|
||
else if (at === 3) cmd = 'accept'
|
||
else if (at === 4) cmd = 'reject'
|
||
else if (at === 5) cmd = 'timeout'
|
||
else if (at === 6) cmd = 'hangup'
|
||
else if (at === 7) cmd = 'linebusy'
|
||
}
|
||
|
||
const cmdMap: Record<string, string> = {
|
||
hangup: '已结束',
|
||
invite: '发起通话',
|
||
linebusy: '对方忙线',
|
||
cancel: '已取消呼叫',
|
||
reject: '已拒绝接听',
|
||
accept: '已接听',
|
||
timeout: '无人接听'
|
||
}
|
||
const action = cmdMap[cmd] || (cmd ? `状态:${cmd}` : '')
|
||
|
||
// 如果有通话时长,追加显示
|
||
const duration = Number(inner.duration ?? inner.call_duration)
|
||
const durationText = (duration > 0 && (action === '已结束' || action === '已挂断')) ? ` (${Math.floor(duration / 60)}分${duration % 60}秒)` : ''
|
||
|
||
const main = action ? `${callTypeLabel} · ${action}${durationText}` : callTypeLabel
|
||
const parts: string[] = []
|
||
//if (inner.inviter) parts.push(`发起方: ${inner.inviter}`)
|
||
if (inner.invitee) parts.push(`对方: ${inner.invitee}`)
|
||
if (inner.groupID) parts.push(`群组: ${inner.groupID}`)
|
||
|
||
// 强制输出兜底调试信息(仅未识别到 action 时触发)
|
||
if (!action) {
|
||
const dbg = JSON.stringify(inner).replace(/"/g, '')
|
||
parts.push(`(原始参数: ${dbg})`)
|
||
}
|
||
|
||
return {
|
||
main,
|
||
sub: parts.length ? parts.join(',') : undefined,
|
||
tag: '通话'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将 IM 自定义 / 信令 JSON 解析为可读文案(医生进诊室、音视频通话等)。
|
||
* 与后台 IM 漫游、小程序 TUIKit 约定字段一致。
|
||
*/
|
||
export function parseImBusinessPayload(raw: string): FriendlyParse | null {
|
||
const s = raw.trim()
|
||
if (!s.startsWith('{')) return null
|
||
let o: Record<string, unknown>
|
||
try {
|
||
o = JSON.parse(s) as Record<string, unknown>
|
||
} catch {
|
||
return null
|
||
}
|
||
|
||
if (!('businessID' in o) && !('cmd' in o)) {
|
||
return null
|
||
}
|
||
|
||
if (o.businessID === 'doctor_entered_consult_room') {
|
||
const t = formatBizTime(o.time)
|
||
return { main: '医生已进入诊室', sub: t, tag: '诊室' }
|
||
}
|
||
|
||
if (o.businessID === 'patient_opened_chat') {
|
||
const parts: string[] = []
|
||
if (o.patientName) parts.push(`患者:${String(o.patientName)}`)
|
||
if (o.patientId != null && o.patientId !== '') parts.push(`患者 ID:${String(o.patientId)}`)
|
||
if (o.doctorId != null && o.doctorId !== '') parts.push(`关联医生:${String(o.doctorId)}`)
|
||
const t = formatBizTime(o.time)
|
||
if (t) parts.push(t)
|
||
return { main: '患者进入聊天', sub: parts.length ? parts.join(' · ') : undefined, tag: '诊室' }
|
||
}
|
||
|
||
if (o.businessID === 'patient_closed_chat') {
|
||
const parts: string[] = []
|
||
if (o.patientName) parts.push(`患者:${String(o.patientName)}`)
|
||
if (o.patientId != null && o.patientId !== '') parts.push(`患者 ID:${String(o.patientId)}`)
|
||
const t = formatBizTime(o.time)
|
||
if (t) parts.push(t)
|
||
return { main: '患者离开聊天页', sub: parts.length ? parts.join(' · ') : undefined, tag: '诊室' }
|
||
}
|
||
|
||
if (o.businessID === 'user_typing_status') {
|
||
return { main: '对方正在输入…', tag: '状态' }
|
||
}
|
||
|
||
if (o.businessID === 'consultation_complete') {
|
||
return { main: '问诊已完成', sub: formatBizTime(o.time), tag: '诊室' }
|
||
}
|
||
|
||
if (o.businessID === 1 || o.businessID === '1' || o.businessID === 'av_call') {
|
||
let inner: unknown = o.data
|
||
if (typeof inner === 'string') {
|
||
try {
|
||
inner = JSON.parse(inner)
|
||
} catch {
|
||
// fallback
|
||
}
|
||
}
|
||
// 深远兼容:合并外层字段和 data 层字段,避免各端版本取参有遗漏
|
||
const merged = { ...o, ...(typeof inner === 'object' && inner ? inner : {}) }
|
||
return parseRtcInner(merged as Record<string, unknown>)
|
||
}
|
||
|
||
if (o.businessID === 'rtc_call' || o.cmd) {
|
||
return parseRtcInner(o as Record<string, unknown>)
|
||
}
|
||
|
||
return null
|
||
}
|