2026-06-20 09:19:16 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* WebSocket 连接管理工具
|
|
|
|
|
|
* 职责:建立和维护 WebSocket 连接,接收实时推送消息
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
var auth = require("./auth");
|
|
|
|
|
|
var config = require("../config");
|
|
|
|
|
|
|
|
|
|
|
|
var socketTask = null;
|
|
|
|
|
|
var heartbeatTimer = null;
|
|
|
|
|
|
var reconnectTimer = null;
|
|
|
|
|
|
var reconnectCount = 0;
|
|
|
|
|
|
var MAX_RECONNECT = 5;
|
|
|
|
|
|
var HEARTBEAT_INTERVAL = 30000; // 30秒心跳
|
|
|
|
|
|
var isConnecting = false;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 建立 WebSocket 连接
|
|
|
|
|
|
*/
|
|
|
|
|
|
function connect() {
|
|
|
|
|
|
if (socketTask || isConnecting) return;
|
|
|
|
|
|
|
|
|
|
|
|
var token = auth.getToken();
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
console.log("[WS] 未登录,跳过连接");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isConnecting = true;
|
|
|
|
|
|
var wsUrl = config.wsBaseUrl + "/ws/reminders?token=" + token;
|
|
|
|
|
|
|
|
|
|
|
|
console.log("[WS] 开始连接...");
|
|
|
|
|
|
socketTask = wx.connectSocket({
|
|
|
|
|
|
url: wsUrl,
|
|
|
|
|
|
success: function () {
|
|
|
|
|
|
console.log("[WS] 连接请求已发送");
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
socketTask.onOpen(function () {
|
|
|
|
|
|
console.log("[WS] 连接成功");
|
|
|
|
|
|
isConnecting = false;
|
2026-07-01 10:05:30 +08:00
|
|
|
|
// 如果是重连成功,触发提醒事件让页面刷新数据
|
|
|
|
|
|
if (reconnectCount > 0) {
|
|
|
|
|
|
console.log("[WS] 重连成功,触发提醒事件刷新数据");
|
|
|
|
|
|
handleMessage({ type: "reconnected" });
|
|
|
|
|
|
}
|
2026-06-20 09:19:16 +08:00
|
|
|
|
reconnectCount = 0;
|
|
|
|
|
|
startHeartbeat();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
socketTask.onMessage(function (res) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
var data = JSON.parse(res.data);
|
|
|
|
|
|
if (data.type === "reminder") {
|
|
|
|
|
|
console.log("[WS] 收到提醒:", data.data);
|
|
|
|
|
|
handleMessage(data.data);
|
|
|
|
|
|
} else if (data.type === "connected") {
|
|
|
|
|
|
console.log("[WS] 服务端确认连接:", data.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error("[WS] 消息解析失败:", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
socketTask.onClose(function (res) {
|
|
|
|
|
|
console.log("[WS] 连接关闭:", res);
|
|
|
|
|
|
isConnecting = false;
|
|
|
|
|
|
stopHeartbeat();
|
|
|
|
|
|
socketTask = null;
|
|
|
|
|
|
attemptReconnect();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
socketTask.onError(function (err) {
|
|
|
|
|
|
console.error("[WS] 连接错误:", err);
|
|
|
|
|
|
isConnecting = false;
|
|
|
|
|
|
socketTask = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 断开 WebSocket 连接
|
|
|
|
|
|
*/
|
|
|
|
|
|
function disconnect() {
|
|
|
|
|
|
stopHeartbeat();
|
|
|
|
|
|
stopReconnect();
|
|
|
|
|
|
|
|
|
|
|
|
if (socketTask) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
socketTask.close();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
socketTask = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
isConnecting = false;
|
|
|
|
|
|
console.log("[WS] 已断开连接");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 启动心跳定时器
|
|
|
|
|
|
*/
|
|
|
|
|
|
function startHeartbeat() {
|
|
|
|
|
|
stopHeartbeat();
|
|
|
|
|
|
heartbeatTimer = setInterval(function () {
|
|
|
|
|
|
if (socketTask) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
socketTask.send({ data: "ping" });
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error("[WS] 心跳发送失败:", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, HEARTBEAT_INTERVAL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 停止心跳定时器
|
|
|
|
|
|
*/
|
|
|
|
|
|
function stopHeartbeat() {
|
|
|
|
|
|
if (heartbeatTimer) {
|
|
|
|
|
|
clearInterval(heartbeatTimer);
|
|
|
|
|
|
heartbeatTimer = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 尝试重连
|
|
|
|
|
|
*/
|
|
|
|
|
|
function attemptReconnect() {
|
|
|
|
|
|
if (reconnectCount >= MAX_RECONNECT) {
|
|
|
|
|
|
console.log("[WS] 达到最大重连次数,停止重连");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var delay = Math.min(3000 * (reconnectCount + 1), 15000); // 递增延迟,最大15秒
|
|
|
|
|
|
console.log("[WS] " + delay / 1000 + "秒后尝试第" + (reconnectCount + 1) + "次重连");
|
|
|
|
|
|
|
|
|
|
|
|
reconnectTimer = setTimeout(function () {
|
|
|
|
|
|
reconnectCount++;
|
|
|
|
|
|
connect();
|
|
|
|
|
|
}, delay);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 停止重连
|
|
|
|
|
|
*/
|
|
|
|
|
|
function stopReconnect() {
|
|
|
|
|
|
if (reconnectTimer) {
|
|
|
|
|
|
clearTimeout(reconnectTimer);
|
|
|
|
|
|
reconnectTimer = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
reconnectCount = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理收到的消息
|
|
|
|
|
|
*/
|
|
|
|
|
|
function handleMessage(data) {
|
|
|
|
|
|
var app = getApp();
|
|
|
|
|
|
if (!app) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 触发全局事件
|
|
|
|
|
|
if (app._eventHandlers && app._eventHandlers["reminder"]) {
|
|
|
|
|
|
app._eventHandlers["reminder"].forEach(function (handler) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
handler(data);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error("[WS] 事件处理异常:", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新角标
|
|
|
|
|
|
var badge = require("./badge");
|
|
|
|
|
|
badge.updateTabBadge();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取连接状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
function isConnected() {
|
|
|
|
|
|
return socketTask !== null && !isConnecting;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
|
connect: connect,
|
|
|
|
|
|
disconnect: disconnect,
|
|
|
|
|
|
isConnected: isConnected,
|
|
|
|
|
|
};
|