import { create } from 'zustand'; import { getUnreadCount, listMessages, markRead, type MessageInfo } from '../api/messages'; const INITIAL_DELAY_MS = 1000; const MAX_DELAY_MS = 30_000; const MAX_RETRIES = 10; interface MessageState { unreadCount: number; recentMessages: MessageInfo[]; fetchUnreadCount: () => Promise; fetchRecentMessages: () => Promise; markAsRead: (id: string) => Promise; connectSSE: () => () => void; } // 请求去重:记录正在进行的请求,防止并发重复调用 let unreadCountPromise: Promise | null = null; let recentMessagesPromise: Promise | null = null; export const useMessageStore = create((set, get) => ({ unreadCount: 0, recentMessages: [], fetchUnreadCount: async () => { // 如果已有进行中的请求,复用该 Promise if (unreadCountPromise) { await unreadCountPromise; return; } unreadCountPromise = (async () => { try { const result = await getUnreadCount(); set({ unreadCount: result.count }); } catch { // 静默失败,不影响用户体验 } finally { unreadCountPromise = null; } })(); await unreadCountPromise; }, fetchRecentMessages: async () => { if (recentMessagesPromise) { await recentMessagesPromise; return; } recentMessagesPromise = (async () => { try { const result = await listMessages({ page: 1, page_size: 5 }); set({ recentMessages: result.data }); } catch { // 静默失败 } finally { recentMessagesPromise = null; } })(); await recentMessagesPromise; }, markAsRead: async (id: string) => { const prev = { unreadCount: get().unreadCount, recentMessages: get().recentMessages }; set((state) => ({ unreadCount: Math.max(0, state.unreadCount - 1), recentMessages: state.recentMessages.map((m) => m.id === id ? { ...m, is_read: true } : m, ), })); try { await markRead(id); } catch { set({ unreadCount: prev.unreadCount, recentMessages: prev.recentMessages }); } }, connectSSE: () => { let es: EventSource | null = null; let retryCount = 0; let reconnectTimer: ReturnType | null = null; let disposed = false; const clearReconnectTimer = () => { if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } }; const connect = () => { if (es) { es.close(); es = null; } clearReconnectTimer(); const token = localStorage.getItem('access_token'); if (!token || disposed) return; const baseUrl = import.meta.env.VITE_API_BASE_URL || '/api/v1'; const url = `${baseUrl}/messages/stream?token=${encodeURIComponent(token)}`; es = new EventSource(url); es.onopen = () => { retryCount = 0; }; es.addEventListener('message', () => { get().fetchUnreadCount(); get().fetchRecentMessages(); }); es.addEventListener('alert', () => { get().fetchUnreadCount(); }); es.addEventListener('vital_update', () => { // 体征数据更新事件 — 预留:未来可触发趋势图刷新 }); es.onerror = () => { es?.close(); es = null; retryCount += 1; if (retryCount > MAX_RETRIES || disposed) return; const delay = Math.min( INITIAL_DELAY_MS * Math.pow(2, retryCount - 1), MAX_DELAY_MS, ); const jitter = delay * (0.5 + Math.random() * 0.5); reconnectTimer = setTimeout(() => { reconnectTimer = null; if (disposed) return; const tokenNow = localStorage.getItem('access_token'); if (!tokenNow) return; connect(); }, jitter); }; }; connect(); return () => { disposed = true; if (es) { es.close(); es = null; } clearReconnectTimer(); }; }, }));