import { MESSAGETYPES, getMessageTypeName } from "@/constants"; import { getUserGameState } from "@/apis"; let socket = null; let heartbeatInterval = null; let reconnectTimer = null; let manualClose = false; let checkingSession = false; let kickedOut = false; let isConnecting = false; function createWebSocket(token, onMessage) { if (!token) return; if (kickedOut) kickedOut = false; if (socket || isConnecting) return; manualClose = false; isConnecting = true; let url = "wss://api.shelingxingqiu.com/socket"; try { const accountInfo = uni.getAccountInfoSync(); const envVersion = accountInfo.miniProgram.envVersion; switch (envVersion) { case "develop": // 开发版 // url = "ws://192.168.1.30:8000/socket"; url = "wss://apitest.shelingxingqiu.com/socket"; break; case "trial": // 体验版 url = "wss://apitest.shelingxingqiu.com/socket"; break; case "trial": url = "wss://apitest.shelingxingqiu.com/socket"; break; case "release": url = "wss://api.shelingxingqiu.com/socket"; break; default: break; } } catch (e) { console.error("获取 WebSocket 环境信息失败,使用默认正式环境", e); } url += `?authorization=${token}`; const socketTask = uni.connectSocket({ url, success: () => { console.log("WebSocket 已发起连接"); }, fail: (err) => { if (socket !== socketTask) return; console.error("WebSocket 连接失败", err); socket = null; isConnecting = false; reconnect(onMessage); }, }); socket = socketTask; socketTask.onOpen(() => { if (socket !== socketTask) return; console.log("WebSocket 连接成功"); isConnecting = false; startHeartbeat(onMessage); }); socketTask.onMessage((res) => { if (socket !== socketTask) return; const { data, event } = JSON.parse(res.data); if (event === "pong") return; if (data.type) { console.log( "收到 WebSocket 消息", getMessageTypeName(data.type), data.data ); if (onMessage) onMessage({ ...(data.data || {}), type: data.type }); return; } if (onMessage && data.updates) onMessage(data.updates); const msg = data.updates[0]; if (msg) { console.log( "收到 WebSocket 更新", getMessageTypeName(msg.constructor), msg ); if (msg.constructor === MESSAGETYPES.RankUpdate) { uni.setStorageSync("latestRank", msg.lvl); } else if (msg.constructor === MESSAGETYPES.LvlUpdate) { uni.setStorageSync("latestLvl", msg.lvl); } else if (msg.constructor === MESSAGETYPES.DeviceOnline) { uni.$emit("update-online"); } else if (msg.constructor === MESSAGETYPES.DeviceOffline) { uni.$emit("update-online"); } } }); socketTask.onError((err) => { if (socket !== socketTask) return; console.error("WebSocket 错误", err); }); socketTask.onClose(async (result) => { if (socket !== socketTask) return; console.log("WebSocket 已关闭", result); stopHeartbeat(); socket = null; isConnecting = false; if (manualClose || kickedOut) return; await handleUnexpectedClose(onMessage); }); } async function handleUnexpectedClose(onMessage) { if (checkingSession || manualClose || kickedOut) return; const token = uni.getStorageSync( `${uni.getAccountInfoSync().miniProgram.envVersion}_token` ); if (!token) return; checkingSession = true; try { await getUserGameState(); if (!manualClose && !kickedOut) reconnect(onMessage); } catch (err) { if (err?.type === "AUTH_INVALID") { kickedOut = true; manualClose = true; reconnectTimer && clearTimeout(reconnectTimer); uni.$emit("session-kicked-out"); return; } if (!manualClose && !kickedOut) reconnect(onMessage); } finally { checkingSession = false; } } function reconnect(onMessage) { reconnectTimer && clearTimeout(reconnectTimer); const token = uni.getStorageSync( `${uni.getAccountInfoSync().miniProgram.envVersion}_token` ); if (!token || manualClose || kickedOut || socket || isConnecting) return; reconnectTimer = setTimeout(() => { if (manualClose || kickedOut || socket || isConnecting) return; console.log("WebSocket 正在重连..."); createWebSocket(token, onMessage); }, 1000); } function closeWebSocket(isManual = true) { manualClose = isManual; reconnectTimer && clearTimeout(reconnectTimer); stopHeartbeat(); isConnecting = false; if (socket) { const currentSocket = socket; socket = null; try { currentSocket.close(); } catch (err) { console.error("关闭 WebSocket 失败", err); } } } function sendHeartbeat(onMessage) { if (!socket) return; const currentSocket = socket; currentSocket.send({ data: JSON.stringify({ event: "ping", data: {} }), success: () => {}, fail: (err) => { if (socket !== currentSocket) return; console.error("心跳发送失败", err); stopHeartbeat(); closeWebSocket(false); reconnect(onMessage); }, }); } function startHeartbeat(onMessage) { stopHeartbeat(); heartbeatInterval = setInterval(() => { if (socket) { sendHeartbeat(onMessage); } }, 10000); } function stopHeartbeat() { if (heartbeatInterval) { clearInterval(heartbeatInterval); heartbeatInterval = null; } } export default { createWebSocket, closeWebSocket, };