2025-09-05 16:19:31 +09:00
|
|
|
import { apiClient, ApiResponse } from "./client";
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
// 테이블 간 데이터 관계 설정 관련 타입 정의
|
2025-09-05 16:19:31 +09:00
|
|
|
|
2025-09-12 09:49:53 +09:00
|
|
|
// 조건부 연결 관련 타입들
|
|
|
|
|
export interface ConditionControl {
|
|
|
|
|
triggerType: "insert" | "update" | "delete" | "insert_update";
|
2025-09-15 11:17:46 +09:00
|
|
|
conditionTree: ConditionNode | ConditionNode[] | null;
|
2025-09-12 09:49:53 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ConditionNode {
|
2025-09-15 11:17:46 +09:00
|
|
|
id: string; // 고유 ID
|
|
|
|
|
type: "condition" | "group-start" | "group-end";
|
2025-09-12 09:49:53 +09:00
|
|
|
field?: string;
|
2025-09-12 11:33:54 +09:00
|
|
|
operator_type?: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE";
|
2025-09-15 15:12:02 +09:00
|
|
|
value?: string | number | boolean;
|
2025-09-12 09:49:53 +09:00
|
|
|
dataType?: string;
|
2025-09-15 11:17:46 +09:00
|
|
|
logicalOperator?: "AND" | "OR"; // 다음 조건과의 논리 연산자
|
|
|
|
|
groupId?: string; // 그룹 ID (group-start와 group-end가 같은 groupId를 가짐)
|
|
|
|
|
groupLevel?: number; // 중첩 레벨 (0, 1, 2, ...)
|
2025-09-12 09:49:53 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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[];
|
2025-09-15 10:11:22 +09:00
|
|
|
conditions?: Array<{
|
|
|
|
|
field: string;
|
|
|
|
|
operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE";
|
|
|
|
|
value: string;
|
|
|
|
|
logicalOperator?: "AND" | "OR"; // 다음 조건과의 논리 연산자
|
|
|
|
|
}>;
|
|
|
|
|
splitConfig?: {
|
|
|
|
|
sourceField: string;
|
|
|
|
|
delimiter: string;
|
|
|
|
|
targetField: string;
|
|
|
|
|
};
|
2025-09-12 09:49:53 +09:00
|
|
|
description?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface FieldMapping {
|
|
|
|
|
sourceField: string;
|
|
|
|
|
targetField: string;
|
|
|
|
|
transformFunction?: string;
|
|
|
|
|
defaultValue?: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 16:19:31 +09:00
|
|
|
export interface ColumnInfo {
|
|
|
|
|
columnName: string;
|
|
|
|
|
columnLabel?: string;
|
|
|
|
|
displayName?: string;
|
|
|
|
|
dataType?: string;
|
|
|
|
|
dbType?: string;
|
|
|
|
|
webType?: string;
|
|
|
|
|
isNullable?: string;
|
|
|
|
|
columnDefault?: string;
|
|
|
|
|
characterMaximumLength?: number;
|
|
|
|
|
numericPrecision?: number;
|
|
|
|
|
numericScale?: number;
|
|
|
|
|
detailSettings?: string;
|
|
|
|
|
codeCategory?: string;
|
|
|
|
|
referenceTable?: string;
|
|
|
|
|
referenceColumn?: string;
|
|
|
|
|
isVisible?: string;
|
|
|
|
|
displayOrder?: number;
|
|
|
|
|
description?: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
export interface TableDefinition {
|
|
|
|
|
tableName: string;
|
|
|
|
|
displayName?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
columns: ColumnInfo[];
|
2025-09-05 16:19:31 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
export interface TableInfo {
|
|
|
|
|
tableName: string;
|
|
|
|
|
displayName: string;
|
|
|
|
|
description: string;
|
|
|
|
|
columnCount: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TableRelationship {
|
2025-09-09 11:35:05 +09:00
|
|
|
relationship_id?: number;
|
2025-09-09 18:42:01 +09:00
|
|
|
diagram_id?: number; // 새 관계도 생성 시에는 optional
|
2025-09-09 11:35:05 +09:00
|
|
|
relationship_name: string;
|
|
|
|
|
from_table_name: string;
|
|
|
|
|
from_column_name: string;
|
|
|
|
|
to_table_name: string;
|
|
|
|
|
to_column_name: string;
|
2025-09-12 09:49:53 +09:00
|
|
|
connection_type: "simple-key" | "data-save" | "external-call" | "conditional-link";
|
2025-09-08 16:46:53 +09:00
|
|
|
settings?: Record<string, unknown>;
|
2025-09-09 11:35:05 +09:00
|
|
|
company_code: string;
|
|
|
|
|
is_active?: string;
|
2025-09-05 16:19:31 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-08 16:46:53 +09:00
|
|
|
// 데이터 연결 중계 테이블 타입
|
|
|
|
|
export interface DataBridge {
|
|
|
|
|
bridgeId: number;
|
|
|
|
|
relationshipId: number;
|
|
|
|
|
fromTableName: string;
|
|
|
|
|
fromColumnName: string;
|
|
|
|
|
toTableName: string;
|
|
|
|
|
toColumnName: string;
|
|
|
|
|
connectionType: string;
|
|
|
|
|
companyCode: string;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
createdBy?: string;
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
updatedBy?: string;
|
|
|
|
|
isActive: string;
|
|
|
|
|
bridgeData?: Record<string, unknown>;
|
|
|
|
|
relationship?: {
|
|
|
|
|
relationshipName: string;
|
|
|
|
|
relationshipType: string;
|
|
|
|
|
connectionType: string;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 18:18:47 +09:00
|
|
|
// 테이블 데이터 조회 응답 타입
|
|
|
|
|
export interface TableDataResponse {
|
|
|
|
|
data: Record<string, unknown>[];
|
|
|
|
|
pagination: {
|
|
|
|
|
page: number;
|
|
|
|
|
limit: number;
|
|
|
|
|
total: number;
|
|
|
|
|
totalPages: number;
|
|
|
|
|
hasNext: boolean;
|
|
|
|
|
hasPrev: boolean;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 11:35:05 +09:00
|
|
|
// 관계도 정보 인터페이스
|
|
|
|
|
export interface DataFlowDiagram {
|
2025-09-09 18:42:01 +09:00
|
|
|
diagramId: number;
|
2025-09-09 11:35:05 +09:00
|
|
|
diagramName: string;
|
|
|
|
|
connectionType: string;
|
|
|
|
|
relationshipType: string;
|
|
|
|
|
tableCount: number;
|
|
|
|
|
relationshipCount: number;
|
|
|
|
|
tables: string[];
|
2025-09-10 15:40:49 +09:00
|
|
|
companyCode: string; // 회사 코드 추가
|
2025-09-12 09:49:53 +09:00
|
|
|
|
|
|
|
|
// 조건부 연결 관련 필드
|
|
|
|
|
control?: ConditionControl; // 조건 설정
|
|
|
|
|
category?: ConnectionCategory; // 연결 종류
|
|
|
|
|
plan?: ExecutionPlan; // 실행 계획
|
2025-09-09 11:35:05 +09:00
|
|
|
createdAt: Date;
|
|
|
|
|
createdBy: string;
|
|
|
|
|
updatedAt: Date;
|
|
|
|
|
updatedBy: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 관계도 목록 응답 인터페이스
|
|
|
|
|
export interface DataFlowDiagramsResponse {
|
|
|
|
|
diagrams: DataFlowDiagram[];
|
|
|
|
|
total: number;
|
|
|
|
|
page: number;
|
|
|
|
|
size: number;
|
|
|
|
|
totalPages: number;
|
|
|
|
|
hasNext: boolean;
|
|
|
|
|
hasPrev: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 17:48:55 +09:00
|
|
|
// 노드 위치 정보 타입
|
|
|
|
|
export interface NodePosition {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface NodePositions {
|
|
|
|
|
[tableName: string]: NodePosition;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 15:30:14 +09:00
|
|
|
// 새로운 JSON 기반 타입들
|
|
|
|
|
export interface JsonDataFlowDiagram {
|
|
|
|
|
diagram_id: number;
|
|
|
|
|
diagram_name: string;
|
|
|
|
|
relationships: {
|
|
|
|
|
relationships: JsonRelationship[];
|
|
|
|
|
tables: string[];
|
|
|
|
|
};
|
2025-09-10 17:48:55 +09:00
|
|
|
node_positions?: NodePositions;
|
2025-09-16 12:37:57 +09:00
|
|
|
category?: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
category: string;
|
|
|
|
|
}>;
|
|
|
|
|
control?: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
triggerType: "insert" | "update" | "delete";
|
|
|
|
|
conditions: Array<{
|
|
|
|
|
id?: string;
|
|
|
|
|
type?: string;
|
|
|
|
|
field?: string;
|
|
|
|
|
operator?: string;
|
|
|
|
|
value?: unknown;
|
|
|
|
|
logicalOperator?: string;
|
|
|
|
|
groupId?: string;
|
|
|
|
|
groupLevel?: number;
|
|
|
|
|
}>;
|
|
|
|
|
}>;
|
|
|
|
|
plan?: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
sourceTable: string;
|
|
|
|
|
actions: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
actionType: "insert" | "update" | "delete" | "upsert";
|
|
|
|
|
conditions?: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
type: string;
|
|
|
|
|
field?: string;
|
|
|
|
|
operator_type?: string;
|
|
|
|
|
value?: unknown;
|
|
|
|
|
logicalOperator?: string;
|
|
|
|
|
groupId?: string;
|
|
|
|
|
groupLevel?: number;
|
|
|
|
|
}>;
|
|
|
|
|
fieldMappings: Array<{
|
|
|
|
|
sourceTable?: string;
|
|
|
|
|
sourceField: string;
|
|
|
|
|
targetTable?: string;
|
|
|
|
|
targetField: string;
|
|
|
|
|
defaultValue?: string;
|
|
|
|
|
transformFunction?: string;
|
|
|
|
|
}>;
|
|
|
|
|
splitConfig?: {
|
|
|
|
|
sourceField: string;
|
|
|
|
|
delimiter: string;
|
|
|
|
|
targetField: string;
|
|
|
|
|
};
|
|
|
|
|
}>;
|
|
|
|
|
}>;
|
2025-09-10 15:30:14 +09:00
|
|
|
company_code: string;
|
|
|
|
|
created_at?: string;
|
|
|
|
|
updated_at?: string;
|
|
|
|
|
created_by?: string;
|
|
|
|
|
updated_by?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface JsonRelationship {
|
|
|
|
|
id: string;
|
2025-09-10 18:30:22 +09:00
|
|
|
relationshipName: string; // 연결 이름 추가
|
2025-09-10 15:30:14 +09:00
|
|
|
fromTable: string;
|
|
|
|
|
toTable: string;
|
|
|
|
|
fromColumns: string[];
|
|
|
|
|
toColumns: string[];
|
2025-09-15 20:07:28 +09:00
|
|
|
connectionType: "simple-key" | "data-save" | "external-call";
|
2025-09-15 15:12:02 +09:00
|
|
|
settings?: Record<string, unknown>;
|
2025-09-10 15:30:14 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateDiagramRequest {
|
|
|
|
|
diagram_name: string;
|
|
|
|
|
relationships: {
|
|
|
|
|
relationships: JsonRelationship[];
|
|
|
|
|
tables: string[];
|
|
|
|
|
};
|
2025-09-10 17:48:55 +09:00
|
|
|
node_positions?: NodePositions;
|
2025-09-15 20:07:28 +09:00
|
|
|
// 🔥 수정: 각 관계별 정보를 배열로 저장
|
|
|
|
|
category?: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
category: "simple-key" | "data-save" | "external-call";
|
|
|
|
|
}>;
|
|
|
|
|
// 🔥 전체 실행 조건 - relationships의 id와 동일한 id 사용
|
|
|
|
|
control?: Array<{
|
|
|
|
|
id: string; // relationships의 id와 동일
|
|
|
|
|
triggerType: "insert" | "update" | "delete";
|
2025-09-16 12:37:57 +09:00
|
|
|
conditions: Array<{
|
|
|
|
|
id?: string;
|
|
|
|
|
type?: string;
|
|
|
|
|
field?: string;
|
|
|
|
|
operator?: string;
|
|
|
|
|
value?: unknown;
|
|
|
|
|
dataType?: string;
|
2025-09-15 20:07:28 +09:00
|
|
|
logicalOperator?: "AND" | "OR";
|
2025-09-16 12:37:57 +09:00
|
|
|
groupId?: string;
|
|
|
|
|
groupLevel?: number;
|
2025-09-15 20:07:28 +09:00
|
|
|
}>;
|
|
|
|
|
}>;
|
|
|
|
|
// 🔥 저장 액션 - relationships의 id와 동일한 id 사용
|
|
|
|
|
plan?: Array<{
|
|
|
|
|
id: string; // relationships의 id와 동일
|
|
|
|
|
sourceTable: string;
|
|
|
|
|
actions: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
actionType: "insert" | "update" | "delete" | "upsert";
|
|
|
|
|
fieldMappings: Array<{
|
|
|
|
|
sourceTable?: string;
|
|
|
|
|
sourceField: string;
|
|
|
|
|
targetTable?: string;
|
|
|
|
|
targetField: string;
|
|
|
|
|
defaultValue?: string;
|
|
|
|
|
}>;
|
2025-09-16 12:37:57 +09:00
|
|
|
splitConfig?: {
|
|
|
|
|
sourceField: string;
|
|
|
|
|
delimiter: string;
|
|
|
|
|
targetField: string;
|
|
|
|
|
};
|
2025-09-15 20:07:28 +09:00
|
|
|
conditions?: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
type: string;
|
2025-09-16 12:37:57 +09:00
|
|
|
field?: string;
|
|
|
|
|
operator_type?: string;
|
|
|
|
|
value?: unknown;
|
|
|
|
|
dataType?: string;
|
2025-09-15 20:07:28 +09:00
|
|
|
logicalOperator?: string;
|
2025-09-16 12:37:57 +09:00
|
|
|
groupId?: string;
|
|
|
|
|
groupLevel?: number;
|
2025-09-15 20:07:28 +09:00
|
|
|
}>;
|
|
|
|
|
}>;
|
|
|
|
|
}>;
|
2025-09-10 15:30:14 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface JsonDataFlowDiagramsResponse {
|
|
|
|
|
diagrams: JsonDataFlowDiagram[];
|
|
|
|
|
pagination: {
|
|
|
|
|
page: number;
|
|
|
|
|
size: number;
|
|
|
|
|
total: number;
|
|
|
|
|
totalPages: number;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:00:18 +09:00
|
|
|
// 테이블 간 데이터 관계 설정 API 클래스
|
2025-09-05 16:19:31 +09:00
|
|
|
export class DataFlowAPI {
|
|
|
|
|
/**
|
2025-09-05 18:00:18 +09:00
|
|
|
* 테이블 목록 조회
|
2025-09-05 16:19:31 +09:00
|
|
|
*/
|
2025-09-05 18:00:18 +09:00
|
|
|
static async getTables(): Promise<TableInfo[]> {
|
2025-09-05 16:19:31 +09:00
|
|
|
try {
|
2025-09-05 18:00:18 +09:00
|
|
|
const response = await apiClient.get<ApiResponse<TableInfo[]>>("/table-management/tables");
|
2025-09-05 16:19:31 +09:00
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
2025-09-05 18:00:18 +09:00
|
|
|
throw new Error(response.data.message || "테이블 목록 조회에 실패했습니다.");
|
2025-09-05 16:19:31 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data || [];
|
|
|
|
|
} catch (error) {
|
2025-09-05 18:00:18 +09:00
|
|
|
console.error("테이블 목록 조회 오류:", error);
|
2025-09-05 16:19:31 +09:00
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 테이블 컬럼 정보 조회
|
|
|
|
|
*/
|
|
|
|
|
static async getTableColumns(tableName: string): Promise<ColumnInfo[]> {
|
|
|
|
|
try {
|
2025-09-10 11:27:05 +09:00
|
|
|
const response = await apiClient.get<
|
|
|
|
|
ApiResponse<{
|
|
|
|
|
columns: ColumnInfo[];
|
|
|
|
|
page: number;
|
|
|
|
|
total: number;
|
|
|
|
|
totalPages: number;
|
|
|
|
|
}>
|
|
|
|
|
>(`/table-management/tables/${tableName}/columns`);
|
2025-09-05 16:19:31 +09:00
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "컬럼 정보 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 11:27:05 +09:00
|
|
|
// 페이지네이션된 응답에서 columns 배열만 추출
|
|
|
|
|
return response.data.data?.columns || [];
|
2025-09-05 16:19:31 +09:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error("컬럼 정보 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-05 18:00:18 +09:00
|
|
|
* 테이블 정보와 컬럼 정보를 함께 조회
|
2025-09-05 16:19:31 +09:00
|
|
|
*/
|
2025-09-05 18:00:18 +09:00
|
|
|
static async getTableWithColumns(tableName: string): Promise<TableDefinition | null> {
|
2025-09-05 16:19:31 +09:00
|
|
|
try {
|
2025-09-05 18:00:18 +09:00
|
|
|
const columns = await this.getTableColumns(tableName);
|
2025-09-05 16:19:31 +09:00
|
|
|
|
|
|
|
|
return {
|
2025-09-05 18:00:18 +09:00
|
|
|
tableName,
|
|
|
|
|
displayName: tableName,
|
|
|
|
|
description: `${tableName} 테이블`,
|
|
|
|
|
columns,
|
2025-09-05 16:19:31 +09:00
|
|
|
};
|
|
|
|
|
} catch (error) {
|
2025-09-05 18:00:18 +09:00
|
|
|
console.error("테이블 및 컬럼 정보 조회 오류:", error);
|
2025-09-05 16:19:31 +09:00
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-05 18:00:18 +09:00
|
|
|
* 테이블 관계 생성
|
2025-09-05 16:19:31 +09:00
|
|
|
*/
|
2025-09-09 18:42:01 +09:00
|
|
|
static async createRelationship(
|
2025-09-15 15:12:02 +09:00
|
|
|
relationship: Omit<TableRelationship, "relationship_id">, // 백엔드 API 형식 (camelCase)
|
2025-09-09 18:42:01 +09:00
|
|
|
): Promise<TableRelationship> {
|
2025-09-05 16:19:31 +09:00
|
|
|
try {
|
2025-09-08 16:46:53 +09:00
|
|
|
const response = await apiClient.post<ApiResponse<TableRelationship>>(
|
|
|
|
|
"/dataflow/table-relationships",
|
|
|
|
|
relationship,
|
|
|
|
|
);
|
2025-09-05 16:19:31 +09:00
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계 생성에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data!;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계 생성 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-05 18:00:18 +09:00
|
|
|
* 회사별 테이블 관계 목록 조회
|
2025-09-05 16:19:31 +09:00
|
|
|
*/
|
2025-09-05 18:00:18 +09:00
|
|
|
static async getRelationshipsByCompany(companyCode: string): Promise<TableRelationship[]> {
|
2025-09-05 16:19:31 +09:00
|
|
|
try {
|
2025-09-08 16:46:53 +09:00
|
|
|
const response = await apiClient.get<ApiResponse<TableRelationship[]>>("/dataflow/table-relationships", {
|
2025-09-05 16:19:31 +09:00
|
|
|
params: { companyCode },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계 목록 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data || [];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계 목록 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-05 18:00:18 +09:00
|
|
|
* 테이블 관계 수정
|
2025-09-05 16:19:31 +09:00
|
|
|
*/
|
|
|
|
|
static async updateRelationship(
|
|
|
|
|
relationshipId: number,
|
2025-09-05 18:00:18 +09:00
|
|
|
relationship: Partial<TableRelationship>,
|
|
|
|
|
): Promise<TableRelationship> {
|
2025-09-05 16:19:31 +09:00
|
|
|
try {
|
2025-09-05 18:00:18 +09:00
|
|
|
const response = await apiClient.put<ApiResponse<TableRelationship>>(
|
2025-09-08 16:46:53 +09:00
|
|
|
`/dataflow/table-relationships/${relationshipId}`,
|
2025-09-05 16:19:31 +09:00
|
|
|
relationship,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계 수정에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data!;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계 수정 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-05 18:00:18 +09:00
|
|
|
* 테이블 관계 삭제
|
2025-09-05 16:19:31 +09:00
|
|
|
*/
|
|
|
|
|
static async deleteRelationship(relationshipId: number): Promise<void> {
|
|
|
|
|
try {
|
2025-09-08 16:46:53 +09:00
|
|
|
const response = await apiClient.delete<ApiResponse<null>>(`/dataflow/table-relationships/${relationshipId}`);
|
2025-09-05 16:19:31 +09:00
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계 삭제에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계 삭제 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-08 16:46:53 +09:00
|
|
|
|
|
|
|
|
// ==================== 데이터 연결 관리 API ====================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 데이터 연결 생성
|
|
|
|
|
*/
|
|
|
|
|
static async createDataLink(linkData: {
|
|
|
|
|
relationshipId: number;
|
|
|
|
|
fromTableName: string;
|
|
|
|
|
fromColumnName: string;
|
|
|
|
|
toTableName: string;
|
|
|
|
|
toColumnName: string;
|
|
|
|
|
connectionType: string;
|
|
|
|
|
bridgeData?: Record<string, unknown>;
|
|
|
|
|
}): Promise<DataBridge> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await apiClient.post<ApiResponse<DataBridge>>("/dataflow/data-links", linkData);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "데이터 연결 생성에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as DataBridge;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("데이터 연결 생성 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 관계별 연결된 데이터 조회
|
|
|
|
|
*/
|
|
|
|
|
static async getLinkedDataByRelationship(relationshipId: number): Promise<DataBridge[]> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await apiClient.get<ApiResponse<DataBridge[]>>(
|
|
|
|
|
`/dataflow/data-links/relationship/${relationshipId}`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "연결된 데이터 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as DataBridge[];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("연결된 데이터 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 데이터 연결 삭제
|
|
|
|
|
*/
|
|
|
|
|
static async deleteDataLink(bridgeId: number): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await apiClient.delete<ApiResponse<null>>(`/dataflow/data-links/${bridgeId}`);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "데이터 연결 삭제에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("데이터 연결 삭제 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-08 18:18:47 +09:00
|
|
|
|
|
|
|
|
// ==================== 테이블 데이터 조회 API ====================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 테이블 실제 데이터 조회 (페이징)
|
|
|
|
|
*/
|
|
|
|
|
static async getTableData(
|
|
|
|
|
tableName: string,
|
|
|
|
|
page: number = 1,
|
|
|
|
|
limit: number = 10,
|
|
|
|
|
search: string = "",
|
|
|
|
|
searchColumn: string = "",
|
|
|
|
|
): Promise<TableDataResponse> {
|
|
|
|
|
try {
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
page: page.toString(),
|
|
|
|
|
limit: limit.toString(),
|
|
|
|
|
...(search && { search }),
|
|
|
|
|
...(searchColumn && { searchColumn }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.get<ApiResponse<TableDataResponse>>(
|
|
|
|
|
`/dataflow/table-data/${tableName}?${params}`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "테이블 데이터 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as TableDataResponse;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("테이블 데이터 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-09 11:35:05 +09:00
|
|
|
|
|
|
|
|
// ==================== 관계도 관리 ====================
|
|
|
|
|
|
|
|
|
|
// 관계도 목록 조회
|
|
|
|
|
static async getDataFlowDiagrams(
|
|
|
|
|
page: number = 1,
|
|
|
|
|
size: number = 20,
|
|
|
|
|
searchTerm: string = "",
|
|
|
|
|
): Promise<DataFlowDiagramsResponse> {
|
|
|
|
|
try {
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
page: page.toString(),
|
|
|
|
|
size: size.toString(),
|
|
|
|
|
...(searchTerm && { searchTerm }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.get<ApiResponse<DataFlowDiagramsResponse>>(`/dataflow/diagrams?${params}`);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 목록 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as DataFlowDiagramsResponse;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계도 목록 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 13:48:57 +09:00
|
|
|
// 특정 관계도의 모든 관계 조회 (관계도명으로)
|
2025-09-09 11:35:05 +09:00
|
|
|
static async getDiagramRelationships(diagramName: string): Promise<TableRelationship[]> {
|
|
|
|
|
try {
|
|
|
|
|
const encodedDiagramName = encodeURIComponent(diagramName);
|
|
|
|
|
const response = await apiClient.get<ApiResponse<TableRelationship[]>>(
|
|
|
|
|
`/dataflow/diagrams/${encodedDiagramName}/relationships`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 관계 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as TableRelationship[];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계도 관계 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-09 13:10:03 +09:00
|
|
|
|
|
|
|
|
// 관계도 복사
|
|
|
|
|
static async copyDiagram(diagramName: string): Promise<string> {
|
|
|
|
|
try {
|
|
|
|
|
const encodedDiagramName = encodeURIComponent(diagramName);
|
|
|
|
|
const response = await apiClient.post<ApiResponse<{ newDiagramName: string }>>(
|
|
|
|
|
`/dataflow/diagrams/${encodedDiagramName}/copy`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 복사에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data?.newDiagramName || "";
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계도 복사 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 관계도 삭제
|
|
|
|
|
static async deleteDiagram(diagramName: string): Promise<number> {
|
|
|
|
|
try {
|
|
|
|
|
const encodedDiagramName = encodeURIComponent(diagramName);
|
|
|
|
|
const response = await apiClient.delete<ApiResponse<{ deletedCount: number }>>(
|
|
|
|
|
`/dataflow/diagrams/${encodedDiagramName}`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 삭제에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data?.deletedCount || 0;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계도 삭제 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-09 13:48:57 +09:00
|
|
|
|
2025-09-10 15:30:14 +09:00
|
|
|
// 특정 관계도의 모든 관계 조회 (diagram_id로) - JSON 기반 시스템
|
|
|
|
|
static async getDiagramRelationshipsByDiagramId(
|
|
|
|
|
diagramId: number,
|
|
|
|
|
companyCode: string = "*",
|
|
|
|
|
): Promise<TableRelationship[]> {
|
2025-09-09 13:48:57 +09:00
|
|
|
try {
|
2025-09-10 15:30:14 +09:00
|
|
|
// 새로운 JSON 기반 시스템에서 관계도 조회
|
|
|
|
|
const jsonDiagram = await this.getJsonDataFlowDiagramById(diagramId, companyCode);
|
|
|
|
|
|
|
|
|
|
if (!jsonDiagram || !jsonDiagram.relationships) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JSON 관계를 TableRelationship 형식으로 변환
|
|
|
|
|
const relationshipsData = jsonDiagram.relationships as { relationships: JsonRelationship[]; tables: string[] };
|
|
|
|
|
const relationships: TableRelationship[] = relationshipsData.relationships.map((rel: JsonRelationship) => ({
|
|
|
|
|
relationship_id: 0, // JSON 기반에서는 개별 relationship_id가 없음
|
2025-09-10 18:30:22 +09:00
|
|
|
relationship_name: rel.relationshipName || rel.id || "관계", // relationshipName 우선 사용
|
2025-09-10 15:30:14 +09:00
|
|
|
from_table_name: rel.fromTable,
|
|
|
|
|
to_table_name: rel.toTable,
|
|
|
|
|
from_column_name: rel.fromColumns.join(","),
|
|
|
|
|
to_column_name: rel.toColumns.join(","),
|
2025-09-16 12:37:57 +09:00
|
|
|
connection_type: rel.connectionType || "simple-key", // 각 관계의 connectionType 사용
|
2025-09-10 15:30:14 +09:00
|
|
|
company_code: companyCode, // 실제 사용자 회사 코드 사용
|
|
|
|
|
settings: rel.settings || {},
|
|
|
|
|
created_at: jsonDiagram.created_at,
|
|
|
|
|
updated_at: jsonDiagram.updated_at,
|
|
|
|
|
created_by: jsonDiagram.created_by,
|
|
|
|
|
updated_by: jsonDiagram.updated_by,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
return relationships;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("관계도 관계 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==================== 새로운 JSON 기반 관계도 API ====================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON 기반 관계도 목록 조회
|
|
|
|
|
*/
|
|
|
|
|
static async getJsonDataFlowDiagrams(
|
|
|
|
|
page: number = 1,
|
|
|
|
|
size: number = 20,
|
|
|
|
|
searchTerm: string = "",
|
|
|
|
|
companyCode: string = "*",
|
|
|
|
|
): Promise<JsonDataFlowDiagramsResponse> {
|
|
|
|
|
try {
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
page: page.toString(),
|
|
|
|
|
size: size.toString(),
|
|
|
|
|
companyCode: companyCode,
|
|
|
|
|
...(searchTerm && { searchTerm }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.get<ApiResponse<JsonDataFlowDiagramsResponse>>(`/dataflow-diagrams?${params}`);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 목록 조회에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as JsonDataFlowDiagramsResponse;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("JSON 관계도 목록 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON 기반 특정 관계도 조회
|
|
|
|
|
*/
|
|
|
|
|
static async getJsonDataFlowDiagramById(diagramId: number, companyCode: string = "*"): Promise<JsonDataFlowDiagram> {
|
|
|
|
|
try {
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
companyCode: companyCode,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.get<ApiResponse<JsonDataFlowDiagram>>(
|
|
|
|
|
`/dataflow-diagrams/${diagramId}?${params}`,
|
2025-09-09 13:48:57 +09:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
2025-09-10 15:30:14 +09:00
|
|
|
throw new Error(response.data.message || "관계도 조회에 실패했습니다.");
|
2025-09-09 13:48:57 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-10 15:30:14 +09:00
|
|
|
return response.data.data as JsonDataFlowDiagram;
|
2025-09-09 13:48:57 +09:00
|
|
|
} catch (error) {
|
2025-09-10 15:30:14 +09:00
|
|
|
console.error("JSON 관계도 조회 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON 기반 관계도 생성
|
|
|
|
|
*/
|
|
|
|
|
static async createJsonDataFlowDiagram(
|
|
|
|
|
request: CreateDiagramRequest,
|
|
|
|
|
companyCode: string = "*",
|
|
|
|
|
userId: string = "SYSTEM",
|
|
|
|
|
): Promise<JsonDataFlowDiagram> {
|
|
|
|
|
try {
|
|
|
|
|
const requestWithUserInfo = {
|
|
|
|
|
...request,
|
|
|
|
|
company_code: companyCode,
|
|
|
|
|
created_by: userId,
|
|
|
|
|
updated_by: userId,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.post<ApiResponse<JsonDataFlowDiagram>>(
|
|
|
|
|
"/dataflow-diagrams",
|
|
|
|
|
requestWithUserInfo,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 생성에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as JsonDataFlowDiagram;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("JSON 관계도 생성 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON 기반 관계도 수정
|
|
|
|
|
*/
|
|
|
|
|
static async updateJsonDataFlowDiagram(
|
|
|
|
|
diagramId: number,
|
|
|
|
|
request: Partial<CreateDiagramRequest>,
|
|
|
|
|
companyCode: string = "*",
|
|
|
|
|
userId: string = "SYSTEM",
|
|
|
|
|
): Promise<JsonDataFlowDiagram> {
|
|
|
|
|
try {
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
companyCode: companyCode,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const requestWithUserInfo = {
|
|
|
|
|
...request,
|
|
|
|
|
updated_by: userId,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.put<ApiResponse<JsonDataFlowDiagram>>(
|
|
|
|
|
`/dataflow-diagrams/${diagramId}?${params}`,
|
|
|
|
|
requestWithUserInfo,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 수정에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as JsonDataFlowDiagram;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("JSON 관계도 수정 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON 기반 관계도 삭제
|
|
|
|
|
*/
|
|
|
|
|
static async deleteJsonDataFlowDiagram(diagramId: number, companyCode: string = "*"): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
companyCode: companyCode,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.delete<ApiResponse<void>>(`/dataflow-diagrams/${diagramId}?${params}`);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 삭제에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("JSON 관계도 삭제 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON 기반 관계도 복제
|
|
|
|
|
*/
|
|
|
|
|
static async copyJsonDataFlowDiagram(
|
|
|
|
|
diagramId: number,
|
|
|
|
|
companyCode: string = "*",
|
|
|
|
|
newName?: string,
|
|
|
|
|
userId: string = "SYSTEM",
|
|
|
|
|
): Promise<JsonDataFlowDiagram> {
|
|
|
|
|
try {
|
|
|
|
|
const requestData = {
|
|
|
|
|
companyCode: companyCode,
|
|
|
|
|
userId: userId,
|
|
|
|
|
...(newName && { new_name: newName }),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await apiClient.post<ApiResponse<JsonDataFlowDiagram>>(
|
|
|
|
|
`/dataflow-diagrams/${diagramId}/copy`,
|
|
|
|
|
requestData,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.data.success) {
|
|
|
|
|
throw new Error(response.data.message || "관계도 복제에 실패했습니다.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.data.data as JsonDataFlowDiagram;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("JSON 관계도 복제 오류:", error);
|
2025-09-09 13:48:57 +09:00
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-05 16:19:31 +09:00
|
|
|
}
|