import { Response } from "express"; import { dynamicFormService } from "../services/dynamicFormService"; import { enhancedDynamicFormService } from "../services/enhancedDynamicFormService"; import { AuthenticatedRequest } from "../types/auth"; // 폼 데이터 저장 (기존 버전 - 레거시 지원) export const saveFormData = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, userId } = req.user as any; const { screenId, tableName, data } = req.body; // 🔍 디버깅: 사용자 정보 확인 console.log("🔍 [saveFormData] 사용자 정보:", { userId, companyCode, reqUser: req.user, dataWriter: data.writer, }); // 필수 필드 검증 (screenId는 0일 수 있으므로 undefined 체크) if (screenId === undefined || screenId === null || !tableName || !data) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (screenId, tableName, data)", }); } // 메타데이터 추가 (사용자가 입력한 경우에만 company_code 추가) const formDataWithMeta = { ...data, created_by: userId, updated_by: userId, writer: data.writer || userId, // ✅ writer가 없으면 userId로 설정 screen_id: screenId, }; console.log("✅ [saveFormData] 최종 writer 값:", formDataWithMeta.writer); // company_code는 사용자가 명시적으로 입력한 경우에만 추가 if (data.company_code !== undefined) { formDataWithMeta.company_code = data.company_code; } else if (companyCode && companyCode !== "*") { // 기본 company_code가 '*'가 아닌 경우에만 추가 formDataWithMeta.company_code = companyCode; } // 클라이언트 IP 주소 추출 const ipAddress = req.ip || (req.headers["x-forwarded-for"] as string) || req.socket.remoteAddress || "unknown"; const result = await dynamicFormService.saveFormData( screenId, tableName, formDataWithMeta, ipAddress ); res.json({ success: true, data: result, message: "데이터가 성공적으로 저장되었습니다.", }); } catch (error: any) { console.error("❌ 폼 데이터 저장 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 저장에 실패했습니다.", }); } }; // 개선된 폼 데이터 저장 (새 버전) export const saveFormDataEnhanced = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, userId } = req.user as any; const { screenId, tableName, data } = req.body; // 필수 필드 검증 if (screenId === undefined || screenId === null || !tableName || !data) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (screenId, tableName, data)", }); } // 메타데이터 추가 const formDataWithMeta = { ...data, created_by: userId, updated_by: userId, writer: data.writer || userId, // ✅ writer가 없으면 userId로 설정 screen_id: screenId, }; // company_code 처리 if (data.company_code !== undefined) { formDataWithMeta.company_code = data.company_code; } else if (companyCode && companyCode !== "*") { formDataWithMeta.company_code = companyCode; } // 개선된 서비스 사용 const result = await enhancedDynamicFormService.saveFormData( screenId, tableName, formDataWithMeta ); res.json(result); } catch (error: any) { console.error("❌ 개선된 폼 데이터 저장 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 저장에 실패했습니다.", }); } }; // 폼 데이터 업데이트 export const updateFormData = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { companyCode, userId } = req.user as any; const { tableName, data } = req.body; if (!tableName || !data) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (tableName, data)", }); } // 메타데이터 추가 const formDataWithMeta = { ...data, updated_by: userId, writer: data.writer || userId, // ✅ writer가 없으면 userId로 설정 updated_at: new Date(), }; const result = await dynamicFormService.updateFormData( id, // parseInt 제거 - 문자열 ID 지원 tableName, formDataWithMeta ); res.json({ success: true, data: result, message: "데이터가 성공적으로 업데이트되었습니다.", }); } catch (error: any) { console.error("❌ 폼 데이터 업데이트 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 업데이트에 실패했습니다.", }); } }; // 폼 데이터 부분 업데이트 (변경된 필드만) export const updateFormDataPartial = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { companyCode, userId } = req.user as any; const { tableName, originalData, newData } = req.body; if (!tableName || !originalData || !newData) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (tableName, originalData, newData)", }); } console.log("🔄 컨트롤러: 부분 업데이트 요청:", { id, tableName, originalData, newData, }); // 메타데이터 추가 const newDataWithMeta = { ...newData, updated_by: userId, writer: newData.writer || userId, // ✅ writer가 없으면 userId로 설정 }; const result = await dynamicFormService.updateFormDataPartial( id, // 🔧 parseInt 제거 - UUID 문자열도 지원 tableName, originalData, newDataWithMeta ); res.json({ success: true, data: result, message: "데이터가 성공적으로 업데이트되었습니다.", }); } catch (error: any) { console.error("❌ 부분 업데이트 실패:", error); res.status(500).json({ success: false, message: error.message || "부분 업데이트에 실패했습니다.", }); } }; // 폼 데이터 삭제 export const deleteFormData = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { companyCode, userId } = req.user as any; const { tableName } = req.body; if (!tableName) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (tableName)", }); } await dynamicFormService.deleteFormData(id, tableName, companyCode, userId); // userId 추가 res.json({ success: true, message: "데이터가 성공적으로 삭제되었습니다.", }); } catch (error: any) { console.error("❌ 폼 데이터 삭제 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 삭제에 실패했습니다.", }); } }; // 테이블의 기본키 조회 export const getTablePrimaryKeys = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { tableName } = req.params; if (!tableName) { return res.status(400).json({ success: false, message: "테이블명이 누락되었습니다.", }); } console.log(`🔑 테이블 ${tableName}의 기본키 조회 요청`); const primaryKeys = await dynamicFormService.getTablePrimaryKeys(tableName); console.log(`✅ 테이블 ${tableName}의 기본키:`, primaryKeys); res.json({ success: true, data: primaryKeys, message: "기본키 조회가 완료되었습니다.", }); } catch (error: any) { console.error("❌ 기본키 조회 실패:", error); res.status(500).json({ success: false, message: error.message || "기본키 조회에 실패했습니다.", }); } }; // 단일 폼 데이터 조회 export const getFormData = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { id } = req.params; const { companyCode } = req.user as any; const data = await dynamicFormService.getFormData(parseInt(id)); if (!data) { return res.status(404).json({ success: false, message: "데이터를 찾을 수 없습니다.", }); } res.json({ success: true, data: data, }); } catch (error: any) { console.error("❌ 폼 데이터 단건 조회 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 조회에 실패했습니다.", }); } }; // 화면별 폼 데이터 목록 조회 export const getFormDataList = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { screenId } = req.params; const { companyCode } = req.user as any; const { page = 1, size = 10, search = "", sortBy = "created_at", sortOrder = "desc", } = req.query; const result = await dynamicFormService.getFormDataList( parseInt(screenId as string), { page: parseInt(page as string), size: parseInt(size as string), search: search as string, sortBy: sortBy as string, sortOrder: sortOrder as "asc" | "desc", } ); res.json({ success: true, data: result, }); } catch (error: any) { console.error("❌ 폼 데이터 목록 조회 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 조회에 실패했습니다.", }); } }; // 폼 데이터 검증 export const validateFormData = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { tableName, data } = req.body; if (!tableName || !data) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (tableName, data)", }); } const validationResult = await dynamicFormService.validateFormData( tableName, data ); res.json({ success: true, data: validationResult, }); } catch (error: any) { console.error("❌ 폼 데이터 검증 실패:", error); res.status(500).json({ success: false, message: error.message || "데이터 검증에 실패했습니다.", }); } }; // 테이블 컬럼 정보 조회 (검증용) export const getTableColumns = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { tableName } = req.params; const columns = await dynamicFormService.getTableColumns(tableName); res.json({ success: true, data: { tableName, columns, }, }); } catch (error: any) { console.error("❌ 테이블 컬럼 정보 조회 실패:", error); res.status(500).json({ success: false, message: error.message || "테이블 정보 조회에 실패했습니다.", }); } }; // 특정 필드만 업데이트 (다른 테이블 지원) export const updateFieldValue = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, userId } = req.user as any; const { tableName, keyField, keyValue, updateField, updateValue } = req.body; console.log("🔄 [updateFieldValue] 요청:", { tableName, keyField, keyValue, updateField, updateValue, userId, companyCode, }); // 필수 필드 검증 if ( !tableName || !keyField || keyValue === undefined || !updateField || updateValue === undefined ) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (tableName, keyField, keyValue, updateField, updateValue)", }); } // SQL 인젝션 방지를 위한 테이블명/컬럼명 검증 const validNamePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/; if ( !validNamePattern.test(tableName) || !validNamePattern.test(keyField) || !validNamePattern.test(updateField) ) { return res.status(400).json({ success: false, message: "유효하지 않은 테이블명 또는 컬럼명입니다.", }); } // 업데이트 쿼리 실행 const result = await dynamicFormService.updateFieldValue( tableName, keyField, keyValue, updateField, updateValue, companyCode, userId ); console.log("✅ [updateFieldValue] 성공:", result); res.json({ success: true, data: result, message: "필드 값이 업데이트되었습니다.", }); } catch (error: any) { console.error("❌ [updateFieldValue] 실패:", error); res.status(500).json({ success: false, message: error.message || "필드 업데이트에 실패했습니다.", }); } }; /** * 위치 이력 저장 (연속 위치 추적용) * POST /api/dynamic-form/location-history */ export const saveLocationHistory = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode, userId: loginUserId } = req.user as any; const { latitude, longitude, accuracy, altitude, speed, heading, tripId, tripStatus, departure, arrival, departureName, destinationName, recordedAt, vehicleId, userId: requestUserId, // 프론트엔드에서 보낸 userId (차량 번호판 등) } = req.body; // 프론트엔드에서 보낸 userId가 있으면 그것을 사용 (차량 번호판 등) // 없으면 로그인한 사용자의 userId 사용 const userId = requestUserId || loginUserId; console.log("📍 [saveLocationHistory] 요청:", { userId, requestUserId, loginUserId, companyCode, latitude, longitude, tripId, }); // 필수 필드 검증 if (latitude === undefined || longitude === undefined) { return res.status(400).json({ success: false, message: "필수 필드가 누락되었습니다. (latitude, longitude)", }); } const result = await dynamicFormService.saveLocationHistory({ userId, companyCode, latitude, longitude, accuracy, altitude, speed, heading, tripId, tripStatus: tripStatus || "active", departure, arrival, departureName, destinationName, recordedAt: recordedAt || new Date().toISOString(), vehicleId, }); console.log("✅ [saveLocationHistory] 성공:", result); res.json({ success: true, data: result, message: "위치 이력이 저장되었습니다.", }); } catch (error: any) { console.error("❌ [saveLocationHistory] 실패:", error); res.status(500).json({ success: false, message: error.message || "위치 이력 저장에 실패했습니다.", }); } }; /** * 위치 이력 조회 (경로 조회용) * GET /api/dynamic-form/location-history/:tripId */ export const getLocationHistory = async ( req: AuthenticatedRequest, res: Response ): Promise => { try { const { companyCode } = req.user as any; const { tripId } = req.params; const { userId, startDate, endDate, limit } = req.query; console.log("📍 [getLocationHistory] 요청:", { tripId, userId, startDate, endDate, limit, }); const result = await dynamicFormService.getLocationHistory({ companyCode, tripId, userId: userId as string, startDate: startDate as string, endDate: endDate as string, limit: limit ? parseInt(limit as string) : 1000, }); res.json({ success: true, data: result, count: result.length, }); } catch (error: any) { console.error("❌ [getLocationHistory] 실패:", error); res.status(500).json({ success: false, message: error.message || "위치 이력 조회에 실패했습니다.", }); } };