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