/* 碰盏日记 - UniAppWebSocket 适配器 * 将 uni-app 的 uni.connectSocket 适配为 HaveADrink SDK WebsocketInterface * (标准 WebSocket 风格:constructor(host)、onopen/onmessage/onclose/onerror、send、close) * 用法:在 SDK 初始化时传入 websocket: UniAppWebSocket */ export default class UniAppWebSocket { /** * @param {string} host - 完整 ws/wss 连接地址(SDK 已处理 https→wss 协议转换) */ constructor(host) { this.url = host // 可赋值的事件回调(SDK ChatWebSocketConn.connect() 中直接赋值) this.onopen = null this.onmessage = null this.onclose = null this.onerror = null // 连接状态: connecting | open | closed this.readyState = 0 this._sendQueue = [] this._closedByUser = false this._connect() } _connect() { // 微信小程序下直接用 wx.connectSocket: // uni 的 API 运行时(uni.api.esm.js)会向 connectSocket 注入 success/fail/complete 回调, // 而微信规则是“传入回调后不再返回 SocketTask”,导致 uni.connectSocket 永远拿不到 task。 // wx.connectSocket 不传回调时正常返回 SocketTask,避免降级到全局 socket API // (全局 API 重连时会重复注册监听器,引发重连风暴) console.log('[UniAppWebSocket] 发起连接:', this.url) const rawConnect = (typeof wx !== 'undefined' && typeof wx.connectSocket === 'function') ? wx.connectSocket.bind(wx) : uni.connectSocket const task = rawConnect({ url: this.url }) // 部分平台 uni.connectSocket 无返回值,降级为全局 API 监听 if (task && typeof task.onOpen === 'function') { this.socketTask = task task.onOpen((res) => { console.log('[UniAppWebSocket] 连接已打开(open)') this.readyState = 1 this._flush() if (typeof this.onopen === 'function') { this.onopen({ type: 'open' }) } }) task.onMessage((res) => { if (typeof this.onmessage === 'function') { // 与浏览器 MessageEvent 对齐:消息内容放在 event.data this.onmessage({ type: 'message', data: res.data }) } }) task.onClose((event) => { // 诊断:打印关闭码/原因,用于判断是服务端拒连还是网络问题 // 常见 code:1006 异常断开、1000 正常关闭;reason 由服务端给出 console.warn('[UniAppWebSocket] 连接被关闭(close) code:', event && event.code, 'reason:', event && event.reason) this.readyState = 3 if (!this._closedByUser && typeof this.onclose === 'function') { this.onclose(Object.assign({ type: 'close' }, event || {})) } }) task.onError((err) => { // 诊断:微信会在 errMsg 中给出具体原因(域名不在合法列表、TLS 失败等) console.warn('[UniAppWebSocket] 连接错误(error):', err && (err.errMsg || err.message || JSON.stringify(err))) if (typeof this.onerror === 'function') { this.onerror(Object.assign({ type: 'error' }, err || {})) } }) } else { console.warn('[UniAppWebSocket] 未获得 SocketTask,降级到全局 socket API') // 兜底:旧版小程序全局 socket API uni.onSocketOpen(() => { this.readyState = 1 this._flush() if (typeof this.onopen === 'function') { this.onopen({ type: 'open' }) } }) uni.onSocketMessage((res) => { if (typeof this.onmessage === 'function') { this.onmessage({ type: 'message', data: res.data }) } }) uni.onSocketClose((event) => { this.readyState = 3 if (!this._closedByUser && typeof this.onclose === 'function') { this.onclose(Object.assign({ type: 'close' }, event || {})) } }) uni.onSocketError((err) => { if (typeof this.onerror === 'function') { this.onerror(Object.assign({ type: 'error' }, err || {})) } }) } } /** * 发送数据(与浏览器 WebSocket.send 签名一致) * @param {string} data */ send(data) { if (this.readyState !== 1) { // 连接未就绪时暂存,连接建立后补发 this._sendQueue.push(data) return } if (this.socketTask) { this.socketTask.send({ data, fail: (err) => { console.warn('[UniAppWebSocket] 发送失败', err) } }) } else { // 兜底:旧版小程序全局 socket API uni.sendSocketMessage({ data, fail: (err) => { console.warn('[UniAppWebSocket] 发送失败', err) } }) } } /** 主动关闭连接(主动关闭不再触发 onclose,由 SDK 自行控制重连) */ close() { this._closedByUser = true this.readyState = 3 if (this.socketTask) { this.socketTask.close({ complete: () => {} }) this.socketTask = null } else { uni.closeSocket({ complete: () => {} }) } } /** 连接建立后补发暂存消息 */ _flush() { if (!this._sendQueue.length) return const queue = [...this._sendQueue] this._sendQueue = [] queue.forEach((data) => this.send(data)) } }