/** * 数据库工具类 - 使用serverId作为主键的优化架构 * * 架构设计: * 1. 使用serverId作为数据库主键,直接对应接口返回的id字段 * 2. 保留原始的id字段,用于存储接口数据的完整性 * 3. 简化数据处理逻辑,避免ID映射的复杂性 * * 优势: * - 直接使用服务器ID作为主键,避免ID冲突 * - 保持数据的一致性和可追溯性 * - 简化查询逻辑,提高性能 * - 支持重复数据检测和去重 * * 使用方法: * - 存储接口数据:使用 createWithServerId() 或 createManyWithServerId() * - 查询数据:使用 findById(id) 根据原始ID查询,或 findByPrimaryKey(serverId) 根据主键查询 * - 批量查询:使用 findByIds([id1, id2, ...]) 根据原始ID批量查询 * - 内部操作:serverId作为主键用于数据库内部管理 * * 示例: * const serverData = { id: 1001, name: '测试', ... }; // 接口返回的数据 * const serverId = await service.createWithServerId(serverData); // 存储,返回serverId * const data = await service.findById(1001); // 根据原始ID查询(用户友好) * const dataByPK = await service.findByPrimaryKey(serverId); // 根据主键查询(内部使用) */ import Dexie, { Table } from "dexie"; import { KfUserListData, weChatGroup, ContractData, MessageListData, } from "@/pages/pc/ckbox/data"; // 数据类型定义,使用serverId作为主键 export interface KfUserWithServerId extends Omit { serverId: number | string; // 服务器ID作为主键 id?: number; // 接口数据的原始ID字段 } // 新联系人列表数据接口 export interface NewContactListData { serverId: number | string; // 服务器ID作为主键 id?: number; // 接口数据的原始ID字段 groupName: string; contacts: ContractData[]; weChatGroup: weChatGroup[]; } // 数据库类 class CunkebaoDatabase extends Dexie { kfUsers!: Table; weChatGroup!: Table; contracts!: Table; newContractList!: Table; messageList!: Table; constructor() { super("CunkebaoDatabase"); // 版本1:使用serverId作为主键的架构 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, unreadCount, 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, unreadCount, duplicate", newContractList: "serverId, id, groupName, contacts", messageList: "serverId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, unreadCount, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName", }); } } // 创建数据库实例 export const db = new CunkebaoDatabase(); // 简单的数据库操作类 export class DatabaseService { constructor(private table: Table) {} // 基础 CRUD 操作 - 使用serverId作为主键 async create(data: Omit): Promise { return await this.table.add(data as T); } // 创建数据(直接使用接口数据) // 接口数据的id字段直接作为serverId主键,原id字段保留 async createWithServerId(data: any): Promise { const dataToInsert = { ...data, serverId: data.id, // 使用接口的id作为serverId主键 }; return await this.table.add(dataToInsert as T); } // 根据原始ID查询(用户友好的查询方法) async findById(id: string | number): Promise { return await this.table.where("id").equals(id).first(); } // 根据serverId查询(内部主键查询) async findByPrimaryKey(serverId: string | number): Promise { return await this.table.get(serverId); } async findAll(): Promise { return await this.table.toArray(); } async update(serverId: string | number, data: Partial): Promise { return await this.table.update(serverId, data as any); } async updateMany( dataList: { serverId: string | number; data: Partial }[], ): Promise { return await this.table.bulkUpdate( dataList.map(item => ({ key: item.serverId, changes: item.data as any, })), ); } async createMany( dataList: Omit[], ): Promise<(string | number)[]> { return await this.table.bulkAdd(dataList as T[], { allKeys: true }); } // 批量创建数据(直接使用接口数据) // 接口数据的id字段直接作为serverId主键 async createManyWithServerId(dataList: any[]): Promise<(string | number)[]> { // 检查是否存在重复的serverId const serverIds = dataList.map(item => item.id); const existingData = await this.table .where("serverId") .anyOf(serverIds) .toArray(); const existingServerIds = new Set( existingData.map((item: any) => item.serverId), ); // 过滤掉已存在的数据 const newData = dataList.filter(item => !existingServerIds.has(item.id)); if (newData.length === 0) { console.log("所有数据都已存在,跳过插入"); return []; } const processedData = newData.map(item => ({ ...item, serverId: item.id, // 使用接口的id作为serverId主键 })); console.log( `插入 ${processedData.length} 条新数据,跳过 ${dataList.length - newData.length} 条重复数据`, ); return await this.table.bulkAdd(processedData as T[], { allKeys: true }); } async delete(serverId: string | number): Promise { await this.table.delete(serverId); } async clear(): Promise { await this.table.clear(); } // 条件查询 async findWhere(field: keyof T, value: any): Promise { return await this.table .where(field as string) .equals(value) .toArray(); } // 根据服务器ID查询(兼容性方法) async findByServerId(serverId: any): Promise { return await this.table.get(serverId); } // 根据原始ID批量查询 async findByIds(ids: (string | number)[]): Promise { return await this.table.where("id").anyOf(ids).toArray(); } // 多值查询(IN 查询) async findWhereIn(field: keyof T, values: any[]): Promise { return await this.table .where(field as string) .anyOf(values) .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) .notEqual(value) .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, direction: "asc" | "desc" = "asc", ): Promise { const collection = this.table.orderBy(field as string); return direction === "desc" ? await collection.reverse().toArray() : await collection.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(); } } // 创建各表的服务实例 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.newContractList); export const messageListService = new DatabaseService(db.messageList); // 默认导出数据库实例 export default db;