/** * πŸ”₯ μ„±λŠ₯ μ΅œμ ν™”: λ²„νŠΌ λ°μ΄ν„°ν”Œλ‘œμš° μ„œλΉ„μŠ€ * * μ¦‰μ‹œ 응닡 + λ°±κ·ΈλΌμš΄λ“œ μ‹€ν–‰ νŒ¨ν„΄μœΌλ‘œ * μ‚¬μš©μžμ—κ²Œ 졜고의 μ„±λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. */ import { ButtonActionType, ExtendedButtonTypeConfig, ButtonDataflowConfig, DataflowExecutionResult, DataflowCondition, } from "@/types"; import { dataflowConfigCache } from "./dataflowCache"; import { dataflowJobQueue, JobPriority } from "./dataflowJobQueue"; import { apiClient } from "@/lib/api/client"; export interface OptimizedExecutionResult { jobId: string; immediateResult?: any; isBackground?: boolean; timing?: "before" | "after" | "replace"; } export interface QuickValidationResult { success: boolean; message?: string; canExecuteImmediately: boolean; actions?: any[]; // 쑰건 만쑱 μ‹œ μ‹€ν–‰ν•  μ•‘μ…˜λ“€ } /** * μ œμ–΄ 데이터 μ†ŒμŠ€ νƒ€μž… */ export type ControlDataSource = "form" | "table-selection" | "both"; /** * ν™•μž₯된 μ œμ–΄ μ»¨ν…μŠ€νŠΈ */ export interface ExtendedControlContext { // κΈ°μ‘΄ 폼 데이터 formData: Record; // ν…Œμ΄λΈ” 선택 데이터 selectedRows?: any[]; selectedRowsData?: any[]; // μ œμ–΄ 데이터 μ†ŒμŠ€ νƒ€μž… controlDataSource: ControlDataSource; // 기타 μ»¨ν…μŠ€νŠΈ buttonId: string; componentData?: any; timestamp: string; clickCount?: number; } /** * πŸ”₯ μ΅œμ ν™”λœ λ²„νŠΌ λ°μ΄ν„°ν”Œλ‘œμš° μ„œλΉ„μŠ€ * * 핡심 원칙: * 1. μ¦‰μ‹œ 응닡 μš°μ„  (0-100ms) * 2. λ³΅μž‘ν•œ μž‘μ—…μ€ λ°±κ·ΈλΌμš΄λ“œ * 3. μΊμ‹œ ν™œμš©μœΌλ‘œ 속도 ν–₯상 * 4. μŠ€λ§ˆνŠΈν•œ 타이밍 μ œμ–΄ */ export class OptimizedButtonDataflowService { /** * πŸ”₯ 메인 μ—”νŠΈλ¦¬ν¬μΈνŠΈ: μ¦‰μ‹œ 응닡 + λ°±κ·ΈλΌμš΄λ“œ μ‹€ν–‰ */ static async executeButtonWithDataflow( buttonId: string, actionType: ButtonActionType, buttonConfig: ExtendedExtendedButtonTypeConfig, contextData: Record, companyCode: string, ): Promise { const { enableDataflowControl, dataflowTiming } = buttonConfig; // πŸ”₯ μ œμ–΄κ΄€λ¦¬κ°€ λΉ„ν™œμ„±ν™”λœ 경우: μ¦‰μ‹œ μ‹€ν–‰ if (!enableDataflowControl) { const result = await this.executeOriginalAction(actionType, buttonConfig, contextData); return { jobId: "immediate", immediateResult: result, timing: undefined, }; } // πŸ”₯ 타이밍별 μ¦‰μ‹œ 응닡 μ „λž΅ switch (dataflowTiming) { case "before": return await this.executeBeforeTiming(buttonId, actionType, buttonConfig, contextData, companyCode); case "after": return await this.executeAfterTiming(buttonId, actionType, buttonConfig, contextData, companyCode); case "replace": return await this.executeReplaceTiming(buttonId, actionType, buttonConfig, contextData, companyCode); default: // 기본값은 after return await this.executeAfterTiming(buttonId, actionType, buttonConfig, contextData, companyCode); } } /** * πŸ”₯ After 타이밍: μ¦‰μ‹œ κΈ°μ‘΄ μ•‘μ…˜ + λ°±κ·ΈλΌμš΄λ“œ μ œμ–΄κ΄€λ¦¬ * * κ°€μž₯ 일반적이고 μ•ˆμ „ν•œ νŒ¨ν„΄ * - κΈ°μ‘΄ μ•‘μ…˜ μ¦‰μ‹œ μ‹€ν–‰ (50-200ms) * - μ œμ–΄κ΄€λ¦¬λŠ” λ°±κ·ΈλΌμš΄λ“œμ—μ„œ 처리 */ private static async executeAfterTiming( buttonId: string, actionType: ButtonActionType, buttonConfig: ExtendedButtonTypeConfig, contextData: Record, companyCode: string, ): Promise { // πŸ”₯ Step 1: κΈ°μ‘΄ μ•‘μ…˜ μ¦‰μ‹œ μ‹€ν–‰ const immediateResult = await this.executeOriginalAction(actionType, buttonConfig, contextData); // πŸ”₯ Step 2: μ œμ–΄κ΄€λ¦¬λŠ” λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ‹€ν–‰ const enrichedContext = { ...contextData, originalActionResult: immediateResult, }; const jobId = dataflowJobQueue.enqueue( buttonId, actionType, buttonConfig, enrichedContext, companyCode, "normal", // 일반 μš°μ„ μˆœμœ„ ); return { jobId, immediateResult, isBackground: true, timing: "after", }; } /** * πŸ”₯ Before 타이밍: λΉ λ₯Έ 검증 + κΈ°μ‘΄ μ•‘μ…˜ * * 검증 λͺ©μ μœΌλ‘œ 주둜 μ‚¬μš© * - κ°„λ‹¨ν•œ 검증: μ¦‰μ‹œ 처리 * - λ³΅μž‘ν•œ 검증: λ°±κ·ΈλΌμš΄λ“œ 처리 */ private static async executeBeforeTiming( buttonId: string, actionType: ButtonActionType, buttonConfig: ExtendedButtonTypeConfig, contextData: Record, companyCode: string, ): Promise { // πŸ”₯ μ„€μ • μΊμ‹œμ—μ„œ λΉ λ₯΄κ²Œ λ‘œλ“œ const dataflowConfig = buttonConfig.dataflowConfig || (await dataflowConfigCache.getConfig(buttonId)); if (!dataflowConfig) { // 섀정이 μ—†μœΌλ©΄ κΈ°μ‘΄ μ•‘μ…˜λ§Œ μ‹€ν–‰ const result = await this.executeOriginalAction(actionType, buttonConfig, contextData); return { jobId: "immediate", immediateResult: result, timing: "before" }; } // κ°„λ‹¨ν•œ 검증인지 νŒλ‹¨ const isSimpleValidation = await this.isSimpleValidationOnly(dataflowConfig); if (isSimpleValidation) { // πŸ”₯ κ°„λ‹¨ν•œ 검증: λ©”λͺ¨λ¦¬μ—μ„œ μ¦‰μ‹œ 처리 (1-10ms) const validationResult = await this.executeQuickValidation(dataflowConfig, contextData); if (!validationResult.success) { return { jobId: "validation_failed", immediateResult: { success: false, message: validationResult.message, }, timing: "before", }; } // 검증 톡과 μ‹œ κΈ°μ‘΄ μ•‘μ…˜ μ‹€ν–‰ const actionResult = await this.executeOriginalAction(actionType, buttonConfig, contextData); return { jobId: "immediate", immediateResult: actionResult, timing: "before", }; } else { // πŸ”₯ λ³΅μž‘ν•œ 검증: μ‚¬μš©μžμ—κ²Œ μ•Œλ¦Ό ν›„ λ°±κ·ΈλΌμš΄λ“œ 처리 const jobId = dataflowJobQueue.enqueue( buttonId, actionType, buttonConfig, contextData, companyCode, "high", // 높은 μš°μ„ μˆœμœ„ (μ‚¬μš©μž λŒ€κΈ° 쀑) ); return { jobId, immediateResult: { success: true, message: "검증 μ€‘μž…λ‹ˆλ‹€. μž μ‹œλ§Œ κΈ°λ‹€λ €μ£Όμ„Έμš”.", processing: true, }, isBackground: true, timing: "before", }; } } /** * πŸ”₯ Replace 타이밍: μ œμ–΄κ΄€λ¦¬λ‘œ μ™„μ „ λŒ€μ²΄ * * κΈ°μ‘΄ μ•‘μ…˜ λŒ€μ‹  μ œμ–΄κ΄€λ¦¬λ§Œ μ‹€ν–‰ * - κ°„λ‹¨ν•œ μ œμ–΄: μ¦‰μ‹œ μ‹€ν–‰ * - λ³΅μž‘ν•œ μ œμ–΄: λ°±κ·ΈλΌμš΄λ“œ μ‹€ν–‰ */ private static async executeReplaceTiming( buttonId: string, actionType: ButtonActionType, buttonConfig: ExtendedButtonTypeConfig, contextData: Record, companyCode: string, ): Promise { const dataflowConfig = buttonConfig.dataflowConfig || (await dataflowConfigCache.getConfig(buttonId)); if (!dataflowConfig) { throw new Error("Replace λͺ¨λ“œμ΄μ§€λ§Œ μ œμ–΄κ΄€λ¦¬ 섀정이 μ—†μŠ΅λ‹ˆλ‹€."); } // κ°„λ‹¨ν•œ μ œμ–΄κ΄€λ¦¬μΈμ§€ νŒλ‹¨ const isSimpleControl = this.isSimpleControl(dataflowConfig); if (isSimpleControl) { // πŸ”₯ κ°„λ‹¨ν•œ μ œμ–΄: μ¦‰μ‹œ μ‹€ν–‰ try { const result = await this.executeSimpleDataflow(dataflowConfig, contextData, companyCode); return { jobId: "immediate", immediateResult: result, timing: "replace", }; } catch (error) { return { jobId: "immediate", immediateResult: { success: false, message: "μ œμ–΄κ΄€λ¦¬ μ‹€ν–‰ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", error: error.message, }, timing: "replace", }; } } else { // πŸ”₯ λ³΅μž‘ν•œ μ œμ–΄: λ°±κ·ΈλΌμš΄λ“œ μ‹€ν–‰ const jobId = dataflowJobQueue.enqueue(buttonId, actionType, buttonConfig, contextData, companyCode, "normal"); return { jobId, immediateResult: { success: true, message: "μ‚¬μš©μž μ •μ˜ μž‘μ—…μ„ 처리 μ€‘μž…λ‹ˆλ‹€...", processing: true, }, isBackground: true, timing: "replace", }; } } /** * πŸ”₯ κ°„λ‹¨ν•œ 쑰건인지 νŒλ‹¨ * * λ©”λͺ¨λ¦¬μ—μ„œ μ¦‰μ‹œ 처리 κ°€λŠ₯ν•œ 쑰건: * - 쑰건 5개 μ΄ν•˜ * - λ‹¨μˆœ 비ꡐ μ—°μ‚°μžλ§Œ μ‚¬μš© * - κ·Έλ£Ήν•‘ μ—†μŒ */ private static async isSimpleValidationOnly(config: ButtonDataflowConfig): Promise { if (config.controlMode !== "advanced") { return true; // κ°„νŽΈ λͺ¨λ“œλŠ” 일단 κ°„λ‹¨ν•˜λ‹€κ³  κ°€μ • } const conditions = config.directControl?.conditions || []; return ( conditions.length <= 5 && conditions.every((c) => c.type === "condition" && ["=", "!=", ">", "<", ">=", "<="].includes(c.operator || "")) ); } /** * πŸ”₯ κ°„λ‹¨ν•œ μ œμ–΄κ΄€λ¦¬μΈμ§€ νŒλ‹¨ */ private static isSimpleControl(config: ButtonDataflowConfig): boolean { if (config.controlMode === "simple") { return true; // κ°„νŽΈ λͺ¨λ“œλŠ” λŒ€λΆ€λΆ„ 간단 } const actions = config.directControl?.actions || []; const conditions = config.directControl?.conditions || []; // μ•‘μ…˜ 3개 μ΄ν•˜, 쑰건 5개 μ΄ν•˜λ©΄ κ°„λ‹¨ν•œ μ œμ–΄λ‘œ νŒλ‹¨ return actions.length <= 3 && conditions.length <= 5; } /** * πŸ”₯ λΉ λ₯Έ 검증 (λ©”λͺ¨λ¦¬μ—μ„œ μ¦‰μ‹œ 처리) - ν™•μž₯된 버전 */ private static async executeQuickValidation( config: ButtonDataflowConfig, data: Record, ): Promise { if (config.controlMode === "simple") { // κ°„νŽΈ λͺ¨λ“œλŠ” 일단 톡과 (μ‹€μ œ 검증은 λ°±κ·ΈλΌμš΄λ“œμ—μ„œ) return { success: true, canExecuteImmediately: true, }; } const conditions = config.directControl?.conditions || []; for (const condition of conditions) { if (condition.type === "condition") { const fieldValue = data[condition.field!]; const isValid = this.evaluateSimpleCondition(fieldValue, condition.operator!, condition.value); if (!isValid) { return { success: false, message: `쑰건 뢈만쑱: ${condition.field} ${condition.operator} ${condition.value}`, canExecuteImmediately: true, }; } } } return { success: true, canExecuteImmediately: true, }; } /** * πŸ”₯ ν™•μž₯된 쑰건 검증 (폼 + ν…Œμ΄λΈ” 선택 데이터) */ static async executeExtendedValidation( config: ButtonDataflowConfig, context: ExtendedControlContext, ): Promise { console.log("πŸ” executeExtendedValidation μ‹œμž‘:", { controlMode: config.controlMode, controlDataSource: context.controlDataSource, selectedRowsData: context.selectedRowsData, formData: context.formData, directControlConditions: config.directControl?.conditions, selectedDiagramId: config.selectedDiagramId, selectedRelationshipId: config.selectedRelationshipId, fullConfig: config, }); // πŸ”₯ simple λͺ¨λ“œμ—μ„œλ„ 쑰건 검증을 μˆ˜ν–‰ν•΄μ•Ό 함 // if (config.controlMode === "simple") { // return { // success: true, // canExecuteImmediately: true, // }; // } let conditions = config.directControl?.conditions || []; // πŸ”₯ 관계도 방식인 경우 μ‹€μ œ APIμ—μ„œ 쑰건 κ°€μ Έμ˜€κΈ° if (conditions.length === 0 && config.selectedDiagramId && config.selectedRelationshipId) { console.log("πŸ” 관계도 λ°©μ‹μ—μ„œ μ‹€μ œ 쑰건 λ‘œλ”©:", { selectedDiagramId: config.selectedDiagramId, selectedRelationshipId: config.selectedRelationshipId, }); try { // κ΄€κ³„λ„μ—μ„œ μ‹€μ œ 쑰건을 κ°€μ Έμ˜€λŠ” API 호좜 const response = await apiClient.get( `/test-button-dataflow/diagrams/${config.selectedDiagramId}/relationships/${config.selectedRelationshipId}/preview`, ); if (response.data.success && response.data.data) { const previewData = response.data.data; // control.conditionsμ—μ„œ μ‹€μ œ 쑰건 μΆ”μΆœ if (previewData.control && previewData.control.conditions) { conditions = previewData.control.conditions.filter((cond: any) => cond.type === "condition"); console.log("βœ… κ΄€κ³„λ„μ—μ„œ control 쑰건 λ‘œλ”© 성곡:", { totalConditions: previewData.control.conditions.length, filteredConditions: conditions.length, conditions, }); } else { console.warn("⚠️ 관계도 control에 쑰건이 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€:", previewData); } // πŸ”§ control.conditionsκ°€ λΉ„μ–΄μžˆμœΌλ©΄ plan.actions[].conditionsμ—μ„œ 쑰건 μΆ”μΆœ if (conditions.length === 0 && previewData.plan && previewData.plan.actions) { console.log("πŸ” control 쑰건이 μ—†μ–΄μ„œ μ•‘μ…˜λ³„ 쑰건 확인:", previewData.plan.actions); previewData.plan.actions.forEach((action: any, index: number) => { if (action.conditions && Array.isArray(action.conditions) && action.conditions.length > 0) { conditions.push(...action.conditions); console.log( `βœ… μ•‘μ…˜ ${index + 1}(${action.name})μ—μ„œ 쑰건 ${action.conditions.length}개 λ‘œλ”©:`, action.conditions, ); } }); if (conditions.length > 0) { console.log("βœ… 총 μ•‘μ…˜λ³„ 쑰건 λ‘œλ”© 성곡:", { totalConditions: conditions.length, conditions: conditions, }); } } // plan.actionsμ—μ„œ μ‹€μ œ μ•‘μ…˜λ„ μ €μž₯ (쑰건 만쑱 μ‹œ μ‹€ν–‰μš©) if (previewData.plan && previewData.plan.actions) { // config에 μ•‘μ…˜ 정보 μž„μ‹œ μ €μž₯ (config as any)._relationshipActions = previewData.plan.actions; console.log("βœ… κ΄€κ³„λ„μ—μ„œ μ‹€μ œ μ•‘μ…˜ λ‘œλ”© 성곡:", { totalActions: previewData.plan.actions.length, actions: previewData.plan.actions, }); } else { console.warn("⚠️ 관계도에 μ•‘μ…˜μ΄ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€:", { hasPlan: !!previewData.plan, planActions: previewData.plan?.actions, fullPreviewData: previewData, }); } } else { console.warn("⚠️ 관계도 API 응닡이 μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€:", response.data); } } catch (error) { console.error("❌ κ΄€κ³„λ„μ—μ„œ 쑰건 λ‘œλ”© μ‹€νŒ¨:", error); // API μ‹€νŒ¨ μ‹œ μ‚¬μš©μžμ—κ²Œ λͺ…ν™•ν•œ μ•ˆλ‚΄ return { success: false, message: "κ΄€κ³„λ„μ—μ„œ 쑰건을 뢈러올 수 μ—†μŠ΅λ‹ˆλ‹€. 관계도 섀정을 ν™•μΈν•΄μ£Όμ„Έμš”.", canExecuteImmediately: true, }; } } // πŸ”₯ μ—¬μ „νžˆ 쑰건이 μ—†μœΌλ©΄ κ²½κ³  if (conditions.length === 0) { console.warn("⚠️ μ œμ–΄ 쑰건이 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."); return { success: false, message: "μ œμ–΄ 쑰건이 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. κ΄€κ³„λ„μ—μ„œ 관계λ₯Ό μ„ νƒν•˜κ±°λ‚˜ 직접 쑰건을 μΆ”κ°€ν•΄μ£Όμ„Έμš”.", canExecuteImmediately: true, }; } console.log("πŸ” 쑰건 검증 μ‹œμž‘:", conditions); for (const condition of conditions) { if (condition.type === "condition") { let dataToCheck: Record = {}; // μ œμ–΄ 데이터 μ†ŒμŠ€μ— 따라 검증할 데이터 κ²°μ • console.log("πŸ” μ œμ–΄ 데이터 μ†ŒμŠ€ 확인:", { controlDataSource: context.controlDataSource, hasFormData: Object.keys(context.formData).length > 0, hasSelectedRowsData: context.selectedRowsData && context.selectedRowsData.length > 0, selectedRowsData: context.selectedRowsData, }); switch (context.controlDataSource) { case "form": dataToCheck = context.formData; console.log("πŸ“ 폼 데이터 μ‚¬μš©:", dataToCheck); break; case "table-selection": // μ„ νƒλœ 첫 번째 ν–‰μ˜ 데이터 μ‚¬μš© (닀쀑 선택 μ‹œ) if (context.selectedRowsData && context.selectedRowsData.length > 0) { dataToCheck = context.selectedRowsData[0]; console.log("πŸ“‹ ν…Œμ΄λΈ” 선택 데이터 μ‚¬μš©:", dataToCheck); } else { console.warn("⚠️ ν…Œμ΄λΈ”μ—μ„œ ν•­λͺ©μ΄ μ„ νƒλ˜μ§€ μ•ŠμŒ"); return { success: false, message: "ν…Œμ΄λΈ”μ—μ„œ ν•­λͺ©μ„ μ„ νƒν•΄μ£Όμ„Έμš”.", canExecuteImmediately: true, }; } break; case "both": // 폼 데이터와 μ„ νƒλœ 데이터 λͺ¨λ‘ 병합 dataToCheck = { ...context.formData, ...(context.selectedRowsData?.[0] || {}), }; console.log("πŸ”„ 폼 + ν…Œμ΄λΈ” 데이터 병합 μ‚¬μš©:", dataToCheck); break; default: // 기본값이 μ—†λŠ” 경우 μžλ™ νŒλ‹¨ if (context.selectedRowsData && context.selectedRowsData.length > 0) { dataToCheck = context.selectedRowsData[0]; console.log("πŸ”„ μžλ™ νŒλ‹¨: ν…Œμ΄λΈ” 선택 데이터 μ‚¬μš©:", dataToCheck); } else { dataToCheck = context.formData; console.log("πŸ”„ μžλ™ νŒλ‹¨: 폼 데이터 μ‚¬μš©:", dataToCheck); } break; } const fieldValue = dataToCheck[condition.field!]; const isValid = this.evaluateSimpleCondition(fieldValue, condition.operator!, condition.value); console.log("πŸ” 쑰건 검증 κ²°κ³Ό:", { field: condition.field, operator: condition.operator, expectedValue: condition.value, actualValue: fieldValue, isValid, dataToCheck, }); if (!isValid) { const sourceLabel = getDataSourceLabel(context.controlDataSource); const actualValueMsg = fieldValue !== undefined ? ` (μ‹€μ œκ°’: ${fieldValue})` : " (κ°’ μ—†μŒ)"; return { success: false, message: `${sourceLabel} 쑰건 뢈만쑱: ${condition.field} ${condition.operator} ${condition.value}${actualValueMsg}`, canExecuteImmediately: true, }; } } } // πŸ”₯ λͺ¨λ“  쑰건을 λ§Œμ‘±ν–ˆμœΌλ―€λ‘œ μ•‘μ…˜ 정보도 ν•¨κ»˜ λ°˜ν™˜ const relationshipActions = (config as any)._relationshipActions || []; return { success: true, canExecuteImmediately: true, actions: relationshipActions, }; } /** * πŸ”₯ λ‹¨μˆœ 쑰건 평가 (λ©”λͺ¨λ¦¬μ—μ„œ μ¦‰μ‹œ) */ private static evaluateSimpleCondition(fieldValue: any, operator: string, conditionValue: any): boolean { switch (operator) { case "=": return fieldValue === conditionValue; case "!=": return fieldValue !== conditionValue; case ">": return Number(fieldValue) > Number(conditionValue); case "<": return Number(fieldValue) < Number(conditionValue); case ">=": return Number(fieldValue) >= Number(conditionValue); case "<=": return Number(fieldValue) <= Number(conditionValue); case "LIKE": return String(fieldValue).toLowerCase().includes(String(conditionValue).toLowerCase()); default: return true; } } /** * πŸ”₯ κ°„λ‹¨ν•œ λ°μ΄ν„°ν”Œλ‘œμš° μ¦‰μ‹œ μ‹€ν–‰ */ private static async executeSimpleDataflow( config: ButtonDataflowConfig, contextData: Record, companyCode: string, ): Promise { try { const response = await apiClient.post("/api/button-dataflow/execute-simple", { config, contextData, companyCode, }); if (response.data.success) { return response.data.data as DataflowExecutionResult; } else { throw new Error(response.data.message || "Simple dataflow execution failed"); } } catch (error) { console.error("Simple dataflow execution failed:", error); throw error; } } /** * πŸ”₯ κΈ°μ‘΄ μ•‘μ…˜ μ‹€ν–‰ (μ΅œμ ν™”) */ private static async executeOriginalAction( actionType: ButtonActionType, buttonConfig: ExtendedButtonTypeConfig, contextData: Record, ): Promise { const startTime = performance.now(); try { // μ•‘μ…˜λ³„ λΆ„κΈ° 처리 switch (actionType) { case "save": return await this.executeSaveAction(buttonConfig, contextData); case "delete": return await this.executeDeleteAction(buttonConfig, contextData); case "search": return await this.executeSearchAction(buttonConfig, contextData); case "edit": return await this.executeEditAction(buttonConfig, contextData); case "add": return await this.executeAddAction(buttonConfig, contextData); case "reset": return await this.executeResetAction(buttonConfig, contextData); case "submit": return await this.executeSubmitAction(buttonConfig, contextData); case "close": return await this.executeCloseAction(buttonConfig, contextData); case "popup": return await this.executePopupAction(buttonConfig, contextData); case "navigate": return await this.executeNavigateAction(buttonConfig, contextData); default: return { success: true, message: `${actionType} μ•‘μ…˜μ΄ μ‹€ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€.`, }; } } catch (error) { console.error(`Action execution failed: ${actionType}`, error); return { success: false, message: `${actionType} μ•‘μ…˜ μ‹€ν–‰ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.`, error: error.message, }; } finally { const executionTime = performance.now() - startTime; if (executionTime > 200) { console.warn(`🐌 Slow action: ${actionType} took ${executionTime.toFixed(2)}ms`); } else { console.log(`⚑ ${actionType} completed in ${executionTime.toFixed(2)}ms`); } } } /** * κ°œλ³„ μ•‘μ…˜ κ΅¬ν˜„λ“€ */ private static async executeSaveAction(config: ExtendedButtonTypeConfig, data: Record) { // TODO: μ‹€μ œ μ €μž₯ 둜직 κ΅¬ν˜„ return { success: true, message: "μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executeDeleteAction(config: ExtendedButtonTypeConfig, data: Record) { // TODO: μ‹€μ œ μ‚­μ œ 둜직 κ΅¬ν˜„ return { success: true, message: "μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executeSearchAction(config: ExtendedButtonTypeConfig, data: Record) { // TODO: μ‹€μ œ 검색 둜직 κ΅¬ν˜„ return { success: true, message: "κ²€μƒ‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", data: [] }; } private static async executeEditAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "μˆ˜μ • λͺ¨λ“œλ‘œ μ „ν™˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executeAddAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "μΆ”κ°€ λͺ¨λ“œλ‘œ μ „ν™˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executeResetAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "μ΄ˆκΈ°ν™”λ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executeSubmitAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "μ œμΆœλ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executeCloseAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "λ‹«κΈ° μ•‘μ…˜μ΄ μ‹€ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€." }; } private static async executePopupAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "νŒμ—…μ΄ μ—΄λ ΈμŠ΅λ‹ˆλ‹€.", popupUrl: config.navigateUrl, popupScreenId: config.popupScreenId, }; } private static async executeNavigateAction(config: ExtendedButtonTypeConfig, data: Record) { return { success: true, message: "νŽ˜μ΄μ§€ 이동이 μ‹€ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", navigateUrl: config.navigateUrl, navigateTarget: config.navigateTarget, }; } /** * πŸ”₯ μž‘μ—… μƒνƒœ 쑰회 */ static getJobStatus(jobId: string): { status: string; result?: any; progress?: number } { try { return dataflowJobQueue.getJobStatus(jobId); } catch (error) { return { status: "not_found" }; } } /** * πŸ”₯ μ„±λŠ₯ λ©”νŠΈλ¦­ 쑰회 */ static getPerformanceMetrics(): { cache: any; queue: any; } { return { cache: dataflowConfigCache.getMetrics(), queue: dataflowJobQueue.getMetrics(), }; } } /** * 데이터 μ†ŒμŠ€ νƒ€μž…μ— λ”°λ₯Έ ν•œκΈ€ λ ˆμ΄λΈ” λ°˜ν™˜ */ function getDataSourceLabel(dataSource: string | undefined): string { switch (dataSource) { case "form": return "폼"; case "table-selection": return "ν…Œμ΄λΈ” 선택 ν•­λͺ©"; case "table-all": return "ν…Œμ΄λΈ” 전체"; case "flow-selection": return "ν”Œλ‘œμš° 선택 ν•­λͺ©"; case "flow-step-all": return "ν”Œλ‘œμš° μŠ€ν… 전체"; case "both": return "폼 + ν…Œμ΄λΈ” 선택"; case "all-sources": return "λͺ¨λ“  μ†ŒμŠ€"; default: return "데이터"; } } // πŸ”₯ μ „μ—­ 접근을 μœ„ν•œ 싱글톀 μ„œλΉ„μŠ€ export const optimizedButtonDataflowService = OptimizedButtonDataflowService;