Browse Source

修改转接客服功能

master
[yxf] 1 month ago
parent
commit
b64791bad8
  1. 3
      im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java
  2. 6
      im-platform/src/main/java/com/bx/implatform/entity/User.java
  3. 4
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  4. 141
      im-uniapp/pages/chat/chat-box.vue
  5. 39
      im-uniapp/pages/login/login.vue
  6. 488
      im-uniapp/store/chatStore.js

3
im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java

@ -28,4 +28,7 @@ public class LoginDTO {
@Schema(description = "ip地址")
private String ip;
@Schema(description = "来源网址")
private String sourceUrl;
}

6
im-platform/src/main/java/com/bx/implatform/entity/User.java

@ -104,4 +104,10 @@ public class User {
*/
private String ipAddress;
/**
* 来源网址
*/
private String sourceUrl;
}

4
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -93,7 +93,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
@Override
public LoginVO login(LoginDTO dto) {
log.info("【测试】前端传的IP:{}", dto.getIp());
log.info("【测试】前端传的IP:{}", dto.getSourceUrl());
// 生成游客唯一标识UUID
String guestUuid = UUID.randomUUID().toString();
@ -108,7 +108,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
guestUser.setUuid(guestUuid);
// ========== 先设置IP ==========
guestUser.setLastLoginIp(dto.getIp());
guestUser.setSourceUrl(dto.getSourceUrl());
// 保存到数据库
this.save(guestUser);

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

@ -135,6 +135,8 @@ export default {
chatTabBox: 'none',
currentTargetId: null, //
currentChatType: 'PRIVATE',
isLoading: true, //
defaultTitle: '加载中...', //
// activeChatIdx: 0,
_activeChatIdx: 0, //
showRecord: false,
@ -295,24 +297,21 @@ export default {
sendTextMessage() {
this.editorCtx.getContents({
success: (e) => {
//
this.editorCtx.clear();
//
if (this.isBanned) {
this.showBannedTip();
return;
}
let sendText = "";
e.delta.ops.forEach((op) => {
if (op.insert.image) {
// emo
sendText += `#${op.attributes.alt};`
} else{
//
} else {
sendText += op.insert
}
})
//
sendText = sendText.trim();
if (!sendText && this.atUserIds.length == 0) {
return uni.showToast({
@ -320,6 +319,7 @@ export default {
icon: "none"
});
}
let receiptText = this.isReceipt ? "【回执消息】" : "";
let atText = this.createAtText();
let msgInfo = {
@ -329,35 +329,72 @@ export default {
receipt: this.isReceipt,
type: this.$enums.MESSAGE_TYPE.TEXT
}
// @
this.atUserIds = [];
this.isReceipt = false;
// id
this.fillTargetId(msgInfo, this.chat.targetId);
//
const chat = this.chat;
// const chat = this.chatStore.chats[this.activeChatIdx];
if (!chat) return;
//
//
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();
//
const tmpMessageCopy = JSON.parse(JSON.stringify(tmpMessage));
// store
this.chatStore.insertMessage(tmpMessageCopy, chat);
// 使 chat
const chatIndex = this.chatStore.chats.findIndex(c =>
c.targetId === chat.targetId && c.type === chat.type
);
if (chatIndex !== -1) {
const targetChat = this.chatStore.chats[chatIndex];
const msgIndex = targetChat.messages.findIndex(m => m.tmpId === msgInfo.tmpId);
if (msgIndex !== -1) {
//
this.$nextTick(() => {
this.scrollToBottom();
});
//
this.sendMessageRequest(msgInfo).then((m) => {
//
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.id = m.id;
tmpMessage.status = m.status;
tmpMessage.content = m.content;
this.chatStore.updateMessage(tmpMessage, chat);
// - 使
const updatedMessage = {
...targetChat.messages[msgIndex],
id: m.id,
status: m.status,
content: m.content
};
// 使 Vue.set
this.$set(targetChat.messages, msgIndex, updatedMessage);
// chat
this.$forceUpdate();
}).catch(() => {
//
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.updateMessage(tmpMessage, chat);
})
//
const failedMessage = {
...targetChat.messages[msgIndex],
status: this.$enums.MESSAGE_STATUS.FAILED
};
this.$set(targetChat.messages, msgIndex, failedMessage);
this.$forceUpdate();
uni.showToast({
title: "发送失败",
icon: "none"
});
});
}
}
}
})
},
createAtText() {
let atText = "";
@ -1012,6 +1049,21 @@ export default {
}
},
computed: {
title() {
//
if (this.isLoading) {
return this.defaultTitle;
}
if (!this.chat) {
return "";
}
let title = this.chat.showName;
if (this.isGroup) {
let size = this.groupMembers.filter(m => !m.quit).length;
title += `(${size})`;
}
return title;
},
chat() {
if (!this.currentTargetId) return null;
return this.chatStore.chats.find(c =>
@ -1105,16 +1157,38 @@ export default {
}
},
watch: {
chat: {
handler(newChat, oldChat) {
if (newChat && newChat.messages && newChat.messages.length > 0) {
//
if (this.isInBottom) {
this.$nextTick(() => {
this.scrollToBottom();
});
}
}
},
deep: true,
immediate: true
},
//
'chat.messages.length': function(newLength, oldLength) {
if (newLength > oldLength && this.isInBottom) {
this.$nextTick(() => {
this.scrollToBottom();
});
}
},
// messageSize watch
messageSize: function(newSize, oldSize) {
//
if (newSize > oldSize && oldSize > 0) {
let lastMessage = this.chat.messages[newSize - 1];
if (this.$msgType.isNormal(lastMessage.type)) {
if (this.isInBottom || lastMessage.selfSend) {
// ,
this.scrollToBottom();
} else {
//
this.newMessageSize++;
}
}
@ -1179,8 +1253,17 @@ export default {
await this.$nextTick();
//
// chat
if (!this.chat) {
console.error('Chat 初始化失败');
uni.hideLoading();
return;
}
// chat
this.readedMessage();
//
this.loadFriend(targetId);
this.loadReaded(targetId);

39
im-uniapp/pages/login/login.vue

@ -18,11 +18,41 @@ export default {
terminal: 1,
userName: '',
password: '',
ip: ''
ip: '',
sourceUrl: ''
}
}
},
methods: {
//
getSourceUrl() {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const options = currentPage.options || {};
if (options.from) {
this.dataForm.sourceUrl = options.from;
}
// #ifdef H5
this.dataForm.sourceUrl = window.location.origin;
// #endif
// #ifdef APP-PLUS
this.dataForm.sourceUrl = 'app';
// #endif
// #ifdef MP-WEIXIN
this.dataForm.sourceUrl = 'wechat-miniprogram';
// #endif
//
if (!this.dataForm.sourceUrl) {
this.dataForm.sourceUrl = 'unknown';
}
console.log("来源网址:", this.dataForm.sourceUrl);
},
async autoLogin() {
if (GLOBAL_AUTO_LOGIN_LOCK) return;
GLOBAL_AUTO_LOGIN_LOCK = true;
@ -45,7 +75,8 @@ export default {
// }
// return;
// }
//
this.getSourceUrl();
await this.getIp();
this.$http({
@ -70,8 +101,10 @@ export default {
delete: false
}
this.friendStore.addFriend(friend);
// ID
uni.reLaunch({
url: "/pages/chat/chat-box?chatIdx=0"
url: `/pages/chat/chat-box?targetId=${loginInfo.customerServiceId}&type=PRIVATE`
});
})

488
im-uniapp/store/chatStore.js

@ -6,19 +6,23 @@ import useUserStore from './userStore';
let cacheChats = [];
export default defineStore('chatStore', {
state: () => ({
state: () => {
return {
chats: [],
privateMsgMaxId: 0,
groupMsgMaxId: 0,
loading: false
}),
}
},
actions: {
initChats(chatsData) {
cacheChats = [];
this.chats = [];
for (let chat of chatsData.chats) {
chat.stored = false;
// 暂存至缓冲区
cacheChats.push(JSON.parse(JSON.stringify(chat)));
// 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间
if (this.chats.length < 15) {
this.chats.push(chat);
}
@ -27,15 +31,18 @@ export default defineStore('chatStore', {
this.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
},
openChat(chatInfo) {
let chats = this.chats;
let chats = this.curChats;
let chat = null;
for (let idx in chats) {
if (chats[idx].type == chatInfo.type && chats[idx].targetId === chatInfo.targetId) {
if (chats[idx].type == chatInfo.type &&
chats[idx].targetId === chatInfo.targetId) {
chat = chats[idx];
this.moveTop(idx);
// 放置头部
this.moveTop(idx)
break;
}
}
// 创建会话
if (chat == null) {
chat = {
targetId: chatInfo.targetId,
@ -58,265 +65,323 @@ export default defineStore('chatStore', {
}
},
activeChat(idx) {
let chats = this.curChats;
if (idx >= 0) {
this.chats[idx] = { ...this.chats[idx], unreadCount: 0 };
chats[idx].unreadCount = 0;
}
},
resetUnreadCount(chatInfo) {
this.chats = this.chats.map(chat => {
if (chat.type == chatInfo.type && chat.targetId == chatInfo.targetId) {
return { ...chat, unreadCount: 0, atMe: false, atAll: false, stored: false };
}
return chat;
});
let chats = this.curChats;
for (let idx in chats) {
if (chats[idx].type == chatInfo.type &&
chats[idx].targetId == chatInfo.targetId) {
chats[idx].unreadCount = 0;
chats[idx].atMe = false;
chats[idx].atAll = false;
chats[idx].stored = false;
this.saveToStorage();
}
}
},
readedMessage(pos) {
let chat = this.findChatByFriend(pos.friendId);
if (!chat) return;
let messages = [...chat.messages];
let changed = false;
for (let idx = chat.readedMessageIdx; idx < messages.length; idx++) {
let m = messages[idx];
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) {
messages[idx] = { ...m, status: MESSAGE_STATUS.READED };
m.status = MESSAGE_STATUS.READED
chat.readedMessageIdx = idx;
changed = true;
chat.stored = false;
}
}
}
if (changed) {
chat.messages = messages;
chat.stored = false;
if (!chat.stored) {
this.saveToStorage();
}
},
cleanMessage(idx) {
let chat = { ...this.chats[idx] };
let chat = this.curChats[idx];
chat.lastContent = '';
chat.hotMinIdx = 0;
chat.unreadCount = 0;
chat.atMe = false;
chat.atAll = false;
chat.stored = false;
chat.stored = false
chat.messages = [];
this.chats[idx] = chat;
this.saveToStorage(true);
},
removeChat(idx) {
this.chats[idx] = { ...this.chats[idx], delete: true, stored: false };
let chats = this.curChats;
chats[idx].delete = true;
chats[idx].stored = false;
this.saveToStorage();
},
removePrivateChat(userId) {
this.chats = this.chats.map(chat => {
if (chat.type == 'PRIVATE' && chat.targetId == userId) {
return { ...chat, delete: true, stored: false };
let chats = this.curChats;
for (let idx in chats) {
if (chats[idx].type == 'PRIVATE' &&
chats[idx].targetId == userId) {
this.removeChat(idx);
}
}
return chat;
});
this.saveToStorage();
},
removeGroupChat(groupId) {
this.chats = this.chats.map(chat => {
if (chat.type == 'GROUP' && chat.targetId == groupId) {
return { ...chat, delete: true, stored: false };
let chats = this.curChats;
for (let idx in chats) {
if (chats[idx].type == 'GROUP' &&
chats[idx].targetId == groupId) {
this.removeChat(idx);
}
}
return chat;
});
this.saveToStorage();
},
moveTop(idx) {
if (this.loading) return;
if (this.loading) {
return;
}
let chats = this.curChats;
if (idx > 0) {
let chats = [...this.chats];
let chat = chats.splice(idx, 1)[0];
chat = { ...chat, lastSendTime: new Date().getTime(), stored: false };
let chat = chats[idx];
chats.splice(idx, 1);
chats.unshift(chat);
this.chats = chats;
chat.lastSendTime = new Date().getTime();
chat.stored = false;
this.saveToStorage();
}
},
insertMessage(msgInfo, chatInfo) {
// 获取对方id或群id
let type = chatInfo.type;
// 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
this.privateMsgMaxId = msgInfo.id;
}
if (msgInfo.id && type == "GROUP" && msgInfo.id > this.groupMsgMaxId) {
this.groupMsgMaxId = msgInfo.id;
}
// 如果是已存在消息,则覆盖旧的消息数据
let chat = this.findChat(chatInfo);
if (!chat) return;
let message = this.findMessage(chat, msgInfo);
if (message) {
chat.messages = chat.messages.map(m =>
(m.id === msgInfo.id || m.tmpId === msgInfo.tmpId) ? { ...m, ...msgInfo } : m
);
console.log("message:", message)
Object.assign(message, msgInfo);
chat.stored = false;
this.saveToStorage();
return;
}
chat = { ...chat };
if (msgInfo.type == MESSAGE_TYPE.IMAGE) chat.lastContent = "[图片]";
else if (msgInfo.type == MESSAGE_TYPE.FILE) chat.lastContent = "[文件]";
else if (msgInfo.type == MESSAGE_TYPE.AUDIO) chat.lastContent = "[语音]";
else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) chat.lastContent = "[语音通话]";
else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) chat.lastContent = "[视频通话]";
else if (msgInfo.type == MESSAGE_TYPE.TEXT || msgInfo.type == MESSAGE_TYPE.RECALL || msgInfo.type == MESSAGE_TYPE.TIP_TEXT) {
// 会话列表内容
if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
chat.lastContent = "[图片]";
} else if (msgInfo.type == MESSAGE_TYPE.FILE) {
chat.lastContent = "[文件]";
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = "[语音]";
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) {
chat.lastContent = "[语音通话]";
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) {
chat.lastContent = "[视频通话]";
} else if (msgInfo.type == MESSAGE_TYPE.TEXT ||
msgInfo.type == MESSAGE_TYPE.RECALL ||
msgInfo.type == MESSAGE_TYPE.TIP_TEXT) {
chat.lastContent = msgInfo.content;
}
chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = msgInfo.sendNickName;
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED && msgInfo.status != MESSAGE_STATUS.RECALL && msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
// 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
msgInfo.status != MESSAGE_STATUS.RECALL && msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
chat.unreadCount++;
}
if (!msgInfo.selfSend && chat.type == "GROUP" && msgInfo.atUserIds && msgInfo.status != MESSAGE_STATUS.READED) {
// 是否有人@我
if (!msgInfo.selfSend && chat.type == "GROUP" && msgInfo.atUserIds &&
msgInfo.status != MESSAGE_STATUS.READED) {
const userStore = useUserStore();
let userId = userStore.userInfo.id;
if (msgInfo.atUserIds.indexOf(userId) >= 0) chat.atMe = true;
if (msgInfo.atUserIds.indexOf(-1) >= 0) chat.atAll = true;
if (msgInfo.atUserIds.indexOf(userId) >= 0) {
chat.atMe = true;
}
let messages = [...chat.messages];
if (!chat.lastTimeTip || chat.lastTimeTip < msgInfo.sendTime - 600 * 1000) {
messages.push({ sendTime: msgInfo.sendTime, type: MESSAGE_TYPE.TIP_TIME });
if (msgInfo.atUserIds.indexOf(-1) >= 0) {
chat.atAll = true;
}
}
// 间隔大于10分钟插入时间显示
if (!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600 * 1000)) {
chat.messages.push({
sendTime: msgInfo.sendTime,
type: MESSAGE_TYPE.TIP_TIME,
});
chat.lastTimeTip = msgInfo.sendTime;
}
messages.push(msgInfo);
chat.messages = messages;
// 插入消息
chat.messages.push(msgInfo);
chat.stored = false;
this.chats = this.chats.map(c => c.type === chat.type && c.targetId === chat.targetId ? chat : c);
this.saveToStorage();
},
updateMessage(msgInfo, chatInfo) {
// 获取对方id或群id
let chat = this.findChat(chatInfo);
if (!chat) return;
this.chats = this.chats.map(c => {
if (c.type === chat.type && c.targetId === chat.targetId) {
return {
...c,
messages: c.messages.map(m =>
(m.id && m.id === msgInfo.id) || (m.tmpId && m.tmpId === msgInfo.tmpId)
? { ...m, ...msgInfo }
: m
),
stored: false
};
}
return c;
});
let message = this.findMessage(chat, msgInfo);
if (message) {
// 属性拷贝
Object.assign(message, msgInfo);
chat.stored = false;
this.saveToStorage();
}
},
deleteMessage(msgInfo, chatInfo) {
let isColdMessage = false;
let chat = this.findChat(chatInfo);
if (!chat) return;
this.chats = this.chats.map(c => {
if (c.type === chat.type && c.targetId === chat.targetId) {
let messages = c.messages.filter(m =>
!(m.id && m.id === msgInfo.id) && !(m.tmpId && m.tmpId === msgInfo.tmpId)
);
return { ...c, messages, stored: false };
}
return c;
});
this.saveToStorage();
let delIdx = -1;
for (let idx in chat.messages) {
// 已经发送成功的,根据id删除
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
delIdx = idx;
break;
}
// 正在发送中的消息可能没有id,只有临时id
if (chat.messages[idx].tmpId && chat.messages[idx].tmpId == msgInfo.tmpId) {
delIdx = idx;
break;
}
}
if (delIdx >= 0) {
chat.messages.splice(delIdx, 1);
if (delIdx < chat.hotMinIdx) {
isColdMessage = true;
chat.hotMinIdx--;
}
if (delIdx < chat.readedMessageIdx) {
chat.readedMessageIdx--;
}
chat.stored = false;
this.saveToStorage(isColdMessage);
}
},
recallMessage(msgInfo, chatInfo) {
let chat = this.findChat(chatInfo);
if (!chat) return;
let isColdMessage = false;
// 要撤回的消息id
let id = msgInfo.content;
let name = msgInfo.selfSend ? '你' : chat.type == 'PRIVATE' ? '对方' : msgInfo.sendNickName;
this.chats = this.chats.map(c => {
if (c.type === chat.type && c.targetId === chat.targetId) {
let messages = c.messages.map(m => {
for (let idx in chat.messages) {
let m = chat.messages[idx];
if (m.id && m.id == id) {
return {
...m,
status: MESSAGE_STATUS.RECALL,
content: name + "撤回了一条消息",
type: MESSAGE_TYPE.TIP_TEXT
};
// 改造成一条提示消息
m.status = MESSAGE_STATUS.RECALL;
m.content = name + "撤回了一条消息";
m.type = MESSAGE_TYPE.TIP_TEXT
// 会话列表
chat.lastContent = m.content;
chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = '';
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED) {
chat.unreadCount++;
}
isColdMessage = idx < chat.hotMinIdx;
}
// 被引用的消息也要撤回
if (m.quoteMessage && m.quoteMessage.id == msgInfo.id) {
return {
...m,
quoteMessage: { ...m.quoteMessage, content: "引用内容已撤回", status: MESSAGE_STATUS.RECALL, type: MESSAGE_TYPE.TIP_TEXT }
};
m.quoteMessage.content = "引用内容已撤回";
m.quoteMessage.status = MESSAGE_STATUS.RECALL;
m.quoteMessage.type = MESSAGE_TYPE.TIP_TEXT
}
return m;
});
return { ...c, messages, stored: false };
}
return c;
});
this.saveToStorage();
chat.stored = false;
this.saveToStorage(isColdMessage);
},
updateChatFromFriend(friend) {
this.chats = this.chats.map(chat => {
if (chat.type === 'PRIVATE' && chat.targetId === friend.id) {
return { ...chat, headImage: friend.headImage, showName: friend.nickName, stored: false };
}
return chat;
});
let chat = this.findChatByFriend(friend.id)
if (chat && (chat.headImage != friend.headImage ||
chat.showName != friend.nickName)) {
// 更新会话中的群名和头像
chat.headImage = friend.headImage;
chat.showName = friend.nickName;
chat.stored = false;
this.saveToStorage();
}
},
updateChatFromUser(user) {
this.chats = this.chats.map(chat => {
if (chat.type === 'PRIVATE' && chat.targetId === user.id) {
return { ...chat, headImage: user.headImageThumb, showName: user.nickName, stored: false };
}
return chat;
});
let chat = this.findChatByFriend(user.id);
// 更新会话中的昵称和头像
if (chat && (chat.headImage != user.headImageThumb ||
chat.showName != user.nickName)) {
chat.headImage = user.headImageThumb;
chat.showName = user.nickName;
chat.stored = false;
this.saveToStorage();
}
},
updateChatFromGroup(group) {
this.chats = this.chats.map(chat => {
if (chat.type === 'GROUP' && chat.targetId === group.id) {
return { ...chat, headImage: group.headImageThumb, showName: group.showGroupName, stored: false };
}
return chat;
});
let chat = this.findChatByGroup(group.id);
if (chat && (chat.headImage != group.headImageThumb ||
chat.showName != group.showGroupName)) {
// 更新会话中的群名称和头像
chat.headImage = group.headImageThumb;
chat.showName = group.showGroupName;
chat.stored = false;
this.saveToStorage();
}
},
setLoading(loading) {
this.loading = loading;
},
setDnd(chatInfo, isDnd) {
this.chats = this.chats.map(chat => {
if (chat.type === chatInfo.type && chat.targetId === chatInfo.targetId) {
return { ...chat, isDnd };
let chat = this.findChat(chatInfo);
if (chat) {
chat.isDnd = isDnd;
}
return chat;
});
},
refreshChats() {
let chats = cacheChats || this.chats;
// 更新会话免打扰状态
const friendStore = useFriendStore();
const groupStore = useGroupStore();
chats.forEach(chat => {
if (chat.type == 'PRIVATE') {
let friend = friendStore.findFriend(chat.targetId);
if (friend) chat.isDnd = friend.isDnd;
if (friend) {
chat.isDnd = friend.isDnd
}
} else if (chat.type == 'GROUP') {
let group = groupStore.findGroup(chat.targetId);
if (group) chat.isDnd = group.isDnd;
if (group) {
chat.isDnd = group.isDnd
}
});
}
})
// 排序
chats.sort((chat1, chat2) => chat2.lastSendTime - chat1.lastSendTime);
// #ifndef APP-PLUS
// h5和小程序的stroge一般只有5m,大约只能存储1w条消息,所以可能需要清理部分历史消息
const storageInfo = uni.getStorageInfoSync();
console.log(`storage缓存: ${storageInfo.currentSize} KB`)
// 空间不足(大于3mb)时,清理这个设备登录过其他账户的消息
if (storageInfo && storageInfo.currentSize > 3000) {
console.log("storage空间不足,清理其他用户缓存..")
this.cleanOtherUserCache();
}
// 保证消息总数量不超过3000条,每个会话不超过500条
this.fliterMessage(chats, 3000, 500);
// #endif
// 记录热数据索引位置
chats.forEach(chat => {
if (!chat.hotMinIdx || chat.hotMinIdx != chat.messages.length) {
chat.hotMinIdx = chat.messages.length;
chat.stored = false;
}
});
// 将消息一次性装载回来
this.chats = chats;
// 清空缓存,不再使用
cacheChats = null;
// 消息持久化
this.saveToStorage(true);
},
fliterMessage(chats, maxTotalSize, maxPerChatSize) {
// 每个会话只保留maxPerChatSize条消息
let remainTotalSize = 0;
chats.forEach(chat => {
if (chat.messages.length > maxPerChatSize) {
@ -324,10 +389,13 @@ export default defineStore('chatStore', {
chat.messages = chat.messages.slice(idx);
}
remainTotalSize += chat.messages.length;
});
})
// 保证消息总数不超过maxTotalSize条,否则继续清理
if (remainTotalSize > maxTotalSize) {
this.fliterMessage(chats, maxTotalSize, maxPerChatSize / 2);
}
console.log("消息留存总数量:", remainTotalSize)
console.log("单会话消息数量:", maxPerChatSize)
},
cleanOtherUserCache() {
const userStore = useUserStore();
@ -335,101 +403,163 @@ export default defineStore('chatStore', {
const prefix = "chats-app-" + userId;
const res = uni.getStorageInfoSync();
res.keys.forEach(key => {
// 清理其他用户的消息
if (key.startsWith("chats-app") && !key.startsWith(prefix)) {
uni.removeStorageSync(key);
console.log("清理key:", key)
}
});
})
},
saveToStorage(withColdMessage) {
if (this.loading) return;
// 加载中不保存,防止卡顿
if (this.loading) {
return;
}
const userStore = useUserStore();
let userId = userStore.userInfo.id;
let key = "chats-app-" + userId;
let chatKeys = [];
// 按会话为单位存储,只存储有改动的会话
this.chats.forEach((chat) => {
let chatKey = `${key}-${chat.type}-${chat.targetId}`;
let chatKey = `${key}-${chat.type}-${chat.targetId}`
if (!chat.stored) {
if (chat.delete) {
uni.removeStorageSync(chatKey);
} else {
// 存储冷数据
if (withColdMessage) {
let coldChat = { ...chat };
let coldChat = Object.assign({}, chat);
coldChat.messages = chat.messages.slice(0, chat.hotMinIdx);
uni.setStorageSync(chatKey, coldChat);
uni.setStorageSync(chatKey, coldChat)
}
// 存储热消息
let hotKey = chatKey + '-hot';
let hotChat = { ...chat };
hotChat.messages = chat.messages.slice(chat.hotMinIdx);
let hotChat = Object.assign({}, chat);
hotChat.messages = chat.messages.slice(chat.hotMinIdx)
uni.setStorageSync(hotKey, hotChat);
}
chat.stored = true;
}
if (!chat.delete) chatKeys.push(chatKey);
});
if (!chat.delete) {
chatKeys.push(chatKey);
}
})
// 会话核心信息
let chatsData = {
privateMsgMaxId: this.privateMsgMaxId,
groupMsgMaxId: this.groupMsgMaxId,
chatKeys: chatKeys
};
uni.setStorageSync(key, chatsData);
this.chats = this.chats.filter(chat => !chat.delete);
}
uni.setStorageSync(key, chatsData)
// 清理已删除的会话
this.chats = this.chats.filter(chat => !chat.delete)
},
clear() {
clear(state) {
cacheChats = [];
this.chats = [];
this.privateMsgMaxId = 0;
this.groupMsgMaxId = 0;
this.loadingPrivateMsg = false;
this.loadingGroupMsg = false;
},
loadChat() {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
let userStore = useUserStore();
let userId = userStore.userInfo.id;
let chatsData = uni.getStorageSync("chats-app-" + userId);
if (chatsData && chatsData.chatKeys) {
let chatsData = uni.getStorageSync("chats-app-" + userId)
if (chatsData) {
if (chatsData.chatKeys) {
chatsData.chats = [];
chatsData.chatKeys.forEach(key => {
let coldChat = uni.getStorageSync(key);
let hotChat = uni.getStorageSync(key + '-hot');
if (!coldChat && !hotChat) return;
if (hotChat) {
hotChat.messages.forEach(msg => {
if (msg.status == MESSAGE_STATUS.SENDING) msg.status = MESSAGE_STATUS.FAILED;
});
if (!coldChat && !hotChat) {
return;
}
// 防止消息一直处在发送中状态
hotChat && hotChat.messages.forEach(msg => {
if (msg.status == MESSAGE_STATUS.SENDING) {
msg.status = MESSAGE_STATUS.FAILED
}
let chat = { ...coldChat, ...hotChat };
})
// 冷热消息合并
let chat = Object.assign({}, coldChat, hotChat);
if (hotChat && coldChat) {
chat.messages = [...(coldChat.messages || []), ...(hotChat.messages || [])];
chat.messages = coldChat.messages.concat(hotChat.messages)
}
// 历史版本没有readedMessageIdx字段,做兼容一下
chat.readedMessageIdx = chat.readedMessageIdx || 0;
chatsData.chats.push(chat);
});
})
}
this.initChats(chatsData);
}
resolve();
});
resolve()
})
}
},
getters: {
curChats: (state) => state.chats, // ✅ 修复
curChats: (state) => {
if (cacheChats && state.loading) {
return cacheChats;
}
return state.chats;
},
findChatIdx: (state) => (chat) => {
return state.chats.findIndex(c => c.type == chat.type && c.targetId === chat.targetId);
let chats = state.curChats;
for (let idx in chats) {
if (chats[idx].type == chat.type &&
chats[idx].targetId === chat.targetId) {
chat = state.chats[idx];
return idx;
}
}
},
findChat: (state) => (chat) => {
return state.chats.find(c => c.type == chat.type && c.targetId === chat.targetId);
let chats = state.curChats;
let idx = state.findChatIdx(chat);
return chats[idx];
},
findChatByFriend: (state) => (fid) => {
return state.chats.find(c => c.type == 'PRIVATE' && c.targetId == fid);
return state.curChats.find(chat => chat.type == 'PRIVATE' &&
chat.targetId == fid)
},
findChatByGroup: (state) => (gid) => {
return state.chats.find(c => c.type == 'GROUP' && c.targetId == gid);
return state.curChats.find(chat => chat.type == 'GROUP' &&
chat.targetId == gid)
},
findMessage: (state) => (chat, msgInfo) => {
if (!chat) return null;
if (!chat) {
return null;
}
// 通过id判断
if (msgInfo.id) {
return chat.messages.find(m => m.id === msgInfo.id);
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
let m = chat.messages[idx];
if (m.id && msgInfo.id == m.id) {
return m;
}
// 如果id比要查询的消息小,说明没有这条消息
if (m.id && m.id < msgInfo.id) {
break;
}
}
}
// 正在发送中的临时消息可能没有id,只有tmpId
if (msgInfo.selfSend && msgInfo.tmpId) {
return chat.messages.find(m => m.tmpId === msgInfo.tmpId);
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
let m = chat.messages[idx];
if (!m.selfSend || !m.tmpId) {
continue;
}
if (msgInfo.tmpId == m.tmpId) {
return m;
}
// 如果id比要查询的消息小,说明没有这条消息
if (m.tmpId && m.tmpId < msgInfo.tmpId) {
break;
}
}
}
return null;
}

Loading…
Cancel
Save