"use strict"; const common_vendor = require("../common/vendor.js"); const utils_request = require("./request.js"); const BASE_URL = utils_request.UPLOAD_URL; function getWsUrl() { return BASE_URL.replace(/^http/, "ws") + "/api/chat/ws/chat/websocket"; } let socketTask = null; let isConnected = false; let subscriptionId = 0; const subscriptions = {}; let reconnectCount = 0; const MAX_RECONNECT = 5; let reconnectTimer = null; let heartbeatTimer = null; let connectHeaders = {}; let onConnectCallback = null; let onDisconnectCallback = null; function connectChat(options = {}) { const { token, onConnect, onDisconnect } = options; onConnectCallback = onConnect || null; onDisconnectCallback = onDisconnect || null; connectHeaders = {}; if (token) { connectHeaders["Authorization"] = "Bearer " + token; } const wsUrl = getWsUrl(); common_vendor.index.__f__("log", "at utils/chatSocket.js:46", "[ChatSocket] 连接中...", wsUrl); socketTask = common_vendor.index.connectSocket({ url: wsUrl, header: { "Authorization": connectHeaders["Authorization"] || "" }, success: () => { common_vendor.index.__f__("log", "at utils/chatSocket.js:54", "[ChatSocket] 连接请求已发送"); }, fail: (err) => { common_vendor.index.__f__("error", "at utils/chatSocket.js:57", "[ChatSocket] 连接失败", err); scheduleReconnect(); } }); socketTask.onOpen(() => { common_vendor.index.__f__("log", "at utils/chatSocket.js:63", "[ChatSocket] WebSocket 已打开,发送 STOMP CONNECT"); sendStompConnect(); }); socketTask.onMessage((res) => { handleStompFrame(res.data); }); socketTask.onClose((res) => { common_vendor.index.__f__("log", "at utils/chatSocket.js:72", "[ChatSocket] WebSocket 已关闭", res); isConnected = false; stopHeartbeat(); if (onDisconnectCallback) onDisconnectCallback(); scheduleReconnect(); }); socketTask.onError((err) => { common_vendor.index.__f__("error", "at utils/chatSocket.js:80", "[ChatSocket] WebSocket 错误", err); isConnected = false; }); } function sendStompConnect() { const headers = { "accept-version": "1.1,1.0", "heart-beat": "10000,10000" }; if (connectHeaders["Authorization"]) { headers["Authorization"] = connectHeaders["Authorization"]; } sendFrame("CONNECT", headers, ""); } function handleStompFrame(data) { if (!data || typeof data !== "string") return; const frames = data.split("\0"); for (const raw of frames) { if (!raw.trim()) continue; parseFrame(raw.trim()); } } function parseFrame(raw) { const lines = raw.split("\n"); if (lines.length === 0) return; const command = lines[0].trim(); const headers = {}; let bodyStart = 0; for (let i = 1; i < lines.length; i++) { const line = lines[i]; if (line === "") { bodyStart = i + 1; break; } const colonIdx = line.indexOf(":"); if (colonIdx > 0) { headers[line.substring(0, colonIdx).trim()] = line.substring(colonIdx + 1).trim(); } } const body = lines.slice(bodyStart).join("\n").replace(/\0$/, "").trim(); switch (command) { case "CONNECTED": common_vendor.index.__f__("log", "at utils/chatSocket.js:142", "[ChatSocket] STOMP 连接成功"); isConnected = true; reconnectCount = 0; startHeartbeat(); Object.keys(subscriptions).forEach((id) => { const sub = subscriptions[id]; sendFrame("SUBSCRIBE", { id, destination: sub.destination, ack: "auto" }, ""); }); if (onConnectCallback) onConnectCallback(); break; case "MESSAGE": handleStompMessage(headers, body); break; case "RECEIPT": common_vendor.index.__f__("log", "at utils/chatSocket.js:159", "[ChatSocket] 收到回执:", headers["receipt-id"]); break; case "ERROR": common_vendor.index.__f__("error", "at utils/chatSocket.js:163", "[ChatSocket] STOMP 错误:", headers["message"], body); break; } } function handleStompMessage(headers, body) { const destination = headers["destination"]; if (!destination) return; let data = body; try { data = JSON.parse(body); } catch (e) { } Object.keys(subscriptions).forEach((id) => { const sub = subscriptions[id]; if (destination === sub.destination || destination.startsWith(sub.destination)) { sub.callback(data, headers); } }); } function subscribe(destination, callback) { const id = "sub-" + ++subscriptionId; subscriptions[id] = { destination, callback }; if (isConnected) { sendFrame("SUBSCRIBE", { id, destination, ack: "auto" }, ""); } return id; } function unsubscribe(id) { if (subscriptions[id]) { if (isConnected) { sendFrame("UNSUBSCRIBE", { id }, ""); } delete subscriptions[id]; } } function sendTextByWs(sessionId, content, msgType = "text", msgNo = "", senderId = null) { if (!isConnected) { common_vendor.index.__f__("warn", "at utils/chatSocket.js:238", "[ChatSocket] 未连接,无法发送消息"); return false; } if (!msgNo) { msgNo = generateMsgNo(); } const bodyObj = { msgType, sessionId, msgNo, content }; if (senderId) { bodyObj.senderId = senderId; } const body = JSON.stringify(bodyObj); sendFrame("SEND", { destination: "/app/chat/send", "content-type": "application/json" }, body); return true; } function disconnectChat() { if (socketTask && isConnected) { sendFrame("DISCONNECT", { receipt: "disconnect-" + Date.now() }, ""); } clearAll(); if (socketTask) { socketTask.close({}); socketTask = null; } } function clearAll() { isConnected = false; stopHeartbeat(); if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } Object.keys(subscriptions).forEach((id) => delete subscriptions[id]); } function sendFrame(command, headers, body) { if (!socketTask) return; let frame = command + "\n"; if (headers) { Object.keys(headers).forEach((key) => { frame += key + ":" + headers[key] + "\n"; }); } frame += "\n"; if (body) { frame += body; } frame += "\0"; socketTask.send({ data: frame, fail: (err) => { common_vendor.index.__f__("error", "at utils/chatSocket.js:314", "[ChatSocket] 发送帧失败:", command, err); } }); } function startHeartbeat() { stopHeartbeat(); heartbeatTimer = setInterval(() => { if (isConnected && socketTask) { socketTask.send({ data: "\n", fail: () => { } }); } }, 1e4); } function stopHeartbeat() { if (heartbeatTimer) { clearInterval(heartbeatTimer); heartbeatTimer = null; } } function scheduleReconnect() { if (reconnectCount >= MAX_RECONNECT) { common_vendor.index.__f__("log", "at utils/chatSocket.js:347", "[ChatSocket] 达到最大重连次数,停止重连"); return; } reconnectCount++; const delay = Math.min(3e3 * reconnectCount, 15e3); common_vendor.index.__f__("log", "at utils/chatSocket.js:352", `[ChatSocket] ${delay}ms 后进行第 ${reconnectCount} 次重连...`); reconnectTimer = setTimeout(() => { const token = common_vendor.index.getStorageSync("token"); connectChat({ token, onConnect: onConnectCallback, onDisconnect: onDisconnectCallback }); }, delay); } function generateMsgNo() { return "MSG_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8); } function isSocketConnected() { return isConnected; } exports.connectChat = connectChat; exports.disconnectChat = disconnectChat; exports.generateMsgNo = generateMsgNo; exports.isSocketConnected = isSocketConnected; exports.sendTextByWs = sendTextByWs; exports.subscribe = subscribe; exports.unsubscribe = unsubscribe; //# sourceMappingURL=../../.sourcemap/mp-weixin/utils/chatSocket.js.map