ERP-node/frontend/lib/api/multiConnection.ts

453 lines
13 KiB
TypeScript
Raw Normal View History

/**
* API
*/
import { apiClient } from "./client";
/**
*
* @param dataType -
* @returns
*/
const mapDataTypeToWebType = (dataType: string | undefined | null): string => {
if (!dataType || typeof dataType !== "string") {
console.warn(`⚠️ 잘못된 데이터 타입: ${dataType}, 기본값 'text' 사용`);
return "text";
}
const lowerType = dataType.toLowerCase();
// 텍스트 타입
if (lowerType.includes("varchar") || lowerType.includes("char") || lowerType.includes("text")) {
return "text";
}
// 숫자 타입
if (lowerType.includes("int") || lowerType.includes("bigint") || lowerType.includes("smallint")) {
return "number";
}
if (
lowerType.includes("decimal") ||
lowerType.includes("numeric") ||
lowerType.includes("float") ||
lowerType.includes("double")
) {
return "decimal";
}
// 날짜/시간 타입
if (lowerType.includes("timestamp") || lowerType.includes("datetime")) {
return "datetime";
}
if (lowerType.includes("date")) {
return "date";
}
if (lowerType.includes("time")) {
return "time";
}
// 불린 타입
if (lowerType.includes("boolean") || lowerType.includes("bit")) {
return "boolean";
}
// 바이너리/파일 타입
if (lowerType.includes("bytea") || lowerType.includes("blob") || lowerType.includes("binary")) {
return "file";
}
// JSON 타입
if (lowerType.includes("json")) {
return "text";
}
// 기본값
console.log(`🔍 알 수 없는 데이터 타입: ${dataType} → text로 매핑`);
return "text";
};
/**
*
*
*/
const inferCodeCategory = (columnName: string): string => {
const lowerName = columnName.toLowerCase();
// 실제 데이터베이스에 존재하는 것으로 확인된 카테고리만 반환
if (lowerName.includes("status")) return "STATUS";
// 다른 카테고리들은 실제 존재 여부를 확인한 후 추가
// if (lowerName.includes("type")) return "TYPE";
// if (lowerName.includes("grade")) return "GRADE";
// if (lowerName.includes("level")) return "LEVEL";
// if (lowerName.includes("priority")) return "PRIORITY";
// if (lowerName.includes("category")) return "CATEGORY";
// if (lowerName.includes("role")) return "ROLE";
// 확인되지 않은 컬럼은 일단 STATUS로 매핑 (임시)
return "STATUS";
};
export interface MultiConnectionTableInfo {
tableName: string;
displayName?: string;
columnCount: number;
connectionId: number;
connectionName: string;
dbType: string;
}
export interface ColumnInfo {
columnName: string;
displayName: string;
dataType: string;
dbType: string;
webType: string;
isNullable: boolean;
isPrimaryKey: boolean;
defaultValue?: string;
maxLength?: number;
description?: string;
}
export interface ConnectionInfo {
id: number;
connection_name: string;
description?: string;
db_type: string;
host: string;
port: number;
database_name: string;
username: string;
is_active: string;
company_code: string;
created_date: Date;
updated_date: Date;
}
export interface ValidationResult {
isValid: boolean;
error?: string;
warnings?: string[];
}
/**
* ( DB )
*/
export const getActiveConnections = async (): Promise<ConnectionInfo[]> => {
const response = await apiClient.get("/external-db-connections/control/active");
return response.data.data || [];
};
/**
*
*/
export const getTablesFromConnection = async (connectionId: number): Promise<MultiConnectionTableInfo[]> => {
const response = await apiClient.get(`/multi-connection/connections/${connectionId}/tables`);
return response.data.data || [];
};
/**
* ( )
*/
export const getBatchTablesWithColumns = async (
connectionId: number,
): Promise<{ tableName: string; displayName?: string; columnCount: number }[]> => {
console.log(`🚀 getBatchTablesWithColumns 호출: connectionId=${connectionId}`);
try {
const response = await apiClient.get(`/multi-connection/connections/${connectionId}/tables/batch`);
console.log("✅ 배치 테이블 정보 조회 성공:", response.data);
const result = response.data.data || [];
console.log(`📊 배치 조회 결과: ${result.length}개 테이블`, result);
return result;
} catch (error) {
console.error("❌ 배치 테이블 정보 조회 실패:", error);
throw error;
}
};
/**
*
*/
export const getColumnsFromConnection = async (connectionId: number, tableName: string): Promise<ColumnInfo[]> => {
console.log(`🔍 getColumnsFromConnection 호출: connectionId=${connectionId}, tableName=${tableName}`);
try {
// 메인 데이터베이스(connectionId = 0)인 경우 기존 API 사용
if (connectionId === 0) {
console.log("📡 메인 DB API 호출:", `/table-management/tables/${tableName}/columns`);
const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
console.log("✅ 메인 DB 응답:", response.data);
const rawResult = response.data.data || [];
// 메인 DB는 페이지네이션 구조로 반환됨: {columns: [], total, page, size, totalPages}
const columns = rawResult.columns || rawResult;
// 메인 DB 컬럼에도 코드 타입 감지 로직 적용
const result = Array.isArray(columns)
? columns.map((col: any) => {
const columnName = col.columnName || "";
// 컬럼명으로 코드 타입 감지
const isCodeColumn =
columnName.toLowerCase().includes("code") ||
columnName.toLowerCase().includes("status") ||
columnName.toLowerCase().includes("type") ||
columnName.toLowerCase().includes("grade") ||
columnName.toLowerCase().includes("level");
return {
...col,
webType: isCodeColumn ? "code" : col.webType || mapDataTypeToWebType(col.dataType),
codeCategory: isCodeColumn ? inferCodeCategory(columnName) : col.codeCategory,
};
})
: columns;
console.log("📊 메인 DB 최종 결과:", {
rawType: typeof rawResult,
rawIsArray: Array.isArray(rawResult),
hasColumns: rawResult && typeof rawResult === "object" && "columns" in rawResult,
finalType: typeof result,
finalIsArray: Array.isArray(result),
length: Array.isArray(result) ? result.length : "N/A",
sample: Array.isArray(result) ? result.slice(0, 1) : result,
});
return result;
}
// 외부 커넥션인 경우 external-db-connections API 사용
console.log("📡 외부 DB API 호출:", `/external-db-connections/${connectionId}/tables/${tableName}/columns`);
const response = await apiClient.get(`/external-db-connections/${connectionId}/tables/${tableName}/columns`);
console.log("✅ 외부 DB 응답:", response.data);
const rawResult = response.data.data || [];
// 외부 DB 컬럼 구조를 메인 DB 형식으로 변환
const result = Array.isArray(rawResult)
? rawResult.map((col: any) => {
const columnName = col.column_name || col.columnName || "";
const dataType = col.data_type || col.dataType || "unknown";
// 컬럼명이 '_code'로 끝나거나 'status', 'type' 등의 이름을 가진 경우 코드 타입으로 간주
const isCodeColumn =
columnName.toLowerCase().includes("code") ||
columnName.toLowerCase().includes("status") ||
columnName.toLowerCase().includes("type") ||
columnName.toLowerCase().includes("grade") ||
columnName.toLowerCase().includes("level");
return {
columnName: columnName,
displayName: col.column_comment || col.displayName || columnName,
dataType: dataType,
dbType: dataType,
webType: isCodeColumn ? "code" : mapDataTypeToWebType(dataType),
isNullable: col.is_nullable === "YES" || col.isNullable === true,
columnDefault: col.column_default || col.columnDefault,
description: col.column_comment || col.description,
// 코드 타입인 경우 카테고리 추론
codeCategory: isCodeColumn ? inferCodeCategory(columnName) : undefined,
};
})
: rawResult;
console.log("📊 외부 DB 최종 결과:", {
rawType: typeof rawResult,
rawIsArray: Array.isArray(rawResult),
finalType: typeof result,
finalIsArray: Array.isArray(result),
length: Array.isArray(result) ? result.length : "N/A",
sample: Array.isArray(result) ? result.slice(0, 1) : result,
sampleOriginal: Array.isArray(rawResult) ? rawResult.slice(0, 1) : rawResult,
});
return result;
} catch (error) {
console.error("❌ 컬럼 정보 조회 실패:", error);
// 개발 환경에서 Mock 데이터 반환
if (process.env.NODE_ENV === "development") {
console.warn("🔄 개발 환경: Mock 컬럼 데이터 사용");
const mockResult = getMockColumnsForTable(tableName);
console.log("📊 Mock 데이터 반환:", {
type: typeof mockResult,
isArray: Array.isArray(mockResult),
length: mockResult.length,
sample: mockResult.slice(0, 1),
});
return mockResult;
}
throw error;
}
};
/**
* Mock (/)
*/
const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
const baseColumns: ColumnInfo[] = [
{
columnName: "id",
displayName: "ID",
dataType: "NUMBER",
webType: "number",
isNullable: false,
isPrimaryKey: true,
columnComment: "고유 식별자",
},
{
columnName: "name",
displayName: "이름",
dataType: "VARCHAR",
webType: "text",
isNullable: false,
isPrimaryKey: false,
columnComment: "이름",
},
{
columnName: "status",
displayName: "상태",
dataType: "VARCHAR",
webType: "code",
isNullable: true,
isPrimaryKey: false,
columnComment: "상태 코드",
codeCategory: "STATUS",
},
{
columnName: "created_date",
displayName: "생성일시",
dataType: "TIMESTAMP",
webType: "datetime",
isNullable: true,
isPrimaryKey: false,
columnComment: "생성일시",
},
{
columnName: "updated_date",
displayName: "수정일시",
dataType: "TIMESTAMP",
webType: "datetime",
isNullable: true,
isPrimaryKey: false,
columnComment: "수정일시",
},
];
// 테이블명에 따라 추가 컬럼 포함
if (tableName.toLowerCase().includes("user")) {
baseColumns.push({
columnName: "email",
displayName: "이메일",
dataType: "VARCHAR",
webType: "email",
isNullable: true,
isPrimaryKey: false,
columnComment: "이메일 주소",
});
}
if (tableName.toLowerCase().includes("product")) {
baseColumns.push({
columnName: "price",
displayName: "가격",
dataType: "DECIMAL",
webType: "decimal",
isNullable: true,
isPrimaryKey: false,
columnComment: "상품 가격",
});
}
return baseColumns;
};
/**
*
*/
export const queryDataFromConnection = async (
connectionId: number,
tableName: string,
conditions?: Record<string, any>,
): Promise<Record<string, any>[]> => {
const response = await apiClient.post(`/multi-connection/connections/${connectionId}/query`, {
tableName,
conditions,
});
return response.data.data || [];
};
/**
*
*/
export const insertDataToConnection = async (
connectionId: number,
tableName: string,
data: Record<string, any>,
): Promise<any> => {
const response = await apiClient.post(`/multi-connection/connections/${connectionId}/insert`, {
tableName,
data,
});
return response.data.data || {};
};
/**
*
*/
export const updateDataInConnection = async (
connectionId: number,
tableName: string,
data: Record<string, any>,
conditions: Record<string, any>,
): Promise<any> => {
const response = await apiClient.put(`/multi-connection/connections/${connectionId}/update`, {
tableName,
data,
conditions,
});
return response.data.data || {};
};
/**
*
*/
export const deleteDataFromConnection = async (
connectionId: number,
tableName: string,
conditions: Record<string, any>,
maxDeleteCount?: number,
): Promise<any> => {
const response = await apiClient.delete(`/multi-connection/connections/${connectionId}/delete`, {
data: {
tableName,
conditions,
maxDeleteCount,
},
});
return response.data.data || {};
};
/**
*
*/
export const validateSelfTableOperation = async (
tableName: string,
operation: "update" | "delete",
conditions: any[],
): Promise<ValidationResult> => {
const response = await apiClient.post("/multi-connection/validate-self-operation", {
tableName,
operation,
conditions,
});
return response.data.data || {};
};