重构数据库管理逻辑,新增旧数据库清理功能,优化数据库初始化流程,更新联系人管理和消息处理逻辑,提升代码可读性和用户体验。
This commit is contained in:
@@ -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<void>((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")!);
|
||||
|
||||
@@ -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<ToContractProps> = ({
|
||||
// 删除聊天会话
|
||||
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<ToContractProps> = ({
|
||||
// 删除聊天会话
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -111,27 +111,58 @@ const ClickMenu: React.FC<ClickMenuProps> = ({
|
||||
|
||||
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: <ExportOutlined />,
|
||||
label: "转发",
|
||||
},
|
||||
{
|
||||
key: "copy",
|
||||
icon: <CopyOutlined />,
|
||||
label: "复制",
|
||||
},
|
||||
// 只在文本消息时显示复制选项
|
||||
...(isTextMessage()
|
||||
? [
|
||||
{
|
||||
key: "copy",
|
||||
icon: <CopyOutlined />,
|
||||
label: "复制",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: "multipleForwarding",
|
||||
icon: <CheckSquareOutlined />,
|
||||
label: "多条转发",
|
||||
},
|
||||
|
||||
...(isText()
|
||||
// 只在文本消息时显示引用选项
|
||||
...(isTextMessage()
|
||||
? [
|
||||
{
|
||||
key: "quote",
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
// 检查联系人是否已选择
|
||||
|
||||
@@ -349,20 +349,38 @@ const MessageList: React.FC<MessageListProps> = () => {
|
||||
// 初始化加载会话列表
|
||||
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<MessageListProps> = () => {
|
||||
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); // 标记已加载过
|
||||
|
||||
@@ -12,23 +12,12 @@ interface SidebarMenuProps {
|
||||
}
|
||||
|
||||
const SidebarMenu: React.FC<SidebarMenuProps> = ({ 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);
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<PageSkeleton loading={loading}>
|
||||
<PageSkeleton loading={false}>
|
||||
<Layout className={styles.ckboxLayout}>
|
||||
<Layout>
|
||||
{/* 垂直侧边栏 */}
|
||||
@@ -44,7 +43,7 @@ const CkboxPage: React.FC = () => {
|
||||
|
||||
{/* 左侧联系人边栏 */}
|
||||
<Sider width={280} className={styles.sider}>
|
||||
<SidebarMenu loading={loading} />
|
||||
<SidebarMenu />
|
||||
</Sider>
|
||||
|
||||
{/* 主内容区 */}
|
||||
|
||||
@@ -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<number>();
|
||||
|
||||
// 遍历联系人列表,将每个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);
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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<CkChatState>(
|
||||
},
|
||||
//异步设置联系人分组
|
||||
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: () => {
|
||||
|
||||
@@ -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<WeChatState>()(
|
||||
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);
|
||||
// 新创建的会话会自动添加到列表顶部,无需额外置顶
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<WeChatState>()(
|
||||
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);
|
||||
// 新创建的会话会自动添加到列表顶部,无需额外置顶
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<KfUserListData, "id"> {
|
||||
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<KfUserWithServerId>;
|
||||
weChatGroup!: Table<weChatGroup>;
|
||||
contracts!: Table<ContractData>;
|
||||
newContactList!: Table<NewContactListData>;
|
||||
messageList!: Table<MessageListData>;
|
||||
|
||||
// ==================== 新增统一表 ====================
|
||||
// ==================== 统一表结构 ====================
|
||||
chatSessions!: Table<ChatSession>; // 统一会话表
|
||||
contactsUnified!: Table<Contact>; // 统一联系人表
|
||||
contactLabelMap!: Table<ContactLabelMap>; // 联系人标签映射表
|
||||
@@ -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<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建各表的服务实例
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user