From 13cb684abd1076f8c49cec66e1f846fd89a7c765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Sat, 30 Aug 2025 11:52:52 +0800 Subject: [PATCH] =?UTF-8?q?refactor(store):=20=E9=87=8D=E6=9E=84ckchat?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=B8=BA=E5=AD=90=E7=9B=AE=E5=BD=95=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(test): 添加数据库测试页面和工具 将ckchat相关代码移动到store/module/ckchat子目录,包含数据定义和实现文件 添加数据库测试页面和工具类,支持服务器ID与本地ID映射 移除不再使用的initSafeArea函数 --- Cunkebao/src/pages/login/Login.tsx | 2 +- Cunkebao/src/pages/mobile/test/db.tsx | 597 +++++++++++ .../ChatWindow/components/Person/index.tsx | 2 +- .../pc/ckbox/components/ChatWindow/index.tsx | 2 +- .../SidebarMenu/WechatFriends/index.tsx | 2 +- .../pc/ckbox/components/SidebarMenu/index.tsx | 2 +- .../components/VerticalUserList/index.tsx | 2 +- Cunkebao/src/pages/pc/ckbox/index.tsx | 2 +- Cunkebao/src/pages/pc/ckbox/main.ts | 2 +- Cunkebao/src/router/module/test.tsx | 6 + Cunkebao/src/store/module/ckchat.ts | 2 +- .../store/module/{ => ckchat}/ckchat.data.ts | 0 Cunkebao/src/store/module/ckchat/ckchat.ts | 205 ++++ Cunkebao/src/store/module/websocket.ts | 2 +- Cunkebao/src/utils/common.ts | 6 - Cunkebao/src/utils/db-examples.ts | 980 +++++++++--------- Cunkebao/src/utils/db-test.ts | 146 +++ Cunkebao/src/utils/db.ts | 546 ++++------ 18 files changed, 1664 insertions(+), 842 deletions(-) create mode 100644 Cunkebao/src/pages/mobile/test/db.tsx rename Cunkebao/src/store/module/{ => ckchat}/ckchat.data.ts (100%) create mode 100644 Cunkebao/src/store/module/ckchat/ckchat.ts create mode 100644 Cunkebao/src/utils/db-test.ts diff --git a/Cunkebao/src/pages/login/Login.tsx b/Cunkebao/src/pages/login/Login.tsx index 73701a38..d375422a 100644 --- a/Cunkebao/src/pages/login/Login.tsx +++ b/Cunkebao/src/pages/login/Login.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { useCkChatStore } from "@/store/module/ckchat"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; import { Form, Input, Button, Toast, Checkbox } from "antd-mobile"; import { EyeInvisibleOutline, diff --git a/Cunkebao/src/pages/mobile/test/db.tsx b/Cunkebao/src/pages/mobile/test/db.tsx new file mode 100644 index 00000000..abb773c4 --- /dev/null +++ b/Cunkebao/src/pages/mobile/test/db.tsx @@ -0,0 +1,597 @@ +import React, { useState, useEffect } from "react"; +import { + Button, + Card, + Statistic, + Row, + Col, + Alert, + Typography, + Space, + Spin, + Modal, + message, +} from "antd"; +import { + UserOutlined, + TeamOutlined, + ContactsOutlined, + FolderOutlined, + PlayCircleOutlined, + DeleteOutlined, + ReloadOutlined, +} from "@ant-design/icons"; +import { DatabaseExamples } from "@/utils/db-examples"; +import { + db, + kfUserService, + groupService, + contractService, + DatabaseService, +} from "@/utils/db"; +import { testDatabaseUpgrade, resetDatabase } from "@/utils/db-test"; +import { KfUserListData, GroupData, ContractData } from "@/pages/pc/ckbox/data"; + +const { Title, Text } = Typography; + +interface LogEntry { + id: number; + timestamp: string; + type: "info" | "success" | "error"; + message: string; +} + +const DatabaseTestPage: React.FC = () => { + const [logs, setLogs] = useState([]); + const [isRunning, setIsRunning] = useState(false); + const [dbStats, setDbStats] = useState({ + kfUsers: 0, + groups: 0, + contracts: 0, + newContactList: 0, + }); + + const addLog = (type: LogEntry["type"], msg: string) => { + const newLog: LogEntry = { + id: Date.now(), + timestamp: new Date().toLocaleTimeString(), + type, + message: msg, + }; + setLogs(prev => [newLog, ...prev].slice(0, 100)); // 保留最新100条日志 + + // 使用 Ant Design 的 message 组件显示通知 + if (type === "success") { + message.success(msg); + } else if (type === "error") { + message.error(msg); + } else { + message.info(msg); + } + }; + + const updateStats = async () => { + try { + const newContactListService = new DatabaseService(db.newContractList); + const stats = { + kfUsers: await kfUserService.count(), + groups: await groupService.count(), + contracts: await contractService.count(), + newContactList: await newContactListService.count(), + }; + setDbStats(stats); + } catch (error) { + addLog("error", `统计数据更新失败: ${error}`); + } + }; + + const initDatabase = async () => { + try { + const result = await testDatabaseUpgrade(); + if (result.success) { + addLog( + "success", + `数据库初始化成功 - 版本: ${result.version}, 表: ${result.tables?.join(", ")}`, + ); + await updateStats(); + } else { + addLog("error", `数据库初始化失败: ${result.error}`); + } + } catch (error) { + addLog("error", `数据库初始化失败: ${error}`); + } + }; + + const runKfUserExample = async () => { + setIsRunning(true); + addLog("info", "开始客服用户操作示例..."); + + try { + // 模拟从接口获取的客服用户数据(包含服务器ID) + const serverKfUser = { + id: Date.now(), // 模拟服务器返回的ID + tenantId: 1, + wechatId: `test_${Date.now()}`, + nickname: "测试客服", + alias: "客服小王", + avatar: "https://example.com/avatar.jpg", + gender: 1, + region: "北京", + signature: "专业客服,为您服务", + bindQQ: "123456789", + bindEmail: "test@example.com", + bindMobile: "13800138000", + createTime: new Date().toISOString(), + currentDeviceId: 1, + isDeleted: false, + deleteTime: "", + groupId: 1, + memo: "优秀客服", + wechatVersion: "8.0.0", + labels: ["VIP客服", "专业"], + lastUpdateTime: new Date().toISOString(), + isOnline: true, + }; + + const userId = await kfUserService.createWithServerId(serverKfUser); + addLog( + "success", + `创建客服用户成功,本地ID: ${userId}, 服务器ID: ${serverKfUser.id}`, + ); + + // 查询用户(按本地ID) + const user = await kfUserService.findById(userId); + addLog("info", `查询到用户: ${user?.nickname}`); + + // 根据服务器ID查询 + const userByServerId = await kfUserService.findByServerId( + serverKfUser.id, + ); + addLog("info", `根据服务器ID查询到用户: ${userByServerId?.nickname}`); + + // 更新用户 + await kfUserService.update(userId, { + nickname: "更新后的昵称", + lastUpdateTime: new Date().toISOString(), + }); + addLog("success", "用户信息更新成功"); + + await updateStats(); + } catch (error) { + addLog("error", `客服用户操作失败: ${error}`); + } finally { + setIsRunning(false); + } + }; + + const runGroupExample = async () => { + setIsRunning(true); + addLog("info", "开始群组操作示例..."); + + try { + // 模拟从接口获取的群组数据 + const serverGroup = { + id: Date.now(), // 模拟服务器返回的ID + wechatAccountId: 1, + tenantId: 1, + accountId: 1, + chatroomId: `chatroom_${Date.now()}`, + chatroomOwner: "owner_001", + conRemark: "测试群组", + nickname: "产品讨论群", + chatroomAvatar: "https://example.com/group-avatar.jpg", + groupId: 1, + config: { + chat: true, + }, + unreadCount: 0, + notice: "欢迎加入产品讨论群", + selfDisplyName: "群管理员", + }; + + const groupId = await groupService.createWithServerId(serverGroup); + addLog( + "success", + `创建群组成功,本地ID: ${groupId}, 服务器ID: ${serverGroup.id}`, + ); + + // 更新群组 + await groupService.update(groupId, { + unreadCount: 5, + notice: "更新的群公告", + }); + addLog("success", "群组信息更新成功"); + + await updateStats(); + } catch (error) { + addLog("error", `群组操作失败: ${error}`); + } finally { + setIsRunning(false); + } + }; + + const runContractExample = async () => { + setIsRunning(true); + addLog("info", "开始联系人操作示例..."); + + try { + // 模拟从接口获取的联系人数据 + const serverContract = { + id: Date.now(), // 模拟服务器返回的ID + wechatAccountId: 1, + wechatId: `contact_${Date.now()}`, + alias: "张三", + conRemark: "重要客户", + nickname: "张总", + quanPin: "zhangsan", + avatar: "https://example.com/contact-avatar.jpg", + gender: 1, + region: "上海", + addFrom: 1, + phone: "13900139000", + labels: ["VIP客户", "重点关注"], + signature: "专业人士", + accountId: 1, + extendFields: null, + city: "上海", + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 1, + thirdParty: null, + additionalPicture: "", + desc: "优质客户", + config: { + chat: true, + }, + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + }; + + const contractId = + await contractService.createWithServerId(serverContract); + addLog( + "success", + `创建联系人成功,本地ID: ${contractId}, 服务器ID: ${serverContract.id}`, + ); + + // 查询联系人 + const searchResults = await contractService.findWhereStartsWith( + "nickname", + "张", + ); + addLog("info", `找到姓张的联系人: ${searchResults.length} 个`); + + await updateStats(); + } catch (error) { + addLog("error", `联系人操作失败: ${error}`); + } finally { + setIsRunning(false); + } + }; + + const runBatchExample = async () => { + setIsRunning(true); + addLog("info", "开始批量操作示例..."); + + try { + // 模拟从接口获取的批量联系人数据 + const batchServerContacts = Array.from({ length: 5 }, (_, i) => ({ + id: Date.now() + i, // 模拟服务器返回的ID + wechatAccountId: 1, + wechatId: `batch_${Date.now()}_${i}`, + alias: `批量用户${i + 1}`, + conRemark: "批量导入", + nickname: `用户${i + 1}`, + quanPin: `yonghu${i + 1}`, + gender: (i % 2) + 1, + region: i % 2 === 0 ? "北京" : "上海", + addFrom: 3, + phone: `1380000000${i}`, + labels: ["批量导入"], + signature: "", + accountId: 1, + extendFields: null, + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 1, + thirdParty: null, + additionalPicture: "", + desc: "批量导入的用户", + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + })); + + const batchIds = + await contractService.createManyWithServerId(batchServerContacts); + addLog( + "success", + `批量创建联系人成功,创建了 ${batchIds.length} 个联系人`, + ); + + await updateStats(); + } catch (error) { + addLog("error", `批量操作失败: ${error}`); + } finally { + setIsRunning(false); + } + }; + + const clearAllData = async () => { + Modal.confirm({ + title: "确认清空数据", + content: "确定要清空所有测试数据吗?此操作不可恢复!", + okText: "确定", + cancelText: "取消", + onOk: async () => { + setIsRunning(true); + addLog("info", "开始清空数据..."); + try { + await db.transaction( + "rw", + [db.kfUsers, db.groups, db.contracts, db.newContractList], + async () => { + await db.kfUsers.clear(); + await db.groups.clear(); + await db.contracts.clear(); + await db.newContractList.clear(); + }, + ); + + addLog("success", "所有数据清空成功"); + await updateStats(); + } catch (error) { + addLog("error", `清空数据失败: ${error}`); + } finally { + setIsRunning(false); + } + }, + }); + }; + + const runAllExamples = async () => { + setIsRunning(true); + addLog("info", "开始运行所有示例..."); + + try { + await DatabaseExamples.runAllExamples(); + addLog("success", "所有示例运行完成"); + await updateStats(); + } catch (error) { + addLog("error", `运行示例失败: ${error}`); + } finally { + setIsRunning(false); + } + }; + + useEffect(() => { + initDatabase(); + }, []); + + return ( +
+ + 数据库操作演示 + + + {/* 数据统计 */} + + + + } + valueStyle={{ color: "#1890ff" }} + /> + + + + + } + valueStyle={{ color: "#52c41a" }} + /> + + + + + } + valueStyle={{ color: "#faad14" }} + /> + + + + + } + valueStyle={{ color: "#722ed1" }} + /> + + + + + {/* 操作按钮 */} + + + + + + + + + + + + + {/* 运行状态 */} + {isRunning && ( + } + style={{ marginBottom: "24px" }} + /> + )} + + {/* 日志显示 */} + } + onClick={() => setLogs([])} + size="small" + > + 清空日志 + + } + > +
+ {logs.length === 0 ? ( +
+ 暂无日志 +
+ ) : ( + + {logs.map(log => ( + + {log.message} + + {log.timestamp} + +
+ } + type={ + log.type === "success" + ? "success" + : log.type === "error" + ? "error" + : "info" + } + showIcon + style={{ margin: 0 }} + /> + ))} + + )} +
+ + + ); +}; + +export default DatabaseTestPage; diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx index da0dea8b..710d399a 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/Person/index.tsx @@ -24,7 +24,7 @@ import { CheckOutlined, } from "@ant-design/icons"; import { ContractData } from "@/pages/pc/ckbox/data"; -import { useCkChatStore } from "@/store/module/ckchat"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; import styles from "./Person.module.scss"; const { Sider } = Layout; diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx index 037edf5f..6402f78e 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx @@ -40,7 +40,7 @@ import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api"; import styles from "./ChatWindow.module.scss"; import { useWebSocketStore, WebSocketMessage } from "@/store/module/websocket"; import { formatWechatTime } from "@/utils/common"; -import { useCkChatStore } from "@/store/module/ckchat"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; import Person from "./components/Person"; const { Header, Content, Footer } = Layout; const { TextArea } = Input; diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx index fe0fe10d..730f3ede 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx @@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from "react"; import { List, Avatar, Collapse, Button } from "antd"; import type { CollapseProps } from "antd"; import styles from "./WechatFriends.module.scss"; -import { useCkChatStore } from "@/store/module/ckchat"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; import { ContractData, GroupData } from "@/pages/pc/ckbox/data"; interface WechatFriendsProps { diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx index 4c3f88ab..98c5c712 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx @@ -10,7 +10,7 @@ import { ContractData, GroupData } from "@/pages/pc/ckbox/data"; import WechatFriends from "./WechatFriends"; import MessageList from "./MessageList/index"; import styles from "./SidebarMenu.module.scss"; -import { getChatSessions } from "@/store/module/ckchat"; +import { getChatSessions } from "@/store/module/ckchat/ckchat"; interface SidebarMenuProps { contracts: ContractData[] | GroupData[]; diff --git a/Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/index.tsx index 387af936..dec9d17e 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/index.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { Avatar, Badge, Tooltip } from "antd"; import styles from "./VerticalUserList.module.scss"; -import { useCkChatStore, asyncKfSelected } from "@/store/module/ckchat"; +import { useCkChatStore, asyncKfSelected } from "@/store/module/ckchat/ckchat"; import { TeamOutlined } from "@ant-design/icons"; const VerticalUserList: React.FC = () => { diff --git a/Cunkebao/src/pages/pc/ckbox/index.tsx b/Cunkebao/src/pages/pc/ckbox/index.tsx index 408f1ea5..c8f50eff 100644 --- a/Cunkebao/src/pages/pc/ckbox/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/index.tsx @@ -7,7 +7,7 @@ import SidebarMenu from "./components/SidebarMenu/index"; import VerticalUserList from "./components/VerticalUserList"; import PageSkeleton from "./components/Skeleton"; import styles from "./index.module.scss"; -import { addChatSession } from "@/store/module/ckchat"; +import { addChatSession } from "@/store/module/ckchat/ckchat"; const { Header, Content, Sider } = Layout; import { chatInitAPIdata } from "./main"; import { KfUserListData, GroupData, ContractData } from "@/pages/pc/ckbox/data"; diff --git a/Cunkebao/src/pages/pc/ckbox/main.ts b/Cunkebao/src/pages/pc/ckbox/main.ts index c06209fd..ff62d021 100644 --- a/Cunkebao/src/pages/pc/ckbox/main.ts +++ b/Cunkebao/src/pages/pc/ckbox/main.ts @@ -4,7 +4,7 @@ import { asyncContractList, asyncChatSessions, asyncNewContractList, -} from "@/store/module/ckchat"; +} from "@/store/module/ckchat/ckchat"; import { useWebSocketStore } from "@/store/module/websocket"; import { diff --git a/Cunkebao/src/router/module/test.tsx b/Cunkebao/src/router/module/test.tsx index ab416284..53dedef3 100644 --- a/Cunkebao/src/router/module/test.tsx +++ b/Cunkebao/src/router/module/test.tsx @@ -1,6 +1,7 @@ import SelectTest from "@/pages/mobile/test/select"; import TestIndex from "@/pages/mobile/test/index"; import UploadTest from "@/pages/mobile/test/upload"; +import DbTest from "@/pages/mobile/test/db"; import UpdateNotificationTest from "@/pages/mobile/test/update-notification"; import IframeDebugPage from "@/pages/iframe"; import { DEV_FEATURES } from "@/utils/env"; @@ -33,6 +34,11 @@ const componentTestRoutes = DEV_FEATURES.SHOW_TEST_PAGES element: , auth: false, // 不需要认证,方便调试 }, + { + path: "/test/db", + element: , + auth: false, // 不需要认证,方便调试 + }, ] : []; diff --git a/Cunkebao/src/store/module/ckchat.ts b/Cunkebao/src/store/module/ckchat.ts index 591a2bf1..ed62f71a 100644 --- a/Cunkebao/src/store/module/ckchat.ts +++ b/Cunkebao/src/store/module/ckchat.ts @@ -1,5 +1,5 @@ import { createPersistStore } from "@/store/createPersistStore"; -import { CkChatState, CkUserInfo, CkTenant } from "./ckchat.data"; +import { CkChatState, CkUserInfo, CkTenant } from "./ckchat/ckchat.data"; import { ContractData, GroupData, diff --git a/Cunkebao/src/store/module/ckchat.data.ts b/Cunkebao/src/store/module/ckchat/ckchat.data.ts similarity index 100% rename from Cunkebao/src/store/module/ckchat.data.ts rename to Cunkebao/src/store/module/ckchat/ckchat.data.ts diff --git a/Cunkebao/src/store/module/ckchat/ckchat.ts b/Cunkebao/src/store/module/ckchat/ckchat.ts new file mode 100644 index 00000000..591a2bf1 --- /dev/null +++ b/Cunkebao/src/store/module/ckchat/ckchat.ts @@ -0,0 +1,205 @@ +import { createPersistStore } from "@/store/createPersistStore"; +import { CkChatState, CkUserInfo, CkTenant } from "./ckchat.data"; +import { + ContractData, + GroupData, + CkAccount, + KfUserListData, +} from "@/pages/pc/ckbox/data"; + +export const useCkChatStore = createPersistStore( + set => ({ + userInfo: null, + isLoggedIn: false, + contractList: [], //联系人列表 + chatSessions: [], //聊天会话 + kfUserList: [], //客服列表 + kfSelected: 0, + newContractList: [], //联系人分组 + kfSelectedUser: () => { + const state = useCkChatStore.getState(); + return state.kfUserList.find(item => item.id === state.kfSelected); + }, + asyncKfSelected: (data: number) => { + set({ kfSelected: data }); + }, + // 异步设置会话列表 + asyncNewContractList: data => { + set({ newContractList: data }); + }, + getNewContractList: () => { + const state = useCkChatStore.getState(); + return state.newContractList; + }, + // 异步设置会话列表 + asyncChatSessions: data => { + set({ chatSessions: data }); + }, + // 异步设置联系人列表 + asyncContractList: data => { + set({ contractList: data }); + }, + // 控制终端用户列表 + getkfUserList: () => { + const state = useCkChatStore.getState(); + return state.kfUserList; + }, + asyncKfUserList: data => { + set({ kfUserList: data }); + }, + // 删除控制终端用户 + deleteCtrlUser: (userId: number) => { + set(state => ({ + kfUserList: state.kfUserList.filter(item => item.id !== userId), + })); + }, + // 更新控制终端用户 + updateCtrlUser: (user: KfUserListData) => { + set(state => ({ + kfUserList: state.kfUserList.map(item => + item.id === user.id ? user : item, + ), + })); + }, + // 清空控制终端用户列表 + clearkfUserList: () => { + set({ kfUserList: [] }); + }, + // 获取聊天会话 + getChatSessions: () => { + const state = useCkChatStore.getState(); + return state.chatSessions; + }, + // 添加聊天会话 + addChatSession: (session: ContractData | GroupData) => { + set(state => { + // 检查是否已存在相同id的会话 + const exists = state.chatSessions.some(item => item.id === session.id); + // 如果已存在则不添加,否则添加到列表中 + return { + chatSessions: exists + ? state.chatSessions + : [...state.chatSessions, session as ContractData | GroupData], + }; + }); + }, + // 更新聊天会话 + updateChatSession: (session: ContractData | GroupData) => { + set(state => ({ + chatSessions: state.chatSessions.map(item => + item.id === session.id ? session : item, + ), + })); + }, + // 删除聊天会话 + deleteChatSession: (sessionId: string) => { + set(state => ({ + chatSessions: state.chatSessions.filter(item => item.id !== sessionId), + })); + }, + // 设置用户信息 + setUserInfo: (userInfo: CkUserInfo) => { + set({ userInfo, isLoggedIn: true }); + }, + + // 清除用户信息 + clearUserInfo: () => { + set({ userInfo: null, isLoggedIn: false }); + }, + + // 更新账户信息 + updateAccount: (account: Partial) => { + set(state => ({ + userInfo: state.userInfo + ? { + ...state.userInfo, + account: { ...state.userInfo.account, ...account }, + } + : null, + })); + }, + + // 更新租户信息 + updateTenant: (tenant: Partial) => { + set(state => ({ + userInfo: state.userInfo + ? { + ...state.userInfo, + tenant: { ...state.userInfo.tenant, ...tenant }, + } + : null, + })); + }, + + // 获取账户ID + getAccountId: () => { + const state = useCkChatStore.getState(); + return Number(state.userInfo?.account?.id) || null; + }, + + // 获取租户ID + getTenantId: () => { + const state = useCkChatStore.getState(); + return state.userInfo?.tenant?.id || null; + }, + + // 获取账户名称 + getAccountName: () => { + const state = useCkChatStore.getState(); + return ( + state.userInfo?.account?.realName || + state.userInfo?.account?.userName || + null + ); + }, + + // 获取租户名称 + getTenantName: () => { + const state = useCkChatStore.getState(); + return state.userInfo?.tenant?.name || null; + }, + }), + { + name: "ckchat-store", + partialize: state => ({ + userInfo: state.userInfo, + isLoggedIn: state.isLoggedIn, + }), + onRehydrateStorage: () => state => { + // console.log("CkChat store hydrated:", state); + }, + }, +); + +// 导出便捷的获取方法 +export const getCkAccountId = () => useCkChatStore.getState().getAccountId(); +export const getCkTenantId = () => useCkChatStore.getState().getTenantId(); +export const getCkAccountName = () => + useCkChatStore.getState().getAccountName(); +export const getCkTenantName = () => useCkChatStore.getState().getTenantName(); +export const getChatSessions = () => + useCkChatStore.getState().getChatSessions(); +export const addChatSession = (session: ContractData | GroupData) => + useCkChatStore.getState().addChatSession(session); +export const updateChatSession = (session: ContractData | GroupData) => + useCkChatStore.getState().updateChatSession(session); +export const deleteChatSession = (sessionId: string) => + useCkChatStore.getState().deleteChatSession(sessionId); +export const getkfUserList = () => useCkChatStore.getState().kfUserList; +export const addCtrlUser = (user: KfUserListData) => + useCkChatStore.getState().addCtrlUser(user); +export const deleteCtrlUser = (userId: number) => + useCkChatStore.getState().deleteCtrlUser(userId); +export const updateCtrlUser = (user: KfUserListData) => + useCkChatStore.getState().updateCtrlUser(user); +export const asyncKfUserList = (data: KfUserListData[]) => + useCkChatStore.getState().asyncKfUserList(data); +export const asyncContractList = (data: ContractData[]) => + useCkChatStore.getState().asyncContractList(data); +export const asyncChatSessions = (data: ContractData[]) => + useCkChatStore.getState().asyncChatSessions(data); +export const asyncNewContractList = ( + data: { groupName: string; contacts: any[] }[], +) => useCkChatStore.getState().asyncNewContractList(data); +export const asyncKfSelected = (data: number) => + useCkChatStore.getState().asyncKfSelected(data); diff --git a/Cunkebao/src/store/module/websocket.ts b/Cunkebao/src/store/module/websocket.ts index 397733c9..5c21f900 100644 --- a/Cunkebao/src/store/module/websocket.ts +++ b/Cunkebao/src/store/module/websocket.ts @@ -1,7 +1,7 @@ import { createPersistStore } from "@/store/createPersistStore"; import { Toast } from "antd-mobile"; import { useUserStore } from "./user"; -import { useCkChatStore } from "@/store/module/ckchat"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; const { getAccountId } = useCkChatStore.getState(); // WebSocket消息类型 diff --git a/Cunkebao/src/utils/common.ts b/Cunkebao/src/utils/common.ts index fd740223..b68934bb 100644 --- a/Cunkebao/src/utils/common.ts +++ b/Cunkebao/src/utils/common.ts @@ -113,9 +113,3 @@ export function getSafeAreaHeight() { // 3. 默认值 return 0; } -// 设置全局 CSS 变量 -export function initSafeArea() { - const root = document.documentElement; - const height = getSafeAreaHeight(); - root.style.setProperty("--safe-area-top", `${height}px`); -} diff --git a/Cunkebao/src/utils/db-examples.ts b/Cunkebao/src/utils/db-examples.ts index daa7d71b..4387431e 100644 --- a/Cunkebao/src/utils/db-examples.ts +++ b/Cunkebao/src/utils/db-examples.ts @@ -1,508 +1,530 @@ -// 数据库使用示例 import { db, - userService, - messageService, - chatRoomService, - settingService, - userBusinessService, - messageBusinessService, - settingBusinessService, - DatabaseUtils, - type User, - type Message, - type ChatRoom, - type Setting, + kfUserService, + groupService, + contractService, + DatabaseService, + NewContactListData, } from "./db"; +import { KfUserListData, GroupData, ContractData } from "@/pages/pc/ckbox/data"; -// ============= 基础 CRUD 操作示例 ============= +// 创建 newContractList 服务实例 +const newContactListService = new DatabaseService(db.newContractList); -// 1. 用户操作示例 -export const userExamples = { - // 创建用户 - async createUser() { - const userId = await userService.create({ - name: "张三", - email: "zhangsan@example.com", - status: "active", - }); - console.log("创建用户成功,ID:", userId); - return userId; - }, - - // 批量创建用户 - async createMultipleUsers() { - const userIds = await userService.createMany([ - { name: "李四", email: "lisi@example.com", status: "active" }, - { name: "王五", email: "wangwu@example.com", status: "inactive" }, - { name: "赵六", email: "zhaoliu@example.com", status: "active" }, - ]); - console.log("批量创建用户成功,IDs:", userIds); - return userIds; - }, - - // 查询用户 - async findUsers() { - // 查询所有用户 - const allUsers = await userService.findAll(); - console.log("所有用户:", allUsers); - - // 根据ID查询 - const user = await userService.findById(1); - console.log("ID为1的用户:", user); - - // 条件查询 - const activeUsers = await userService.findWhere("status", "active"); - console.log("活跃用户:", activeUsers); - - return { allUsers, user, activeUsers }; - }, - - // 分页查询 - async paginateUsers() { - const result = await userService.paginate(1, 5); // 第1页,每页5条 - console.log("分页结果:", result); - return result; - }, - - // 更新用户 - async updateUser() { - const count = await userService.update(1, { - name: "张三(已更新)", - status: "inactive", - }); - console.log("更新用户数量:", count); - return count; - }, - - // 删除用户 - async deleteUser() { - await userService.delete(1); - console.log("删除用户成功"); - }, -}; - -// 2. 消息操作示例 -export const messageExamples = { - // 创建消息 - async createMessage() { - const messageId = await messageService.create({ - userId: 1, - content: "这是一条测试消息", - type: "text", - isRead: false, - }); - console.log("创建消息成功,ID:", messageId); - return messageId; - }, - - // 查询未读消息 - async findUnreadMessages() { - const unreadMessages = await messageService.findWhere("isRead", false); - console.log("未读消息:", unreadMessages); - return unreadMessages; - }, - - // 标记消息为已读 - async markMessageAsRead() { - const count = await messageService.update(1, { isRead: true }); - console.log("标记已读消息数量:", count); - return count; - }, - - // 获取最近消息 - async getRecentMessages() { - const recentMessages = await messageService.findAllSorted( - "createdAt", - "desc", - ); - console.log("最近消息:", recentMessages.slice(0, 10)); - return recentMessages.slice(0, 10); - }, -}; - -// 3. 设置操作示例 -export const settingExamples = { - // 保存设置 - async saveSetting() { - const settingId = await settingService.create({ - key: "theme", - value: "dark", - category: "appearance", - }); - console.log("保存设置成功,ID:", settingId); - return settingId; - }, - - // 批量保存设置 - async saveMultipleSettings() { - const settingIds = await settingService.createMany([ - { key: "language", value: "zh-CN", category: "general" }, - { key: "fontSize", value: 14, category: "appearance" }, - { key: "autoSave", value: true, category: "editor" }, - ]); - console.log("批量保存设置成功,IDs:", settingIds); - return settingIds; - }, - - // 查询设置 - async getSettings() { - // 查询所有设置 - const allSettings = await settingService.findAll(); - console.log("所有设置:", allSettings); - - // 按分类查询 - const appearanceSettings = await settingService.findWhere( - "category", - "appearance", - ); - console.log("外观设置:", appearanceSettings); - - return { allSettings, appearanceSettings }; - }, -}; - -// ============= 业务方法示例 ============= - -// 4. 用户业务操作示例 -export const userBusinessExamples = { - // 根据邮箱查找用户 - async findUserByEmail() { - const user = await userBusinessService.findByEmail("zhangsan@example.com"); - console.log("根据邮箱查找的用户:", user); - return user; - }, - - // 查找活跃用户 - async findActiveUsers() { - const activeUsers = await userBusinessService.findActiveUsers(); - console.log("活跃用户列表:", activeUsers); - return activeUsers; - }, - - // 搜索用户 - async searchUsers() { - const users = await userBusinessService.searchByName("张"); - console.log("搜索结果:", users); - return users; - }, - - // 更新用户状态 - async updateUserStatus() { - const count = await userBusinessService.updateStatus(1, "active"); - console.log("更新用户状态数量:", count); - return count; - }, -}; - -// 5. 消息业务操作示例 -export const messageBusinessExamples = { - // 查找用户消息 - async findUserMessages() { - const messages = await messageBusinessService.findByUserId(1); - console.log("用户消息:", messages); - return messages; - }, - - // 查找未读消息 - async findUnreadMessages() { - const unreadMessages = await messageBusinessService.findUnreadMessages(); - console.log("未读消息:", unreadMessages); - return unreadMessages; - }, - - // 标记消息为已读 - async markAsRead() { - const count = await messageBusinessService.markAsRead(1); - console.log("标记已读数量:", count); - return count; - }, - - // 标记用户所有消息为已读 - async markAllAsRead() { - const count = await messageBusinessService.markAllAsRead(1); - console.log("标记用户所有消息已读数量:", count); - return count; - }, - - // 获取最近消息 - async getRecentMessages() { - const messages = await messageBusinessService.getRecentMessages(20); - console.log("最近20条消息:", messages); - return messages; - }, -}; - -// 6. 设置业务操作示例 -export const settingBusinessExamples = { - // 获取设置值 - async getSetting() { - const theme = await settingBusinessService.getSetting("theme"); - console.log("主题设置:", theme); - return theme; - }, - - // 设置值 - async setSetting() { - const settingId = await settingBusinessService.setSetting( - "theme", - "light", - "appearance", - ); - console.log("设置主题成功,ID:", settingId); - return settingId; - }, - - // 获取分类设置 - async getSettingsByCategory() { - const settings = - await settingBusinessService.getSettingsByCategory("appearance"); - console.log("外观设置:", settings); - return settings; - }, - - // 删除设置 - async deleteSetting() { - await settingBusinessService.deleteSetting("oldSetting"); - console.log("删除设置成功"); - }, -}; - -// ============= 数据库工具示例 ============= - -// 7. 数据库工具操作示例 -export const databaseUtilsExamples = { - // 获取数据库统计信息 - async getStats() { - const stats = await DatabaseUtils.getStats(); - console.log("数据库统计:", stats); - return stats; - }, - - // 健康检查 - async healthCheck() { - const health = await DatabaseUtils.healthCheck(); - console.log("数据库健康状态:", health); - return health; - }, - - // 导出数据 - async exportData() { - const jsonData = await DatabaseUtils.exportData(); - console.log("导出的数据长度:", jsonData.length); - return jsonData; - }, - - // 备份到文件 - async backupToFile() { - await DatabaseUtils.backupToFile(); - console.log("备份文件下载成功"); - }, - - // 从文件恢复(需要文件输入) - async restoreFromFile(file: File) { +/** + * 数据库使用示例 + */ +export class DatabaseExamples { + /** + * 初始化数据库 + */ + static async initDatabase() { try { - await DatabaseUtils.restoreFromFile(file); - console.log("数据恢复成功"); + await db.open(); + console.log("数据库初始化成功"); } catch (error) { - console.error("数据恢复失败:", error); + console.error("数据库初始化失败:", error); } - }, + } - // 清空所有数据 - async clearAllData() { - const confirmed = confirm("确定要清空所有数据吗?此操作不可恢复!"); - if (confirmed) { - await DatabaseUtils.clearAllData(); - console.log("所有数据已清空"); - } - }, -}; + /** + * 客服用户操作示例 + */ + static async kfUserExamples() { + console.log("=== 客服用户操作示例 ==="); -// ============= 高级查询示例 ============= - -// 8. 高级查询示例 -export const advancedQueryExamples = { - // 复杂条件查询 - async complexQuery() { - // 查询活跃用户的未读消息 - const activeUsers = await db.users - .where("status") - .equals("active") - .toArray(); - const activeUserIds = activeUsers.map(user => user.id!); - const unreadMessages = await db.messages - .where("userId") - .anyOf(activeUserIds) - .and(msg => !msg.isRead) - .toArray(); - - console.log("活跃用户的未读消息:", unreadMessages); - return unreadMessages; - }, - - // 统计查询 - async statisticsQuery() { - const stats = { - totalUsers: await db.users.count(), - activeUsers: await db.users.where("status").equals("active").count(), - totalMessages: await db.messages.count(), - unreadMessages: await db.messages.where("isRead").equals(false).count(), - messagesThisWeek: await db.messages - .where("createdAt") - .above(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)) - .count(), + // 模拟从接口获取的数据(包含服务器ID) + const serverKfUser = { + id: 1001, // 服务器返回的ID + tenantId: 1, + wechatId: "test_wechat_001", + nickname: "测试客服", + alias: "客服小王", + avatar: "https://example.com/avatar.jpg", + gender: 1, + region: "北京", + signature: "专业客服,为您服务", + bindQQ: "123456789", + bindEmail: "test@example.com", + bindMobile: "13800138000", + createTime: new Date().toISOString(), + currentDeviceId: 1, + isDeleted: false, + deleteTime: "", + groupId: 1, + memo: "优秀客服", + wechatVersion: "8.0.0", + labels: ["VIP客服", "专业"], + lastUpdateTime: new Date().toISOString(), + isOnline: true, }; - console.log("统计信息:", stats); - return stats; - }, - - // 事务操作 - async transactionExample() { try { - await db.transaction("rw", [db.users, db.messages], async () => { - // 创建用户 - const userId = await db.users.add({ - name: "事务用户", - email: "transaction@example.com", - status: "active", - }); - - // 为该用户创建欢迎消息 - await db.messages.add({ - userId, - content: "欢迎使用我们的应用!", - type: "text", - isRead: false, - }); - - console.log("事务执行成功"); - }); - } catch (error) { - console.error("事务执行失败:", error); - } - }, -}; - -// ============= 完整使用流程示例 ============= - -// 9. 完整的应用场景示例 -export const fullScenarioExample = { - // 模拟用户注册和使用流程 - async simulateUserFlow() { - console.log("=== 开始模拟用户流程 ==="); - - try { - // 1. 用户注册 - const userId = await userBusinessService.create({ - name: "新用户", - email: "newuser@example.com", - status: "active", - }); - console.log("1. 用户注册成功,ID:", userId); - - // 2. 设置用户偏好 - await settingBusinessService.setSetting( - "theme", - "dark", - "user-" + userId, - ); - await settingBusinessService.setSetting( - "language", - "zh-CN", - "user-" + userId, - ); - console.log("2. 用户偏好设置完成"); - - // 3. 发送欢迎消息 - const messageId = await messageBusinessService.create({ + // 使用新方法添加客服用户(处理服务器ID映射) + const userId = await kfUserService.createWithServerId(serverKfUser); + console.log( + "创建客服用户成功,本地ID:", userId, - content: "欢迎加入我们的平台!", - type: "text", - isRead: false, + "服务器ID:", + serverKfUser.id, + ); + + // 查询单个用户(按本地ID) + const user = await kfUserService.findById(userId); + console.log("查询用户:", user); + + // 根据服务器ID查询 + const userByServerId = await kfUserService.findByServerId(1001); + console.log("根据服务器ID查询用户:", userByServerId); + + // 按条件查询 + const onlineUsers = await kfUserService.findWhere("isOnline", true); + console.log("在线用户数量:", onlineUsers.length); + + // 按租户查询 + const tenantUsers = await kfUserService.findWhere("tenantId", 1); + console.log("租户用户数量:", tenantUsers.length); + + // 更新用户信息 + await kfUserService.update(userId, { + nickname: "更新后的昵称", + lastUpdateTime: new Date().toISOString(), }); - console.log("3. 欢迎消息发送成功,ID:", messageId); + console.log("更新用户成功"); - // 4. 用户查看消息 - const userMessages = await messageBusinessService.findByUserId(userId); - console.log("4. 用户消息列表:", userMessages); - - // 5. 标记消息为已读 - await messageBusinessService.markAsRead(messageId); - console.log("5. 消息已标记为已读"); - - // 6. 获取用户统计信息 - const userStats = { - totalMessages: await messageService.countWhere("userId", userId), - unreadMessages: await db.messages - .where("userId") - .equals(userId) - .and(msg => !msg.isRead) - .count(), - }; - console.log("6. 用户统计信息:", userStats); - - console.log("=== 用户流程模拟完成 ==="); - return { userId, messageId, userStats }; + // 分页查询 + const paginatedUsers = await kfUserService.findWithPagination(1, 5); + console.log("分页查询结果:", paginatedUsers); } catch (error) { - console.error("用户流程模拟失败:", error); - throw error; + console.error("客服用户操作失败:", error); } - }, -}; + } -// 导出所有示例 -export const allExamples = { - userExamples, - messageExamples, - settingExamples, - userBusinessExamples, - messageBusinessExamples, - settingBusinessExamples, - databaseUtilsExamples, - advancedQueryExamples, - fullScenarioExample, -}; + /** + * 群组操作示例 + */ + static async groupExamples() { + console.log("=== 群组操作示例 ==="); -// 快速测试函数 -export async function quickTest() { - console.log("=== 开始快速测试 ==="); + // 模拟从接口获取的群组数据 + const serverGroup = { + id: 2001, // 服务器返回的ID + wechatAccountId: 1, + tenantId: 1, + accountId: 1, + chatroomId: "chatroom_001", + chatroomOwner: "owner_001", + conRemark: "测试群组", + nickname: "产品讨论群", + chatroomAvatar: "https://example.com/group-avatar.jpg", + groupId: 1, + config: { + chat: true, + }, + unreadCount: 0, + notice: "欢迎加入产品讨论群", + selfDisplyName: "群管理员", + }; - try { - // 健康检查 - const health = await DatabaseUtils.healthCheck(); - console.log("数据库健康状态:", health); + try { + // 使用新方法创建群组 + const groupId = await groupService.createWithServerId(serverGroup); + console.log( + "创建群组成功,本地ID:", + groupId, + "服务器ID:", + serverGroup.id, + ); - // 创建测试数据 - const userId = await userBusinessService.create({ - name: "测试用户", - email: "test@example.com", - status: "active", - }); + // 查询群组 + const groups = await groupService.findWhere("tenantId", 1); + console.log("租户群组数量:", groups.length); - const messageId = await messageBusinessService.create({ - userId, - content: "测试消息", - type: "text", - isRead: false, - }); + // 查询有未读消息的群组 + const unreadGroups = await groupService.findWhereGreaterThan( + "unreadCount", + 0, + ); + console.log("有未读消息的群组:", unreadGroups.length); - // 查询测试 - const user = await userBusinessService.findById(userId); - const message = await messageBusinessService.findById(messageId); + // 批量更新群组 + await groupService.updateMany([ + { + id: groupId, + data: { unreadCount: 5, notice: "更新的群公告" }, + }, + ]); + console.log("批量更新群组成功"); + } catch (error) { + console.error("群组操作失败:", error); + } + } - console.log("创建的用户:", user); - console.log("创建的消息:", message); + /** + * 联系人操作示例 + */ + static async contractExamples() { + console.log("=== 联系人操作示例 ==="); - // 统计信息 - const stats = await DatabaseUtils.getStats(); - console.log("数据库统计:", stats); + // 模拟从接口获取的联系人数据 + const serverContract = { + id: 3001, // 服务器返回的ID + wechatAccountId: 1, + wechatId: "contact_001", + alias: "张三", + conRemark: "重要客户", + nickname: "张总", + quanPin: "zhangsan", + avatar: "https://example.com/contact-avatar.jpg", + gender: 1, + region: "上海", + addFrom: 1, + phone: "13900139000", + labels: ["VIP客户", "重点关注"], + signature: "专业人士", + accountId: 1, + extendFields: null, + city: "上海", + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 1, + thirdParty: null, + additionalPicture: "", + desc: "优质客户", + config: { + chat: true, + }, + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + }; - console.log("=== 快速测试完成 ==="); - return { userId, messageId, stats }; - } catch (error) { - console.error("快速测试失败:", error); - throw error; + try { + // 使用新方法创建联系人 + const contractId = + await contractService.createWithServerId(serverContract); + console.log( + "创建联系人成功,本地ID:", + contractId, + "服务器ID:", + serverContract.id, + ); + + // 模糊查询(按昵称开头) + const searchResults = await contractService.findWhereStartsWith( + "nickname", + "张", + ); + console.log("姓张的联系人:", searchResults.length); + + // 多条件查询 + const vipContacts = await contractService.findWhereMultiple([ + { field: "tenantId", operator: "equals", value: 1 }, + { field: "isPassed", operator: "equals", value: true }, + ]); + console.log("VIP联系人数量:", vipContacts.length); + + // 按更新时间排序 + const sortedContacts = await contractService.findAllSorted( + "lastUpdateTime", + "desc", + ); + console.log("最近更新的联系人:", sortedContacts.slice(0, 3)); + + // 统计联系人数量 + const totalCount = await contractService.count(); + const tenantCount = await contractService.countWhere("tenantId", 1); + console.log(`总联系人数: ${totalCount}, 租户联系人数: ${tenantCount}`); + } catch (error) { + console.error("联系人操作失败:", error); + } + } + + /** + * 新联系人列表操作示例 + */ + static async newContactListExamples() { + console.log("=== 新联系人列表操作示例 ==="); + + // 模拟从接口获取的联系人分组数据 + const serverContactGroup = { + id: 4001, // 服务器返回的ID + groupName: "已购买", + contacts: [ + { + id: 3002, // 联系人的服务器ID + wechatAccountId: 1, + wechatId: "buyer_001", + alias: "李四", + conRemark: "已购买客户", + nickname: "李总", + quanPin: "lisi", + gender: 1, + region: "深圳", + addFrom: 2, + phone: "13700137000", + labels: ["已购买"], + signature: "企业家", + accountId: 1, + extendFields: null, + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 2, + thirdParty: null, + additionalPicture: "", + desc: "已购买产品的客户", + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + }, + ], + }; + + try { + // 使用新方法创建联系人分组 + const groupId = + await newContactListService.createWithServerId(serverContactGroup); + console.log( + "创建联系人分组成功,本地ID:", + groupId, + "服务器ID:", + serverContactGroup.id, + ); + + // 查询所有分组 + const allGroups = await newContactListService.findAll(); + console.log("所有联系人分组:", allGroups); + + // 按分组名称查询 + const purchasedGroups = await newContactListService.findWhere( + "groupName", + "已购买", + ); + console.log("已购买分组:", purchasedGroups); + + // 更新分组(添加更多联系人) + const updatedContacts = [ + ...serverContactGroup.contacts, + { + id: 3003, + wechatAccountId: 1, + conRemark: "已购买客户", + quanPin: "wangwu", + gender: 1, + region: "广州", + addFrom: 2, + phone: "13800000003", + labels: ["已购买"], + signature: "企业家", + accountId: 1, + extendFields: null, + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 2, + thirdParty: null, + additionalPicture: "", + desc: "已购买产品的客户", + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + wechatId: "buyer_002", + alias: "王五", + nickname: "王总", + } as ContractData, + ]; + + await newContactListService.update(groupId, { + contacts: updatedContacts, + }); + console.log("更新联系人分组成功"); + } catch (error) { + console.error("新联系人列表操作失败:", error); + } + } + + /** + * 高级查询示例 + */ + static async advancedQueryExamples() { + console.log("=== 高级查询示例 ==="); + + try { + // 范围查询:查询最近一周更新的联系人 + const oneWeekAgo = new Date( + Date.now() - 7 * 24 * 60 * 60 * 1000, + ).toISOString(); + const now = new Date().toISOString(); + const recentContacts = await contractService.findWhereBetween( + "lastUpdateTime", + oneWeekAgo, + now, + ); + console.log("最近一周更新的联系人:", recentContacts.length); + + // IN 查询:查询指定租户的数据 + const tenantIds = [1, 2, 3]; + const multiTenantUsers = await kfUserService.findWhereIn( + "tenantId", + tenantIds, + ); + console.log("多租户用户数量:", multiTenantUsers.length); + + // 不等于查询:查询非删除状态的用户 + const activeUsers = await kfUserService.findWhereNot("isDeleted", true); + console.log("活跃用户数量:", activeUsers.length); + + // 复合条件查询:在线且有分组的用户 + const onlineGroupUsers = await kfUserService.findWhereMultiple([ + { field: "isOnline", operator: "equals", value: true }, + { field: "groupId", operator: "above", value: 0 }, + ]); + console.log("在线且有分组的用户:", onlineGroupUsers.length); + } catch (error) { + console.error("高级查询失败:", error); + } + } + + /** + * 批量操作示例 + */ + static async batchOperationExamples() { + console.log("=== 批量操作示例 ==="); + + try { + // 模拟从接口获取的批量联系人数据 + const batchServerContacts = [ + { + id: 3003, // 服务器ID + wechatAccountId: 1, + wechatId: "batch_001", + alias: "批量用户1", + conRemark: "批量导入", + nickname: "用户1", + quanPin: "yonghu1", + gender: 1, + region: "北京", + addFrom: 3, + phone: "13800000001", + labels: ["批量导入"], + signature: "", + accountId: 1, + extendFields: null, + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 1, + thirdParty: null, + additionalPicture: "", + desc: "批量导入的用户", + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + }, + { + id: 3004, // 服务器ID + wechatAccountId: 1, + wechatId: "batch_002", + alias: "批量用户2", + conRemark: "批量导入", + nickname: "用户2", + quanPin: "yonghu2", + gender: 2, + region: "上海", + addFrom: 3, + phone: "13800000002", + labels: ["批量导入"], + signature: "", + accountId: 1, + extendFields: null, + lastUpdateTime: new Date().toISOString(), + isPassed: true, + tenantId: 1, + groupId: 1, + thirdParty: null, + additionalPicture: "", + desc: "批量导入的用户", + lastMessageTime: Date.now(), + unreadCount: 0, + duplicate: false, + }, + ]; + + const batchIds = + await contractService.createManyWithServerId(batchServerContacts); + console.log("批量创建联系人成功,本地IDs:", batchIds); + + // 批量更新 + const updateData = batchIds.map(id => ({ + id, + data: { + desc: "批量更新的描述", + lastUpdateTime: new Date().toISOString(), + }, + })); + + await contractService.updateMany(updateData); + console.log("批量更新成功"); + } catch (error) { + console.error("批量操作失败:", error); + } + } + + /** + * 数据清理示例 + */ + static async dataCleanupExamples() { + console.log("=== 数据清理示例 ==="); + + try { + // 清理测试数据(谨慎使用) + const testUsers = await kfUserService.findWhereStartsWith( + "wechatId", + "test_", + ); + console.log("找到测试用户:", testUsers.length); + + // 删除特定测试数据 + for (const user of testUsers) { + if (user.id) { + await kfUserService.delete(user.id); + } + } + console.log("清理测试用户完成"); + + // 统计清理后的数据 + const remainingCount = await kfUserService.count(); + console.log("剩余用户数量:", remainingCount); + } catch (error) { + console.error("数据清理失败:", error); + } + } + + /** + * 运行所有示例 + */ + static async runAllExamples() { + console.log("开始运行数据库操作示例..."); + + await this.initDatabase(); + await this.kfUserExamples(); + await this.groupExamples(); + await this.contractExamples(); + await this.newContactListExamples(); + await this.advancedQueryExamples(); + await this.batchOperationExamples(); + // await this.dataCleanupExamples(); // 谨慎使用 + + console.log("所有示例运行完成!"); } } + +// 导出便捷方法 +export const runDatabaseExamples = () => DatabaseExamples.runAllExamples(); + +// 如果直接运行此文件,执行示例 +if (typeof window !== "undefined" && (window as any).__RUN_DB_EXAMPLES__) { + runDatabaseExamples(); +} diff --git a/Cunkebao/src/utils/db-test.ts b/Cunkebao/src/utils/db-test.ts new file mode 100644 index 00000000..3a84f3b9 --- /dev/null +++ b/Cunkebao/src/utils/db-test.ts @@ -0,0 +1,146 @@ +/** + * 数据库版本升级测试脚本 + * 用于验证数据库版本升级逻辑是否正常工作 + */ + +import { db } from "./db"; + +// 重置数据库(完全删除并重新创建) +export async function resetDatabase() { + try { + console.log("开始重置数据库..."); + + // 关闭数据库连接 + if (db.isOpen()) { + db.close(); + } + + // 删除数据库 + await db.delete(); + console.log("旧数据库已删除"); + + // 重新打开数据库(这会创建新的数据库) + await db.open(); + + console.log("数据库重置成功!"); + console.log("当前数据库版本:", db.verno); + console.log("数据库名称:", db.name); + + return { + success: true, + version: db.verno, + tables: db.tables.map(table => table.name), + message: "数据库重置成功", + }; + } catch (error) { + console.error("数据库重置失败:", error); + return { + success: false, + error: error instanceof Error ? error.message : String(error), + message: "数据库重置失败", + }; + } +} + +// 测试数据库初始化和版本升级 +export async function testDatabaseUpgrade() { + try { + console.log("开始测试数据库初始化..."); + + // 首先尝试正常打开数据库 + try { + await db.open(); + } catch (upgradeError) { + // 如果遇到升级错误,尝试重置数据库 + if ( + upgradeError.message && + upgradeError.message.includes("primary key") + ) { + console.log("检测到主键冲突,尝试重置数据库..."); + const resetResult = await resetDatabase(); + if (!resetResult.success) { + throw new Error(`数据库重置失败: ${resetResult.error}`); + } + } else { + throw upgradeError; + } + } + + console.log("数据库初始化成功!"); + console.log("当前数据库版本:", db.verno); + console.log("数据库名称:", db.name); + + // 检查表是否存在 + const tables = db.tables.map(table => table.name); + console.log("数据库表:", tables); + + // 测试基本操作 + const testData = { + tenantId: 1, // 修正为number类型 + wechatId: "test-wechat-id", + nickname: "测试用户", + alias: "测试别名", + }; + + // 测试创建数据 + const userId = await db.kfUsers.add({ + ...testData, + id: 0, // 添加必需的id字段 + currentDeviceId: 0, + isDeleted: false, + deleteTime: "", + groupId: 0, + memo: "", // 备注信息 + wechatVersion: "", + labels: [], + lastUpdateTime: new Date().toISOString(), // 修复语法错误,使用字符串类型 + serverId: "test-server-id-001", // 提供有意义的测试值 + // 移除不属于KfUserListData接口的字段 + signature: "", + bindQQ: "", + bindEmail: "", + bindMobile: "", + bindWeixin: "", + bindAlipay: "", + bindTaobao: "", + bindJd: "", + bindDouyin: "", + bindKuaishou: "", + bindBilibili: "", + avatar: "", + gender: 0, + region: "", + createTime: new Date().toISOString(), // 使用字符串类型 + }); + console.log("创建测试用户成功,ID:", userId); + + // 测试查询数据 + const user = await db.kfUsers.get(userId); + console.log("查询测试用户:", user); + + // 清理测试数据 + await db.kfUsers.delete(userId); + console.log("清理测试数据完成"); + + return { + success: true, + version: db.verno, + tables: tables, + message: "数据库版本升级测试通过", + }; + } catch (error) { + console.error("数据库测试失败:", error); + return { + success: false, + error: error instanceof Error ? error.message : String(error), + message: "数据库版本升级测试失败", + }; + } +} + +// 如果直接运行此文件,执行测试 +if (typeof window === "undefined") { + testDatabaseUpgrade().then(result => { + console.log("测试结果:", result); + }); +} diff --git a/Cunkebao/src/utils/db.ts b/Cunkebao/src/utils/db.ts index 6f6fc9ff..2ec84c0b 100644 --- a/Cunkebao/src/utils/db.ts +++ b/Cunkebao/src/utils/db.ts @@ -1,102 +1,111 @@ +/** + * 数据库工具类 - 解决服务器ID与本地自增主键冲突问题 + * + * 问题描述: + * 接口返回的数据包含id字段,直接存储到数据库会与Dexie的自增主键(++id)产生冲突 + * + * 解决方案: + * 1. 将服务器返回的id字段映射为serverId字段存储 + * 2. 数据库使用自增的id作为主键 + * 3. 提供专门的方法处理服务器数据的存储和查询 + * + * 使用方法: + * - 存储接口数据:使用 createWithServerId() 或 createManyWithServerId() + * - 查询服务器数据:使用 findByServerId() + * - 常规操作:使用原有的 create(), findById() 等方法 + * + * 示例: + * const serverData = { id: 1001, name: '测试', ... }; // 接口返回的数据 + * const localId = await service.createWithServerId(serverData); // 存储,返回本地ID + * const data = await service.findByServerId(1001); // 根据服务器ID查询 + */ + import Dexie, { Table } from "dexie"; import { KfUserListData, GroupData, ContractData } from "@/pages/pc/ckbox/data"; -// 定义数据库表结构接口 -export interface BaseEntity { +// 扩展数据类型,添加serverId字段 +export interface KfUserWithServerId extends KfUserListData { + serverId?: number | string; // 服务器返回的原始ID +} + +export interface GroupWithServerId extends GroupData { + serverId?: number | string; // 服务器返回的原始ID +} + +export interface ContractWithServerId extends ContractData { + serverId?: number | string; // 服务器返回的原始ID +} + +// 新联系人列表数据接口 +export interface NewContactListData { id?: number; - createdAt?: Date; - updatedAt?: Date; -} - -export interface User extends BaseEntity { - name: string; - email: string; - avatar?: string; - status: "active" | "inactive"; -} - -export interface Message extends BaseEntity { - userId: number; - content: string; - type: "text" | "image" | "file"; - isRead: boolean; -} - -export interface ChatRoom extends BaseEntity { - name: string; - description?: string; - memberIds: number[]; - lastMessageAt?: Date; -} - -export interface Setting extends BaseEntity { - key: string; - value: any; - category: string; + serverId?: number | string; // 服务器返回的原始ID + groupName: string; + contacts: ContractData[] | GroupData[]; } // 数据库类 -class AppDatabase extends Dexie { - users!: Table; - messages!: Table; - chatRooms!: Table; - settings!: Table; +class CunkebaoDatabase extends Dexie { + kfUsers!: Table; + groups!: Table; + contracts!: Table; + newContractList!: Table; constructor() { super("CunkebaoDatabase"); + // 版本1:初始版本(不包含serverId字段) this.version(1).stores({ - users: "++id, name, email, status, createdAt", - messages: "++id, userId, type, isRead, createdAt", - chatRooms: "++id, name, lastMessageAt, createdAt", - settings: "++id, key, category, createdAt", + kfUsers: + "++id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline", + groups: + "++id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar, groupId, config, unreadCount, notice, selfDisplyName", + contracts: + "++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, unreadCount, duplicate", + newContractList: "++id, groupName, contacts", }); - // 自动添加时间戳 - this.users.hook("creating", (primKey, obj, trans) => { - obj.createdAt = new Date(); - obj.updatedAt = new Date(); - }); - - this.users.hook("updating", (modifications, primKey, obj, trans) => { - modifications.updatedAt = new Date(); - }); - - this.messages.hook("creating", (primKey, obj, trans) => { - obj.createdAt = new Date(); - obj.updatedAt = new Date(); - }); - - this.chatRooms.hook("creating", (primKey, obj, trans) => { - obj.createdAt = new Date(); - obj.updatedAt = new Date(); - }); - - this.settings.hook("creating", (primKey, obj, trans) => { - obj.createdAt = new Date(); - obj.updatedAt = new Date(); - }); + // 版本2:添加serverId字段支持 + this.version(2) + .stores({ + kfUsers: + "++id, serverId, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline", + groups: + "++id, serverId, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar, groupId, config, unreadCount, notice, selfDisplyName", + contracts: + "++id, serverId, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, unreadCount, duplicate", + newContractList: "++id, serverId, groupName, contacts", + }) + .upgrade(tx => { + // 数据库升级逻辑:为现有数据添加serverId字段(可选) + console.log("数据库升级到版本2:添加serverId字段支持"); + // 注意:这里不需要迁移数据,因为serverId是可选字段 + // 如果需要迁移现有数据,可以在这里添加相应逻辑 + }); } } // 创建数据库实例 -export const db = new AppDatabase(); +export const db = new CunkebaoDatabase(); -// 通用数据库操作类 -export class DatabaseService { +// 简单的数据库操作类 +export class DatabaseService { constructor(private table: Table) {} // 基础 CRUD 操作 - async create( - data: Omit, - ): Promise { + async create(data: Omit): Promise { return await this.table.add(data as T); } - async createMany( - dataList: Omit[], - ): Promise { - return await this.table.bulkAdd(dataList as T[], { allKeys: true }); + // 创建数据(处理服务器ID映射) + // 用于存储从接口获取的数据,将服务器的id字段映射为serverId,避免与数据库自增主键冲突 + async createWithServerId(data: any): Promise { + const { id, ...restData } = data; + const dataToInsert = { + ...restData, + serverId: id, // 将服务器的id映射为serverId + }; + return await this.table.add(dataToInsert as T); } async findById(id: number): Promise { @@ -107,49 +116,46 @@ export class DatabaseService { return await this.table.toArray(); } - async findByIds(ids: number[]): Promise { - return await this.table.where("id").anyOf(ids).toArray(); - } - - async update( - id: number, - data: Partial>, - ): Promise { - return await this.table.update(id, data); + async update(id: number, data: Partial): Promise { + return await this.table.update(id, data as any); } async updateMany( - updates: { id: number; data: Partial> }[], + dataList: { id: number; data: Partial }[], ): Promise { return await this.table.bulkUpdate( - updates.map(u => ({ key: u.id, changes: u.data })), + dataList.map(item => ({ + key: item.id, + changes: item.data as any, + })), ); } + async createMany(dataList: Omit[]): Promise { + return await this.table.bulkAdd(dataList as T[], { allKeys: true }); + } + + // 批量创建数据(处理服务器ID映射) + // 用于批量存储从接口获取的数据,将服务器的id字段映射为serverId + async createManyWithServerId(dataList: any[]): Promise { + const processedData = dataList.map(item => { + const { id, ...restData } = item; + return { + ...restData, + serverId: id, // 将服务器的id映射为serverId + }; + }); + return await this.table.bulkAdd(processedData as T[], { allKeys: true }); + } + async delete(id: number): Promise { await this.table.delete(id); } - async deleteMany(ids: number[]): Promise { - await this.table.bulkDelete(ids); - } - - async deleteAll(): Promise { + async clear(): Promise { await this.table.clear(); } - // 分页查询 - async paginate( - page: number = 1, - limit: number = 10, - ): Promise<{ data: T[]; total: number; page: number; limit: number }> { - const offset = (page - 1) * limit; - const total = await this.table.count(); - const data = await this.table.offset(offset).limit(limit).toArray(); - - return { data, total, page, limit }; - } - // 条件查询 async findWhere(field: keyof T, value: any): Promise { return await this.table @@ -158,6 +164,13 @@ export class DatabaseService { .toArray(); } + // 根据服务器ID查询 + // 用于根据原始的服务器ID查找数据 + async findByServerId(serverId: any): Promise { + return await this.table.where("serverId").equals(serverId).first(); + } + + // 多值查询(IN 查询) async findWhereIn(field: keyof T, values: any[]): Promise { return await this.table .where(field as string) @@ -165,6 +178,23 @@ export class DatabaseService { .toArray(); } + // 范围查询 + async findWhereBetween(field: keyof T, min: any, max: any): Promise { + return await this.table + .where(field as string) + .between(min, max) + .toArray(); + } + + // 模糊查询(以指定字符串开头) + async findWhereStartsWith(field: keyof T, prefix: string): Promise { + return await this.table + .where(field as string) + .startsWith(prefix) + .toArray(); + } + + // 不等于查询 async findWhereNot(field: keyof T, value: any): Promise { return await this.table .where(field as string) @@ -172,6 +202,68 @@ export class DatabaseService { .toArray(); } + // 大于查询 + async findWhereGreaterThan(field: keyof T, value: any): Promise { + return await this.table + .where(field as string) + .above(value) + .toArray(); + } + + // 小于查询 + async findWhereLessThan(field: keyof T, value: any): Promise { + return await this.table + .where(field as string) + .below(value) + .toArray(); + } + + // 复合条件查询 + async findWhereMultiple( + conditions: { + field: keyof T; + operator: "equals" | "above" | "below" | "startsWith"; + value: any; + }[], + ): Promise { + let collection = this.table.toCollection(); + + for (const condition of conditions) { + const { field, operator, value } = condition; + collection = collection.and(item => { + const fieldValue = (item as any)[field]; + switch (operator) { + case "equals": + return fieldValue === value; + case "above": + return fieldValue > value; + case "below": + return fieldValue < value; + case "startsWith": + return ( + typeof fieldValue === "string" && fieldValue.startsWith(value) + ); + default: + return true; + } + }); + } + + return await collection.toArray(); + } + + // 分页查询 + async findWithPagination( + page: number = 1, + limit: number = 10, + ): Promise<{ data: T[]; total: number; page: number; limit: number }> { + const offset = (page - 1) * limit; + const total = await this.table.count(); + const data = await this.table.offset(offset).limit(limit).toArray(); + + return { data, total, page, limit }; + } + // 排序查询 async findAllSorted( field: keyof T, @@ -183,264 +275,24 @@ export class DatabaseService { : await collection.toArray(); } - // 搜索功能 - async search(field: keyof T, keyword: string): Promise { - return await this.table - .where(field as string) - .startsWithIgnoreCase(keyword) - .toArray(); - } - - // 统计功能 + // 统计 async count(): Promise { return await this.table.count(); } + // 条件统计 async countWhere(field: keyof T, value: any): Promise { return await this.table .where(field as string) .equals(value) .count(); } - - // 存在性检查 - async exists(id: number): Promise { - const item = await this.table.get(id); - return !!item; - } - - async existsWhere(field: keyof T, value: any): Promise { - const count = await this.table - .where(field as string) - .equals(value) - .count(); - return count > 0; - } } // 创建各表的服务实例 -export const userService = new DatabaseService(db.users); -export const messageService = new DatabaseService(db.messages); -export const chatRoomService = new DatabaseService(db.chatRooms); -export const settingService = new DatabaseService(db.settings); - -// 专门的业务方法 -export class UserService extends DatabaseService { - constructor() { - super(db.users); - } - - async findByEmail(email: string): Promise { - return await db.users.where("email").equals(email).first(); - } - - async findActiveUsers(): Promise { - return await db.users.where("status").equals("active").toArray(); - } - - async searchByName(name: string): Promise { - return await db.users.where("name").startsWithIgnoreCase(name).toArray(); - } - - async updateStatus( - id: number, - status: "active" | "inactive", - ): Promise { - return await this.update(id, { status }); - } -} - -export class MessageService extends DatabaseService { - constructor() { - super(db.messages); - } - - async findByUserId(userId: number): Promise { - return await db.messages.where("userId").equals(userId).toArray(); - } - - async findUnreadMessages(): Promise { - return await db.messages.where("isRead").equals(false).toArray(); - } - - async markAsRead(id: number): Promise { - return await this.update(id, { isRead: true }); - } - - async markAllAsRead(userId: number): Promise { - const messages = await db.messages - .where("userId") - .equals(userId) - .and(msg => !msg.isRead) - .toArray(); - const updates = messages.map(msg => ({ - id: msg.id!, - data: { isRead: true }, - })); - return await this.updateMany(updates); - } - - async getRecentMessages(limit: number = 50): Promise { - return await db.messages - .orderBy("createdAt") - .reverse() - .limit(limit) - .toArray(); - } -} - -export class SettingService extends DatabaseService { - constructor() { - super(db.settings); - } - - async getSetting(key: string): Promise { - const setting = await db.settings.where("key").equals(key).first(); - return setting?.value; - } - - async setSetting( - key: string, - value: any, - category: string = "general", - ): Promise { - const existing = await db.settings.where("key").equals(key).first(); - if (existing) { - return await this.update(existing.id!, { value }); - } else { - return await this.create({ key, value, category }); - } - } - - async getSettingsByCategory(category: string): Promise { - return await db.settings.where("category").equals(category).toArray(); - } - - async deleteSetting(key: string): Promise { - await db.settings.where("key").equals(key).delete(); - } -} - -// 数据库工具类 -export class DatabaseUtils { - // 数据导出 - static async exportData(): Promise { - const data = { - users: await db.users.toArray(), - messages: await db.messages.toArray(), - chatRooms: await db.chatRooms.toArray(), - settings: await db.settings.toArray(), - exportedAt: new Date().toISOString(), - }; - return JSON.stringify(data, null, 2); - } - - // 数据导入 - static async importData(jsonData: string): Promise { - try { - const data = JSON.parse(jsonData); - - await db.transaction( - "rw", - [db.users, db.messages, db.chatRooms, db.settings], - async () => { - if (data.users) await db.users.bulkPut(data.users); - if (data.messages) await db.messages.bulkPut(data.messages); - if (data.chatRooms) await db.chatRooms.bulkPut(data.chatRooms); - if (data.settings) await db.settings.bulkPut(data.settings); - }, - ); - } catch (error) { - throw new Error("导入数据失败: " + error); - } - } - - // 清空所有数据 - static async clearAllData(): Promise { - await db.transaction( - "rw", - [db.users, db.messages, db.chatRooms, db.settings], - async () => { - await db.users.clear(); - await db.messages.clear(); - await db.chatRooms.clear(); - await db.settings.clear(); - }, - ); - } - - // 获取数据库统计信息 - static async getStats(): Promise<{ - users: number; - messages: number; - chatRooms: number; - settings: number; - totalSize: number; - }> { - const [users, messages, chatRooms, settings] = await Promise.all([ - db.users.count(), - db.messages.count(), - db.chatRooms.count(), - db.settings.count(), - ]); - - // 估算数据库大小(简单估算) - const totalSize = users + messages + chatRooms + settings; - - return { users, messages, chatRooms, settings, totalSize }; - } - - // 数据库健康检查 - static async healthCheck(): Promise<{ - status: "healthy" | "error"; - message: string; - }> { - try { - await db.users.limit(1).toArray(); - return { status: "healthy", message: "数据库连接正常" }; - } catch (error) { - return { status: "error", message: "数据库连接异常: " + error }; - } - } - - // 数据备份到文件 - static async backupToFile(): Promise { - const data = await this.exportData(); - const blob = new Blob([data], { type: "application/json" }); - const url = URL.createObjectURL(blob); - - const a = document.createElement("a"); - a.href = url; - a.download = `cunkebao-backup-${new Date().toISOString().split("T")[0]}.json`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - } - - // 从文件恢复数据 - static async restoreFromFile(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = async e => { - try { - const jsonData = e.target?.result as string; - await this.importData(jsonData); - resolve(); - } catch (error) { - reject(error); - } - }; - reader.onerror = () => reject(new Error("文件读取失败")); - reader.readAsText(file); - }); - } -} - -// 创建业务服务实例 -export const userBusinessService = new UserService(); -export const messageBusinessService = new MessageService(); -export const settingBusinessService = new SettingService(); +export const kfUserService = new DatabaseService(db.kfUsers); +export const groupService = new DatabaseService(db.groups); +export const contractService = new DatabaseService(db.contracts); // 默认导出数据库实例 export default db;