You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

623 lines
19 KiB

import { defineStore } from 'pinia';
import { MESSAGE_TYPE, MESSAGE_STATUS } from '@/common/enums.js';
import useFriendStore from './friendStore.js';
import useGroupStore from './groupStore.js';
import useUserStore from './userStore';
import { i18n } from '@/main.js'
let cacheChats = [];
export default defineStore('chatStore', {
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)));
if (this.chats.length < 15) {
this.chats.push(chat);
}
}
this.privateMsgMaxId = chatsData.privateMsgMaxId || 0;
this.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
},
// 未知客服触发:全量合并所有旧客服到新客服
forceMergeAllOldCustomerToNew(newKfId) {
const userStore = useUserStore();
const uid = userStore.userInfo.id;
const prefix = `chats-app-${uid}-PRIVATE-`;
const rootKey = `chats-app-${uid}`;
const allKeys = uni.getStorageInfoSync().keys;
const oldKeys = allKeys.filter(k =>
k.startsWith(prefix) &&
!k.endsWith('-hot') &&
!k.includes(`-${newKfId}`)
);
if (oldKeys.length === 0) return;
let newChat = this.findChatByFriend(newKfId);
if (!newChat) {
newChat = {
targetId: newKfId,
type: "PRIVATE",
messages: [],
unreadCount: 0,
lastContent: "",
lastSendTime: 0,
hotMinIdx: 0,
readedMessageIdx: 0,
atMe: false,
atAll: false,
stored: false
};
this.chats.unshift(newChat);
}
// 读取每一个旧客服 【冷+热完整消息】
let allOldMessages = [];
oldKeys.forEach(oldKey => {
const oldCold = uni.getStorageSync(oldKey);
const oldHot = uni.getStorageSync(oldKey + "-hot");
// 旧客服完整消息:冷历史 + 热新消息
const oldFullMsg = [...(oldCold?.messages || []), ...(oldHot?.messages || [])];
allOldMessages = [...oldFullMsg, ...allOldMessages];
// 合并完成后才删除旧缓存,绝不提前删!
uni.removeStorageSync(oldKey);
uni.removeStorageSync(oldKey + "-hot");
});
newChat.messages = [...allOldMessages, ...newChat.messages];
// 根缓存只保留最新客服
const root = uni.getStorageSync(rootKey);
if (root) {
root.chatKeys = [`${prefix}${newKfId}`];
uni.setStorageSync(rootKey, root);
}
newChat.stored = false;
// 按照原生冷热规则拆分保存
this.saveToStorage(true);
},
openChat(chatInfo) {
let chats = this.curChats;
let chat = null;
for (let idx in chats) {
if (chats[idx].type == chatInfo.type &&
chats[idx].targetId === chatInfo.targetId) {
chat = chats[idx];
this.moveTop(idx)
break;
}
}
if (chat == null) {
chat = {
targetId: chatInfo.targetId,
type: chatInfo.type,
showName: chatInfo.showName,
headImage: chatInfo.headImage,
isDnd: chatInfo.isDnd,
lastContent: "",
lastSendTime: new Date().getTime(),
unreadCount: 0,
hotMinIdx: 0,
readedMessageIdx: 0,
messages: [],
atMe: false,
atAll: false,
stored: false
};
chats.unshift(chat);
this.saveToStorage();
}
},
activeChat(idx) {
let chats = this.curChats;
if (idx >= 0) {
chats[idx].unreadCount = 0;
}
},
resetUnreadCount(chatInfo) {
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;
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) {
if (!pos.maxId || m.id <= pos.maxId) {
m.status = MESSAGE_STATUS.READED
chat.readedMessageIdx = idx;
chat.stored = false;
}
}
}
if (!chat.stored) {
this.saveToStorage();
}
},
cleanMessage(idx) {
let chat = this.curChats[idx];
chat.lastContent = '';
chat.hotMinIdx = 0;
chat.unreadCount = 0;
chat.atMe = false;
chat.atAll = false;
chat.stored = false
chat.messages = [];
this.saveToStorage(true);
},
removeChat(idx) {
let chats = this.curChats;
chats[idx].delete = true;
chats[idx].stored = false;
this.saveToStorage();
},
removePrivateChat(userId) {
let chats = this.curChats;
for (let idx in chats) {
if (chats[idx].type == 'PRIVATE' &&
chats[idx].targetId == userId) {
this.removeChat(idx);
}
}
},
removeGroupChat(groupId) {
let chats = this.curChats;
for (let idx in chats) {
if (chats[idx].type == 'GROUP' &&
chats[idx].targetId == groupId) {
this.removeChat(idx);
}
}
},
moveTop(idx) {
if (this.loading) {
return;
}
let chats = this.curChats;
if (idx > 0) {
let chat = chats[idx];
chats.splice(idx, 1);
chats.unshift(chat);
chat.lastSendTime = new Date().getTime();
chat.stored = false;
this.saveToStorage();
}
},
insertMessage(msgInfo, chatInfo) {
const t = i18n.global.t;
let type = chatInfo.type;
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);
let message = this.findMessage(chat, msgInfo);
if (message) {
const msgIndex = chat.messages.findIndex(m =>
(m.id && m.id === msgInfo.id) ||
(m.tmpId && m.tmpId === msgInfo.tmpId)
);
if (msgIndex !== -1) {
const updatedMessage = { ...message, ...msgInfo };
chat.messages.splice(msgIndex, 1, updatedMessage);
}
chat.stored = false;
this.saveToStorage();
return;
}
if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
chat.lastContent = t('chat.image');
} else if (msgInfo.type == MESSAGE_TYPE.FILE) {
chat.lastContent = t('chat.file');
} else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
chat.lastContent = t('chat.voice');
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) {
chat.lastContent = t('chat.voiceCall');
} else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) {
chat.lastContent = t('chat.videoCall');
} 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) {
chat.unreadCount++;
}
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 (!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600 * 1000)) {
chat.messages.push({
sendTime: msgInfo.sendTime,
type: MESSAGE_TYPE.TIP_TIME,
});
chat.lastTimeTip = msgInfo.sendTime;
}
chat.messages.push(msgInfo);
chat.stored = false;
this.saveToStorage();
},
updateMessage(msgInfo, chatInfo) {
let chat = this.findChat(chatInfo);
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);
let delIdx = -1;
for (let idx in chat.messages) {
if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
delIdx = idx;
break;
}
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) {
const t = i18n.global.t;
let chat = this.findChat(chatInfo);
if (!chat) return;
let isColdMessage = false;
let id = msgInfo.content;
let name = msgInfo.selfSend ? t('chat.you') : (chat.type == 'PRIVATE' ? t('chat.other') : msgInfo.sendNickName);
for (let idx in chat.messages) {
let m = chat.messages[idx];
if (m.id && m.id == id) {
m.status = MESSAGE_STATUS.RECALL;
m.content = `${name}${t('chat.recalledMessage')}`;
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) {
m.quoteMessage.content = t('chat.quoteRecalled');
m.quoteMessage.status = MESSAGE_STATUS.RECALL;
m.quoteMessage.type = MESSAGE_TYPE.TIP_TEXT
}
}
chat.stored = false;
this.saveToStorage(isColdMessage);
},
updateChatFromFriend(friend) {
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) {
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) {
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) {
let chat = this.findChat(chatInfo);
if (chat) {
chat.isDnd = isDnd;
}
},
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
}
} else if (chat.type == 'GROUP') {
let group = groupStore.findGroup(chat.targetId);
if (group) {
chat.isDnd = group.isDnd
}
}
})
chats.sort((chat1, chat2) => chat2.lastSendTime - chat1.lastSendTime);
const storageInfo = uni.getStorageInfoSync();
if (storageInfo && storageInfo.currentSize > 3000) {
this.cleanOtherUserCache();
}
this.fliterMessage(chats, 3000, 500);
chats.forEach(chat => {
// 原生规则:所有消息默认都放入热缓存,hotMinIdx归0
chat.hotMinIdx = 0;
chat.stored = false;
});
this.chats = chats;
cacheChats = null;
this.saveToStorage(true);
},
fliterMessage(chats, maxTotalSize, maxPerChatSize) {
let remainTotalSize = 0;
chats.forEach(chat => {
if (chat.messages.length > maxPerChatSize) {
let idx = chat.messages.length - maxPerChatSize;
chat.messages = chat.messages.slice(idx);
}
remainTotalSize += chat.messages.length;
})
if (remainTotalSize > maxTotalSize) {
this.fliterMessage(chats, maxTotalSize, maxPerChatSize / 2);
}
},
cleanOtherUserCache() {
const userStore = useUserStore();
const userId = userStore.userInfo.id;
const prefix = "chats-app-" + userId;
const res = uni.getStorageInfoSync();
res.keys.forEach(key => {
if (key.startsWith("chats-app") && !key.startsWith(prefix)) {
uni.removeStorageSync(key);
}
})
},
saveToStorage(withColdMessage) {
if (this.loading) {
return;
}
const userStore = useUserStore();
let userId = userStore.userInfo.id;
let rootKey = "chats-app-" + userId;
let chatKeys = [];
const keepHotCount = 300; // 原生热缓存保留条数
this.chats.forEach((chat) => {
let chatKey = `${rootKey}-${chat.type}-${chat.targetId}`
if (!chat.stored) {
if (chat.delete) {
uni.removeStorageSync(chatKey);
uni.removeStorageSync(chatKey + '-hot');
} else {
// 原生标准切割:hotMinIdx = 总消息长度 - 最新300条
const totalMsgLen = chat.messages.length;
chat.hotMinIdx = Math.max(0, totalMsgLen - keepHotCount);
// cold:前面所有久远历史消息
const coldMsg = chat.messages.slice(0, chat.hotMinIdx);
// hot:后面最新的300条消息(所有新发消息全在这里!)
const hotMsg = chat.messages.slice(chat.hotMinIdx);
// 写入冷缓存
if (withColdMessage) {
let coldChat = { ...chat, messages: coldMsg };
uni.setStorageSync(chatKey, coldChat);
}
// 写入热缓存(新发消息100%保存)
let hotChat = { ...chat, messages: hotMsg };
uni.setStorageSync(chatKey + '-hot', hotChat);
}
chat.stored = true;
}
if (!chat.delete) {
chatKeys.push(chatKey);
}
})
// 更新根会话列表
let chatsData = {
privateMsgMaxId: this.privateMsgMaxId,
groupMsgMaxId: this.groupMsgMaxId,
chatKeys: chatKeys
}
uni.setStorageSync(rootKey, chatsData)
this.chats = this.chats.filter(chat => !chat.delete)
},
clear(state) {
cacheChats = [];
this.chats = [];
this.privateMsgMaxId = 0;
this.groupMsgMaxId = 0;
this.loadingPrivateMsg = false;
this.loadingGroupMsg = false;
},
loadChat() {
return new Promise((resolve, reject) => {
let userStore = useUserStore();
let userId = userStore.userInfo.id;
const rootCacheKey = `chats-app-${userId}`;
let chatsData = uni.getStorageSync(rootCacheKey);
// 安全处理:没有数据直接返回
if (!chatsData) {
chatsData = {
privateMsgMaxId: 0,
groupMsgMaxId: 0,
chatKeys: []
};
}
// 安全处理:chatKeys 必须是数组
if (!chatsData.chatKeys || !Array.isArray(chatsData.chatKeys)) {
chatsData.chatKeys = [];
}
uni.removeStorageSync("currentActiveCustomerId");
let finalChats = [];
chatsData.chatKeys.forEach(key => {
try {
const coldChat = uni.getStorageSync(key) || {};
const hotChat = uni.getStorageSync(key + "-hot") || {};
// 修复发送中状态
if (hotChat.messages && Array.isArray(hotChat.messages)) {
hotChat.messages.forEach(msg => {
if (msg.status === MESSAGE_STATUS.SENDING) {
msg.status = MESSAGE_STATUS.FAILED;
}
});
}
// 消息合并(绝对安全)
const coldMsg = Array.isArray(coldChat.messages) ? coldChat.messages : [];
const hotMsg = Array.isArray(hotChat.messages) ? hotChat.messages : [];
const fullMsg = [...coldMsg, ...hotMsg];
let chat = { ...coldChat, ...hotChat };
chat.messages = fullMsg;
chat.hotMinIdx = coldMsg.length;
chat.readedMessageIdx = chat.readedMessageIdx || 0;
finalChats.push(chat);
} catch (e) {}
});
chatsData.chats = finalChats;
this.initChats(chatsData);
resolve();
});
},
},
getters: {
curChats: (state) => {
if (cacheChats && state.loading) {
return cacheChats;
}
return state.chats;
},
findChatIdx: (state) => (chat) => {
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) => {
let chats = state.curChats;
let idx = state.findChatIdx(chat);
return chats[idx];
},
findChatByFriend: (state) => (fid) => {
return state.curChats.find(chat => chat.type == 'PRIVATE' &&
chat.targetId == fid)
},
findChatByGroup: (state) => (gid) => {
return state.curChats.find(chat => chat.type == 'GROUP' &&
chat.targetId == gid)
},
findMessage: (state) => (chat, msgInfo) => {
if (!chat) {
return null;
}
if (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;
}
if (m.id && m.id < msgInfo.id) {
break;
}
}
}
if (msgInfo.selfSend && 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;
}
if (m.tmpId && m.tmpId < msgInfo.tmpId) {
break;
}
}
}
return null;
}
}
})