DB 스키마 업데이트
This commit is contained in:
parent
898866a2f0
commit
978a4937ad
|
|
@ -5328,6 +5328,12 @@ model dataflow_diagrams {
|
||||||
diagram_name String @db.VarChar(255)
|
diagram_name String @db.VarChar(255)
|
||||||
relationships Json // 모든 관계 정보를 JSON으로 저장
|
relationships Json // 모든 관계 정보를 JSON으로 저장
|
||||||
node_positions Json? // 테이블 노드의 캔버스 위치 정보 (JSON 형태)
|
node_positions Json? // 테이블 노드의 캔버스 위치 정보 (JSON 형태)
|
||||||
|
|
||||||
|
// 조건부 연결 관련 컬럼들
|
||||||
|
control Json? // 조건 설정 (트리거 타입, 조건 트리)
|
||||||
|
category Json? // 연결 종류 (타입, 기본 옵션)
|
||||||
|
plan Json? // 실행 계획 (대상 액션들)
|
||||||
|
|
||||||
company_code String @db.VarChar(50)
|
company_code String @db.VarChar(50)
|
||||||
created_at DateTime? @default(now()) @db.Timestamp(6)
|
created_at DateTime? @default(now()) @db.Timestamp(6)
|
||||||
updated_at DateTime? @default(now()) @updatedAt @db.Timestamp(6)
|
updated_at DateTime? @default(now()) @updatedAt @db.Timestamp(6)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ interface CreateDataflowDiagramData {
|
||||||
diagram_name: string;
|
diagram_name: string;
|
||||||
relationships: any; // JSON 데이터
|
relationships: any; // JSON 데이터
|
||||||
node_positions?: any; // JSON 데이터 (노드 위치 정보)
|
node_positions?: any; // JSON 데이터 (노드 위치 정보)
|
||||||
|
|
||||||
|
// 조건부 연결 관련 필드
|
||||||
|
control?: any; // JSON 데이터 (조건 설정)
|
||||||
|
category?: any; // JSON 데이터 (연결 종류)
|
||||||
|
plan?: any; // JSON 데이터 (실행 계획)
|
||||||
|
|
||||||
company_code: string;
|
company_code: string;
|
||||||
created_by: string;
|
created_by: string;
|
||||||
updated_by: string;
|
updated_by: string;
|
||||||
|
|
@ -17,6 +23,12 @@ interface UpdateDataflowDiagramData {
|
||||||
diagram_name?: string;
|
diagram_name?: string;
|
||||||
relationships?: any; // JSON 데이터
|
relationships?: any; // JSON 데이터
|
||||||
node_positions?: any; // JSON 데이터 (노드 위치 정보)
|
node_positions?: any; // JSON 데이터 (노드 위치 정보)
|
||||||
|
|
||||||
|
// 조건부 연결 관련 필드
|
||||||
|
control?: any; // JSON 데이터 (조건 설정)
|
||||||
|
category?: any; // JSON 데이터 (연결 종류)
|
||||||
|
plan?: any; // JSON 데이터 (실행 계획)
|
||||||
|
|
||||||
updated_by: string;
|
updated_by: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,297 @@
|
||||||
|
# 🔗 조건부 연결 기능 구현 계획
|
||||||
|
|
||||||
|
## 📋 프로젝트 개요
|
||||||
|
|
||||||
|
현재 DataFlow 시스템에서 3가지 연결 종류를 지원하고 있으며, 이 중 **데이터 저장**과 **외부 호출** 기능에 조건부 실행 로직을 추가해야 합니다.
|
||||||
|
|
||||||
|
### 현재 연결 종류
|
||||||
|
|
||||||
|
1. **단순 키값 연결** - 조건 설정 불필요 (기존 방식 유지)
|
||||||
|
2. **데이터 저장** - 조건부 실행 필요 ✨
|
||||||
|
3. **외부 호출** - 조건부 실행 필요 ✨
|
||||||
|
|
||||||
|
## 🎯 기능 요구사항
|
||||||
|
|
||||||
|
### 데이터 저장 기능
|
||||||
|
|
||||||
|
```
|
||||||
|
"from 테이블의 컬럼이 특정 조건을 만족하면 to 테이블에 특정 액션을 취할 것"
|
||||||
|
```
|
||||||
|
|
||||||
|
**예시 시나리오:**
|
||||||
|
|
||||||
|
- `work_order` 테이블의 `status = 'APPROVED'` 이고 `quantity > 0` 일 때
|
||||||
|
- `material_requirement` 테이블에 자재 소요량 데이터 INSERT
|
||||||
|
|
||||||
|
### 외부 호출 기능
|
||||||
|
|
||||||
|
```
|
||||||
|
"from테이블의 컬럼이 특정 조건을 만족하면 외부 api호출이나 이메일 발송 등의 동작을 취해야 함"
|
||||||
|
```
|
||||||
|
|
||||||
|
**예시 시나리오:**
|
||||||
|
|
||||||
|
- `employee_master` 테이블의 `employment_status = 'APPROVED'` 일 때
|
||||||
|
- 외부 이메일 API 호출하여 환영 메일 발송
|
||||||
|
|
||||||
|
## 🗄️ 데이터베이스 스키마 변경
|
||||||
|
|
||||||
|
### 1. 컬럼 추가
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 기존 데이터 삭제 후 dataflow_diagrams 테이블에 3개 컬럼 추가
|
||||||
|
DELETE FROM dataflow_diagrams; -- 기존 데이터 전체 삭제
|
||||||
|
|
||||||
|
ALTER TABLE dataflow_diagrams
|
||||||
|
ADD COLUMN control JSONB, -- 조건 설정
|
||||||
|
ADD COLUMN category JSONB, -- 연결 종류 설정
|
||||||
|
ADD COLUMN plan JSONB; -- 실행 계획 설정
|
||||||
|
|
||||||
|
-- 인덱스 추가
|
||||||
|
CREATE INDEX idx_dataflow_control_trigger ON dataflow_diagrams USING GIN ((control->'triggerType'));
|
||||||
|
CREATE INDEX idx_dataflow_category_type ON dataflow_diagrams USING GIN ((category->'type'));
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 데이터 구조 설계
|
||||||
|
|
||||||
|
#### `control` 컬럼 - 조건 설정
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"triggerType": "insert",
|
||||||
|
"conditionTree": {
|
||||||
|
"type": "group",
|
||||||
|
"operator": "AND",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "condition",
|
||||||
|
"field": "status",
|
||||||
|
"operator": "=",
|
||||||
|
"value": "APPROVED"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `category` 컬럼 - 연결 종류
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "data-save", // "simple-key" | "data-save" | "external-call"
|
||||||
|
"rollbackOnError": true,
|
||||||
|
"enableLogging": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `plan` 컬럼 - 실행 계획
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sourceTable": "work_order",
|
||||||
|
"targetActions": [
|
||||||
|
{
|
||||||
|
"id": "action_1",
|
||||||
|
"actionType": "insert",
|
||||||
|
"targetTable": "material_requirement",
|
||||||
|
"enabled": true,
|
||||||
|
"fieldMappings": [
|
||||||
|
{
|
||||||
|
"sourceField": "work_order_id",
|
||||||
|
"targetField": "order_id"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 프론트엔드 UI 개선
|
||||||
|
|
||||||
|
### ConnectionSetupModal.tsx 재설계
|
||||||
|
|
||||||
|
#### 현재 구조 문제점
|
||||||
|
|
||||||
|
- 모든 연결 종류에 동일한 UI 적용
|
||||||
|
- 조건 설정 기능 없음
|
||||||
|
- 단순 키값 연결과 조건부 연결의 구분 없음
|
||||||
|
|
||||||
|
#### 개선 방안
|
||||||
|
|
||||||
|
##### 1. 연결 종류별 UI 분기
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// 연결 종류 선택 후 조건부 렌더링
|
||||||
|
{
|
||||||
|
config.connectionType === "simple-key" && <SimpleKeyConnectionSettings />;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
(config.connectionType === "data-save" ||
|
||||||
|
config.connectionType === "external-call") && (
|
||||||
|
<ConditionalConnectionSettings />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 2. 조건 설정 섹션 추가
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// control.html의 제어 조건 설정 섹션을 참조하여 구현
|
||||||
|
<div className="control-conditions">
|
||||||
|
<h4>📋 실행 조건 설정</h4>
|
||||||
|
<ConditionBuilder
|
||||||
|
conditions={conditions}
|
||||||
|
onConditionsChange={setConditions}
|
||||||
|
availableFields={fromTableColumns}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 3. 액션 설정 섹션
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<div className="control-actions">
|
||||||
|
<h4>⚡ 실행 액션</h4>
|
||||||
|
{config.connectionType === "data-save" && <DataSaveActionSettings />}
|
||||||
|
{config.connectionType === "external-call" && <ExternalCallActionSettings />}
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 새로운 컴포넌트 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
ConnectionSetupModal.tsx
|
||||||
|
├── BasicConnectionInfo (공통)
|
||||||
|
├── ConnectionTypeSelector (공통)
|
||||||
|
├── SimpleKeyConnectionSettings (단순 키값 전용)
|
||||||
|
└── ConditionalConnectionSettings (조건부 연결 전용)
|
||||||
|
├── ConditionBuilder (조건 설정)
|
||||||
|
├── DataSaveActionSettings (데이터 저장 액션)
|
||||||
|
└── ExternalCallActionSettings (외부 호출 액션)
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ 백엔드 서비스 구현
|
||||||
|
|
||||||
|
### 1. EventTriggerService 생성
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// backend-node/src/services/eventTriggerService.ts
|
||||||
|
export class EventTriggerService {
|
||||||
|
static async executeEventTriggers(
|
||||||
|
triggerType: "insert" | "update" | "delete",
|
||||||
|
tableName: string,
|
||||||
|
data: Record<string, any>,
|
||||||
|
companyCode: string
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
|
static async executeDataSaveAction(
|
||||||
|
action: TargetAction,
|
||||||
|
sourceData: Record<string, any>
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
|
static async executeExternalCallAction(
|
||||||
|
action: ExternalCallAction,
|
||||||
|
sourceData: Record<string, any>
|
||||||
|
): Promise<void>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. DynamicFormService 연동
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 기존 saveFormData 메서드에 트리거 실행 추가
|
||||||
|
async saveFormData(screenId: number, tableName: string, data: Record<string, any>) {
|
||||||
|
// 기존 저장 로직
|
||||||
|
const result = await this.saveToDatabase(data);
|
||||||
|
|
||||||
|
// 🔥 조건부 연결 실행
|
||||||
|
await EventTriggerService.executeEventTriggers("insert", tableName, data, companyCode);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. API 엔드포인트 추가
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// backend-node/src/routes/dataflowRoutes.ts
|
||||||
|
router.post("/diagrams/:id/test-conditions", async (req, res) => {
|
||||||
|
// 조건 테스트 실행
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/diagrams/:id/execute-actions", async (req, res) => {
|
||||||
|
// 액션 수동 실행
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 구현 단계별 계획
|
||||||
|
|
||||||
|
### Phase 1: 데이터베이스 준비
|
||||||
|
|
||||||
|
- [ ] dataflow_diagrams 테이블 컬럼 추가 (기존 데이터 삭제 후 진행)
|
||||||
|
- [ ] Prisma 스키마 업데이트
|
||||||
|
|
||||||
|
### Phase 2: 프론트엔드 UI 개선
|
||||||
|
|
||||||
|
- [ ] ConnectionSetupModal.tsx 재구조화
|
||||||
|
- [ ] ConditionBuilder 컴포넌트 개발
|
||||||
|
- [ ] 연결 종류별 설정 컴포넌트 분리
|
||||||
|
- [ ] control.html 참조하여 조건 설정 UI 구현
|
||||||
|
|
||||||
|
### Phase 3: 백엔드 서비스 개발
|
||||||
|
|
||||||
|
- [ ] EventTriggerService 기본 구조 생성
|
||||||
|
- [ ] 조건 평가 엔진 구현
|
||||||
|
- [ ] 데이터 저장 액션 실행 로직
|
||||||
|
- [ ] DynamicFormService 연동
|
||||||
|
|
||||||
|
### Phase 4: 외부 호출 기능
|
||||||
|
|
||||||
|
- [ ] 외부 API 호출 서비스
|
||||||
|
- [ ] 이메일 발송 기능
|
||||||
|
- [ ] 웹훅 지원
|
||||||
|
- [ ] 오류 처리 및 재시도 로직
|
||||||
|
|
||||||
|
## 🔧 기술적 고려사항
|
||||||
|
|
||||||
|
### 1. 성능 최적화
|
||||||
|
|
||||||
|
- 조건 평가 시 인덱스 활용
|
||||||
|
- 대량 데이터 처리 시 배치 처리
|
||||||
|
- 비동기 실행으로 메인 로직 블로킹 방지
|
||||||
|
|
||||||
|
### 2. 오류 처리
|
||||||
|
|
||||||
|
- 트랜잭션 롤백 지원
|
||||||
|
- 부분 실패 시 복구 메커니즘
|
||||||
|
- 상세한 실행 로그 저장
|
||||||
|
|
||||||
|
### 3. 보안
|
||||||
|
|
||||||
|
- SQL 인젝션 방지
|
||||||
|
- 외부 API 호출 시 인증 처리
|
||||||
|
- 민감 데이터 마스킹
|
||||||
|
|
||||||
|
### 4. 확장성
|
||||||
|
|
||||||
|
- 새로운 액션 타입 추가 용이성
|
||||||
|
- 복잡한 조건문 지원
|
||||||
|
- 다양한 외부 서비스 연동
|
||||||
|
|
||||||
|
## 📚 참고 자료
|
||||||
|
|
||||||
|
- [control.html](../control.html) - 제어 조건 설정 UI 참조
|
||||||
|
- [ConnectionSetupModal.tsx](../frontend/components/dataflow/ConnectionSetupModal.tsx) - 현재 구현
|
||||||
|
- [화면간*데이터*관계*설정*시스템\_설계.md](./화면간_데이터_관계_설정_시스템_설계.md) - 전체 시스템 설계
|
||||||
|
|
||||||
|
## 🚀 다음 단계
|
||||||
|
|
||||||
|
1. **데이터베이스 스키마 업데이트** 부터 시작
|
||||||
|
2. **UI 재설계** - control.html 참조하여 조건 설정 UI 구현
|
||||||
|
3. **백엔드 서비스** 단계별 구현
|
||||||
|
4. **외부 호출 기능** 구현
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_이 문서는 조건부 연결 기능 구현을 위한 전체적인 로드맵을 제시합니다. 각 단계별로 상세한 구현 계획을 수립하여 진행할 예정입니다._
|
||||||
|
|
@ -2,6 +2,68 @@ import { apiClient, ApiResponse } from "./client";
|
||||||
|
|
||||||
// 테이블 간 데이터 관계 설정 관련 타입 정의
|
// 테이블 간 데이터 관계 설정 관련 타입 정의
|
||||||
|
|
||||||
|
// 조건부 연결 관련 타입들
|
||||||
|
export interface ConditionControl {
|
||||||
|
triggerType: "insert" | "update" | "delete" | "insert_update";
|
||||||
|
conditionTree: ConditionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConditionNode {
|
||||||
|
type: "group" | "condition";
|
||||||
|
operator?: "AND" | "OR";
|
||||||
|
children?: ConditionNode[];
|
||||||
|
field?: string;
|
||||||
|
operator_type?:
|
||||||
|
| "="
|
||||||
|
| "!="
|
||||||
|
| ">"
|
||||||
|
| "<"
|
||||||
|
| ">="
|
||||||
|
| "<="
|
||||||
|
| "LIKE"
|
||||||
|
| "NOT_LIKE"
|
||||||
|
| "CONTAINS"
|
||||||
|
| "STARTS_WITH"
|
||||||
|
| "ENDS_WITH"
|
||||||
|
| "IN"
|
||||||
|
| "NOT_IN"
|
||||||
|
| "IS_NULL"
|
||||||
|
| "IS_NOT_NULL"
|
||||||
|
| "BETWEEN"
|
||||||
|
| "NOT_BETWEEN";
|
||||||
|
value?: any;
|
||||||
|
dataType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConnectionCategory {
|
||||||
|
type: "simple-key" | "data-save" | "external-call" | "conditional-link";
|
||||||
|
rollbackOnError?: boolean;
|
||||||
|
enableLogging?: boolean;
|
||||||
|
maxRetryCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExecutionPlan {
|
||||||
|
sourceTable: string;
|
||||||
|
targetActions: TargetAction[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TargetAction {
|
||||||
|
id: string;
|
||||||
|
actionType: "insert" | "update" | "delete" | "upsert";
|
||||||
|
targetTable: string;
|
||||||
|
enabled: boolean;
|
||||||
|
fieldMappings: FieldMapping[];
|
||||||
|
conditions?: ConditionNode;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FieldMapping {
|
||||||
|
sourceField: string;
|
||||||
|
targetField: string;
|
||||||
|
transformFunction?: string;
|
||||||
|
defaultValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ColumnInfo {
|
export interface ColumnInfo {
|
||||||
columnName: string;
|
columnName: string;
|
||||||
columnLabel?: string;
|
columnLabel?: string;
|
||||||
|
|
@ -46,7 +108,7 @@ export interface TableRelationship {
|
||||||
to_table_name: string;
|
to_table_name: string;
|
||||||
to_column_name: string;
|
to_column_name: string;
|
||||||
relationship_type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
|
relationship_type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
|
||||||
connection_type: "simple-key" | "data-save" | "external-call";
|
connection_type: "simple-key" | "data-save" | "external-call" | "conditional-link";
|
||||||
settings?: Record<string, unknown>;
|
settings?: Record<string, unknown>;
|
||||||
company_code: string;
|
company_code: string;
|
||||||
is_active?: string;
|
is_active?: string;
|
||||||
|
|
@ -98,6 +160,11 @@ export interface DataFlowDiagram {
|
||||||
relationshipCount: number;
|
relationshipCount: number;
|
||||||
tables: string[];
|
tables: string[];
|
||||||
companyCode: string; // 회사 코드 추가
|
companyCode: string; // 회사 코드 추가
|
||||||
|
|
||||||
|
// 조건부 연결 관련 필드
|
||||||
|
control?: ConditionControl; // 조건 설정
|
||||||
|
category?: ConnectionCategory; // 연결 종류
|
||||||
|
plan?: ExecutionPlan; // 실행 계획
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue