import { ref, Ref } from 'vue' import { getAccessToken } from '@/utils/auth' interface WebSocketConfig { wsUrl: string onSOSAlert?: (data: any) => void onOrderAlert?: (data: any) => void onHealthAlert?: (data: any) => void onDeviceDataUpdate?: (data: any) => void onStatsUpdate?: (data: any) => void } export const useWebSocket = (config: WebSocketConfig) => { const socket: Ref = ref(null) const isConnecting = ref(false) const connectionId: Ref = ref(null) const reconnectAttempts = ref(0) const maxReconnectAttempts = ref(10) const lastActivityTime = ref('-') const heartbeatStatus = ref('normal') const lastHeartbeatTime: Ref = ref(null) const lastHeartbeatAckTime: Ref = ref(null) let heartbeatInterval: ReturnType | null = null let heartbeatTimeout: ReturnType | null = null let reconnectTimeout: ReturnType | null = null let lastActivity = Date.now() const heartbeatIntervalTime = 25000 const heartbeatTimeoutTime = 10000 const updateLastActivity = () => { lastActivityTime.value = new Date().toLocaleTimeString() } const generateClientId = () => { const timestamp = Date.now() const random = Math.random().toString(36).substr(2, 9) return `monitor_${timestamp}_${random}` } const sendMessage = (message: any) => { if (socket.value && socket.value.readyState === WebSocket.OPEN) { try { socket.value.send(JSON.stringify(message)) lastActivity = Date.now() updateLastActivity() return true } catch (error) { console.error('发送消息失败:', error) return false } } return false } const handleHeartbeatAck = (data: any) => { console.log('心跳消息', data) if (heartbeatTimeout) { clearTimeout(heartbeatTimeout) heartbeatTimeout = null } heartbeatStatus.value = 'normal' lastHeartbeatAckTime.value = Date.now() lastActivity = Date.now() updateLastActivity() } const handleHeartbeatExpired = () => { console.error('🚨 心跳已过期,关闭连接并重新连接') stopHeartbeat() if (socket.value) { socket.value.close(1000, '心跳过期') socket.value = null } reconnectAttempts.value = 0 setTimeout(() => { if (!socket.value && !isConnecting.value) { connect() } }, 1000) } const handleHeartbeatTimeout = () => { console.warn('⏰ 心跳响应超时,关闭连接触发重连') stopHeartbeat() if (socket.value) { socket.value.close(1000, '心跳响应超时') } } const startHeartbeat = () => { stopHeartbeat() heartbeatStatus.value = 'normal' lastHeartbeatTime.value = Date.now() heartbeatInterval = setInterval(() => { if (!socket.value || socket.value.readyState !== WebSocket.OPEN) { heartbeatStatus.value = 'expired' stopHeartbeat() return } const now = Date.now() const timeSinceLastHeartbeat = now - (lastHeartbeatTime.value || 0) const totalTimeout = heartbeatIntervalTime + heartbeatTimeoutTime if (lastHeartbeatTime.value && timeSinceLastHeartbeat > totalTimeout) { console.warn(`💔 心跳已过期,距离上次心跳${Math.round(timeSinceLastHeartbeat / 1000)}秒`) heartbeatStatus.value = 'expired' handleHeartbeatExpired() return } heartbeatStatus.value = 'waiting' lastHeartbeatTime.value = now const params = { type: 'HEARTBEAT', timestamp: now, clientTime: now } const success = sendMessage(params) if (success) { if (heartbeatTimeout) { clearTimeout(heartbeatTimeout) } heartbeatTimeout = setTimeout(() => { console.warn('💔 心跳响应超时') heartbeatStatus.value = 'timeout' handleHeartbeatTimeout() }, heartbeatTimeoutTime) } else { heartbeatStatus.value = 'timeout' handleHeartbeatTimeout() } }, heartbeatIntervalTime) } const stopHeartbeat = () => { if (heartbeatInterval) { clearInterval(heartbeatInterval) heartbeatInterval = null } if (heartbeatTimeout) { clearTimeout(heartbeatTimeout) heartbeatTimeout = null } } const checkConnectionHealth = () => { if (!socket.value || socket.value.readyState !== WebSocket.OPEN) { return } const now = Date.now() const timeSinceLastActivity = now - lastActivity const timeSinceLastHeartbeat = now - (lastHeartbeatTime.value || 0) const totalTimeout = heartbeatIntervalTime + heartbeatTimeoutTime if (timeSinceLastActivity > totalTimeout + 10000) { console.error('🚨 连接长时间无活动') heartbeatStatus.value = 'expired' handleHeartbeatExpired() return } if ( heartbeatStatus.value === 'waiting' && timeSinceLastHeartbeat > heartbeatTimeoutTime + 5000 ) { console.warn('⚠️ 心跳响应延迟') sendMessage({ type: 'PING', timestamp: now }) } } const handleOpen = () => { isConnecting.value = false reconnectAttempts.value = 0 lastActivity = Date.now() startHeartbeat() const postData = { type: 'AUTH', clientType: 'homecare-web', clientId: generateClientId(), timestamp: Date.now(), accessToken: `Bearer ${getAccessToken()}` } sendMessage(postData) } const handleMessage = (event: MessageEvent) => { try { lastActivity = Date.now() updateLastActivity() const data = JSON.parse(event.data) processIncomingData(data) } catch (error) { console.error('消息解析错误:', error) } } const processIncomingData = (data: any) => { if (!data || !data.type) return console.log('data', data) switch (data.type) { case 'CONNECT_SUCCESS': connectionId.value = data.connectionId break case 'AUTH_SUCCESS': console.log('身份验证成功') break case 'SOS_ALERT': config.onSOSAlert?.(data) break case 'HEALTH_ALERT': config.onHealthAlert?.(data) break case 'DEVICE_DATA_UPDATE': config.onDeviceDataUpdate?.(data) break case 'ORDER_ALERT': config.onOrderAlert?.(data) break case 'SYSTEM_STATS_UPDATE': config.onStatsUpdate?.(data) break case 'HEARTBEAT_ACK': handleHeartbeatAck(data) break default: lastActivity = Date.now() updateLastActivity() } } const handleClose = (event: CloseEvent) => { console.log(`WebSocket连接关闭: 代码 ${event.code}`) isConnecting.value = false socket.value = null connectionId.value = null heartbeatStatus.value = 'expired' stopHeartbeat() if (reconnectTimeout) { clearTimeout(reconnectTimeout) reconnectTimeout = null } if (event.code !== 1000 && reconnectAttempts.value < maxReconnectAttempts.value) { reconnectAttempts.value++ const delay = Math.min(3000 * Math.pow(1.5, reconnectAttempts.value - 1), 30000) console.log(`${Math.round(delay / 1000)}秒后尝试重连`) reconnectTimeout = setTimeout(() => { if (!socket.value && !isConnecting.value) { connect() } }, delay) } else if (reconnectAttempts.value >= maxReconnectAttempts.value) { console.error('已达到最大重连次数') heartbeatStatus.value = 'expired' } } const handleError = (event: Event) => { console.error('WebSocket错误:', event) } const connect = () => { if (isConnecting.value || socket.value) { return } isConnecting.value = true if (reconnectTimeout) { clearTimeout(reconnectTimeout) reconnectTimeout = null } try { const clientId = generateClientId() const wsUrl = config.wsUrl + clientId socket.value = new WebSocket(wsUrl) socket.value.onopen = handleOpen socket.value.onmessage = handleMessage socket.value.onclose = handleClose socket.value.onerror = handleError } catch (error) { console.error('连接创建错误:', error) isConnecting.value = false socket.value = null if (reconnectAttempts.value < maxReconnectAttempts.value) { reconnectAttempts.value++ const delay = Math.min(3000 * Math.pow(1.5, reconnectAttempts.value - 1), 30000) reconnectTimeout = setTimeout(() => { if (!socket.value && !isConnecting.value) { connect() } }, delay) } } } const disconnect = () => { stopHeartbeat() if (reconnectTimeout) { clearTimeout(reconnectTimeout) reconnectTimeout = null } if (socket.value) { socket.value.close(1000, '主动关闭') socket.value = null } } return { socket, isConnecting, connectionId, reconnectAttempts, lastActivityTime, heartbeatStatus, lastHeartbeatTime, lastHeartbeatAckTime, connect, disconnect, sendMessage, checkConnectionHealth, stopHeartbeat } }