/** * WebSocket管理器 * 负责WebSocket连接、消息收发、重连、心跳等功能 */ import { websocketConfig } from '@/config/websocket.js'; export class WebSocketManager { constructor(url = null) { // 使用配置文件中的URL,如果传入了url则使用传入的 this.url = url || websocketConfig.getUrl(); this.socket = null; this.isConnected = false; this.isAuthenticated = false; this.reconnectAttempts = 0; // 从配置文件读取重连配置 this.maxReconnectAttempts = websocketConfig.reconnect.maxAttempts; this.reconnectDelay = websocketConfig.reconnect.initialDelay; this.maxReconnectDelay = websocketConfig.reconnect.maxDelay; this.messageHandlers = []; this.pingInterval = null; this.cachedToken = null; // 从配置文件读取用户配置 this.userPrefix = websocketConfig.user.prefix; this.platform = websocketConfig.user.platform; } getToken() { let token = uni.getStorageSync('token'); if (token) { token = token.replace('Bearer ', ''); this.cachedToken = token; } return token || null; } clearCachedToken() { this.cachedToken = null; } rebindUser() { const userId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id'); if (!userId) { console.warn('重新绑定用户失败:doctor_id不存在'); return false; } this.clearCachedToken(); if (this.isConnected) { return this.bindUser(userId, true); } else { this.connect(); return true; } } 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(); // 绑定当前用户(医生小程序使用doctor-前缀) const userId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id'); if (userId) { this.bindUser(userId, true); } }); 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); }); } unbindUser(userId) { const token = this.getToken(); if (!token) { console.warn('解绑用户失败:token不存在'); return false; } // 使用配置文件中的用户前缀 const normalizedUserId = userId.toString().startsWith(this.userPrefix) ? userId : `${this.userPrefix}${userId}`; console.log('发送unbind请求,清理旧连接:', normalizedUserId); return this.send({ request_type: 'unbind', user_type: 'doctor', sender_user_id: normalizedUserId, token: token }, false); } bindUser(userId, cleanOldConnections = true) { const token = this.getToken(); if (!token) { console.error('绑定用户失败:token不存在'); return false; } // 使用配置文件中的用户前缀 const normalizedUserId = userId.toString().startsWith(this.userPrefix) ? userId : `${this.userPrefix}${userId}`; // 构建bind请求对象 const bindMessage = { request_type: 'bind', user_type: 'doctor', sender_user_id: normalizedUserId, token: token, platform: this.platform, // 使用配置文件中的平台标识 clean_old_connections: cleanOldConnections }; // 输出详细的调试日志 console.log('========== WebSocket Bind 请求 =========='); console.log('用户ID:', normalizedUserId); console.log('用户类型:', 'doctor'); console.log('平台标识:', this.platform); console.log('清理旧连接:', cleanOldConnections); console.log('Token存在:', !!token); console.log('完整请求内容:', JSON.stringify(bindMessage, null, 2)); console.log('=========================================='); return this.send(bindMessage); } send(message, requireAuth = true) { if (!this.isConnected) { console.error('WebSocket未连接'); return false; } try { if (requireAuth && !message.token && message.request_type !== 'ping') { const token = this.getToken(); if (token) { message.token = token; } } // 如果是发送消息,确保添加platform标识 if (message.request_type === 'send_message' && !message.platform) { message.platform = this.platform; } 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); } } handleMessage(data) { if (data.auth_status !== undefined) { if (data.auth_status === 'success') { console.log('========== WebSocket 认证成功 =========='); console.log('认证状态:', data.auth_status); console.log('完整响应:', JSON.stringify(data, null, 2)); console.log('========================================'); this.isAuthenticated = true; } else if (data.auth_status === 'failed') { console.error('========== WebSocket 认证失败 =========='); console.error('认证状态:', data.auth_status); console.error('错误消息:', data.message || '未知错误'); console.error('错误代码:', data.code || '无'); console.error('完整响应:', JSON.stringify(data, null, 2)); console.error('当前用户ID:', uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id')); console.error('Token存在:', !!this.getToken()); console.error('平台标识:', this.platform); console.error('========================================'); this.isAuthenticated = false; this.cachedToken = null; } else if (data.auth_status === 'token_expired') { console.warn('========== Token 已过期 =========='); console.warn('认证状态:', data.auth_status); console.warn('错误消息:', data.message || 'Token已过期'); console.warn('完整响应:', JSON.stringify(data, null, 2)); console.warn('=================================='); this.isAuthenticated = false; this.cachedToken = null; uni.$emit('ws-token-expired'); const userId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id'); if (userId) { console.log('尝试重新绑定用户...'); this.bindUser(userId, true); } } else { console.warn('========== 未知的认证状态 =========='); console.warn('认证状态:', data.auth_status); console.warn('完整响应:', JSON.stringify(data, null, 2)); console.warn('=================================='); } return; } if (data.clientId) { const userId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id'); if (userId) { this.bindUser(userId, true); } } this.messageHandlers.forEach(handler => { try { handler(data); } catch (e) { console.error('消息处理出错:', e); } }); } /** * 启动心跳 * 职责:按照配置的心跳间隔发送ping消息 */ startHeartbeat() { this.pingInterval = setInterval(() => { if (this.isConnected) { this.send({ request_type: 'ping' }, false); } }, websocketConfig.heartbeat.interval); } 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, this.maxReconnectDelay); 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实例 // 使用配置文件中的URL export const webSocketManager = new WebSocketManager();