import { PrismaClient } from '@prisma/client'; import { PaginationParams, PaginationResult, FilterOptions } from '../types/crud.types'; {{#if features.logging}} import { logger } from '../utils/logger'; {{/if}} {{#if features.cache}} import { CacheService } from '../services/cache.service'; {{/if}} {{#if features.audit}} import { AuditService } from '../services/audit.service'; {{/if}} const prisma = new PrismaClient(); export class {{entity}}Service { {{#if features.cache}} private cacheService: CacheService; {{/if}} {{#if features.audit}} private auditService: AuditService; {{/if}} constructor() { {{#if features.cache}} this.cacheService = new CacheService(); {{/if}} {{#if features.audit}} this.auditService = new AuditService(); {{/if}} } /** * Obtiene todos los {{entityPlural}} con paginación */ async findAll(params: PaginationParams): Promise> { try { const { page = 1, limit = 10, search, sort } = params; const skip = (page - 1) * limit; // Construir filtros de búsqueda const where: any = {}; if (search) { where.OR = [ // Agregar campos de búsqueda según la entidad { name: { contains: search, mode: 'insensitive' } }, // Agregar más campos según sea necesario ]; } // Construir ordenamiento let orderBy: any = { createdAt: 'desc' }; if (sort) { const [field, order] = sort.split(':'); orderBy = { [field]: order || 'asc' }; } {{#if features.cache}} // Verificar cache const cacheKey = `{{entityLower}}s:${JSON.stringify({ page, limit, search, sort })}`; const cached = await this.cacheService.get(cacheKey); if (cached) { {{#if features.logging}} logger.debug('{{entity}}s retrieved from cache', { cacheKey }); {{/if}} return cached; } {{/if}} // Ejecutar consultas const [data, total] = await Promise.all([ prisma.{{entityLower}}.findMany({ where, orderBy, skip, take: limit, }), prisma.{{entityLower}}.count({ where }) ]); const result: PaginationResult = { data, pagination: { page, limit, total, totalPages: Math.ceil(total / limit), hasNext: page < Math.ceil(total / limit), hasPrev: page > 1 } }; {{#if features.cache}} // Guardar en cache por 5 minutos await this.cacheService.set(cacheKey, result, 300); {{/if}} {{#if features.logging}} logger.info('{{entity}}s retrieved successfully', { count: data.length, total, page, limit }); {{/if}} return result; } catch (error) { {{#if features.logging}} logger.error('Error retrieving {{entityPlural}}:', error); {{/if}} throw error; } } /** * Obtiene un {{entity}} por ID */ async findById(id: string): Promise { try { {{#if features.cache}} // Verificar cache const cacheKey = `{{entityLower}}:${id}`; const cached = await this.cacheService.get(cacheKey); if (cached) { {{#if features.logging}} logger.debug('{{entity}} retrieved from cache', { id, cacheKey }); {{/if}} return cached; } {{/if}} const {{entityLower}} = await prisma.{{entityLower}}.findUnique({ where: { id } }); {{#if features.cache}} if ({{entityLower}}) { // Guardar en cache por 10 minutos await this.cacheService.set(cacheKey, {{entityLower}}, 600); } {{/if}} {{#if features.logging}} logger.info('{{entity}} retrieved by ID', { id, found: !!{{entityLower}} }); {{/if}} return {{entityLower}}; } catch (error) { {{#if features.logging}} logger.error('Error retrieving {{entity}} by ID:', error); {{/if}} throw error; } } /** * Crea un nuevo {{entity}} */ async create(data: any): Promise { try { const {{entityLower}} = await prisma.{{entityLower}}.create({ data }); {{#if features.cache}} // Invalidar cache relacionado await this.cacheService.deletePattern('{{entityLower}}s:*'); // Guardar en cache individual await this.cacheService.set(`{{entityLower}}:${ {{entityLower}}.id}`, {{entityLower}}, 600); {{/if}} {{#if features.audit}} // Registrar auditoría await this.auditService.log({ action: 'CREATE', entity: '{{entity}}', entityId: {{entityLower}}.id, data: {{entityLower}} }); {{/if}} {{#if features.logging}} logger.info('{{entity}} created successfully', { id: {{entityLower}}.id }); {{/if}} return {{entityLower}}; } catch (error) { {{#if features.logging}} logger.error('Error creating {{entity}}:', error); {{/if}} throw error; } } /** * Actualiza un {{entity}} existente */ async update(id: string, data: any): Promise { try { const {{entityLower}} = await prisma.{{entityLower}}.update({ where: { id }, data }); {{#if features.cache}} // Invalidar cache await this.cacheService.delete(`{{entityLower}}:${id}`); await this.cacheService.deletePattern('{{entityLower}}s:*'); // Actualizar cache individual await this.cacheService.set(`{{entityLower}}:${id}`, {{entityLower}}, 600); {{/if}} {{#if features.audit}} // Registrar auditoría await this.auditService.log({ action: 'UPDATE', entity: '{{entity}}', entityId: id, data: {{entityLower}} }); {{/if}} {{#if features.logging}} logger.info('{{entity}} updated successfully', { id }); {{/if}} return {{entityLower}}; } catch (error) { {{#if features.logging}} logger.error('Error updating {{entity}}:', error); {{/if}} throw error; } } /** * Elimina un {{entity}} */ async delete(id: string): Promise { try { await prisma.{{entityLower}}.delete({ where: { id } }); {{#if features.cache}} // Invalidar cache await this.cacheService.delete(`{{entityLower}}:${id}`); await this.cacheService.deletePattern('{{entityLower}}s:*'); {{/if}} {{#if features.audit}} // Registrar auditoría await this.auditService.log({ action: 'DELETE', entity: '{{entity}}', entityId: id }); {{/if}} {{#if features.logging}} logger.info('{{entity}} deleted successfully', { id }); {{/if}} } catch (error) { {{#if features.logging}} logger.error('Error deleting {{entity}}:', error); {{/if}} throw error; } } /** * Búsqueda avanzada con filtros personalizados */ async findWithFilters(filters: FilterOptions): Promise { try { const {{entityPlural}} = await prisma.{{entityLower}}.findMany({ where: filters.where, orderBy: filters.orderBy, include: filters.include }); {{#if features.logging}} logger.info('{{entity}}s retrieved with filters', { count: {{entityPlural}}.length, filters }); {{/if}} return {{entityPlural}}; } catch (error) { {{#if features.logging}} logger.error('Error retrieving {{entityPlural}} with filters:', error); {{/if}} throw error; } } /** * Cuenta total de registros con filtros opcionales */ async count(where?: any): Promise { try { const count = await prisma.{{entityLower}}.count({ where }); {{#if features.logging}} logger.info('{{entity}}s counted', { count, where }); {{/if}} return count; } catch (error) { {{#if features.logging}} logger.error('Error counting {{entityPlural}}:', error); {{/if}} throw error; } } /** * Búsqueda avanzada de texto */ async search(query: string, fields?: string[]): Promise { try { const searchFields = fields || ['name', 'description']; // Campos por defecto const where = { OR: searchFields.map(field => ({ [field]: { contains: query, mode: 'insensitive' as const } })) }; const results = await prisma.{{entityLower}}.findMany({ where }); {{#if features.logging}} logger.info('{{entity}}s search completed', { query, fields: searchFields, resultCount: results.length }); {{/if}} return results; } catch (error) { {{#if features.logging}} logger.error('Error searching {{entityPlural}}:', error); {{/if}} throw error; } } /** * Exporta datos en diferentes formatos */ async export(format: string, options: { fields?: string[], filters?: any } = {}): Promise { try { const { fields, filters = {} } = options; const data = await prisma.{{entityLower}}.findMany({ where: filters, select: fields ? fields.reduce((acc, field) => ({ ...acc, [field]: true }), {}) : undefined }); {{#if features.logging}} logger.info('{{entity}}s export completed', { format, fields, filters, recordCount: data.length }); {{/if}} switch (format) { case 'csv': return this.convertToCSV(data, fields); case 'xlsx': return this.convertToXLSX(data, fields); case 'json': default: return JSON.stringify(data, null, 2); } } catch (error) { {{#if features.logging}} logger.error('Error exporting {{entityPlural}}:', error); {{/if}} throw error; } } /** * Operaciones en lote */ async bulkOperation(operation: string, data: any[]): Promise { try { let result; switch (operation) { case 'create': result = await prisma.{{entityLower}}.createMany({ data, skipDuplicates: true }); break; case 'update': result = await Promise.all( data.map(item => prisma.{{entityLower}}.update({ where: { id: item.id }, data: item }) ) ); break; case 'delete': const ids = data.map(item => item.id || item); result = await prisma.{{entityLower}}.deleteMany({ where: { id: { in: ids } } }); break; default: throw new Error(`Unsupported bulk operation: ${operation}`); } {{#if features.cache}} // Invalidar cache después de operaciones en lote await this.cacheService.deletePattern('{{entityLower}}*'); {{/if}} {{#if features.audit}} // Registrar auditoría para operaciones en lote await this.auditService.log({ action: `BULK_${operation.toUpperCase()}`, entity: '{{entity}}', data: { operation, count: data.length } }); {{/if}} {{#if features.logging}} logger.info('{{entity}}s bulk operation completed', { operation, count: data.length, result }); {{/if}} return result; } catch (error) { {{#if features.logging}} logger.error('Error in bulk operation:', error); {{/if}} throw error; } } /** * Convierte datos a formato CSV */ private convertToCSV(data: any[], fields?: string[]): string { if (data.length === 0) return ''; const headers = fields || Object.keys(data[0]); const csvHeaders = headers.join(','); const csvRows = data.map(row => headers.map(header => { const value = row[header]; // Escapar comillas y envolver en comillas si contiene comas if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) { return `"${value.replace(/"/g, '""')}"`; } return value; }).join(',') ); return [csvHeaders, ...csvRows].join('\n'); } /** * Convierte datos a formato XLSX (requiere librería externa) */ private convertToXLSX(data: any[], fields?: string[]): Buffer { // Nota: Esto requiere instalar 'xlsx' package // npm install xlsx @types/xlsx try { const XLSX = require('xlsx'); const worksheet = XLSX.utils.json_to_sheet(data); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, '{{entity}}s'); return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }); } catch (error) { throw new Error('XLSX package not installed. Run: npm install xlsx @types/xlsx'); } } }