저장버튼 제어기능 (insert)
This commit is contained in:
520
backend-node/src/services/dataflowControlService.ts
Normal file
520
backend-node/src/services/dataflowControlService.ts
Normal file
@@ -0,0 +1,520 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export interface ControlCondition {
|
||||
id: string;
|
||||
type: "condition" | "group-start" | "group-end";
|
||||
field?: string;
|
||||
value?: any;
|
||||
operator?: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN";
|
||||
dataType?: "string" | "number" | "date" | "boolean";
|
||||
logicalOperator?: "AND" | "OR";
|
||||
groupId?: string;
|
||||
groupLevel?: number;
|
||||
}
|
||||
|
||||
export interface ControlAction {
|
||||
id: string;
|
||||
name: string;
|
||||
actionType: "insert" | "update" | "delete";
|
||||
conditions: ControlCondition[];
|
||||
fieldMappings: {
|
||||
sourceField?: string;
|
||||
sourceTable?: string;
|
||||
targetField: string;
|
||||
targetTable: string;
|
||||
defaultValue?: any;
|
||||
}[];
|
||||
splitConfig?: {
|
||||
delimiter?: string;
|
||||
sourceField?: string;
|
||||
targetField?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ControlPlan {
|
||||
id: string;
|
||||
sourceTable: string;
|
||||
actions: ControlAction[];
|
||||
}
|
||||
|
||||
export interface ControlRule {
|
||||
id: string;
|
||||
triggerType: "insert" | "update" | "delete";
|
||||
conditions: ControlCondition[];
|
||||
}
|
||||
|
||||
export class DataflowControlService {
|
||||
/**
|
||||
* 제어관리 실행 메인 함수
|
||||
*/
|
||||
async executeDataflowControl(
|
||||
diagramId: number,
|
||||
relationshipId: string,
|
||||
triggerType: "insert" | "update" | "delete",
|
||||
sourceData: Record<string, any>,
|
||||
tableName: string
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
message: string;
|
||||
executedActions?: any[];
|
||||
errors?: string[];
|
||||
}> {
|
||||
try {
|
||||
console.log(`🎯 제어관리 실행 시작:`, {
|
||||
diagramId,
|
||||
relationshipId,
|
||||
triggerType,
|
||||
sourceData,
|
||||
tableName,
|
||||
});
|
||||
|
||||
// 관계도 정보 조회
|
||||
const diagram = await prisma.dataflow_diagrams.findUnique({
|
||||
where: { diagram_id: diagramId },
|
||||
});
|
||||
|
||||
if (!diagram) {
|
||||
return {
|
||||
success: false,
|
||||
message: `관계도를 찾을 수 없습니다. (ID: ${diagramId})`,
|
||||
};
|
||||
}
|
||||
|
||||
// 제어 규칙과 실행 계획 추출
|
||||
const controlRules = (diagram.control as unknown as ControlRule[]) || [];
|
||||
const executionPlans = (diagram.plan as unknown as ControlPlan[]) || [];
|
||||
|
||||
console.log(`📋 제어 규칙:`, controlRules);
|
||||
console.log(`📋 실행 계획:`, executionPlans);
|
||||
|
||||
// 해당 관계의 제어 규칙 찾기
|
||||
const targetRule = controlRules.find(
|
||||
(rule) => rule.id === relationshipId && rule.triggerType === triggerType
|
||||
);
|
||||
|
||||
if (!targetRule) {
|
||||
console.log(
|
||||
`⚠️ 해당 관계의 제어 규칙을 찾을 수 없습니다: ${relationshipId}`
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: "해당 관계의 제어 규칙이 없습니다.",
|
||||
};
|
||||
}
|
||||
|
||||
// 제어 조건 검증
|
||||
const conditionResult = await this.evaluateConditions(
|
||||
targetRule.conditions,
|
||||
sourceData
|
||||
);
|
||||
|
||||
console.log(`🔍 조건 검증 결과:`, conditionResult);
|
||||
|
||||
if (!conditionResult.satisfied) {
|
||||
return {
|
||||
success: true,
|
||||
message: `제어 조건을 만족하지 않습니다: ${conditionResult.reason}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 실행 계획 찾기
|
||||
const targetPlan = executionPlans.find(
|
||||
(plan) => plan.id === relationshipId
|
||||
);
|
||||
|
||||
if (!targetPlan) {
|
||||
return {
|
||||
success: true,
|
||||
message: "실행할 계획이 없습니다.",
|
||||
};
|
||||
}
|
||||
|
||||
// 액션 실행
|
||||
const executedActions = [];
|
||||
const errors = [];
|
||||
|
||||
for (const action of targetPlan.actions) {
|
||||
try {
|
||||
console.log(`⚡ 액션 실행: ${action.name} (${action.actionType})`);
|
||||
|
||||
// 액션 조건 검증 (있는 경우)
|
||||
if (action.conditions && action.conditions.length > 0) {
|
||||
const actionConditionResult = await this.evaluateConditions(
|
||||
action.conditions,
|
||||
sourceData
|
||||
);
|
||||
|
||||
if (!actionConditionResult.satisfied) {
|
||||
console.log(
|
||||
`⚠️ 액션 조건 미충족: ${actionConditionResult.reason}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const actionResult = await this.executeAction(action, sourceData);
|
||||
executedActions.push({
|
||||
actionId: action.id,
|
||||
actionName: action.name,
|
||||
result: actionResult,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`❌ 액션 실행 오류: ${action.name}`, error);
|
||||
errors.push(
|
||||
`액션 '${action.name}' 실행 오류: ${error instanceof Error ? error.message : String(error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `제어관리 실행 완료. ${executedActions.length}개 액션 실행됨.`,
|
||||
executedActions,
|
||||
errors: errors.length > 0 ? errors : undefined,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("❌ 제어관리 실행 오류:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `제어관리 실행 중 오류 발생: ${error instanceof Error ? error.message : String(error)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건 평가
|
||||
*/
|
||||
private async evaluateConditions(
|
||||
conditions: ControlCondition[],
|
||||
data: Record<string, any>
|
||||
): Promise<{ satisfied: boolean; reason?: string }> {
|
||||
if (!conditions || conditions.length === 0) {
|
||||
return { satisfied: true };
|
||||
}
|
||||
|
||||
try {
|
||||
// 조건을 SQL WHERE 절로 변환
|
||||
const whereClause = this.buildWhereClause(conditions, data);
|
||||
console.log(`🔍 생성된 WHERE 절:`, whereClause);
|
||||
|
||||
// 간단한 조건 평가 (실제로는 더 복잡한 로직 필요)
|
||||
for (const condition of conditions) {
|
||||
if (condition.type === "condition" && condition.field) {
|
||||
const fieldValue = data[condition.field];
|
||||
const conditionValue = condition.value;
|
||||
|
||||
console.log(
|
||||
`🔍 조건 평가: ${condition.field} ${condition.operator} ${conditionValue} (실제값: ${fieldValue})`
|
||||
);
|
||||
|
||||
const result = this.evaluateSingleCondition(
|
||||
fieldValue,
|
||||
condition.operator || "=",
|
||||
conditionValue,
|
||||
condition.dataType || "string"
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
satisfied: false,
|
||||
reason: `조건 미충족: ${condition.field} ${condition.operator} ${conditionValue}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { satisfied: true };
|
||||
} catch (error) {
|
||||
console.error("조건 평가 오류:", error);
|
||||
return {
|
||||
satisfied: false,
|
||||
reason: `조건 평가 오류: ${error instanceof Error ? error.message : String(error)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 조건 평가
|
||||
*/
|
||||
private evaluateSingleCondition(
|
||||
fieldValue: any,
|
||||
operator: string,
|
||||
conditionValue: any,
|
||||
dataType: string
|
||||
): boolean {
|
||||
// 타입 변환
|
||||
let actualValue = fieldValue;
|
||||
let expectedValue = conditionValue;
|
||||
|
||||
if (dataType === "number") {
|
||||
actualValue = parseFloat(fieldValue) || 0;
|
||||
expectedValue = parseFloat(conditionValue) || 0;
|
||||
} else if (dataType === "string") {
|
||||
actualValue = String(fieldValue || "");
|
||||
expectedValue = String(conditionValue || "");
|
||||
}
|
||||
|
||||
// 연산자별 평가
|
||||
switch (operator) {
|
||||
case "=":
|
||||
return actualValue === expectedValue;
|
||||
case "!=":
|
||||
return actualValue !== expectedValue;
|
||||
case ">":
|
||||
return actualValue > expectedValue;
|
||||
case "<":
|
||||
return actualValue < expectedValue;
|
||||
case ">=":
|
||||
return actualValue >= expectedValue;
|
||||
case "<=":
|
||||
return actualValue <= expectedValue;
|
||||
case "LIKE":
|
||||
return String(actualValue).includes(String(expectedValue));
|
||||
default:
|
||||
console.warn(`지원되지 않는 연산자: ${operator}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WHERE 절 생성 (복잡한 그룹 조건 처리)
|
||||
*/
|
||||
private buildWhereClause(
|
||||
conditions: ControlCondition[],
|
||||
data: Record<string, any>
|
||||
): string {
|
||||
// 실제로는 더 복잡한 그룹 처리 로직이 필요
|
||||
// 현재는 간단한 AND/OR 처리만 구현
|
||||
const clauses = [];
|
||||
|
||||
for (const condition of conditions) {
|
||||
if (condition.type === "condition") {
|
||||
const clause = `${condition.field} ${condition.operator} '${condition.value}'`;
|
||||
clauses.push(clause);
|
||||
}
|
||||
}
|
||||
|
||||
return clauses.join(" AND ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 액션 실행
|
||||
*/
|
||||
private async executeAction(
|
||||
action: ControlAction,
|
||||
sourceData: Record<string, any>
|
||||
): Promise<any> {
|
||||
console.log(`🚀 액션 실행: ${action.actionType}`, action);
|
||||
|
||||
switch (action.actionType) {
|
||||
case "insert":
|
||||
return await this.executeInsertAction(action, sourceData);
|
||||
case "update":
|
||||
return await this.executeUpdateAction(action, sourceData);
|
||||
case "delete":
|
||||
return await this.executeDeleteAction(action, sourceData);
|
||||
default:
|
||||
throw new Error(`지원되지 않는 액션 타입: ${action.actionType}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INSERT 액션 실행
|
||||
*/
|
||||
private async executeInsertAction(
|
||||
action: ControlAction,
|
||||
sourceData: Record<string, any>
|
||||
): Promise<any> {
|
||||
const results = [];
|
||||
|
||||
for (const mapping of action.fieldMappings) {
|
||||
const { targetTable, targetField, defaultValue, sourceField } = mapping;
|
||||
|
||||
// 삽입할 데이터 준비
|
||||
const insertData: Record<string, any> = {};
|
||||
|
||||
if (sourceField && sourceData[sourceField]) {
|
||||
insertData[targetField] = sourceData[sourceField];
|
||||
} else if (defaultValue !== undefined) {
|
||||
insertData[targetField] = defaultValue;
|
||||
}
|
||||
|
||||
// 동적으로 테이블 컬럼 정보 조회하여 기본 필드 추가
|
||||
await this.addDefaultFieldsForTable(targetTable, insertData);
|
||||
|
||||
console.log(`📝 INSERT 실행: ${targetTable}.${targetField}`, insertData);
|
||||
|
||||
try {
|
||||
// 동적 테이블 INSERT 실행
|
||||
const result = await prisma.$executeRawUnsafe(
|
||||
`
|
||||
INSERT INTO ${targetTable} (${Object.keys(insertData).join(", ")})
|
||||
VALUES (${Object.keys(insertData)
|
||||
.map((_, index) => `$${index + 1}`)
|
||||
.join(", ")})
|
||||
`,
|
||||
...Object.values(insertData)
|
||||
);
|
||||
|
||||
results.push({
|
||||
table: targetTable,
|
||||
field: targetField,
|
||||
data: insertData,
|
||||
result,
|
||||
});
|
||||
|
||||
console.log(`✅ INSERT 성공: ${targetTable}.${targetField}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ INSERT 실패: ${targetTable}.${targetField}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* UPDATE 액션 실행
|
||||
*/
|
||||
private async executeUpdateAction(
|
||||
action: ControlAction,
|
||||
sourceData: Record<string, any>
|
||||
): Promise<any> {
|
||||
// UPDATE 로직 구현
|
||||
console.log("UPDATE 액션 실행 (미구현)");
|
||||
return { message: "UPDATE 액션은 아직 구현되지 않았습니다." };
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE 액션 실행
|
||||
*/
|
||||
private async executeDeleteAction(
|
||||
action: ControlAction,
|
||||
sourceData: Record<string, any>
|
||||
): Promise<any> {
|
||||
// DELETE 로직 구현
|
||||
console.log("DELETE 액션 실행 (미구현)");
|
||||
return { message: "DELETE 액션은 아직 구현되지 않았습니다." };
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블의 컬럼 정보를 동적으로 조회하여 기본 필드 추가
|
||||
*/
|
||||
private async addDefaultFieldsForTable(
|
||||
tableName: string,
|
||||
insertData: Record<string, any>
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 테이블의 컬럼 정보 조회
|
||||
const columns = await prisma.$queryRawUnsafe<
|
||||
Array<{ column_name: string; data_type: string; is_nullable: string }>
|
||||
>(
|
||||
`
|
||||
SELECT column_name, data_type, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = $1
|
||||
ORDER BY ordinal_position
|
||||
`,
|
||||
tableName
|
||||
);
|
||||
|
||||
console.log(`📋 ${tableName} 테이블 컬럼 정보:`, columns);
|
||||
|
||||
const currentDate = new Date();
|
||||
|
||||
// 일반적인 타임스탬프 필드들 확인 및 추가
|
||||
const timestampFields = [
|
||||
{
|
||||
names: ["created_at", "create_date", "reg_date", "regdate"],
|
||||
value: currentDate,
|
||||
},
|
||||
{
|
||||
names: ["updated_at", "update_date", "mod_date", "moddate"],
|
||||
value: currentDate,
|
||||
},
|
||||
];
|
||||
|
||||
for (const fieldGroup of timestampFields) {
|
||||
for (const fieldName of fieldGroup.names) {
|
||||
const column = columns.find(
|
||||
(col) => col.column_name.toLowerCase() === fieldName.toLowerCase()
|
||||
);
|
||||
if (column && !insertData[column.column_name]) {
|
||||
// 해당 컬럼이 존재하고 아직 값이 설정되지 않은 경우
|
||||
if (
|
||||
column.data_type.includes("timestamp") ||
|
||||
column.data_type.includes("date")
|
||||
) {
|
||||
insertData[column.column_name] = fieldGroup.value;
|
||||
console.log(
|
||||
`📅 기본 타임스탬프 필드 추가: ${column.column_name} = ${fieldGroup.value}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 필수 필드 중 값이 없는 경우 기본값 설정
|
||||
for (const column of columns) {
|
||||
if (column.is_nullable === "NO" && !insertData[column.column_name]) {
|
||||
// NOT NULL 필드인데 값이 없는 경우 기본값 설정
|
||||
const defaultValue = this.getDefaultValueForColumn(column);
|
||||
if (defaultValue !== null) {
|
||||
insertData[column.column_name] = defaultValue;
|
||||
console.log(
|
||||
`🔧 필수 필드 기본값 설정: ${column.column_name} = ${defaultValue}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ ${tableName} 테이블 컬럼 정보 조회 실패:`, error);
|
||||
// 에러가 발생해도 INSERT는 계속 진행 (기본 필드 없이)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 타입에 따른 기본값 반환
|
||||
*/
|
||||
private getDefaultValueForColumn(column: {
|
||||
column_name: string;
|
||||
data_type: string;
|
||||
}): any {
|
||||
const dataType = column.data_type.toLowerCase();
|
||||
const columnName = column.column_name.toLowerCase();
|
||||
|
||||
// 컬럼명 기반 기본값
|
||||
if (columnName.includes("status")) {
|
||||
return "Y"; // 상태 필드는 보통 'Y'
|
||||
}
|
||||
if (columnName.includes("type")) {
|
||||
return "default"; // 타입 필드는 'default'
|
||||
}
|
||||
|
||||
// 데이터 타입 기반 기본값
|
||||
if (
|
||||
dataType.includes("varchar") ||
|
||||
dataType.includes("text") ||
|
||||
dataType.includes("char")
|
||||
) {
|
||||
return ""; // 문자열은 빈 문자열
|
||||
}
|
||||
if (
|
||||
dataType.includes("int") ||
|
||||
dataType.includes("numeric") ||
|
||||
dataType.includes("decimal")
|
||||
) {
|
||||
return 0; // 숫자는 0
|
||||
}
|
||||
if (dataType.includes("bool")) {
|
||||
return false; // 불린은 false
|
||||
}
|
||||
if (dataType.includes("timestamp") || dataType.includes("date")) {
|
||||
return new Date(); // 날짜는 현재 시간
|
||||
}
|
||||
|
||||
return null; // 기본값을 설정할 수 없는 경우
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import prisma from "../config/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { EventTriggerService } from "./eventTriggerService";
|
||||
import { DataflowControlService } from "./dataflowControlService";
|
||||
|
||||
export interface FormDataResult {
|
||||
id: number;
|
||||
@@ -42,6 +43,71 @@ export interface TableColumn {
|
||||
}
|
||||
|
||||
export class DynamicFormService {
|
||||
private dataflowControlService = new DataflowControlService();
|
||||
/**
|
||||
* 값을 PostgreSQL 타입에 맞게 변환
|
||||
*/
|
||||
private convertValueForPostgreSQL(value: any, dataType: string): any {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lowerDataType = dataType.toLowerCase();
|
||||
|
||||
// 숫자 타입 처리
|
||||
if (
|
||||
lowerDataType.includes("integer") ||
|
||||
lowerDataType.includes("bigint") ||
|
||||
lowerDataType.includes("serial")
|
||||
) {
|
||||
return parseInt(value) || null;
|
||||
}
|
||||
|
||||
if (
|
||||
lowerDataType.includes("numeric") ||
|
||||
lowerDataType.includes("decimal") ||
|
||||
lowerDataType.includes("real") ||
|
||||
lowerDataType.includes("double")
|
||||
) {
|
||||
return parseFloat(value) || null;
|
||||
}
|
||||
|
||||
// 불린 타입 처리
|
||||
if (lowerDataType.includes("boolean")) {
|
||||
if (typeof value === "boolean") return value;
|
||||
if (typeof value === "string") {
|
||||
return value.toLowerCase() === "true" || value === "1";
|
||||
}
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
// 기본적으로 문자열로 반환
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블의 컬럼 정보 조회 (타입 포함)
|
||||
*/
|
||||
private async getTableColumnInfo(
|
||||
tableName: string
|
||||
): Promise<Array<{ column_name: string; data_type: string }>> {
|
||||
try {
|
||||
const result = await prisma.$queryRaw<
|
||||
Array<{ column_name: string; data_type: string }>
|
||||
>`
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = ${tableName}
|
||||
AND table_schema = 'public'
|
||||
`;
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`테이블 ${tableName}의 컬럼 정보 조회 실패:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블의 컬럼명 목록 조회 (간단 버전)
|
||||
*/
|
||||
@@ -196,6 +262,32 @@ export class DynamicFormService {
|
||||
dataToInsert,
|
||||
});
|
||||
|
||||
// 테이블 컬럼 정보 조회하여 타입 변환 적용
|
||||
console.log("🔍 테이블 컬럼 정보 조회 중...");
|
||||
const columnInfo = await this.getTableColumnInfo(tableName);
|
||||
console.log("📊 테이블 컬럼 정보:", columnInfo);
|
||||
|
||||
// 각 컬럼의 타입에 맞게 데이터 변환
|
||||
Object.keys(dataToInsert).forEach((columnName) => {
|
||||
const column = columnInfo.find((col) => col.column_name === columnName);
|
||||
if (column) {
|
||||
const originalValue = dataToInsert[columnName];
|
||||
const convertedValue = this.convertValueForPostgreSQL(
|
||||
originalValue,
|
||||
column.data_type
|
||||
);
|
||||
|
||||
if (originalValue !== convertedValue) {
|
||||
console.log(
|
||||
`🔄 타입 변환: ${columnName} (${column.data_type}) = "${originalValue}" -> ${convertedValue}`
|
||||
);
|
||||
dataToInsert[columnName] = convertedValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ 타입 변환 완료된 데이터:", dataToInsert);
|
||||
|
||||
// 동적 SQL을 사용하여 실제 테이블에 UPSERT
|
||||
const columns = Object.keys(dataToInsert);
|
||||
const values: any[] = Object.values(dataToInsert);
|
||||
@@ -264,6 +356,19 @@ export class DynamicFormService {
|
||||
// 트리거 오류는 로그만 남기고 메인 저장 프로세스는 계속 진행
|
||||
}
|
||||
|
||||
// 🎯 제어관리 실행 (새로 추가)
|
||||
try {
|
||||
await this.executeDataflowControlIfConfigured(
|
||||
screenId,
|
||||
tableName,
|
||||
insertedRecord as Record<string, any>,
|
||||
"insert"
|
||||
);
|
||||
} catch (controlError) {
|
||||
console.error("⚠️ 제어관리 실행 오류:", controlError);
|
||||
// 제어관리 오류는 로그만 남기고 메인 저장 프로세스는 계속 진행
|
||||
}
|
||||
|
||||
return {
|
||||
id: insertedRecord.id || insertedRecord.objid || 0,
|
||||
screenId: screenId,
|
||||
@@ -674,6 +779,85 @@ export class DynamicFormService {
|
||||
throw new Error(`테이블 컬럼 정보 조회 실패: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 제어관리 실행 (화면에 설정된 경우)
|
||||
*/
|
||||
private async executeDataflowControlIfConfigured(
|
||||
screenId: number,
|
||||
tableName: string,
|
||||
savedData: Record<string, any>,
|
||||
triggerType: "insert" | "update" | "delete"
|
||||
): Promise<void> {
|
||||
try {
|
||||
console.log(`🎯 제어관리 설정 확인 중... (screenId: ${screenId})`);
|
||||
|
||||
// 화면의 저장 버튼에서 제어관리 설정 조회
|
||||
const screenLayouts = await prisma.screen_layouts.findMany({
|
||||
where: {
|
||||
screen_id: screenId,
|
||||
component_type: "component",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`📋 화면 컴포넌트 조회 결과:`, screenLayouts.length);
|
||||
|
||||
// 저장 버튼 중에서 제어관리가 활성화된 것 찾기
|
||||
for (const layout of screenLayouts) {
|
||||
const properties = layout.properties as any;
|
||||
|
||||
// 버튼 컴포넌트이고 저장 액션이며 제어관리가 활성화된 경우
|
||||
if (
|
||||
properties?.componentType === "button-primary" &&
|
||||
properties?.componentConfig?.action?.type === "save" &&
|
||||
properties?.webTypeConfig?.enableDataflowControl === true &&
|
||||
properties?.webTypeConfig?.dataflowConfig?.selectedDiagramId
|
||||
) {
|
||||
const diagramId =
|
||||
properties.webTypeConfig.dataflowConfig.selectedDiagramId;
|
||||
const relationshipId =
|
||||
properties.webTypeConfig.dataflowConfig.selectedRelationshipId;
|
||||
|
||||
console.log(`🎯 제어관리 설정 발견:`, {
|
||||
componentId: layout.component_id,
|
||||
diagramId,
|
||||
relationshipId,
|
||||
triggerType,
|
||||
});
|
||||
|
||||
// 제어관리 실행
|
||||
const controlResult =
|
||||
await this.dataflowControlService.executeDataflowControl(
|
||||
diagramId,
|
||||
relationshipId,
|
||||
triggerType,
|
||||
savedData,
|
||||
tableName
|
||||
);
|
||||
|
||||
console.log(`🎯 제어관리 실행 결과:`, controlResult);
|
||||
|
||||
if (controlResult.success) {
|
||||
console.log(`✅ 제어관리 실행 성공: ${controlResult.message}`);
|
||||
if (
|
||||
controlResult.executedActions &&
|
||||
controlResult.executedActions.length > 0
|
||||
) {
|
||||
console.log(`📊 실행된 액션들:`, controlResult.executedActions);
|
||||
}
|
||||
} else {
|
||||
console.warn(`⚠️ 제어관리 실행 실패: ${controlResult.message}`);
|
||||
}
|
||||
|
||||
// 첫 번째 설정된 제어관리만 실행 (여러 개가 있을 경우)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 제어관리 설정 확인 및 실행 오류:", error);
|
||||
// 에러를 다시 던지지 않음 - 메인 저장 프로세스에 영향 주지 않기 위해
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 싱글톤 인스턴스 생성 및 export
|
||||
|
||||
Reference in New Issue
Block a user