SSE 推送替代轮训
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { onBeforeUnmount, ref, type Ref } from 'vue'
|
||||
import { getAccessToken, type AuthScope } from '@/utils/authStorage'
|
||||
|
||||
export interface SSEMessage {
|
||||
id: number
|
||||
conversation_id: number
|
||||
sender_type: string
|
||||
sender_id: number
|
||||
sender_role: string
|
||||
sender_name: string
|
||||
content_type: string
|
||||
content: string
|
||||
attachment_urls: string[]
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface ChatEvent {
|
||||
type: 'new_message' | 'conversation_updated'
|
||||
conversation_id: number
|
||||
message?: SSEMessage
|
||||
}
|
||||
|
||||
type EventHandler = (event: ChatEvent) => void
|
||||
|
||||
const reconnectDelay = 3000
|
||||
|
||||
export function useChatSSE(scope: AuthScope, endpoint: string) {
|
||||
const connected: Ref<boolean> = ref(false)
|
||||
let source: EventSource | null = null
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let stopped = false
|
||||
const handlers: EventHandler[] = []
|
||||
|
||||
function onEvent(handler: EventHandler) {
|
||||
handlers.push(handler)
|
||||
}
|
||||
|
||||
function connect() {
|
||||
const token = getAccessToken(scope)
|
||||
if (!token) return
|
||||
|
||||
const url = `${endpoint}?token=${encodeURIComponent(token)}`
|
||||
source = new EventSource(url)
|
||||
|
||||
source.addEventListener('connected', () => {
|
||||
connected.value = true
|
||||
})
|
||||
|
||||
source.addEventListener('new_message', (e) => {
|
||||
try {
|
||||
const data = JSON.parse((e as MessageEvent).data) as ChatEvent
|
||||
handlers.forEach(h => h(data))
|
||||
} catch { /* ignore */ }
|
||||
})
|
||||
|
||||
source.addEventListener('conversation_updated', (e) => {
|
||||
try {
|
||||
const data = JSON.parse((e as MessageEvent).data) as ChatEvent
|
||||
handlers.forEach(h => h(data))
|
||||
} catch { /* ignore */ }
|
||||
})
|
||||
|
||||
source.onerror = () => {
|
||||
connected.value = false
|
||||
source?.close()
|
||||
source = null
|
||||
if (!stopped) {
|
||||
reconnectTimer = setTimeout(connect, reconnectDelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
stopped = true
|
||||
if (reconnectTimer) {
|
||||
clearTimeout(reconnectTimer)
|
||||
reconnectTimer = null
|
||||
}
|
||||
source?.close()
|
||||
source = null
|
||||
connected.value = false
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
disconnect()
|
||||
})
|
||||
|
||||
connect()
|
||||
|
||||
return { connected, onEvent, disconnect }
|
||||
}
|
||||
Reference in New Issue
Block a user