| 1 | import { Pagination } from '@yikart/common' |
| 2 | import { DeleteOptions } from 'mongodb' |
| 3 | import { |
| 4 | FilterQuery, |
| 5 | FlattenMaps, |
| 6 | Model, |
| 7 | MongooseBaseQueryOptions, |
| 8 | ProjectionType, |
| 9 | QueryOptions, |
| 10 | Require_id, |
| 11 | UpdateQuery, |
| 12 | } from 'mongoose' |
| 13 | |
| 14 | export type LeanDoc<T> = FlattenMaps<Require_id<T>> |
| 15 | |
| 16 | export interface PaginationParams<TDocument> extends Pagination { |
| 17 | filter?: FilterQuery<TDocument> |
| 18 | projection?: ProjectionType<TDocument> | null | undefined |
| 19 | options?: QueryOptions<TDocument> |
| 20 | } |
| 21 | |
| 22 | export type CreateDocumentType<TDocument> = Partial<TDocument> |
| 23 | |
| 24 | export type UpdateDocumentType<TDocument> = UpdateQuery<TDocument> |
| 25 | |
| 26 | export class BaseRepository<TDocument> { |
| 27 | constructor( |
| 28 | protected readonly model: Model<TDocument>, |
| 29 | ) { } |
| 30 | |
| 31 | /** |
| 32 | * 根据ID获取单个文档 |
| 33 | */ |
| 34 | async getById(id: string, options?: QueryOptions<TDocument>): Promise<LeanDoc<TDocument> | null> { |
| 35 | return await this.model.findById(id, undefined, options).lean({ virtuals: true }).exec() as LeanDoc<TDocument> | null |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * 创建新文档 |
| 40 | */ |
| 41 | async create(data: CreateDocumentType<TDocument>): Promise<LeanDoc<TDocument>> { |
| 42 | const created = new this.model(data) |
| 43 | const saved = await created.save() |
| 44 | return saved.toObject() as LeanDoc<TDocument> |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * 批量创建文档 |
| 49 | */ |
| 50 | async createMany(data: CreateDocumentType<TDocument>[]): Promise<LeanDoc<TDocument>[]> { |
| 51 | const docs = await this.model.insertMany(data, { lean: true }) |
| 52 | return docs.map(doc => ({ ...doc, id: String(doc._id) })) as unknown as LeanDoc<TDocument>[] |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * 根据ID更新文档 |
| 57 | */ |
| 58 | async updateById( |
| 59 | id: string, |
| 60 | update: UpdateDocumentType<TDocument>, |
| 61 | options?: QueryOptions<TDocument>, |
| 62 | ): Promise<LeanDoc<TDocument> | null> { |
| 63 | return await this.model.findByIdAndUpdate(id, update, { new: true, ...options }).lean({ virtuals: true }).exec() as LeanDoc<TDocument> | null |
| 64 | } |
| 65 | |
| 66 | /** |
| 67 | * 更新单个文档 |
| 68 | */ |
| 69 | protected async updateOne( |
| 70 | filter: FilterQuery<TDocument>, |
| 71 | update: UpdateDocumentType<TDocument>, |
| 72 | options?: QueryOptions<TDocument>, |
| 73 | ): Promise<LeanDoc<TDocument> | null> { |
| 74 | return await this.model.findOneAndUpdate(filter, update, { new: true, ...options }).lean({ virtuals: true }).exec() as LeanDoc<TDocument> | null |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * 根据ID删除文档 |
| 79 | */ |
| 80 | async deleteById(id: string, options?: QueryOptions<TDocument>): Promise<LeanDoc<TDocument> | null> { |
| 81 | return await this.model.findByIdAndDelete(id, options).lean({ virtuals: true }).exec() as LeanDoc<TDocument> | null |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * 删除单个文档 |
| 86 | */ |
| 87 | protected async deleteOne(filter: FilterQuery<TDocument>, options?: (DeleteOptions & MongooseBaseQueryOptions<TDocument>)) { |
| 88 | return await this.model.deleteOne(filter, options).exec() |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * 批量删除文档 |
| 93 | */ |
| 94 | protected async deleteMany(filter: FilterQuery<TDocument>): Promise<void> { |
| 95 | await this.model.deleteMany(filter).exec() |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * 分页查询 |
| 100 | */ |
| 101 | protected async findWithPagination(params: PaginationParams<TDocument>): Promise<readonly [LeanDoc<TDocument>[], number]> { |
| 102 | const { page, pageSize, filter = {}, options = {}, projection } = params |
| 103 | const skip = (page - 1) * pageSize |
| 104 | |
| 105 | const findOptions = { ...options, skip, limit: pageSize } |
| 106 | |
| 107 | const [items, total] = await Promise.all([ |
| 108 | this.model.find(filter, projection, findOptions).lean({ virtuals: true }).exec() as Promise<LeanDoc<TDocument>[]>, |
| 109 | this.model.countDocuments(filter).exec(), |
| 110 | ]) |
| 111 | |
| 112 | return [items, total] as const |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * 查找单个文档 |
| 117 | */ |
| 118 | protected async findOne(filter: FilterQuery<TDocument>, options?: QueryOptions<TDocument>): Promise<LeanDoc<TDocument> | null> { |
| 119 | return await this.model.findOne(filter, undefined, options).lean({ virtuals: true }).exec() as LeanDoc<TDocument> | null |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * 查找多个文档 |
| 124 | */ |
| 125 | protected async find(filter: FilterQuery<TDocument> = {}, options?: QueryOptions<TDocument>): Promise<LeanDoc<TDocument>[]> { |
| 126 | return await this.model.find(filter, undefined, options).lean({ virtuals: true }).exec() as LeanDoc<TDocument>[] |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * 统计文档数量 |
| 131 | */ |
| 132 | protected async count(filter: FilterQuery<TDocument> = {}): Promise<number> { |
| 133 | return await this.model.countDocuments(filter).exec() |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * 检查文档是否存在 |
| 138 | */ |
| 139 | protected async exists(filter: FilterQuery<TDocument>): Promise<boolean> { |
| 140 | const result = await this.model.exists(filter).exec() |
| 141 | return result !== null |
| 142 | } |
| 143 | } |
| 144 |