优化sse

This commit is contained in:
yml2213
2026-05-29 11:09:15 +08:00
parent 2346f2e064
commit a334f1837d
3 changed files with 40 additions and 12 deletions
+6 -5
View File
@@ -191,14 +191,15 @@ func (r *Repository) EnsureSupportConversation(userID uint64) (*ConversationDTO,
Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id").
Where("c.type = ? AND cp.participant_type = ? AND cp.participant_id = ?", "general_support", "user", userID).
Order("c.id ASC").
First(&existing).Error
if err == nil {
Limit(1).
Find(&existing).Error
if err != nil {
return err
}
if existing.ID > 0 {
conversationID = existing.ID
return nil
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
now := time.Now()
conversation := model.ChatConversation{
+2 -2
View File
@@ -73,7 +73,7 @@ function rejectPendingRequests(scope: AuthScope, error: unknown) {
pendingRequests.forEach(({ reject }) => reject(error))
}
async function refreshToken(scope: AuthScope): Promise<string> {
export async function refreshAccessToken(scope: AuthScope): Promise<string> {
const refreshToken = getRefreshToken(scope)
if (!refreshToken) throw new Error('no refresh token')
@@ -153,7 +153,7 @@ apiClient.interceptors.response.use(
state.refreshing = true
try {
const newToken = await refreshToken(scope)
const newToken = await refreshAccessToken(scope)
resolvePendingRequests(scope, newToken)
originalRequest._retry = true
originalRequest.headers.Authorization = `Bearer ${newToken}`
+32 -5
View File
@@ -1,4 +1,5 @@
import { onBeforeUnmount, ref, type Ref } from 'vue'
import { refreshAccessToken } from '@/api/client'
import { getAccessToken, type AuthScope } from '@/utils/authStorage'
export interface SSEMessage {
@@ -29,6 +30,7 @@ export function useChatSSE(scope: AuthScope, endpoint: string) {
let source: EventSource | null = null
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
let stopped = false
let refreshing = false
const handlers: EventHandler[] = []
function onEvent(handler: EventHandler) {
@@ -36,6 +38,7 @@ export function useChatSSE(scope: AuthScope, endpoint: string) {
}
function connect() {
if (stopped || source) return
const token = getAccessToken(scope)
if (!token) return
@@ -60,18 +63,27 @@ export function useChatSSE(scope: AuthScope, endpoint: string) {
} catch { /* ignore */ }
})
source.onerror = () => {
source.onerror = async () => {
connected.value = false
source?.close()
source = null
if (!stopped) {
reconnectTimer = setTimeout(connect, reconnectDelay)
if (stopped || refreshing) return
refreshing = true
try {
await refreshAccessToken(scope)
if (!stopped) {
reconnectTimer = setTimeout(connect, reconnectDelay)
}
} catch {
closeSource()
} finally {
refreshing = false
}
}
}
function disconnect() {
stopped = true
function closeSource() {
if (reconnectTimer) {
clearTimeout(reconnectTimer)
reconnectTimer = null
@@ -81,6 +93,21 @@ export function useChatSSE(scope: AuthScope, endpoint: string) {
connected.value = false
}
function handleAuthStorageChanged(event: Event) {
const detail = (event as CustomEvent<{ scope?: AuthScope }>).detail
if (detail?.scope !== scope || stopped) return
closeSource()
connect()
}
function disconnect() {
stopped = true
closeSource()
window.removeEventListener('auth-storage-changed', handleAuthStorageChanged)
}
window.addEventListener('auth-storage-changed', handleAuthStorageChanged)
onBeforeUnmount(() => {
disconnect()
})