refactor(sse): 改用原生 EventSource + 事件总线,支持多 event name

- 新增 utils/sseEventBus.ts mitt 单例
- sse.ts 从 useEventSource 改为原生 EventSource,显式 addEventListener
- 'message' event 沿用旧通知行为(noticeStore + ElNotification)
- FORWARD_EVENTS(目前 customerservice)转发到事件总线供业务模块订阅
This commit is contained in:
i548450 2026-06-09 11:27:39 +08:00
parent 9a17d63de2
commit 0d03699fcf
2 changed files with 68 additions and 22 deletions

View File

@ -1,42 +1,67 @@
import { getToken } from '@/utils/auth';
import { ElNotification } from 'element-plus';
import { useNoticeStore } from '@/store/modules/notice';
import { getSseEventBus } from '@/utils/sseEventBus';
// Business-level SSE event names forwarded to the global event bus.
// Feature modules subscribe via getSseEventBus().on('xxx', handler).
const FORWARD_EVENTS = ['customerservice'];
const MAX_RETRIES = 5;
const RETRY_DELAY = 5000;
let eventSource: EventSource | null = null;
let retries = 0;
// 初始化
export const initSSE = (url: any) => {
if (import.meta.env.VITE_APP_SSE === 'false') {
return;
}
connect(url);
};
url = url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
const { data, error } = useEventSource(url, [], {
autoReconnect: {
retries: 5,
delay: 5000,
onFailed() {
console.log('Failed to connect after 5 retries');
}
}
});
function connect(url: string) {
const fullUrl = url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
eventSource = new EventSource(fullUrl);
watch(error, () => {
console.log('SSE connection error:', error.value);
error.value = null;
});
watch(data, () => {
if (!data.value) return;
// Legacy "message" event: keep existing notification behavior
eventSource.addEventListener('message', (e: MessageEvent) => {
if (!e.data) return;
useNoticeStore().addNotice({
message: data.value,
message: e.data,
read: false,
time: new Date().toLocaleString()
});
ElNotification({
title: '消息',
message: data.value,
message: e.data,
type: 'success',
duration: 3000
});
data.value = null;
});
};
// Business events: forward to global bus, feature modules subscribe themselves
const bus = getSseEventBus();
FORWARD_EVENTS.forEach((name) => {
eventSource!.addEventListener(name, (e: MessageEvent) => {
bus.emit(name, e.data);
});
});
eventSource.onopen = () => {
retries = 0;
console.log('SSE connected');
};
eventSource.onerror = (err) => {
console.log('SSE connection error:', err);
eventSource?.close();
eventSource = null;
if (retries < MAX_RETRIES) {
retries += 1;
setTimeout(() => connect(url), RETRY_DELAY);
} else {
console.log('SSE failed after', MAX_RETRIES, 'retries');
}
};
}

21
src/utils/sseEventBus.ts Normal file
View File

@ -0,0 +1,21 @@
import mitt, { Emitter } from 'mitt';
/**
* SSE event bus events shape: protocol-level event name -> raw data string
* Consumers JSON.parse the payload themselves
*/
export type SseBusEvents = Record<string, string>;
let bus: Emitter<SseBusEvents> | null = null;
/**
* Lazily-initialized singleton event bus
* Used by sse.ts to forward business event names (e.g. "customerservice")
* to feature modules without creating a second EventSource
*/
export function getSseEventBus(): Emitter<SseBusEvents> {
if (!bus) {
bus = mitt<SseBusEvents>();
}
return bus;
}