테이블 추가기능 수정사항
This commit is contained in:
@@ -342,14 +342,11 @@ export class DDLExecutionService {
|
||||
tableName: string,
|
||||
columns: CreateColumnDefinition[]
|
||||
): string {
|
||||
// 사용자 정의 컬럼들
|
||||
// 사용자 정의 컬럼들 - 모두 VARCHAR(500)로 통일
|
||||
const columnDefinitions = columns
|
||||
.map((col) => {
|
||||
const postgresType = this.mapWebTypeToPostgresType(
|
||||
col.webType,
|
||||
col.length
|
||||
);
|
||||
let definition = `"${col.name}" ${postgresType}`;
|
||||
// 입력 타입과 관계없이 모든 컬럼을 VARCHAR(500)로 생성
|
||||
let definition = `"${col.name}" varchar(500)`;
|
||||
|
||||
if (!col.nullable) {
|
||||
definition += " NOT NULL";
|
||||
@@ -363,13 +360,13 @@ export class DDLExecutionService {
|
||||
})
|
||||
.join(",\n ");
|
||||
|
||||
// 기본 컬럼들 (시스템 필수 컬럼)
|
||||
// 기본 컬럼들 (날짜는 TIMESTAMP, 나머지는 VARCHAR)
|
||||
const baseColumns = `
|
||||
"id" serial PRIMARY KEY,
|
||||
"id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
||||
"created_date" timestamp DEFAULT now(),
|
||||
"updated_date" timestamp DEFAULT now(),
|
||||
"writer" varchar(100),
|
||||
"company_code" varchar(50) DEFAULT '*'`;
|
||||
"writer" varchar(500),
|
||||
"company_code" varchar(500)`;
|
||||
|
||||
// 최종 CREATE TABLE 쿼리
|
||||
return `
|
||||
@@ -385,11 +382,8 @@ CREATE TABLE "${tableName}" (${baseColumns},
|
||||
tableName: string,
|
||||
column: CreateColumnDefinition
|
||||
): string {
|
||||
const postgresType = this.mapWebTypeToPostgresType(
|
||||
column.webType,
|
||||
column.length
|
||||
);
|
||||
let definition = `"${column.name}" ${postgresType}`;
|
||||
// 새로 추가되는 컬럼도 VARCHAR(500)로 통일
|
||||
let definition = `"${column.name}" varchar(500)`;
|
||||
|
||||
if (!column.nullable) {
|
||||
definition += " NOT NULL";
|
||||
@@ -403,23 +397,27 @@ CREATE TABLE "${tableName}" (${baseColumns},
|
||||
}
|
||||
|
||||
/**
|
||||
* 웹타입을 PostgreSQL 타입으로 매핑
|
||||
* 입력타입을 PostgreSQL 타입으로 매핑 (날짜는 TIMESTAMP, 나머지는 VARCHAR)
|
||||
* 날짜 타입만 TIMESTAMP로, 나머지는 VARCHAR(500)로 통일
|
||||
*/
|
||||
private mapInputTypeToPostgresType(inputType?: string): string {
|
||||
switch (inputType) {
|
||||
case "date":
|
||||
return "timestamp";
|
||||
default:
|
||||
// 날짜 외의 모든 타입은 VARCHAR(500)로 통일
|
||||
return "varchar(500)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 레거시 지원: 웹타입을 PostgreSQL 타입으로 매핑
|
||||
* @deprecated 새로운 시스템에서는 mapInputTypeToPostgresType 사용
|
||||
*/
|
||||
private mapWebTypeToPostgresType(webType: WebType, length?: number): string {
|
||||
const mapping = WEB_TYPE_TO_POSTGRES_MAP[webType];
|
||||
|
||||
if (!mapping) {
|
||||
logger.warn(`알 수 없는 웹타입: ${webType}, text로 대체`);
|
||||
return "text";
|
||||
}
|
||||
|
||||
if (mapping.supportsLength && length && length > 0) {
|
||||
if (mapping.postgresType === "varchar") {
|
||||
return `varchar(${length})`;
|
||||
}
|
||||
}
|
||||
|
||||
return mapping.postgresType;
|
||||
// 레거시 지원을 위해 유지하되, VARCHAR(500)로 통일
|
||||
logger.info(`레거시 웹타입 사용: ${webType} → varchar(500)로 변환`);
|
||||
return "varchar(500)";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,6 +470,127 @@ CREATE TABLE "${tableName}" (${baseColumns},
|
||||
},
|
||||
});
|
||||
|
||||
// 기본 컬럼들 정의 (모든 테이블에 자동으로 추가되는 시스템 컬럼)
|
||||
const defaultColumns = [
|
||||
{
|
||||
name: "id",
|
||||
label: "ID",
|
||||
inputType: "text",
|
||||
description: "기본키 (자동생성)",
|
||||
order: -5,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: "created_date",
|
||||
label: "생성일시",
|
||||
inputType: "date",
|
||||
description: "레코드 생성일시",
|
||||
order: -4,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: "updated_date",
|
||||
label: "수정일시",
|
||||
inputType: "date",
|
||||
description: "레코드 수정일시",
|
||||
order: -3,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: "writer",
|
||||
label: "작성자",
|
||||
inputType: "text",
|
||||
description: "레코드 작성자",
|
||||
order: -2,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: "company_code",
|
||||
label: "회사코드",
|
||||
inputType: "text",
|
||||
description: "회사 구분 코드",
|
||||
order: -1,
|
||||
isVisible: true,
|
||||
},
|
||||
];
|
||||
|
||||
// 기본 컬럼들을 table_type_columns에 등록
|
||||
for (const defaultCol of defaultColumns) {
|
||||
await tx.$executeRaw`
|
||||
INSERT INTO table_type_columns (
|
||||
table_name, column_name, input_type, detail_settings,
|
||||
is_nullable, display_order, created_date, updated_date
|
||||
) VALUES (
|
||||
${tableName}, ${defaultCol.name}, ${defaultCol.inputType}, '{}',
|
||||
'Y', ${defaultCol.order}, now(), now()
|
||||
)
|
||||
ON CONFLICT (table_name, column_name)
|
||||
DO UPDATE SET
|
||||
input_type = ${defaultCol.inputType},
|
||||
display_order = ${defaultCol.order},
|
||||
updated_date = now();
|
||||
`;
|
||||
}
|
||||
|
||||
// 사용자 정의 컬럼들을 table_type_columns에 등록
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const column = columns[i];
|
||||
const inputType = this.convertWebTypeToInputType(
|
||||
column.webType || "text"
|
||||
);
|
||||
|
||||
await tx.$executeRaw`
|
||||
INSERT INTO table_type_columns (
|
||||
table_name, column_name, input_type, detail_settings,
|
||||
is_nullable, display_order, created_date, updated_date
|
||||
) VALUES (
|
||||
${tableName}, ${column.name}, ${inputType}, ${JSON.stringify(column.detailSettings || {})},
|
||||
'Y', ${i}, now(), now()
|
||||
)
|
||||
ON CONFLICT (table_name, column_name)
|
||||
DO UPDATE SET
|
||||
input_type = ${inputType},
|
||||
detail_settings = ${JSON.stringify(column.detailSettings || {})},
|
||||
display_order = ${i},
|
||||
updated_date = now();
|
||||
`;
|
||||
}
|
||||
|
||||
// 레거시 지원: column_labels 테이블에도 등록 (기존 시스템 호환성)
|
||||
// 1. 기본 컬럼들을 column_labels에 등록
|
||||
for (const defaultCol of defaultColumns) {
|
||||
await tx.column_labels.upsert({
|
||||
where: {
|
||||
table_name_column_name: {
|
||||
table_name: tableName,
|
||||
column_name: defaultCol.name,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
column_label: defaultCol.label,
|
||||
input_type: defaultCol.inputType,
|
||||
detail_settings: JSON.stringify({}),
|
||||
description: defaultCol.description,
|
||||
display_order: defaultCol.order,
|
||||
is_visible: defaultCol.isVisible,
|
||||
updated_date: new Date(),
|
||||
},
|
||||
create: {
|
||||
table_name: tableName,
|
||||
column_name: defaultCol.name,
|
||||
column_label: defaultCol.label,
|
||||
input_type: defaultCol.inputType,
|
||||
detail_settings: JSON.stringify({}),
|
||||
description: defaultCol.description,
|
||||
display_order: defaultCol.order,
|
||||
is_visible: defaultCol.isVisible,
|
||||
created_date: new Date(),
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 사용자 정의 컬럼들을 column_labels에 등록
|
||||
for (const column of columns) {
|
||||
await tx.column_labels.upsert({
|
||||
where: {
|
||||
@@ -482,7 +601,7 @@ CREATE TABLE "${tableName}" (${baseColumns},
|
||||
},
|
||||
update: {
|
||||
column_label: column.label || column.name,
|
||||
web_type: column.webType,
|
||||
input_type: this.convertWebTypeToInputType(column.webType || "text"),
|
||||
detail_settings: JSON.stringify(column.detailSettings || {}),
|
||||
description: column.description,
|
||||
display_order: column.order || 0,
|
||||
@@ -493,7 +612,7 @@ CREATE TABLE "${tableName}" (${baseColumns},
|
||||
table_name: tableName,
|
||||
column_name: column.name,
|
||||
column_label: column.label || column.name,
|
||||
web_type: column.webType,
|
||||
input_type: this.convertWebTypeToInputType(column.webType || "text"),
|
||||
detail_settings: JSON.stringify(column.detailSettings || {}),
|
||||
description: column.description,
|
||||
display_order: column.order || 0,
|
||||
@@ -505,6 +624,47 @@ CREATE TABLE "${tableName}" (${baseColumns},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 웹 타입을 입력 타입으로 변환
|
||||
*/
|
||||
private convertWebTypeToInputType(webType: string): string {
|
||||
const webTypeToInputTypeMap: Record<string, string> = {
|
||||
// 텍스트 관련
|
||||
text: "text",
|
||||
textarea: "text",
|
||||
email: "text",
|
||||
tel: "text",
|
||||
url: "text",
|
||||
password: "text",
|
||||
|
||||
// 숫자 관련
|
||||
number: "number",
|
||||
decimal: "number",
|
||||
|
||||
// 날짜 관련
|
||||
date: "date",
|
||||
datetime: "date",
|
||||
time: "date",
|
||||
|
||||
// 선택 관련
|
||||
select: "select",
|
||||
dropdown: "select",
|
||||
checkbox: "checkbox",
|
||||
boolean: "checkbox",
|
||||
radio: "radio",
|
||||
|
||||
// 참조 관련
|
||||
code: "code",
|
||||
entity: "entity",
|
||||
|
||||
// 기타
|
||||
file: "text",
|
||||
button: "text",
|
||||
};
|
||||
|
||||
return webTypeToInputTypeMap[webType] || "text";
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 검증 (슈퍼관리자 확인)
|
||||
*/
|
||||
|
||||
@@ -256,11 +256,11 @@ export class DDLSafetyValidator {
|
||||
if (column.length !== undefined) {
|
||||
if (
|
||||
!["text", "code", "email", "tel", "select", "radio"].includes(
|
||||
column.webType
|
||||
column.webType || "text"
|
||||
)
|
||||
) {
|
||||
warnings.push(
|
||||
`${prefix}${column.webType} 타입에서는 길이 설정이 무시됩니다.`
|
||||
`${prefix}${column.webType || "text"} 타입에서는 길이 설정이 무시됩니다.`
|
||||
);
|
||||
} else if (column.length <= 0 || column.length > 65535) {
|
||||
errors.push(`${prefix}길이는 1 이상 65535 이하여야 합니다.`);
|
||||
|
||||
282
backend-node/src/services/inputTypeService.ts
Normal file
282
backend-node/src/services/inputTypeService.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* 입력 타입 처리 서비스
|
||||
* VARCHAR 통일 방식에서 입력 타입별 형변환 및 검증 처리
|
||||
*/
|
||||
|
||||
import { InputType } from "../types/input-types";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
export interface ValidationResult {
|
||||
isValid: boolean;
|
||||
message?: string;
|
||||
convertedValue?: string;
|
||||
}
|
||||
|
||||
export class InputTypeService {
|
||||
/**
|
||||
* 데이터 저장 전 형변환 (화면 입력값 → DB 저장값)
|
||||
* 모든 값을 VARCHAR(500)에 저장하기 위해 문자열로 변환
|
||||
*/
|
||||
static convertForStorage(value: any, inputType: InputType): string {
|
||||
if (value === null || value === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
switch (inputType) {
|
||||
case "text":
|
||||
case "select":
|
||||
case "radio":
|
||||
return String(value).trim();
|
||||
|
||||
case "number":
|
||||
if (value === "" || value === null || value === undefined) {
|
||||
return "0";
|
||||
}
|
||||
const num = parseFloat(String(value));
|
||||
return isNaN(num) ? "0" : String(num);
|
||||
|
||||
case "date":
|
||||
if (!value || value === "") {
|
||||
return "";
|
||||
}
|
||||
const date = new Date(value);
|
||||
if (isNaN(date.getTime())) {
|
||||
logger.warn(`Invalid date value: ${value}`);
|
||||
return "";
|
||||
}
|
||||
return date.toISOString().split("T")[0]; // YYYY-MM-DD 형식
|
||||
|
||||
case "checkbox":
|
||||
// 다양한 형태의 true 값을 "Y"로, 나머지는 "N"으로 변환
|
||||
const truthyValues = ["true", "1", "Y", "yes", "on", true, 1];
|
||||
return truthyValues.includes(value) ? "Y" : "N";
|
||||
|
||||
case "code":
|
||||
case "entity":
|
||||
return String(value || "").trim();
|
||||
|
||||
default:
|
||||
return String(value);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error converting value for storage: ${error}`, {
|
||||
value,
|
||||
inputType,
|
||||
});
|
||||
return String(value || "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면 표시용 형변환 (DB 저장값 → 화면 표시값)
|
||||
* VARCHAR에서 읽어온 문자열을 적절한 타입으로 변환
|
||||
*/
|
||||
static convertForDisplay(value: string, inputType: InputType): any {
|
||||
if (!value && value !== "0") {
|
||||
// 빈 값 처리
|
||||
switch (inputType) {
|
||||
case "number":
|
||||
return 0;
|
||||
case "checkbox":
|
||||
return false;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
switch (inputType) {
|
||||
case "text":
|
||||
case "select":
|
||||
case "radio":
|
||||
case "code":
|
||||
case "entity":
|
||||
return value;
|
||||
|
||||
case "number":
|
||||
const num = parseFloat(value);
|
||||
return isNaN(num) ? 0 : num;
|
||||
|
||||
case "date":
|
||||
// YYYY-MM-DD 형식 그대로 반환 (HTML date input 호환)
|
||||
return value;
|
||||
|
||||
case "checkbox":
|
||||
return value === "Y" || value === "true" || value === "1";
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error converting value for display: ${error}`, {
|
||||
value,
|
||||
inputType,
|
||||
});
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 입력값 검증
|
||||
* 저장 전에 값이 해당 입력 타입에 적합한지 검증
|
||||
*/
|
||||
static validate(value: any, inputType: InputType): ValidationResult {
|
||||
// 빈 값은 일반적으로 허용 (필수 여부는 별도 검증)
|
||||
if (!value && value !== 0 && value !== false) {
|
||||
return {
|
||||
isValid: true,
|
||||
convertedValue: this.convertForStorage(value, inputType),
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
switch (inputType) {
|
||||
case "text":
|
||||
case "select":
|
||||
case "radio":
|
||||
case "code":
|
||||
case "entity":
|
||||
const strValue = String(value).trim();
|
||||
if (strValue.length > 500) {
|
||||
return {
|
||||
isValid: false,
|
||||
message: "입력값이 너무 깁니다. (최대 500자)",
|
||||
};
|
||||
}
|
||||
return {
|
||||
isValid: true,
|
||||
convertedValue: this.convertForStorage(value, inputType),
|
||||
};
|
||||
|
||||
case "number":
|
||||
const num = parseFloat(String(value));
|
||||
if (isNaN(num)) {
|
||||
return {
|
||||
isValid: false,
|
||||
message: "숫자 형식이 올바르지 않습니다.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
isValid: true,
|
||||
convertedValue: this.convertForStorage(value, inputType),
|
||||
};
|
||||
|
||||
case "date":
|
||||
if (!value) {
|
||||
return { isValid: true, convertedValue: "" };
|
||||
}
|
||||
const date = new Date(value);
|
||||
if (isNaN(date.getTime())) {
|
||||
return {
|
||||
isValid: false,
|
||||
message: "날짜 형식이 올바르지 않습니다.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
isValid: true,
|
||||
convertedValue: this.convertForStorage(value, inputType),
|
||||
};
|
||||
|
||||
case "checkbox":
|
||||
// 체크박스는 모든 값을 허용 (Y/N으로 변환)
|
||||
return {
|
||||
isValid: true,
|
||||
convertedValue: this.convertForStorage(value, inputType),
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
isValid: true,
|
||||
convertedValue: this.convertForStorage(value, inputType),
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error validating value: ${error}`, { value, inputType });
|
||||
return {
|
||||
isValid: false,
|
||||
message: "값 검증 중 오류가 발생했습니다.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 배치 데이터 변환 (여러 필드를 한번에 처리)
|
||||
*/
|
||||
static convertBatchForStorage(
|
||||
data: Record<string, any>,
|
||||
columnTypes: Record<string, InputType>
|
||||
): Record<string, string> {
|
||||
const converted: Record<string, string> = {};
|
||||
|
||||
for (const [columnName, value] of Object.entries(data)) {
|
||||
const inputType = columnTypes[columnName];
|
||||
if (inputType) {
|
||||
converted[columnName] = this.convertForStorage(value, inputType);
|
||||
} else {
|
||||
// 입력 타입이 정의되지 않은 경우 기본적으로 text로 처리
|
||||
converted[columnName] = this.convertForStorage(value, "text");
|
||||
}
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 배치 데이터 표시용 변환
|
||||
*/
|
||||
static convertBatchForDisplay(
|
||||
data: Record<string, string>,
|
||||
columnTypes: Record<string, InputType>
|
||||
): Record<string, any> {
|
||||
const converted: Record<string, any> = {};
|
||||
|
||||
for (const [columnName, value] of Object.entries(data)) {
|
||||
const inputType = columnTypes[columnName];
|
||||
if (inputType) {
|
||||
converted[columnName] = this.convertForDisplay(value, inputType);
|
||||
} else {
|
||||
// 입력 타입이 정의되지 않은 경우 문자열 그대로 반환
|
||||
converted[columnName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 배치 데이터 검증
|
||||
*/
|
||||
static validateBatch(
|
||||
data: Record<string, any>,
|
||||
columnTypes: Record<string, InputType>
|
||||
): {
|
||||
isValid: boolean;
|
||||
errors: Record<string, string>;
|
||||
convertedData: Record<string, string>;
|
||||
} {
|
||||
const errors: Record<string, string> = {};
|
||||
const convertedData: Record<string, string> = {};
|
||||
|
||||
for (const [columnName, value] of Object.entries(data)) {
|
||||
const inputType = columnTypes[columnName];
|
||||
if (inputType) {
|
||||
const result = this.validate(value, inputType);
|
||||
if (!result.isValid) {
|
||||
errors[columnName] = result.message || "검증 실패";
|
||||
} else {
|
||||
convertedData[columnName] = result.convertedValue || "";
|
||||
}
|
||||
} else {
|
||||
// 입력 타입이 정의되지 않은 경우 기본적으로 text로 처리
|
||||
convertedData[columnName] = this.convertForStorage(value, "text");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: Object.keys(errors).length === 0,
|
||||
errors,
|
||||
convertedData,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,7 @@ export class TableManagementService {
|
||||
},
|
||||
update: {
|
||||
column_label: settings.columnLabel,
|
||||
web_type: settings.webType,
|
||||
input_type: settings.inputType,
|
||||
detail_settings: settings.detailSettings,
|
||||
code_category: settings.codeCategory,
|
||||
code_value: settings.codeValue,
|
||||
@@ -345,7 +345,7 @@ export class TableManagementService {
|
||||
table_name: tableName,
|
||||
column_name: columnName,
|
||||
column_label: settings.columnLabel,
|
||||
web_type: settings.webType,
|
||||
input_type: settings.inputType,
|
||||
detail_settings: settings.detailSettings,
|
||||
code_category: settings.codeCategory,
|
||||
code_value: settings.codeValue,
|
||||
@@ -626,7 +626,123 @@ export class TableManagementService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 웹 타입별 기본 상세 설정 생성
|
||||
* 컬럼 입력 타입 설정 (새로운 시스템)
|
||||
*/
|
||||
async updateColumnInputType(
|
||||
tableName: string,
|
||||
columnName: string,
|
||||
inputType: string,
|
||||
detailSettings?: Record<string, any>
|
||||
): Promise<void> {
|
||||
try {
|
||||
logger.info(
|
||||
`컬럼 입력 타입 설정 시작: ${tableName}.${columnName} = ${inputType}`
|
||||
);
|
||||
|
||||
// 입력 타입별 기본 상세 설정 생성
|
||||
const defaultDetailSettings =
|
||||
this.generateDefaultInputTypeSettings(inputType);
|
||||
|
||||
// 사용자 정의 설정과 기본 설정 병합
|
||||
const finalDetailSettings = {
|
||||
...defaultDetailSettings,
|
||||
...detailSettings,
|
||||
};
|
||||
|
||||
// table_type_columns 테이블에서 업데이트
|
||||
await prisma.$executeRaw`
|
||||
INSERT INTO table_type_columns (
|
||||
table_name, column_name, input_type, detail_settings,
|
||||
is_nullable, display_order, created_date, updated_date
|
||||
) VALUES (
|
||||
${tableName}, ${columnName}, ${inputType}, ${JSON.stringify(finalDetailSettings)},
|
||||
'Y', 0, now(), now()
|
||||
)
|
||||
ON CONFLICT (table_name, column_name)
|
||||
DO UPDATE SET
|
||||
input_type = ${inputType},
|
||||
detail_settings = ${JSON.stringify(finalDetailSettings)},
|
||||
updated_date = now();
|
||||
`;
|
||||
|
||||
logger.info(
|
||||
`컬럼 입력 타입 설정 완료: ${tableName}.${columnName} = ${inputType}`
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`컬럼 입력 타입 설정 실패: ${tableName}.${columnName}`,
|
||||
error
|
||||
);
|
||||
throw new Error(
|
||||
`컬럼 입력 타입 설정 실패: ${error instanceof Error ? error.message : "Unknown error"}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 입력 타입별 기본 상세 설정 생성
|
||||
*/
|
||||
private generateDefaultInputTypeSettings(
|
||||
inputType: string
|
||||
): Record<string, any> {
|
||||
switch (inputType) {
|
||||
case "text":
|
||||
return {
|
||||
maxLength: 500,
|
||||
placeholder: "텍스트를 입력하세요",
|
||||
};
|
||||
|
||||
case "number":
|
||||
return {
|
||||
min: 0,
|
||||
step: 1,
|
||||
placeholder: "숫자를 입력하세요",
|
||||
};
|
||||
|
||||
case "date":
|
||||
return {
|
||||
format: "YYYY-MM-DD",
|
||||
placeholder: "날짜를 선택하세요",
|
||||
};
|
||||
|
||||
case "code":
|
||||
return {
|
||||
placeholder: "코드를 선택하세요",
|
||||
searchable: true,
|
||||
};
|
||||
|
||||
case "entity":
|
||||
return {
|
||||
placeholder: "항목을 선택하세요",
|
||||
searchable: true,
|
||||
};
|
||||
|
||||
case "select":
|
||||
return {
|
||||
placeholder: "선택하세요",
|
||||
searchable: false,
|
||||
};
|
||||
|
||||
case "checkbox":
|
||||
return {
|
||||
defaultChecked: false,
|
||||
trueValue: "Y",
|
||||
falseValue: "N",
|
||||
};
|
||||
|
||||
case "radio":
|
||||
return {
|
||||
inline: false,
|
||||
};
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 웹 타입별 기본 상세 설정 생성 (레거시 지원)
|
||||
* @deprecated generateDefaultInputTypeSettings 사용 권장
|
||||
*/
|
||||
private generateDefaultDetailSettings(webType: string): Record<string, any> {
|
||||
switch (webType) {
|
||||
@@ -2363,22 +2479,21 @@ export class TableManagementService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 웹타입 정보 조회 (화면관리 연동용)
|
||||
* 컬럼 입력타입 정보 조회 (화면관리 연동용)
|
||||
*/
|
||||
async getColumnWebTypes(tableName: string): Promise<ColumnTypeInfo[]> {
|
||||
async getColumnInputTypes(tableName: string): Promise<ColumnTypeInfo[]> {
|
||||
try {
|
||||
logger.info(`컬럼 웹타입 정보 조회: ${tableName}`);
|
||||
logger.info(`컬럼 입력타입 정보 조회: ${tableName}`);
|
||||
|
||||
// table_type_columns에서 웹타입 정보 조회
|
||||
const rawWebTypes = await prisma.$queryRaw<any[]>`
|
||||
// table_type_columns에서 입력타입 정보 조회
|
||||
const rawInputTypes = await prisma.$queryRaw<any[]>`
|
||||
SELECT
|
||||
ttc.column_name as "columnName",
|
||||
ttc.column_name as "displayName",
|
||||
COALESCE(ttc.web_type, 'text') as "webType",
|
||||
COALESCE(ttc.input_type, 'text') as "inputType",
|
||||
COALESCE(ttc.detail_settings, '{}') as "detailSettings",
|
||||
ttc.is_nullable as "isNullable",
|
||||
ic.data_type as "dataType",
|
||||
ic.udt_name as "dbType"
|
||||
ic.data_type as "dataType"
|
||||
FROM table_type_columns ttc
|
||||
LEFT JOIN information_schema.columns ic
|
||||
ON ttc.table_name = ic.table_name AND ttc.column_name = ic.column_name
|
||||
@@ -2386,14 +2501,12 @@ export class TableManagementService {
|
||||
ORDER BY ttc.display_order, ttc.column_name
|
||||
`;
|
||||
|
||||
const webTypes: ColumnTypeInfo[] = rawWebTypes.map((col) => ({
|
||||
const inputTypes: ColumnTypeInfo[] = rawInputTypes.map((col) => ({
|
||||
tableName: tableName,
|
||||
columnName: col.columnName,
|
||||
displayName: col.displayName,
|
||||
dataType: col.dataType || "text",
|
||||
dbType: col.dbType || "text",
|
||||
webType: col.webType,
|
||||
inputType: "direct",
|
||||
dataType: col.dataType || "varchar",
|
||||
inputType: col.inputType,
|
||||
detailSettings: col.detailSettings,
|
||||
description: "", // 필수 필드 추가
|
||||
isNullable: col.isNullable,
|
||||
@@ -2403,15 +2516,26 @@ export class TableManagementService {
|
||||
}));
|
||||
|
||||
logger.info(
|
||||
`컬럼 웹타입 정보 조회 완료: ${tableName}, ${webTypes.length}개 컬럼`
|
||||
`컬럼 입력타입 정보 조회 완료: ${tableName}, ${inputTypes.length}개 컬럼`
|
||||
);
|
||||
return webTypes;
|
||||
return inputTypes;
|
||||
} catch (error) {
|
||||
logger.error(`컬럼 웹타입 정보 조회 실패: ${tableName}`, error);
|
||||
logger.error(`컬럼 입력타입 정보 조회 실패: ${tableName}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 레거시 지원: 컬럼 웹타입 정보 조회
|
||||
* @deprecated getColumnInputTypes 사용 권장
|
||||
*/
|
||||
async getColumnWebTypes(tableName: string): Promise<ColumnTypeInfo[]> {
|
||||
logger.warn(
|
||||
`레거시 메서드 사용: getColumnWebTypes → getColumnInputTypes 사용 권장`
|
||||
);
|
||||
return this.getColumnInputTypes(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 연결 상태 확인
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user