import type Message from '@/mode/Message' import type Receipt from '@/mode/Receipt' import type Webrtc from '@/mode/Webrtc' import SendCode from '@/utils/SendCode' import ChatUtils from '@/utils/ChatUtils' import vimConfig from '@/config/VimConfig' import Auth from '@/api/Auth' import ChatType from '@/utils/ChatType' import MessageType from '@/utils/MessageType' import {useUserStore} from '@/store/userStore' import {useChatStore} from '@/store/chatStore' import {useFriendStore} from '@/store/friendStore' import {useGroupStore} from '@/store/groupStore' import MessageUtils from '@/utils/MessageUtils' import VimPlugin from "@/plugins/VimPlugin"; import WebrtcCtr from "@/imcall/WebrtcCtr"; import dbApi from '@/api/DBactApi'; import { encryptLong2,decryptLong2} from '@/store/cryptoAES'; import el from 'date-fns/locale/el' const ready = `{"code":${SendCode.READY}}` const ping = `{"code":${SendCode.PING}}` class WsRequest { lockReconnect: boolean url: string | undefined //是否主动关闭 closeByUser: boolean //心跳检测 多少秒执行检测 timeout: number //重连超时时间 timeoutError: number heartTask: number | null reconnectTimeoutTask: number | null socket: UniApp.SocketTask | null uuid: string private static instance: WsRequest private constructor() { this.lockReconnect = false //避免重复连接 this.url = '' //是否主动关闭 this.closeByUser = false //心跳检测 多少秒执行检测 this.timeout = 3000 //超过多少秒没反应就重连 this.timeoutError = 5000 this.heartTask = null this.reconnectTimeoutTask = null this.socket = null this.uuid = `${new Date().getTime()}`; } static getInstance() { if (!this.instance) { this.instance = new WsRequest() } return this.instance } public init(): void { this.closeByUser = false this.url = `${vimConfig.wsProtocol}://${vimConfig.host}:${vimConfig.wsPort}?token=${Auth.getToken()}&client=${vimConfig.client}&uuid=${this.uuid}` //console.log(this.url) this.socket = uni.connectSocket({ url: this.url, //接口地址。 fail: (err) => { console.log(err, '连接错误') } }) this.socket.onOpen( () => { //告知服务器准备就绪 console.log('告知服务器准备就绪'); this.send(ready) // 开启检测 this.reset() }) // 如果希望websocket连接一直保持,在close或者error上绑定重新连接方法。 this.socket.onClose(() => { if (!this.closeByUser) { this.reconnect() } }) this.socket.onError (() => { this.reconnect() }) this.socket.onMessage((res) => { const data = res.data //防止每次心跳都要进行 JSON.parse 优化性能 if (data === ping) { this.reset() return } const sendInfo = JSON.parse(data) console.log('onMessage',sendInfo) // 真正的消息类型 if (sendInfo.code === SendCode.MESSAGE) { this.onmessage(sendInfo.message) } else if (sendInfo.code === SendCode.OTHER_LOGIN && this.uuid !== sendInfo.message.uuid) { MessageUtils.error('账号已经在别处登录') Auth.logout() }else if (sendInfo.code === SendCode.NEW_FRIEND) { useFriendStore().loadValidateList() useFriendStore().loadData() } else if (sendInfo.code === SendCode.GROUP_VALIDATE) { useGroupStore().loadWaitCheckList() } else if (sendInfo.code === SendCode.READ){ useChatStore().setLastReadTime(sendInfo.message) } else if (sendInfo.code === SendCode.WEBRTC_xinling){ WebrtcCtr.messageListener(sendInfo.message); //this.callback(undefined); } else if (sendInfo.code === SendCode.WEBRTC_CALL){ console.log('WEBRTC_CALL',sendInfo) if(uni.getSystemInfoSync().platform == "ios"){ //ios不拉起 //VimPlugin.messageListener(sendInfo) //WebrtcCtr.messageListener(sendInfo.message);//新的通话方式 } else{ WebrtcCtr.messageListener(sendInfo.message);//新的通话方式 //VimPlugin.messageListener(sendInfo) } } else{ WebrtcCtr.messageListener(sendInfo.message);//新的通话方式 //VimPlugin.messageListener(sendInfo) } //接受任何消息都说明当前连接是正常的 this.reset() }) } /** * 发送状态 * @param value */ send(value: string): void { this.socket?.send({ data:value }) } /** * 打开socket,监听服务器实时消息和拉取未读消息 * 收到消息 * @param message 消息 */ onmessage = (message: Message): void => { const user = useUserStore().getUser() //群聊里面,自己发的消息不再显示 if (user?.id === message.fromId) { message.mine = true } //友聊换chatId,chatId 不一样 if (ChatType.FRIEND === message.type && user?.id !== message.fromId) { message.chatId = message.fromId } //console.log("onmessage",message); if (message.messageType === MessageType.back) { useChatStore().backMessage(message)//消息撤回 //dbApi.deletemsg(message); dbApi.deleteLocalmsg(message); } else { if(message.mine){ useChatStore().delLocalMessageList(message); dbApi.deleteMyLocalmsg(message); } //dbApi.insertmsg(message); dbApi.insertLocalmsg(message); useChatStore().pushMessage(message)//收到消息 } if(!message.mine){ this.callback(message) } } /** * 文件类消息预发送 * @param message 消息 */ sendfileMessage(message: Message): void { var content=''; if(message.messageType===MessageType.voice){ content=encryptLong2('【音频】'); message.content=content; } else if(message.messageType===MessageType.video){ content=encryptLong2('【视频】'); message.content=content; } else if(message.messageType===MessageType.image){ content=encryptLong2('【图片】'); message.content=content; } else if(message.messageType===MessageType.file){ content=encryptLong2('【文件】'); message.content=content; } dbApi.insertLocalmsg(message); useChatStore().pushMessage(message);//消息插入队列 this.callback(message); } sendfileMsgFalse(message: Message): void { useChatStore().delLocalMessageList(message); dbApi.deleteMyLocalmsg(message); this.callback(message); } /** * 发送真正的聊天消息 * @param message 消息 */ sendMessage(message: Message): void { //数据加密后发送 //console.log('sendMessage1',message) var content=''; if(message.messageType===MessageType.text){ content=encryptLong2(message.content); message.content=content; } else if(message.messageType===MessageType.voice){ content=encryptLong2('【音频】'); message.content=content; } else if(message.messageType===MessageType.video){ content=encryptLong2('【视频】'); message.content=content; } else if(message.messageType===MessageType.image){ content=encryptLong2('【图片】'); message.content=content; } else if(message.messageType===MessageType.file){ content=encryptLong2('【文件】'); message.content=content; } console.log('sendMessage2',message) if(message.localtime){ const sendInfo = { code: SendCode.MESSAGE, message: message } this.send(JSON.stringify(sendInfo)); } else{ var currentTime = new Date().getTime(); message.localtime=currentTime; message.timestamp=currentTime; const sendInfo = { code: SendCode.MESSAGE, message: message } this.send(JSON.stringify(sendInfo)); message.mine=true; message.id=JSON.stringify(currentTime); console.log('sendMessage3',message) dbApi.insertLocalmsg(message); useChatStore().pushMessage(message);//消息插入队列 this.callback(message); } } /** * 发送webrtc通讯结果 * @param receipt 消息读取回执 */ sendWEBRTCresult(message: any): void { var currentTime = new Date().getTime(); message.localtime=currentTime; message.timestamp=currentTime; const sendInfo = { code: SendCode.MESSAGE, message: message } this.send(JSON.stringify(sendInfo)); message.mine=true; message.id=JSON.stringify(currentTime); console.log('sendMessage3',message) dbApi.insertLocalmsg(message); useChatStore().pushMessage(message);//消息插入队列 this.callback(message); } /** * 发送webrtc信令 * @param receipt 消息读取回执 */ sendWEBRTC(webrtc: Webrtc): void { const sendInfo = { code: SendCode.WEBRTC_xinling, message: webrtc } this.send(JSON.stringify(sendInfo)) } /** * 发送已读取消息 * @param receipt 消息读取回执 */ sendRead(receipt: Receipt): void { const sendInfo = { code: SendCode.READ, message: receipt } this.send(JSON.stringify(sendInfo)) } /** * reset和start方法主要用来控制心跳的定时。 */ reset(): void { // 清除定时器重新发送一个心跳信息 if (this.heartTask) { clearTimeout(this.heartTask) } if (this.reconnectTimeoutTask) { clearTimeout(this.reconnectTimeoutTask) } this.lockReconnect = false this.heartTask = setTimeout(() => { //这里发送一个心跳,后端收到后,返回一个心跳消息, //onmessage拿到返回的心跳就说明连接正常 this.send(ping) }, this.timeout) //onmessage拿到消息就会清理 reconnectTimeoutTask,如果没有清理,就会执行重连 this.reconnectTimeoutTask = setTimeout(() => { this.reconnect() }, this.timeoutError) } /** * 立即验证连接有效性 * 重置心跳检测和重连检测 * 立刻发送一个心跳信息 * 如果没有收到消息,就会执行重连 */ checkStatus(): void { // 清除定时器重新发送一个心跳信息 if (this.heartTask) { clearTimeout(this.heartTask) } if (this.reconnectTimeoutTask) { clearTimeout(this.reconnectTimeoutTask) } this.lockReconnect = false this.send(ping) //onmessage拿到消息就会清理 reconnectTimeoutTask,如果没有清理,就会执行重连 this.reconnectTimeoutTask = setTimeout(() => { this.reconnect() }, this.timeoutError - this.timeout) } /** * 收到消息的回调函数 */ callback = (message: Message) => { } // 重连 reconnect(): void { // 防止多个方法调用,多处重连 if (this.lockReconnect) { return } this.lockReconnect = true //没连接上会一直重连,设置延迟避免请求过多 this.reconnectTimeoutTask = setTimeout(() => { // 重新连接 this.init() this.lockReconnect = false }, this.timeoutError) } // 手动关闭 close(closeByUser:boolean): void { this.lockReconnect = false //主动关闭 if (this.heartTask) { clearTimeout(this.heartTask) } if (this.reconnectTimeoutTask) { clearTimeout(this.reconnectTimeoutTask) } this.closeByUser = closeByUser if (this.socket) { this.socket.close({ code: 1000, reason: '用户主动关闭' }) } } } export default WsRequest