summaryrefslogtreecommitdiff
path: root/lib/sedp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sedp')
-rw-r--r--lib/sedp/get-form-tags.ts5
-rw-r--r--lib/sedp/get-tags.ts1
-rw-r--r--lib/sedp/sync-form.ts4
-rw-r--r--lib/sedp/sync-object-class.ts442
4 files changed, 280 insertions, 172 deletions
diff --git a/lib/sedp/get-form-tags.ts b/lib/sedp/get-form-tags.ts
index 4e819414..f307d54a 100644
--- a/lib/sedp/get-form-tags.ts
+++ b/lib/sedp/get-form-tags.ts
@@ -388,8 +388,9 @@ export async function importTagsFromSEDP(
// tagClass 조회 (CLS_ID -> label)
let tagClassLabel = tagEntry.CLS_ID; // 기본값
+ let tagClassId = null; // 기본값
if (tagEntry.CLS_ID) {
- const tagClassRecord = await tx.select({ label: tagClasses.label })
+ const tagClassRecord = await tx.select({ id:tagClasses,id, label: tagClasses.label })
.from(tagClasses)
.where(and(
eq(tagClasses.code, tagEntry.CLS_ID),
@@ -399,6 +400,7 @@ export async function importTagsFromSEDP(
if (tagClassRecord && tagClassRecord.length > 0) {
tagClassLabel = tagClassRecord[0].label;
+ tagClassId = tagClassRecord[0].id;
}
}
@@ -416,6 +418,7 @@ export async function importTagsFromSEDP(
tagNo: tagEntry.TAG_NO,
tagType: tagTypeDescription,
class: tagClassLabel,
+ tagClassId: tagClassId,
description: tagEntry.TAG_DESC || null,
createdAt: new Date(),
updatedAt: new Date()
diff --git a/lib/sedp/get-tags.ts b/lib/sedp/get-tags.ts
index 00916eb2..cb549d8c 100644
--- a/lib/sedp/get-tags.ts
+++ b/lib/sedp/get-tags.ts
@@ -369,6 +369,7 @@ export async function importTagsFromSEDP(
formId: formId,
tagNo: entry.TAG_NO,
tagType: tagType?.description || entry.TAG_TYPE_ID,
+ tagClassId: tagClass?.id,
class: tagClass?.label || entry.CLS_ID,
description: entry.TAG_DESC
}).onConflictDoUpdate({
diff --git a/lib/sedp/sync-form.ts b/lib/sedp/sync-form.ts
index 87de4645..c3c88f07 100644
--- a/lib/sedp/sync-form.ts
+++ b/lib/sedp/sync-form.ts
@@ -984,7 +984,7 @@ export async function saveFormMappingsAndMetas(
if (!attribute) continue;
const tmplMeta = templateAttrMap.get(attId);
- const isShi = mapAtt.INOUT === "OUT";
+ const isShi = mapAtt.INOUT === "IN";
let uomSymbol: string | undefined; let uomId: string | undefined;
if (legacy?.LNK_ATT) {
@@ -996,7 +996,7 @@ export async function saveFormMappingsAndMetas(
key: attId,
label: attribute.DESC as string,
type: (attribute.VAL_TYPE === "LIST" || attribute.VAL_TYPE === "DYNAMICLIST") ? "LIST" : (attribute.VAL_TYPE || "STRING"),
- shi: isShi,
+ shi: !isShi,
hidden: tmplMeta?.hidden ?? false,
seq: tmplMeta?.seq ?? 0,
head: tmplMeta?.head ?? "",
diff --git a/lib/sedp/sync-object-class.ts b/lib/sedp/sync-object-class.ts
index 5fd3ebff..1a325407 100644
--- a/lib/sedp/sync-object-class.ts
+++ b/lib/sedp/sync-object-class.ts
@@ -6,6 +6,12 @@ import { getSEDPToken } from "./sedp-token";
// 환경 변수
const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api';
+// 서브클래스 타입 정의
+interface SubclassInfo {
+ id: string;
+ desc: string;
+}
+
// ObjectClass 인터페이스 정의
interface ObjectClass {
PROJ_NO: string;
@@ -55,6 +61,60 @@ interface LinkAttribute {
SEQ: number;
}
+interface SubClassCodeValue {
+ PRNT_VALUE: string;
+ VALUE: string;
+ DESC: string;
+ REMARK: string;
+ USE_YN: boolean;
+ SEQ: number;
+ ATTRIBUTES: Array<{
+ ATT_ID: string;
+ VALUE: string;
+ }>;
+}
+
+/**
+ * 프로젝트별 CodeList(클래스 코드 값) 조회
+ */
+export async function getCodeListsByID(projectCode: string): Promise<SubClassCodeValue[]> {
+ try {
+ // 1) 토큰(API 키) 발급
+ const apiKey = await getSEDPToken();
+
+ // 2) API 호출
+ const response = await fetch(`${SEDP_API_BASE_URL}/CodeList/GetByID`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ accept: '*/*',
+ ApiKey: apiKey,
+ ProjectNo: projectCode,
+ },
+ body: JSON.stringify({
+ ProjectNo: projectCode,
+ CL_ID: 'EVCP_TAG_FUNC',
+ ContainDeleted: false,
+ }),
+ });
+
+ if (!response.ok) {
+ // 네트워크·인증 오류 등
+ throw new Error(`코드 리스트 요청 실패: ${response.status} ${response.statusText}`);
+ }
+
+ // 3) 응답 파싱
+ const { VALUES = [] } = (await response.json()) as { VALUES?: SubClassCodeValue[] };
+
+ return VALUES; // 정상 반환
+ } catch (err) {
+ // 4) 파싱 오류·기타 예외
+ console.error(`프로젝트 ${projectCode}의 코드 리스트 가져오기 실패:`, err);
+
+ return [];
+ }
+}
+
// 태그 클래스 속성 저장 함수
async function saveTagClassAttributes(
tagClassId: number,
@@ -366,197 +426,237 @@ async function getAllTagTypes(projectCode: string, token: string): Promise<TagTy
}
}
-// LNK_ATT 속성 처리가 포함된 오브젝트 클래스 저장 함수
+// 수정된 함수: ID와 DESC을 함께 반환
+function findSubclasses(parentCode: string, allClasses: ObjectClass[]): SubclassInfo[] {
+ return allClasses
+ .filter(cls => cls.PRT_CLS_ID === parentCode)
+ .map(cls => ({
+ id: cls.CLS_ID,
+ desc: cls.DESC
+ }));
+}
+
+// 서브클래스별 리마크 가져오기 (수정됨)
+async function getSubclassRemarks(
+ subclasses: SubclassInfo[],
+ projectCode: string
+): Promise<Record<string, string>> {
+ try {
+ if (subclasses.length === 0) {
+ return {};
+ }
+
+ // getCodeListsByID로 코드 리스트 가져오기
+ const codeValues = await getCodeListsByID(projectCode);
+
+ if (!Array.isArray(codeValues)) {
+ console.log(`프로젝트 ${projectCode}의 코드 리스트가 배열이 아닙니다.`);
+ return {};
+ }
+
+ // 서브클래스별 리마크 매핑
+ const remarkMap: Record<string, string> = {};
+
+ for (const subclass of subclasses) {
+ // VALUE가 서브클래스 ID와 일치하는 항목 찾기
+ const matchedValue = codeValues.find(value => value.VALUE === subclass.id);
+ if (matchedValue && matchedValue.REMARK) {
+ remarkMap[subclass.id] = matchedValue.REMARK;
+ } else {
+ // REMARK가 없는 경우 빈 문자열 또는 기본값
+ remarkMap[subclass.id] = '';
+ }
+ }
+
+ return remarkMap;
+ } catch (error) {
+ console.error(`서브클래스 리마크 가져오기 실패 (프로젝트: ${projectCode}):`, error);
+ return {};
+ }
+}
+
+// LNK_ATT 속성 처리가 포함된 오브젝트 클래스 저장 함수 (수정됨)
async function saveObjectClassesToDatabase(
projectId: number,
classes: ObjectClass[],
projectCode: string,
token: string,
- skipTagTypeSync: boolean = false // 태그 타입 동기화를 건너뛸지 여부
+ skipTagTypeSync: boolean = false
): Promise<number> {
try {
- // null이 아닌 TAG_TYPE_ID만 필터링
- const validClasses = classes.filter(cls => cls.TAG_TYPE_ID !== null && cls.TAG_TYPE_ID !== "") ;
-
- if (validClasses.length === 0) {
- console.log(`프로젝트 ID ${projectId}에 저장할 유효한 오브젝트 클래스가 없습니다.`);
- return 0;
- }
-
- // 모든 태그 타입 ID 목록 추출
- const tagTypeCodes = validClasses.map(cls => cls.TAG_TYPE_ID!);
-
- // skipTagTypeSync가 true인 경우 태그 타입 동기화 단계 건너뜀
- if (!skipTagTypeSync) {
- // 태그 타입이 없는 경우를 대비해 태그 타입 정보 먼저 가져와서 저장
- console.log(`프로젝트 ID ${projectId}의 태그 타입 동기화 시작...`);
+ // null이 아닌 TAG_TYPE_ID만 필터링
+ const validClasses = classes.filter(cls => cls.TAG_TYPE_ID !== null && cls.TAG_TYPE_ID !== "");
- try {
- // 프로젝트의 모든 태그 타입 가져오기
- const allTagTypes = await getAllTagTypes(projectCode, token);
-
- // 태그 타입 저장
- await saveTagTypesToDatabase(allTagTypes, projectCode);
- } catch (error) {
- console.error(`프로젝트 ${projectCode}의 태그 타입 동기화 실패:`, error);
- // 에러가 발생해도 계속 진행
+ if (validClasses.length === 0) {
+ console.log(`프로젝트 ID ${projectId}에 저장할 유효한 오브젝트 클래스가 없습니다.`);
+ return 0;
}
- console.log(`프로젝트 ID ${projectId}의 태그 타입 동기화 완료`);
- }
-
- // 존재하는 태그 타입 확인
- const existingTagTypeCodes = await verifyTagTypes(projectId, tagTypeCodes);
-
- // 태그 타입이 존재하는 오브젝트 클래스만 필터링
- const classesToSave = validClasses.filter(cls =>
- cls.TAG_TYPE_ID !== null && existingTagTypeCodes.has(cls.TAG_TYPE_ID)
- );
-
- if (classesToSave.length === 0) {
- console.log(`프로젝트 ID ${projectId}에 저장할 유효한 오브젝트 클래스가 없습니다 (태그 타입 존재하지 않음).`);
- return 0;
- }
-
- // 현재 프로젝트의 오브젝트 클래스 코드 가져오기
- const existingClasses = await db.select()
- .from(tagClasses)
- .where(eq(tagClasses.projectId, projectId));
-
- // 코드 기준으로 맵 생성
- const existingClassMap = new Map(
- existingClasses.map(cls => [cls.code, cls])
- );
-
- // 새로 추가할 항목
- const toInsert = [];
-
- // 업데이트할 항목
- const toUpdate = [];
-
- // API에 있는 코드 목록
- const apiClassCodes = new Set(classesToSave.map(cls => cls.CLS_ID));
-
- // 삭제할 코드 목록
- const codesToDelete = existingClasses
- .map(cls => cls.code)
- .filter(code => !apiClassCodes.has(code));
-
- // 클래스 데이터 처리
- for (const cls of classesToSave) {
- // 데이터베이스 레코드 준비
- const record = {
- code: cls.CLS_ID,
- projectId: projectId,
- label: cls.DESC,
- tagTypeCode: cls.TAG_TYPE_ID!,
- updatedAt: new Date()
- };
+ // 태그 타입 동기화 (기존 로직 유지)
+ if (!skipTagTypeSync) {
+ console.log(`프로젝트 ID ${projectId}의 태그 타입 동기화 시작...`);
+ try {
+ const allTagTypes = await getAllTagTypes(projectCode, token);
+ await saveTagTypesToDatabase(allTagTypes, projectCode);
+ } catch (error) {
+ console.error(`프로젝트 ${projectCode}의 태그 타입 동기화 실패:`, error);
+ }
+ console.log(`프로젝트 ID ${projectId}의 태그 타입 동기화 완료`);
+ }
- // 이미 존재하는 코드인지 확인
- if (existingClassMap.has(cls.CLS_ID)) {
- // 업데이트 항목에 추가
- toUpdate.push(record);
- } else {
- // 새로 추가할 항목에 추가 (createdAt 필드 추가)
- toInsert.push({
- ...record,
- createdAt: new Date()
- });
+ // 존재하는 태그 타입 확인
+ const tagTypeCodes = validClasses.map(cls => cls.TAG_TYPE_ID!);
+ const existingTagTypeCodes = await verifyTagTypes(projectId, tagTypeCodes);
+
+ // 태그 타입이 존재하는 오브젝트 클래스만 필터링
+ const classesToSave = validClasses.filter(cls =>
+ cls.TAG_TYPE_ID !== null && existingTagTypeCodes.has(cls.TAG_TYPE_ID)
+ );
+
+ if (classesToSave.length === 0) {
+ console.log(`프로젝트 ID ${projectId}에 저장할 유효한 오브젝트 클래스가 없습니다.`);
+ return 0;
}
- }
-
- // 트랜잭션 실행
- let totalChanged = 0;
-
- // 1. 새 항목 삽입 및 속성 처리
- if (toInsert.length > 0) {
- // returning을 사용하여 삽입된 레코드의 ID와 code를 가져옴
- const insertedClasses = await db.insert(tagClasses)
- .values(toInsert)
- .returning({ id: tagClasses.id, code: tagClasses.code });
-
- totalChanged += toInsert.length;
- console.log(`프로젝트 ID ${projectId}에 ${toInsert.length}개의 새 오브젝트 클래스 추가 완료`);
- // 새로 삽입된 각 클래스의 LNK_ATT 속성 처리
- for (const insertedClass of insertedClasses) {
- const originalClass = classesToSave.find(cls => cls.CLS_ID === insertedClass.code);
- if (originalClass && originalClass.LNK_ATT && originalClass.LNK_ATT.length > 0) {
- try {
- await saveTagClassAttributes(insertedClass.id, originalClass.LNK_ATT);
- } catch (error) {
- console.error(`태그 클래스 ${insertedClass.code}의 속성 저장 실패:`, error);
- // 속성 저장 실패해도 계속 진행
+ // 현재 프로젝트의 모든 오브젝트 클래스 가져오기
+ const existingClasses = await db.select()
+ .from(tagClasses)
+ .where(eq(tagClasses.projectId, projectId));
+
+ // 코드 기준으로 맵 생성
+ const existingClassMap = new Map(
+ existingClasses.map(cls => [cls.code, cls])
+ );
+
+ // 새로 추가할 항목과 업데이트할 항목 분리
+ const toInsert = [];
+ const toUpdate = [];
+
+ // API에 있는 코드 목록
+ const apiClassCodes = new Set(classesToSave.map(cls => cls.CLS_ID));
+
+ // 삭제할 코드 목록
+ const codesToDelete = existingClasses
+ .map(cls => cls.code)
+ .filter(code => !apiClassCodes.has(code));
+
+ // 각 클래스별로 서브클래스와 리마크 처리
+ for (const cls of classesToSave) {
+ // 서브클래스 찾기 (이제 {id, desc} 형태로 반환)
+ const subclasses = findSubclasses(cls.CLS_ID, classes);
+
+ // 서브클래스별 리마크 가져오기
+ const subclassRemark = await getSubclassRemarks(subclasses, projectCode);
+
+ const record = {
+ code: cls.CLS_ID,
+ projectId: projectId,
+ label: cls.DESC,
+ tagTypeCode: cls.TAG_TYPE_ID!,
+ subclasses: subclasses, // 이제 {id, desc}[] 형태
+ subclassRemark: subclassRemark,
+ updatedAt: new Date()
+ };
+
+ if (existingClassMap.has(cls.CLS_ID)) {
+ toUpdate.push(record);
+ } else {
+ toInsert.push({
+ ...record,
+ createdAt: new Date()
+ });
}
- }
}
- }
-
- // 2. 기존 항목 업데이트 및 속성 처리
- for (const item of toUpdate) {
- await db.update(tagClasses)
- .set({
- label: item.label,
- tagTypeCode: item.tagTypeCode,
- updatedAt: item.updatedAt
- })
- .where(
- and(
- eq(tagClasses.code, item.code),
- eq(tagClasses.projectId, item.projectId)
- )
- );
- // 업데이트된 클래스의 ID 조회
- const updatedClass = await db.select({ id: tagClasses.id })
- .from(tagClasses)
- .where(
- and(
- eq(tagClasses.code, item.code),
- eq(tagClasses.projectId, item.projectId)
- )
- )
- .limit(1);
+ let totalChanged = 0;
- if (updatedClass.length > 0) {
- const originalClass = classesToSave.find(cls => cls.CLS_ID === item.code);
- if (originalClass && originalClass.LNK_ATT) {
- try {
- await saveTagClassAttributes(updatedClass[0].id, originalClass.LNK_ATT);
- } catch (error) {
- console.error(`태그 클래스 ${item.code}의 속성 업데이트 실패:`, error);
- // 속성 업데이트 실패해도 계속 진행
+ // 새 항목 삽입
+ if (toInsert.length > 0) {
+ const insertedClasses = await db.insert(tagClasses)
+ .values(toInsert)
+ .returning({ id: tagClasses.id, code: tagClasses.code });
+
+ totalChanged += toInsert.length;
+ console.log(`프로젝트 ID ${projectId}에 ${toInsert.length}개의 새 오브젝트 클래스 추가 완료`);
+
+ // 새로 삽입된 각 클래스의 LNK_ATT 속성 처리
+ for (const insertedClass of insertedClasses) {
+ const originalClass = classesToSave.find(cls => cls.CLS_ID === insertedClass.code);
+ if (originalClass && originalClass.LNK_ATT && originalClass.LNK_ATT.length > 0) {
+ try {
+ await saveTagClassAttributes(insertedClass.id, originalClass.LNK_ATT);
+ } catch (error) {
+ console.error(`태그 클래스 ${insertedClass.code}의 속성 저장 실패:`, error);
+ }
+ }
}
- }
}
- totalChanged += 1;
- }
-
- if (toUpdate.length > 0) {
- console.log(`프로젝트 ID ${projectId}의 ${toUpdate.length}개 오브젝트 클래스 업데이트 완료`);
- }
-
- // 3. 더 이상 존재하지 않는 항목 삭제 (CASCADE로 속성도 자동 삭제됨)
- if (codesToDelete.length > 0) {
- for (const code of codesToDelete) {
- await db.delete(tagClasses)
- .where(
- and(
- eq(tagClasses.code, code),
- eq(tagClasses.projectId, projectId)
- )
- );
+ // 기존 항목 업데이트
+ for (const item of toUpdate) {
+ await db.update(tagClasses)
+ .set({
+ label: item.label,
+ tagTypeCode: item.tagTypeCode,
+ subclasses: item.subclasses,
+ subclassRemark: item.subclassRemark,
+ updatedAt: item.updatedAt
+ })
+ .where(
+ and(
+ eq(tagClasses.code, item.code),
+ eq(tagClasses.projectId, item.projectId)
+ )
+ );
+
+ // 업데이트된 클래스의 속성 처리
+ const updatedClass = await db.select({ id: tagClasses.id })
+ .from(tagClasses)
+ .where(
+ and(
+ eq(tagClasses.code, item.code),
+ eq(tagClasses.projectId, item.projectId)
+ )
+ )
+ .limit(1);
+
+ if (updatedClass.length > 0) {
+ const originalClass = classesToSave.find(cls => cls.CLS_ID === item.code);
+ if (originalClass && originalClass.LNK_ATT) {
+ try {
+ await saveTagClassAttributes(updatedClass[0].id, originalClass.LNK_ATT);
+ } catch (error) {
+ console.error(`태그 클래스 ${item.code}의 속성 업데이트 실패:`, error);
+ }
+ }
+ }
+
+ totalChanged += 1;
}
- console.log(`프로젝트 ID ${projectId}에서 ${codesToDelete.length}개의 오브젝트 클래스 삭제 완료`);
- totalChanged += codesToDelete.length;
- }
-
- return totalChanged;
+
+ if (toUpdate.length > 0) {
+ console.log(`프로젝트 ID ${projectId}의 ${toUpdate.length}개 오브젝트 클래스 업데이트 완료`);
+ }
+
+ // 더 이상 존재하지 않는 항목 삭제
+ if (codesToDelete.length > 0) {
+ for (const code of codesToDelete) {
+ await db.delete(tagClasses)
+ .where(
+ and(
+ eq(tagClasses.code, code),
+ eq(tagClasses.projectId, projectId)
+ )
+ );
+ }
+ console.log(`프로젝트 ID ${projectId}에서 ${codesToDelete.length}개의 오브젝트 클래스 삭제 완료`);
+ totalChanged += codesToDelete.length;
+ }
+
+ return totalChanged;
} catch (error) {
- console.error(`오브젝트 클래스 저장 실패 (프로젝트 ID: ${projectId}):`, error);
- throw error;
+ console.error(`오브젝트 클래스 저장 실패 (프로젝트 ID: ${projectId}):`, error);
+ throw error;
}
}
@@ -717,6 +817,8 @@ export async function getProjectTagClassesWithAttributes(projectId: number) {
code: tagClasses.code,
label: tagClasses.label,
tagTypeCode: tagClasses.tagTypeCode,
+ subclasses: tagClasses.subclasses,
+ subclassRemark: tagClasses.subclassRemark,
createdAt: tagClasses.createdAt,
updatedAt: tagClasses.updatedAt,
// 속성 정보
@@ -741,6 +843,8 @@ export async function getProjectTagClassesWithAttributes(projectId: number) {
code: row.code,
label: row.label,
tagTypeCode: row.tagTypeCode,
+ subclasses: row.subclasses, // 이제 {id, desc}[] 형태
+ subclassRemark: row.subclassRemark,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
attributes: []