From 5dedc178b1e482505b02fe296b23dbfad218a691 Mon Sep 17 00:00:00 2001 From: "xie.bx" Date: Thu, 9 Nov 2023 22:40:51 +0800 Subject: [PATCH] =?UTF-8?q?ws=E9=87=8D=E8=BF=9E=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/GroupMessageServiceImpl.java | 24 ++++--- im-ui/src/api/wssocket.js | 33 +++------- im-ui/src/components/chat/ChatBox.vue | 5 +- im-ui/src/view/Home.vue | 63 ++++++++++--------- im-uniapp/App.vue | 19 ++---- im-uniapp/common/wssocket.js | 38 +++++------ 6 files changed, 86 insertions(+), 96 deletions(-) diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java index 5be6caf..d13bf57 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java @@ -1,8 +1,10 @@ package com.bx.implatform.service.impl; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.bx.imclient.IMClient; @@ -198,6 +200,9 @@ public class GroupMessageServiceImpl extends ServiceImpl members = groupMemberService.findByUserId(session.getUserId()); List ids = members.stream().map(GroupMember::getGroupId).collect(Collectors.toList()); + if(CollectionUtil.isEmpty(ids)){ + return Collections.EMPTY_LIST; + } // 只能拉取最近1个月的 Date minDate = DateTimeUtils.addMonths(new Date(), -1); LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); @@ -242,6 +247,16 @@ public class GroupMessageServiceImpl extends ServiceImpl wrapper = Wrappers.lambdaQuery(); + wrapper.eq(GroupMessage::getGroupId, groupId) + .orderByDesc(GroupMessage::getId) + .last("limit 1") + .select(GroupMessage::getId); + GroupMessage message = this.getOne(wrapper); + if(Objects.isNull(message)){ + return; + } // 推送消息给自己的其他终端 GroupMessageVO msgInfo = new GroupMessageVO(); msgInfo.setType(MessageType.READED.code()); @@ -254,14 +269,7 @@ public class GroupMessageServiceImpl extends ServiceImpl wrapper = Wrappers.lambdaQuery(); - wrapper.eq(GroupMessage::getGroupId, groupId) - .orderByDesc(GroupMessage::getId) - .last("limit 1") - .select(GroupMessage::getId); - GroupMessage message = this.getOne(wrapper); + // 记录已读消息位置 String key = StrUtil.join(":",RedisKey.IM_GROUP_READED_POSITION,groupId,session.getUserId()); redisTemplate.opsForValue().set(key, message.getId()); diff --git a/im-ui/src/api/wssocket.js b/im-ui/src/api/wssocket.js index e014edd..ef2a237 100644 --- a/im-ui/src/api/wssocket.js +++ b/im-ui/src/api/wssocket.js @@ -1,19 +1,11 @@ var websock = null; let rec; //断线重连后,延迟5秒重新创建WebSocket连接 rec用来存储延迟请求的代码 let isConnect = false; //连接标识 避免重复连接 -let wsurl = ""; -let accessToken = ""; let messageCallBack = null; -let openCallBack = null; let closeCallBack = null -let init = (url,token) => { - wsurl = url; - accessToken = token; -}; - -let connect = () => { +let connect = (wsurl,accessToken) => { try { if (isConnect) { return; @@ -25,8 +17,6 @@ let connect = () => { if (sendInfo.cmd == 0) { heartCheck.start() console.log('WebSocket登录成功') - // 登录成功才算连接完成 - openCallBack && openCallBack(); } else if (sendInfo.cmd == 1) { // 重新开启心跳定时 heartCheck.reset(); @@ -59,16 +49,16 @@ let connect = () => { websock.onerror = function() { console.log('WebSocket连接发生错误') isConnect = false; //连接断开修改标识 - reConnect(); + reconnect(wsurl,accessToken); } } catch (e) { console.log("尝试创建连接失败"); - reConnect(); //如果无法连接上webSocket 那么重新连接!可能会因为服务器重新部署,或者短暂断网等导致无法创建连接 + reconnect(wsurl,accessToken); //如果无法连接上webSocket 那么重新连接!可能会因为服务器重新部署,或者短暂断网等导致无法创建连接 } }; //定义重连函数 -let reConnect = () => { +let reconnect = (wsurl,accessToken) => { console.log("尝试重新连接"); if (isConnect){ //如果已经连上就不在重连了 @@ -76,12 +66,12 @@ let reConnect = () => { } rec && clearTimeout(rec); rec = setTimeout(function() { // 延迟5秒重连 避免过多次过频繁请求重连 - connect(); - }, 5000); + connect(wsurl,accessToken); + }, 15000); }; //设置关闭连接 -let close = () => { - websock && websock.close(); +let close = (code) => { + websock && websock.close(code); }; @@ -136,20 +126,15 @@ let onMessage = (callback) => { } -let onOpen = (callback) => { - openCallBack = callback; -} - let onClose = (callback) => { closeCallBack = callback; } // 将方法暴露出去 export { - init, connect, + reconnect, close, sendMessage, - onOpen, onMessage, onClose } diff --git a/im-ui/src/components/chat/ChatBox.vue b/im-ui/src/components/chat/ChatBox.vue index e4f06b4..5fd5e1e 100644 --- a/im-ui/src/components/chat/ChatBox.vue +++ b/im-ui/src/components/chat/ChatBox.vue @@ -512,11 +512,13 @@ // 滚到底部 this.scrollToBottom(); this.sendText = ""; + this.showSide = false; // 消息已读 this.readedMessage() // 初始状态只显示30条消息 let size = this.chat.messages.length; this.showMinIdx = size > 30 ? size - 30 : 0; + // 保持输入框焦点 this.$nextTick(() => { this.$refs.sendBox.focus(); @@ -573,7 +575,7 @@ .im-chat-box { >ul { - padding: 20px; + padding: 0 20px; li { list-style-type: none; @@ -612,6 +614,7 @@ } .send-content-area { + position: relative; display: flex; flex-direction: column; height: 100%; diff --git a/im-ui/src/view/Home.vue b/im-ui/src/view/Home.vue index d70cdbb..20b4d01 100644 --- a/im-ui/src/view/Home.vue +++ b/im-ui/src/view/Home.vue @@ -74,7 +74,7 @@ data() { return { showSettingDialog: false, - lastPlayAudioTime: new Date()-1000 + lastPlayAudioTime: new Date() - 1000 } }, methods: { @@ -84,16 +84,19 @@ this.loadPrivateMessage(this.$store.state.chatStore.privateMsgMaxId); this.loadGroupMessage(this.$store.state.chatStore.groupMsgMaxId); // ws初始化 - this.$wsApi.init(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken")); - this.$wsApi.connect(); - this.$wsApi.onOpen(); + this.$wsApi.connect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken")); this.$wsApi.onMessage((cmd, msgInfo) => { if (cmd == 2) { + // 关闭ws + this.$wsApi.close(3000) // 异地登录,强制下线 - this.$message.error("您已在其他地方登陆,将被强制下线"); - setTimeout(() => { - location.href = "/"; - }, 1000) + this.$alert("您已在其他地方登陆,将被强制下线", "强制下线通知", { + confirmButtonText: '确定', + callback: action => { + location.href = "/"; + } + }); + } else if (cmd == 3) { // 插入私聊消息 this.handlePrivateMessage(msgInfo); @@ -104,20 +107,18 @@ }) this.$wsApi.onClose((e) => { console.log(e); - if (e.code == 1006) { - // 服务器主动断开 - this.$message.error("连接已断开,请重新登录"); - location.href = "/"; - } else { - this.$wsApi.connect(); + if (e.code != 3000) { + // 断线重连 + this.$message.error("连接断开,正在尝试重新连接..."); + this.$wsApi.reconnect(process.env.VUE_APP_WS_URL, sessionStorage.getItem("accessToken")); } }); }).catch((e) => { - console.log("初始化失败",e); + console.log("初始化失败", e); }) }, loadPrivateMessage(minId) { - this.$store.commit("loadingPrivateMsg",true) + this.$store.commit("loadingPrivateMsg", true) this.$http({ url: "/message/private/loadMessage?minId=" + minId, method: 'get' @@ -126,20 +127,20 @@ msgInfo.selfSend = msgInfo.sendId == this.$store.state.userStore.userInfo.id; let friendId = msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId; let friend = this.$store.state.friendStore.friends.find((f) => f.id == friendId); - if(friend){ - this.insertPrivateMessage(friend,msgInfo); - } + if (friend) { + this.insertPrivateMessage(friend, msgInfo); + } }) if (msgInfos.length == 100) { // 继续拉取 this.loadPrivateMessage(msgInfos[99].id); - }else{ - this.$store.commit("loadingPrivateMsg",false) + } else { + this.$store.commit("loadingPrivateMsg", false) } }) }, loadGroupMessage(minId) { - this.$store.commit("loadingGroupMsg",true) + this.$store.commit("loadingGroupMsg", true) this.$http({ url: "/message/group/loadMessage?minId=" + minId, method: 'get' @@ -148,15 +149,15 @@ msgInfo.selfSend = msgInfo.sendId == this.$store.state.userStore.userInfo.id; let groupId = msgInfo.groupId; let group = this.$store.state.groupStore.groups.find((g) => g.id == groupId); - if(group){ - this.insertGroupMessage(group,msgInfo); + if (group) { + this.insertGroupMessage(group, msgInfo); } }) if (msgInfos.length == 100) { // 继续拉取 this.loadGroupMessage(msgInfos[99].id); - }else{ - this.$store.commit("loadingGroupMsg",false) + } else { + this.$store.commit("loadingGroupMsg", false) } }) }, @@ -212,7 +213,7 @@ // 插入消息 this.$store.commit("insertMessage", msg); // 播放提示音 - if(!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED){ + if (!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED) { this.playAudioTip(); } }, @@ -247,7 +248,7 @@ // 插入消息 this.$store.commit("insertMessage", msg); // 播放提示音 - if(!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED){ + if (!msg.selfSend && msg.status != this.$enums.MESSAGE_STATUS.READED) { this.playAudioTip(); } }, @@ -257,14 +258,14 @@ location.href = "/"; }, playAudioTip() { - if(new Date() - this.lastPlayAudioTime > 1000){ + if (new Date() - this.lastPlayAudioTime > 1000) { this.lastPlayAudioTime = new Date(); let audio = new Audio(); let url = require(`@/assets/audio/tip.wav`); audio.src = url; audio.play(); - } - + } + }, showSetting() { this.showSettingDialog = true; diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index 2dddb5e..715f6ac 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -28,10 +28,8 @@ }, initWebSocket() { let loginInfo = uni.getStorageSync("loginInfo") - let userId = store.state.userStore.userInfo.id; - wsApi.init(process.env.WS_URL, loginInfo.accessToken); - wsApi.connect(); - wsApi.onOpen() + wsApi.init(); + wsApi.connect(process.env.WS_URL, loginInfo.accessToken); wsApi.onMessage((cmd, msgInfo) => { if (cmd == 2) { // 异地登录,强制下线 @@ -49,20 +47,15 @@ } }); wsApi.onClose((res) => { - // 1006是服务器主动断开,3000是APP主动关闭 - if (res.code == 1006) { - uni.showToast({ - title: '连接已断开,请重新登录', - icon: 'none', - }) - this.exit(); - } else if (res.code != 3000) { + // 3000是客户端主动关闭 + if (res.code != 3000) { // 重新连接 uni.showToast({ title: '连接已断开,尝试重新连接...', icon: 'none', }) - wsApi.connect(); + let loginInfo = uni.getStorageSync("loginInfo") + wsApi.reconnect(process.env.WS_URL, loginInfo.accessToken); } }) }, diff --git a/im-uniapp/common/wssocket.js b/im-uniapp/common/wssocket.js index a724820..1f3a04a 100644 --- a/im-uniapp/common/wssocket.js +++ b/im-uniapp/common/wssocket.js @@ -1,20 +1,12 @@ let wsurl = ""; let accessToken = ""; -let openCallBack = null; let messageCallBack = null; let closeCallBack = null; let isConnect = false; //连接标识 避免重复连接 -let hasInit = false; +let rec = null; + +let init = () => { -let init = (url, token) => { - wsurl = url; - accessToken = token; - // 防止重新注册事件 - if(hasInit){ - return; - } - hasInit = true; - uni.onSocketOpen((res) => { console.log("WebSocket连接已打开"); isConnect = true; @@ -35,8 +27,6 @@ let init = (url, token) => { if (sendInfo.cmd == 0) { heartCheck.start() console.log('WebSocket登录成功') - // 登录成功才算连接完成 - openCallBack && openCallBack(); } else if (sendInfo.cmd == 1) { // 重新开启心跳定时 heartCheck.reset(); @@ -48,7 +38,6 @@ let init = (url, token) => { }) uni.onSocketClose((res) => { - console.log(res) console.log('WebSocket连接关闭') isConnect = false; //断开后修改标识 closeCallBack && closeCallBack(res); @@ -64,7 +53,9 @@ let init = (url, token) => { }) }; -let connect = ()=>{ +let connect = (url, token)=>{ + wsurl = url; + accessToken = token; if (isConnect) { return; } @@ -83,6 +74,18 @@ let connect = ()=>{ }); } +//定义重连函数 +let reconnect = (wsurl,accessToken) => { + console.log("尝试重新连接"); + if (isConnect){ + //如果已经连上就不在重连了 + return; + } + rec && clearTimeout(rec); + rec = setTimeout(function() { // 延迟15秒重连 避免过多次过频繁请求重连 + connect(wsurl,accessToken); + }, 15000); +}; //设置关闭连接 let close = () => { @@ -142,9 +145,6 @@ function onMessage(callback) { messageCallBack = callback; } -function onOpen(callback) { - openCallBack = callback; -} function onClose(callback) { closeCallBack = callback; @@ -155,9 +155,9 @@ function onClose(callback) { export { init, connect, + reconnect, close, sendMessage, onMessage, - onOpen, onClose } \ No newline at end of file