diff --git a/Touchkebao/src/main.tsx b/Touchkebao/src/main.tsx index 24238f27..a3392df2 100644 --- a/Touchkebao/src/main.tsx +++ b/Touchkebao/src/main.tsx @@ -12,11 +12,90 @@ import { db } from "./utils/db"; // 引入数据库实例 // 设置dayjs为中文 dayjs.locale("zh-cn"); +// 清理旧数据库 +async function cleanupOldDatabase() { + try { + // 获取所有数据库 + const databases = await indexedDB.databases(); + + for (const dbInfo of databases) { + if (dbInfo.name === "CunkebaoDatabase") { + console.log("检测到旧版数据库,开始清理..."); + + // 打开数据库检查版本 + const openRequest = indexedDB.open(dbInfo.name); + + await new Promise((resolve, reject) => { + openRequest.onsuccess = async event => { + const database = (event.target as IDBOpenDBRequest).result; + const objectStoreNames = Array.from(database.objectStoreNames); + + // 检查是否存在旧表 + const hasOldTables = objectStoreNames.some(name => + [ + "kfUsers", + "weChatGroup", + "contracts", + "newContactList", + "messageList", + ].includes(name), + ); + + if (hasOldTables) { + console.log("发现旧表,删除整个数据库:", objectStoreNames); + database.close(); + + // 删除整个数据库 + const deleteRequest = indexedDB.deleteDatabase(dbInfo.name); + deleteRequest.onsuccess = () => { + console.log("旧数据库已删除"); + resolve(); + }; + deleteRequest.onerror = () => { + console.error("删除旧数据库失败"); + reject(); + }; + } else { + console.log("数据库结构正确,无需清理"); + database.close(); + resolve(); + } + }; + + openRequest.onerror = () => { + console.error("无法打开数据库进行检查"); + reject(); + }; + }); + } + } + } catch (error) { + console.warn("清理旧数据库时出错(可忽略):", error); + } +} + // 数据库初始化 async function initializeApp() { - // 确保数据库已打开 - await db.open(); - console.log("数据库初始化成功"); + try { + // 1. 清理旧数据库 + await cleanupOldDatabase(); + + // 2. 打开新数据库 + await db.open(); + console.log("数据库初始化成功"); + + // 3. 开发环境清空数据(可选) + if (process.env.NODE_ENV === "development") { + console.log("开发环境:跳过数据清理"); + // 如需清空数据,取消下面的注释 + // await db.chatSessions.clear(); + // await db.contactsUnified.clear(); + // await db.contactLabelMap.clear(); + // await db.userLoginRecords.clear(); + } + } catch (error) { + console.error("数据库初始化失败:", error); + } // 渲染应用 const root = createRoot(document.getElementById("root")!); diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx index be2f9cb2..7c569755 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx @@ -8,7 +8,8 @@ import { } from "@/pages/pc/ckbox/weChat/api"; import { useCurrentContact } from "@/store/module/weChat/weChat"; import { useCkChatStore } from "@/store/module/ckchat/ckchat"; -import { contractService, weChatGroupService } from "@/utils/db"; +import { ContactManager } from "@/utils/dbAction"; +import { useUserStore } from "@/store/module/user"; const { TextArea } = Input; const { Option } = Select; @@ -92,11 +93,9 @@ const ToContract: React.FC = ({ // 删除聊天会话 deleteChatSession(currentContact.id); // 删除本地数据库记录 - if ("chatroomId" in currentContact) { - await weChatGroupService.delete(currentContact.id); - } else { - await contractService.delete(currentContact.id); - } + const currentUserId = useUserStore.getState().user?.id || 0; + const contactType = "chatroomId" in currentContact ? "group" : "friend"; + await ContactManager.deleteContact(currentContact.id); } catch (deleteError) { console.error("删除本地数据失败:", deleteError); } @@ -132,11 +131,9 @@ const ToContract: React.FC = ({ // 删除聊天会话 deleteChatSession(currentContact.id); // 删除本地数据库记录 - if ("chatroomId" in currentContact) { - await weChatGroupService.delete(currentContact.id); - } else { - await contractService.delete(currentContact.id); - } + const currentUserId = useUserStore.getState().user?.id || 0; + const contactType = "chatroomId" in currentContact ? "group" : "friend"; + await ContactManager.deleteContact(currentContact.id); } catch (deleteError) { console.error("删除本地数据失败:", deleteError); } diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/ClickMeau/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/ClickMeau/index.tsx index b9b69d97..b930e1b7 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/ClickMeau/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/ClickMeau/index.tsx @@ -111,27 +111,58 @@ const ClickMenu: React.FC = ({ return timeDiffInSeconds <= 108; }; - const isText = (): boolean => { - return messageData.msgType === 1; + // 检查是否为文本消息 + const isTextMessage = (): boolean => { + // msgType === 1 是纯文本消息 + if (messageData.msgType === 1) return true; + + // 尝试解析 content,如果是 JSON 且只包含 text/content 字段,也认为是文本 + try { + const parsed = JSON.parse(messageData.content || ""); + // 如果包含图片、视频、语音等字段,则不是纯文本 + if ( + parsed.previewImage || + parsed.tencentUrl || + parsed.voiceUrl || + parsed.voice || + parsed.videoUrl || + parsed.video || + parsed.fileUrl || + parsed.file + ) { + return false; + } + // 包含 text 或 content 字段,认为是文本 + return !!(parsed.text || parsed.content); + } catch (error) { + // 不是 JSON,如果 msgType 不确定,按文本处理 + return true; + } }; + const menuItems = [ { key: "transmit", icon: , label: "转发", }, - { - key: "copy", - icon: , - label: "复制", - }, + // 只在文本消息时显示复制选项 + ...(isTextMessage() + ? [ + { + key: "copy", + icon: , + label: "复制", + }, + ] + : []), { key: "multipleForwarding", icon: , label: "多条转发", }, - - ...(isText() + // 只在文本消息时显示引用选项 + ...(isTextMessage() ? [ { key: "quote", diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/TransmitModal/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/TransmitModal/index.tsx index 671888ff..62e41990 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/TransmitModal/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/TransmitModal/index.tsx @@ -17,8 +17,10 @@ import { TeamOutlined, } from "@ant-design/icons"; import styles from "./TransmitModal.module.scss"; -import { weChatGroupService, contractService } from "@/utils/db"; +import { ContactManager } from "@/utils/dbAction"; import { useWeChatStore } from "@/store/module/weChat/weChat"; +import { useContactStore } from "@/store/module/weChat/contacts"; +import { useUserStore } from "@/store/module/user"; import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; import { useWebSocketStore } from "@/store/module/websocket/websocket"; const TransmitModal: React.FC = () => { @@ -33,11 +35,11 @@ const TransmitModal: React.FC = () => { const [page, setPage] = useState(1); const pageSize = 20; const { sendCommand } = useWebSocketStore.getState(); + const currentUserId = useUserStore(state => state.user?.id) || 0; + // 从 Zustand store 获取更新方法 - const openTransmitModal = useWeChatStore(state => state.openTransmitModal); - const updateTransmitModal = useWeChatStore( - state => state.updateTransmitModal, - ); + const openTransmitModal = useContactStore(state => state.openTransmitModal); + const setTransmitModal = useContactStore(state => state.setTransmitModal); const updateSelectedChatRecords = useWeChatStore( state => state.updateSelectedChatRecords, ); @@ -50,14 +52,10 @@ const TransmitModal: React.FC = () => { const loadContacts = async () => { setLoading(true); try { - // 并行加载联系人和群组数据 - const [contractsData, groupsData] = await Promise.all([ - contractService.findAll(), - weChatGroupService.findAll(), - ]); - // 合并并排序(联系人在前,群组在后) - const allContactsData = [...contractsData, ...groupsData]; - setAllContacts(allContactsData); + // 从统一联系人表加载所有联系人 + const allContactsData = + await ContactManager.getUserContacts(currentUserId); + setAllContacts(allContactsData as any); } catch (err) { console.error("加载联系人数据失败:", err); message.error("加载联系人数据失败"); @@ -132,7 +130,7 @@ const TransmitModal: React.FC = () => { } } updateSelectedChatRecords([]); - updateTransmitModal(false); + setTransmitModal(false); }; // 检查联系人是否已选择 diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx index 7df16161..60f93569 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx @@ -349,20 +349,38 @@ const MessageList: React.FC = () => { // 初始化加载会话列表 useEffect(() => { const initializeSessions = async () => { - if (!currentUserId) return; + console.log("MessageList 初始化加载,currentUserId:", currentUserId); + console.log("user 对象:", user); - // 如果已经加载过一次,只从本地数据库读取,不请求接口 - if (hasLoadedOnce) { - try { - const cachedSessions = - await MessageManager.getUserSessions(currentUserId); - setSessions(cachedSessions); - } catch (error) { - console.error("从本地加载会话列表失败:", error); - } + if (!currentUserId || currentUserId === 0) { + console.warn("currentUserId 无效,跳过加载:", currentUserId); return; } + // 如果已经加载过一次,只从本地数据库读取,不请求接口 + if (hasLoadedOnce) { + console.log("已加载过,只从本地数据库读取"); + try { + const cachedSessions = + await MessageManager.getUserSessions(currentUserId); + console.log("从本地加载会话数:", cachedSessions.length); + + // 如果本地数据为空,重置 hasLoadedOnce 并重新加载 + if (cachedSessions.length === 0) { + console.warn("本地数据为空,重置加载状态并重新加载"); + setHasLoadedOnce(false); + // 不 return,继续执行下面的首次加载逻辑 + } else { + setSessions(cachedSessions); + return; + } + } catch (error) { + console.error("从本地加载会话列表失败:", error); + return; + } + } + + console.log("首次加载,开始初始化..."); setLoading(true); try { @@ -370,19 +388,26 @@ const MessageList: React.FC = () => { const cachedSessions = await MessageManager.getUserSessions(currentUserId); + console.log("本地缓存会话数:", cachedSessions.length); + if (cachedSessions.length > 0) { // 有缓存数据,立即显示 + console.log("有缓存数据,立即显示"); setSessions(cachedSessions); setLoading(false); // 2. 后台静默同步(不显示同步提示) + console.log("后台静默同步中..."); await syncWithServer(); setHasLoadedOnce(true); // 标记已加载过 + console.log("同步完成"); } else { // 无缓存,直接API加载 + console.log("无缓存,从服务器加载..."); await syncWithServer(); const newSessions = await MessageManager.getUserSessions(currentUserId); + console.log("从服务器加载会话数:", newSessions.length); setSessions(newSessions); setLoading(false); setHasLoadedOnce(true); // 标记已加载过 diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx index 4d032784..05a37cce 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx @@ -12,23 +12,12 @@ interface SidebarMenuProps { } const SidebarMenu: React.FC = ({ loading = false }) => { - const { - searchKeyword, - setSearchKeyword, - clearSearchKeyword, - currentContact, - } = useContactStore(); + const { searchKeyword, setSearchKeyword, clearSearchKeyword } = + useContactStore(); const currentCustomer = useCustomerStore(state => state.currentCustomer); const [activeTab, setActiveTab] = useState("chats"); - // 监听 currentContact 变化,自动切换到聊天tab - useEffect(() => { - if (currentContact !== null) { - setActiveTab("chats"); - } - }, [currentContact]); - const handleSearch = (value: string) => { setSearchKeyword(value); }; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx index c28d1a5d..0bb8510c 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useEffect } from "react"; import { Layout } from "antd"; import { MessageOutlined } from "@ant-design/icons"; import ChatWindow from "./components/ChatWindow/index"; @@ -6,34 +6,33 @@ import SidebarMenu from "./components/SidebarMenu/index"; import CustomerList from "./components/CustomerList"; import PageSkeleton from "./components/Skeleton"; import styles from "./index.module.scss"; -const { Content, Sider } = Layout; -import { chatInitAPIdata, initSocket } from "./main"; +import { useWebSocketStore } from "@/store/module/websocket/websocket"; +import { useUserStore } from "@/store/module/user"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; import { useWeChatStore } from "@/store/module/weChat/weChat"; -import { getIsLoadWeChat } from "@/store/module/ckchat/ckchat"; + +const { Content, Sider } = Layout; + const CkboxPage: React.FC = () => { - // 不要在组件初始化时获取sendCommand,而是在需要时动态获取 - const [loading, setLoading] = useState(false); const currentContract = useWeChatStore(state => state.currentContract); + + // 初始化 WebSocket 连接 useEffect(() => { - // 方法一:使用 Promise 链式调用处理异步函数 - // if (!getIsLoadWeChat()) { - // setLoading(true); - // } - chatInitAPIdata() - .then(() => { - // 数据加载完成后初始化WebSocket连接 - initSocket(); - }) - .catch(error => { - console.error("获取联系人列表失败:", error); - }) - .finally(() => { - setLoading(false); - }); + const { token2 } = useUserStore.getState(); + const { getAccountId } = useCkChatStore.getState(); + const { connect } = useWebSocketStore.getState(); + + connect({ + accessToken: token2, + accountId: Number(getAccountId()), + client: "kefu-client", + cmdType: "CmdSignIn", + seq: +new Date(), + }); }, []); return ( - + {/* 垂直侧边栏 */} @@ -44,7 +43,7 @@ const CkboxPage: React.FC = () => { {/* 左侧联系人边栏 */} - + {/* 主内容区 */} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts b/Touchkebao/src/pages/pc/ckbox/weChat/main.ts deleted file mode 100644 index 551d85d5..00000000 --- a/Touchkebao/src/pages/pc/ckbox/weChat/main.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { - asyncChatSessions, - asyncWeChatGroup, - asyncCountLables, - useCkChatStore, - updateIsLoadWeChat, - getIsLoadWeChat, -} from "@/store/module/ckchat/ckchat"; -import { useContactStore } from "@/store/module/weChat/contacts"; -import { useWebSocketStore } from "@/store/module/websocket/websocket"; -import { useUserStore } from "@/store/module/user"; -import { weChatGroupService, contractService } from "@/utils/db"; - -import { - getControlTerminalList, - getContactList, - getGroupList, - getCustomerList, - getLabelsListByGroup, - getMessageList, -} from "./api"; - -import { - KfUserListData, - ContractData, - weChatGroup, -} from "@/pages/pc/ckbox/data"; - -import { updateCustomerList } from "@weChatStore/customer"; -//获取触客宝基础信息 -export const chatInitAPIdata = async () => { - try { - let contractList = []; - let groupList = []; - - if (getIsLoadWeChat()) { - //获取联系人列表 - contractList = await contractService.findAll(); - //获取群列表 - groupList = await weChatGroupService.findAll(); - } else { - //获取联系人列表 - contractList = await getAllContactList(); - - //获取群列表 - groupList = await getAllGroupList(); - - updateIsLoadWeChat(true); - } - //获取联系人列表 - 使用新的 contacts store - // 注意:这里需要将 contractList 和 groupList 转换为统一的 Contact 格式 - // 暂时保留旧的数据库写入逻辑,后续需要统一到 ContactManager - await contractService.createManyWithServerId(contractList); - await asyncWeChatGroup(groupList); - - //获取标签列表 - const countLables = await getCountLables(); - await asyncCountLables(countLables); - - // 获取显示名称的辅助函数(优先 conRemark,其次 nickname) - const getDisplayName = (session: any) => { - return session.conRemark || session.nickname || ""; - }; - - const messageList = await getAllMessageList(); - - //排序功能 - const sortedSessions = messageList.sort((a, b) => { - // 获取置顶状态 - const aTop = a.config?.top || false; - const bTop = b.config?.top || false; - - // 首先按置顶状态排序(置顶的排在前面) - if (aTop !== bTop) { - return aTop ? -1 : 1; - } - - // 如果都是置顶或都不是置顶,则按未读消息数量降序排列(未读消息多的排在前面) - const aUnread = a.config?.unreadCount || 0; - const bUnread = b.config?.unreadCount || 0; - - if (aUnread !== bUnread) { - return bUnread - aUnread; - } - - // 如果未读消息数量相同,则按显示名称排序(字母排序) - const aName = getDisplayName(a).toLowerCase(); - const bName = getDisplayName(b).toLowerCase(); - - if (aName !== bName) { - return aName.localeCompare(bName, "zh-CN"); - } - - // 如果名称也相同,则按时间降序排列(最新的在前面) - // 如果lastUpdateTime不存在,则将其排在最后 - if (!a.lastUpdateTime) return 1; - if (!b.lastUpdateTime) return -1; - - const timeCompare = - new Date(b.lastUpdateTime).getTime() - - new Date(a.lastUpdateTime).getTime(); - - return timeCompare; - }); - //会话数据同步 - asyncChatSessions(sortedSessions); - - return { - contractList, - groupList, - }; - } catch (error) { - console.error("获取联系人列表失败:", error); - return []; - } -}; - -// 递归获取所有联系人列表 -export const getAllMessageList = async () => { - try { - let allMessages = []; - let page = 1; - const limit = 500; - let hasMore = true; - - while (hasMore) { - const Result = await getMessageList({ - page, - limit, - }); - const messageList = Result; - if ( - !messageList || - !Array.isArray(messageList) || - messageList.length === 0 - ) { - hasMore = false; - break; - } - - allMessages = [...allMessages, ...messageList]; - - // 如果返回的数据少于请求的数量,说明已经没有更多数据了 - if (messageList.length == 0) { - hasMore = false; - } else { - page = page + 1; - } - } - return allMessages; - } catch (error) { - console.error("获取所有联系人列表失败:", error); - return []; - } -}; - -//发起soket连接 -export const initSocket = () => { - // 从store获取token和accountId - const { token2 } = useUserStore.getState(); - const { getAccountId } = useCkChatStore.getState(); - const Token = token2; - const accountId = getAccountId(); - // 使用WebSocket store初始化连接 - const { connect } = useWebSocketStore.getState(); - - // 连接WebSocket - connect({ - accessToken: Token, - accountId: Number(accountId), - client: "kefu-client", - cmdType: "CmdSignIn", - seq: +new Date(), - }); -}; - -export const getCountLables = async () => { - const Result = await getLabelsListByGroup({}); - const LablesRes = Result.list; - return [ - ...[ - { - id: 0, - groupName: "默认群分组", - groupType: 2, - }, - ], - ...LablesRes, - ...[ - { - id: 0, - groupName: "未分组", - groupType: 1, - }, - ], - ]; -}; -/** - * 根据标签组织联系人 - * @param contractList 联系人列表 - * @param countLables 标签列表 - * @returns 按标签分组的联系人 - */ - -//获取控制终端列表 -export const getControlTerminalListByWechatAccountIds = ( - WechatAccountIds: number[], -) => { - return Promise.all( - WechatAccountIds.map(id => getControlTerminalList({ id: id })), - ); -}; -// 递归获取所有联系人列表 -export const getAllContactList = async () => { - try { - let allContacts = []; - let page = 1; - const limit = 500; - let hasMore = true; - - while (hasMore) { - const Result = await getContactList({ - page, - limit, - }); - const contractList = Result.list; - if ( - !contractList || - !Array.isArray(contractList) || - contractList.length === 0 - ) { - hasMore = false; - break; - } - - allContacts = [...allContacts, ...contractList]; - - // 如果返回的数据少于请求的数量,说明已经没有更多数据了 - if (contractList.length == 0) { - hasMore = false; - } else { - page = page + 1; - } - } - return allContacts; - } catch (error) { - console.error("获取所有联系人列表失败:", error); - return []; - } -}; -// 递归获取所有群列表 -export const getAllGroupList = async () => { - try { - let allContacts = []; - let page = 1; - const limit = 500; - let hasMore = true; - - while (hasMore) { - const Result = await getGroupList({ - page, - limit, - }); - const contractList = Result.list; - if ( - !contractList || - !Array.isArray(contractList) || - contractList.length === 0 - ) { - hasMore = false; - break; - } - - allContacts = [...allContacts, ...contractList]; - - // 如果返回的数据少于请求的数量,说明已经没有更多数据了 - if (contractList.length < limit) { - hasMore = false; - } else { - // 获取最后一条数据的id作为下一次请求的page - const lastContact = contractList[contractList.length - 1]; - page = lastContact.id; - } - } - - return allContacts; - } catch (error) { - console.error("获取所有群列表失败:", error); - return []; - } -}; - -// 提取不重复的wechatAccountId组 -export const getUniqueWechatAccountIds = ( - contacts: ContractData[], - groupList: weChatGroup[], -) => { - if (!contacts || !Array.isArray(contacts) || contacts.length === 0) { - return []; - } - - // 使用Set来存储不重复的wechatAccountId - const uniqueAccountIdsSet = new Set(); - - // 遍历联系人列表,将每个wechatAccountId添加到Set中 - contacts.forEach(contact => { - if (contact && contact.wechatAccountId) { - uniqueAccountIdsSet.add(contact.wechatAccountId); - } - }); - - // 遍历联系人列表,将每个wechatAccountId添加到Set中 - groupList.forEach(group => { - if (group && group.wechatAccountId) { - uniqueAccountIdsSet.add(group.wechatAccountId); - } - }); - - // 将Set转换为数组并返回 - return Array.from(uniqueAccountIdsSet); -}; diff --git a/Touchkebao/src/store/module/ckchat/api.ts b/Touchkebao/src/store/module/ckchat/api.ts index 804509a5..c98a02fa 100644 --- a/Touchkebao/src/store/module/ckchat/api.ts +++ b/Touchkebao/src/store/module/ckchat/api.ts @@ -1,5 +1,5 @@ //构建联系人列表标签 -import { weChatGroupService, contractService } from "@/utils/db"; +import { contactUnifiedService } from "@/utils/db"; import { request } from "@/api/request2"; import { ContactGroupByLabel } from "@/pages/pc/ckbox/data"; @@ -11,7 +11,6 @@ export const createContractList = async ( kfSelected: number, countLables: ContactGroupByLabel[], ) => { - // 根据 groupType 决定查询不同的服务 const realGroup = countLables .filter(item => item.id !== 0) .map(item => item.id); @@ -19,44 +18,27 @@ export const createContractList = async ( const dataByLabels = []; for (const label of countLables) { let data; - if (Number(label.groupType) === 1) { - if (label.id == 0) { - data = await contractService.findWhereMultiple([ - { - field: "groupId", - operator: "notIn", - value: realGroup, - }, - ]); - } else { - data = await contractService.findWhere("groupId", label.id); - } - // 过滤出 kfSelected 对应的联系人 - if (kfSelected && kfSelected != 0) { - data = data.filter(contact => contact.wechatAccountId === kfSelected); - } - // console.log(`标签 ${label.groupName} 对应的联系人数据:`, data); - } else if (Number(label.groupType) === 2) { - // groupType: 2, 查询 weChatGroupService - if (label.id == 0) { - data = await weChatGroupService.findWhereMultiple([ - { - field: "groupId", - operator: "notIn", - value: realGroup, - }, - ]); - } else { - data = await weChatGroupService.findWhere("groupId", label.id); - // 过滤出 kfSelected 对应的联系人 - } - if (kfSelected && kfSelected != 0) { - data = data.filter(contact => contact.wechatAccountId === kfSelected); - } + + // 使用统一的联系人服务查询 + if (label.id == 0) { + // 未分组:排除所有已分组的联系人 + data = await contactUnifiedService.findWhereMultiple([ + { + field: "groupId", + operator: "notIn", + value: realGroup, + }, + ]); } else { - console.warn(`未知的 groupType: ${label.groupType}`); - data = []; + // 指定分组:查询该分组的联系人 + data = await contactUnifiedService.findWhere("groupId", label.id); } + + // 过滤出 kfSelected 对应的联系人 + if (kfSelected && kfSelected != 0) { + data = data.filter(contact => contact.wechatAccountId === kfSelected); + } + dataByLabels.push({ ...label, contacts: data, diff --git a/Touchkebao/src/store/module/ckchat/ckchat.ts b/Touchkebao/src/store/module/ckchat/ckchat.ts index 44507597..63924f3b 100644 --- a/Touchkebao/src/store/module/ckchat/ckchat.ts +++ b/Touchkebao/src/store/module/ckchat/ckchat.ts @@ -7,9 +7,10 @@ import { KfUserListData, ContactGroupByLabel, } from "@/pages/pc/ckbox/data"; -import { weChatGroupService, contractService } from "@/utils/db"; +import { ContactManager } from "@/utils/dbAction"; import { createContractList } from "@/store/module/ckchat/api"; import { useWeChatStore } from "@/store/module/weChat/weChat"; +import { useUserStore } from "@/store/module/user"; // 从weChat store获取clearCurrentContact方法 const getClearCurrentContact = () => useWeChatStore.getState().clearCurrentContact; @@ -69,7 +70,27 @@ export const useCkChatStore = createPersistStore( }, //异步设置联系人分组 asyncWeChatGroup: async (data: weChatGroup[]) => { - await weChatGroupService.createManyWithServerId(data); + const currentUserId = useUserStore.getState().user?.id || 0; + // 转换为统一的 Contact 格式并保存 + await ContactManager.addContacts( + data.map(g => ({ + serverId: `group_${g.id}`, + userId: currentUserId, + id: g.id, + type: "group" as const, + wechatAccountId: g.wechatAccountId, + nickname: g.nickname, + conRemark: g.conRemark, + avatar: g.chatroomAvatar || "", + lastUpdateTime: new Date().toISOString(), + sortKey: "", + searchKey: (g.conRemark || g.nickname || "").toLowerCase(), + chatroomId: g.chatroomId, + chatroomOwner: g.chatroomOwner, + selfDisplayName: g.selfDisplyName, + notice: g.notice, + })), + ); }, //获取选中的客服信息 getKfSelectedUser: () => { diff --git a/Touchkebao/src/store/module/dataCenter/index.ts b/Touchkebao/src/store/module/dataCenter/index.ts index dcfd4b61..fe83e150 100644 --- a/Touchkebao/src/store/module/dataCenter/index.ts +++ b/Touchkebao/src/store/module/dataCenter/index.ts @@ -18,7 +18,8 @@ import { getFriendInjectConfig, } from "@/pages/pc/ckbox/api"; import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; -import { weChatGroupService, contractService } from "@/utils/db"; +import { ContactManager } from "@/utils/dbAction"; +import { useUserStore } from "@/store/module/user"; import { addChatSession, updateChatSession, @@ -323,21 +324,21 @@ export const useWeChatStore = create()( pinChatSessionToTop(getMessageId); } else { // 如果会话不存在,创建新会话 - if (isWechatGroup) { - const [group] = await weChatGroupService.findByIds(getMessageId); - if (group) { - addChatSession({ - ...group, - config: { unreadCount: 1 }, - }); - // 新创建的会话会自动添加到列表顶部,无需额外置顶 - } - } else { - const [user] = await contractService.findByIds(getMessageId); + const currentUserId = useUserStore.getState().user?.id || 0; + const contactType = isWechatGroup ? "group" : "friend"; + + // 从统一联系人表查询 + const contacts = + await ContactManager.getUserContacts(currentUserId); + const contact = contacts.find( + c => c.id === getMessageId[0] && c.type === contactType, + ); + + if (contact) { addChatSession({ - ...user, + ...contact, config: { unreadCount: 1 }, - }); + } as any); // 新创建的会话会自动添加到列表顶部,无需额外置顶 } } diff --git a/Touchkebao/src/store/module/weChat/weChat.ts b/Touchkebao/src/store/module/weChat/weChat.ts index 46da6e54..d3d51007 100644 --- a/Touchkebao/src/store/module/weChat/weChat.ts +++ b/Touchkebao/src/store/module/weChat/weChat.ts @@ -18,7 +18,8 @@ import { getFriendInjectConfig, } from "@/pages/pc/ckbox/api"; import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; -import { weChatGroupService, contractService } from "@/utils/db"; +import { ContactManager } from "@/utils/dbAction"; +import { useUserStore } from "@/store/module/user"; import { addChatSession, updateChatSession, @@ -342,22 +343,21 @@ export const useWeChatStore = create()( pinChatSessionToTop(getMessageId); } else { // 如果会话不存在,创建新会话 - if (isWechatGroup) { - const [group] = - await weChatGroupService.findByIds(getMessageId); - if (group) { - addChatSession({ - ...group, - config: { unreadCount: 1 }, - }); - // 新创建的会话会自动添加到列表顶部,无需额外置顶 - } - } else { - const [user] = await contractService.findByIds(getMessageId); + const currentUserId = useUserStore.getState().user?.id || 0; + const contactType = isWechatGroup ? "group" : "friend"; + + // 从统一联系人表查询 + const contacts = + await ContactManager.getUserContacts(currentUserId); + const contact = contacts.find( + c => c.id === getMessageId[0] && c.type === contactType, + ); + + if (contact) { addChatSession({ - ...user, + ...contact, config: { unreadCount: 1 }, - }); + } as any); // 新创建的会话会自动添加到列表顶部,无需额外置顶 } } diff --git a/Touchkebao/src/utils/db.ts b/Touchkebao/src/utils/db.ts index ce0450e7..d314eca9 100644 --- a/Touchkebao/src/utils/db.ts +++ b/Touchkebao/src/utils/db.ts @@ -16,12 +16,6 @@ */ import Dexie, { Table } from "dexie"; -import { - KfUserListData, - weChatGroup, - ContractData, - MessageListData, -} from "@/pages/pc/ckbox/data"; // ==================== 用户登录记录 ==================== export interface UserLoginRecord { @@ -119,32 +113,9 @@ export interface ContactLabelMap { lastUpdateTime: string; } -// ==================== 保留原有数据类型(向后兼容) ==================== -export interface KfUserWithServerId extends Omit { - serverId: number | string; // 服务器ID作为主键 - userId: number; // 用户ID(数据隔离) - id?: number; // 接口数据的原始ID字段 -} - -export interface NewContactListData { - serverId: number | string; // 服务器ID作为主键 - userId: number; // 用户ID(数据隔离) - id?: number; // 接口数据的原始ID字段 - groupName: string; - contacts: Contact[]; - weChatGroup: Contact[]; -} - // 数据库类 class CunkebaoDatabase extends Dexie { - // ==================== 保留原有表(向后兼容) ==================== - kfUsers!: Table; - weChatGroup!: Table; - contracts!: Table; - newContactList!: Table; - messageList!: Table; - - // ==================== 新增统一表 ==================== + // ==================== 统一表结构 ==================== chatSessions!: Table; // 统一会话表 contactsUnified!: Table; // 统一联系人表 contactLabelMap!: Table; // 联系人标签映射表 @@ -153,63 +124,15 @@ class CunkebaoDatabase extends Dexie { constructor() { super("CunkebaoDatabase"); - // 版本1:使用serverId作为主键的架构(保留原有表) + // 版本1:统一表结构 this.version(1).stores({ - kfUsers: - "serverId, id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline", - weChatGroup: - "serverId, id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar,wechatChatroomId, groupId, config, notice, selfDisplyName", - contracts: - "serverId, id, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, duplicate", - newContactList: "serverId, id, groupName, contacts", - messageList: - "serverId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName", - }); - - // 版本2:添加用户隔离字段到原有表 - this.version(2) - .stores({ - kfUsers: - "serverId, userId, id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline", - weChatGroup: - "serverId, userId, id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar,wechatChatroomId, groupId, config, notice, selfDisplyName", - contracts: - "serverId, userId, id, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, duplicate", - newContactList: "serverId, userId, id, groupName, contacts", - messageList: - "serverId, userId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName", - }) - .upgrade(trans => { - // 为现有数据添加默认userId=0(需要手动清理) - return trans - .table("kfUsers") - .toCollection() - .modify(item => { - item.userId = 0; - }); - }); - - // 版本3:添加新的统一表和用户登录记录表 - this.version(3).stores({ - // 保留原有表 - kfUsers: - "serverId, userId, id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline", - weChatGroup: - "serverId, userId, id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar,wechatChatroomId, groupId, config, notice, selfDisplyName", - contracts: - "serverId, userId, id, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, duplicate", - newContactList: "serverId, userId, id, groupName, contacts", - messageList: - "serverId, userId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName", - - // 新增统一表 // 会话表索引:支持按用户、类型、时间、置顶等查询 chatSessions: "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+lastUpdateTime], sortKey, nickname, conRemark, avatar, content, lastUpdateTime", // 联系人表索引:支持按用户、类型、标签、搜索等查询 contactsUnified: - "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime", + "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime, groupId", // 联系人标签映射表索引:支持按用户、标签、联系人、类型查询 contactLabelMap: @@ -479,14 +402,7 @@ export class DatabaseService { } } -// 创建各表的服务实例 -export const kfUserService = new DatabaseService(db.kfUsers); -export const weChatGroupService = new DatabaseService(db.weChatGroup); -export const contractService = new DatabaseService(db.contracts); -export const newContactListService = new DatabaseService(db.newContactList); -export const messageListService = new DatabaseService(db.messageList); - -// 新增统一表服务 +// 创建统一表的服务实例 export const chatSessionService = new DatabaseService(db.chatSessions); export const contactUnifiedService = new DatabaseService(db.contactsUnified); export const contactLabelMapService = new DatabaseService(db.contactLabelMap);