Browse Source

优化: 提升拉取离线消息效率

master
xsx 8 months ago
parent
commit
4f205e1cbe
  1. 9
      im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java
  2. 9
      im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java
  3. 8
      im-platform/src/main/java/com/bx/implatform/service/GroupMessageService.java
  4. 8
      im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java
  5. 83
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  6. 36
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  7. 90
      im-uniapp/App.vue
  8. 1
      im-uniapp/common/request.js
  9. 2
      im-uniapp/components/chat-item/chat-item.vue
  10. 3
      im-uniapp/pages/chat/chat-box.vue
  11. 2
      im-uniapp/pages/chat/chat.vue
  12. 63
      im-uniapp/store/chatStore.js
  13. 57
      im-web/src/store/chatStore.js
  14. 2
      im-web/src/view/Chat.vue
  15. 73
      im-web/src/view/Home.vue

9
im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java

@ -35,12 +35,19 @@ public class GroupMessageController {
}
@GetMapping("/pullOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送")
@Operation(summary = "拉取离线消息(已废弃)", description = "拉取离线消息,消息将通过webscoket异步推送")
public Result pullOfflineMessage(@RequestParam Long minId) {
groupMessageService.pullOfflineMessage(minId);
return ResultUtils.success();
}
@GetMapping(value = "/loadOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息")
public Result<List<GroupMessageVO>> loadOfflineMessage(@RequestParam Long minId) {
return ResultUtils.success(groupMessageService.loadOffineMessage(minId));
}
@PutMapping("/readed")
@Operation(summary = "消息已读", description = "将群聊中的消息状态置为已读")
public Result readedMessage(@RequestParam Long groupId) {

9
im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java

@ -35,12 +35,19 @@ public class PrivateMessageController {
}
@GetMapping("/pullOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送")
@Operation(summary = "拉取离线消息(已废弃)", description = "拉取离线消息,消息将通过webscoket异步推送")
public Result pullOfflineMessage(@RequestParam Long minId) {
privateMessageService.pullOfflineMessage(minId);
return ResultUtils.success();
}
@GetMapping(value = "/loadOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息")
public Result<List<PrivateMessageVO>> loadOfflineMessage(@RequestParam Long minId) {
return ResultUtils.success(privateMessageService.loadOfflineMessage(minId));
}
@PutMapping("/readed")
@Operation(summary = "消息已读", description = "将会话中接收的消息状态置为已读")
public Result readedMessage(@RequestParam Long friendId) {

8
im-platform/src/main/java/com/bx/implatform/service/GroupMessageService.java

@ -31,6 +31,14 @@ public interface GroupMessageService extends IService<GroupMessage> {
*/
void pullOfflineMessage(Long minId);
/**
* 拉取离线消息只能拉取最近1个月的消息
*
* @param minId 消息起始id
*/
List<GroupMessageVO> loadOffineMessage(Long minId);
/**
* 消息已读,同步其他终端清空未读数量
*

8
im-platform/src/main/java/com/bx/implatform/service/PrivateMessageService.java

@ -43,6 +43,14 @@ public interface PrivateMessageService extends IService<PrivateMessage> {
*/
void pullOfflineMessage(Long minId);
/**
* 拉取离线消息只能拉取最近1个月的消息
*
* @param minId 消息起始id
*/
List<PrivateMessageVO> loadOfflineMessage(Long minId);
/**
* 消息已读,将整个会话的消息都置为已读状态
*

83
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java

@ -80,6 +80,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
msg.setSendTime(new Date());
msg.setSendNickName(member.getShowNickName());
msg.setAtUserIds(CommaTextUtils.asText(dto.getAtUserIds()));
msg.setStatus(MessageStatus.PENDING.code());
// 过滤内容中的敏感词
if (MessageType.TEXT.code().equals(dto.getType())) {
msg.setContent(sensitiveFilterUtil.filter(dto.getContent()));
@ -164,10 +165,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
wrapper.gt(GroupMessage::getSendTime, minDate);
wrapper.in(GroupMessage::getGroupId, groupIds);
wrapper.orderByDesc(GroupMessage::getId);
if (minId <= 0) {
// 首次拉取限制消息数量大小,防止内存溢出
wrapper.last("limit 100000");
}
wrapper.last("limit 50000");
List<GroupMessage> messages = this.list(wrapper);
// 通过群聊对消息进行分组
Map<Long, List<GroupMessage>> messageGroupMap =
@ -254,6 +252,83 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
});
}
@Override
public List<GroupMessageVO> loadOffineMessage(Long minId) {
UserSession session = SessionContext.getSession();
// 查询用户加入的群组
List<GroupMember> members = groupMemberService.findByUserId(session.getUserId());
Map<Long, GroupMember> groupMemberMap = CollStreamUtil.toIdentityMap(members, GroupMember::getGroupId);
Set<Long> groupIds = groupMemberMap.keySet();
// 只能拉取最近1个月的消息
Date minDate = DateUtils.addMonths(new Date(), -1);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId);
wrapper.gt(GroupMessage::getSendTime, minDate);
wrapper.in(GroupMessage::getGroupId, groupIds);
wrapper.orderByDesc(GroupMessage::getId);
wrapper.last("limit 50000");
List<GroupMessage> messages = this.list(wrapper);
// 退群前的消息
List<GroupMember> quitMembers = groupMemberService.findQuitInMonth(session.getUserId());
for (GroupMember quitMember : quitMembers) {
wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId);
wrapper.between(GroupMessage::getSendTime, minDate, quitMember.getQuitTime());
wrapper.eq(GroupMessage::getGroupId, quitMember.getGroupId());
wrapper.orderByDesc(GroupMessage::getId);
wrapper.last("limit 1000");
List<GroupMessage> groupMessages = this.list(wrapper);
if (!groupMessages.isEmpty()) {
messages.addAll(groupMessages);
groupMemberMap.put(quitMember.getGroupId(), quitMember);
}
}
// 通过群聊对消息进行分组
Map<Long, List<GroupMessage>> messageGroupMap =
messages.stream().collect(Collectors.groupingBy(GroupMessage::getGroupId));
List<GroupMessageVO> vos = new LinkedList<>();
for (Map.Entry<Long, List<GroupMessage>> entry : messageGroupMap.entrySet()) {
Long groupId = entry.getKey();
List<GroupMessage> groupMessages = entry.getValue();
// 填充消息状态
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
Object o = redisTemplate.opsForHash().get(key, session.getUserId().toString());
long readedMaxId = Objects.isNull(o) ? -1 : Long.parseLong(o.toString());
Map<Object, Object> maxIdMap = null;
for (GroupMessage m : groupMessages) {
// 排除加群之前的消息
GroupMember member = groupMemberMap.get(m.getGroupId());
if (DateUtil.compare(member.getCreatedTime(), m.getSendTime()) > 0) {
continue;
}
// 排除不需要接收的消息
List<String> recvIds = CommaTextUtils.asList(m.getRecvIds());
if (!recvIds.isEmpty() && !recvIds.contains(session.getUserId().toString())) {
continue;
}
// 组装vo
GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class);
// 被@用户列表
List<String> atIds = CommaTextUtils.asList(m.getAtUserIds());
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
// 填充状态
vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.PENDING.code());
// 针对回执消息填充已读人数
if (m.getReceipt()) {
if (Objects.isNull(maxIdMap)) {
maxIdMap = redisTemplate.opsForHash().entries(key);
}
int count = getReadedUserIds(maxIdMap, m.getId(), m.getSendId()).size();
vo.setReadedCount(count);
}
vos.add(vo);
}
}
// 排序
return vos.stream().sorted(Comparator.comparing(GroupMessageVO::getId)).collect(Collectors.toList());
}
@Override
public void readedMessage(Long groupId) {
UserSession session = SessionContext.getSession();

36
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -32,6 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;
@ -140,9 +141,8 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
UserSession session = SessionContext.getSession();
// 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
// 只能拉取最近3个月的消息,移动端只拉取一个月消息
int months = session.getTerminal().equals(IMTerminalType.APP.code()) ? 1 : 3;
Date minDate = DateUtils.addMonths(new Date(), -months);
// 只能拉取最近1个月的消息
Date minDate = DateUtils.addMonths(new Date(), -1);
wrapper.gt(PrivateMessage::getId, minId);
wrapper.ge(PrivateMessage::getSendTime, minDate);
wrapper.and(wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).or()
@ -175,6 +175,36 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
});
}
@Override
public List<PrivateMessageVO> loadOfflineMessage(Long minId) {
UserSession session = SessionContext.getSession();
// 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
// 只能拉取最近1个月的消息
Date minDate = DateUtils.addMonths(new Date(), -1);
wrapper.gt(PrivateMessage::getId, minId);
wrapper.ge(PrivateMessage::getSendTime, minDate);
wrapper.and(wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).or()
.eq(PrivateMessage::getRecvId, session.getUserId()));
wrapper.orderByAsc(PrivateMessage::getId);
List<PrivateMessage> messages = this.list(wrapper);
// 更新消息为送达状态
List<Long> messageIds =
messages.stream().filter(m -> m.getStatus().equals(MessageStatus.PENDING.code())).map(PrivateMessage::getId)
.collect(Collectors.toList());
if (!messageIds.isEmpty()) {
LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.in(PrivateMessage::getId, messageIds);
updateWrapper.set(PrivateMessage::getStatus, MessageStatus.DELIVERED.code());
update(updateWrapper);
}
// 转换vo
List<PrivateMessageVO> vos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class))
.collect(Collectors.toList());
log.info("拉取私聊消息,用户id:{},数量:{},minId:{}", session.getUserId(), messages.size(), minId);
return vos;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void readedMessage(Long friendId) {

90
im-uniapp/App.vue

@ -11,7 +11,9 @@ export default {
return {
isExit: false, // 退
audioTip: null,
reconnecting: false //
reconnecting: false, //
privateMessagesBuffer: [],
groupMessagesBuffer: [],
}
},
methods: {
@ -36,8 +38,7 @@ export default {
this.onReconnectWs();
} else {
// 线
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
this.pullOfflineMessage();
this.configStore.setAppInit(true);
}
});
@ -50,11 +51,21 @@ export default {
})
this.exit();
} else if (cmd == 3) {
//
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.privateMessagesBuffer.push(msgInfo);
} else {
//
this.handlePrivateMessage(msgInfo);
}
} else if (cmd == 4) {
//
this.handleGroupMessage(msgInfo);
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.privateMessagesBuffer.push(msgInfo);
} else {
//
this.handlePrivateMessage(msgInfo);
}
} else if (cmd == 5) {
//
this.handleSystemMessage(msgInfo);
@ -84,30 +95,43 @@ export default {
this.configStore.clear();
this.userStore.clear();
},
pullPrivateOfflineMessage(minId) {
this.chatStore.setLoadingPrivateMsg(true)
http({
url: "/message/private/pullOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
uni.showToast({
title: "消息拉取失败,请重新登陆",
icon: 'none'
pullOfflineMessage() {
this.chatStore.setLoading(true);
const promises = [];
promises.push(this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId));
promises.push(this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId));
Promise.all(promises).then(messages => {
// 线
messages[0].forEach(m => this.handlePrivateMessage(m));
messages[1].forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer.forEach(m => this.handlePrivateMessage(m));
this.groupMessagesBuffer.forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer = [];
this.groupMessagesBuffer = [];
// 线
this.chatStore.setLoading(false);
//
this.chatStore.refreshChats();
}).catch((e) => {
console.log(e)
this.$message.error("拉取离线消息失败");
this.onExit();
})
this.exit()
},
pullPrivateOfflineMessage(minId) {
return this.$http({
url: "/message/private/loadOfflineMessage?minId=" + minId,
method: 'GET',
timeout: 300000
})
},
pullGroupOfflineMessage(minId) {
this.chatStore.setLoadingGroupMsg(true)
http({
url: "/message/group/pullOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
uni.showToast({
title: "消息拉取失败,请重新登陆",
icon: 'none'
})
this.exit()
return this.$http({
url: "/message/group/loadOfflineMessage?minId=" + minId,
method: 'GET',
timeout: 300000
})
},
handlePrivateMessage(msg) {
@ -120,11 +144,6 @@ export default {
type: 'PRIVATE',
targetId: friendId
}
//
if (msg.type == enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == enums.MESSAGE_TYPE.READED) {
this.chatStore.resetUnreadCount(chatInfo);
@ -204,7 +223,7 @@ export default {
this.chatStore.insertMessage(msg, chatInfo);
//
this.chatStore.insertMessage(msg, chatInfo);
if (!friend.isDnd && !this.chatStore.isLoading() &&
if (!friend.isDnd && !this.chatStore.loading &&
!msg.selfSend && msgType.isNormal(msg.type) &&
msg.status != enums.MESSAGE_STATUS.READED) {
this.playAudioTip();
@ -220,11 +239,6 @@ export default {
type: 'GROUP',
targetId: msg.groupId
}
//
if (msg.type == enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == enums.MESSAGE_TYPE.READED) {
//
@ -323,7 +337,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
if (!group.isDnd && !this.chatStore.isLoading() &&
if (!group.isDnd && !this.chatStore.loading &&
!msg.selfSend && msgType.isNormal(msg.type) &&
msg.status != enums.MESSAGE_STATUS.READED) {
this.playAudioTip();

1
im-uniapp/common/request.js

@ -17,6 +17,7 @@ const request = (options) => {
method: options.method || 'GET',
header: header,
data: options.data || {},
timeout: options.timeout || 3000,
async success(res) {
if (res.data.code == 200) {
return resolve(res.data.data)

2
im-uniapp/components/chat-item/chat-item.vue

@ -45,7 +45,7 @@ export default {
methods: {
showChatBox() {
//
if (!this.configStore.appInit || this.chatStore.isLoading()) {
if (!this.configStore.appInit || this.chatStore.loading) {
uni.showToast({
title: "正在初始化页面,请稍后...",
icon: 'none'

3
im-uniapp/pages/chat/chat-box.vue

@ -325,7 +325,6 @@ export default {
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();
this.sendMessageRequest(msgInfo).then((m) => {
//
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
@ -542,7 +541,7 @@ export default {
//
this.chatStore.deleteMessage(msgInfo, chat);
//
msgInfo.temId = this.generateId();
msgInfo.tmpId = this.generateId();
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();

2
im-uniapp/pages/chat/chat.vue

@ -111,7 +111,7 @@ export default {
return count;
},
loading() {
return this.chatStore.isLoading();
return this.chatStore.loading;
},
initializing() {
return !this.configStore.appInit;

63
im-uniapp/store/chatStore.js

@ -3,7 +3,6 @@ import { MESSAGE_TYPE, MESSAGE_STATUS } from '@/common/enums.js';
import useFriendStore from './friendStore.js';
import useGroupStore from './groupStore.js';
import useUserStore from './userStore';
import useConfigStore from './configStore.js';
let cacheChats = [];
export default defineStore('chatStore', {
@ -12,8 +11,7 @@ export default defineStore('chatStore', {
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loadingPrivateMsg: false,
loadingGroupMsg: false
loading: false
}
},
actions: {
@ -140,7 +138,7 @@ export default defineStore('chatStore', {
}
},
moveTop(idx) {
if (this.isLoading()) {
if (this.loading) {
return;
}
let chats = this.curChats;
@ -156,8 +154,6 @@ export default defineStore('chatStore', {
insertMessage(msgInfo, chatInfo) {
// 获取对方id或群id
let type = chatInfo.type;
// 完成初始化之前不能修改消息最大id,否则可能导致拉不到离线消息
if (useConfigStore().appInit) {
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
@ -165,11 +161,11 @@ export default defineStore('chatStore', {
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
}
// 如果是已存在消息,则覆盖旧的消息数据
let chat = this.findChat(chatInfo);
let message = this.findMessage(chat, msgInfo);
if (message) {
console.log("message:",message)
Object.assign(message, msgInfo);
chat.stored = false;
this.saveToStorage();
@ -218,24 +214,8 @@ export default defineStore('chatStore', {
});
chat.lastTimeTip = msgInfo.sendTime;
}
// 根据id顺序插入,防止消息乱序
let insertPos = chat.messages.length;
// 防止 图片、文件 在发送方 显示 在顶端 因为还没存库,id=0
if (msgInfo.id && msgInfo.id > 0) {
for (let idx in chat.messages) {
if (chat.messages[idx].id && msgInfo.id < chat.messages[idx].id) {
insertPos = idx;
console.log(`消息出现乱序,位置:${chat.messages.length},修正至:${insertPos}`);
break;
}
}
}
if (insertPos == chat.messages.length) {
// 这种赋值效率最高
chat.messages[insertPos] = msgInfo;
} else {
chat.messages.splice(insertPos, 0, msgInfo);
}
// 插入消息
chat.messages.push(msgInfo);
chat.stored = false;
this.saveToStorage();
},
@ -345,17 +325,8 @@ export default defineStore('chatStore', {
this.saveToStorage();
}
},
setLoadingPrivateMsg(loading) {
this.loadingPrivateMsg = loading;
if (!this.isLoading()) {
this.refreshChats()
}
},
setLoadingGroupMsg(loading) {
this.loadingGroupMsg = loading;
if (!this.isLoading()) {
this.refreshChats()
}
setLoading(loading) {
this.loading = loading;
},
setDnd(chatInfo, isDnd) {
let chat = this.findChat(chatInfo);
@ -406,7 +377,7 @@ export default defineStore('chatStore', {
},
saveToStorage(withColdMessage) {
// 加载中不保存,防止卡顿
if (this.isLoading()) {
if (this.loading) {
return;
}
const userStore = useUserStore();
@ -493,11 +464,8 @@ export default defineStore('chatStore', {
}
},
getters: {
isLoading: (state) => () => {
return state.loadingPrivateMsg || state.loadingGroupMsg
},
curChats: (state) => {
if (cacheChats && state.isLoading()) {
if (cacheChats && state.loading) {
return cacheChats;
}
return state.chats;
@ -529,7 +497,10 @@ export default defineStore('chatStore', {
if (!chat) {
return null;
}
for (let idx in chat.messages) {
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
if (!chat.messages[idx].id && !chat.messages[idx].tmpId) {
continue;
}
// 通过id判断
if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
return chat.messages[idx];
@ -539,7 +510,15 @@ export default defineStore('chatStore', {
chat.messages[idx].tmpId == msgInfo.tmpId) {
return chat.messages[idx];
}
// 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.id && msgInfo.id > chat.messages[idx].id) {
break;
}
if (msgInfo.tmpId && msgInfo.tmpId > chat.messages[idx].tmpId) {
break;
}
}
return null;
}
}
});

57
im-web/src/store/chatStore.js

@ -3,7 +3,6 @@ import { MESSAGE_TYPE, MESSAGE_STATUS } from "../api/enums.js"
import useFriendStore from './friendStore.js';
import useGroupStore from './groupStore.js';
import useUserStore from './userStore.js';
import useConfigStore from './configStore.js';
import localForage from 'localforage';
/**
@ -28,11 +27,10 @@ export default defineStore('chatStore', {
state: () => {
return {
activeChat: null,
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loadingPrivateMsg: false,
loadingGroupMsg: false,
chats: []
loading: false
}
},
actions: {
@ -141,7 +139,7 @@ export default defineStore('chatStore', {
},
moveTop(idx) {
// 加载中不移动,很耗性能
if (this.isLoading()) {
if (this.loading) {
return;
}
if (idx > 0) {
@ -156,8 +154,6 @@ export default defineStore('chatStore', {
},
insertMessage(msgInfo, chatInfo) {
let type = chatInfo.type;
// 完成初始化之前不能修改消息最大id,否则可能导致拉不到离线消息
if (useConfigStore().appInit) {
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
@ -165,7 +161,6 @@ export default defineStore('chatStore', {
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
}
// 如果是已存在消息,则覆盖旧的消息数据
let chat = this.findChat(chatInfo);
let message = this.findMessage(chat, msgInfo);
@ -218,19 +213,7 @@ export default defineStore('chatStore', {
});
chat.lastTimeTip = msgInfo.sendTime;
}
// 根据id顺序插入,防止消息乱序
let insertPos = chat.messages.length;
// 防止 图片、文件 在发送方 显示 在顶端 因为还没存库,id=0
if (msgInfo.id && msgInfo.id > 0) {
for (let idx in chat.messages) {
if (chat.messages[idx].id && msgInfo.id < chat.messages[idx].id) {
insertPos = idx;
console.log(`消息出现乱序,位置:${chat.messages.length},修正至:${insertPos}`);
break;
}
}
}
chat.messages.splice(insertPos, 0, msgInfo);
chat.messages.push(msgInfo);
chat.stored = false;
this.saveToStorage();
},
@ -340,17 +323,8 @@ export default defineStore('chatStore', {
this.saveToStorage()
}
},
setLoadingPrivateMsg(loading) {
this.loadingPrivateMsg = loading;
if (!this.isLoading()) {
this.refreshChats();
}
},
setLoadingGroupMsg(loading) {
this.loadingGroupMsg = loading;
if (!this.isLoading()) {
this.refreshChats();
}
setLoading(loading) {
this.loading = loading;
},
setDnd(chatInfo, isDnd) {
let chat = this.findChat(chatInfo);
@ -399,7 +373,7 @@ export default defineStore('chatStore', {
},
saveToStorage(withColdMessage) {
// 加载中不保存,防止卡顿
if (this.isLoading()) {
if (this.loading) {
return;
}
const userStore = useUserStore();
@ -456,6 +430,9 @@ export default defineStore('chatStore', {
cacheChats = []
this.chats = [];
this.activeChat = null;
this.privateMsgMaxId = 0;
this.groupMsgMaxId = 0;
this.loading = false;
},
loadChat() {
return new Promise((resolve, reject) => {
@ -511,7 +488,7 @@ export default defineStore('chatStore', {
return state.loadingPrivateMsg || state.loadingGroupMsg
},
findChats: (state) => () => {
if (cacheChats && state.isLoading()) {
if (cacheChats && state.loading) {
return cacheChats;
}
return state.chats;
@ -545,7 +522,10 @@ export default defineStore('chatStore', {
if (!chat) {
return null;
}
for (let idx in chat.messages) {
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
if (!chat.messages[idx].id && !chat.messages[idx].tmpId) {
continue;
}
// 通过id判断
if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
return chat.messages[idx];
@ -555,6 +535,13 @@ export default defineStore('chatStore', {
chat.messages[idx].tmpId == msgInfo.tmpId) {
return chat.messages[idx];
}
// 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.id && msgInfo.id > chat.messages[idx].id) {
break;
}
if (msgInfo.tmpId && msgInfo.tmpId > chat.messages[idx].tmpId) {
break;
}
}
}
}

2
im-web/src/view/Chat.vue

@ -89,7 +89,7 @@ export default {
},
computed: {
loading() {
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg
return this.chatStore.loading;
}
}
}

73
im-web/src/view/Home.vue

@ -78,7 +78,9 @@ export default {
showSettingDialog: false,
lastPlayAudioTime: new Date().getTime() - 1000,
isFullscreen: true,
reconnecting: false
reconnecting: false,
privateMessagesBuffer: [],
groupMessagesBuffer: []
}
},
methods: {
@ -108,8 +110,7 @@ export default {
this.onReconnectWs();
} else {
// 线
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
this.pullOfflineMessage();
this.configStore.setAppInit(true);
}
});
@ -126,11 +127,21 @@ export default {
});
} else if (cmd == 3) {
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.privateMessagesBuffer.push(msgInfo);
} else {
//
this.handlePrivateMessage(msgInfo);
}
} else if (cmd == 4) {
if (!this.configStore.appInit || this.chatStore.loading) {
// 线
this.groupMessagesBuffer.push(msgInfo);
} else {
//
this.handleGroupMessage(msgInfo);
}
} else if (cmd == 5) {
//
this.handleSystemMessage(msgInfo);
@ -170,8 +181,7 @@ export default {
promises.push(this.groupStore.loadGroup());
Promise.all(promises).then(() => {
// 线
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId);
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId);
this.pullOfflineMessage();
this.configStore.setAppInit(true)
this.$message.success("重新连接成功");
}).catch(() => {
@ -194,23 +204,42 @@ export default {
this.groupStore.clear();
this.chatStore.clear();
this.userStore.clear();
},
pullOfflineMessage() {
this.chatStore.setLoading(true);
const promises = [];
promises.push(this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId));
promises.push(this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId));
Promise.all(promises).then(messages => {
// 线
messages[0].forEach(m => this.handlePrivateMessage(m));
messages[1].forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer.forEach(m => this.handlePrivateMessage(m));
this.groupMessagesBuffer.forEach(m => this.handleGroupMessage(m));
//
this.privateMessagesBuffer = [];
this.groupMessagesBuffer = [];
// 线
this.chatStore.setLoading(false);
//
this.chatStore.refreshChats();
}).catch((e) => {
console.log(e)
this.$message.error("拉取离线消息失败");
this.onExit();
})
},
pullPrivateOfflineMessage(minId) {
this.chatStore.setLoadingPrivateMsg(true)
this.$http({
url: "/message/private/pullOfflineMessage?minId=" + minId,
return this.$http({
url: "/message/private/loadOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
this.chatStore.setLoadingPrivateMsg(false)
})
},
pullGroupOfflineMessage(minId) {
this.chatStore.setLoadingGroupMsg(true)
this.$http({
url: "/message/group/pullOfflineMessage?minId=" + minId,
return this.$http({
url: "/message/group/loadOfflineMessage?minId=" + minId,
method: 'GET'
}).catch(() => {
this.chatStore.setLoadingGroupMsg(false)
})
},
handlePrivateMessage(msg) {
@ -223,11 +252,6 @@ export default {
type: 'PRIVATE',
targetId: friendId
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.READED) {
this.chatStore.resetUnreadCount(chatInfo)
@ -285,7 +309,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
if (!friend.isDnd && !this.chatStore.isLoading() && !msg.selfSend && this.$msgType.isNormal(msg.type) &&
if (!friend.isDnd && !this.chatStore.loading && !msg.selfSend && this.$msgType.isNormal(msg.type) &&
msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip();
}
@ -297,11 +321,6 @@ export default {
type: 'GROUP',
targetId: msg.groupId
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.LOADING) {
this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content))
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.READED) {
//
@ -367,7 +386,7 @@ export default {
//
this.chatStore.insertMessage(msg, chatInfo);
//
if (!group.isDnd && !this.chatStore.isLoading() &&
if (!group.isDnd && !this.chatStore.loading &&
!msg.selfSend && this.$msgType.isNormal(msg.type)
&& msg.status != this.$enums.MESSAGE_STATUS.READED) {
this.playAudioTip();

Loading…
Cancel
Save