import { Response } from "express"; import { screenManagementService } from "../services/screenManagementService"; import { AuthenticatedRequest } from "../types/auth"; // 화면 목록 조회 export const getScreens = async (req: AuthenticatedRequest, res: Response) => { try { const userCompanyCode = (req.user as any).companyCode; const { page = 1, size = 20, searchTerm, companyCode } = req.query; // 쿼리 파라미터로 companyCode가 전달되면 해당 회사의 화면 조회 (최고 관리자 전용) // 아니면 현재 사용자의 companyCode 사용 const targetCompanyCode = (companyCode as string) || userCompanyCode; // 최고 관리자가 아닌 경우 자신의 회사 코드만 사용 가능 if (userCompanyCode !== "*" && targetCompanyCode !== userCompanyCode) { return res.status(403).json({ success: false, message: "다른 회사의 화면을 조회할 권한이 없습니다.", }); } const result = await screenManagementService.getScreensByCompany( targetCompanyCode, parseInt(page as string), parseInt(size as string), searchTerm as string // 검색어 전달 ); res.json({ success: true, data: result.data, total: result.pagination.total, page: result.pagination.page, size: result.pagination.size, totalPages: result.pagination.totalPages, }); } catch (error) { console.error("화면 목록 조회 실패:", error); res .status(500) .json({ success: false, message: "화면 목록 조회에 실패했습니다." }); } }; // 단일 화면 조회 export const getScreen = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { companyCode } = req.user as any; const screen = await screenManagementService.getScreen( parseInt(id), companyCode ); if (!screen) { res.status(404).json({ success: false, message: "화면을 찾을 수 없습니다.", }); return; } res.json({ success: true, data: screen }); } catch (error) { console.error("화면 조회 실패:", error); res .status(500) .json({ success: false, message: "화면 조회에 실패했습니다." }); } }; // 화면에 할당된 메뉴 조회 export const getScreenMenu = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { companyCode } = req.user as any; const menuInfo = await screenManagementService.getMenuByScreen( parseInt(id), companyCode ); res.json({ success: true, data: menuInfo }); } catch (error) { console.error("화면 메뉴 조회 실패:", error); res .status(500) .json({ success: false, message: "화면 메뉴 조회에 실패했습니다." }); } }; // 화면 생성 export const createScreen = async ( req: AuthenticatedRequest, res: Response ) => { try { const { companyCode } = req.user as any; const screenData = { ...req.body, companyCode }; const newScreen = await screenManagementService.createScreen( screenData, companyCode ); res.status(201).json({ success: true, data: newScreen }); } catch (error) { console.error("화면 생성 실패:", error); res .status(500) .json({ success: false, message: "화면 생성에 실패했습니다." }); } }; // 화면 수정 export const updateScreen = async ( req: AuthenticatedRequest, res: Response ) => { try { const { id } = req.params; const { companyCode } = req.user as any; const updateData = { ...req.body, companyCode }; const updatedScreen = await screenManagementService.updateScreen( parseInt(id), updateData, companyCode ); res.json({ success: true, data: updatedScreen }); } catch (error) { console.error("화면 수정 실패:", error); res .status(500) .json({ success: false, message: "화면 수정에 실패했습니다." }); } }; // 화면 정보 수정 (메타데이터만) export const updateScreenInfo = async ( req: AuthenticatedRequest, res: Response ) => { try { const { id } = req.params; const { companyCode } = req.user as any; const { screenName, tableName, description, isActive } = req.body; await screenManagementService.updateScreenInfo( parseInt(id), { screenName, tableName, description, isActive }, companyCode ); res.json({ success: true, message: "화면 정보가 수정되었습니다." }); } catch (error) { console.error("화면 정보 수정 실패:", error); res .status(500) .json({ success: false, message: "화면 정보 수정에 실패했습니다." }); } }; // 화면 의존성 체크 export const checkScreenDependencies = async ( req: AuthenticatedRequest, res: Response ) => { try { const { id } = req.params; const { companyCode } = req.user as any; const result = await screenManagementService.checkScreenDependencies( parseInt(id), companyCode ); res.json({ success: true, ...result }); } catch (error) { console.error("화면 의존성 체크 실패:", error); res .status(500) .json({ success: false, message: "의존성 체크에 실패했습니다." }); } }; // 화면 삭제 (휴지통으로 이동) export const deleteScreen = async ( req: AuthenticatedRequest, res: Response ) => { try { const { id } = req.params; const { companyCode, userId } = req.user as any; const { deleteReason, force } = req.body; await screenManagementService.deleteScreen( parseInt(id), companyCode, userId, deleteReason, force || false ); res.json({ success: true, message: "화면이 휴지통으로 이동되었습니다." }); } catch (error: any) { console.error("화면 삭제 실패:", error); // 의존성 오류인 경우 특별 처리 if (error.code === "SCREEN_HAS_DEPENDENCIES") { res.status(409).json({ success: false, message: error.message, code: error.code, dependencies: error.dependencies, }); return; } res .status(500) .json({ success: false, message: "화면 삭제에 실패했습니다." }); } }; // 화면 복원 (휴지통에서 복원) export const restoreScreen = async ( req: AuthenticatedRequest, res: Response ) => { try { const { id } = req.params; const { companyCode, userId } = req.user as any; await screenManagementService.restoreScreen( parseInt(id), companyCode, userId ); res.json({ success: true, message: "화면이 복원되었습니다." }); } catch (error) { console.error("화면 복원 실패:", error); res .status(500) .json({ success: false, message: "화면 복원에 실패했습니다." }); } }; // 화면 영구 삭제 export const permanentDeleteScreen = async ( req: AuthenticatedRequest, res: Response ) => { try { const { id } = req.params; const { companyCode } = req.user as any; await screenManagementService.permanentDeleteScreen( parseInt(id), companyCode ); res.json({ success: true, message: "화면이 영구적으로 삭제되었습니다." }); } catch (error) { console.error("화면 영구 삭제 실패:", error); res .status(500) .json({ success: false, message: "화면 영구 삭제에 실패했습니다." }); } }; // 휴지통 화면 목록 조회 export const getDeletedScreens = async ( req: AuthenticatedRequest, res: Response ) => { try { const { companyCode } = req.user as any; const page = parseInt(req.query.page as string) || 1; const size = parseInt(req.query.size as string) || 20; const result = await screenManagementService.getDeletedScreens( companyCode, page, size ); res.json({ success: true, ...result }); } catch (error) { console.error("휴지통 화면 목록 조회 실패:", error); res.status(500).json({ success: false, message: "휴지통 화면 목록 조회에 실패했습니다.", }); } }; // 휴지통 화면 일괄 영구 삭제 export const bulkPermanentDeleteScreens = async ( req: AuthenticatedRequest, res: Response ) => { try { const { companyCode } = req.user as any; const { screenIds } = req.body; if (!Array.isArray(screenIds) || screenIds.length === 0) { return res.status(400).json({ success: false, message: "삭제할 화면 ID 목록이 필요합니다.", }); } const result = await screenManagementService.bulkPermanentDeleteScreens( screenIds, companyCode ); let message = `${result.deletedCount}개 화면이 영구 삭제되었습니다.`; if (result.skippedCount > 0) { message += ` (${result.skippedCount}개 화면은 삭제되지 않았습니다.)`; } return res.json({ success: true, message, result: { deletedCount: result.deletedCount, skippedCount: result.skippedCount, errors: result.errors, }, }); } catch (error) { console.error("휴지통 화면 일괄 삭제 실패:", error); return res.status(500).json({ success: false, message: "일괄 삭제에 실패했습니다.", }); } }; // 연결된 모달 화면 감지 (화면 복사 전 확인) export const detectLinkedScreens = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const linkedScreens = await screenManagementService.detectLinkedModalScreens( parseInt(id) ); res.json({ success: true, data: linkedScreens, message: linkedScreens.length > 0 ? `${linkedScreens.length}개의 연결된 모달 화면을 감지했습니다.` : "연결된 모달 화면이 없습니다.", }); } catch (error: any) { console.error("연결된 화면 감지 실패:", error); res.status(500).json({ success: false, message: error.message || "연결된 화면 감지에 실패했습니다.", }); } }; // 화면명 중복 체크 export const checkDuplicateScreenName = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, screenName } = req.body; if (!companyCode || !screenName) { res.status(400).json({ success: false, message: "companyCode와 screenName은 필수입니다.", }); return; } const isDuplicate = await screenManagementService.checkDuplicateScreenName( companyCode, screenName ); res.json({ success: true, data: { isDuplicate }, message: isDuplicate ? "이미 존재하는 화면명입니다." : "사용 가능한 화면명입니다.", }); } catch (error: any) { console.error("화면명 중복 체크 실패:", error); res.status(500).json({ success: false, message: error.message || "화면명 중복 체크에 실패했습니다.", }); } }; // 화면 일괄 복사 (메인 + 모달 화면들) export const copyScreenWithModals = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { mainScreen, modalScreens, targetCompanyCode } = req.body; const { companyCode, userId } = req.user as any; if (!mainScreen || !mainScreen.screenName || !mainScreen.screenCode) { res.status(400).json({ success: false, message: "메인 화면 정보(screenName, screenCode)가 필요합니다.", }); return; } const result = await screenManagementService.copyScreenWithModals({ sourceScreenId: parseInt(id), companyCode, userId, targetCompanyCode, // 최고 관리자가 다른 회사로 복사할 때 사용 mainScreen: { screenName: mainScreen.screenName, screenCode: mainScreen.screenCode, description: mainScreen.description, }, modalScreens: modalScreens || [], }); res.json({ success: true, data: result, message: `화면 복사가 완료되었습니다. (메인 1개 + 모달 ${result.modalScreens.length}개)`, }); } catch (error: any) { console.error("화면 일괄 복사 실패:", error); res.status(500).json({ success: false, message: error.message || "화면 일괄 복사에 실패했습니다.", }); } }; // 화면 복사 (단일 - 하위 호환용) export const copyScreen = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { screenName, screenCode, description } = req.body; const { companyCode, userId } = req.user as any; const copiedScreen = await screenManagementService.copyScreen( parseInt(id), { screenName, screenCode, description, companyCode, createdBy: userId, } ); res.json({ success: true, data: copiedScreen, message: "화면이 복사되었습니다.", }); } catch (error: any) { console.error("화면 복사 실패:", error); res.status(500).json({ success: false, message: error.message || "화면 복사에 실패했습니다.", }); } }; // 테이블 목록 조회 (모든 테이블) export const getTables = async (req: AuthenticatedRequest, res: Response) => { try { const { companyCode } = req.user as any; const tables = await screenManagementService.getTables(companyCode); res.json({ success: true, data: tables }); } catch (error) { console.error("테이블 목록 조회 실패:", error); res .status(500) .json({ success: false, message: "테이블 목록 조회에 실패했습니다." }); } }; // 특정 테이블 정보 조회 (최적화된 단일 테이블 조회) export const getTableInfo = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { tableName } = req.params; const { companyCode } = req.user as any; if (!tableName) { res.status(400).json({ success: false, message: "테이블명이 필요합니다.", }); return; } const tableInfo = await screenManagementService.getTableInfo( tableName, companyCode ); if (!tableInfo) { res.status(404).json({ success: false, message: `테이블 '${tableName}'을 찾을 수 없습니다.`, }); return; } res.json({ success: true, data: tableInfo }); } catch (error) { console.error("테이블 정보 조회 실패:", error); res .status(500) .json({ success: false, message: "테이블 정보 조회에 실패했습니다." }); } }; // 테이블 컬럼 정보 조회 export const getTableColumns = async ( req: AuthenticatedRequest, res: Response ) => { try { const { tableName } = req.params; const { companyCode } = req.user as any; const columns = await screenManagementService.getTableColumns( tableName, companyCode ); res.json({ success: true, data: columns }); } catch (error) { console.error("테이블 컬럼 조회 실패:", error); res .status(500) .json({ success: false, message: "테이블 컬럼 조회에 실패했습니다." }); } }; // 레이아웃 저장 export const saveLayout = async (req: AuthenticatedRequest, res: Response) => { try { const { screenId } = req.params; const { companyCode } = req.user as any; const layoutData = req.body; const savedLayout = await screenManagementService.saveLayout( parseInt(screenId), layoutData, companyCode ); res.json({ success: true, data: savedLayout }); } catch (error) { console.error("레이아웃 저장 실패:", error); res .status(500) .json({ success: false, message: "레이아웃 저장에 실패했습니다." }); } }; // 레이아웃 조회 export const getLayout = async (req: AuthenticatedRequest, res: Response) => { try { const { screenId } = req.params; const { companyCode } = req.user as any; const layout = await screenManagementService.getLayout( parseInt(screenId), companyCode ); res.json({ success: true, data: layout }); } catch (error) { console.error("레이아웃 조회 실패:", error); res .status(500) .json({ success: false, message: "레이아웃 조회에 실패했습니다." }); } }; // 화면 코드 자동 생성 export const generateScreenCode = async ( req: AuthenticatedRequest, res: Response ) => { try { const { companyCode: paramCompanyCode } = req.params; const { companyCode: userCompanyCode } = req.user as any; // 사용자의 회사 코드 또는 파라미터의 회사 코드 사용 const targetCompanyCode = paramCompanyCode || userCompanyCode; const generatedCode = await screenManagementService.generateScreenCode(targetCompanyCode); res.json({ success: true, data: { screenCode: generatedCode } }); } catch (error) { console.error("화면 코드 생성 실패:", error); res .status(500) .json({ success: false, message: "화면 코드 생성에 실패했습니다." }); } }; // 여러 개의 화면 코드 일괄 생성 export const generateMultipleScreenCodes = async ( req: AuthenticatedRequest, res: Response ) => { try { const { companyCode, count } = req.body; if (!companyCode || typeof companyCode !== "string") { res.status(400).json({ success: false, message: "회사 코드(companyCode)는 필수입니다.", }); return; } if (!count || typeof count !== "number" || count < 1 || count > 100) { res.status(400).json({ success: false, message: "count는 1~100 사이의 숫자여야 합니다.", }); return; } const screenCodes = await screenManagementService.generateMultipleScreenCodes( companyCode, count ); res.json({ success: true, data: { screenCodes }, message: `${count}개의 화면 코드가 생성되었습니다.`, }); } catch (error: any) { console.error("화면 코드 일괄 생성 실패:", error); res.status(500).json({ success: false, message: error.message || "화면 코드 일괄 생성에 실패했습니다.", }); } }; // 화면-메뉴 할당 export const assignScreenToMenu = async ( req: AuthenticatedRequest, res: Response ) => { try { const { screenId } = req.params; const { companyCode } = req.user as any; const assignmentData = { ...req.body, companyCode }; await screenManagementService.assignScreenToMenu( parseInt(screenId), assignmentData ); res.json({ success: true, message: "화면이 메뉴에 성공적으로 할당되었습니다.", }); } catch (error) { console.error("화면-메뉴 할당 실패:", error); res .status(500) .json({ success: false, message: "화면-메뉴 할당에 실패했습니다." }); } }; // 메뉴별 할당된 화면 목록 조회 export const getScreensByMenu = async ( req: AuthenticatedRequest, res: Response ) => { try { const { menuObjid } = req.params; const { companyCode } = req.user as any; const screens = await screenManagementService.getScreensByMenu( parseInt(menuObjid), companyCode ); res.json({ success: true, data: screens }); } catch (error) { console.error("메뉴별 화면 조회 실패:", error); res .status(500) .json({ success: false, message: "메뉴별 화면 조회에 실패했습니다." }); } }; // 화면-메뉴 할당 해제 export const unassignScreenFromMenu = async ( req: AuthenticatedRequest, res: Response ) => { try { const { screenId, menuObjid } = req.params; const { companyCode } = req.user as any; await screenManagementService.unassignScreenFromMenu( parseInt(screenId), parseInt(menuObjid), companyCode ); res.json({ success: true, message: "화면-메뉴 할당이 해제되었습니다." }); } catch (error) { console.error("화면-메뉴 할당 해제 실패:", error); res .status(500) .json({ success: false, message: "화면-메뉴 할당 해제에 실패했습니다." }); } }; // 휴지통 화면들의 메뉴 할당 정리 (관리자용) export const cleanupDeletedScreenMenuAssignments = async ( req: AuthenticatedRequest, res: Response ) => { try { const result = await screenManagementService.cleanupDeletedScreenMenuAssignments(); return res.json({ success: true, message: result.message, updatedCount: result.updatedCount, }); } catch (error) { console.error("메뉴 할당 정리 실패:", error); return res.status(500).json({ success: false, message: "메뉴 할당 정리에 실패했습니다.", }); } };