import { NotFoundException, Logger } from '@nestjs/common'; import { PrismaService } from '../../database/prisma.service'; import { PaginationDto } from '../dto/pagination.dto'; import { PaginationMeta } from '../types/api-response.type'; /** * Generic base service with common CRUD operations * Extend this class for entity-specific services */ export abstract class BaseService { protected readonly logger: Logger; constructor( protected readonly prisma: PrismaService, protected readonly modelName: string, ) { this.logger = new Logger(`${modelName}Service`); } /** * Get the Prisma model delegate */ protected get model() { return (this.prisma as any)[this.modelName.toLowerCase()]; } /** * Find all records with pagination */ async findAll( pagination: PaginationDto, where?: any, ): Promise<{ items: T[]; meta: PaginationMeta }> { const { skip, take, orderBy } = pagination; const [items, total] = await Promise.all([ this.model.findMany({ where, skip, take, orderBy, }), this.model.count({ where }), ]); const totalPages = Math.ceil(total / take); return { items, meta: { total, page: pagination.page || 1, limit: pagination.limit || 10, totalPages, hasNextPage: (pagination.page || 1) < totalPages, hasPreviousPage: (pagination.page || 1) > 1, }, }; } /** * Find a single record by ID */ async findOne(id: string, include?: any): Promise { const record = await this.model.findUnique({ where: { id }, include, }); if (!record) { throw new NotFoundException(`${this.modelName} not found`); } return record; } /** * Find a single record by custom criteria */ findOneBy(where: any, include?: any): Promise { return this.model.findFirst({ where, include, }); } /** * Create a new record */ create(data: CreateDto, include?: any): Promise { return this.model.create({ data, include, }); } /** * Update an existing record */ async update(id: string, data: UpdateDto, include?: any): Promise { // Check if record exists await this.findOne(id); return this.model.update({ where: { id }, data, include, }); } /** * Soft delete a record (sets deletedAt) */ async delete(id: string): Promise { // Check if record exists await this.findOne(id); return this.model.delete({ where: { id }, }); } /** * Hard delete a record (permanently removes) */ async hardDelete(id: string): Promise { // Check if record exists await this.findOne(id); return this.prisma.hardDelete(this.modelName, { id }); } /** * Restore a soft-deleted record */ async restore(id: string): Promise { return this.prisma.restore(this.modelName, { id }); } /** * Check if a record exists */ async exists(id: string): Promise { const count = await this.model.count({ where: { id }, }); return count > 0; } /** * Count records matching criteria */ count(where?: any): Promise { return this.model.count({ where }); } /** * Execute a transaction */ transaction(fn: (prisma: PrismaService) => Promise): Promise { return this.prisma.$transaction(async (tx) => { return fn(tx as unknown as PrismaService); }); } }