179 lines
3.4 KiB
JavaScript
179 lines
3.4 KiB
JavaScript
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;
|
||
}
|
||
}
|