diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-28 09:19:42 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-28 09:19:42 +0000 |
| commit | 50ae0b8f02c034e60d4cbb504620dfa1575a836f (patch) | |
| tree | 24c661a0c7354e15ad56e2bded4d300bd7fd2b41 /lib/docu-list-rule/document-class/service.ts | |
| parent | 738f956aa61264ffa761e30398eca23393929f8c (diff) | |
(박서영) 설계 document Numbering Rule 개발-최겸 업로드
Diffstat (limited to 'lib/docu-list-rule/document-class/service.ts')
| -rw-r--r-- | lib/docu-list-rule/document-class/service.ts | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/lib/docu-list-rule/document-class/service.ts b/lib/docu-list-rule/document-class/service.ts new file mode 100644 index 00000000..04dfa50e --- /dev/null +++ b/lib/docu-list-rule/document-class/service.ts @@ -0,0 +1,462 @@ +"use server" + +import { revalidatePath } from "next/cache" +import db from "@/db/db" +import { documentClasses, documentClassOptions, codeGroups } from "@/db/schema/docu-list-rule" +import { eq, desc, asc, sql } from "drizzle-orm" + +// Document Class 목록 조회 (A Class, B Class 등) +export async function getDocumentClassCodeGroups(input: { + page: number + perPage: number + search?: string + sort?: Array<{ id: string; desc: boolean }> + filters?: Array<{ id: string; value: string }> + joinOperator?: "and" | "or" + flags?: string[] + classId?: string + description?: string + isActive?: string +}) { + try { + const { page, perPage, sort, search } = input + const offset = (page - 1) * perPage + + // 기본 조건 + let whereConditions = sql`${documentClasses.isActive} = true` + + // 검색 조건 + if (search) { + const searchTerm = `%${search}%` + whereConditions = sql`${whereConditions} AND ( + ${documentClasses.code} ILIKE ${searchTerm} OR + ${documentClasses.value} ILIKE ${searchTerm} OR + ${documentClasses.description} ILIKE ${searchTerm} + )` + } + + // 정렬 + let orderBy = sql`${documentClasses.createdAt} DESC` + if (sort && sort.length > 0) { + const sortField = sort[0] + const direction = sortField.desc ? sql`DESC` : sql`ASC` + + switch (sortField.id) { + case "code": + orderBy = sql`${documentClasses.code} ${direction}` + break + case "value": + orderBy = sql`${documentClasses.value} ${direction}` + break + case "description": + orderBy = sql`${documentClasses.description} ${direction}` + break + case "createdAt": + orderBy = sql`${documentClasses.createdAt} ${direction}` + break + default: + orderBy = sql`${documentClasses.createdAt} DESC` + } + } + + // 데이터 조회 + const data = await db + .select({ + id: documentClasses.id, + code: documentClasses.code, + value: documentClasses.value, + description: documentClasses.description, + isActive: documentClasses.isActive, + createdAt: documentClasses.createdAt, + updatedAt: documentClasses.updatedAt, + }) + .from(documentClasses) + .where(whereConditions) + .orderBy(orderBy) + .limit(perPage) + .offset(offset) + + // 총 개수 조회 + const [{ count: total }] = await db + .select({ count: sql`count(*)` }) + .from(documentClasses) + .where(whereConditions) + + const pageCount = Math.ceil(Number(total) / perPage) + + return { + success: true, + data, + pageCount, + } + } catch (error) { + console.error("Error fetching document classes:", error) + return { + success: false, + error: "Failed to fetch document classes", + data: [], + pageCount: 0, + } + } +} + +// Document Class 생성 +export async function createDocumentClassCodeGroup(input: { + value: string + description?: string +}) { + try { + // Value 자동 변환: "A", "AB", "A Class", "A CLASS" 등을 "A Class", "AB Class" 형태로 변환 + const formatValue = (value: string): string => { + // 공백 제거 및 대소문자 정규화 + const cleaned = value.trim().toLowerCase() + + // "class"가 포함되어 있으면 제거 + const withoutClass = cleaned.replace(/\s*class\s*/g, '') + + // 알파벳과 숫자만 추출 + const letters = withoutClass.replace(/[^a-z0-9]/g, '') + + if (letters.length === 0) { + return value.trim() // 변환할 수 없으면 원본 반환 + } + + // 첫 글자를 대문자로 변환하고 "Class" 추가 + return letters.charAt(0).toUpperCase() + letters.slice(1) + " Class" + } + + const formattedValue = formatValue(input.value) + + // 자동으로 code 생성 (예: "DOC_CLASS_001", "DOC_CLASS_002" 등) + const existingClasses = await db + .select({ code: documentClasses.code }) + .from(documentClasses) + .orderBy(desc(documentClasses.code)) + + let newCode = "DOC_CLASS_001" + if (existingClasses.length > 0) { + const lastClass = existingClasses[0] + if (lastClass.code) { + const lastNumber = parseInt(lastClass.code.replace("DOC_CLASS_", "")) || 0 + newCode = `DOC_CLASS_${String(lastNumber + 1).padStart(3, '0')}` + } + } + + // Code Group이 존재하는지 확인 + const existingCodeGroup = await db + .select({ id: codeGroups.id }) + .from(codeGroups) + .where(eq(codeGroups.groupId, 'DOC_CLASS')) + .limit(1) + + let codeGroupId: number | null = null + + if (existingCodeGroup.length === 0) { + // Code Group이 없으면 자동으로 생성 + const [newCodeGroup] = await db + .insert(codeGroups) + .values({ + groupId: 'DOC_CLASS', + description: 'Document Class', + codeFormat: 'DOC_CLASS_###', + expressions: '^DOC_CLASS_\\d{3}$', + controlType: 'Combobox', + isActive: true, + }) + .returning({ id: codeGroups.id }) + + codeGroupId = newCodeGroup.id + } else { + codeGroupId = existingCodeGroup[0].id + } + + const [newDocumentClass] = await db + .insert(documentClasses) + .values({ + code: newCode, + value: formattedValue, + description: input.description || "", + codeGroupId: codeGroupId, + isActive: true, + }) + .returning({ id: documentClasses.id }) + + revalidatePath("/evcp/docu-list-rule/document-class") + revalidatePath("/evcp/docu-list-rule/code-groups") + + return { + success: true, + data: newDocumentClass, + message: "Document Class created successfully" + } + } catch (error) { + console.error("Error creating document class:", error) + return { + success: false, + error: "Failed to create document class" + } + } +} + +// Document Class 수정 +export async function updateDocumentClassCodeGroup(input: { + id: number + value: string + description?: string +}) { + try { + // Value 자동 변환: "A", "AB", "A Class", "A CLASS" 등을 "A Class", "AB Class" 형태로 변환 + const formatValue = (value: string): string => { + // 공백 제거 및 대소문자 정규화 + const cleaned = value.trim().toLowerCase() + + // "class"가 포함되어 있으면 제거 + const withoutClass = cleaned.replace(/\s*class\s*/g, '') + + // 알파벳과 숫자만 추출 + const letters = withoutClass.replace(/[^a-z0-9]/g, '') + + if (letters.length === 0) { + return value.trim() // 변환할 수 없으면 원본 반환 + } + + // 첫 글자를 대문자로 변환하고 "Class" 추가 + return letters.charAt(0).toUpperCase() + letters.slice(1) + " Class" + } + + const formattedValue = formatValue(input.value) + + const [updatedDocumentClass] = await db + .update(documentClasses) + .set({ + value: formattedValue, + description: input.description || "", + updatedAt: new Date(), + }) + .where(eq(documentClasses.id, input.id)) + .returning({ id: documentClasses.id }) + + revalidatePath("/evcp/docu-list-rule/document-class") + + return { + success: true, + data: updatedDocumentClass, + message: "Document Class updated successfully" + } + } catch (error) { + console.error("Error updating document class:", error) + return { + success: false, + error: "Failed to update document class" + } + } +} + +// Document Class 삭제 +export async function deleteDocumentClassCodeGroup(id: number) { + try { + // 삭제할 Document Class의 codeGroupId 확인 + const documentClassToDelete = await db + .select({ codeGroupId: documentClasses.codeGroupId }) + .from(documentClasses) + .where(eq(documentClasses.id, id)) + .limit(1) + + const [deletedDocumentClass] = await db + .delete(documentClasses) + .where(eq(documentClasses.id, id)) + .returning({ id: documentClasses.id }) + + // 같은 codeGroupId를 가진 다른 Document Class가 있는지 확인 + if (documentClassToDelete.length > 0 && documentClassToDelete[0].codeGroupId) { + const remainingClasses = await db + .select({ id: documentClasses.id }) + .from(documentClasses) + .where(eq(documentClasses.codeGroupId, documentClassToDelete[0].codeGroupId)) + .limit(1) + + // 더 이상 Document Class가 없으면 Code Group도 삭제 + if (remainingClasses.length === 0) { + await db + .delete(codeGroups) + .where(eq(codeGroups.id, documentClassToDelete[0].codeGroupId)) + } + } + + revalidatePath("/evcp/docu-list-rule/document-class") + revalidatePath("/evcp/docu-list-rule/code-groups") + + return { + success: true, + data: deletedDocumentClass, + message: "Document Class deleted successfully" + } + } catch (error) { + console.error("Error deleting document class:", error) + return { + success: false, + error: "Failed to delete document class" + } + } +} + +// Document Class 옵션 목록 조회 +export async function getDocumentClassSubOptions(documentClassId: number) { + try { + const data = await db + .select({ + id: documentClassOptions.id, + documentClassId: documentClassOptions.documentClassId, + optionValue: documentClassOptions.optionValue, + optionCode: documentClassOptions.optionCode, + sortOrder: documentClassOptions.sortOrder, + isActive: documentClassOptions.isActive, + createdAt: documentClassOptions.createdAt, + updatedAt: documentClassOptions.updatedAt, + }) + .from(documentClassOptions) + .where(eq(documentClassOptions.documentClassId, documentClassId)) + .orderBy(asc(documentClassOptions.sortOrder), asc(documentClassOptions.optionValue)) + + return { + success: true, + data, + } + } catch (error) { + console.error("Error fetching document class options:", error) + return { + success: false, + error: "Failed to fetch document class options", + data: [], + } + } +} + +// Document Class 옵션 생성 +export async function createDocumentClassOptionItem(input: { + documentClassId: number + optionValue: string +}) { + try { + // Document Class 정보 조회하여 Value 가져오기 + const documentClass = await db + .select({ value: documentClasses.value }) + .from(documentClasses) + .where(eq(documentClasses.id, input.documentClassId)) + .limit(1) + + if (!documentClass.length) { + return { + success: false, + error: "Document Class not found" + } + } + + // Value에서 클래스명 추출 (예: "A Class" → "A") + const classValue = documentClass[0].value + const className = classValue.split(' ')[0] // "A Class"에서 "A" 추출 + + // 자동으로 optionCode 생성 (예: "A_OP_01", "A_OP_02" 등) + const existingOptions = await db + .select({ optionCode: documentClassOptions.optionCode }) + .from(documentClassOptions) + .where(eq(documentClassOptions.documentClassId, input.documentClassId)) + .orderBy(desc(documentClassOptions.optionCode)) + + let newOptionCode = `${className}_OP_01` + if (existingOptions.length > 0) { + const lastOption = existingOptions[0] + if (lastOption.optionCode) { + // "A_OP_01" 형태에서 숫자 추출 + const match = lastOption.optionCode.match(/_OP_(\d+)$/) + if (match) { + const lastNumber = parseInt(match[1]) || 0 + newOptionCode = `${className}_OP_${String(lastNumber + 1).padStart(2, '0')}` + } else { + // 기존 형식이 다른 경우 01부터 시작 + newOptionCode = `${className}_OP_01` + } + } + } + + const [newOption] = await db + .insert(documentClassOptions) + .values({ + documentClassId: input.documentClassId, + optionValue: input.optionValue, + optionCode: newOptionCode, + sortOrder: 0, + isActive: true, + }) + .returning({ id: documentClassOptions.id }) + + revalidatePath("/evcp/docu-list-rule/document-class") + + return { + success: true, + data: newOption, + message: "Document Class option created successfully" + } + } catch (error) { + console.error("Error creating document class option:", error) + return { + success: false, + error: "Failed to create document class option" + } + } +} + +// Document Class 옵션 수정 +export async function updateDocumentClassOption(input: { + id: number + optionValue: string +}) { + try { + const [updatedOption] = await db + .update(documentClassOptions) + .set({ + optionValue: input.optionValue, + updatedAt: new Date(), + }) + .where(eq(documentClassOptions.id, input.id)) + .returning({ id: documentClassOptions.id }) + + revalidatePath("/evcp/docu-list-rule/document-class") + + return { + success: true, + data: updatedOption, + message: "Document Class option updated successfully" + } + } catch (error) { + console.error("Error updating document class option:", error) + return { + success: false, + error: "Failed to update document class option" + } + } +} + +// Document Class 옵션 삭제 +export async function deleteDocumentClassOption(id: number) { + try { + const [deletedOption] = await db + .delete(documentClassOptions) + .where(eq(documentClassOptions.id, id)) + .returning({ id: documentClassOptions.id }) + + revalidatePath("/evcp/docu-list-rule/document-class") + + return { + success: true, + data: deletedOption, + message: "Document Class option deleted successfully" + } + } catch (error) { + console.error("Error deleting document class option:", error) + return { + success: false, + error: "Failed to delete document class option" + } + } +}
\ No newline at end of file |
