feat: 重新实现SseClient

This commit is contained in:
2026-06-24 09:33:38 +08:00
parent 112ab3312d
commit 3f6d3f2761
5 changed files with 26 additions and 28 deletions

79
src/common/sse/useSse.ts Normal file
View File

@@ -0,0 +1,79 @@
import { ref } from 'vue'
import { SseClient } from './SseClient.ts'
import type { SseEvents } from './sseBus.ts'
import { sseBus } from './sseBus.ts'
import { useAccountStore } from '@/stores'
/** SSE 连接 URL */
const SSE_URL = '/api/sse/subscribe'
/** 是否正在刷新 token防并发 */
let isRefreshing = false
// ==================== 模块级单例 ====================
const isConnected = ref(false)
const client = new SseClient(SSE_URL, () => useAccountStore().accessToken, {
onConnected() {
isConnected.value = true
sseBus.emit('connected', undefined)
},
onMessage(event: string, data: string) {
try {
const payload = JSON.parse(data)
sseBus.emit(event as keyof SseEvents, payload)
} catch {
console.warn('[SSE] 事件数据解析失败:', event, data)
}
},
onError(message: string) {
sseBus.emit('error', { message, retryCount: 0 })
},
onUnauthorized() {
void refreshTokenAndReconnect()
}
})
// ==================== Token 刷新 ====================
async function refreshTokenAndReconnect(): Promise<void> {
if (isRefreshing) return
const accountStore = useAccountStore()
if (!accountStore.refreshToken) {
sseBus.emit('error', { message: '登录已过期,请重新登录', retryCount: 0 })
return
}
isRefreshing = true
try {
const newToken = await accountStore.refreshAccessToken()
if (newToken) {
await client.connect()
}
} catch {
sseBus.emit('error', { message: '实时连接异常,请刷新页面', retryCount: 0 })
} finally {
isRefreshing = false
}
}
// ==================== Composable ====================
/**
* 全局 SSE 连接管理
*
* SseClient 只管连接生命周期,业务消息通过 sseBus 分发。
* 组件只需 `useSse().connect()` / `useSse().disconnect()`。
*/
export function useSse() {
return {
isConnected,
connect: () => client.connect(),
disconnect: () => {
client.disconnect()
isConnected.value = false
}
}
}