import { getToken } from "../until"; let ws = null; let reconnectTimer = null; let manualClose = false; let retryCount = 0; const subscribers = new Set(); const WS_PATH = "/ws"; const MAX_RECONNECT_DELAY = 30000; function getWsBaseUrl() { const baseApi = import.meta.env.VITE_APP_BASE_API || ""; if (/^https?:\/\//.test(baseApi)) { return baseApi.replace(/^http/, "ws").replace(/\/$/, ""); } // UniApp 没有 window,用 uni 接口获取 const platform = uni.getSystemInfoSync().platform; const isH5 = platform === "h5"; const protocol = isH5 && window.location.protocol === "https:" ? "wss:" : "ws:"; let host = ""; if (isH5) { host = window.location.host; } else { // 非H5(小程序/App)需要你配置真实域名 host = "你的后端域名"; } return `${protocol}//${host}${baseApi}`.replace(/\/$/, ""); } function buildWsUrl() { const baseUrl = getWsBaseUrl(); const url = `${baseUrl}${WS_PATH}`; const token = getToken(); const params = new URLSearchParams(); if (token) { params.append("token", token); } const query = params.toString(); return query ? `${url}?${query}` : url; } export function connectWs(onMessage) { if (!getToken()) { console.warn("[WebSocket] missing token"); return; } if (typeof onMessage === "function") { subscribers.add(onMessage); } manualClose = false; clearReconnectTimer(); if (ws) { return; } // ========== UniApp 专用 ========== ws = uni.connectSocket({ url: buildWsUrl(), success: () => {}, }); ws.onOpen = () => { retryCount = 0; console.log("[WebSocket] connected"); }; ws.onMessage = (event) => { let data = event.data; try { data = JSON.parse(event.data); } catch (e) {} subscribers.forEach((handler) => { try { handler(data, event); } catch (error) { console.error("[WebSocket] subscriber error", error); } }); }; ws.onClose = () => { console.log("[WebSocket] closed"); ws = null; if (!manualClose && subscribers.size > 0) { reconnectTimer = setTimeout(() => { console.log("[WebSocket] reconnecting"); connectWs(); }, getReconnectDelay()); } }; ws.onError = (error) => { console.error("[WebSocket] error", error); }; } export function closeWs(onMessage) { if (typeof onMessage === "function") { subscribers.delete(onMessage); } else { subscribers.clear(); } if (subscribers.size > 0) { return; } manualClose = true; retryCount = 0; clearReconnectTimer(); if (ws) { ws.close(); ws = null; } } export function reconnectWs(onMessage) { if (typeof onMessage === "function") { subscribers.add(onMessage); } manualClose = false; retryCount = 0; clearReconnectTimer(); if (ws) { ws.close(); ws = null; } manualClose = false; connectWs(); } export function sendWs(data) { if (!ws) { console.warn("[WebSocket] send failed, socket is not open"); return false; } ws.send({ data: typeof data === "string" ? data : JSON.stringify(data), success: () => {}, fail: () => {}, }); return true; } export function getWsState() { if (!ws) return 3; // CLOSED return ws.readyState; } function getReconnectDelay() { retryCount += 1; return Math.min(5000 * retryCount, MAX_RECONNECT_DELAY); } function clearReconnectTimer() { if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } }