/** * ๐Ÿ”ฅ ๊ฐœ์„ ๋œ ๋ฒ„ํŠผ ์•ก์…˜ ์‹คํ–‰๊ธฐ * * ๊ณ„ํš์„œ์— ๋”ฐ๋ฅธ ์ƒˆ๋กœ์šด ์‹คํ–‰ ํ”Œ๋กœ์šฐ: * 1. Before ํƒ€์ด๋ฐ ์ œ์–ด ์‹คํ–‰ * 2. ๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰ (replace๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ) * 3. After ํƒ€์ด๋ฐ ์ œ์–ด ์‹คํ–‰ */ import { toast } from "sonner"; import { apiClient } from "@/lib/api/client"; import { ExtendedButtonTypeConfig, ButtonDataflowConfig } from "@/types/control-management"; import { ButtonActionType } from "@/types/unified-core"; // ===== ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ ===== export interface ButtonExecutionContext { buttonId: string; screenId: string; userId: string; companyCode: string; startTime: number; formData?: Record; selectedRows?: any[]; tableData?: any[]; flowSelectedData?: any[]; flowSelectedStepId?: number | null; } export interface ExecutionResult { success: boolean; message: string; executionTime: number; data?: any; error?: string; } export interface ButtonExecutionResult { success: boolean; results: ExecutionResult[]; executionTime: number; error?: string; } interface ControlConfig { type: "relationship"; relationshipConfig: { relationshipId: string; relationshipName: string; executionTiming: "before" | "after" | "replace"; contextData?: Record; }; } interface ExecutionPlan { beforeControls: ControlConfig[]; afterControls: ControlConfig[]; hasReplaceControl: boolean; } // ===== ๋ฉ”์ธ ์‹คํ–‰๊ธฐ ํด๋ž˜์Šค ===== export class ImprovedButtonActionExecutor { /** * ๐Ÿ”ฅ ๊ฐœ์„ ๋œ ๋ฒ„ํŠผ ์•ก์…˜ ์‹คํ–‰ */ static async executeButtonAction( buttonConfig: ExtendedButtonTypeConfig, formData: Record, context: ButtonExecutionContext, ): Promise { console.log("๐Ÿ”ฅ ImprovedButtonActionExecutor ์‹œ์ž‘:", { buttonConfig, formData, context, }); const executionPlan = this.createExecutionPlan(buttonConfig); const results: ExecutionResult[] = []; console.log("๐Ÿ“‹ ์ƒ์„ฑ๋œ ์‹คํ–‰ ๊ณ„ํš:", { beforeControls: executionPlan.beforeControls, afterControls: executionPlan.afterControls, hasReplaceControl: executionPlan.hasReplaceControl, }); try { console.log("๐Ÿš€ ๋ฒ„ํŠผ ์•ก์…˜ ์‹คํ–‰ ์‹œ์ž‘:", { actionType: buttonConfig.actionType, hasControls: executionPlan.beforeControls.length + executionPlan.afterControls.length > 0, hasReplace: executionPlan.hasReplaceControl, }); // 1. Before ํƒ€์ด๋ฐ ์ œ์–ด ์‹คํ–‰ if (executionPlan.beforeControls.length > 0) { console.log("โฐ Before ์ œ์–ด ์‹คํ–‰ ์‹œ์ž‘"); const beforeResults = await this.executeControls(executionPlan.beforeControls, formData, context); results.push(...beforeResults); // Before ์ œ์–ด ์ค‘ ์‹คํŒจ๊ฐ€ ์žˆ์œผ๋ฉด ์ค‘๋‹จ const hasFailure = beforeResults.some((r) => !r.success); if (hasFailure) { throw new Error("Before ์ œ์–ด ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } } // 2. ๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰ (replace๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ์—๋งŒ) if (!executionPlan.hasReplaceControl) { console.log("โšก ๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰:", buttonConfig.actionType); const mainResult = await this.executeMainAction(buttonConfig, formData, context); results.push(mainResult); if (!mainResult.success) { throw new Error("๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } } else { console.log("๐Ÿ”„ Replace ๋ชจ๋“œ: ๋ฉ”์ธ ์•ก์…˜ ๊ฑด๋„ˆ๋œ€"); } // 3. After ํƒ€์ด๋ฐ ์ œ์–ด ์‹คํ–‰ if (executionPlan.afterControls.length > 0) { console.log("โฐ After ์ œ์–ด ์‹คํ–‰ ์‹œ์ž‘"); const afterResults = await this.executeControls(executionPlan.afterControls, formData, context); results.push(...afterResults); } const totalExecutionTime = Date.now() - context.startTime; console.log("โœ… ๋ฒ„ํŠผ ์•ก์…˜ ์‹คํ–‰ ์™„๋ฃŒ:", `${totalExecutionTime}ms`); return { success: true, results, executionTime: totalExecutionTime, }; } catch (error) { console.error("โŒ ๋ฒ„ํŠผ ์•ก์…˜ ์‹คํ–‰ ์‹คํŒจ:", error); // ๋กค๋ฐฑ ์ฒ˜๋ฆฌ await this.handleExecutionError(error, results, buttonConfig); return { success: false, results, executionTime: Date.now() - context.startTime, error: error.message, }; } } /** * ๐Ÿ”ฅ ์‹คํ–‰ ๊ณ„ํš ์ƒ์„ฑ */ private static createExecutionPlan(buttonConfig: ExtendedButtonTypeConfig): ExecutionPlan { const plan: ExecutionPlan = { beforeControls: [], afterControls: [], hasReplaceControl: false, }; const dataflowConfig = buttonConfig.dataflowConfig; if (!dataflowConfig) { console.log("โš ๏ธ dataflowConfig๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"); return plan; } // enableDataflowControl ์ฒดํฌ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  dataflowConfig๋งŒ ์žˆ์œผ๋ฉด ์‹คํ–‰ console.log("๐Ÿ“‹ ์‹คํ–‰ ๊ณ„ํš ์ƒ์„ฑ:", { controlMode: dataflowConfig.controlMode, hasRelationshipConfig: !!dataflowConfig.relationshipConfig, enableDataflowControl: buttonConfig.enableDataflowControl, }); // ๊ด€๊ณ„ ๊ธฐ๋ฐ˜ ์ œ์–ด๋งŒ ์ง€์› if (dataflowConfig.controlMode === "relationship" && dataflowConfig.relationshipConfig) { const control: ControlConfig = { type: "relationship", relationshipConfig: dataflowConfig.relationshipConfig, }; switch (dataflowConfig.relationshipConfig.executionTiming) { case "before": plan.beforeControls.push(control); break; case "after": plan.afterControls.push(control); break; case "replace": plan.afterControls.push(control); // Replace๋Š” after๋กœ ์ฒ˜๋ฆฌํ•˜๋˜ ํ”Œ๋ž˜๊ทธ ์„ค์ • plan.hasReplaceControl = true; break; } } return plan; } /** * ๐Ÿ”ฅ ์ œ์–ด ์‹คํ–‰ (๊ด€๊ณ„ ๋˜๋Š” ์™ธ๋ถ€ํ˜ธ์ถœ) */ private static async executeControls( controls: ControlConfig[], formData: Record, context: ButtonExecutionContext, ): Promise { const results: ExecutionResult[] = []; for (const control of controls) { try { // ๊ด€๊ณ„ ์‹คํ–‰๋งŒ ์ง€์› const result = await this.executeRelationship(control.relationshipConfig, formData, context); results.push(result); // ์ œ์–ด ์‹คํ–‰ ์‹คํŒจ ์‹œ ์ค‘๋‹จ if (!result.success) { throw new Error(result.message); } } catch (error) { console.error(`์ œ์–ด ์‹คํ–‰ ์‹คํŒจ (${control.type}):`, error); results.push({ success: false, message: `${control.type} ์ œ์–ด ์‹คํ–‰ ์‹คํŒจ: ${error.message}`, executionTime: 0, error: error.message, }); throw error; } } return results; } /** * ๐Ÿ”ฅ ๊ด€๊ณ„ ์‹คํ–‰ */ private static async executeRelationship( config: { relationshipId: string; relationshipName: string; executionTiming: "before" | "after" | "replace"; contextData?: Record; }, formData: Record, context: ButtonExecutionContext, ): Promise { try { console.log(`๐Ÿ”— ๊ด€๊ณ„ ์‹คํ–‰ ์‹œ์ž‘: ${config.relationshipName} (ID: ${config.relationshipId})`); // 1. ๊ด€๊ณ„ ์ •๋ณด ์กฐํšŒ const relationshipData = await this.getRelationshipData(config.relationshipId); if (!relationshipData) { throw new Error(`๊ด€๊ณ„ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ${config.relationshipId}`); } console.log("๐Ÿ“‹ ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ:", relationshipData); // 2. ๊ด€๊ณ„ ํƒ€์ž…์— ๋”ฐ๋ฅธ ์‹คํ–‰ const relationships = relationshipData.relationships; const connectionType = relationships.connectionType; console.log("๐Ÿ” ๊ด€๊ณ„ ์ƒ์„ธ ์ •๋ณด:", { connectionType, hasExternalCallConfig: !!relationships.externalCallConfig, externalCallConfig: relationships.externalCallConfig, hasDataSaveConfig: !!relationships.dataSaveConfig, dataSaveConfig: relationships.dataSaveConfig, }); let result: ExecutionResult; if (connectionType === "external_call") { // ์™ธ๋ถ€ ํ˜ธ์ถœ ์‹คํ–‰ result = await this.executeExternalCall(relationships, formData, context); } else if (connectionType === "data_save") { // ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹คํ–‰ result = await this.executeDataSave(relationships, formData, context); } else { throw new Error(`์ง€์›ํ•˜์ง€ ์•Š๋Š” ์—ฐ๊ฒฐ ํƒ€์ž…: ${connectionType}`); } console.log(`โœ… ๊ด€๊ณ„ ์‹คํ–‰ ์™„๋ฃŒ: ${config.relationshipName}`, result); if (result.success) { toast.success(`๊ด€๊ณ„ '${config.relationshipName}' ์‹คํ–‰ ์™„๋ฃŒ`); } else { toast.error(`๊ด€๊ณ„ '${config.relationshipName}' ์‹คํ–‰ ์‹คํŒจ: ${result.message}`); } return result; } catch (error: any) { console.error(`โŒ ๊ด€๊ณ„ ์‹คํ–‰ ์‹คํŒจ: ${config.relationshipName}`, error); const errorResult = { success: false, message: `๊ด€๊ณ„ '${config.relationshipName}' ์‹คํ–‰ ์‹คํŒจ: ${error.message}`, executionTime: 0, error: error.message, }; toast.error(errorResult.message); return errorResult; } } /** * ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ */ private static async getRelationshipData(relationshipId: string): Promise { try { console.log(`๐Ÿ” ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹œ์ž‘: ${relationshipId}`); const response = await apiClient.get(`/dataflow-diagrams/${relationshipId}`); console.log("โœ… ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์„ฑ๊ณต:", response.data); if (!response.data.success) { throw new Error(response.data.message || "๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ"); } return response.data.data; } catch (error) { console.error("๊ด€๊ณ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์˜ค๋ฅ˜:", error); throw error; } } /** * ์™ธ๋ถ€ ํ˜ธ์ถœ ์‹คํ–‰ */ private static async executeExternalCall( relationships: any, formData: Record, context: ButtonExecutionContext, ): Promise { try { console.log("๐Ÿ” ์™ธ๋ถ€ ํ˜ธ์ถœ ์‹คํ–‰ ์‹œ์ž‘ - relationships ๊ตฌ์กฐ:", relationships); const externalCallConfig = relationships.externalCallConfig; console.log("๐Ÿ” externalCallConfig:", externalCallConfig); if (!externalCallConfig) { console.error("โŒ ์™ธ๋ถ€ ํ˜ธ์ถœ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค. relationships ๊ตฌ์กฐ:", relationships); throw new Error("์™ธ๋ถ€ ํ˜ธ์ถœ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค"); } const restApiSettings = externalCallConfig.restApiSettings; if (!restApiSettings) { throw new Error("REST API ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค"); } console.log(`๐ŸŒ ์™ธ๋ถ€ API ํ˜ธ์ถœ: ${restApiSettings.apiUrl}`); // API ํ˜ธ์ถœ ์ค€๋น„ const headers: Record = { "Content-Type": "application/json", ...restApiSettings.headers, }; // ์ธ์ฆ ์ฒ˜๋ฆฌ if (restApiSettings.authentication?.type === "api-key") { headers["Authorization"] = `Bearer ${restApiSettings.authentication.apiKey}`; } // ์š”์ฒญ ๋ฐ”๋”” ์ค€๋น„ (ํ…œํ”Œ๋ฆฟ ์ฒ˜๋ฆฌ) let requestBody = restApiSettings.bodyTemplate || ""; if (requestBody) { // ๊ฐ„๋‹จํ•œ ํ…œํ”Œ๋ฆฟ ์น˜ํ™˜ ({{๋ณ€์ˆ˜๋ช…}} ํ˜•ํƒœ) requestBody = requestBody.replace(/\{\{(\w+)\}\}/g, (match: string, key: string) => { return formData[key] || (context as any).contextData?.[key] || new Date().toISOString(); }); } // ๋ฐฑ์—”๋“œ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•œ ์™ธ๋ถ€ API ํ˜ธ์ถœ (CORS ๋ฌธ์ œ ํ•ด๊ฒฐ) console.log("๐ŸŒ ๋ฐฑ์—”๋“œ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•œ ์™ธ๋ถ€ API ํ˜ธ์ถœ ์ค€๋น„:", { originalUrl: restApiSettings.apiUrl, method: restApiSettings.httpMethod || "GET", headers, body: restApiSettings.httpMethod !== "GET" ? requestBody : undefined, }); // ๋ฐฑ์—”๋“œ ํ”„๋ก์‹œ API ํ˜ธ์ถœ - GenericApiSettings ํ˜•์‹์— ๋งž๊ฒŒ ์ „๋‹ฌ const requestPayload = { diagramId: relationships.diagramId || 45, // ๊ด€๊ณ„ ID ์‚ฌ์šฉ relationshipId: relationships.relationshipId || "relationship-45", settings: { callType: "rest-api", apiType: "generic", url: restApiSettings.apiUrl, method: restApiSettings.httpMethod || "POST", headers: restApiSettings.headers || {}, body: requestBody, authentication: restApiSettings.authentication || { type: "none" }, timeout: restApiSettings.timeout || 30000, retryCount: restApiSettings.retryCount || 3, }, templateData: restApiSettings.httpMethod !== "GET" && requestBody ? JSON.parse(requestBody) : formData, }; console.log("๐Ÿ“ค ๋ฐฑ์—”๋“œ๋กœ ์ „์†กํ•  ๋ฐ์ดํ„ฐ:", requestPayload); const proxyResponse = await apiClient.post("/external-calls/execute", requestPayload); console.log("๐Ÿ“ก ๋ฐฑ์—”๋“œ ํ”„๋ก์‹œ ์‘๋‹ต:", proxyResponse.data); if (!proxyResponse.data.success) { throw new Error(`ํ”„๋ก์‹œ API ํ˜ธ์ถœ ์‹คํŒจ: ${proxyResponse.data.error || proxyResponse.data.message}`); } const responseData = proxyResponse.data.result; console.log("โœ… ์™ธ๋ถ€ API ํ˜ธ์ถœ ์„ฑ๊ณต (ํ”„๋ก์‹œ):", responseData); // ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์ฒ˜๋ฆฌ (inbound mapping) if (externalCallConfig.dataMappingConfig?.inboundMapping) { console.log(`๐Ÿ“ฅ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์„ค์ • ๋ฐœ๊ฒฌ - HTTP ๋ฉ”์„œ๋“œ: ${restApiSettings.httpMethod}`); console.log("๐Ÿ“ฅ ๋งคํ•‘ ์„ค์ •:", externalCallConfig.dataMappingConfig.inboundMapping); console.log("๐Ÿ“ฅ ์‘๋‹ต ๋ฐ์ดํ„ฐ:", responseData); await this.processInboundMapping(externalCallConfig.dataMappingConfig.inboundMapping, responseData, context); } else { console.log(`โ„น๏ธ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค - HTTP ๋ฉ”์„œ๋“œ: ${restApiSettings.httpMethod}`); } return { success: true, message: "์™ธ๋ถ€ ํ˜ธ์ถœ ์‹คํ–‰ ์™„๋ฃŒ", executionTime: Date.now() - context.startTime, data: responseData, }; } catch (error: any) { console.error("์™ธ๋ถ€ ํ˜ธ์ถœ ์‹คํ–‰ ์˜ค๋ฅ˜:", error); return { success: false, message: `์™ธ๋ถ€ ํ˜ธ์ถœ ์‹คํ–‰ ์‹คํŒจ: ${error.message}`, executionTime: Date.now() - context.startTime, error: error.message, }; } } /** * ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹คํ–‰ */ private static async executeDataSave( relationships: any, formData: Record, context: ButtonExecutionContext, ): Promise { try { console.log("๐Ÿ’พ ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹คํ–‰ ์‹œ์ž‘"); // ์ œ์–ด ์กฐ๊ฑด ํ™•์ธ const controlConditions = relationships.controlConditions || []; if (controlConditions.length > 0) { const conditionsMet = this.evaluateConditions(controlConditions, formData, context); if (!conditionsMet) { return { success: false, message: "์ œ์–ด ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ์•Š์•„ ๋ฐ์ดํ„ฐ ์ €์žฅ์„ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค", executionTime: Date.now() - context.startTime, }; } } // ์•ก์…˜ ๊ทธ๋ฃน ์‹คํ–‰ const actionGroups = relationships.actionGroups || []; const results = []; for (const actionGroup of actionGroups) { if (!actionGroup.isEnabled) { console.log(`โญ๏ธ ๋น„ํ™œ์„ฑํ™”๋œ ์•ก์…˜ ๊ทธ๋ฃน ๊ฑด๋„ˆ๋œ€: ${actionGroup.name}`); continue; } console.log(`๐ŸŽฏ ์•ก์…˜ ๊ทธ๋ฃน ์‹คํ–‰: ${actionGroup.name}`); for (const action of actionGroup.actions) { if (!action.isEnabled) { console.log(`โญ๏ธ ๋น„ํ™œ์„ฑํ™”๋œ ์•ก์…˜ ๊ฑด๋„ˆ๋œ€: ${action.name}`); continue; } const actionResult = await this.executeDataAction(action, relationships, formData, context); results.push(actionResult); if (!actionResult.success) { console.error(`โŒ ์•ก์…˜ ์‹คํ–‰ ์‹คํŒจ: ${action.name}`, actionResult); } } } const successCount = results.filter((r) => r.success).length; const totalCount = results.length; return { success: successCount > 0, message: `๋ฐ์ดํ„ฐ ์ €์žฅ ์™„๋ฃŒ: ${successCount}/${totalCount} ์•ก์…˜ ์„ฑ๊ณต`, executionTime: Date.now() - context.startTime, data: { results, successCount, totalCount, }, }; } catch (error: any) { console.error("๋ฐ์ดํ„ฐ ์ €์žฅ ์‹คํ–‰ ์˜ค๋ฅ˜:", error); return { success: false, message: `๋ฐ์ดํ„ฐ ์ €์žฅ ์‹คํ–‰ ์‹คํŒจ: ${error.message}`, executionTime: Date.now() - context.startTime, error: error.message, }; } } /** * ๊ฐœ๋ณ„ ๋ฐ์ดํ„ฐ ์•ก์…˜ ์‹คํ–‰ */ private static async executeDataAction( action: any, relationships: any, formData: Record, context: ButtonExecutionContext, ): Promise { try { console.log(`๐Ÿ”ง ๋ฐ์ดํ„ฐ ์•ก์…˜ ์‹คํ–‰: ${action.name} (${action.actionType})`); console.log("๐Ÿ“ฅ ๋ฐ›์€ formData:", formData); console.log("๐Ÿ“ฅ formData ํ‚ค๋“ค:", Object.keys(formData)); // ๐Ÿ”ฅ UPDATE ์•ก์…˜์˜ ๊ฒฝ์šฐ formData๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‹œ์ž‘ (๊ธฐ๋ณธํ‚ค ํฌํ•จ) const mappedData: Record = action.actionType === "update" ? { ...formData } : {}; // ํ•„๋“œ ๋งคํ•‘ ์ฒ˜๋ฆฌ (๊ธฐ์กด ๋ฐ์ดํ„ฐ์— ๋ฎ์–ด์“ฐ๊ธฐ) for (const mapping of action.fieldMappings) { if (mapping.valueType === "static") { // ์ •์  ๊ฐ’ ์ฒ˜๋ฆฌ let value = mapping.value; if (value === "#NOW") { value = new Date().toISOString(); } mappedData[mapping.targetField] = value; console.log(`๐Ÿ”ง ์ •์  ๊ฐ’ ๋งคํ•‘: ${mapping.targetField} = ${value}`); } else { // ํ•„๋“œ ๋งคํ•‘ ์ฒ˜๋ฆฌ const sourceField = mapping.fromField?.columnName; if (sourceField && formData[sourceField] !== undefined) { mappedData[mapping.toField.columnName] = formData[sourceField]; console.log(`๐Ÿ”ง ํ•„๋“œ ๋งคํ•‘: ${sourceField} โ†’ ${mapping.toField.columnName} = ${formData[sourceField]}`); } } } console.log("๐Ÿ“‹ ์ตœ์ข… ๋งคํ•‘๋œ ๋ฐ์ดํ„ฐ:", mappedData); console.log("๐Ÿ”‘ ๊ธฐ๋ณธํ‚ค ํฌํ•จ ์—ฌ๋ถ€ ์ฒดํฌ:", { hasId: "id" in mappedData, keys: Object.keys(mappedData), values: Object.values(mappedData), }); // ๋Œ€์ƒ ์—ฐ๊ฒฐ ์ •๋ณด const toConnection = relationships.toConnection; const targetTable = relationships.toTable?.tableName; if (!targetTable) { throw new Error("๋Œ€์ƒ ํ…Œ์ด๋ธ”์ด ์ง€์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค"); } // ๋ฐ์ดํ„ฐ ์ €์žฅ API ํ˜ธ์ถœ const saveResult = await this.saveDataToTable(targetTable, mappedData, action.actionType, toConnection); return { success: true, message: `๋ฐ์ดํ„ฐ ์•ก์…˜ "${action.name}" ์‹คํ–‰ ์™„๋ฃŒ`, executionTime: Date.now() - context.startTime, data: saveResult, }; } catch (error: any) { console.error(`๋ฐ์ดํ„ฐ ์•ก์…˜ ์‹คํ–‰ ์˜ค๋ฅ˜: ${action.name}`, error); return { success: false, message: `๋ฐ์ดํ„ฐ ์•ก์…˜ ์‹คํ–‰ ์‹คํŒจ: ${error.message}`, executionTime: Date.now() - context.startTime, error: error.message, }; } } /** * ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ ์ €์žฅ */ private static async saveDataToTable( tableName: string, data: Record, actionType: string, connection?: any, ): Promise { try { console.log(`๐Ÿ’พ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ €์žฅ ์‹œ์ž‘: ${tableName}`, { actionType, data, connection, }); // ๋ฐ์ดํ„ฐ ์ €์žฅ API ํ˜ธ์ถœ (apiClient ์‚ฌ์šฉ) const response = await apiClient.post("/dataflow/execute-data-action", { tableName, data, actionType, connection, }); console.log(`โœ… ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ €์žฅ ์„ฑ๊ณต: ${tableName}`, response.data); return response.data; } catch (error) { console.error("ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ €์žฅ ์˜ค๋ฅ˜:", error); throw error; } } /** * ์กฐ๊ฑด ํ‰๊ฐ€ */ private static evaluateConditions( conditions: any[], formData: Record, context: ButtonExecutionContext, ): boolean { console.log("๐Ÿ” ์กฐ๊ฑด ํ‰๊ฐ€ ์‹œ์ž‘:", { conditions, formDataKeys: Object.keys(formData), formData, contextData: context.contextData, }); for (const condition of conditions) { const fieldValue = formData[condition.field]; const conditionValue = condition.value; const operator = condition.operator; console.log("๐Ÿ” ๊ฐœ๋ณ„ ์กฐ๊ฑด ๊ฒ€์ฆ:", { field: condition.field, operator, expectedValue: conditionValue, actualValue: fieldValue, formDataHasField: condition.field in formData, allFormDataKeys: Object.keys(formData), }); let conditionMet = false; switch (operator) { case "=": conditionMet = fieldValue === conditionValue; break; case "!=": conditionMet = fieldValue !== conditionValue; break; case ">": conditionMet = Number(fieldValue) > Number(conditionValue); break; case "<": conditionMet = Number(fieldValue) < Number(conditionValue); break; case ">=": conditionMet = Number(fieldValue) >= Number(conditionValue); break; case "<=": conditionMet = Number(fieldValue) <= Number(conditionValue); break; default: console.warn(`์ง€์›ํ•˜์ง€ ์•Š๋Š” ์—ฐ์‚ฐ์ž: ${operator}`); conditionMet = true; } if (!conditionMet) { console.log(`โŒ ์กฐ๊ฑด ๋ถˆ๋งŒ์กฑ: ${condition.field} ${operator} ${conditionValue} (์‹ค์ œ๊ฐ’: ${fieldValue})`); console.log("โŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ•„๋“œ๋“ค:", Object.keys(formData)); console.log("โŒ ์ „์ฒด formData:", formData); return false; } } console.log("โœ… ๋ชจ๋“  ์กฐ๊ฑด ๋งŒ์กฑ"); return true; } /** * ๋‹ค์–‘ํ•œ API ์‘๋‹ต ๊ตฌ์กฐ์—์„œ ์‹ค์ œ ๋ฐ์ดํ„ฐ ์ถ”์ถœ */ private static extractActualData(responseData: any): any { console.log(`๐Ÿ” ๋ฐ์ดํ„ฐ ์ถ”์ถœ ์‹œ์ž‘ - ์›๋ณธ ํƒ€์ž…: ${typeof responseData}`); // null์ด๋‚˜ undefined์ธ ๊ฒฝ์šฐ if (!responseData) { console.log("โš ๏ธ ์‘๋‹ต ๋ฐ์ดํ„ฐ๊ฐ€ null ๋˜๋Š” undefined"); return []; } // ์ด๋ฏธ ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ (์ง์ ‘ ๋ฐฐ์—ด ์‘๋‹ต) if (Array.isArray(responseData)) { console.log("โœ… ์ง์ ‘ ๋ฐฐ์—ด ์‘๋‹ต ๊ฐ์ง€"); return responseData; } // ๋ฌธ์ž์—ด์ธ ๊ฒฝ์šฐ JSON ํŒŒ์‹ฑ ์‹œ๋„ if (typeof responseData === "string") { console.log("๐Ÿ”„ JSON ๋ฌธ์ž์—ด ํŒŒ์‹ฑ ์‹œ๋„"); try { const parsed = JSON.parse(responseData); console.log("โœ… JSON ํŒŒ์‹ฑ ์„ฑ๊ณต, ์žฌ๊ท€ ํ˜ธ์ถœ"); return this.extractActualData(parsed); } catch (error) { console.log("โš ๏ธ JSON ํŒŒ์‹ฑ ์‹คํŒจ, ์›๋ณธ ๋ฌธ์ž์—ด ๋ฐ˜ํ™˜:", error); return [responseData]; } } // ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ (์ˆซ์ž ๋“ฑ) if (typeof responseData !== "object") { console.log(`โš ๏ธ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ์‘๋‹ต: ${typeof responseData}`); return [responseData]; } // ์ผ๋ฐ˜์ ์ธ ๋ฐ์ดํ„ฐ ํ•„๋“œ๋ช…๋“ค์„ ์šฐ์„ ์ˆœ์œ„๋Œ€๋กœ ํ™•์ธ const commonDataFields = [ "data", // { data: [...] } "result", // { result: [...] } "results", // { results: [...] } "items", // { items: [...] } "list", // { list: [...] } "records", // { records: [...] } "rows", // { rows: [...] } "content", // { content: [...] } "payload", // { payload: [...] } "response", // { response: [...] } ]; for (const field of commonDataFields) { if (responseData[field] !== undefined) { console.log(`โœ… '${field}' ํ•„๋“œ์—์„œ ๋ฐ์ดํ„ฐ ์ถ”์ถœ`); const extractedData = responseData[field]; // ์ถ”์ถœ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฌธ์ž์—ด์ธ ๊ฒฝ์šฐ JSON ํŒŒ์‹ฑ ์‹œ๋„ if (typeof extractedData === "string") { console.log("๐Ÿ”„ ์ถ”์ถœ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ JSON ๋ฌธ์ž์—ด, ํŒŒ์‹ฑ ์‹œ๋„"); try { const parsed = JSON.parse(extractedData); console.log("โœ… JSON ํŒŒ์‹ฑ ์„ฑ๊ณต, ์žฌ๊ท€ ํ˜ธ์ถœ"); return this.extractActualData(parsed); } catch (error) { console.log("โš ๏ธ JSON ํŒŒ์‹ฑ ์‹คํŒจ, ์›๋ณธ ๋ฌธ์ž์—ด ๋ฐ˜ํ™˜:", error); return [extractedData]; } } // ์ถ”์ถœ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ์ฒด์ด๊ณ  ๋˜ ๋‹ค๋ฅธ ์ค‘์ฒฉ ๊ตฌ์กฐ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์žฌ๊ท€ ํ˜ธ์ถœ if (typeof extractedData === "object" && !Array.isArray(extractedData)) { console.log("๐Ÿ”„ ์ค‘์ฒฉ๋œ ๊ฐ์ฒด ๊ฐ์ง€, ์žฌ๊ท€ ์ถ”์ถœ ์‹œ๋„"); return this.extractActualData(extractedData); } return extractedData; } } // ํŠน๋ณ„ํ•œ ํ•„๋“œ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ, ๊ฐ์ฒด์˜ ๊ฐ’๋“ค ์ค‘์—์„œ ๋ฐฐ์—ด์„ ์ฐพ๊ธฐ const objectValues = Object.values(responseData); const arrayValue = objectValues.find((value) => Array.isArray(value)); if (arrayValue) { console.log("โœ… ๊ฐ์ฒด ๊ฐ’ ์ค‘ ๋ฐฐ์—ด ๋ฐœ๊ฒฌ"); return arrayValue; } // ๊ฐ์ฒด์˜ ๊ฐ’๋“ค ์ค‘์—์„œ ๊ฐ์ฒด๋ฅผ ์ฐพ์•„์„œ ์žฌ๊ท€ ํƒ์ƒ‰ for (const value of objectValues) { if (value && typeof value === "object" && !Array.isArray(value)) { console.log("๐Ÿ”„ ๊ฐ์ฒด ๊ฐ’์—์„œ ์žฌ๊ท€ ํƒ์ƒ‰"); const nestedResult = this.extractActualData(value); if (Array.isArray(nestedResult) && nestedResult.length > 0) { return nestedResult; } } } // ๋ชจ๋“  ์‹œ๋„๊ฐ€ ์‹คํŒจํ•œ ๊ฒฝ์šฐ, ์›๋ณธ ๊ฐ์ฒด๋ฅผ ๋‹จ์ผ ํ•ญ๋ชฉ ๋ฐฐ์—ด๋กœ ๋ฐ˜ํ™˜ console.log("๐Ÿ“ฆ ์›๋ณธ ๊ฐ์ฒด๋ฅผ ๋‹จ์ผ ํ•ญ๋ชฉ์œผ๋กœ ์ฒ˜๋ฆฌ"); return [responseData]; } /** * ์ธ๋ฐ”์šด๋“œ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์ฒ˜๋ฆฌ */ private static async processInboundMapping( inboundMapping: any, responseData: any, context: ButtonExecutionContext, ): Promise { try { console.log("๐Ÿ“ฅ ์ธ๋ฐ”์šด๋“œ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์ฒ˜๋ฆฌ ์‹œ์ž‘"); console.log("๐Ÿ“ฅ ์›๋ณธ ์‘๋‹ต ๋ฐ์ดํ„ฐ:", responseData); const targetTable = inboundMapping.targetTable; const fieldMappings = inboundMapping.fieldMappings || []; const insertMode = inboundMapping.insertMode || "insert"; console.log("๐Ÿ“ฅ ๋งคํ•‘ ์„ค์ •:", { targetTable, fieldMappings, insertMode, }); // ์‘๋‹ต ๋ฐ์ดํ„ฐ์—์„œ ์‹ค์ œ ๋ฐ์ดํ„ฐ ์ถ”์ถœ (๋‹ค์–‘ํ•œ ๊ตฌ์กฐ ์ง€์›) const actualData = this.extractActualData(responseData); console.log("๐Ÿ“ฅ ์ถ”์ถœ๋œ ์‹ค์ œ ๋ฐ์ดํ„ฐ:", actualData); // ๋ฐฐ์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ const dataArray = Array.isArray(actualData) ? actualData : [actualData]; console.log("๐Ÿ“ฅ ์ฒ˜๋ฆฌํ•  ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด:", dataArray); if (dataArray.length === 0) { console.log("โš ๏ธ ์ฒ˜๋ฆฌํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"); return; } for (const item of dataArray) { const mappedData: Record = {}; console.log("๐Ÿ“ฅ ๊ฐœ๋ณ„ ์•„์ดํ…œ ์ฒ˜๋ฆฌ:", item); // ํ•„๋“œ ๋งคํ•‘ ์ ์šฉ for (const mapping of fieldMappings) { const sourceValue = item[mapping.sourceField]; console.log(`๐Ÿ“ฅ ํ•„๋“œ ๋งคํ•‘: ${mapping.sourceField} -> ${mapping.targetField} = ${sourceValue}`); if (sourceValue !== undefined && sourceValue !== null) { mappedData[mapping.targetField] = sourceValue; } } console.log("๐Ÿ“‹ ๋งคํ•‘๋œ ๋ฐ์ดํ„ฐ:", mappedData); // ๋งคํ•‘๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋งŒ ์ €์žฅ if (Object.keys(mappedData).length > 0) { await this.saveDataToTable(targetTable, mappedData, insertMode); } else { console.log("โš ๏ธ ๋งคํ•‘๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ์–ด ์ €์žฅ์„ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค"); } } console.log("โœ… ์ธ๋ฐ”์šด๋“œ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์™„๋ฃŒ"); } catch (error) { console.error("์ธ๋ฐ”์šด๋“œ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ์˜ค๋ฅ˜:", error); throw error; } } /** * ๐Ÿ”ฅ ๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰ */ private static async executeMainAction( buttonConfig: ExtendedButtonTypeConfig, formData: Record, context: ButtonExecutionContext, ): Promise { try { const startTime = performance.now(); // transferData ์•ก์…˜ ์ฒ˜๋ฆฌ if (buttonConfig.actionType === "transferData") { return await this.executeTransferDataAction(buttonConfig, formData, context); } // ๊ธฐ์กด ์•ก์…˜๋“ค (์ž„์‹œ ๊ตฌํ˜„) const result = { success: true, message: `${buttonConfig.actionType} ์•ก์…˜ ์‹คํ–‰ ์™„๋ฃŒ`, executionTime: performance.now() - startTime, data: { actionType: buttonConfig.actionType, formData }, }; console.log("โœ… ๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰ ์™„๋ฃŒ:", result.message); return result; } catch (error) { console.error("๋ฉ”์ธ ์•ก์…˜ ์‹คํ–‰ ์˜ค๋ฅ˜:", error); return { success: false, message: `${buttonConfig.actionType} ์•ก์…˜ ์‹คํ–‰ ์‹คํŒจ: ${error.message}`, executionTime: 0, error: error.message, }; } } /** * ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์•ก์…˜ ์‹คํ–‰ */ private static async executeTransferDataAction( buttonConfig: ExtendedButtonTypeConfig, formData: Record, context: ButtonExecutionContext, ): Promise { const startTime = performance.now(); try { const dataTransferConfig = buttonConfig.dataTransfer; if (!dataTransferConfig) { throw new Error("๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค."); } console.log("๐Ÿ“ฆ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์‹œ์ž‘:", dataTransferConfig); // 1. ํ™”๋ฉด ์ปจํ…์ŠคํŠธ์—์„œ ์†Œ์Šค ์ปดํฌ๋„ŒํŠธ ์ฐพ๊ธฐ const { ScreenContextProvider } = await import("@/contexts/ScreenContext"); // ์‹ค์ œ๋กœ๋Š” ํ˜„์žฌ ํ™”๋ฉด์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” ์ „์—ญ์ ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Œ // ๋Œ€์‹  context์— screenContext๋ฅผ ์ „๋‹ฌํ•˜๋„๋ก ์ˆ˜์ • ํ•„์š” throw new Error("๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๊ธฐ๋Šฅ์€ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ง์ ‘ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."); } catch (error) { console.error("โŒ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์‹คํŒจ:", error); return { success: false, message: `๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์‹คํŒจ: ${error.message}`, executionTime: performance.now() - startTime, error: error.message, }; } } /** * ๐Ÿ”ฅ ์‹คํ–‰ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฐ ๋กค๋ฐฑ */ private static async handleExecutionError( error: Error, results: ExecutionResult[], buttonConfig: ExtendedButtonTypeConfig, ): Promise { console.error("๐Ÿ”„ ์‹คํ–‰ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์‹œ์ž‘:", error.message); // ๋กค๋ฐฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ฒ˜๋ฆฌ const rollbackNeeded = buttonConfig.dataflowConfig?.executionOptions?.rollbackOnError; if (rollbackNeeded) { console.log("๐Ÿ”„ ๋กค๋ฐฑ ์ฒ˜๋ฆฌ ์‹œ์ž‘..."); // ์„ฑ๊ณตํ•œ ๊ฒฐ๊ณผ๋“ค์„ ์—ญ์ˆœ์œผ๋กœ ๋กค๋ฐฑ const successfulResults = results.filter((r) => r.success).reverse(); for (const result of successfulResults) { try { // ๋กค๋ฐฑ ๋กœ์ง ๊ตฌํ˜„ (ํ•„์š”์‹œ) console.log("๐Ÿ”„ ๋กค๋ฐฑ:", result.message); } catch (rollbackError) { console.error("๋กค๋ฐฑ ์‹คํŒจ:", rollbackError); } } } // ์˜ค๋ฅ˜ ํ† ์ŠคํŠธ ํ‘œ์‹œ toast.error(error.message || "์ž‘์—… ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); } }