From 43078dfc90dba8d932d229194cd4dde3ce97051b Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Thu, 17 Jul 2025 00:55:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96:=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=98=BE=E7=A4=BA=E5=8F=91=E9=80=81=E7=8A=B6?= =?UTF-8?q?=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/im-platform.sql | 4 +++- .../bx/implatform/dto/GroupMessageDTO.java | 3 +++ .../bx/implatform/dto/PrivateMessageDTO.java | 4 +++- .../bx/implatform/entity/GroupMessage.java | 6 +++++ .../bx/implatform/entity/PrivateMessage.java | 6 +++++ .../service/impl/FileServiceImpl.java | 9 ++++---- .../com/bx/implatform/vo/GroupMessageVO.java | 3 +++ .../bx/implatform/vo/PrivateMessageVO.java | 3 +++ .../chat-message-item/chat-message-item.vue | 2 +- im-uniapp/pages/chat/chat-box.vue | 5 ++-- im-uniapp/store/chatStore.js | 23 +++++++++++-------- im-web/src/api/wssocket.js | 1 - im-web/src/components/chat/ChatBox.vue | 5 ++-- .../src/components/chat/ChatMessageItem.vue | 4 ++-- im-web/src/store/chatStore.js | 23 +++++++++++-------- 15 files changed, 67 insertions(+), 34 deletions(-) diff --git a/db/im-platform.sql b/db/im-platform.sql index 575610f..60f72fb 100644 --- a/db/im-platform.sql +++ b/db/im-platform.sql @@ -31,6 +31,7 @@ create table `im_friend`( create table `im_private_message`( `id` bigint not null auto_increment primary key comment 'id', + `tmp_id` varchar(32) comment '临时id,由前端生成', `send_id` bigint not null comment '发送用户id', `recv_id` bigint not null comment '接收用户id', `content` text comment '发送内容', @@ -44,6 +45,7 @@ create table `im_private_message`( create table `im_group`( `id` bigint not null auto_increment primary key comment 'id', + `tmp_id` varchar(32) comment '临时id,由前端生成', `name` varchar(255) not null comment '群名字', `owner_id` bigint not null comment '群主id', `head_image` varchar(255) default '' comment '群头像', @@ -106,5 +108,5 @@ CREATE TABLE `im_file_info` ( `upload_time` datetime DEFAULT CURRENT_TIMESTAMP comment '上传时间', `is_permanent` tinyint DEFAULT 0 comment '是否永久文件', `md5` VARCHAR(64) NOT NULL comment '文件md5', - UNIQUE KEY `idx_md5` (md5) + KEY `idx_md5` (md5) ) ENGINE = InnoDB CHARSET = utf8mb4 comment '文件'; \ No newline at end of file diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java index 9f8e2c8..8b4f257 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java @@ -13,6 +13,9 @@ import java.util.List; @Schema(description = "群聊消息DTO") public class GroupMessageDTO { + @Schema(description = "临时id") + private String tmpId; + @NotNull(message = "群聊id不可为空") @Schema(description = "群聊id") private Long groupId; diff --git a/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java index f954f93..639325c 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java @@ -10,11 +10,13 @@ import org.hibernate.validator.constraints.Length; @Schema(description = "私聊消息DTO") public class PrivateMessageDTO { + @Schema(description = "临时id") + private String tmpId; + @NotNull(message = "接收用户id不可为空") @Schema(description = "接收用户id") private Long recvId; - @Length(max = 1024, message = "内容长度不得大于1024") @NotEmpty(message = "发送内容不可为空") @Schema(description = "发送内容") diff --git a/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java b/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java index a0ec72e..77a038a 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java @@ -25,6 +25,12 @@ public class GroupMessage { @TableId private Long id; + /** + * 临时id,由前端生成 + * 作用:如果用户正在发送消息时掉线了,可以通过此字段获取该消息的实际发送状态 + */ + private String tmpId; + /** * 群id */ diff --git a/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java b/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java index cc605f1..8c1dd52 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java @@ -22,6 +22,12 @@ public class PrivateMessage { */ private Long id; + /** + * 临时id,由前端生成 + * 作用:如果用户正在发送消息时掉线了,可以通过此字段获取该消息的实际发送状态 + */ + private String tmpId; + /** * 发送用户id */ 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 efca8e5..f509e35 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 @@ -63,7 +63,7 @@ public class FileServiceImpl extends ServiceImpl imple } // 如果文件已存在,直接复用 String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); - FileInfo fileInfo = findByMd5(md5); + FileInfo fileInfo = findByMd5(md5, FileType.FILE.code()); if (!Objects.isNull(fileInfo)) { // 更新上传时间 fileInfo.setUploadTime(new Date()); @@ -103,7 +103,7 @@ public class FileServiceImpl extends ServiceImpl imple UploadImageVO vo = new UploadImageVO(); // 如果文件已存在,直接复用 String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); - FileInfo fileInfo = findByMd5(md5); + FileInfo fileInfo = findByMd5(md5, FileType.IMAGE.code()); if (!Objects.isNull(fileInfo)) { // 更新上传时间和持久化标记 fileInfo.setIsPermanent(isPermanent || fileInfo.getIsPermanent()); @@ -131,7 +131,7 @@ public class FileServiceImpl extends ServiceImpl imple vo.setThumbUrl(generUrl(FileType.IMAGE, thumbFileName)); // 保存文件信息 saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), isPermanent); - }else{ + } else { // 小于50k,用原图充当缩略图 vo.setThumbUrl(generUrl(FileType.IMAGE, fileName)); // 保存文件信息,由于缩略图不允许删除,此时原图也不允许删除 @@ -157,9 +157,10 @@ public class FileServiceImpl extends ServiceImpl imple }; } - private FileInfo findByMd5(String md5) { + private FileInfo findByMd5(String md5, Integer fileType) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(FileInfo::getMd5, md5); + wrapper.eq(FileInfo::getFileType, fileType); return getOne(wrapper); } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java index ea41bde..9613a1f 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java @@ -14,6 +14,9 @@ public class GroupMessageVO { @Schema(description = "消息id") private Long id; + @Schema(description = "临时id") + private String tmpId; + @Schema(description = "群聊id") private Long groupId; diff --git a/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java index a1bbebe..7777a5f 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java @@ -14,6 +14,9 @@ public class PrivateMessageVO { @Schema(description = " 消息id") private Long id; + @Schema(description = "临时id") + private String tmpId; + @Schema(description = " 发送者id") private Long sendId; 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 382b700..113ae21 100644 --- a/im-uniapp/components/chat-message-item/chat-message-item.vue +++ b/im-uniapp/components/chat-message-item/chat-message-item.vue @@ -58,7 +58,7 @@ - + diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue index 0e5b1a4..edd21c1 100644 --- a/im-uniapp/pages/chat/chat-box.vue +++ b/im-uniapp/pages/chat/chat-box.vue @@ -167,6 +167,7 @@ export default { return; } let msgInfo = { + tmpId: this.generateId(), content: JSON.stringify(data), type: this.$enums.MESSAGE_TYPE.AUDIO, receipt: this.isReceipt @@ -307,6 +308,7 @@ export default { let receiptText = this.isReceipt ? "【回执消息】" : ""; let atText = this.createAtText(); let msgInfo = { + tmpId: this.generateId(), content: receiptText + sendText + atText, atUserIds: this.atUserIds, receipt: this.isReceipt, @@ -893,7 +895,6 @@ export default { }, buildTmpMessage(msgInfo) { let message = JSON.parse(JSON.stringify(msgInfo)); - message.tmpId = this.generateId(); message.sendId = this.mine.id; message.sendTime = new Date().getTime(); message.status = this.$enums.MESSAGE_STATUS.SENDING; @@ -902,7 +903,7 @@ export default { message.readedCount = 0; } return message; - }, + }, generateId() { // 生成临时id return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); diff --git a/im-uniapp/store/chatStore.js b/im-uniapp/store/chatStore.js index 3f6d251..4b0453f 100644 --- a/im-uniapp/store/chatStore.js +++ b/im-uniapp/store/chatStore.js @@ -30,14 +30,6 @@ export default defineStore('chatStore', { } this.privateMsgMaxId = chatsData.privateMsgMaxId || 0; this.groupMsgMaxId = chatsData.groupMsgMaxId || 0; - // 防止消息一直处在发送中状态 - cacheChats.forEach(chat => { - chat.messages.forEach(msg => { - if (msg.status == MESSAGE_STATUS.SENDING) { - msg.status = MESSAGE_STATUS.FAILED - } - }) - }) }, openChat(chatInfo) { let chats = this.curChats; @@ -63,6 +55,7 @@ export default defineStore('chatStore', { lastSendTime: new Date().getTime(), unreadCount: 0, hotMinIdx: 0, + readedMessageIdx: 0, messages: [], atMe: false, atAll: false, @@ -95,15 +88,17 @@ export default defineStore('chatStore', { readedMessage(pos) { let chat = this.findChatByFriend(pos.friendId); if (!chat) return; - chat.messages.forEach((m) => { + for (let idx = chat.readedMessageIdx; idx < chat.messages.length; idx++) { + let m = chat.messages[idx]; if (m.id && m.selfSend && m.status < MESSAGE_STATUS.RECALL) { // pos.maxId为空表示整个会话已读 if (!pos.maxId || m.id <= pos.maxId) { m.status = MESSAGE_STATUS.READED + chat.readedMessageIdx = idx; chat.stored = false; } } - }) + } if (!chat.stored) { this.saveToStorage(); } @@ -463,11 +458,19 @@ export default defineStore('chatStore', { if (!coldChat && !hotChat) { return; } + // 防止消息一直处在发送中状态 + hotChat && hotChat.messages.forEach(msg => { + if (msg.status == MESSAGE_STATUS.SENDING) { + msg.status = MESSAGE_STATUS.FAILED + } + }) // 冷热消息合并 let chat = Object.assign({}, coldChat, hotChat); if (hotChat && coldChat) { chat.messages = coldChat.messages.concat(hotChat.messages) } + // 历史版本没有readedMessageIdx字段,做兼容一下 + chat.readedMessageIdx = chat.readedMessageIdx || 0; chatsData.chats.push(chat); }) } diff --git a/im-web/src/api/wssocket.js b/im-web/src/api/wssocket.js index 2e57afa..d053d7c 100644 --- a/im-web/src/api/wssocket.js +++ b/im-web/src/api/wssocket.js @@ -92,7 +92,6 @@ let heartCheck = { websock.send(JSON.stringify(heartBeat)) } }, - reset: function () { clearTimeout(this.timeoutObj); this.timeoutObj = setTimeout(function () { diff --git a/im-web/src/components/chat/ChatBox.vue b/im-web/src/components/chat/ChatBox.vue index 848dc10..3bd0ab6 100644 --- a/im-web/src/components/chat/ChatBox.vue +++ b/im-web/src/components/chat/ChatBox.vue @@ -361,6 +361,7 @@ export default { return; } let msgInfo = { + tmpId: this.generateId(), content: JSON.stringify(data), type: this.$enums.MESSAGE_TYPE.AUDIO, receipt: this.isReceipt @@ -452,6 +453,7 @@ export default { reject(); } let msgInfo = { + tmpId: this.generateId(), content: sendText, type: this.$enums.MESSAGE_TYPE.TEXT } @@ -670,7 +672,6 @@ export default { }, buildTmpMessage(msgInfo) { let message = JSON.parse(JSON.stringify(msgInfo)); - message.tmpId = this.generateId(); message.sendId = this.mine.id; message.sendTime = new Date().getTime(); message.status = this.$enums.MESSAGE_STATUS.SENDING; @@ -679,7 +680,7 @@ export default { message.readedCount = 0; } return message; - }, + }, generateId() { // 生成临时id return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); diff --git a/im-web/src/components/chat/ChatMessageItem.vue b/im-web/src/components/chat/ChatMessageItem.vue index b601d6c..ec8968a 100644 --- a/im-web/src/components/chat/ChatMessageItem.vue +++ b/im-web/src/components/chat/ChatMessageItem.vue @@ -254,7 +254,7 @@ export default { .message-content-wrapper { position: relative; display: flex; - align-items: end; + align-items: center; .sending { width: 25px; @@ -270,7 +270,7 @@ export default { color: #e45050; font-size: 30px; cursor: pointer; - margin: 0 20px; + margin: 0 3px; } } diff --git a/im-web/src/store/chatStore.js b/im-web/src/store/chatStore.js index 02be2a0..27719be 100644 --- a/im-web/src/store/chatStore.js +++ b/im-web/src/store/chatStore.js @@ -40,14 +40,6 @@ export default defineStore('chatStore', { this.privateMsgMaxId = chatsData.privateMsgMaxId || 0; this.groupMsgMaxId = chatsData.groupMsgMaxId || 0; cacheChats = chatsData.chats || []; - // 防止消息一直处在发送中状态 - cacheChats.forEach(chat => { - chat.messages.forEach(msg => { - if (msg.status == MESSAGE_STATUS.SENDING) { - msg.status = MESSAGE_STATUS.FAILED - } - }) - }) }, openChat(chatInfo) { let chats = this.findChats() @@ -73,6 +65,7 @@ export default defineStore('chatStore', { lastSendTime: new Date().getTime(), unreadCount: 0, hotMinIdx: 0, + readedMessageIdx: 0, messages: [], atMe: false, atAll: false, @@ -103,15 +96,17 @@ export default defineStore('chatStore', { readedMessage(pos) { let chat = this.findChatByFriend(pos.friendId); if (!chat) return; - chat.messages.forEach((m) => { + for (let idx = chat.readedMessageIdx; idx < chat.messages.length; idx++) { + let m = chat.messages[idx]; if (m.id && m.selfSend && m.status < MESSAGE_STATUS.RECALL) { // pos.maxId为空表示整个会话已读 if (!pos.maxId || m.id <= pos.maxId) { m.status = MESSAGE_STATUS.READED + chat.readedMessageIdx = idx; chat.stored = false; } } - }) + } this.saveToStorage(); }, removeChat(idx) { @@ -474,11 +469,19 @@ export default defineStore('chatStore', { } let coldChat = chats[i]; let hotChat = chats[i + 1]; + // 防止消息一直处在发送中状态 + hotChat && hotChat.messages.forEach(msg => { + if (msg.status == MESSAGE_STATUS.SENDING) { + msg.status = MESSAGE_STATUS.FAILED + } + }) // 冷热消息合并 let chat = Object.assign({}, coldChat, hotChat); if (hotChat && coldChat) { chat.messages = coldChat.messages.concat(hotChat.messages) } + // 历史版本没有readedMessageIdx字段,做兼容一下 + chat.readedMessageIdx = chat.readedMessageIdx || 0; chatsData.chats.push(chat); } this.initChats(chatsData);