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 { 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 { 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 { 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 { 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 { 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 { 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 { 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"}` ); } } }