const WS_BASE_URL = 'wss://attendant.huashengtec.com/health-ws' //const WS_BASE_URL = 'ws://127.0.0.1:9005' class AIChatSocket { constructor(url = `${WS_BASE_URL}/chat`, options = {}) { this.url = url this.options = { timeout: 10000, heartbeatInterval: 30000, autoReconnect: true, maxReconnectAttempts: 3, reconnectDelay: 3000, ...options } this.socketTask = null this.isConnected = false this.isConnecting = false this.messageQueue = [] this.reconnectAttempts = 0 this._openCallback = null this._closeCallback = null this._errorCallback = null this._messageCallback = null this._heartbeatTimer = null } connect() { if (this.isConnected || this.isConnecting) { console.log('[AIChatSocket] Already connected or connecting') return Promise.resolve() } this.isConnecting = true return new Promise((resolve, reject) => { this.socketTask = wx.connectSocket({ url: this.url, header: { 'content-type': 'application/json', ...this.options.header }, protocols: this.options.protocols || [], success: () => { console.log('[AIChatSocket] WebSocket connecting...') }, fail: (err) => { this.isConnecting = false console.error('[AIChatSocket] Connect failed:', err) reject(err) } }) if (!this.socketTask) { this.isConnecting = false reject(new Error('SocketTask creation failed')) return } this.socketTask.onOpen(() => { console.log('[AIChatSocket] Connection opened') this.isConnected = true this.isConnecting = false this.reconnectAttempts = 0 this._startHeartbeat() this._flushMessageQueue() if (this._openCallback) { this._openCallback() } resolve() }) this.socketTask.onClose((res) => { console.log('[AIChatSocket] Connection closed:', res) this.isConnected = false this.isConnecting = false this._stopHeartbeat() if (this._closeCallback) { this._closeCallback(res) } if (this.options.autoReconnect && this.reconnectAttempts < this.options.maxReconnectAttempts) { this._reconnect() } }) this.socketTask.onError((err) => { console.error('[AIChatSocket] WebSocket error:', err) this.isConnected = false this.isConnecting = false if (this._errorCallback) { this._errorCallback(err) } }) this.socketTask.onMessage((res) => { try { const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data if (this._messageCallback) { this._messageCallback(data) } } catch (e) { if (this._messageCallback) { this._messageCallback(res.data) } } }) }) } send(data) { const message = typeof data === 'string' ? data : JSON.stringify(data) if (!this.isConnected || !this.socketTask) { console.log('[AIChatSocket] Not connected, queueing message') this.messageQueue.push(message) return false } try { this.socketTask.send({ data: message, fail: (err) => { console.error('[AIChatSocket] Send failed:', err) this.messageQueue.push(message) } }) return true } catch (err) { console.error('[AIChatSocket] Send error:', err) this.messageQueue.push(message) return false } } close() { this._stopHeartbeat() this.options.autoReconnect = false if (this.socketTask) { this.socketTask.close({ code: 1000, reason: 'User closed', fail: (err) => { console.error('[AIChatSocket] Close failed:', err) } }) this.socketTask = null } this.isConnected = false this.isConnecting = false this.messageQueue = [] } onOpen(callback) { this._openCallback = callback } onClose(callback) { this._closeCallback = callback } onError(callback) { this._errorCallback = callback } onMessage(callback) { this._messageCallback = callback } _flushMessageQueue() { if (this.messageQueue.length === 0) return console.log(`[AIChatSocket] Flushing ${this.messageQueue.length} queued messages`) while (this.messageQueue.length > 0) { const message = this.messageQueue.shift() this.send(message) } } _reconnect() { this.reconnectAttempts++ console.log(`[AIChatSocket] Reconnecting... Attempt ${this.reconnectAttempts}/${this.options.maxReconnectAttempts}`) setTimeout(() => { this.connect().catch((err) => { console.error('[AIChatSocket] Reconnect failed:', err) }) }, this.options.reconnectDelay) } _startHeartbeat() { this._stopHeartbeat() this._heartbeatTimer = setInterval(() => { if (this.isConnected && this.socketTask) { this.socketTask.send({ data: JSON.stringify({ type: 'ping' }), fail: (err) => { console.error('[AIChatSocket] Heartbeat failed:', err) this._stopHeartbeat() } }) } }, this.options.heartbeatInterval) } _stopHeartbeat() { if (this._heartbeatTimer) { clearInterval(this._heartbeatTimer) this._heartbeatTimer = null } } } module.exports = AIChatSocket