314 lines
9.9 KiB
TypeScript
314 lines
9.9 KiB
TypeScript
|
|
import { Client } from "pg";
|
||
|
|
import { logger } from "../utils/logger";
|
||
|
|
import {
|
||
|
|
TableInfo,
|
||
|
|
ColumnTypeInfo,
|
||
|
|
ColumnSettings,
|
||
|
|
TableLabels,
|
||
|
|
ColumnLabels,
|
||
|
|
} from "../types/tableManagement";
|
||
|
|
|
||
|
|
export class TableManagementService {
|
||
|
|
private client: Client;
|
||
|
|
|
||
|
|
constructor(client: Client) {
|
||
|
|
this.client = client;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블 목록 조회 (PostgreSQL information_schema 활용)
|
||
|
|
*/
|
||
|
|
async getTableList(): Promise<TableInfo[]> {
|
||
|
|
try {
|
||
|
|
logger.info("테이블 목록 조회 시작");
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
SELECT
|
||
|
|
t.table_name as "tableName",
|
||
|
|
COALESCE(tl.table_label, t.table_name) as "displayName",
|
||
|
|
COALESCE(tl.description, '') as "description",
|
||
|
|
(SELECT COUNT(*) FROM information_schema.columns
|
||
|
|
WHERE table_name = t.table_name AND table_schema = 'public') as "columnCount"
|
||
|
|
FROM information_schema.tables t
|
||
|
|
LEFT JOIN table_labels tl ON t.table_name = tl.table_name
|
||
|
|
WHERE t.table_schema = 'public'
|
||
|
|
AND t.table_type = 'BASE TABLE'
|
||
|
|
AND t.table_name NOT LIKE 'pg_%'
|
||
|
|
AND t.table_name NOT LIKE 'sql_%'
|
||
|
|
ORDER BY t.table_name
|
||
|
|
`;
|
||
|
|
|
||
|
|
const result = await this.client.query(query);
|
||
|
|
logger.info(`테이블 목록 조회 완료: ${result.rows.length}개`);
|
||
|
|
|
||
|
|
return result.rows;
|
||
|
|
} catch (error) {
|
||
|
|
logger.error("테이블 목록 조회 중 오류 발생:", error);
|
||
|
|
throw new Error(
|
||
|
|
`테이블 목록 조회 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블 컬럼 정보 조회
|
||
|
|
*/
|
||
|
|
async getColumnList(tableName: string): Promise<ColumnTypeInfo[]> {
|
||
|
|
try {
|
||
|
|
logger.info(`컬럼 정보 조회 시작: ${tableName}`);
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
SELECT
|
||
|
|
c.column_name as "columnName",
|
||
|
|
COALESCE(cl.column_label, c.column_name) as "displayName",
|
||
|
|
c.data_type as "dbType",
|
||
|
|
COALESCE(cl.web_type, 'text') as "webType",
|
||
|
|
COALESCE(cl.detail_settings, '') as "detailSettings",
|
||
|
|
COALESCE(cl.description, '') as "description",
|
||
|
|
c.is_nullable as "isNullable",
|
||
|
|
c.column_default as "defaultValue",
|
||
|
|
c.character_maximum_length as "maxLength",
|
||
|
|
c.numeric_precision as "numericPrecision",
|
||
|
|
c.numeric_scale as "numericScale",
|
||
|
|
cl.code_category as "codeCategory",
|
||
|
|
cl.code_value as "codeValue",
|
||
|
|
cl.reference_table as "referenceTable",
|
||
|
|
cl.reference_column as "referenceColumn",
|
||
|
|
cl.display_order as "displayOrder",
|
||
|
|
cl.is_visible as "isVisible"
|
||
|
|
FROM information_schema.columns c
|
||
|
|
LEFT JOIN column_labels cl ON c.table_name = cl.table_name AND c.column_name = cl.column_name
|
||
|
|
WHERE c.table_name = $1
|
||
|
|
ORDER BY c.ordinal_position
|
||
|
|
`;
|
||
|
|
|
||
|
|
const result = await this.client.query(query, [tableName]);
|
||
|
|
logger.info(`컬럼 정보 조회 완료: ${tableName}, ${result.rows.length}개`);
|
||
|
|
|
||
|
|
return result.rows;
|
||
|
|
} catch (error) {
|
||
|
|
logger.error(`컬럼 정보 조회 중 오류 발생: ${tableName}`, error);
|
||
|
|
throw new Error(
|
||
|
|
`컬럼 정보 조회 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블이 table_labels에 없으면 자동 추가
|
||
|
|
*/
|
||
|
|
async insertTableIfNotExists(tableName: string): Promise<void> {
|
||
|
|
try {
|
||
|
|
logger.info(`테이블 라벨 자동 추가 시작: ${tableName}`);
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
INSERT INTO table_labels (table_name, table_label, description)
|
||
|
|
VALUES ($1, $1, '')
|
||
|
|
ON CONFLICT (table_name) DO NOTHING
|
||
|
|
`;
|
||
|
|
|
||
|
|
await this.client.query(query, [tableName]);
|
||
|
|
logger.info(`테이블 라벨 자동 추가 완료: ${tableName}`);
|
||
|
|
} catch (error) {
|
||
|
|
logger.error(`테이블 라벨 자동 추가 중 오류 발생: ${tableName}`, error);
|
||
|
|
throw new Error(
|
||
|
|
`테이블 라벨 자동 추가 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 컬럼 설정 업데이트 (UPSERT 방식)
|
||
|
|
*/
|
||
|
|
async updateColumnSettings(
|
||
|
|
tableName: string,
|
||
|
|
columnName: string,
|
||
|
|
settings: ColumnSettings
|
||
|
|
): Promise<void> {
|
||
|
|
try {
|
||
|
|
logger.info(`컬럼 설정 업데이트 시작: ${tableName}.${columnName}`);
|
||
|
|
|
||
|
|
// 테이블이 table_labels에 없으면 자동 추가
|
||
|
|
await this.insertTableIfNotExists(tableName);
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
INSERT INTO column_labels (
|
||
|
|
table_name, column_name, column_label, web_type,
|
||
|
|
detail_settings, code_category, code_value,
|
||
|
|
reference_table, reference_column, display_order, is_visible
|
||
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||
|
|
ON CONFLICT (table_name, column_name) DO UPDATE SET
|
||
|
|
column_label = EXCLUDED.column_label,
|
||
|
|
web_type = EXCLUDED.web_type,
|
||
|
|
detail_settings = EXCLUDED.detail_settings,
|
||
|
|
code_category = EXCLUDED.code_category,
|
||
|
|
code_value = EXCLUDED.code_value,
|
||
|
|
reference_table = EXCLUDED.reference_table,
|
||
|
|
reference_column = EXCLUDED.reference_column,
|
||
|
|
display_order = EXCLUDED.display_order,
|
||
|
|
is_visible = EXCLUDED.is_visible,
|
||
|
|
updated_date = now()
|
||
|
|
`;
|
||
|
|
|
||
|
|
await this.client.query(query, [
|
||
|
|
tableName,
|
||
|
|
columnName,
|
||
|
|
settings.columnLabel,
|
||
|
|
settings.webType,
|
||
|
|
settings.detailSettings,
|
||
|
|
settings.codeCategory,
|
||
|
|
settings.codeValue,
|
||
|
|
settings.referenceTable,
|
||
|
|
settings.referenceColumn,
|
||
|
|
settings.displayOrder || 0,
|
||
|
|
settings.isVisible !== undefined ? settings.isVisible : true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
logger.info(`컬럼 설정 업데이트 완료: ${tableName}.${columnName}`);
|
||
|
|
} catch (error) {
|
||
|
|
logger.error(
|
||
|
|
`컬럼 설정 업데이트 중 오류 발생: ${tableName}.${columnName}`,
|
||
|
|
error
|
||
|
|
);
|
||
|
|
throw new Error(
|
||
|
|
`컬럼 설정 업데이트 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 전체 컬럼 설정 일괄 업데이트
|
||
|
|
*/
|
||
|
|
async updateAllColumnSettings(
|
||
|
|
tableName: string,
|
||
|
|
columnSettings: ColumnSettings[]
|
||
|
|
): Promise<void> {
|
||
|
|
try {
|
||
|
|
logger.info(
|
||
|
|
`전체 컬럼 설정 일괄 업데이트 시작: ${tableName}, ${columnSettings.length}개`
|
||
|
|
);
|
||
|
|
|
||
|
|
// 트랜잭션 시작
|
||
|
|
await this.client.query("BEGIN");
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 테이블이 table_labels에 없으면 자동 추가
|
||
|
|
await this.insertTableIfNotExists(tableName);
|
||
|
|
|
||
|
|
// 각 컬럼 설정을 순차적으로 업데이트
|
||
|
|
for (const columnSetting of columnSettings) {
|
||
|
|
const columnName =
|
||
|
|
columnSetting.columnLabel || columnSetting.columnName;
|
||
|
|
if (columnName) {
|
||
|
|
await this.updateColumnSettings(
|
||
|
|
tableName,
|
||
|
|
columnName,
|
||
|
|
columnSetting
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 트랜잭션 커밋
|
||
|
|
await this.client.query("COMMIT");
|
||
|
|
logger.info(`전체 컬럼 설정 일괄 업데이트 완료: ${tableName}`);
|
||
|
|
} catch (error) {
|
||
|
|
// 트랜잭션 롤백
|
||
|
|
await this.client.query("ROLLBACK");
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
logger.error(
|
||
|
|
`전체 컬럼 설정 일괄 업데이트 중 오류 발생: ${tableName}`,
|
||
|
|
error
|
||
|
|
);
|
||
|
|
throw new Error(
|
||
|
|
`전체 컬럼 설정 일괄 업데이트 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테이블 라벨 정보 조회
|
||
|
|
*/
|
||
|
|
async getTableLabels(tableName: string): Promise<TableLabels | null> {
|
||
|
|
try {
|
||
|
|
logger.info(`테이블 라벨 정보 조회 시작: ${tableName}`);
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
SELECT
|
||
|
|
table_name as "tableName",
|
||
|
|
table_label as "tableLabel",
|
||
|
|
description,
|
||
|
|
created_date as "createdDate",
|
||
|
|
updated_date as "updatedDate"
|
||
|
|
FROM table_labels
|
||
|
|
WHERE table_name = $1
|
||
|
|
`;
|
||
|
|
|
||
|
|
const result = await this.client.query(query, [tableName]);
|
||
|
|
|
||
|
|
if (result.rows.length === 0) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.info(`테이블 라벨 정보 조회 완료: ${tableName}`);
|
||
|
|
return result.rows[0];
|
||
|
|
} catch (error) {
|
||
|
|
logger.error(`테이블 라벨 정보 조회 중 오류 발생: ${tableName}`, error);
|
||
|
|
throw new Error(
|
||
|
|
`테이블 라벨 정보 조회 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 컬럼 라벨 정보 조회
|
||
|
|
*/
|
||
|
|
async getColumnLabels(
|
||
|
|
tableName: string,
|
||
|
|
columnName: string
|
||
|
|
): Promise<ColumnLabels | null> {
|
||
|
|
try {
|
||
|
|
logger.info(`컬럼 라벨 정보 조회 시작: ${tableName}.${columnName}`);
|
||
|
|
|
||
|
|
const query = `
|
||
|
|
SELECT
|
||
|
|
id,
|
||
|
|
table_name as "tableName",
|
||
|
|
column_name as "columnName",
|
||
|
|
column_label as "columnLabel",
|
||
|
|
web_type as "webType",
|
||
|
|
detail_settings as "detailSettings",
|
||
|
|
description,
|
||
|
|
display_order as "displayOrder",
|
||
|
|
is_visible as "isVisible",
|
||
|
|
code_category as "codeCategory",
|
||
|
|
code_value as "codeValue",
|
||
|
|
reference_table as "referenceTable",
|
||
|
|
reference_column as "referenceColumn",
|
||
|
|
created_date as "createdDate",
|
||
|
|
updated_date as "updatedDate"
|
||
|
|
FROM column_labels
|
||
|
|
WHERE table_name = $1 AND column_name = $2
|
||
|
|
`;
|
||
|
|
|
||
|
|
const result = await this.client.query(query, [tableName, columnName]);
|
||
|
|
|
||
|
|
if (result.rows.length === 0) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.info(`컬럼 라벨 정보 조회 완료: ${tableName}.${columnName}`);
|
||
|
|
return result.rows[0];
|
||
|
|
} catch (error) {
|
||
|
|
logger.error(
|
||
|
|
`컬럼 라벨 정보 조회 중 오류 발생: ${tableName}.${columnName}`,
|
||
|
|
error
|
||
|
|
);
|
||
|
|
throw new Error(
|
||
|
|
`컬럼 라벨 정보 조회 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|