// 스마트공장 활용 로그 조회 컨트롤러 // 최고관리자(*) 전용 — 회사별 필터링 가능 import { Response } from "express"; import { AuthenticatedRequest } from "../middleware/permissionMiddleware"; import { query, queryOne } from "../database/db"; import { logger } from "../utils/logger"; /** * GET /api/admin/smart-factory-log * 스마트공장 로그 목록 조회 */ export const getSmartFactoryLogs = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, userId, sendStatus, dateFrom, dateTo, search, page = "1", limit = "50", } = req.query; const whereConditions: string[] = []; const queryParams: any[] = []; let paramIndex = 1; // 회사 필터 if (companyCode && companyCode !== "all") { whereConditions.push(`sfl.company_code = $${paramIndex}`); queryParams.push(companyCode); paramIndex++; } // 사용자 필터 if (userId && (userId as string).trim()) { whereConditions.push(`sfl.user_id ILIKE $${paramIndex}`); queryParams.push(`%${(userId as string).trim()}%`); paramIndex++; } // 전송 상태 필터 if (sendStatus && sendStatus !== "all") { whereConditions.push(`sfl.send_status = $${paramIndex}`); queryParams.push(sendStatus); paramIndex++; } // 날짜 범위 필터 if (dateFrom) { whereConditions.push(`sfl.created_at >= $${paramIndex}`); queryParams.push(dateFrom); paramIndex++; } if (dateTo) { whereConditions.push(`sfl.created_at < ($${paramIndex}::date + 1)`); queryParams.push(dateTo); paramIndex++; } // 통합 검색 if (search && (search as string).trim()) { whereConditions.push( `(sfl.user_id ILIKE $${paramIndex} OR sfl.user_name ILIKE $${paramIndex} OR sfl.connect_ip ILIKE $${paramIndex} OR sfl.error_message ILIKE $${paramIndex})` ); queryParams.push(`%${(search as string).trim()}%`); paramIndex++; } const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : ""; // 총 개수 const countResult = await queryOne<{ total: string }>( `SELECT COUNT(*) as total FROM smart_factory_log sfl ${whereClause}`, queryParams ); const total = parseInt(countResult?.total || "0", 10); // 페이지네이션 const pageNum = Math.max(1, parseInt(page as string, 10)); const limitNum = Math.min(100, Math.max(1, parseInt(limit as string, 10))); const offset = (pageNum - 1) * limitNum; // 데이터 조회 (회사명 JOIN) const logs = await query( `SELECT sfl.*, cm.company_name FROM smart_factory_log sfl LEFT JOIN company_mng cm ON cm.company_code = sfl.company_code ${whereClause} ORDER BY sfl.created_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`, [...queryParams, limitNum, offset] ); res.status(200).json({ success: true, data: logs, total, page: pageNum, limit: limitNum, }); } catch (error) { logger.error("스마트공장 로그 조회 실패:", error); res.status(500).json({ success: false, message: "스마트공장 로그 조회 중 오류가 발생했습니다.", error: { code: "SERVER_ERROR", details: error instanceof Error ? error.message : "알 수 없는 오류", }, }); } }; /** * GET /api/admin/smart-factory-log/stats * 스마트공장 로그 통계 (회사별 요약) */ export const getSmartFactoryLogStats = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, days = "30" } = req.query; const daysNum = parseInt(days as string, 10) || 30; const whereConditions: string[] = [ `sfl.created_at >= NOW() - INTERVAL '${daysNum} days'`, ]; const queryParams: any[] = []; let paramIndex = 1; if (companyCode && companyCode !== "all") { whereConditions.push(`sfl.company_code = $${paramIndex}`); queryParams.push(companyCode); paramIndex++; } const whereClause = `WHERE ${whereConditions.join(" AND ")}`; // 상태별 건수 const statusCounts = await query<{ send_status: string; count: string }>( `SELECT send_status, COUNT(*) as count FROM smart_factory_log sfl ${whereClause} GROUP BY send_status`, queryParams ); // 회사별 건수 const companyCounts = await query<{ company_code: string; company_name: string; count: string; }>( `SELECT sfl.company_code, COALESCE(cm.company_name, sfl.company_code) as company_name, COUNT(*) as count FROM smart_factory_log sfl LEFT JOIN company_mng cm ON cm.company_code = sfl.company_code ${whereClause} GROUP BY sfl.company_code, cm.company_name ORDER BY count DESC`, queryParams ); // 일별 추이 const dailyCounts = await query<{ date: string; count: string }>( `SELECT DATE(sfl.created_at) as date, COUNT(*) as count FROM smart_factory_log sfl ${whereClause} GROUP BY DATE(sfl.created_at) ORDER BY date DESC LIMIT ${daysNum}`, queryParams ); // 전체 건수 const totalResult = await queryOne<{ total: string }>( `SELECT COUNT(*) as total FROM smart_factory_log sfl ${whereClause}`, queryParams ); res.status(200).json({ success: true, data: { total: parseInt(totalResult?.total || "0", 10), statusCounts: statusCounts.map((r) => ({ status: r.send_status, count: parseInt(r.count, 10), })), companyCounts: companyCounts.map((r) => ({ companyCode: r.company_code, companyName: r.company_name, count: parseInt(r.count, 10), })), dailyCounts: dailyCounts.map((r) => ({ date: r.date, count: parseInt(r.count, 10), })), }, }); } catch (error) { logger.error("스마트공장 로그 통계 조회 실패:", error); res.status(500).json({ success: false, message: "통계 조회 중 오류가 발생했습니다.", error: { code: "SERVER_ERROR", details: error instanceof Error ? error.message : "알 수 없는 오류", }, }); } };