import { logger } from "../utils/logger"; import { query, queryOne } from "../database/db"; export class AdminService { /** * 관리자 메뉴 목록 조회 */ static async getAdminMenuList(paramMap: any): Promise { try { logger.info("AdminService.getAdminMenuList 시작 - 파라미터:", paramMap); const { userLang = "ko" } = paramMap; // 기존 Java의 selectAdminMenuList 쿼리를 Raw Query로 포팅 // WITH RECURSIVE 쿼리 구현 const menuList = await query( ` WITH RECURSIVE v_menu( LEVEL, MENU_TYPE, OBJID, PARENT_OBJ_ID, MENU_NAME_KOR, MENU_URL, MENU_DESC, SEQ, WRITER, REGDATE, STATUS, SYSTEM_NAME, COMPANY_CODE, LANG_KEY, LANG_KEY_DESC, PATH, CYCLE, TRANSLATED_NAME, TRANSLATED_DESC ) AS ( SELECT 1 AS LEVEL, MENU.MENU_TYPE, MENU.OBJID::numeric, MENU.PARENT_OBJ_ID, MENU.MENU_NAME_KOR, MENU.MENU_URL, MENU.MENU_DESC, MENU.SEQ, MENU.WRITER, MENU.REGDATE, MENU.STATUS, MENU.SYSTEM_NAME, MENU.COMPANY_CODE, MENU.LANG_KEY, MENU.LANG_KEY_DESC, ARRAY [MENU.OBJID], FALSE, -- 번역된 메뉴명 (우선순위: 회사별 번역 > 공통 번역 > 기본명) COALESCE( (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU.LANG_KEY AND (MLKM.company_code = MENU.COMPANY_CODE OR (MENU.COMPANY_CODE IS NULL AND MLKM.company_code = '*')) AND MLT.lang_code = $1 LIMIT 1), (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU.LANG_KEY AND MLKM.company_code = '*' AND MLT.lang_code = $1 LIMIT 1), MENU.MENU_NAME_KOR ), -- 번역된 설명 (우선순위: 회사별 번역 > 공통 번역 > 기본명) COALESCE( (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU.LANG_KEY_DESC AND (MLKM.company_code = MENU.COMPANY_CODE OR (MENU.COMPANY_CODE IS NULL AND MLKM.company_code = '*')) AND MLT.lang_code = $1 LIMIT 1), (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU.LANG_KEY_DESC AND MLKM.company_code = '*' AND MLT.lang_code = $1 LIMIT 1), MENU.MENU_DESC ) FROM MENU_INFO MENU WHERE MENU_TYPE = 0 AND NOT EXISTS ( SELECT 1 FROM MENU_INFO parent_menu WHERE parent_menu.OBJID = MENU.PARENT_OBJ_ID ) UNION ALL SELECT V_MENU.LEVEL + 1, MENU_SUB.MENU_TYPE, MENU_SUB.OBJID, MENU_SUB.PARENT_OBJ_ID, MENU_SUB.MENU_NAME_KOR, MENU_SUB.MENU_URL, MENU_SUB.MENU_DESC, MENU_SUB.SEQ, MENU_SUB.WRITER, MENU_SUB.REGDATE, MENU_SUB.STATUS, MENU_SUB.SYSTEM_NAME, MENU_SUB.COMPANY_CODE, MENU_SUB.LANG_KEY, MENU_SUB.LANG_KEY_DESC, PATH || MENU_SUB.SEQ::numeric, MENU_SUB.OBJID = ANY(PATH), -- 번역된 메뉴명 (우선순위: 회사별 번역 > 공통 번역 > 기본명) COALESCE( (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU_SUB.LANG_KEY AND (MLKM.company_code = MENU_SUB.COMPANY_CODE OR (MENU_SUB.COMPANY_CODE IS NULL AND MLKM.company_code = '*')) AND MLT.lang_code = $1 LIMIT 1), (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU_SUB.LANG_KEY AND MLKM.company_code = '*' AND MLT.lang_code = $1 LIMIT 1), MENU_SUB.MENU_NAME_KOR ), -- 번역된 설명 (우선순위: 회사별 번역 > 공통 번역 > 기본명) COALESCE( (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU_SUB.LANG_KEY_DESC AND (MLKM.company_code = MENU_SUB.COMPANY_CODE OR (MENU_SUB.COMPANY_CODE IS NULL AND MLKM.company_code = '*')) AND MLT.lang_code = $1 LIMIT 1), (SELECT MLT.lang_text FROM MULTI_LANG_KEY_MASTER MLKM JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id WHERE MLKM.lang_key = MENU_SUB.LANG_KEY_DESC AND MLKM.company_code = '*' AND MLT.lang_code = $1 LIMIT 1), MENU_SUB.MENU_DESC ) FROM MENU_INFO MENU_SUB JOIN V_MENU ON MENU_SUB.PARENT_OBJ_ID = V_MENU.OBJID WHERE MENU_SUB.OBJID != ANY(V_MENU.PATH) ) SELECT LEVEL AS LEV, CASE MENU_TYPE WHEN '0' THEN 'admin' WHEN '1' THEN 'user' ELSE '' END AS MENU_TYPE, A.OBJID, A.PARENT_OBJ_ID, A.MENU_NAME_KOR, LPAD(' ', 3 * (LEVEL - 1)) || A.MENU_NAME_KOR AS LPAD_MENU_NAME_KOR, A.MENU_URL, A.MENU_DESC, A.SEQ, A.WRITER, TO_CHAR(A.REGDATE, 'YYYY-MM-DD') AS REGDATE, A.STATUS, A.COMPANY_CODE, A.LANG_KEY, A.LANG_KEY_DESC, COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME, A.TRANSLATED_NAME, A.TRANSLATED_DESC, CASE UPPER(A.STATUS) WHEN 'ACTIVE' THEN '활성화' WHEN 'INACTIVE' THEN '비활성화' ELSE '' END AS STATUS_TITLE FROM v_menu A LEFT JOIN COMPANY_MNG CM ON A.COMPANY_CODE = CM.COMPANY_CODE WHERE 1 = 1 ORDER BY PATH, SEQ `, [userLang] ); logger.info(`관리자 메뉴 목록 조회 결과: ${menuList.length}개`); if (menuList.length > 0) { logger.info("첫 번째 메뉴:", menuList[0]); } return menuList; } catch (error) { logger.error("AdminService.getAdminMenuList 오류:", error); throw error; } } /** * 사용자 메뉴 목록 조회 */ static async getUserMenuList(paramMap: any): Promise { try { logger.info("AdminService.getUserMenuList 시작 - 파라미터:", paramMap); const { userLang = "ko" } = paramMap; // 기존 Java의 selectUserMenuList 쿼리를 Raw Query로 포팅 const menuList = await query( ` WITH RECURSIVE v_menu( LEVEL, MENU_TYPE, OBJID, PARENT_OBJ_ID, MENU_NAME_KOR, MENU_URL, MENU_DESC, SEQ, WRITER, REGDATE, STATUS, COMPANY_CODE, LANG_KEY, LANG_KEY_DESC, PATH, CYCLE ) AS ( SELECT 1 AS LEVEL, MENU_TYPE, OBJID::numeric, PARENT_OBJ_ID, MENU_NAME_KOR, MENU_URL, MENU_DESC, SEQ, WRITER, REGDATE, STATUS, COMPANY_CODE, LANG_KEY, LANG_KEY_DESC, ARRAY [MENU.OBJID], FALSE FROM MENU_INFO MENU WHERE PARENT_OBJ_ID = 0 AND MENU_TYPE = 1 UNION ALL SELECT V_MENU.LEVEL + 1, MENU_SUB.MENU_TYPE, MENU_SUB.OBJID, MENU_SUB.PARENT_OBJ_ID, MENU_SUB.MENU_NAME_KOR, MENU_SUB.MENU_URL, MENU_SUB.MENU_DESC, MENU_SUB.SEQ, MENU_SUB.WRITER, MENU_SUB.REGDATE, MENU_SUB.STATUS, MENU_SUB.COMPANY_CODE, MENU_SUB.LANG_KEY, MENU_SUB.LANG_KEY_DESC, PATH || MENU_SUB.SEQ::numeric, MENU_SUB.OBJID = ANY(PATH) FROM MENU_INFO MENU_SUB JOIN V_MENU ON MENU_SUB.PARENT_OBJ_ID = V_MENU.OBJID WHERE 1 = 1 ) SELECT LEVEL AS LEV, CASE MENU_TYPE WHEN '0' THEN 'admin' WHEN '1' THEN 'user' ELSE '' END AS MENU_TYPE, A.OBJID, A.PARENT_OBJ_ID, A.MENU_NAME_KOR, LPAD(' ', 3 * (LEVEL - 1)) || A.MENU_NAME_KOR AS LPAD_MENU_NAME_KOR, A.MENU_URL, A.MENU_DESC, A.SEQ, A.WRITER, TO_CHAR(A.REGDATE, 'YYYY-MM-DD') AS REGDATE, A.STATUS, A.COMPANY_CODE, A.LANG_KEY, A.LANG_KEY_DESC, COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME, -- 번역된 메뉴명 (우선순위: 번역 > 기본명) COALESCE(MLT_NAME.lang_text, A.MENU_NAME_KOR) AS TRANSLATED_NAME, -- 번역된 설명 (우선순위: 번역 > 기본명) COALESCE(MLT_DESC.lang_text, A.MENU_DESC) AS TRANSLATED_DESC, CASE UPPER(A.STATUS) WHEN 'ACTIVE' THEN '활성화' WHEN 'INACTIVE' THEN '비활성화' ELSE '' END AS STATUS_TITLE FROM v_menu A LEFT JOIN COMPANY_MNG CM ON A.COMPANY_CODE = CM.COMPANY_CODE LEFT JOIN MULTI_LANG_KEY_MASTER MLKM_NAME ON A.LANG_KEY = MLKM_NAME.lang_key LEFT JOIN MULTI_LANG_TEXT MLT_NAME ON MLKM_NAME.key_id = MLT_NAME.key_id AND MLT_NAME.lang_code = $1 LEFT JOIN MULTI_LANG_KEY_MASTER MLKM_DESC ON A.LANG_KEY_DESC = MLKM_DESC.lang_key LEFT JOIN MULTI_LANG_TEXT MLT_DESC ON MLKM_DESC.key_id = MLT_DESC.key_id AND MLT_DESC.lang_code = $1 WHERE 1 = 1 ORDER BY PATH, SEQ `, [userLang] ); logger.info(`사용자 메뉴 목록 조회 결과: ${menuList.length}개`); if (menuList.length > 0) { logger.info("첫 번째 메뉴:", menuList[0]); } return menuList; } catch (error) { logger.error("AdminService.getUserMenuList 오류:", error); throw error; } } /** * 메뉴 정보 조회 */ static async getMenuInfo(menuId: string): Promise { try { logger.info(`AdminService.getMenuInfo 시작 - menuId: ${menuId}`); // Raw Query를 사용한 메뉴 정보 조회 (회사 정보 포함) const menuResult = await query( `SELECT m.*, c.company_name FROM menu_info m LEFT JOIN company_mng c ON m.company_code = c.company_code WHERE m.objid = $1::numeric`, [menuId] ); if (!menuResult || menuResult.length === 0) { return null; } const menuInfo = menuResult[0]; // 응답 형식 조정 (기존 형식과 호환성 유지) const result = { ...menuInfo, objid: menuInfo.objid?.toString(), menu_type: menuInfo.menu_type?.toString(), parent_obj_id: menuInfo.parent_obj_id?.toString(), seq: menuInfo.seq?.toString(), company_name: menuInfo.company_name || "미지정", }; logger.info("메뉴 정보 조회 결과:", result); return result; } catch (error) { logger.error("AdminService.getMenuInfo 오류:", error); throw error; } } }