[agent-pipeline] pipe-20260318044621-56k5 round-2

This commit is contained in:
DDD1542 2026-03-18 13:56:03 +09:00
parent 8e4791c57a
commit 351e57dd31
5 changed files with 136 additions and 4 deletions

View File

@ -768,4 +768,41 @@ export class BatchManagementController {
}); });
} }
} }
/**
* ( )
* GET /api/batch-management/node-flows
* 멀티테넌시: 최고 ,
*/
static async getNodeFlows(req: AuthenticatedRequest, res: Response) {
try {
const companyCode = req.user?.companyCode;
let queryText: string;
let queryParams: any[] = [];
if (companyCode === "*") {
queryText = `SELECT flow_id, flow_name, description, created_date
FROM node_flows
ORDER BY flow_name`;
} else {
queryText = `SELECT flow_id, flow_name, description, created_date
FROM node_flows
WHERE company_code = $1
ORDER BY flow_name`;
queryParams = [companyCode];
}
const result = await query(queryText, queryParams);
return res.json({ success: true, data: result });
} catch (error) {
console.error("노드 플로우 목록 조회 오류:", error);
return res.status(500).json({
success: false,
message: "노드 플로우 목록 조회 실패",
error: error instanceof Error ? error.message : "알 수 없는 오류",
});
}
}
} }

View File

