Files
cunkebao_v3/Touchkebao/src/utils/db.ts

343 lines
11 KiB
TypeScript
Raw Normal View History

/**
* - 使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<KfUserListData, "id"> {
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<KfUserWithServerId>;
weChatGroup!: Table<weChatGroup>;
contracts!: Table<ContractData>;
newContractList!: Table<NewContactListData>;
messageList!: Table<MessageListData>;
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<T> {
constructor(private table: Table<T>) {}
// 基础 CRUD 操作 - 使用serverId作为主键
async create(data: Omit<T, "serverId">): Promise<string | number> {
return await this.table.add(data as T);
}
// 创建数据(直接使用接口数据)
// 接口数据的id字段直接作为serverId主键原id字段保留
async createWithServerId(data: any): Promise<string | number> {
const dataToInsert = {
...data,
serverId: data.id, // 使用接口的id作为serverId主键
};
return await this.table.add(dataToInsert as T);
}
// 根据原始ID查询用户友好的查询方法
async findById(id: string | number): Promise<T | undefined> {
return await this.table.where("id").equals(id).first();
}
// 根据serverId查询内部主键查询
async findByPrimaryKey(serverId: string | number): Promise<T | undefined> {
return await this.table.get(serverId);
}
async findAll(): Promise<T[]> {
return await this.table.toArray();
}
async update(serverId: string | number, data: Partial<T>): Promise<number> {
return await this.table.update(serverId, data as any);
}
async updateMany(
dataList: { serverId: string | number; data: Partial<T> }[],
): Promise<number> {
return await this.table.bulkUpdate(
dataList.map(item => ({
key: item.serverId,
changes: item.data as any,
})),
);
}
async createMany(
dataList: Omit<T, "serverId">[],
): 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主键
}));
return await this.table.bulkAdd(processedData as T[], { allKeys: true });
}
async delete(serverId: string | number): Promise<void> {
await this.table.delete(serverId);
}
async clear(): Promise<void> {
await this.table.clear();
}
// 条件查询
async findWhere(field: keyof T, value: any): Promise<T[]> {
return await this.table
.where(field as string)
.equals(value)
.toArray();
}
// 根据服务器ID查询兼容性方法
async findByServerId(serverId: any): Promise<T | undefined> {
return await this.table.get(serverId);
}
// 根据原始ID批量查询
async findByIds(ids: (string | number)[]): Promise<T[]> {
return await this.table.where("id").anyOf(ids).toArray();
}
// 多值查询IN 查询)
async findWhereIn(field: keyof T, values: any[]): Promise<T[]> {
return await this.table
.where(field as string)
.anyOf(values)
.toArray();
}
// 范围查询
async findWhereBetween(field: keyof T, min: any, max: any): Promise<T[]> {
return await this.table
.where(field as string)
.between(min, max)
.toArray();
}
// 模糊查询(以指定字符串开头)
async findWhereStartsWith(field: keyof T, prefix: string): Promise<T[]> {
return await this.table
.where(field as string)
.startsWith(prefix)
.toArray();
}
// 不等于查询
async findWhereNot(field: keyof T, value: any): Promise<T[]> {
return await this.table
.where(field as string)
.notEqual(value)
.toArray();
}
// 大于查询
async findWhereGreaterThan(field: keyof T, value: any): Promise<T[]> {
return await this.table
.where(field as string)
.above(value)
.toArray();
}
// 小于查询
async findWhereLessThan(field: keyof T, value: any): Promise<T[]> {
return await this.table
.where(field as string)
.below(value)
.toArray();
}
// 复合条件查询
async findWhereMultiple(
conditions: {
field: keyof T;
operator:
| "equals"
| "notEqual"
| "above"
| "below"
| "aboveOrEqual"
| "belowOrEqual"
| "startsWith"
| "anyOf"
| "notIn"
| "between"
| "contains";
value: any;
value2?: any; // 用于 between 操作符
}[],
): Promise<T[]> {
let collection = this.table.toCollection();
for (const condition of conditions) {
const { field, operator, value, value2 } = condition;
collection = collection.and(item => {
const fieldValue = (item as any)[field];
switch (operator) {
case "equals":
return fieldValue === value;
case "notEqual":
return fieldValue !== value;
case "above":
return fieldValue > value;
case "below":
return fieldValue < value;
case "aboveOrEqual":
return fieldValue >= value;
case "belowOrEqual":
return fieldValue <= value;
case "startsWith":
return (
typeof fieldValue === "string" && fieldValue.startsWith(value)
);
case "contains":
return typeof fieldValue === "string" && fieldValue.includes(value);
case "anyOf":
return Array.isArray(value) && value.includes(fieldValue);
case "notIn":
return Array.isArray(value) && !value.includes(fieldValue);
case "between":
return fieldValue >= value && fieldValue <= (value2 ?? 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<T[]> {
const collection = this.table.orderBy(field as string);
return direction === "desc"
? await collection.reverse().toArray()
: await collection.toArray();
}
// 统计
async count(): Promise<number> {
return await this.table.count();
}
// 条件统计
async countWhere(field: keyof T, value: any): Promise<number> {
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;