update:优化多端登录
This commit is contained in:
23
src/App.vue
23
src/App.vue
@@ -21,7 +21,10 @@
|
|||||||
} = storeToRefs(store);
|
} = storeToRefs(store);
|
||||||
const {
|
const {
|
||||||
updateUser,
|
updateUser,
|
||||||
updateOnline
|
updateOnline,
|
||||||
|
updateDevice,
|
||||||
|
updateGame,
|
||||||
|
updateRoomNumber
|
||||||
} = store;
|
} = store;
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -46,6 +49,22 @@
|
|||||||
updateUser(value);
|
updateUser(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSessionKickedOut() {
|
||||||
|
uni.removeStorageSync(
|
||||||
|
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
||||||
|
);
|
||||||
|
updateUser();
|
||||||
|
updateDevice("", "");
|
||||||
|
updateOnline(false);
|
||||||
|
updateGame(false, "");
|
||||||
|
updateRoomNumber("");
|
||||||
|
uni.showModal({
|
||||||
|
title: "提示",
|
||||||
|
content: "账号已在其他设备登录",
|
||||||
|
showCancel: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function emitUpdateOnline() {
|
async function emitUpdateOnline() {
|
||||||
const data = await getDeviceBatteryAPI();
|
const data = await getDeviceBatteryAPI();
|
||||||
updateOnline(data.online);
|
updateOnline(data.online);
|
||||||
@@ -65,6 +84,7 @@
|
|||||||
onShow(() => {
|
onShow(() => {
|
||||||
uni.$on("update-user", emitUpdateUser);
|
uni.$on("update-user", emitUpdateUser);
|
||||||
uni.$on("update-online", emitUpdateOnline);
|
uni.$on("update-online", emitUpdateOnline);
|
||||||
|
uni.$on("session-kicked-out", onSessionKickedOut);
|
||||||
const token = uni.getStorageSync(
|
const token = uni.getStorageSync(
|
||||||
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
||||||
);
|
);
|
||||||
@@ -77,6 +97,7 @@
|
|||||||
onHide(() => {
|
onHide(() => {
|
||||||
uni.$off("update-user", emitUpdateUser);
|
uni.$off("update-user", emitUpdateUser);
|
||||||
uni.$off("update-online", emitUpdateOnline);
|
uni.$off("update-online", emitUpdateOnline);
|
||||||
|
uni.$off("session-kicked-out", onSessionKickedOut);
|
||||||
websocket.closeWebSocket();
|
websocket.closeWebSocket();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ function request(method, url, data = {}) {
|
|||||||
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
||||||
);
|
);
|
||||||
uni.$emit("update-user");
|
uni.$emit("update-user");
|
||||||
|
reject({ type: "AUTH_INVALID", message });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (message === "ROOM_FULL") {
|
if (message === "ROOM_FULL") {
|
||||||
resolve({full: true});
|
resolve({full: true});
|
||||||
|
|||||||
157
src/websocket.js
157
src/websocket.js
@@ -1,61 +1,91 @@
|
|||||||
import { MESSAGETYPES, getMessageTypeName } from "@/constants";
|
import { MESSAGETYPES, getMessageTypeName } from "@/constants";
|
||||||
|
import { getUserGameState } from "@/apis";
|
||||||
|
|
||||||
let socket = null;
|
let socket = null;
|
||||||
let heartbeatInterval = null;
|
let heartbeatInterval = null;
|
||||||
let reconnectTimer = null;
|
let reconnectTimer = null;
|
||||||
|
let manualClose = false;
|
||||||
|
let checkingSession = false;
|
||||||
|
let kickedOut = false;
|
||||||
|
let isConnecting = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* 建立 WebSocket 连接
|
|
||||||
*/
|
|
||||||
function createWebSocket(token, onMessage) {
|
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";
|
let url = "wss://api.shelingxingqiu.com/socket";
|
||||||
try {
|
try {
|
||||||
const accountInfo = uni.getAccountInfoSync();
|
const accountInfo = uni.getAccountInfoSync();
|
||||||
const envVersion = accountInfo.miniProgram.envVersion;
|
const envVersion = accountInfo.miniProgram.envVersion;
|
||||||
|
|
||||||
switch (envVersion) {
|
switch (envVersion) {
|
||||||
case "develop": // 开发版
|
case "develop":
|
||||||
// url = "ws://localhost:8000/socket";
|
// url = "ws://localhost:8000/socket";
|
||||||
url = "wss://apitest.shelingxingqiu.com/socket";
|
url = "wss://apitest.shelingxingqiu.com/socket";
|
||||||
break;
|
break;
|
||||||
case "trial": // 体验版
|
case "trial":
|
||||||
url = "wss://apitest.shelingxingqiu.com/socket";
|
url = "wss://apitest.shelingxingqiu.com/socket";
|
||||||
break;
|
break;
|
||||||
case "release": // 正式版
|
case "release":
|
||||||
url = "wss://api.shelingxingqiu.com/socket";
|
url = "wss://api.shelingxingqiu.com/socket";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// 保持默认值
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("获取环境信息失败,使用默认正式环境", e);
|
console.error("获取 WebSocket 环境信息失败,使用默认正式环境", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
url += `?authorization=${token}`;
|
url += `?authorization=${token}`;
|
||||||
socket = uni.connectSocket({
|
const socketTask = uni.connectSocket({
|
||||||
url,
|
url,
|
||||||
success: () => {
|
success: () => {
|
||||||
console.log("websocket 连接成功");
|
console.log("WebSocket 已发起连接");
|
||||||
// 启动心跳
|
|
||||||
startHeartbeat(onMessage);
|
|
||||||
},
|
},
|
||||||
fail: () => {
|
fail: (err) => {
|
||||||
|
if (socket !== socketTask) return;
|
||||||
|
console.error("WebSocket 连接失败", err);
|
||||||
|
socket = null;
|
||||||
|
isConnecting = false;
|
||||||
reconnect(onMessage);
|
reconnect(onMessage);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 接收消息
|
socket = socketTask;
|
||||||
uni.onSocketMessage((res) => {
|
|
||||||
|
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);
|
const { data, event } = JSON.parse(res.data);
|
||||||
if (event === "pong") return;
|
if (event === "pong") return;
|
||||||
if (data.type) {
|
if (data.type) {
|
||||||
console.log("收到消息:", getMessageTypeName(data.type), data.data);
|
console.log(
|
||||||
|
"收到 WebSocket 消息",
|
||||||
|
getMessageTypeName(data.type),
|
||||||
|
data.data
|
||||||
|
);
|
||||||
if (onMessage) onMessage({ ...(data.data || {}), type: data.type });
|
if (onMessage) onMessage({ ...(data.data || {}), type: data.type });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (onMessage && data.updates) onMessage(data.updates);
|
if (onMessage && data.updates) onMessage(data.updates);
|
||||||
const msg = data.updates[0];
|
const msg = data.updates[0];
|
||||||
if (msg) {
|
if (msg) {
|
||||||
console.log("收到消息:", getMessageTypeName(msg.constructor), msg);
|
console.log(
|
||||||
|
"收到 WebSocket 更新",
|
||||||
|
getMessageTypeName(msg.constructor),
|
||||||
|
msg
|
||||||
|
);
|
||||||
if (msg.constructor === MESSAGETYPES.RankUpdate) {
|
if (msg.constructor === MESSAGETYPES.RankUpdate) {
|
||||||
uni.setStorageSync("latestRank", msg.lvl);
|
uni.setStorageSync("latestRank", msg.lvl);
|
||||||
} else if (msg.constructor === MESSAGETYPES.LvlUpdate) {
|
} else if (msg.constructor === MESSAGETYPES.LvlUpdate) {
|
||||||
@@ -68,84 +98,109 @@ function createWebSocket(token, onMessage) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 错误处理
|
socketTask.onError((err) => {
|
||||||
uni.onSocketError((err) => {
|
if (socket !== socketTask) return;
|
||||||
console.error("WebSocket 错误", err);
|
console.error("WebSocket 错误", err);
|
||||||
reconnect(onMessage);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
uni.onSocketClose((result) => {
|
socketTask.onClose(async (result) => {
|
||||||
|
if (socket !== socketTask) return;
|
||||||
console.log("WebSocket 已关闭", result);
|
console.log("WebSocket 已关闭", result);
|
||||||
stopHeartbeat();
|
stopHeartbeat();
|
||||||
reconnect(onMessage);
|
socket = null;
|
||||||
|
isConnecting = false;
|
||||||
|
|
||||||
|
if (manualClose || kickedOut) return;
|
||||||
|
await handleUnexpectedClose(onMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async function handleUnexpectedClose(onMessage) {
|
||||||
* 重连机制
|
if (checkingSession || manualClose || kickedOut) return;
|
||||||
*/
|
|
||||||
function reconnect(onMessage) {
|
|
||||||
reconnectTimer && clearTimeout(reconnectTimer);
|
|
||||||
closeWebSocket(); // 确保关闭旧连接
|
|
||||||
|
|
||||||
const token = uni.getStorageSync(
|
const token = uni.getStorageSync(
|
||||||
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
||||||
);
|
);
|
||||||
if (!token) return;
|
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(() => {
|
reconnectTimer = setTimeout(() => {
|
||||||
console.log("reconnecting...");
|
if (manualClose || kickedOut || socket || isConnecting) return;
|
||||||
|
console.log("WebSocket 正在重连...");
|
||||||
createWebSocket(token, onMessage);
|
createWebSocket(token, onMessage);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeWebSocket() {
|
function closeWebSocket(isManual = true) {
|
||||||
if (socket) {
|
manualClose = isManual;
|
||||||
reconnectTimer && clearTimeout(reconnectTimer);
|
reconnectTimer && clearTimeout(reconnectTimer);
|
||||||
stopHeartbeat();
|
stopHeartbeat();
|
||||||
|
isConnecting = false;
|
||||||
|
|
||||||
|
if (socket) {
|
||||||
|
const currentSocket = socket;
|
||||||
|
socket = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socket.close();
|
currentSocket.close();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("关闭WebSocket连接失败", err);
|
console.error("关闭 WebSocket 失败", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket = null; // 清除socket引用
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendHeartbeat(onMessage) {
|
function sendHeartbeat(onMessage) {
|
||||||
uni.sendSocketMessage({
|
if (!socket) return;
|
||||||
|
|
||||||
|
const currentSocket = socket;
|
||||||
|
currentSocket.send({
|
||||||
data: JSON.stringify({ event: "ping", data: {} }),
|
data: JSON.stringify({ event: "ping", data: {} }),
|
||||||
success: () => {
|
success: () => {},
|
||||||
// console.log("发送心跳成功");
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error("发送心跳失败", err);
|
if (socket !== currentSocket) return;
|
||||||
|
console.error("心跳发送失败", err);
|
||||||
stopHeartbeat();
|
stopHeartbeat();
|
||||||
closeWebSocket(); // 关闭失效的连接
|
closeWebSocket(false);
|
||||||
reconnect(onMessage); // 触发重连
|
reconnect(onMessage);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动心跳
|
|
||||||
*/
|
|
||||||
function startHeartbeat(onMessage) {
|
function startHeartbeat(onMessage) {
|
||||||
stopHeartbeat(); // 防止重复启动
|
stopHeartbeat();
|
||||||
|
|
||||||
heartbeatInterval = setInterval(() => {
|
heartbeatInterval = setInterval(() => {
|
||||||
if (socket && socket.readyState === 1) {
|
if (socket) {
|
||||||
// 检查连接状态
|
|
||||||
sendHeartbeat(onMessage);
|
sendHeartbeat(onMessage);
|
||||||
}
|
}
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 停止心跳
|
|
||||||
*/
|
|
||||||
function stopHeartbeat() {
|
function stopHeartbeat() {
|
||||||
if (heartbeatInterval) {
|
if (heartbeatInterval) {
|
||||||
clearInterval(heartbeatInterval);
|
clearInterval(heartbeatInterval);
|
||||||
|
|||||||
Reference in New Issue
Block a user