// 배치 실행 로그 서비스 // 작성일: 2024-12-24 import prisma from "../config/database"; import { BatchExecutionLog, CreateBatchExecutionLogRequest, UpdateBatchExecutionLogRequest, BatchExecutionLogFilter, BatchExecutionLogWithConfig } from "../types/batchExecutionLogTypes"; import { ApiResponse } from "../types/batchTypes"; export class BatchExecutionLogService { /** * 배치 실행 로그 목록 조회 */ static async getExecutionLogs( filter: BatchExecutionLogFilter = {} ): Promise> { try { const { batch_config_id, execution_status, start_date, end_date, page = 1, limit = 50 } = filter; const skip = (page - 1) * limit; const take = limit; // WHERE 조건 구성 const where: any = {}; if (batch_config_id) { where.batch_config_id = batch_config_id; } if (execution_status) { where.execution_status = execution_status; } if (start_date || end_date) { where.start_time = {}; if (start_date) { where.start_time.gte = start_date; } if (end_date) { where.start_time.lte = end_date; } } // 로그 조회 const [logs, total] = await Promise.all([ prisma.batch_execution_logs.findMany({ where, include: { batch_config: { select: { id: true, batch_name: true, description: true, cron_schedule: true, is_active: true } } }, orderBy: { start_time: 'desc' }, skip, take }), prisma.batch_execution_logs.count({ where }) ]); return { success: true, data: logs as BatchExecutionLogWithConfig[], pagination: { page, limit, total, totalPages: Math.ceil(total / limit) } }; } catch (error) { console.error("배치 실행 로그 조회 실패:", error); return { success: false, message: "배치 실행 로그 조회 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } /** * 배치 실행 로그 생성 */ static async createExecutionLog( data: CreateBatchExecutionLogRequest ): Promise> { try { const log = await prisma.batch_execution_logs.create({ data: { batch_config_id: data.batch_config_id, execution_status: data.execution_status, start_time: data.start_time || new Date(), end_time: data.end_time, duration_ms: data.duration_ms, total_records: data.total_records || 0, success_records: data.success_records || 0, failed_records: data.failed_records || 0, error_message: data.error_message, error_details: data.error_details, server_name: data.server_name || process.env.HOSTNAME || 'unknown', process_id: data.process_id || process.pid?.toString() } }); return { success: true, data: log as BatchExecutionLog, message: "배치 실행 로그가 생성되었습니다." }; } catch (error) { console.error("배치 실행 로그 생성 실패:", error); return { success: false, message: "배치 실행 로그 생성 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } /** * 배치 실행 로그 업데이트 */ static async updateExecutionLog( id: number, data: UpdateBatchExecutionLogRequest ): Promise> { try { const log = await prisma.batch_execution_logs.update({ where: { id }, data: { execution_status: data.execution_status, end_time: data.end_time, duration_ms: data.duration_ms, total_records: data.total_records, success_records: data.success_records, failed_records: data.failed_records, error_message: data.error_message, error_details: data.error_details } }); return { success: true, data: log as BatchExecutionLog, message: "배치 실행 로그가 업데이트되었습니다." }; } catch (error) { console.error("배치 실행 로그 업데이트 실패:", error); return { success: false, message: "배치 실행 로그 업데이트 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } /** * 배치 실행 로그 삭제 */ static async deleteExecutionLog(id: number): Promise> { try { await prisma.batch_execution_logs.delete({ where: { id } }); return { success: true, message: "배치 실행 로그가 삭제되었습니다." }; } catch (error) { console.error("배치 실행 로그 삭제 실패:", error); return { success: false, message: "배치 실행 로그 삭제 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } /** * 특정 배치의 최신 실행 로그 조회 */ static async getLatestExecutionLog( batchConfigId: number ): Promise> { try { const log = await prisma.batch_execution_logs.findFirst({ where: { batch_config_id: batchConfigId }, orderBy: { start_time: 'desc' } }); return { success: true, data: log as BatchExecutionLog | null }; } catch (error) { console.error("최신 배치 실행 로그 조회 실패:", error); return { success: false, message: "최신 배치 실행 로그 조회 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } /** * 배치 실행 통계 조회 */ static async getExecutionStats( batchConfigId?: number, startDate?: Date, endDate?: Date ): Promise> { try { const where: any = {}; if (batchConfigId) { where.batch_config_id = batchConfigId; } if (startDate || endDate) { where.start_time = {}; if (startDate) { where.start_time.gte = startDate; } if (endDate) { where.start_time.lte = endDate; } } const logs = await prisma.batch_execution_logs.findMany({ where, select: { execution_status: true, duration_ms: true, total_records: true } }); const total_executions = logs.length; const success_count = logs.filter((log: any) => log.execution_status === 'SUCCESS').length; const failed_count = logs.filter((log: any) => log.execution_status === 'FAILED').length; const success_rate = total_executions > 0 ? (success_count / total_executions) * 100 : 0; const validDurations = logs .filter((log: any) => log.duration_ms !== null) .map((log: any) => log.duration_ms!); const average_duration_ms = validDurations.length > 0 ? validDurations.reduce((sum: number, duration: number) => sum + duration, 0) / validDurations.length : 0; const total_records_processed = logs .filter((log: any) => log.total_records !== null) .reduce((sum: number, log: any) => sum + (log.total_records || 0), 0); return { success: true, data: { total_executions, success_count, failed_count, success_rate, average_duration_ms, total_records_processed } }; } catch (error) { console.error("배치 실행 통계 조회 실패:", error); return { success: false, message: "배치 실행 통계 조회 중 오류가 발생했습니다.", error: error instanceof Error ? error.message : "알 수 없는 오류" }; } } }