diff --git a/src/frontend/src/store.ts b/src/frontend/src/store.ts index 0479b6b..a29e798 100644 --- a/src/frontend/src/store.ts +++ b/src/frontend/src/store.ts @@ -678,11 +678,14 @@ export function timeAgo(iso: string | undefined): string { // ── SSE 实时事件监听 ── let _es: EventSource | null = null; +let _sseRetryCount = 0; +const SSE_MAX_RETRY = 5; export function startSSE() { if (_es) return; try { _es = new EventSource('/api/events'); + _es.onopen = () => { _sseRetryCount = 0; }; _es.addEventListener('task_updated', (e: MessageEvent) => { try { const data = JSON.parse(e.data); @@ -724,10 +727,39 @@ export function startSSE() { } } catch { /* ignore */ } }); + // 将事件推入 store 供 NotificationCenter 消费 + _es.onmessage = (e: MessageEvent) => { + try { + const sseEvents = useStore.getState().sseEvents as NotifItem[]; + const data = JSON.parse(e.data); + const etype = (e as any).type || data.event_type || ''; + const importantTypes = ['task_completed', 'task_failed', 'review_result', 'task_updated']; + if (!importantTypes.includes(etype)) return; + const ntype = etype === 'task_failed' ? 'error' : etype === 'task_completed' ? 'success' : etype === 'review_result' ? 'warning' : 'info'; + const item: NotifItem = { + id: data.id || `sse-${Date.now()}`, + type: ntype, + title: data.task_id ? `任务 ${data.task_id.slice(0, 8)}...` : etype, + message: `${data.old_status || ''} → ${data.new_status || etype}`, + time: data.timestamp || new Date().toISOString(), + read: false, + source: 'event', + taskId: data.task_id, + projectId: data.project_id, + }; + useStore.setState({ sseEvents: [item, ...sseEvents].slice(0, 30) }); + } catch { /* ignore */ } + }; _es.onerror = () => { _es?.close(); _es = null; - setTimeout(startSSE, 3000); + _sseRetryCount++; + if (_sseRetryCount > SSE_MAX_RETRY) { + console.warn(`SSE failed ${SSE_MAX_RETRY} times, stopping. Manual refresh required.`); + return; + } + const delay = Math.min(3000 * Math.pow(2, _sseRetryCount - 1), 30000); + setTimeout(startSSE, delay); }; } catch { /* ignore */ } } @@ -737,4 +769,5 @@ export function stopSSE() { _es.close(); _es = null; } + _sseRetryCount = 0; }