Files
xk-client-wx/utils/ws/websocket.js

266 lines
7.9 KiB
JavaScript
Raw Normal View History

2026-01-09 13:58:09 +08:00
import {checkDev} from "@/utils/utils";
export class WebSocketManager {
constructor(url) {
this.url = url;
this.socket = null;
this.isConnected = false;
2025-12-30 10:04:30 +08:00
this.isAuthenticated = false; // 是否已认证
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
this.messageHandlers = [];
this.pingInterval = null;
2025-12-30 10:04:30 +08:00
this.cachedToken = null; // 缓存的token
}
/**
* 获取当前用户的token
* @returns {string|null}
*/
getToken() {
// 优先使用缓存的token
if (this.cachedToken) {
return this.cachedToken;
}
// 从本地存储获取token
let token = uni.getStorageSync('token');
if (token) {
// 移除Bearer前缀如果有
token = token.replace('Bearer ', '');
this.cachedToken = token;
}
return token || null;
}
connect() {
if (this.isConnected) return;
console.log('正在连接WebSocket...');
this.socket = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket连接成功');
},
fail: (err) => {
console.error('WebSocket连接失败:', err);
this.handleReconnect();
}
});
this.socket.onOpen(() => {
console.log('WebSocket连接已打开');
this.isConnected = true;
this.reconnectAttempts = 0;
// 开始心跳检测
this.startHeartbeat();
// 绑定当前用户
const userId = uni.getStorageSync('userId');
if (userId) {
this.bindUser(userId);
}
});
this.socket.onMessage((res) => {
try {
const data = JSON.parse(res.data);
console.log('收到消息:', data);
this.handleMessage(data);
} catch (e) {
console.error('解析消息失败:', e);
}
});
this.socket.onClose((res) => {
console.log('WebSocket连接关闭:', res);
this.isConnected = false;
this.stopHeartbeat();
this.handleReconnect();
});
this.socket.onError((err) => {
console.error('WebSocket错误:', err);
});
}
2025-12-30 10:04:30 +08:00
/**
* 绑定用户携带token进行认证
* @param {string} userId 用户ID
*/
bindUser(userId) {
2025-12-30 10:04:30 +08:00
const token = this.getToken();
if (!token) {
console.error('绑定用户失败token不存在');
return false;
}
return this.send({
request_type: 'bind',
user_type: 'user',
sender_user_id: userId,
token: token
});
}
2025-12-30 10:04:30 +08:00
/**
* 发送消息自动携带token
* @param {Object} message 消息对象
* @param {boolean} requireAuth 是否需要认证默认true
* @returns {boolean} 是否发送成功
*/
send(message, requireAuth = true) {
if (!this.isConnected) {
console.error('WebSocket未连接');
return false;
}
try {
2025-12-30 10:04:30 +08:00
// 如果需要认证且消息中没有token自动添加token
if (requireAuth && !message.token && message.request_type !== 'ping') {
const token = this.getToken();
if (token) {
message.token = token;
}
}
const messageStr = JSON.stringify(message);
this.socket.send({
data: messageStr,
success: () => {
console.log('消息发送成功:', message);
},
fail: (err) => {
console.error('消息发送失败:', err);
}
});
return true;
} catch (err) {
console.error('发送消息出错:', err);
return false;
}
}
addMessageHandler(handler) {
this.messageHandlers.push(handler);
}
removeMessageHandler(handler) {
const index = this.messageHandlers.indexOf(handler);
if (index !== -1) {
this.messageHandlers.splice(index, 1);
}
}
2025-12-30 10:04:30 +08:00
/**
* 获取用户ID并绑定客户端携带token
* @returns {string|null} 用户ID
*/
getUserIdAndBindClientId() {
const userId = uni.getStorageSync('user_id'); // 实际项目中从存储获取
2025-12-30 10:04:30 +08:00
const token = this.getToken();
if (userId == null || userId === 'user-' || userId === '') {
2025-12-30 10:04:30 +08:00
// 等待10秒后重试
setTimeout(() => {
this.getUserIdAndBindClientId();
}, 10000)
2025-12-30 10:04:30 +08:00
} else if (!token) {
// token不存在等待后重试
console.warn('token不存在等待后重试绑定');
setTimeout(() => {
this.getUserIdAndBindClientId();
}, 5000)
} else {
const bindMessage = {
request_type: 'bind',
user_type: 'user',
sender_user_id: `user-${userId}`,
2025-12-30 10:04:30 +08:00
token: token
};
this.send(bindMessage);
}
return userId;
}
handleMessage(data) {
2025-12-30 10:04:30 +08:00
// 处理认证响应
if (data.auth_status !== undefined) {
if (data.auth_status === 'success') {
console.log('WebSocket认证成功');
this.isAuthenticated = true;
} else if (data.auth_status === 'failed') {
console.error('WebSocket认证失败:', data.message);
this.isAuthenticated = false;
// 清除缓存的token
this.cachedToken = null;
} else if (data.auth_status === 'token_expired') {
console.warn('Token已过期需要重新认证');
this.isAuthenticated = false;
this.cachedToken = null;
// 通知应用层token过期
uni.$emit('ws-token-expired');
// 尝试重新绑定
this.getUserIdAndBindClientId();
}
return;
}
2025-12-30 10:04:30 +08:00
// 如果对象中有clientId则给websocket发送绑定请求
if (data.clientId) {
this.getUserIdAndBindClientId(data.clientId);
}
2025-12-30 10:04:30 +08:00
// 处理聊天消息或其他消息
this.messageHandlers.forEach(handler => {
try {
handler(data);
} catch (e) {
console.error('消息处理出错:', e);
}
});
}
startHeartbeat() {
this.pingInterval = setInterval(() => {
if (this.isConnected) {
this.send({type: 'ping'});
}
}, 300000);
}
stopHeartbeat() {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
}
handleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log('已达到最大重连次数');
return;
}
setTimeout(() => {
this.reconnectAttempts++;
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000);
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect();
}, this.reconnectDelay);
}
disconnect() {
if (this.socket) {
this.socket.close();
this.socket = null;
}
this.isConnected = false;
this.stopHeartbeat();
}
}
// 全局WebSocket实例
2026-01-09 13:58:09 +08:00
// export const webSocketManager = new WebSocketManager('ws://127.0.0.1:12080/ws');
const wsUrl = checkDev('dev')? 'ws://127.0.0.1:12080/ws':'wss://api.ws.g.xiaokang88.com/ws';
export const webSocketManager = new WebSocketManager(wsUrl);