diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-28 00:32:31 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-28 00:32:31 +0000 |
| commit | 20800b214145ee6056f94ca18fa1054f145eb977 (patch) | |
| tree | b5c8b27febe5b126e6d9ece115ea05eace33a020 /lib/sedp | |
| parent | e1344a5da1aeef8fbf0f33e1dfd553078c064ccc (diff) | |
(대표님) lib 파트 커밋
Diffstat (limited to 'lib/sedp')
| -rw-r--r-- | lib/sedp/get-tags.ts | 458 | ||||
| -rw-r--r-- | lib/sedp/sedp-token.ts | 4 |
2 files changed, 329 insertions, 133 deletions
diff --git a/lib/sedp/get-tags.ts b/lib/sedp/get-tags.ts index 7c5661c3..7021d7d2 100644 --- a/lib/sedp/get-tags.ts +++ b/lib/sedp/get-tags.ts @@ -1,16 +1,16 @@ -// lib/sedp/get-tag.ts -import db from "@/db/db"; -import { +import db from "@/db/db"; +import { contractItems, - tags, - forms, - items, - tagTypeClassFormMappings, + tags, + forms, + items, + tagTypeClassFormMappings, projects, tagTypes, - tagClasses + tagClasses, + contracts } from "@/db/schema"; -import { eq, and, like } from "drizzle-orm"; +import { eq, and, like, inArray } from "drizzle-orm"; import { getSEDPToken } from "./sedp-token"; /** @@ -21,10 +21,10 @@ import { getSEDPToken } from "./sedp-token"; * @param progressCallback 진행 상황을 보고하기 위한 콜백 함수 * @returns 처리 결과 정보 (처리된 태그 수, 오류 목록 등) */ -// 함수 반환 타입 업데이트 export async function importTagsFromSEDP( packageId: number, - progressCallback?: (progress: number) => void + progressCallback?: (progress: number) => void, + mode?: string ): Promise<{ processedCount: number; excludedCount: number; @@ -44,8 +44,17 @@ export async function importTagsFromSEDP( throw new Error(`Contract item with ID ${packageId} not found`); } - // 진행 상황 보고 - if (progressCallback) progressCallback(5); + // Step 1-2: 계약 아이템에서 계약 정보를 가져와 프로젝트 ID 획득 + const contract = await db.query.contracts.findFirst({ + where: eq(contracts.id, contractItem.contractId) + }); + + if (!contract) { + throw new Error(`Contract with ID ${contractItem.contractId} not found`); + } + + // 프로젝트 ID 획득 + const projectId = contract.projectId; // Step 1-2: Get the item using itemId from contractItem const item = await db.query.items.findFirst({ @@ -61,153 +70,340 @@ export async function importTagsFromSEDP( // 진행 상황 보고 if (progressCallback) progressCallback(10); - // Step 2: Find the mapping entry with the item code in remark field - // 더 유연한 검색 패턴 사용 (%itemCode%) - const mapping = await db.query.tagTypeClassFormMappings.findFirst({ - where: like(tagTypeClassFormMappings.remark, `%${itemCode}%`) + // 기본 매핑 검색 - 모든 모드에서 사용 + const baseMappings = await db.query.tagTypeClassFormMappings.findMany({ + where: and( + like(tagTypeClassFormMappings.remark, `%${itemCode}%`), + eq(tagTypeClassFormMappings.projectId, projectId) + ) }); - - if (!mapping) { + + if (baseMappings.length === 0) { throw new Error(`No mapping found for item code ${itemCode}`); } - // 진행 상황 보고 - if (progressCallback) progressCallback(15); - - // Step 3: Get the project code - const project = await db.query.projects.findFirst({ - where: eq(projects.id, mapping.projectId) - }); + // Step 2: Find the mapping entries - 모드에 따라 다른 조건 적용 + let mappings = []; + + if (mode === 'IM') { + // IM 모드일 때는 먼저 SEDP에서 태그 데이터를 가져와 TAG_TYPE_ID 리스트 확보 + + // 프로젝트 코드 가져오기 + const project = await db.query.projects.findFirst({ + where: eq(projects.id, projectId) + }); + + if (!project) { + throw new Error(`Project with ID ${projectId} not found`); + } + + // 각 매핑의 formCode에 대해 태그 데이터 조회 + const tagTypeIds = new Set<string>(); + + for (const mapping of baseMappings) { + try { + // SEDP에서 태그 데이터 가져오기 + const tagData = await fetchTagDataFromSEDP(project.code, mapping.formCode); + + // 첫 번째 키를 테이블 이름으로 사용 + const tableName = Object.keys(tagData)[0]; + const tagEntries = tagData[tableName]; + + if (Array.isArray(tagEntries)) { + // 모든 태그에서 TAG_TYPE_ID 수집 + for (const entry of tagEntries) { + if (entry.TAG_TYPE_ID && entry.TAG_TYPE_ID !== "") { + tagTypeIds.add(entry.TAG_TYPE_ID); + } + } + } + } catch (error) { + console.error(`Error fetching tag data for formCode ${mapping.formCode}:`, error); + } + } + + if (tagTypeIds.size === 0) { + throw new Error('No valid TAG_TYPE_ID found in SEDP tag data'); + } + + // 수집된 TAG_TYPE_ID로 tagTypes에서 정보 조회 + const tagTypeInfo = await db.query.tagTypes.findMany({ + where: and( + inArray(tagTypes.code, Array.from(tagTypeIds)), + eq(tagTypes.projectId, projectId) + ) + }); + + if (tagTypeInfo.length === 0) { + throw new Error('No matching tag types found for the collected TAG_TYPE_IDs'); + } + + // 태그 타입 설명 수집 + const tagLabels = tagTypeInfo.map(tt => tt.description); + + // IM 모드에 맞는 매핑 조회 - ep가 "IMEP"인 항목만 + mappings = await db.query.tagTypeClassFormMappings.findMany({ + where: and( + inArray(tagTypeClassFormMappings.tagTypeLabel, tagLabels), + eq(tagTypeClassFormMappings.projectId, projectId), + eq(tagTypeClassFormMappings.ep, "IMEP") + ) + }); + + } else { + // ENG 모드 또는 기본 모드일 때 - 기본 매핑 사용 + mappings = [...baseMappings]; + + // ENG 모드에서는 ep 필드가 "IMEP"가 아닌 매핑만 필터링 + if (mode === 'ENG') { + mappings = mappings.filter(mapping => mapping.ep !== "IMEP"); + } + } - if (!project) { - throw new Error(`Project with ID ${mapping.projectId} not found`); + // 매핑이 없는 경우 모드에 따라 다른 오류 메시지 사용 + if (mappings.length === 0) { + if (mode === 'IM') { + throw new Error('No suitable mappings found for IM mode'); + } else { + throw new Error(`No mapping found for item code ${itemCode}`); + } } + + // 진행 상황 보고 + if (progressCallback) progressCallback(15); - const projectCode = project.code; - const formCode = mapping.formCode; + // 결과 누적을 위한 변수들 초기화 + let totalProcessedCount = 0; + let totalExcludedCount = 0; + let totalEntriesCount = 0; + const allErrors: string[] = []; - // 진행 상황 보고 - if (progressCallback) progressCallback(20); + // 각 매핑에 대해 처리 + for (let mappingIndex = 0; mappingIndex < mappings.length; mappingIndex++) { + const mapping = mappings[mappingIndex]; - // Step 4: Find the form ID - const form = await db.query.forms.findFirst({ - where: and( - eq(forms.contractItemId, packageId), - eq(forms.formCode, formCode) - ) - }); + // Step 3: Get the project code + const project = await db.query.projects.findFirst({ + where: eq(projects.id, mapping.projectId) + }); - let formId = form?.id; + if (!project) { + allErrors.push(`Project with ID ${mapping.projectId} not found`); + continue; // 다음 매핑으로 진행 + } - // If form doesn't exist, create it - if (!form) { - const insertResult = await db.insert(forms).values({ - contractItemId: packageId, - formCode: formCode, - formName: mapping.formName - }).returning({ id: forms.id }); + const projectCode = project.code; - if (insertResult.length === 0) { - throw new Error('Failed to create form record'); + // IM 모드에서는 baseMappings에서 같은 formCode를 가진 매핑을 찾음 + let formCode = mapping.formCode; + if (mode === 'IM') { + // baseMapping에서 동일한 formCode를 가진 매핑 찾기 + const originalMapping = baseMappings.find( + baseMapping => baseMapping.formCode === mapping.formCode + ); + + // 찾았으면 해당 formCode 사용, 못 찾았으면 현재 매핑의 formCode 유지 + if (originalMapping) { + formCode = originalMapping.formCode; + } } - - formId = insertResult[0].id; - } - // 진행 상황 보고 - if (progressCallback) progressCallback(30); + // 진행 상황 보고 - 매핑별 진행률 조정 + if (progressCallback) { + const baseProgress = 15; + const mappingProgress = Math.floor(15 * (mappingIndex + 1) / mappings.length); + progressCallback(baseProgress + mappingProgress); + } - // Step 5: Call the external API to get tag data - const tagData = await fetchTagDataFromSEDP(projectCode, formCode); + // Step 4: Find the form ID + const form = await db.query.forms.findFirst({ + where: and( + eq(forms.contractItemId, packageId), + eq(forms.formCode, formCode) + ) + }); - // 진행 상황 보고 - if (progressCallback) progressCallback(50); + let formId; + + // If form doesn't exist, create it + if (!form) { + // 폼이 없는 경우 새로 생성 - 모드에 따른 필드 설정 + const insertValues: any = { + contractItemId: packageId, + formCode: formCode, + formName: mapping.formName + }; + + // 모드 정보가 있으면 해당 필드 설정 + if (mode) { + if (mode === "ENG") { + insertValues.eng = true; + } else if (mode === "IM") { + insertValues.im = true; + } + } - // Step 6: Process the data and insert into the tags table - let processedCount = 0; - let excludedCount = 0; - const errors: string[] = []; + const insertResult = await db.insert(forms).values(insertValues).returning({ id: forms.id }); - // Get the first key from the response as the table name - const tableName = Object.keys(tagData)[0]; - const tagEntries = tagData[tableName]; + if (insertResult.length === 0) { + allErrors.push(`Failed to create form record for formCode ${formCode}`); + continue; // 다음 매핑으로 진행 + } - if (!Array.isArray(tagEntries) || tagEntries.length === 0) { - throw new Error('No tag data found in the API response'); - } + formId = insertResult[0].id; + } else { + // 폼이 이미 존재하는 경우 - 필요시 모드 필드 업데이트 + formId = form.id; + + if (mode) { + let shouldUpdate = false; + const updateValues: any = {}; + + if (mode === "ENG" && form.eng !== true) { + updateValues.eng = true; + shouldUpdate = true; + } else if (mode === "IM" && form.im !== true) { + updateValues.im = true; + shouldUpdate = true; + } - const totalEntries = tagEntries.length; + if (shouldUpdate) { + await db.update(forms) + .set({ + ...updateValues, + updatedAt: new Date() + }) + .where(eq(forms.id, formId)); - // Process each tag entry - for (let i = 0; i < tagEntries.length; i++) { - try { - const entry = tagEntries[i]; - - // TAG_TYPE_ID가 null이거나 빈 문자열인 경우 제외 - if (entry.TAG_TYPE_ID === null || entry.TAG_TYPE_ID === "") { - excludedCount++; - - // 주기적으로 진행 상황 보고 (건너뛰어도 진행률은 업데이트) - if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { - progressCallback(Math.floor(50 + (i / tagEntries.length) * 50)); + console.log(`Updated form ${formId} with ${mode} mode enabled`); } - - continue; // 이 항목은 건너뜀 } + } + + // 진행 상황 보고 - 매핑별 진행률 조정 + if (progressCallback) { + const baseProgress = 30; + const mappingProgress = Math.floor(20 * (mappingIndex + 1) / mappings.length); + progressCallback(baseProgress + mappingProgress); + } + + try { + // Step 5: Call the external API to get tag data + - // Get tag type description - const tagType = await db.query.tagTypes.findFirst({ - where: and( - eq(tagTypes.code, entry.TAG_TYPE_ID), - eq(tagTypes.projectId, mapping.projectId) - ) - }); - - // Get tag class label - const tagClass = await db.query.tagClasses.findFirst({ - where: and( - eq(tagClasses.code, entry.CLS_ID), - eq(tagClasses.projectId, mapping.projectId) - ) - }); - - // Insert or update the tag - await db.insert(tags).values({ - contractItemId: packageId, - formId: formId, - tagNo: entry.TAG_NO, - tagType: tagType?.description || entry.TAG_TYPE_ID, - class: tagClass?.label || entry.CLS_ID, - description: entry.TAG_DESC - }).onConflictDoUpdate({ - target: [tags.contractItemId, tags.tagNo], - set: { - formId: formId, - tagType: tagType?.description || entry.TAG_TYPE_ID, - class: tagClass?.label || entry.CLS_ID, - description: entry.TAG_DESC, - updatedAt: new Date() - } - }); + const tagData = await fetchTagDataFromSEDP(projectCode, baseMappings[0].formCode); + + // 진행 상황 보고 + if (progressCallback) { + const baseProgress = 50; + const mappingProgress = Math.floor(10 * (mappingIndex + 1) / mappings.length); + progressCallback(baseProgress + mappingProgress); + } - processedCount++; + // Step 6: Process the data and insert into the tags table + let processedCount = 0; + let excludedCount = 0; - // 주기적으로 진행 상황 보고 - if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { - progressCallback(Math.floor(50 + (i / tagEntries.length) * 50)); + // Get the first key from the response as the table name + const tableName = Object.keys(tagData)[0]; + const tagEntries = tagData[tableName]; + + if (!Array.isArray(tagEntries) || tagEntries.length === 0) { + allErrors.push(`No tag data found in the API response for formCode ${baseFormCode}`); + continue; // 다음 매핑으로 진행 } + + const entriesCount = tagEntries.length; + totalEntriesCount += entriesCount; + + // Process each tag entry + for (let i = 0; i < tagEntries.length; i++) { + try { + const entry = tagEntries[i]; + + // TAG_TYPE_ID가 null이거나 빈 문자열인 경우 제외 + if (entry.TAG_TYPE_ID === null || entry.TAG_TYPE_ID === "") { + excludedCount++; + totalExcludedCount++; + + // 주기적으로 진행 상황 보고 (건너뛰어도 진행률은 업데이트) + if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { + const baseProgress = 60; + const entryProgress = Math.floor(30 * ((mappingIndex * entriesCount + i) / (mappings.length * entriesCount))); + progressCallback(baseProgress + entryProgress); + } + + continue; // 이 항목은 건너뜀 + } + + // Get tag type description + const tagType = await db.query.tagTypes.findFirst({ + where: and( + eq(tagTypes.code, entry.TAG_TYPE_ID), + eq(tagTypes.projectId, mapping.projectId) + ) + }); + + // Get tag class label + const tagClass = await db.query.tagClasses.findFirst({ + where: and( + eq(tagClasses.code, entry.CLS_ID), + eq(tagClasses.projectId, mapping.projectId) + ) + }); + + // Insert or update the tag + await db.insert(tags).values({ + contractItemId: packageId, + formId: formId, + tagNo: entry.TAG_NO, + tagType: tagType?.description || entry.TAG_TYPE_ID, + class: tagClass?.label || entry.CLS_ID, + description: entry.TAG_DESC + }).onConflictDoUpdate({ + target: [tags.contractItemId, tags.tagNo], + set: { + formId: formId, + tagType: tagType?.description || entry.TAG_TYPE_ID, + class: tagClass?.label || entry.CLS_ID, + description: entry.TAG_DESC, + updatedAt: new Date() + } + }); + + processedCount++; + totalProcessedCount++; + + // 주기적으로 진행 상황 보고 + if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { + const baseProgress = 60; + const entryProgress = Math.floor(30 * ((mappingIndex * entriesCount + i) / (mappings.length * entriesCount))); + progressCallback(baseProgress + entryProgress); + } + } catch (error: any) { + console.error(`Error processing tag entry:`, error); + allErrors.push(error.message || 'Unknown error'); + } + } + + } catch (error: any) { - console.error(`Error processing tag entry:`, error); - errors.push(error.message || 'Unknown error'); + console.error(`Error processing mapping for formCode ${formCode}:`, error); + allErrors.push(`Error with formCode ${formCode}: ${error.message || 'Unknown error'}`); } } + // 모든 매핑 처리 완료 - 진행률 100% + if (progressCallback) { + progressCallback(100); + } + // 최종 결과 반환 return { - processedCount, - excludedCount, - totalEntries, - errors: errors.length > 0 ? errors : undefined + processedCount: totalProcessedCount, + excludedCount: totalExcludedCount, + totalEntries: totalEntriesCount, + errors: allErrors.length > 0 ? allErrors : undefined }; } catch (error: any) { console.error("Tag import error:", error); @@ -226,10 +422,10 @@ async function fetchTagDataFromSEDP(projectCode: string, formCode: string): Prom try { // Get the token const apiKey = await getSEDPToken(); - + // Define the API base URL const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; - + // Make the API call const response = await fetch( `${SEDP_API_BASE_URL}/Data/GetPubData`, @@ -248,12 +444,12 @@ async function fetchTagDataFromSEDP(projectCode: string, formCode: string): Prom }) } ); - + if (!response.ok) { const errorText = await response.text(); throw new Error(`SEDP API request failed: ${response.status} ${response.statusText} - ${errorText}`); } - + const data = await response.json(); return data; } catch (error: any) { diff --git a/lib/sedp/sedp-token.ts b/lib/sedp/sedp-token.ts index dc419c87..9335a74e 100644 --- a/lib/sedp/sedp-token.ts +++ b/lib/sedp/sedp-token.ts @@ -1,7 +1,7 @@ // 환경 변수 const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; const SEDP_API_USER_ID = process.env.SEDP_API_USER_ID || 'EVCPUSER'; -const SEDP_API_PASSWORD = process.env.SEDP_API_PASSWORD || 'evcpuser@2025'; +const SEDP_API_PASSWORD = process.env.SEDP_API_PASSWORD || 'evcpusr@2025'; /** * SEDP API에서 인증 토큰을 가져옵니다. @@ -10,7 +10,7 @@ const SEDP_API_PASSWORD = process.env.SEDP_API_PASSWORD || 'evcpuser@2025'; export async function getSEDPToken(): Promise<string> { try { const response = await fetch( - `${SEDP_API_BASE_URL}/Security/RequestToken`, + `${SEDP_API_BASE_URL}/Security/RequestTokenWithMembership`, { method: 'POST', headers: { |
