phase 2.2 테이블 타입관리 쿼리로 변경 완료
This commit is contained in:
parent
f9f31c7bd3
commit
c8c05f1c0d
|
|
@ -1,4 +1,4 @@
|
||||||
import prisma from "../config/database";
|
import { query, queryOne, transaction } from "../database/db";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { cache, CacheKeys } from "../utils/cache";
|
import { cache, CacheKeys } from "../utils/cache";
|
||||||
import {
|
import {
|
||||||
|
|
@ -28,13 +28,14 @@ export class TableManagementService {
|
||||||
): Promise<{ isCodeType: boolean; codeCategory?: string }> {
|
): Promise<{ isCodeType: boolean; codeCategory?: string }> {
|
||||||
try {
|
try {
|
||||||
// column_labels 테이블에서 해당 컬럼의 web_type이 'code'인지 확인
|
// column_labels 테이블에서 해당 컬럼의 web_type이 'code'인지 확인
|
||||||
const result = await prisma.$queryRaw`
|
const result = await query(
|
||||||
SELECT web_type, code_category
|
`SELECT web_type, code_category
|
||||||
FROM column_labels
|
FROM column_labels
|
||||||
WHERE table_name = ${tableName}
|
WHERE table_name = $1
|
||||||
AND column_name = ${columnName}
|
AND column_name = $2
|
||||||
AND web_type = 'code'
|
AND web_type = 'code'`,
|
||||||
`;
|
[tableName, columnName]
|
||||||
|
);
|
||||||
|
|
||||||
if (Array.isArray(result) && result.length > 0) {
|
if (Array.isArray(result) && result.length > 0) {
|
||||||
const row = result[0] as any;
|
const row = result[0] as any;
|
||||||
|
|
@ -70,8 +71,8 @@ export class TableManagementService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// information_schema는 여전히 $queryRaw 사용
|
// information_schema는 여전히 $queryRaw 사용
|
||||||
const rawTables = await prisma.$queryRaw<any[]>`
|
const rawTables = await query<any>(
|
||||||
SELECT
|
`SELECT
|
||||||
t.table_name as "tableName",
|
t.table_name as "tableName",
|
||||||
COALESCE(tl.table_label, t.table_name) as "displayName",
|
COALESCE(tl.table_label, t.table_name) as "displayName",
|
||||||
COALESCE(tl.description, '') as "description",
|
COALESCE(tl.description, '') as "description",
|
||||||
|
|
@ -83,8 +84,8 @@ export class TableManagementService {
|
||||||
AND t.table_type = 'BASE TABLE'
|
AND t.table_type = 'BASE TABLE'
|
||||||
AND t.table_name NOT LIKE 'pg_%'
|
AND t.table_name NOT LIKE 'pg_%'
|
||||||
AND t.table_name NOT LIKE 'sql_%'
|
AND t.table_name NOT LIKE 'sql_%'
|
||||||
ORDER BY t.table_name
|
ORDER BY t.table_name`
|
||||||
`;
|
);
|
||||||
|
|
||||||
// BigInt를 Number로 변환하여 JSON 직렬화 문제 해결
|
// BigInt를 Number로 변환하여 JSON 직렬화 문제 해결
|
||||||
const tables: TableInfo[] = rawTables.map((table) => ({
|
const tables: TableInfo[] = rawTables.map((table) => ({
|
||||||
|
|
@ -147,11 +148,12 @@ export class TableManagementService {
|
||||||
// 전체 컬럼 수 조회 (캐시 확인)
|
// 전체 컬럼 수 조회 (캐시 확인)
|
||||||
let total = cache.get<number>(countCacheKey);
|
let total = cache.get<number>(countCacheKey);
|
||||||
if (!total) {
|
if (!total) {
|
||||||
const totalResult = await prisma.$queryRaw<[{ count: bigint }]>`
|
const totalResult = await query<{ count: bigint }>(
|
||||||
SELECT COUNT(*) as count
|
`SELECT COUNT(*) as count
|
||||||
FROM information_schema.columns c
|
FROM information_schema.columns c
|
||||||
WHERE c.table_name = ${tableName}
|
WHERE c.table_name = $1`,
|
||||||
`;
|
[tableName]
|
||||||
|
);
|
||||||
total = Number(totalResult[0].count);
|
total = Number(totalResult[0].count);
|
||||||
// 컬럼 수는 자주 변하지 않으므로 30분 캐시
|
// 컬럼 수는 자주 변하지 않으므로 30분 캐시
|
||||||
cache.set(countCacheKey, total, 30 * 60 * 1000);
|
cache.set(countCacheKey, total, 30 * 60 * 1000);
|
||||||
|
|
@ -159,8 +161,8 @@ export class TableManagementService {
|
||||||
|
|
||||||
// 페이지네이션 적용한 컬럼 조회
|
// 페이지네이션 적용한 컬럼 조회
|
||||||
const offset = (page - 1) * size;
|
const offset = (page - 1) * size;
|
||||||
const rawColumns = await prisma.$queryRaw<any[]>`
|
const rawColumns = await query<any>(
|
||||||
SELECT
|
`SELECT
|
||||||
c.column_name as "columnName",
|
c.column_name as "columnName",
|
||||||
COALESCE(cl.column_label, c.column_name) as "displayName",
|
COALESCE(cl.column_label, c.column_name) as "displayName",
|
||||||
c.data_type as "dataType",
|
c.data_type as "dataType",
|
||||||
|
|
@ -195,12 +197,13 @@ export class TableManagementService {
|
||||||
ON tc.constraint_name = kcu.constraint_name
|
ON tc.constraint_name = kcu.constraint_name
|
||||||
AND tc.table_schema = kcu.table_schema
|
AND tc.table_schema = kcu.table_schema
|
||||||
WHERE tc.constraint_type = 'PRIMARY KEY'
|
WHERE tc.constraint_type = 'PRIMARY KEY'
|
||||||
AND tc.table_name = ${tableName}
|
AND tc.table_name = $1
|
||||||
) pk ON c.column_name = pk.column_name AND c.table_name = pk.table_name
|
) pk ON c.column_name = pk.column_name AND c.table_name = pk.table_name
|
||||||
WHERE c.table_name = ${tableName}
|
WHERE c.table_name = $1
|
||||||
ORDER BY c.ordinal_position
|
ORDER BY c.ordinal_position
|
||||||
LIMIT ${size} OFFSET ${offset}
|
LIMIT $2 OFFSET $3`,
|
||||||
`;
|
[tableName, size, offset]
|
||||||
|
);
|
||||||
|
|
||||||
// BigInt를 Number로 변환하여 JSON 직렬화 문제 해결
|
// BigInt를 Number로 변환하여 JSON 직렬화 문제 해결
|
||||||
const columns: ColumnTypeInfo[] = rawColumns.map((column) => ({
|
const columns: ColumnTypeInfo[] = rawColumns.map((column) => ({
|
||||||
|
|
@ -251,15 +254,12 @@ export class TableManagementService {
|
||||||
try {
|
try {
|
||||||
logger.info(`테이블 라벨 자동 추가 시작: ${tableName}`);
|
logger.info(`테이블 라벨 자동 추가 시작: ${tableName}`);
|
||||||
|
|
||||||
await prisma.table_labels.upsert({
|
await query(
|
||||||
where: { table_name: tableName },
|
`INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date)
|
||||||
update: {}, // 이미 존재하면 변경하지 않음
|
VALUES ($1, $2, $3, NOW(), NOW())
|
||||||
create: {
|
ON CONFLICT (table_name) DO NOTHING`,
|
||||||
table_name: tableName,
|
[tableName, tableName, ""]
|
||||||
table_label: tableName,
|
);
|
||||||
description: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(`테이블 라벨 자동 추가 완료: ${tableName}`);
|
logger.info(`테이블 라벨 자동 추가 완료: ${tableName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -282,15 +282,16 @@ export class TableManagementService {
|
||||||
logger.info(`테이블 라벨 업데이트 시작: ${tableName}`);
|
logger.info(`테이블 라벨 업데이트 시작: ${tableName}`);
|
||||||
|
|
||||||
// table_labels 테이블에 UPSERT
|
// table_labels 테이블에 UPSERT
|
||||||
await prisma.$executeRaw`
|
await query(
|
||||||
INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date)
|
`INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date)
|
||||||
VALUES (${tableName}, ${displayName}, ${description || ""}, NOW(), NOW())
|
VALUES ($1, $2, $3, NOW(), NOW())
|
||||||
ON CONFLICT (table_name)
|
ON CONFLICT (table_name)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
table_label = EXCLUDED.table_label,
|
table_label = EXCLUDED.table_label,
|
||||||
description = EXCLUDED.description,
|
description = EXCLUDED.description,
|
||||||
updated_date = NOW()
|
updated_date = NOW()`,
|
||||||
`;
|
[tableName, displayName, description || ""]
|
||||||
|
);
|
||||||
|
|
||||||
// 캐시 무효화
|
// 캐시 무효화
|
||||||
cache.delete(CacheKeys.TABLE_LIST);
|
cache.delete(CacheKeys.TABLE_LIST);
|
||||||
|
|
@ -320,43 +321,40 @@ export class TableManagementService {
|
||||||
await this.insertTableIfNotExists(tableName);
|
await this.insertTableIfNotExists(tableName);
|
||||||
|
|
||||||
// column_labels 업데이트 또는 생성
|
// column_labels 업데이트 또는 생성
|
||||||
await prisma.column_labels.upsert({
|
await query(
|
||||||
where: {
|
`INSERT INTO column_labels (
|
||||||
table_name_column_name: {
|
table_name, column_name, column_label, input_type, detail_settings,
|
||||||
table_name: tableName,
|
code_category, code_value, reference_table, reference_column,
|
||||||
column_name: columnName,
|
display_column, display_order, is_visible, created_date, updated_date
|
||||||
},
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, NOW(), NOW())
|
||||||
},
|
ON CONFLICT (table_name, column_name)
|
||||||
update: {
|
DO UPDATE SET
|
||||||
column_label: settings.columnLabel,
|
column_label = EXCLUDED.column_label,
|
||||||
input_type: settings.inputType,
|
input_type = EXCLUDED.input_type,
|
||||||
detail_settings: settings.detailSettings,
|
detail_settings = EXCLUDED.detail_settings,
|
||||||
code_category: settings.codeCategory,
|
code_category = EXCLUDED.code_category,
|
||||||
code_value: settings.codeValue,
|
code_value = EXCLUDED.code_value,
|
||||||
reference_table: settings.referenceTable,
|
reference_table = EXCLUDED.reference_table,
|
||||||
reference_column: settings.referenceColumn,
|
reference_column = EXCLUDED.reference_column,
|
||||||
display_column: settings.displayColumn, // 🎯 Entity 조인에서 표시할 컬럼명
|
display_column = EXCLUDED.display_column,
|
||||||
display_order: settings.displayOrder || 0,
|
display_order = EXCLUDED.display_order,
|
||||||
is_visible:
|
is_visible = EXCLUDED.is_visible,
|
||||||
settings.isVisible !== undefined ? settings.isVisible : true,
|
updated_date = NOW()`,
|
||||||
updated_date: new Date(),
|
[
|
||||||
},
|
tableName,
|
||||||
create: {
|
columnName,
|
||||||
table_name: tableName,
|
settings.columnLabel,
|
||||||
column_name: columnName,
|
settings.inputType,
|
||||||
column_label: settings.columnLabel,
|
settings.detailSettings,
|
||||||
input_type: settings.inputType,
|
settings.codeCategory,
|
||||||
detail_settings: settings.detailSettings,
|
settings.codeValue,
|
||||||
code_category: settings.codeCategory,
|
settings.referenceTable,
|
||||||
code_value: settings.codeValue,
|
settings.referenceColumn,
|
||||||
reference_table: settings.referenceTable,
|
settings.displayColumn,
|
||||||
reference_column: settings.referenceColumn,
|
settings.displayOrder || 0,
|
||||||
display_column: settings.displayColumn, // 🎯 Entity 조인에서 표시할 컬럼명
|
settings.isVisible !== undefined ? settings.isVisible : true,
|
||||||
display_order: settings.displayOrder || 0,
|
]
|
||||||
is_visible:
|
);
|
||||||
settings.isVisible !== undefined ? settings.isVisible : true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 캐시 무효화 - 해당 테이블의 컬럼 캐시 삭제
|
// 캐시 무효화 - 해당 테이블의 컬럼 캐시 삭제
|
||||||
cache.deleteByPattern(`table_columns:${tableName}:`);
|
cache.deleteByPattern(`table_columns:${tableName}:`);
|
||||||
|
|
@ -387,8 +385,8 @@ export class TableManagementService {
|
||||||
`전체 컬럼 설정 일괄 업데이트 시작: ${tableName}, ${columnSettings.length}개`
|
`전체 컬럼 설정 일괄 업데이트 시작: ${tableName}, ${columnSettings.length}개`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prisma 트랜잭션 사용
|
// Raw Query 트랜잭션 사용
|
||||||
await prisma.$transaction(async (tx) => {
|
await transaction(async (client) => {
|
||||||
// 테이블이 table_labels에 없으면 자동 추가
|
// 테이블이 table_labels에 없으면 자동 추가
|
||||||
await this.insertTableIfNotExists(tableName);
|
await this.insertTableIfNotExists(tableName);
|
||||||
|
|
||||||
|
|
@ -434,16 +432,18 @@ export class TableManagementService {
|
||||||
try {
|
try {
|
||||||
logger.info(`테이블 라벨 정보 조회 시작: ${tableName}`);
|
logger.info(`테이블 라벨 정보 조회 시작: ${tableName}`);
|
||||||
|
|
||||||
const tableLabel = await prisma.table_labels.findUnique({
|
const tableLabel = await queryOne<{
|
||||||
where: { table_name: tableName },
|
table_name: string;
|
||||||
select: {
|
table_label: string | null;
|
||||||
table_name: true,
|
description: string | null;
|
||||||
table_label: true,
|
created_date: Date | null;
|
||||||
description: true,
|
updated_date: Date | null;
|
||||||
created_date: true,
|
}>(
|
||||||
updated_date: true,
|
`SELECT table_name, table_label, description, created_date, updated_date
|
||||||
},
|
FROM table_labels
|
||||||
});
|
WHERE table_name = $1`,
|
||||||
|
[tableName]
|
||||||
|
);
|
||||||
|
|
||||||
if (!tableLabel) {
|
if (!tableLabel) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -478,31 +478,30 @@ export class TableManagementService {
|
||||||
try {
|
try {
|
||||||
logger.info(`컬럼 라벨 정보 조회 시작: ${tableName}.${columnName}`);
|
logger.info(`컬럼 라벨 정보 조회 시작: ${tableName}.${columnName}`);
|
||||||
|
|
||||||
const columnLabel = await prisma.column_labels.findUnique({
|
const columnLabel = await queryOne<{
|
||||||
where: {
|
id: number;
|
||||||
table_name_column_name: {
|
table_name: string;
|
||||||
table_name: tableName,
|
column_name: string;
|
||||||
column_name: columnName,
|
column_label: string | null;
|
||||||
},
|
web_type: string | null;
|
||||||
},
|
detail_settings: any;
|
||||||
select: {
|
description: string | null;
|
||||||
id: true,
|
display_order: number | null;
|
||||||
table_name: true,
|
is_visible: boolean | null;
|
||||||
column_name: true,
|
code_category: string | null;
|
||||||
column_label: true,
|
code_value: string | null;
|
||||||
web_type: true,
|
reference_table: string | null;
|
||||||
detail_settings: true,
|
reference_column: string | null;
|
||||||
description: true,
|
created_date: Date | null;
|
||||||
display_order: true,
|
updated_date: Date | null;
|
||||||
is_visible: true,
|
}>(
|
||||||
code_category: true,
|
`SELECT id, table_name, column_name, column_label, web_type, detail_settings,
|
||||||
code_value: true,
|
description, display_order, is_visible, code_category, code_value,
|
||||||
reference_table: true,
|
reference_table, reference_column, created_date, updated_date
|
||||||
reference_column: true,
|
FROM column_labels
|
||||||
created_date: true,
|
WHERE table_name = $1 AND column_name = $2`,
|
||||||
updated_date: true,
|
[tableName, columnName]
|
||||||
},
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (!columnLabel) {
|
if (!columnLabel) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -563,57 +562,28 @@ export class TableManagementService {
|
||||||
...detailSettings,
|
...detailSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
// column_labels 테이블에 해당 컬럼이 있는지 확인
|
// column_labels UPSERT로 업데이트 또는 생성
|
||||||
const existingColumn = await prisma.column_labels.findFirst({
|
await query(
|
||||||
where: {
|
`INSERT INTO column_labels (
|
||||||
table_name: tableName,
|
table_name, column_name, web_type, detail_settings, input_type, created_date, updated_date
|
||||||
column_name: columnName,
|
) VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
|
||||||
},
|
ON CONFLICT (table_name, column_name)
|
||||||
});
|
DO UPDATE SET
|
||||||
|
web_type = EXCLUDED.web_type,
|
||||||
if (existingColumn) {
|
detail_settings = EXCLUDED.detail_settings,
|
||||||
// 기존 컬럼 라벨 업데이트
|
input_type = COALESCE(EXCLUDED.input_type, column_labels.input_type),
|
||||||
const updateData: any = {
|
updated_date = NOW()`,
|
||||||
web_type: webType,
|
[
|
||||||
detail_settings: JSON.stringify(finalDetailSettings),
|
tableName,
|
||||||
updated_date: new Date(),
|
columnName,
|
||||||
};
|
webType,
|
||||||
|
JSON.stringify(finalDetailSettings),
|
||||||
if (inputType) {
|
inputType || null,
|
||||||
updateData.input_type = inputType;
|
]
|
||||||
}
|
);
|
||||||
|
logger.info(
|
||||||
await prisma.column_labels.update({
|
`컬럼 웹 타입 설정 완료: ${tableName}.${columnName} = ${webType}`
|
||||||
where: {
|
);
|
||||||
id: existingColumn.id,
|
|
||||||
},
|
|
||||||
data: updateData,
|
|
||||||
});
|
|
||||||
logger.info(
|
|
||||||
`컬럼 웹 타입 업데이트 완료: ${tableName}.${columnName} = ${webType}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 새로운 컬럼 라벨 생성
|
|
||||||
const createData: any = {
|
|
||||||
table_name: tableName,
|
|
||||||
column_name: columnName,
|
|
||||||
web_type: webType,
|
|
||||||
detail_settings: JSON.stringify(finalDetailSettings),
|
|
||||||
created_date: new Date(),
|
|
||||||
updated_date: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (inputType) {
|
|
||||||
createData.input_type = inputType;
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.column_labels.create({
|
|
||||||
data: createData,
|
|
||||||
});
|
|
||||||
logger.info(
|
|
||||||
`컬럼 라벨 생성 및 웹 타입 설정 완료: ${tableName}.${columnName} = ${webType}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`컬럼 웹 타입 설정 중 오류 발생: ${tableName}.${columnName}`,
|
`컬럼 웹 타입 설정 중 오류 발생: ${tableName}.${columnName}`,
|
||||||
|
|
@ -650,20 +620,18 @@ export class TableManagementService {
|
||||||
};
|
};
|
||||||
|
|
||||||
// table_type_columns 테이블에서 업데이트
|
// table_type_columns 테이블에서 업데이트
|
||||||
await prisma.$executeRaw`
|
await query(
|
||||||
INSERT INTO table_type_columns (
|
`INSERT INTO table_type_columns (
|
||||||
table_name, column_name, input_type, detail_settings,
|
table_name, column_name, input_type, detail_settings,
|
||||||
is_nullable, display_order, created_date, updated_date
|
is_nullable, display_order, created_date, updated_date
|
||||||
) VALUES (
|
) VALUES ($1, $2, $3, $4, 'Y', 0, now(), now())
|
||||||
${tableName}, ${columnName}, ${inputType}, ${JSON.stringify(finalDetailSettings)},
|
|
||||||
'Y', 0, now(), now()
|
|
||||||
)
|
|
||||||
ON CONFLICT (table_name, column_name)
|
ON CONFLICT (table_name, column_name)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
input_type = ${inputType},
|
input_type = EXCLUDED.input_type,
|
||||||
detail_settings = ${JSON.stringify(finalDetailSettings)},
|
detail_settings = EXCLUDED.detail_settings,
|
||||||
updated_date = now();
|
updated_date = now()`,
|
||||||
`;
|
[tableName, columnName, inputType, JSON.stringify(finalDetailSettings)]
|
||||||
|
);
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`컬럼 입력 타입 설정 완료: ${tableName}.${columnName} = ${inputType}`
|
`컬럼 입력 타입 설정 완료: ${tableName}.${columnName} = ${inputType}`
|
||||||
|
|
@ -911,27 +879,24 @@ export class TableManagementService {
|
||||||
);
|
);
|
||||||
|
|
||||||
// 🎯 컬럼명을 doc_type으로 사용하여 파일 구분
|
// 🎯 컬럼명을 doc_type으로 사용하여 파일 구분
|
||||||
const fileInfos = await prisma.attach_file_info.findMany({
|
const fileInfos = await query<{
|
||||||
where: {
|
objid: string;
|
||||||
target_objid: String(targetObjid),
|
real_file_name: string;
|
||||||
doc_type: columnName, // 컬럼명으로 파일 구분
|
file_size: number;
|
||||||
status: "ACTIVE",
|
file_ext: string;
|
||||||
},
|
file_path: string;
|
||||||
select: {
|
doc_type: string;
|
||||||
objid: true,
|
doc_type_name: string;
|
||||||
real_file_name: true,
|
regdate: Date;
|
||||||
file_size: true,
|
writer: string;
|
||||||
file_ext: true,
|
}>(
|
||||||
file_path: true,
|
`SELECT objid, real_file_name, file_size, file_ext, file_path,
|
||||||
doc_type: true,
|
doc_type, doc_type_name, regdate, writer
|
||||||
doc_type_name: true,
|
FROM attach_file_info
|
||||||
regdate: true,
|
WHERE target_objid = $1 AND doc_type = $2 AND status = 'ACTIVE'
|
||||||
writer: true,
|
ORDER BY regdate DESC`,
|
||||||
},
|
[String(targetObjid), columnName]
|
||||||
orderBy: {
|
);
|
||||||
regdate: "desc",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 파일 정보 포맷팅
|
// 파일 정보 포맷팅
|
||||||
return fileInfos.map((fileInfo) => ({
|
return fileInfos.map((fileInfo) => ({
|
||||||
|
|
@ -956,23 +921,24 @@ export class TableManagementService {
|
||||||
*/
|
*/
|
||||||
private async getFileInfoByPath(filePath: string): Promise<any | null> {
|
private async getFileInfoByPath(filePath: string): Promise<any | null> {
|
||||||
try {
|
try {
|
||||||
const fileInfo = await prisma.attach_file_info.findFirst({
|
const fileInfo = await queryOne<{
|
||||||
where: {
|
objid: string;
|
||||||
file_path: filePath,
|
real_file_name: string;
|
||||||
status: "ACTIVE",
|
file_size: number;
|
||||||
},
|
file_ext: string;
|
||||||
select: {
|
file_path: string;
|
||||||
objid: true,
|
doc_type: string;
|
||||||
real_file_name: true,
|
doc_type_name: string;
|
||||||
file_size: true,
|
regdate: Date;
|
||||||
file_ext: true,
|
writer: string;
|
||||||
file_path: true,
|
}>(
|
||||||
doc_type: true,
|
`SELECT objid, real_file_name, file_size, file_ext, file_path,
|
||||||
doc_type_name: true,
|
doc_type, doc_type_name, regdate, writer
|
||||||
regdate: true,
|
FROM attach_file_info
|
||||||
writer: true,
|
WHERE file_path = $1 AND status = 'ACTIVE'
|
||||||
},
|
LIMIT 1`,
|
||||||
});
|
[filePath]
|
||||||
|
);
|
||||||
|
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1000,17 +966,14 @@ export class TableManagementService {
|
||||||
*/
|
*/
|
||||||
private async getFileTypeColumns(tableName: string): Promise<string[]> {
|
private async getFileTypeColumns(tableName: string): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const fileColumns = await prisma.column_labels.findMany({
|
const fileColumns = await query<{ column_name: string }>(
|
||||||
where: {
|
`SELECT column_name
|
||||||
table_name: tableName,
|
FROM column_labels
|
||||||
web_type: "file",
|
WHERE table_name = $1 AND web_type = 'file'`,
|
||||||
},
|
[tableName]
|
||||||
select: {
|
);
|
||||||
column_name: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const columnNames = fileColumns.map((col: any) => col.column_name);
|
const columnNames = fileColumns.map((col) => col.column_name);
|
||||||
logger.info(`파일 타입 컬럼 감지: ${tableName}`, columnNames);
|
logger.info(`파일 타입 컬럼 감지: ${tableName}`, columnNames);
|
||||||
return columnNames;
|
return columnNames;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1379,19 +1342,19 @@ export class TableManagementService {
|
||||||
displayColumn?: string;
|
displayColumn?: string;
|
||||||
} | null> {
|
} | null> {
|
||||||
try {
|
try {
|
||||||
const result = await prisma.column_labels.findFirst({
|
const result = await queryOne<{
|
||||||
where: {
|
web_type: string | null;
|
||||||
table_name: tableName,
|
code_category: string | null;
|
||||||
column_name: columnName,
|
reference_table: string | null;
|
||||||
},
|
reference_column: string | null;
|
||||||
select: {
|
display_column: string | null;
|
||||||
web_type: true,
|
}>(
|
||||||
code_category: true,
|
`SELECT web_type, code_category, reference_table, reference_column, display_column
|
||||||
reference_table: true,
|
FROM column_labels
|
||||||
reference_column: true,
|
WHERE table_name = $1 AND column_name = $2
|
||||||
display_column: true,
|
LIMIT 1`,
|
||||||
},
|
[tableName, columnName]
|
||||||
});
|
);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1535,10 +1498,7 @@ export class TableManagementService {
|
||||||
|
|
||||||
// 전체 개수 조회
|
// 전체 개수 조회
|
||||||
const countQuery = `SELECT COUNT(*) as count FROM ${safeTableName} ${whereClause}`;
|
const countQuery = `SELECT COUNT(*) as count FROM ${safeTableName} ${whereClause}`;
|
||||||
const countResult = await prisma.$queryRawUnsafe<any[]>(
|
const countResult = await query<any>(countQuery, ...searchValues);
|
||||||
countQuery,
|
|
||||||
...searchValues
|
|
||||||
);
|
|
||||||
const total = parseInt(countResult[0].count);
|
const total = parseInt(countResult[0].count);
|
||||||
|
|
||||||
// 데이터 조회
|
// 데이터 조회
|
||||||
|
|
@ -1549,12 +1509,7 @@ export class TableManagementService {
|
||||||
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
let data = await prisma.$queryRawUnsafe<any[]>(
|
let data = await query<any>(dataQuery, ...searchValues, size, offset);
|
||||||
dataQuery,
|
|
||||||
...searchValues,
|
|
||||||
size,
|
|
||||||
offset
|
|
||||||
);
|
|
||||||
|
|
||||||
// 🎯 파일 컬럼이 있으면 파일 정보 보강
|
// 🎯 파일 컬럼이 있으면 파일 정보 보강
|
||||||
if (fileColumns.length > 0) {
|
if (fileColumns.length > 0) {
|
||||||
|
|
@ -1699,10 +1654,9 @@ export class TableManagementService {
|
||||||
ORDER BY ordinal_position
|
ORDER BY ordinal_position
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const columnInfoResult = (await prisma.$queryRawUnsafe(
|
const columnInfoResult = (await query(columnInfoQuery, [
|
||||||
columnInfoQuery,
|
tableName,
|
||||||
tableName
|
])) as any[];
|
||||||
)) as any[];
|
|
||||||
const columnTypeMap = new Map<string, string>();
|
const columnTypeMap = new Map<string, string>();
|
||||||
|
|
||||||
columnInfoResult.forEach((col: any) => {
|
columnInfoResult.forEach((col: any) => {
|
||||||
|
|
@ -1759,15 +1713,15 @@ export class TableManagementService {
|
||||||
.join(", ");
|
.join(", ");
|
||||||
const columnNames = columns.map((col) => `"${col}"`).join(", ");
|
const columnNames = columns.map((col) => `"${col}"`).join(", ");
|
||||||
|
|
||||||
const query = `
|
const insertQuery = `
|
||||||
INSERT INTO "${tableName}" (${columnNames})
|
INSERT INTO "${tableName}" (${columnNames})
|
||||||
VALUES (${placeholders})
|
VALUES (${placeholders})
|
||||||
`;
|
`;
|
||||||
|
|
||||||
logger.info(`실행할 쿼리: ${query}`);
|
logger.info(`실행할 쿼리: ${insertQuery}`);
|
||||||
logger.info(`쿼리 파라미터:`, values);
|
logger.info(`쿼리 파라미터:`, values);
|
||||||
|
|
||||||
await prisma.$queryRawUnsafe(query, ...values);
|
await query(insertQuery, values);
|
||||||
|
|
||||||
logger.info(`테이블 데이터 추가 완료: ${tableName}`);
|
logger.info(`테이블 데이터 추가 완료: ${tableName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1800,10 +1754,9 @@ export class TableManagementService {
|
||||||
ORDER BY c.ordinal_position
|
ORDER BY c.ordinal_position
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const columnInfoResult = (await prisma.$queryRawUnsafe(
|
const columnInfoResult = (await query(columnInfoQuery, [
|
||||||
columnInfoQuery,
|
tableName,
|
||||||
tableName
|
])) as any[];
|
||||||
)) as any[];
|
|
||||||
const columnTypeMap = new Map<string, string>();
|
const columnTypeMap = new Map<string, string>();
|
||||||
const primaryKeys: string[] = [];
|
const primaryKeys: string[] = [];
|
||||||
|
|
||||||
|
|
@ -1866,7 +1819,7 @@ export class TableManagementService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UPDATE 쿼리 생성
|
// UPDATE 쿼리 생성
|
||||||
const query = `
|
const updateQuery = `
|
||||||
UPDATE "${tableName}"
|
UPDATE "${tableName}"
|
||||||
SET ${setConditions.join(", ")}
|
SET ${setConditions.join(", ")}
|
||||||
WHERE ${whereConditions.join(" AND ")}
|
WHERE ${whereConditions.join(" AND ")}
|
||||||
|
|
@ -1874,10 +1827,10 @@ export class TableManagementService {
|
||||||
|
|
||||||
const allValues = [...setValues, ...whereValues];
|
const allValues = [...setValues, ...whereValues];
|
||||||
|
|
||||||
logger.info(`실행할 UPDATE 쿼리: ${query}`);
|
logger.info(`실행할 UPDATE 쿼리: ${updateQuery}`);
|
||||||
logger.info(`쿼리 파라미터:`, allValues);
|
logger.info(`쿼리 파라미터:`, allValues);
|
||||||
|
|
||||||
const result = await prisma.$queryRawUnsafe(query, ...allValues);
|
const result = await query(updateQuery, allValues);
|
||||||
|
|
||||||
logger.info(`테이블 데이터 수정 완료: ${tableName}`, result);
|
logger.info(`테이블 데이터 수정 완료: ${tableName}`, result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1946,9 +1899,10 @@ export class TableManagementService {
|
||||||
ORDER BY kcu.ordinal_position
|
ORDER BY kcu.ordinal_position
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const primaryKeys = await prisma.$queryRawUnsafe<
|
const primaryKeys = await query<{ column_name: string }>(
|
||||||
{ column_name: string }[]
|
primaryKeyQuery,
|
||||||
>(primaryKeyQuery, tableName);
|
[tableName]
|
||||||
|
);
|
||||||
|
|
||||||
if (primaryKeys.length === 0) {
|
if (primaryKeys.length === 0) {
|
||||||
// 기본 키가 없는 경우, 모든 컬럼으로 삭제 조건 생성
|
// 기본 키가 없는 경우, 모든 컬럼으로 삭제 조건 생성
|
||||||
|
|
@ -1965,7 +1919,7 @@ export class TableManagementService {
|
||||||
|
|
||||||
const deleteQuery = `DELETE FROM "${tableName}" WHERE ${conditions}`;
|
const deleteQuery = `DELETE FROM "${tableName}" WHERE ${conditions}`;
|
||||||
|
|
||||||
const result = await prisma.$queryRawUnsafe(deleteQuery, ...values);
|
const result = await query(deleteQuery, values);
|
||||||
deletedCount += Number(result);
|
deletedCount += Number(result);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1987,7 +1941,7 @@ export class TableManagementService {
|
||||||
|
|
||||||
const deleteQuery = `DELETE FROM "${tableName}" WHERE ${conditions}`;
|
const deleteQuery = `DELETE FROM "${tableName}" WHERE ${conditions}`;
|
||||||
|
|
||||||
const result = await prisma.$queryRawUnsafe(deleteQuery, ...values);
|
const result = await query(deleteQuery, values);
|
||||||
deletedCount += Number(result);
|
deletedCount += Number(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2269,8 +2223,8 @@ export class TableManagementService {
|
||||||
|
|
||||||
// 병렬 실행
|
// 병렬 실행
|
||||||
const [dataResult, countResult] = await Promise.all([
|
const [dataResult, countResult] = await Promise.all([
|
||||||
prisma.$queryRawUnsafe(dataQuery),
|
query(dataQuery),
|
||||||
prisma.$queryRawUnsafe(countQuery),
|
query(countQuery),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const data = Array.isArray(dataResult) ? dataResult : [];
|
const data = Array.isArray(dataResult) ? dataResult : [];
|
||||||
|
|
@ -2642,17 +2596,16 @@ export class TableManagementService {
|
||||||
data: Array<{ column_name: string; data_type: string }>;
|
data: Array<{ column_name: string; data_type: string }>;
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const columns = await prisma.$queryRaw<
|
const columns = await query<{
|
||||||
Array<{
|
column_name: string;
|
||||||
column_name: string;
|
data_type: string;
|
||||||
data_type: string;
|
}>(
|
||||||
}>
|
`SELECT column_name, data_type
|
||||||
>`
|
FROM information_schema.columns
|
||||||
SELECT column_name, data_type
|
WHERE table_name = $1
|
||||||
FROM information_schema.columns
|
ORDER BY ordinal_position`,
|
||||||
WHERE table_name = ${tableName}
|
[tableName]
|
||||||
ORDER BY ordinal_position
|
);
|
||||||
`;
|
|
||||||
|
|
||||||
return { data: columns };
|
return { data: columns };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -2687,45 +2640,40 @@ export class TableManagementService {
|
||||||
try {
|
try {
|
||||||
logger.info(`컬럼 라벨 업데이트: ${tableName}.${columnName}`);
|
logger.info(`컬럼 라벨 업데이트: ${tableName}.${columnName}`);
|
||||||
|
|
||||||
await prisma.column_labels.upsert({
|
await query(
|
||||||
where: {
|
`INSERT INTO column_labels (
|
||||||
table_name_column_name: {
|
table_name, column_name, column_label, web_type, detail_settings,
|
||||||
table_name: tableName,
|
description, display_order, is_visible, code_category, code_value,
|
||||||
column_name: columnName,
|
reference_table, reference_column, created_date, updated_date
|
||||||
},
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, NOW(), NOW())
|
||||||
},
|
ON CONFLICT (table_name, column_name)
|
||||||
update: {
|
DO UPDATE SET
|
||||||
column_label: updates.columnLabel,
|
column_label = EXCLUDED.column_label,
|
||||||
web_type: updates.webType,
|
web_type = EXCLUDED.web_type,
|
||||||
detail_settings: updates.detailSettings,
|
detail_settings = EXCLUDED.detail_settings,
|
||||||
description: updates.description,
|
description = EXCLUDED.description,
|
||||||
display_order: updates.displayOrder,
|
display_order = EXCLUDED.display_order,
|
||||||
is_visible: updates.isVisible,
|
is_visible = EXCLUDED.is_visible,
|
||||||
code_category: updates.codeCategory,
|
code_category = EXCLUDED.code_category,
|
||||||
code_value: updates.codeValue,
|
code_value = EXCLUDED.code_value,
|
||||||
reference_table: updates.referenceTable,
|
reference_table = EXCLUDED.reference_table,
|
||||||
reference_column: updates.referenceColumn,
|
reference_column = EXCLUDED.reference_column,
|
||||||
// display_column: updates.displayColumn, // 🎯 새로 추가 (임시 주석)
|
updated_date = NOW()`,
|
||||||
updated_date: new Date(),
|
[
|
||||||
},
|
tableName,
|
||||||
create: {
|
columnName,
|
||||||
table_name: tableName,
|
updates.columnLabel || columnName,
|
||||||
column_name: columnName,
|
updates.webType || "text",
|
||||||
column_label: updates.columnLabel || columnName,
|
updates.detailSettings,
|
||||||
web_type: updates.webType || "text",
|
updates.description,
|
||||||
detail_settings: updates.detailSettings,
|
updates.displayOrder || 0,
|
||||||
description: updates.description,
|
updates.isVisible !== false,
|
||||||
display_order: updates.displayOrder || 0,
|
updates.codeCategory,
|
||||||
is_visible: updates.isVisible !== false,
|
updates.codeValue,
|
||||||
code_category: updates.codeCategory,
|
updates.referenceTable,
|
||||||
code_value: updates.codeValue,
|
updates.referenceColumn,
|
||||||
reference_table: updates.referenceTable,
|
]
|
||||||
reference_column: updates.referenceColumn,
|
);
|
||||||
// display_column: updates.displayColumn, // 🎯 새로 추가 (임시 주석)
|
|
||||||
created_date: new Date(),
|
|
||||||
updated_date: new Date(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(`컬럼 라벨 업데이트 완료: ${tableName}.${columnName}`);
|
logger.info(`컬럼 라벨 업데이트 완료: ${tableName}.${columnName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -2949,8 +2897,8 @@ export class TableManagementService {
|
||||||
try {
|
try {
|
||||||
logger.info(`테이블 스키마 정보 조회: ${tableName}`);
|
logger.info(`테이블 스키마 정보 조회: ${tableName}`);
|
||||||
|
|
||||||
const rawColumns = await prisma.$queryRaw<any[]>`
|
const rawColumns = await query<any>(
|
||||||
SELECT
|
`SELECT
|
||||||
column_name as "columnName",
|
column_name as "columnName",
|
||||||
column_name as "displayName",
|
column_name as "displayName",
|
||||||
data_type as "dataType",
|
data_type as "dataType",
|
||||||
|
|
@ -2963,15 +2911,16 @@ export class TableManagementService {
|
||||||
CASE
|
CASE
|
||||||
WHEN column_name IN (
|
WHEN column_name IN (
|
||||||
SELECT column_name FROM information_schema.key_column_usage
|
SELECT column_name FROM information_schema.key_column_usage
|
||||||
WHERE table_name = ${tableName} AND constraint_name LIKE '%_pkey'
|
WHERE table_name = $1 AND constraint_name LIKE '%_pkey'
|
||||||
) THEN true
|
) THEN true
|
||||||
ELSE false
|
ELSE false
|
||||||
END as "isPrimaryKey"
|
END as "isPrimaryKey"
|
||||||
FROM information_schema.columns
|
FROM information_schema.columns
|
||||||
WHERE table_name = ${tableName}
|
WHERE table_name = $1
|
||||||
AND table_schema = 'public'
|
AND table_schema = 'public'
|
||||||
ORDER BY ordinal_position
|
ORDER BY ordinal_position`,
|
||||||
`;
|
[tableName]
|
||||||
|
);
|
||||||
|
|
||||||
const columns: ColumnTypeInfo[] = rawColumns.map((col) => ({
|
const columns: ColumnTypeInfo[] = rawColumns.map((col) => ({
|
||||||
tableName: tableName,
|
tableName: tableName,
|
||||||
|
|
@ -3012,14 +2961,15 @@ export class TableManagementService {
|
||||||
try {
|
try {
|
||||||
logger.info(`테이블 존재 여부 확인: ${tableName}`);
|
logger.info(`테이블 존재 여부 확인: ${tableName}`);
|
||||||
|
|
||||||
const result = await prisma.$queryRaw<any[]>`
|
const result = await query<any>(
|
||||||
SELECT EXISTS (
|
`SELECT EXISTS (
|
||||||
SELECT 1 FROM information_schema.tables
|
SELECT 1 FROM information_schema.tables
|
||||||
WHERE table_name = ${tableName}
|
WHERE table_name = $1
|
||||||
AND table_schema = 'public'
|
AND table_schema = 'public'
|
||||||
AND table_type = 'BASE TABLE'
|
AND table_type = 'BASE TABLE'
|
||||||
) as "exists"
|
) as "exists"`,
|
||||||
`;
|
[tableName]
|
||||||
|
);
|
||||||
|
|
||||||
const exists = result[0]?.exists || false;
|
const exists = result[0]?.exists || false;
|
||||||
logger.info(`테이블 존재 여부: ${tableName} = ${exists}`);
|
logger.info(`테이블 존재 여부: ${tableName} = ${exists}`);
|
||||||
|
|
@ -3038,8 +2988,8 @@ export class TableManagementService {
|
||||||
logger.info(`컬럼 입력타입 정보 조회: ${tableName}`);
|
logger.info(`컬럼 입력타입 정보 조회: ${tableName}`);
|
||||||
|
|
||||||
// table_type_columns에서 입력타입 정보 조회
|
// table_type_columns에서 입력타입 정보 조회
|
||||||
const rawInputTypes = await prisma.$queryRaw<any[]>`
|
const rawInputTypes = await query<any>(
|
||||||
SELECT
|
`SELECT
|
||||||
ttc.column_name as "columnName",
|
ttc.column_name as "columnName",
|
||||||
ttc.column_name as "displayName",
|
ttc.column_name as "displayName",
|
||||||
COALESCE(ttc.input_type, 'text') as "inputType",
|
COALESCE(ttc.input_type, 'text') as "inputType",
|
||||||
|
|
@ -3049,9 +2999,10 @@ export class TableManagementService {
|
||||||
FROM table_type_columns ttc
|
FROM table_type_columns ttc
|
||||||
LEFT JOIN information_schema.columns ic
|
LEFT JOIN information_schema.columns ic
|
||||||
ON ttc.table_name = ic.table_name AND ttc.column_name = ic.column_name
|
ON ttc.table_name = ic.table_name AND ttc.column_name = ic.column_name
|
||||||
WHERE ttc.table_name = ${tableName}
|
WHERE ttc.table_name = $1
|
||||||
ORDER BY ttc.display_order, ttc.column_name
|
ORDER BY ttc.display_order, ttc.column_name`,
|
||||||
`;
|
[tableName]
|
||||||
|
);
|
||||||
|
|
||||||
const inputTypes: ColumnTypeInfo[] = rawInputTypes.map((col) => ({
|
const inputTypes: ColumnTypeInfo[] = rawInputTypes.map((col) => ({
|
||||||
tableName: tableName,
|
tableName: tableName,
|
||||||
|
|
@ -3099,7 +3050,7 @@ export class TableManagementService {
|
||||||
logger.info("데이터베이스 연결 상태 확인");
|
logger.info("데이터베이스 연결 상태 확인");
|
||||||
|
|
||||||
// 간단한 쿼리로 연결 테스트
|
// 간단한 쿼리로 연결 테스트
|
||||||
const result = await prisma.$queryRaw<any[]>`SELECT 1 as "test"`;
|
const result = await query<any>(`SELECT 1 as "test"`);
|
||||||
|
|
||||||
if (result && result.length > 0) {
|
if (result && result.length > 0) {
|
||||||
logger.info("데이터베이스 연결 성공");
|
logger.info("데이터베이스 연결 성공");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue