import { Response } from "express"; import { AuthenticatedRequest } from "../types/auth"; import { getPool } from "../database/db"; import { logger } from "../utils/logger"; /** * 엔티티 검색 API * GET /api/entity-search/:tableName */ export async function searchEntity(req: AuthenticatedRequest, res: Response) { try { const { tableName } = req.params; const { searchText = "", searchFields = "", filterCondition = "{}", page = "1", limit = "20", } = req.query; // tableName 유효성 검증 if (!tableName || tableName === "undefined" || tableName === "null") { logger.warn("엔티티 검색 실패: 테이블명이 없음", { tableName }); return res.status(400).json({ success: false, message: "테이블명이 지정되지 않았습니다. 컴포넌트 설정에서 sourceTable을 확인해주세요.", }); } // 멀티테넌시 const companyCode = req.user!.companyCode; // 검색 필드 파싱 const fields = searchFields ? (searchFields as string).split(",").map((f) => f.trim()) : []; // WHERE 조건 생성 const whereConditions: string[] = []; const params: any[] = []; let paramIndex = 1; // 멀티테넌시 필터링 if (companyCode !== "*") { whereConditions.push(`company_code = $${paramIndex}`); params.push(companyCode); paramIndex++; } // 검색 조건 if (searchText && fields.length > 0) { const searchConditions = fields.map((field) => { const condition = `${field}::text ILIKE $${paramIndex}`; paramIndex++; return condition; }); whereConditions.push(`(${searchConditions.join(" OR ")})`); // 검색어 파라미터 추가 fields.forEach(() => { params.push(`%${searchText}%`); }); } // 추가 필터 조건 const additionalFilter = JSON.parse(filterCondition as string); for (const [key, value] of Object.entries(additionalFilter)) { whereConditions.push(`${key} = $${paramIndex}`); params.push(value); paramIndex++; } // 페이징 const offset = (parseInt(page as string) - 1) * parseInt(limit as string); const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : ""; // 쿼리 실행 const pool = getPool(); const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`; const dataQuery = ` SELECT * FROM ${tableName} ${whereClause} ORDER BY id DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1} `; params.push(parseInt(limit as string)); params.push(offset); const countResult = await pool.query( countQuery, params.slice(0, params.length - 2) ); const dataResult = await pool.query(dataQuery, params); logger.info("엔티티 검색 성공", { tableName, searchText, companyCode, rowCount: dataResult.rowCount, }); res.json({ success: true, data: dataResult.rows, pagination: { total: parseInt(countResult.rows[0].count), page: parseInt(page as string), limit: parseInt(limit as string), }, }); } catch (error: any) { logger.error("엔티티 검색 오류", { error: error.message, stack: error.stack, }); res.status(500).json({ success: false, message: error.message }); } }