格式化代码,websocket功能完善

This commit is contained in:
2026-02-18 21:50:05 +08:00
parent 6543e2ccdd
commit b6c133952b
101 changed files with 15829 additions and 10739 deletions
+63 -63
View File
@@ -5,88 +5,88 @@ import { message } from "ant-design-vue";
import router from "@/router";
const request = axios.create({
timeout: 30000,
baseURL: config.API_URL,
timeout: 30000,
baseURL: config.API_URL,
});
// 请求拦截器
request.interceptors.request.use(
(config) => {
const userStore = useUserStore();
const token = userStore.token;
(config) => {
const userStore = useUserStore();
const token = userStore.token;
// 如果有 token,添加到请求头
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
// 如果有 token,添加到请求头
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
},
return config;
},
(error) => {
return Promise.reject(error);
},
);
// 响应拦截器
request.interceptors.response.use(
(response) => {
// 根据后端返回的数据结构进行处理
// 后端返回格式为 { code, message, data }
const { code, data, message: msg } = response.data;
(response) => {
// 根据后端返回的数据结构进行处理
// 后端返回格式为 { code, message, data }
const { code, data, message: msg } = response.data;
// 请求成功
if (code === 200 || code === 1) {
return { code, data, message: msg };
}
// 请求成功
if (code === 200 || code === 1) {
return { code, data, message: msg };
}
// 其他错误码处理
message.error(msg || "请求失败");
return Promise.reject(new Error(msg || "请求失败"));
},
async (error) => {
const userStore = useUserStore();
const { response } = error;
// 其他错误码处理
message.error(msg || "请求失败");
return Promise.reject(new Error(msg || "请求失败"));
},
async (error) => {
const userStore = useUserStore();
const { response } = error;
// 无响应(网络错误、超时等)
if (!response) {
message.error("网络错误,请检查网络连接");
return Promise.reject(error);
}
// 无响应(网络错误、超时等)
if (!response) {
message.error("网络错误,请检查网络连接");
return Promise.reject(error);
}
const { status, data } = response;
const { status, data } = response;
// 401 未授权 - token 过期或无效
if (status === 401) {
// 直接登出并跳转到登录页
userStore.logout();
router.push("/login");
message.error("登录已过期,请重新登录");
return Promise.reject(error);
}
// 401 未授权 - token 过期或无效
if (status === 401) {
// 直接登出并跳转到登录页
userStore.logout();
router.push("/login");
message.error("登录已过期,请重新登录");
return Promise.reject(error);
}
// 403 禁止访问
if (status === 403) {
message.error("没有权限访问该资源");
return Promise.reject(error);
}
// 403 禁止访问
if (status === 403) {
message.error("没有权限访问该资源");
return Promise.reject(error);
}
// 404 资源不存在
if (status === 404) {
message.error("请求的资源不存在");
return Promise.reject(error);
}
// 404 资源不存在
if (status === 404) {
message.error("请求的资源不存在");
return Promise.reject(error);
}
// 500 服务器错误
if (status >= 500) {
message.error("服务器错误,请稍后重试");
return Promise.reject(error);
}
// 500 服务器错误
if (status >= 500) {
message.error("服务器错误,请稍后重试");
return Promise.reject(error);
}
// 其他错误
const errorMessage = data?.message || error.message || "请求失败";
message.error(errorMessage);
return Promise.reject(error);
},
// 其他错误
const errorMessage = data?.message || error.message || "请求失败";
message.error(errorMessage);
return Promise.reject(error);
},
);
export default request;
+335 -199
View File
@@ -5,255 +5,391 @@
*/
class WebSocketClient {
constructor(url, options = {}) {
this.url = url
this.ws = null
this.reconnectAttempts = 0
this.maxReconnectAttempts = options.maxReconnectAttempts || 5
this.reconnectInterval = options.reconnectInterval || 3000
this.reconnectDelay = options.reconnectDelay || 1000
this.heartbeatInterval = options.heartbeatInterval || 30000
this.heartbeatTimer = null
this.isManualClose = false
this.isConnecting = false
constructor(url, options = {}) {
this.url = url;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
this.reconnectInterval = options.reconnectInterval || 3000;
this.reconnectDelay = options.reconnectDelay || 1000;
this.heartbeatInterval = options.heartbeatInterval || 30000;
this.heartbeatTimer = null;
this.isManualClose = false;
this.isConnecting = false;
// Event handlers
this.onOpen = options.onOpen || null
this.onMessage = options.onMessage || null
this.onError = options.onError || null
this.onClose = options.onClose || null
// Event handlers
this.onOpen = options.onOpen || null;
this.onMessage = options.onMessage || null;
this.onError = options.onError || null;
this.onClose = options.onClose || null;
// Message handlers
this.messageHandlers = new Map()
}
// Message handlers
this.messageHandlers = new Map();
}
/**
* Connect to WebSocket server
*/
connect() {
if (this.isConnecting || (this.ws && this.ws.readyState === WebSocket.OPEN)) {
return
}
/**
* Get connection state description
*/
getConnectionState() {
if (!this.ws) return "CLOSED";
switch (this.ws.readyState) {
case WebSocket.CONNECTING:
return "CONNECTING";
case WebSocket.OPEN:
return "OPEN";
case WebSocket.CLOSING:
return "CLOSING";
case WebSocket.CLOSED:
return "CLOSED";
default:
return "UNKNOWN";
}
}
this.isConnecting = true
this.isManualClose = false
/**
* Connect to WebSocket server
*/
connect() {
if (
this.isConnecting ||
(this.ws && this.ws.readyState === WebSocket.OPEN)
) {
return;
}
try {
this.ws = new WebSocket(this.url)
this.isConnecting = true;
this.isManualClose = false;
this.ws.onopen = (event) => {
console.log('WebSocket connected', event)
this.isConnecting = false
this.reconnectAttempts = 0
try {
this.ws = new WebSocket(this.url);
// Start heartbeat
this.startHeartbeat()
this.ws.onopen = (event) => {
console.log("WebSocket connected", event);
this.isConnecting = false;
this.reconnectAttempts = 0;
// Call onOpen handler
if (this.onOpen) {
this.onOpen(event)
}
}
// Start heartbeat
this.startHeartbeat();
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data)
console.log('WebSocket message received', message)
// Call onOpen handler
if (this.onOpen) {
this.onOpen(event);
}
};
// Handle different message types
this.handleMessage(message)
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
console.log("WebSocket message received", message);
// Call onMessage handler
if (this.onMessage) {
this.onMessage(message, event)
}
} catch (error) {
console.error('Failed to parse WebSocket message', error)
}
}
// Handle different message types
this.handleMessage(message);
this.ws.onerror = (error) => {
console.error('WebSocket error', error)
this.isConnecting = false
// Call onMessage handler
if (this.onMessage) {
this.onMessage(message, event);
}
} catch (error) {
console.error("Failed to parse WebSocket message", error);
}
};
// Stop heartbeat
this.stopHeartbeat()
this.ws.onerror = (error) => {
console.error("WebSocket error", error);
this.isConnecting = false;
// Call onError handler
if (this.onError) {
this.onError(error)
}
}
// Stop heartbeat
this.stopHeartbeat();
this.ws.onclose = (event) => {
console.log('WebSocket closed', event)
this.isConnecting = false
// Call onError handler
if (this.onError) {
this.onError(error);
}
};
// Stop heartbeat
this.stopHeartbeat()
this.ws.onclose = (event) => {
console.log("WebSocket closed", event);
this.isConnecting = false;
// Call onClose handler
if (this.onClose) {
this.onClose(event)
}
// Stop heartbeat
this.stopHeartbeat();
// Attempt to reconnect if not manually closed
if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnect()
}
}
} catch (error) {
console.error('Failed to create WebSocket connection', error)
this.isConnecting = false
// Call onClose handler
if (this.onClose) {
this.onClose(event);
}
// Call onError handler
if (this.onError) {
this.onError(error)
}
}
}
// Attempt to reconnect if not manually closed
if (
!this.isManualClose &&
this.reconnectAttempts < this.maxReconnectAttempts
) {
this.reconnect();
}
};
} catch (error) {
console.error("Failed to create WebSocket connection", error);
this.isConnecting = false;
/**
* Reconnect to WebSocket server
*/
reconnect() {
if (this.isConnecting || this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log('Max reconnection attempts reached')
return
}
// Call onError handler
if (this.onError) {
this.onError(error);
}
}
}
this.reconnectAttempts++
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1)
/**
* Reconnect to WebSocket server
*/
reconnect() {
if (
this.isConnecting ||
this.reconnectAttempts >= this.maxReconnectAttempts
) {
console.log("Max reconnection attempts reached");
return;
}
console.log(`Reconnecting attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`)
this.reconnectAttempts++;
const delay =
this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
setTimeout(() => {
this.connect()
}, delay)
}
console.log(
`Reconnecting attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`,
);
/**
* Disconnect from WebSocket server
*/
disconnect() {
this.isManualClose = true
this.stopHeartbeat()
setTimeout(() => {
this.connect();
}, delay);
}
if (this.ws) {
this.ws.close()
this.ws = null
}
}
/**
* Disconnect from WebSocket server
*/
disconnect() {
this.isManualClose = true;
this.stopHeartbeat();
/**
* Send message to server
*/
send(type, data = {}) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const message = JSON.stringify({
type,
data
})
this.ws.send(message)
console.log('WebSocket message sent', { type, data })
} else {
console.warn('WebSocket is not connected')
}
}
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
/**
* Handle incoming messages
*/
handleMessage(message) {
const { type, data } = message
/**
* Send message to server
*/
send(type, data = {}) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const message = JSON.stringify({
type,
data,
});
this.ws.send(message);
console.log("WebSocket message sent", { type, data });
} else {
console.warn("WebSocket is not connected");
}
}
// Get handler for this message type
const handler = this.messageHandlers.get(type)
if (handler) {
handler(data)
}
}
/**
* Handle incoming messages
*/
handleMessage(message) {
const { type, data } = message;
/**
* Register message handler
*/
on(messageType, handler) {
this.messageHandlers.set(messageType, handler)
}
// Get handler for this message type
const handler = this.messageHandlers.get(type);
if (handler) {
handler(data);
}
}
/**
* Unregister message handler
*/
off(messageType) {
this.messageHandlers.delete(messageType)
}
/**
* Register message handler
*/
on(messageType, handler) {
this.messageHandlers.set(messageType, handler);
}
/**
* Start heartbeat
*/
startHeartbeat() {
this.stopHeartbeat()
this.heartbeatTimer = setInterval(() => {
this.send('heartbeat', { timestamp: Date.now() })
}, this.heartbeatInterval)
}
/**
* Unregister message handler
*/
off(messageType) {
this.messageHandlers.delete(messageType);
}
/**
* Stop heartbeat
*/
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = null
}
}
/**
* Start heartbeat
*/
startHeartbeat() {
this.stopHeartbeat();
this.heartbeatTimer = setInterval(() => {
this.send("heartbeat", { timestamp: Date.now() });
}, this.heartbeatInterval);
}
/**
* Get connection state
*/
get readyState() {
if (!this.ws) return WebSocket.CLOSED
return this.ws.readyState
}
/**
* Stop heartbeat
*/
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
/**
* Check if connected
*/
get isConnected() {
return this.ws && this.ws.readyState === WebSocket.OPEN
}
/**
* Get connection state
*/
get readyState() {
if (!this.ws) return WebSocket.CLOSED;
return this.ws.readyState;
}
/**
* Check if connected
*/
get isConnected() {
return this.ws && this.ws.readyState === WebSocket.OPEN;
}
}
/**
* Create WebSocket connection
*/
export function createWebSocket(userId, token, options = {}) {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
// 优先使用配置的 WS_URL,否则使用当前域名
const host = options.wsUrl || window.location.host
const url = `${protocol}//${host}/ws?user_id=${userId}&token=${token}`
// 优先使用配置的 WS_URL,否则使用当前域名
const host = options.wsUrl || window.location.host;
const url = `${protocol}//${host}/ws?user_id=${userId}&token=${token}`;
return new WebSocketClient(url, options)
return new WebSocketClient(url, options);
}
/**
* WebSocket singleton instance
* WebSocket singleton instance with connection management
*/
let wsClient = null
let wsClient = null;
let currentUserId = null;
let currentToken = null;
let pendingConnection = null;
export function getWebSocket(userId, token, options = {}) {
if (!wsClient || !wsClient.isConnected) {
wsClient = createWebSocket(userId, token, options)
}
return wsClient
// Check if we have a connection for the same user
if (wsClient) {
// If connection exists and user/token matches
if (currentUserId === userId && currentToken === token) {
const state = wsClient.getConnectionState();
// If already connected or connecting, return existing instance
if (state === "OPEN" || state === "CONNECTING") {
console.log("WebSocket: Reusing existing connection", {
state,
userId: currentUserId,
});
return wsClient;
}
// If connection is closed or closing, clean up
if (state === "CLOSED" || state === "CLOSING") {
console.log("WebSocket: Cleaning up closed connection");
if (wsClient.ws) {
wsClient.ws.onopen = null;
wsClient.ws.onmessage = null;
wsClient.ws.onerror = null;
wsClient.ws.onclose = null;
wsClient.ws = null;
}
}
}
// Different user/token, disconnect old connection
if (currentUserId !== userId || currentToken !== token) {
console.log(
"WebSocket: User changed, disconnecting old connection",
);
if (wsClient) {
wsClient.disconnect();
}
wsClient = null;
}
}
// Check if there's a pending connection
if (pendingConnection) {
return new Promise((resolve) => {
const checkPending = setInterval(() => {
if (!pendingConnection) {
clearInterval(checkPending);
console.log("WebSocket: Pending connection completed");
resolve(getWebSocket(userId, token, options));
}
}, 100);
});
}
// Create new connection
console.log("WebSocket: Creating new connection", {
userId,
token: token ? "***" : null,
});
currentUserId = userId;
currentToken = token;
pendingConnection = true;
wsClient = createWebSocket(userId, token, options);
// Mark connection as no longer pending when connected or failed
const originalOnOpen = wsClient.onOpen;
const originalOnError = wsClient.onError;
const originalOnClose = wsClient.onClose;
wsClient.onOpen = (event) => {
pendingConnection = null;
console.log("WebSocket: Connection established", { userId });
if (originalOnOpen) originalOnOpen(event);
};
wsClient.onError = (error) => {
pendingConnection = null;
console.error("WebSocket: Connection error", { userId, error });
if (originalOnError) originalOnError(error);
};
wsClient.onClose = (event) => {
pendingConnection = null;
console.log("WebSocket: Connection closed", {
userId,
code: event.code,
reason: event.reason,
});
if (originalOnClose) originalOnClose(event);
};
return wsClient;
}
export function closeWebSocket() {
if (wsClient) {
wsClient.disconnect()
wsClient = null
}
if (wsClient) {
console.log("WebSocket: Closing connection manually");
wsClient.disconnect();
// Clean up event handlers
if (wsClient.ws) {
wsClient.ws.onopen = null;
wsClient.ws.onmessage = null;
wsClient.ws.onerror = null;
wsClient.ws.onclose = null;
wsClient.ws = null;
}
wsClient = null;
currentUserId = null;
currentToken = null;
pendingConnection = null;
}
}
export default WebSocketClient
export default WebSocketClient;