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