summaryrefslogtreecommitdiff
path: root/lib/tags
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-06-01 13:52:21 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-06-01 13:52:21 +0000
commitbac0228d21b7195065e9cddcc327ae33659c7bcc (patch)
tree8f3016ae4533c8706d0c00a605d9b1d41968c2bc /lib/tags
parent2fdce8d7a57c792bba0ac36fa554dca9c9cc31e3 (diff)
(대표님) 20250601까지 작업사항
Diffstat (limited to 'lib/tags')
-rw-r--r--lib/tags/form-mapping-service.ts5
-rw-r--r--lib/tags/service.ts411
2 files changed, 375 insertions, 41 deletions
diff --git a/lib/tags/form-mapping-service.ts b/lib/tags/form-mapping-service.ts
index 19b3ab14..3e86e9d9 100644
--- a/lib/tags/form-mapping-service.ts
+++ b/lib/tags/form-mapping-service.ts
@@ -8,6 +8,8 @@ import { eq, and } from "drizzle-orm"
export interface FormMapping {
formCode: string;
formName: string;
+ ep: string;
+ remark: string;
}
/**
@@ -29,6 +31,8 @@ export async function getFormMappingsByTagType(
.select({
formCode: tagTypeClassFormMappings.formCode,
formName: tagTypeClassFormMappings.formName,
+ ep: tagTypeClassFormMappings.ep,
+ remark: tagTypeClassFormMappings.remark
})
.from(tagTypeClassFormMappings)
.where(and(
@@ -49,6 +53,7 @@ export async function getFormMappingsByTagType(
.select({
formCode: tagTypeClassFormMappings.formCode,
formName: tagTypeClassFormMappings.formName,
+ ep: tagTypeClassFormMappings.ep
})
.from(tagTypeClassFormMappings)
.where(and(
diff --git a/lib/tags/service.ts b/lib/tags/service.ts
index 6a34d208..187aba39 100644
--- a/lib/tags/service.ts
+++ b/lib/tags/service.ts
@@ -145,11 +145,15 @@ export async function createTag(
}
// 3) 태그 타입에 따른 폼 정보 가져오기
- const formMappings = await getFormMappingsByTagType(
+ const allFormMappings = await getFormMappingsByTagType(
validated.data.tagType,
projectId, // projectId 전달
validated.data.class
)
+
+ // ep가 "IMEP"인 것만 필터링
+ const formMappings = allFormMappings?.filter(mapping => mapping.ep === "IMEP") || []
+
// 폼 매핑이 없으면 로그만 남기고 진행
if (!formMappings || formMappings.length === 0) {
@@ -171,7 +175,7 @@ export async function createTag(
for (const formMapping of formMappings) {
// 4-1) 이미 존재하는 폼인지 확인
const existingForm = await tx
- .select({ id: forms.id, im: forms.im }) // im 필드 추가로 조회
+ .select({ id: forms.id, im: forms.im, eng: forms.eng }) // eng 필드도 추가로 조회
.from(forms)
.where(
and(
@@ -186,13 +190,29 @@ export async function createTag(
// 이미 존재하면 해당 ID 사용
formId = existingForm[0].id
+ // 업데이트할 필드들 준비
+ const updateValues: any = {};
+ let shouldUpdate = false;
+
+ // im 필드 체크
if (existingForm[0].im !== true) {
+ updateValues.im = true;
+ shouldUpdate = true;
+ }
+
+ // eng 필드 체크 - remark에 "VD_"가 포함되어 있을 때만
+ if (formMapping.remark && formMapping.remark.includes("VD_") && existingForm[0].eng !== true) {
+ updateValues.eng = true;
+ shouldUpdate = true;
+ }
+
+ if (shouldUpdate) {
await tx
.update(forms)
- .set({ im: true })
+ .set(updateValues)
.where(eq(forms.id, formId))
- console.log(`Form ${formId} updated with im: true`)
+ console.log(`Form ${formId} updated with:`, updateValues)
}
createdOrExistingForms.push({
@@ -203,14 +223,21 @@ export async function createTag(
})
} else {
// 존재하지 않으면 새로 생성
+ const insertValues: any = {
+ contractItemId: selectedPackageId,
+ formCode: formMapping.formCode,
+ formName: formMapping.formName,
+ im: true,
+ };
+
+ // remark에 "VD_"가 포함되어 있을 때만 eng: true 설정
+ if (formMapping.remark && formMapping.remark.includes("VD_")) {
+ insertValues.eng = true;
+ }
+
const insertResult = await tx
.insert(forms)
- .values({
- contractItemId: selectedPackageId,
- formCode: formMapping.formCode,
- formName: formMapping.formName,
- im: true
- })
+ .values(insertValues)
.returning({ id: forms.id, formCode: forms.formCode, formName: forms.formName })
console.log("insertResult:", insertResult)
@@ -242,17 +269,93 @@ export async function createTag(
console.log(`tags-${selectedPackageId}`, "create", newTag)
- // 6) 캐시 무효화 (React 서버 액션에서 캐싱 사용 시)
+ // 6) 생성된 각 form에 대해 formEntries에 데이터 추가
+ for (const form of createdOrExistingForms) {
+ try {
+ // 기존 formEntry 가져오기
+ const existingEntry = await tx.query.formEntries.findFirst({
+ where: and(
+ eq(formEntries.formCode, form.formCode),
+ eq(formEntries.contractItemId, selectedPackageId)
+ )
+ });
+
+ // 새로운 태그 데이터 객체 생성
+ const newTagData = {
+ TAG_NO: validated.data.tagNo,
+ TAG_DESC: validated.data.description ?? null,
+ status: "New" // 수동으로 생성된 태그임을 표시
+ };
+
+ if (existingEntry && existingEntry.id) {
+ // 기존 formEntry가 있는 경우
+ let existingData: Array<{
+ TAG_NO: string;
+ TAG_DESC?: string;
+ status?: string;
+ [key: string]: any;
+ }> = [];
+
+ if (Array.isArray(existingEntry.data)) {
+ existingData = existingEntry.data;
+ }
+
+ // TAG_NO가 이미 존재하는지 확인
+ const existingTagIndex = existingData.findIndex(
+ item => item.TAG_NO === validated.data.tagNo
+ );
+
+ if (existingTagIndex === -1) {
+ // TAG_NO가 없으면 새로 추가
+ const updatedData = [...existingData, newTagData];
+
+ await tx
+ .update(formEntries)
+ .set({
+ data: updatedData,
+ updatedAt: new Date()
+ })
+ .where(eq(formEntries.id, existingEntry.id));
+
+ console.log(`[CREATE TAG] Added tag ${validated.data.tagNo} to existing formEntry for form ${form.formCode}`);
+ } else {
+ console.log(`[CREATE TAG] Tag ${validated.data.tagNo} already exists in formEntry for form ${form.formCode}`);
+ }
+ } else {
+ // formEntry가 없는 경우 새로 생성
+ await tx.insert(formEntries).values({
+ formCode: form.formCode,
+ contractItemId: selectedPackageId,
+ data: [newTagData],
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ });
+
+ console.log(`[CREATE TAG] Created new formEntry with tag ${validated.data.tagNo} for form ${form.formCode}`);
+ }
+ } catch (formEntryError) {
+ console.error(`[CREATE TAG] Error updating formEntry for form ${form.formCode}:`, formEntryError);
+ // 개별 formEntry 에러는 로그만 남기고 전체 트랜잭션은 계속 진행
+ }
+ }
+
+ // 7) 캐시 무효화 (React 서버 액션에서 캐싱 사용 시)
revalidateTag(`tags-${selectedPackageId}`)
revalidateTag(`forms-${selectedPackageId}-ENG`)
revalidateTag("tags")
- // 7) 성공 시 반환
+ // 생성된 각 form의 캐시도 무효화
+ createdOrExistingForms.forEach(form => {
+ revalidateTag(`form-data-${form.formCode}-${selectedPackageId}`)
+ })
+
+ // 8) 성공 시 반환
return {
success: true,
data: {
forms: createdOrExistingForms,
primaryFormId,
+ tagNo: validated.data.tagNo
},
}
})
@@ -264,6 +367,7 @@ export async function createTag(
}
}
+
export async function createTagInForm(
formData: CreateTagSchema,
selectedPackageId: number | null,
@@ -321,10 +425,77 @@ export async function createTagInForm(
}
}
- const form = await db.query.forms.findFirst({
- where: eq(forms.formCode, formCode)
+ // 3) 먼저 기존 form 찾기
+ let form = await tx.query.forms.findFirst({
+ where: and(
+ eq(forms.formCode, formCode),
+ eq(forms.contractItemId, selectedPackageId)
+ )
});
+ // 4) form이 없으면 formMappings를 통해 생성
+ if (!form) {
+ console.log(`[CREATE TAG IN FORM] Form ${formCode} not found, attempting to create...`);
+
+ // 태그 타입에 따른 폼 정보 가져오기
+ const allFormMappings = await getFormMappingsByTagType(
+ validated.data.tagType,
+ projectId,
+ validated.data.class
+ )
+
+ // ep가 "IMEP"인 것만 필터링
+ const formMappings = allFormMappings?.filter(mapping => mapping.ep === "IMEP") || []
+
+ // 현재 formCode와 일치하는 매핑 찾기
+ const targetFormMapping = formMappings.find(mapping => mapping.formCode === formCode);
+
+ if (targetFormMapping) {
+ console.log(`[CREATE TAG IN FORM] Found form mapping for ${formCode}, creating form...`);
+
+ // form 생성
+ const insertResult = await tx
+ .insert(forms)
+ .values({
+ contractItemId: selectedPackageId,
+ formCode: targetFormMapping.formCode,
+ formName: targetFormMapping.formName,
+ im: true,
+ })
+ .returning({ id: forms.id, formCode: forms.formCode, formName: forms.formName })
+
+ form = {
+ id: insertResult[0].id,
+ formCode: insertResult[0].formCode,
+ formName: insertResult[0].formName,
+ contractItemId: selectedPackageId,
+ im: true,
+ createdAt: new Date(),
+ updatedAt: new Date()
+ };
+
+ console.log(`[CREATE TAG IN FORM] Successfully created form:`, insertResult[0]);
+ } else {
+ console.log(`[CREATE TAG IN FORM] No IMEP form mapping found for formCode: ${formCode}`);
+ console.log(`[CREATE TAG IN FORM] Available IMEP mappings:`, formMappings.map(m => m.formCode));
+ return {
+ error: `Form ${formCode} not found and no IMEP mapping available for tag type ${validated.data.tagType}`
+ };
+ }
+ } else {
+ console.log(`[CREATE TAG IN FORM] Found existing form:`, form.id);
+
+ // 기존 form이 있지만 im이 false인 경우 true로 업데이트
+ if (form.im !== true) {
+ await tx
+ .update(forms)
+ .set({ im: true })
+ .where(eq(forms.id, form.id))
+
+ console.log(`[CREATE TAG IN FORM] Form ${form.id} updated with im: true`)
+ }
+ }
+
if (form?.id) {
// 5) 새 Tag 생성 (같은 트랜잭션 `tx` 사용)
const [newTag] = await insertTag(tx, {
@@ -336,48 +507,91 @@ export async function createTagInForm(
description: validated.data.description ?? null,
})
- let updatedData: Array<{
- TAG_NO: string;
- TAG_DESC?: string;
- }> = [];
-
- updatedData.push({
- TAG_NO: validated.data.tagNo,
- TAG_DESC: validated.data.description ?? null,
- });
-
- const entry = await db.query.formEntries.findFirst({
+ // 6) 기존 formEntry 가져오기
+ const entry = await tx.query.formEntries.findFirst({
where: and(
eq(formEntries.formCode, formCode),
eq(formEntries.contractItemId, selectedPackageId),
)
});
- if (entry && entry.id && updatedData.length > 0) {
- await db
+ if (entry && entry.id) {
+ // 7) 기존 데이터 가져오기 (배열인지 확인)
+ let existingData: Array<{
+ TAG_NO: string;
+ TAG_DESC?: string;
+ status?: string;
+ [key: string]: any; // 다른 필드들도 포함
+ }> = [];
+
+ if (Array.isArray(entry.data)) {
+ existingData = entry.data;
+ }
+
+ console.log(`[CREATE TAG IN FORM] Existing data count: ${existingData.length}`);
+
+ // 8) 새로운 태그를 기존 데이터에 추가 (status 필드 포함)
+ const newTagData = {
+ TAG_NO: validated.data.tagNo,
+ TAG_DESC: validated.data.description ?? null,
+ status: "New" // 수동으로 생성된 태그임을 표시
+ };
+
+ const updatedData = [...existingData, newTagData];
+
+ console.log(`[CREATE TAG IN FORM] Updated data count: ${updatedData.length}`);
+ console.log(`[CREATE TAG IN FORM] Added tag: ${validated.data.tagNo} with status: 수동 생성`);
+
+ // 9) formEntries 업데이트
+ await tx
.update(formEntries)
- .set({ data: updatedData })
+ .set({
+ data: updatedData,
+ updatedAt: new Date() // 업데이트 시간도 갱신
+ })
.where(eq(formEntries.id, entry.id));
+ } else {
+ // 10) formEntry가 없는 경우 새로 생성 (status 필드 포함)
+ console.log(`[CREATE TAG IN FORM] No existing formEntry found, creating new one`);
+
+ const newEntryData = [{
+ TAG_NO: validated.data.tagNo,
+ TAG_DESC: validated.data.description ?? null,
+ status: "New" // 수동으로 생성된 태그임을 표시
+ }];
+
+ await tx.insert(formEntries).values({
+ formCode: formCode,
+ contractItemId: selectedPackageId,
+ data: newEntryData,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ });
}
- console.log(`tags-${selectedPackageId}`, "create", newTag)
-
+ console.log(`[CREATE TAG IN FORM] Successfully created tag: ${validated.data.tagNo}`)
+ } else {
+ return { error: "Failed to create or find form" };
}
- // 6) 캐시 무효화 (React 서버 액션에서 캐싱 사용 시)
+ // 11) 캐시 무효화 (React 서버 액션에서 캐싱 사용 시)
revalidateTag(`tags-${selectedPackageId}`)
revalidateTag(`forms-${selectedPackageId}`)
+ revalidateTag(`form-data-${formCode}-${selectedPackageId}`) // 폼 데이터 캐시도 무효화
revalidateTag("tags")
- // 7) 성공 시 반환
+ // 12) 성공 시 반환
return {
success: true,
- data: null
+ data: {
+ formId: form.id,
+ tagNo: validated.data.tagNo,
+ formCreated: !form // form이 새로 생성되었는지 여부
+ }
}
})
} catch (err: any) {
console.log("createTag in Form error:", err)
-
console.error("createTag in Form error:", err)
return { error: getErrorMessage(err) }
}
@@ -638,6 +852,13 @@ export async function bulkCreateTags(
// 태그 유형별 폼 매핑 캐싱 (성능 최적화)
const formMappingsCache = new Map();
+
+ // formEntries 업데이트를 위한 맵 (formCode -> 태그 데이터 배열)
+ const tagsByFormCode = new Map<string, Array<{
+ TAG_NO: string;
+ TAG_DESC: string | null;
+ status: string;
+ }>>();
for (const tagData of tagsfromExcel) {
// 캐시 키 생성 (tagType + class)
@@ -648,12 +869,28 @@ export async function bulkCreateTags(
if (formMappingsCache.has(cacheKey)) {
formMappings = formMappingsCache.get(cacheKey);
} else {
+ const tagTypeLabel = await tx
+ .select({ description: tagTypes.description })
+ .from(tagTypes)
+ .where(
+ and(
+ eq(tagTypes.projectId, projectId),
+ eq(tagTypes.code, tagData.tagType),
+ )
+ )
+ .limit(1)
+
+ const tagTypeLabelText = tagTypeLabel[0].description
+
// 각 태그 유형에 대한 폼 매핑 조회 (projectId 전달)
- formMappings = await getFormMappingsByTagType(
- tagData.tagType,
+ const allFormMappings = await getFormMappingsByTagType(
+ tagTypeLabelText,
projectId, // projectId 전달
tagData.class
);
+
+ // ep가 "IMEP"인 것만 필터링
+ formMappings = allFormMappings?.filter(mapping => mapping.ep === "IMEP") || [];
formMappingsCache.set(cacheKey, formMappings);
}
@@ -665,7 +902,7 @@ export async function bulkCreateTags(
for (const formMapping of formMappings) {
// 해당 폼이 이미 존재하는지 확인
const existingForm = await tx
- .select({ id: forms.id })
+ .select({ id: forms.id, im: forms.im })
.from(forms)
.where(
and(
@@ -679,6 +916,15 @@ export async function bulkCreateTags(
if (existingForm.length > 0) {
// 이미 존재하면 해당 ID 사용
formId = existingForm[0].id;
+
+ // im 필드 업데이트 (필요한 경우)
+ if (existingForm[0].im !== true) {
+ await tx
+ .update(forms)
+ .set({ im: true })
+ .where(eq(forms.id, formId));
+ }
+
createdOrExistingForms.push({
id: formId,
formCode: formMapping.formCode,
@@ -693,6 +939,7 @@ export async function bulkCreateTags(
contractItemId: selectedPackageId,
formCode: formMapping.formCode,
formName: formMapping.formName,
+ im: true
})
.returning({ id: forms.id, formCode: forms.formCode, formName: forms.formName });
@@ -709,10 +956,22 @@ export async function bulkCreateTags(
if (primaryFormId === null) {
primaryFormId = formId;
}
+
+ // formEntries 업데이트를 위한 데이터 수집 (tagsfromExcel의 원본 데이터 사용)
+ const newTagEntry = {
+ TAG_NO: tagData.tagNo,
+ TAG_DESC: tagData.description || null,
+ status: "New" // 벌크 생성도 수동 생성으로 분류
+ };
+
+ if (!tagsByFormCode.has(formMapping.formCode)) {
+ tagsByFormCode.set(formMapping.formCode, []);
+ }
+ tagsByFormCode.get(formMapping.formCode)!.push(newTagEntry);
}
} else {
console.log(
- "No form mappings found for tag type:",
+ "No IMEP form mappings found for tag type:",
tagData.tagType,
"class:",
tagData.class || "NONE",
@@ -741,17 +1000,88 @@ export async function bulkCreateTags(
});
}
- // 4. 캐시 무효화 (한 번만)
+ // 4. formEntries 업데이트 처리
+ for (const [formCode, newTagsData] of tagsByFormCode.entries()) {
+ try {
+ // 기존 formEntry 가져오기
+ const existingEntry = await tx.query.formEntries.findFirst({
+ where: and(
+ eq(formEntries.formCode, formCode),
+ eq(formEntries.contractItemId, selectedPackageId)
+ )
+ });
+
+ if (existingEntry && existingEntry.id) {
+ // 기존 formEntry가 있는 경우
+ let existingData: Array<{
+ TAG_NO: string;
+ TAG_DESC?: string | null;
+ status?: string;
+ [key: string]: any;
+ }> = [];
+
+ if (Array.isArray(existingEntry.data)) {
+ existingData = existingEntry.data;
+ }
+
+ // 기존 TAG_NO들 추출
+ const existingTagNos = new Set(existingData.map(item => item.TAG_NO));
+
+ // 중복되지 않은 새 태그들만 필터링
+ const newUniqueTagsData = newTagsData.filter(
+ tagData => !existingTagNos.has(tagData.TAG_NO)
+ );
+
+ if (newUniqueTagsData.length > 0) {
+ const updatedData = [...existingData, ...newUniqueTagsData];
+
+ await tx
+ .update(formEntries)
+ .set({
+ data: updatedData,
+ updatedAt: new Date()
+ })
+ .where(eq(formEntries.id, existingEntry.id));
+
+ console.log(`[BULK CREATE] Added ${newUniqueTagsData.length} tags to existing formEntry for form ${formCode}`);
+ } else {
+ console.log(`[BULK CREATE] All tags already exist in formEntry for form ${formCode}`);
+ }
+ } else {
+ // formEntry가 없는 경우 새로 생성
+ await tx.insert(formEntries).values({
+ formCode: formCode,
+ contractItemId: selectedPackageId,
+ data: newTagsData,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ });
+
+ console.log(`[BULK CREATE] Created new formEntry with ${newTagsData.length} tags for form ${formCode}`);
+ }
+ } catch (formEntryError) {
+ console.error(`[BULK CREATE] Error updating formEntry for form ${formCode}:`, formEntryError);
+ // 개별 formEntry 에러는 로그만 남기고 전체 트랜잭션은 계속 진행
+ }
+ }
+
+ // 5. 캐시 무효화 (한 번만)
revalidateTag(`tags-${selectedPackageId}`);
revalidateTag(`forms-${selectedPackageId}`);
revalidateTag("tags");
+ // 업데이트된 모든 form의 캐시도 무효화
+ for (const formCode of tagsByFormCode.keys()) {
+ revalidateTag(`form-data-${formCode}-${selectedPackageId}`);
+ }
+
return {
success: true,
data: {
createdCount: createdTags.length,
tags: createdTags,
- formsInfo: allFormsInfo
+ formsInfo: allFormsInfo,
+ formEntriesUpdated: tagsByFormCode.size // 업데이트된 formEntry 수
}
};
});
@@ -760,7 +1090,6 @@ export async function bulkCreateTags(
return { error: getErrorMessage(err) || "Failed to create tags" };
}
}
-
/** 복수 삭제 */
interface RemoveTagsInput {
ids: number[];