218 lines
5.5 KiB
JavaScript
218 lines
5.5 KiB
JavaScript
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,
|
|
};
|