Files
DianZhanDemo/server/lib/crud-service.ts

253 lines
5.4 KiB
TypeScript
Raw Normal View History

2025-12-11 02:09:07 +08:00
/**
* CRUD
* Generic CRUD Service Base Class
*
* CRUD
* Provides standard CRUD operations with pagination, search, and sorting
*/
import { PrismaClient } from '@prisma/client'
import type { BaseEntity, PaginatedResponse, CrudOptions } from '@nuxt4crud/shared'
/**
* CRUD
* CRUD Service Base Class
*/
export abstract class BaseCrudService<
T extends BaseEntity,
CreateInput,
UpdateInput,
QueryParams = any,
> {
protected model: any
protected options: CrudOptions
constructor(
protected prisma: PrismaClient,
protected modelName: string,
options: Partial<CrudOptions> = {}
) {
this.model = (prisma as any)[modelName]
this.options = {
softDelete: false,
defaultOrderBy: 'createdAt',
defaultOrderDirection: 'desc',
searchableFields: [],
uniqueFields: [],
...options,
}
}
/**
*
* Get list with pagination, search, and sorting
*/
async findMany(
params: QueryParams & {
page?: number
limit?: number
search?: string
orderBy?: string
orderDirection?: 'asc' | 'desc'
}
): Promise<PaginatedResponse<T>> {
const {
page = 1,
limit = 10,
search = '',
orderBy = this.options.defaultOrderBy,
orderDirection = this.options.defaultOrderDirection,
...filters
} = params
const where = this.buildWhereClause(search, filters)
const skip = (page - 1) * limit
const take = limit
const [data, total] = await Promise.all([
this.model.findMany({
where,
skip,
take,
orderBy: { [orderBy]: orderDirection },
}),
this.model.count({ where }),
])
return {
data,
total,
page,
limit,
totalPages: Math.ceil(total / limit),
}
}
/**
* ID查找
* Find by ID
*/
async findById(id: number): Promise<T | null> {
return await this.model.findUnique({
where: { id },
})
}
/**
* ID查找记录
* Find records by multiple IDs
*
* @param ids ID数组
* @returns
*/
async findByIds(ids: number[]): Promise<T[]> {
const normalizedIds = Array.from(
new Set(
(ids || [])
.map(id => Number(id))
.filter(id => Number.isFinite(id) && id > 0)
)
)
if (normalizedIds.length === 0) {
return []
}
return await this.model.findMany({
where: { id: { in: normalizedIds } },
})
}
/**
*
* Create record
*/
async create(data: CreateInput): Promise<T> {
await this.validateCreate(data)
const transformedData = await this.transformCreateData(data)
return await this.model.create({
data: transformedData,
})
}
/**
*
* Update record
*/
async update(id: number, data: UpdateInput): Promise<T> {
const existing = await this.findById(id)
if (!existing) {
const error = new Error('记录不存在')
;(error as any).statusCode = 404
throw error
}
await this.validateUpdate(id, data, existing)
const transformedData = await this.transformUpdateData(data, existing)
return await this.model.update({
where: { id },
data: transformedData,
})
}
/**
*
* Delete record
*/
async delete(id: number): Promise<void> {
const existing = await this.findById(id)
if (!existing) {
const error = new Error('记录不存在')
;(error as any).statusCode = 404
throw error
}
await this.validateDelete(id, existing)
if (this.options.softDelete) {
await this.model.update({
where: { id },
data: { deletedAt: new Date() },
})
} else {
await this.model.delete({
where: { id },
})
}
}
/**
*
* Build where clause
*/
protected buildWhereClause(search: string, filters: any): any {
const where: any = {}
// 搜索功能
if (search && this.options.searchableFields?.length) {
where.OR = this.options.searchableFields.map(field => ({
[field]: { contains: search },
}))
}
// 过滤条件
Object.entries(filters).forEach(([key, value]) => {
if (value !== undefined && value !== null && key !== 'search') {
where[key] = value
}
})
// 软删除过滤
if (this.options.softDelete) {
where.deletedAt = null
}
return where
}
/**
*
* Pre-create validation hook
*/
protected async validateCreate(data: CreateInput): Promise<void> {
// 子类可以重写此方法
}
/**
*
* Pre-update validation hook
*/
protected async validateUpdate(id: number, data: UpdateInput, existing: T): Promise<void> {
// 子类可以重写此方法
}
/**
*
* Pre-delete validation hook
*/
protected async validateDelete(id: number, existing: T): Promise<void> {
// 子类可以重写此方法
}
/**
*
* Create data transformation hook
*/
protected async transformCreateData(data: CreateInput): Promise<any> {
return data
}
/**
*
* Update data transformation hook
*/
protected async transformUpdateData(data: UpdateInput, existing: T): Promise<any> {
return data
}
}