refactor: Enhance unique constraint validation across data operations

- Integrated `TableManagementService` to validate unique constraints before insert, update, and upsert actions in various controllers, including `dataflowExecutionController`, `dynamicFormController`, and `tableManagementController`.
- Improved error handling in `errorHandler` to provide detailed messages indicating which field has a unique constraint violation.
- Updated the `formatPgError` utility to extract and display specific column labels for unique constraint violations, enhancing user feedback.
- Adjusted the table schema retrieval to include company-specific nullable and unique constraints, ensuring accurate representation of database rules.

These changes improve data integrity by preventing duplicate entries and enhance user experience through clearer error messages related to unique constraints.
This commit is contained in:
kjs
2026-03-10 16:15:20 +09:00
parent d56e46b17c
commit 3982aabc24
10 changed files with 225 additions and 47 deletions

View File

@@ -1,6 +1,7 @@
import { Response } from "express";
import { dynamicFormService } from "../services/dynamicFormService";
import { enhancedDynamicFormService } from "../services/enhancedDynamicFormService";
import { TableManagementService } from "../services/tableManagementService";
import { AuthenticatedRequest } from "../types/auth";
import { formatPgError } from "../utils/pgErrorUtil";
@@ -48,6 +49,21 @@ export const saveFormData = async (
formDataWithMeta.company_code = companyCode;
}
// UNIQUE 제약조건 검증 (INSERT 전)
const tms = new TableManagementService();
const uniqueViolations = await tms.validateUniqueConstraints(
tableName,
formDataWithMeta,
companyCode || "*"
);
if (uniqueViolations.length > 0) {
res.status(400).json({
success: false,
message: `중복된 값이 존재합니다: ${uniqueViolations.join(", ")}`,
});
return;
}
// 클라이언트 IP 주소 추출
const ipAddress =
req.ip ||
@@ -112,6 +128,21 @@ export const saveFormDataEnhanced = async (
formDataWithMeta.company_code = companyCode;
}
// UNIQUE 제약조건 검증 (INSERT 전)
const tmsEnhanced = new TableManagementService();
const uniqueViolations = await tmsEnhanced.validateUniqueConstraints(
tableName,
formDataWithMeta,
companyCode || "*"
);
if (uniqueViolations.length > 0) {
res.status(400).json({
success: false,
message: `중복된 값이 존재합니다: ${uniqueViolations.join(", ")}`,
});
return;
}
// 개선된 서비스 사용
const result = await enhancedDynamicFormService.saveFormData(
screenId,
@@ -153,12 +184,28 @@ export const updateFormData = async (
const formDataWithMeta = {
...data,
updated_by: userId,
writer: data.writer || userId, // ✅ writer가 없으면 userId로 설정
writer: data.writer || userId,
updated_at: new Date(),
};
// UNIQUE 제약조건 검증 (UPDATE 시 자기 자신 제외)
const tmsUpdate = new TableManagementService();
const uniqueViolations = await tmsUpdate.validateUniqueConstraints(
tableName,
formDataWithMeta,
companyCode || "*",
id
);
if (uniqueViolations.length > 0) {
res.status(400).json({
success: false,
message: `중복된 값이 존재합니다: ${uniqueViolations.join(", ")}`,
});
return;
}
const result = await dynamicFormService.updateFormData(
id, // parseInt 제거 - 문자열 ID 지원
id,
tableName,
formDataWithMeta
);
@@ -209,11 +256,27 @@ export const updateFormDataPartial = async (
const newDataWithMeta = {
...newData,
updated_by: userId,
writer: newData.writer || userId, // ✅ writer가 없으면 userId로 설정
writer: newData.writer || userId,
};
// UNIQUE 제약조건 검증 (부분 UPDATE 시 자기 자신 제외)
const tmsPartial = new TableManagementService();
const uniqueViolations = await tmsPartial.validateUniqueConstraints(
tableName,
newDataWithMeta,
companyCode || "*",
id
);
if (uniqueViolations.length > 0) {
res.status(400).json({
success: false,
message: `중복된 값이 존재합니다: ${uniqueViolations.join(", ")}`,
});
return;
}
const result = await dynamicFormService.updateFormDataPartial(
id, // 🔧 parseInt 제거 - UUID 문자열도 지원
id,
tableName,
originalData,
newDataWithMeta