@ -85,4 +85,10 @@ router.post("/rest-api/save", authenticateToken, BatchManagementController.saveR
*/ */
router.get("/auth-services", authenticateToken, BatchManagementController.getAuthServiceNames); router.get("/auth-services", authenticateToken, BatchManagementController.getAuthServiceNames);
/**
* GET /api/batch-management/node-flows
* ( )
*/
router.get("/node-flows", authenticateToken, BatchManagementController.getNodeFlows);
export default router; export default router;

View File

@ -165,8 +165,20 @@ export class BatchSchedulerService {
executionLog = executionLogResponse.data; executionLog = executionLogResponse.data;
// 실제 배치 실행 로직 (수동 실행과 동일한 로직 사용) // 실행 유형에 따라 분기: node_flow면 노드 플로우 실행, 아니면 매핑 배치 실행
const result = await this.executeBatchMappings(config); let result: {
totalRecords: number;
successRecords: number;
failedRecords: number;
};
if (
config.execution_type === "node_flow" &&
config.node_flow_id != null
) {
result = await this.executeNodeFlow(config);
} else {
result = await this.executeBatchMappings(config);
}
// 실행 로그 업데이트 (성공) // 실행 로그 업데이트 (성공)
await BatchExecutionLogService.updateExecutionLog(executionLog.id, { await BatchExecutionLogService.updateExecutionLog(executionLog.id, {
@ -207,6 +219,50 @@ export class BatchSchedulerService {
} }
} }
/**
* (execution_type === 'node_flow' )
* node_flows NodeFlowExecutionService로
*/
private static async executeNodeFlow(config: any): Promise<{
totalRecords: number;
successRecords: number;
failedRecords: number;
}> {
const { NodeFlowExecutionService } = await import(
"./nodeFlowExecutionService"
);
// 플로우 존재 여부 확인
const flowCheck = await query<{ flow_id: number; flow_name: string }>(
"SELECT flow_id, flow_name FROM node_flows WHERE flow_id = $1",
[config.node_flow_id]
);
if (flowCheck.length === 0) {
throw new Error(
`노드 플로우를 찾을 수 없습니다 (flow_id: ${config.node_flow_id})`
);
}
const contextData: Record<string, any> = {
...(config.node_flow_context || {}),
_batchId: config.id,
_batchName: config.batch_name,
_companyCode: config.company_code,
_executedBy: "batch_system",
};
const flowResult = await NodeFlowExecutionService.executeFlow(
config.node_flow_id,
contextData
);
return {
totalRecords: flowResult.summary.total,
successRecords: flowResult.summary.success,
failedRecords: flowResult.summary.failed,
};
}
/** /**
* ( ) * ( )
*/ */

View File

@ -176,8 +176,8 @@ export class BatchService {
// 배치 설정 생성 // 배치 설정 생성
const batchConfigResult = await client.query( const batchConfigResult = await client.query(
`INSERT INTO batch_configs `INSERT INTO batch_configs
(batch_name, description, cron_schedule, is_active, company_code, save_mode, conflict_key, auth_service_name, data_array_path, created_by, created_date, updated_date) (batch_name, description, cron_schedule, is_active, company_code, save_mode, conflict_key, auth_service_name, data_array_path, execution_type, node_flow_id, node_flow_context, created_by, created_date, updated_date)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW(), NOW()) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, NOW(), NOW())
RETURNING *`, RETURNING *`,
[ [
data.batchName, data.batchName,
@ -189,6 +189,11 @@ export class BatchService {
data.conflictKey || null, data.conflictKey || null,
data.authServiceName || null, data.authServiceName || null,
data.dataArrayPath || null, data.dataArrayPath || null,
data.executionType || "mapping",
data.nodeFlowId ?? null,
data.nodeFlowContext != null
? JSON.stringify(data.nodeFlowContext)
: null,
userId, userId,
] ]
); );
@ -332,6 +337,22 @@ export class BatchService {
updateFields.push(`data_array_path = $${paramIndex++}`); updateFields.push(`data_array_path = $${paramIndex++}`);
updateValues.push(data.dataArrayPath || null); updateValues.push(data.dataArrayPath || null);
} }
if (data.executionType !== undefined) {
updateFields.push(`execution_type = $${paramIndex++}`);
updateValues.push(data.executionType);
}
if (data.nodeFlowId !== undefined) {
updateFields.push(`node_flow_id = $${paramIndex++}`);
updateValues.push(data.nodeFlowId ?? null);
}
if (data.nodeFlowContext !== undefined) {
updateFields.push(`node_flow_context = $${paramIndex++}`);
updateValues.push(
data.nodeFlowContext != null
? JSON.stringify(data.nodeFlowContext)
: null
);
}
// 배치 설정 업데이트 // 배치 설정 업데이트
const batchConfigResult = await client.query( const batchConfigResult = await client.query(

View File

@ -91,6 +91,12 @@ export interface BatchConfig {
conflict_key?: string; // UPSERT 시 충돌 기준 컬럼명 conflict_key?: string; // UPSERT 시 충돌 기준 컬럼명
auth_service_name?: string; // REST API 인증에 사용할 토큰 서비스명 auth_service_name?: string; // REST API 인증에 사용할 토큰 서비스명
data_array_path?: string; // REST API 응답에서 데이터 배열 경로 (예: response, data.items) data_array_path?: string; // REST API 응답에서 데이터 배열 경로 (예: response, data.items)
/** 실행 유형: mapping(테이블 매핑) | node_flow(노드 플로우) */
execution_type?: "mapping" | "node_flow";
/** 노드 플로우 실행 시 사용할 flow_id (node_flows.flow_id) */
node_flow_id?: number;
/** 노드 플로우 실행 시 전달할 컨텍스트 (Record<string, any>) */
node_flow_context?: Record<string, any>;
created_by?: string; created_by?: string;
created_date?: Date; created_date?: Date;
updated_by?: string; updated_by?: string;
@ -150,6 +156,9 @@ export interface CreateBatchConfigRequest {
conflictKey?: string; conflictKey?: string;
authServiceName?: string; authServiceName?: string;
dataArrayPath?: string; // REST API 응답에서 데이터 배열 경로 dataArrayPath?: string; // REST API 응답에서 데이터 배열 경로
executionType?: "mapping" | "node_flow";
nodeFlowId?: number;
nodeFlowContext?: Record<string, any>;
mappings: BatchMappingRequest[]; mappings: BatchMappingRequest[];
} }
@ -162,6 +171,9 @@ export interface UpdateBatchConfigRequest {
conflictKey?: string; conflictKey?: string;
authServiceName?: string; authServiceName?: string;
dataArrayPath?: string; // REST API 응답에서 데이터 배열 경로 dataArrayPath?: string; // REST API 응답에서 데이터 배열 경로
executionType?: "mapping" | "node_flow";
nodeFlowId?: number;
nodeFlowContext?: Record<string, any>;
mappings?: BatchMappingRequest[]; mappings?: BatchMappingRequest[];
} }