diff --git a/im-platform/src/main/java/com/bx/implatform/contant/Constant.java b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java index 3884cd7..d255ba3 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/Constant.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java @@ -15,6 +15,11 @@ public final class Constant { */ public static final Long MAX_FILE_SIZE = 20 * 1024 * 1024L; + /** + * 最大文件名长度 + */ + public static final Long MAX_FILE_NAME_LENGTH = 128L; + /** * 大群人数上限 */ diff --git a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java index d75ab86..ba61cd2 100644 --- a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java +++ b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java @@ -26,19 +26,23 @@ public class PrivateMessageListener implements MessageListener @Lazy @Autowired private PrivateMessageService privateMessageService; + @Override public void process(List> results) { Set messageIds = new HashSet<>(); - for(IMSendResult result : results){ + for (IMSendResult result : results) { PrivateMessageVO messageInfo = result.getData(); // 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态 if (result.getCode().equals(IMSendCode.SUCCESS.code()) && !Objects.isNull(messageInfo.getId())) { - messageIds.add(messageInfo.getId()); - log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); + if (result.getReceiver().getId().equals(messageInfo.getRecvId())) { + messageIds.add(messageInfo.getId()); + log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), + result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); + } } } // 批量修改状态 - if(CollUtil.isNotEmpty(messageIds)){ + if (CollUtil.isNotEmpty(messageIds)) { UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.lambda().in(PrivateMessage::getId, messageIds) .eq(PrivateMessage::getStatus, MessageStatus.PENDING.code()) diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java index 761f2dc..9eb4ce5 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java @@ -60,6 +60,8 @@ public class FileServiceImpl extends ServiceImpl imple public String uploadFile(MultipartFile file) { try { Long userId = SessionContext.getSession().getUserId(); + // 文件名长度校验 + checkFileNameLength(file); // 大小校验 if (file.getSize() > Constant.MAX_FILE_SIZE) { throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件大小不能超过20M"); @@ -95,6 +97,8 @@ public class FileServiceImpl extends ServiceImpl imple public UploadImageVO uploadImage(MultipartFile file, Boolean isPermanent,Long thumbSize) { try { Long userId = SessionContext.getSession().getUserId(); + // 文件名长度校验 + checkFileNameLength(file); // 大小校验 if (file.getSize() > Constant.MAX_IMAGE_SIZE) { throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片大小不能超过20M"); @@ -170,6 +174,7 @@ public class FileServiceImpl extends ServiceImpl imple LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(FileInfo::getMd5, md5); wrapper.eq(FileInfo::getFileType, fileType); + wrapper.last("limit 1"); return getOne(wrapper); } @@ -199,4 +204,9 @@ public class FileServiceImpl extends ServiceImpl imple this.save(fileInfo); } + private void checkFileNameLength(MultipartFile file){ + if(file.getOriginalFilename().length() > Constant.MAX_FILE_NAME_LENGTH){ + throw new GlobalException("文件名长度不能超过" + Constant.MAX_FILE_NAME_LENGTH); + } + } } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java index 24c4972..3401f8a 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java @@ -189,9 +189,9 @@ public class PrivateMessageServiceImpl extends ServiceImpl messages = this.list(wrapper); // 更新消息为送达状态 - List messageIds = - messages.stream().filter(m -> m.getStatus().equals(MessageStatus.PENDING.code())).map(PrivateMessage::getId) - .collect(Collectors.toList()); + List messageIds = messages.stream().filter(m -> m.getRecvId().equals(session.getUserId())) + .filter(m -> m.getStatus().equals(MessageStatus.PENDING.code())).map(PrivateMessage::getId) + .collect(Collectors.toList()); if (!messageIds.isEmpty()) { LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.in(PrivateMessage::getId, messageIds); diff --git a/im-uniapp/components/chat-message-item/chat-message-item.vue b/im-uniapp/components/chat-message-item/chat-message-item.vue index 5b1a43d..dbcecc3 100644 --- a/im-uniapp/components/chat-message-item/chat-message-item.vue +++ b/im-uniapp/components/chat-message-item/chat-message-item.vue @@ -11,7 +11,7 @@ :name="showName" size="small"> - {{ showName }} + {{ showName }} @@ -65,7 +65,7 @@ {{ msgInfo.content }} - + { // 更新消息 + tmpMessage = JSON.parse(JSON.stringify(tmpMessage)); tmpMessage.id = m.id; tmpMessage.status = m.status; this.chatStore.updateMessage(tmpMessage, chat); @@ -194,7 +196,10 @@ export default { // 滚动到底部 this.scrollToBottom(); this.isReceipt = false; - + }).catch(() => { + tmpMessage = JSON.parse(JSON.stringify(tmpMessage)); + tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED; + this.chatStore.updateMessage(tmpMessage, chat); }) }, onRtCall(msgInfo) { @@ -474,6 +479,9 @@ export default { msgInfo.status = m.status; this.isReceipt = false; this.chatStore.updateMessage(msgInfo, file.chat); + }).catch(() => { + msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; + this.chatStore.updateMessage(msgInfo, file.chat); }) }, onUploadImageFail(file, err) { @@ -527,6 +535,9 @@ export default { msgInfo.status = m.status; this.isReceipt = false; this.chatStore.updateMessage(msgInfo, file.chat); + }).catch(() => { + msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; + this.chatStore.updateMessage(msgInfo, file.chat); }) }, onUploadFileFail(file, res) { @@ -631,13 +642,21 @@ export default { }); }, onClickToBottom() { - this.scrollToBottom(); - // 有些设备滚到底部时会莫名触发滚动到顶部的事件 - // 所以这里延迟100s保证能准确设置底部标志 + /** + * 有些ios设备在点击回到底部后页面会卡住,原因不详 + * 解决方式: 延迟300ms再滚动 + */ setTimeout(() => { - this.isInBottom = true; - this.newMessageSize = 0; - }, 100) + this.scrollToBottom(); + /** + * 有些设备滚到底部时会莫名触发滚动到顶部的事件 + * 解决方式: 延迟100ms保证能准确设置底部标志 + */ + setTimeout(() => { + this.isInBottom = true; + this.newMessageSize = 0; + }, 100) + }, 300) }, onScroll(e) { // 记录当前滚动条高度 @@ -645,16 +664,14 @@ export default { }, onScrollToTop() { if (this.showMinIdx > 0) { - // #ifndef H5 - // 防止滚动条定格在顶部,不能一直往上滚,app和小程序采用scroll-into-view定位 - this.scrollToMsgIdx(this.showMinIdx); - // #endif - // #ifdef H5 - // 防止滚动条定格在顶部,不能一直往上滚,h5采用scroll-top定位 + // 防止滚动条定格在顶部,不能一直往上滚 if (uni.getSystemInfoSync().platform == 'ios') { + // ios采用scroll-top定位,否则可能会出现白屏 this.holdingScrollBar(this.scrollViewHeight); + } else { + // 安卓采用scroll-into-view定位 + this.scrollToMsgIdx(this.showMinIdx); } - // #endif // 多展示20条信息 this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0; } @@ -973,7 +990,13 @@ export default { }, generateId() { // 生成临时id - return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); + const id = String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); + // 必须保证id是递增 + if (this.maxTmpId > id) { + return this.generateId(); + } + this.maxTmpId = id; + return id; } }, computed: { @@ -1098,6 +1121,8 @@ export default { // 清空底部标志 this.isInBottom = true; this.newMessageSize = 0; + // 清空消息临时id + this.maxTmpId = 0; // 监听键盘高度 this.listenKeyBoard(); // 计算聊天窗口高度 diff --git a/im-web/src/components/chat/ChatBox.vue b/im-web/src/components/chat/ChatBox.vue index 69152a1..c6e2322 100644 --- a/im-web/src/components/chat/ChatBox.vue +++ b/im-web/src/components/chat/ChatBox.vue @@ -132,7 +132,8 @@ export default { reqQueue: [], // 等待发送的请求队列 isSending: false, // 是否正在发消息 isInBottom: false, // 滚动条是否在底部 - newMessageSize: 0 // 滚动条不在底部时新的消息数量 + newMessageSize: 0, // 滚动条不在底部时新的消息数量 + maxTmpId: 0 // 最后生成的临时ID } }, methods: { @@ -164,6 +165,9 @@ export default { msgInfo.status = m.status; this.isReceipt = false; this.chatStore.updateMessage(msgInfo, file.chat); + }).catch(() => { + msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; + this.chatStore.updateMessage(msgInfo, file.chat); }) }, onImageFail(e, file) { @@ -227,6 +231,9 @@ export default { this.isReceipt = false; this.refreshPlaceHolder(); this.chatStore.updateMessage(msgInfo, file.chat); + }).catch(() => { + msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; + this.chatStore.updateMessage(msgInfo, file.chat); }) }, onFileFail(e, file) { @@ -567,7 +574,7 @@ export default { this.$http({ url: url, method: 'put' - }).then(() => { }) + }).then(() => {}) this.chatStore.resetUnreadCount(this.chat) } }, @@ -718,25 +725,31 @@ export default { getImageSize(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onload = function (event) { + reader.onload = function(event) { const img = new Image(); - img.onload = function () { + img.onload = function() { resolve({ width: img.width, height: img.height }); }; - img.onerror = function () { + img.onerror = function() { reject(new Error('无法加载图片')); }; img.src = event.target.result; }; - reader.onerror = function () { + reader.onerror = function() { reject(new Error('无法读取文件')); }; reader.readAsDataURL(file); }); }, generateId() { - // 生成临时id - return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); + // 生成临时id + const id = String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); + // 必须保证id是递增 + if (this.maxTmpId > id) { + return this.generateId(); + } + this.maxTmpId = id; + return id; } }, computed: { @@ -793,7 +806,7 @@ export default { chat: { handler(newChat, oldChat) { if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type || - newChat.targetId != oldChat.targetId)) { + newChat.targetId != oldChat.targetId)) { this.userInfo = {} this.group = {}; this.groupMembers = []; @@ -816,6 +829,8 @@ export default { this.resetEditor(); // 复位回执消息 this.isReceipt = false; + // 清空消息临时id + this.maxTmpId = 0; // 更新placeholder this.refreshPlaceHolder(); }