fix: 修复 SSE 令牌过期后自动刷新重连,无令牌重连受最大次数约束,字典同步订阅改为幂等
This commit is contained in:
@@ -19,6 +19,7 @@ function createDictSyncComposable() {
|
|||||||
|
|
||||||
const callbacks: DictChangeCallback[] = [];
|
const callbacks: DictChangeCallback[] = [];
|
||||||
let unsubscribe: (() => void) | null = null;
|
let unsubscribe: (() => void) | null = null;
|
||||||
|
let initialized = false; // 防止重复初始化导致重复订阅
|
||||||
|
|
||||||
// 处理字典变更消息:清除指定字典缓存,并通知所有已注册回调
|
// 处理字典变更消息:清除指定字典缓存,并通知所有已注册回调
|
||||||
const handleDictChange = (data: DictChangeMessage) => {
|
const handleDictChange = (data: DictChangeMessage) => {
|
||||||
@@ -38,13 +39,16 @@ function createDictSyncComposable() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 订阅 SSE 字典变更事件
|
// 订阅 SSE 字典变更事件(幂等:重复调用不会产生重复订阅)
|
||||||
const initialize = () => {
|
const initialize = () => {
|
||||||
|
if (initialized) return;
|
||||||
|
initialized = true;
|
||||||
unsubscribe = sse.on(SseTopics.DICT, handleDictChange);
|
unsubscribe = sse.on(SseTopics.DICT, handleDictChange);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取消 SSE 订阅并清空所有回调
|
// 取消 SSE 订阅并清空所有回调
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
|
initialized = false;
|
||||||
unsubscribe?.();
|
unsubscribe?.();
|
||||||
unsubscribe = null;
|
unsubscribe = null;
|
||||||
callbacks.length = 0;
|
callbacks.length = 0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AuthStorage } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
|
import { useUserStoreHook } from "@/stores/user";
|
||||||
|
|
||||||
/** SSE 连接配置选项 */
|
/** SSE 连接配置选项 */
|
||||||
export interface UseSseOptions {
|
export interface UseSseOptions {
|
||||||
@@ -58,6 +59,7 @@ function createSseConnection(options: UseSseOptions = {}) {
|
|||||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
let reconnectAttempts = 0;
|
let reconnectAttempts = 0;
|
||||||
let currentReconnectInterval = config.reconnectInterval;
|
let currentReconnectInterval = config.reconnectInterval;
|
||||||
|
let tokenRefreshed = false; // 本轮拒绝是否已刷新过令牌,防止无限刷新
|
||||||
|
|
||||||
const eventHandlers = new Map<string, Set<EventHandler>>();
|
const eventHandlers = new Map<string, Set<EventHandler>>();
|
||||||
|
|
||||||
@@ -189,7 +191,8 @@ function createSseConnection(options: UseSseOptions = {}) {
|
|||||||
const token = AuthStorage.getAccessToken();
|
const token = AuthStorage.getAccessToken();
|
||||||
if (!token) {
|
if (!token) {
|
||||||
log("未检测到有效令牌,稍后重试");
|
log("未检测到有效令牌,稍后重试");
|
||||||
reconnectTimer = setTimeout(() => connect(), config.reconnectInterval);
|
// 走统一重连调度,受 maxReconnectAttempts 上限约束
|
||||||
|
scheduleReconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,9 +216,26 @@ function createSseConnection(options: UseSseOptions = {}) {
|
|||||||
},
|
},
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401 || response.status === 403) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
// 令牌过期:刷新后用新令牌重连,刷新失败或令牌仍无效则停止重连
|
||||||
|
if (!tokenRefreshed) {
|
||||||
|
tokenRefreshed = true;
|
||||||
|
connectionTimeoutTimer = clearTimer(connectionTimeoutTimer);
|
||||||
|
try {
|
||||||
|
const userStore = useUserStoreHook();
|
||||||
|
await userStore.refreshTokenOnce();
|
||||||
|
if (AuthStorage.getAccessToken()) {
|
||||||
|
connectionState.value = SseConnectionState.DISCONNECTED;
|
||||||
|
log(`SSE 连接被拒绝(HTTP ${response.status}),令牌已刷新,使用新令牌重连`);
|
||||||
|
connect();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logError("SSE 令牌刷新失败:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
isManualDisconnect = true;
|
isManualDisconnect = true;
|
||||||
connectionState.value = SseConnectionState.DISCONNECTED;
|
connectionState.value = SseConnectionState.DISCONNECTED;
|
||||||
log(`SSE 连接被拒绝(HTTP ${response.status}),不再重连`);
|
log(`SSE 连接被拒绝(HTTP ${response.status}),不再重连`);
|
||||||
@@ -225,6 +245,7 @@ function createSseConnection(options: UseSseOptions = {}) {
|
|||||||
}
|
}
|
||||||
connectionTimeoutTimer = clearTimer(connectionTimeoutTimer);
|
connectionTimeoutTimer = clearTimer(connectionTimeoutTimer);
|
||||||
connectionState.value = SseConnectionState.CONNECTED;
|
connectionState.value = SseConnectionState.CONNECTED;
|
||||||
|
tokenRefreshed = false;
|
||||||
resetReconnectState();
|
resetReconnectState();
|
||||||
log("SSE 连接已建立");
|
log("SSE 连接已建立");
|
||||||
return response.body?.getReader();
|
return response.body?.getReader();
|
||||||
|
|||||||
Reference in New Issue
Block a user