Merge branch 'jskim-node' of http://39.117.244.52:3000/kjs/ERP-node into gbpark-node

This commit is contained in:
DDD1542
2026-02-09 15:03:29 +09:00
parent 946ce1964d
commit 2b035ce6e1
5 changed files with 406 additions and 1024 deletions

View File

@@ -1405,7 +1405,7 @@ class DataService {
console.log(`✅ 기존 레코드: ${existingRecords.rows.length}`);
// 2. 새 레코드와 기존 레코드 비교
// 2. id 기반 UPSERT: 레코드에 id(PK)가 있으면 UPDATE, 없으면 INSERT
let inserted = 0;
let updated = 0;
let deleted = 0;
@@ -1413,125 +1413,86 @@ class DataService {
// 날짜 필드를 YYYY-MM-DD 형식으로 변환하는 헬퍼 함수
const normalizeDateValue = (value: any): any => {
if (value == null) return value;
// ISO 날짜 문자열 감지 (YYYY-MM-DDTHH:mm:ss.sssZ)
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return value.split("T")[0]; // YYYY-MM-DD 만 추출
return value.split("T")[0];
}
return value;
};
// 새 레코드 처리 (INSERT or UPDATE)
for (const newRecord of records) {
console.log(`🔍 처리할 새 레코드:`, newRecord);
const existingIds = new Set(existingRecords.rows.map((r: any) => r[pkColumn]));
const processedIds = new Set<string>(); // UPDATE 처리된 id 추적
// DEBUG: 수신된 레코드와 기존 레코드 id 확인
console.log(`🔑 [UPSERT DEBUG] pkColumn: ${pkColumn}`);
console.log(`🔑 [UPSERT DEBUG] existingIds:`, Array.from(existingIds));
console.log(`🔑 [UPSERT DEBUG] records received:`, records.map((r: any) => ({ id: r[pkColumn], keys: Object.keys(r) })));
for (const newRecord of records) {
// 날짜 필드 정규화
const normalizedRecord: Record<string, any> = {};
for (const [key, value] of Object.entries(newRecord)) {
normalizedRecord[key] = normalizeDateValue(value);
}
console.log(`🔄 정규화된 레코드:`, normalizedRecord);
const recordId = normalizedRecord[pkColumn]; // 프론트에서 보낸 기존 레코드의 id
// 전체 레코드 데이터 (parentKeys + normalizedRecord)
const fullRecord = { ...parentKeys, ...normalizedRecord };
// 고유 키: parentKeys 제외한 나머지 필드들
const uniqueFields = Object.keys(normalizedRecord);
console.log(`🔑 고유 필드들:`, uniqueFields);
// 기존 레코드에서 일치하는 것 찾기
const existingRecord = existingRecords.rows.find((existing) => {
return uniqueFields.every((field) => {
const existingValue = existing[field];
const newValue = normalizedRecord[field];
// null/undefined 처리
if (existingValue == null && newValue == null) return true;
if (existingValue == null || newValue == null) return false;
// Date 타입 처리
if (existingValue instanceof Date && typeof newValue === "string") {
return (
existingValue.toISOString().split("T")[0] ===
newValue.split("T")[0]
);
}
// 문자열 비교
return String(existingValue) === String(newValue);
});
});
if (existingRecord) {
// UPDATE: 기존 레코드가 있으면 업데이트
if (recordId && existingIds.has(recordId)) {
// ===== UPDATE: id(PK)가 DB에 존재 → 해당 레코드 업데이트 =====
const fullRecord = { ...parentKeys, ...normalizedRecord };
const updateFields: string[] = [];
const updateValues: any[] = [];
let updateParamIndex = 1;
let paramIdx = 1;
for (const [key, value] of Object.entries(fullRecord)) {
if (key !== pkColumn) {
// Primary Key는 업데이트하지 않음
updateFields.push(`"${key}" = $${updateParamIndex}`);
updateFields.push(`"${key}" = $${paramIdx}`);
updateValues.push(value);
updateParamIndex++;
paramIdx++;
}
}
updateValues.push(existingRecord[pkColumn]); // WHERE 조건용
const updateQuery = `
UPDATE "${tableName}"
SET ${updateFields.join(", ")}, updated_date = NOW()
WHERE "${pkColumn}" = $${updateParamIndex}
`;
await pool.query(updateQuery, updateValues);
updated++;
console.log(`✏️ UPDATE: ${pkColumn} = ${existingRecord[pkColumn]}`);
if (updateFields.length > 0) {
updateValues.push(recordId);
const updateQuery = `
UPDATE "${tableName}"
SET ${updateFields.join(", ")}, updated_date = NOW()
WHERE "${pkColumn}" = $${paramIdx}
`;
await pool.query(updateQuery, updateValues);
updated++;
processedIds.add(recordId);
console.log(`✏️ UPDATE by id: ${pkColumn} = ${recordId}`);
}
} else {
// INSERT: 기존 레코드가 없으면 삽입
// 🆕 자동 필드 추가 (company_code, writer, created_date, updated_date, id)
// created_date는 프론트엔드에서 전달된 값 무시하고 항상 현재 시간 설정
const { created_date: _, ...recordWithoutCreatedDate } = fullRecord;
// ===== INSERT: id 없음 또는 DB에 없음 → 새 레코드 삽입 =====
const { [pkColumn]: _removedId, created_date: _cd, ...cleanRecord } = normalizedRecord;
const fullRecord = { ...parentKeys, ...cleanRecord };
const newId = uuidv4();
const recordWithMeta: Record<string, any> = {
...recordWithoutCreatedDate,
id: uuidv4(), // 새 ID 생성
...fullRecord,
[pkColumn]: newId,
created_date: "NOW()",
updated_date: "NOW()",
};
// company_code가 없으면 userCompany 사용 (단, userCompany가 "*"가 아닐 때만)
if (
!recordWithMeta.company_code &&
userCompany &&
userCompany !== "*"
) {
if (!recordWithMeta.company_code && userCompany && userCompany !== "*") {
recordWithMeta.company_code = userCompany;
}
// writer가 없으면 userId 사용
if (!recordWithMeta.writer && userId) {
recordWithMeta.writer = userId;
}
const insertFields = Object.keys(recordWithMeta).filter(
(key) => recordWithMeta[key] !== "NOW()"
);
const insertPlaceholders: string[] = [];
const insertValues: any[] = [];
let insertParamIndex = 1;
let paramIdx = 1;
for (const field of Object.keys(recordWithMeta)) {
if (recordWithMeta[field] === "NOW()") {
insertPlaceholders.push("NOW()");
} else {
insertPlaceholders.push(`$${insertParamIndex}`);
insertPlaceholders.push(`$${paramIdx}`);
insertValues.push(recordWithMeta[field]);
insertParamIndex++;
paramIdx++;
}
}
@@ -1541,49 +1502,21 @@ class DataService {
.join(", ")})
VALUES (${insertPlaceholders.join(", ")})
`;
console.log(` INSERT 쿼리:`, {
query: insertQuery,
values: insertValues,
});
await pool.query(insertQuery, insertValues);
inserted++;
console.log(` INSERT: 새 레코드`);
processedIds.add(newId);
console.log(` INSERT: 새 레코드 ${pkColumn} = ${newId}`);
}
}
// 3. 삭제할 레코드 찾기 (기존 레코드 중 새 레코드에 없는 것)
for (const existingRecord of existingRecords.rows) {
const uniqueFields = Object.keys(records[0] || {});
const stillExists = records.some((newRecord) => {
return uniqueFields.every((field) => {
const existingValue = existingRecord[field];
const newValue = newRecord[field];
if (existingValue == null && newValue == null) return true;
if (existingValue == null || newValue == null) return false;
if (existingValue instanceof Date && typeof newValue === "string") {
return (
existingValue.toISOString().split("T")[0] ===
newValue.split("T")[0]
);
}
return String(existingValue) === String(newValue);
});
});
if (!stillExists) {
// DELETE: 새 레코드에 없으면 삭제
// 3. 고아 레코드 삭제: 기존 레코드 중 이번에 처리되지 않은 것 삭제
for (const existingRow of existingRecords.rows) {
const existId = existingRow[pkColumn];
if (!processedIds.has(existId)) {
const deleteQuery = `DELETE FROM "${tableName}" WHERE "${pkColumn}" = $1`;
await pool.query(deleteQuery, [existingRecord[pkColumn]]);
await pool.query(deleteQuery, [existId]);
deleted++;
console.log(`🗑️ DELETE: ${pkColumn} = ${existingRecord[pkColumn]}`);
console.log(`🗑️ DELETE orphan: ${pkColumn} = ${existId}`);
}
}