diff --git a/backend-node/src/routes/dataflow/node-flows.ts b/backend-node/src/routes/dataflow/node-flows.ts index 7ede970a..f13d65cf 100644 --- a/backend-node/src/routes/dataflow/node-flows.ts +++ b/backend-node/src/routes/dataflow/node-flows.ts @@ -7,6 +7,7 @@ import { query, queryOne } from "../../database/db"; import { logger } from "../../utils/logger"; import { NodeFlowExecutionService } from "../../services/nodeFlowExecutionService"; import { AuthenticatedRequest } from "../../types/auth"; +import { authenticateToken } from "../../middleware/authMiddleware"; const router = Router(); @@ -217,19 +218,29 @@ router.delete("/:flowId", async (req: Request, res: Response) => { * 플로우 실행 * POST /api/dataflow/node-flows/:flowId/execute */ -router.post("/:flowId/execute", async (req: Request, res: Response) => { +router.post("/:flowId/execute", authenticateToken, async (req: AuthenticatedRequest, res: Response) => { try { const { flowId } = req.params; const contextData = req.body; logger.info(`🚀 플로우 실행 요청: flowId=${flowId}`, { contextDataKeys: Object.keys(contextData), + userId: req.user?.userId, + companyCode: req.user?.companyCode, }); + // 사용자 정보를 contextData에 추가 + const enrichedContextData = { + ...contextData, + userId: req.user?.userId, + userName: req.user?.userName, + companyCode: req.user?.companyCode, + }; + // 플로우 실행 const result = await NodeFlowExecutionService.executeFlow( parseInt(flowId, 10), - contextData + enrichedContextData ); return res.json({ diff --git a/backend-node/src/services/nodeFlowExecutionService.ts b/backend-node/src/services/nodeFlowExecutionService.ts index 546b215a..9cdd85f3 100644 --- a/backend-node/src/services/nodeFlowExecutionService.ts +++ b/backend-node/src/services/nodeFlowExecutionService.ts @@ -938,6 +938,30 @@ export class NodeFlowExecutionService { insertedData[mapping.targetField] = value; }); + // 🆕 writer와 company_code 자동 추가 (필드 매핑에 없는 경우) + const hasWriterMapping = fieldMappings.some((m: any) => m.targetField === "writer"); + const hasCompanyCodeMapping = fieldMappings.some((m: any) => m.targetField === "company_code"); + + // 컨텍스트에서 사용자 정보 추출 + const userId = context.buttonContext?.userId; + const companyCode = context.buttonContext?.companyCode; + + // writer 자동 추가 (매핑에 없고, 컨텍스트에 userId가 있는 경우) + if (!hasWriterMapping && userId) { + fields.push("writer"); + values.push(userId); + insertedData.writer = userId; + console.log(` 🔧 자동 추가: writer = ${userId}`); + } + + // company_code 자동 추가 (매핑에 없고, 컨텍스트에 companyCode가 있는 경우) + if (!hasCompanyCodeMapping && companyCode && companyCode !== "*") { + fields.push("company_code"); + values.push(companyCode); + insertedData.company_code = companyCode; + console.log(` 🔧 자동 추가: company_code = ${companyCode}`); + } + const sql = ` INSERT INTO ${targetTable} (${fields.join(", ")}) VALUES (${fields.map((_, i) => `$${i + 1}`).join(", ")}) diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 5f825cdc..0d69254b 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -1673,7 +1673,11 @@ export class ButtonActionExecutor { }); // 🔥 새로운 버튼 액션 실행 시스템 사용 - if (config.dataflowConfig?.controlMode === "flow" && config.dataflowConfig?.flowConfig) { + // flowConfig가 있으면 controlMode가 명시되지 않아도 플로우 모드로 간주 + const hasFlowConfig = config.dataflowConfig?.flowConfig && config.dataflowConfig.flowConfig.flowId; + const isFlowMode = config.dataflowConfig?.controlMode === "flow" || hasFlowConfig; + + if (isFlowMode && config.dataflowConfig?.flowConfig) { console.log("🎯 노드 플로우 실행:", config.dataflowConfig.flowConfig); const { flowId, executionTiming } = config.dataflowConfig.flowConfig; @@ -1711,6 +1715,8 @@ export class ButtonActionExecutor { }); } else { console.warn("⚠️ flow-selection 모드이지만 선택된 플로우 데이터가 없습니다."); + toast.error("플로우에서 데이터를 먼저 선택해주세요."); + return false; } break; @@ -1723,6 +1729,8 @@ export class ButtonActionExecutor { }); } else { console.warn("⚠️ table-selection 모드이지만 선택된 행이 없습니다."); + toast.error("테이블에서 처리할 항목을 먼저 선택해주세요."); + return false; } break;