diff options
| -rw-r--r-- | db/schema/SWP/swp-documents.ts | 15 | ||||
| -rw-r--r-- | lib/swp/sync-service.ts | 119 |
2 files changed, 75 insertions, 59 deletions
diff --git a/db/schema/SWP/swp-documents.ts b/db/schema/SWP/swp-documents.ts index 2c7d06b0..c7661ff6 100644 --- a/db/schema/SWP/swp-documents.ts +++ b/db/schema/SWP/swp-documents.ts @@ -32,8 +32,9 @@ export const syncStatusEnum = pgEnum("swp_sync_status", [ export const swpDocuments = swpSchema.table( "swp_documents", { - // Primary Key - DOC_NO: varchar("DOC_NO", { length: 1000 }).primaryKey(), + // Composite Primary Key: DOC_NO + PROJ_NO + DOC_NO: varchar("DOC_NO", { length: 1000 }).notNull(), + PROJ_NO: varchar("PROJ_NO", { length: 1000 }).notNull(), // 문서 기본 정보 DOC_TITLE: varchar("DOC_TITLE", { length: 1000 }).notNull(), @@ -43,7 +44,6 @@ export const swpDocuments = swpSchema.table( SHI_DOC_NO: varchar("SHI_DOC_NO", { length: 1000 }), // 프로젝트 정보 - PROJ_NO: varchar("PROJ_NO", { length: 1000 }).notNull(), PROJ_NM: varchar("PROJ_NM", { length: 1000 }), PKG_NO: varchar("PKG_NO", { length: 1000 }), @@ -89,6 +89,9 @@ export const swpDocuments = swpSchema.table( updated_at: timestamp("updated_at").defaultNow().notNull(), }, (table) => ({ + // Composite Primary Key + pk: uniqueIndex("swp_documents_pk").on(table.DOC_NO, table.PROJ_NO), + // Indexes projNoIdx: index("swp_documents_proj_no_idx").on(table.PROJ_NO), vndrCdIdx: index("swp_documents_vndr_cd_idx").on(table.VNDR_CD), pkgNoIdx: index("swp_documents_pkg_no_idx").on(table.PKG_NO), @@ -107,10 +110,8 @@ export const swpDocumentRevisions = swpSchema.table( // Primary Key id: serial("id").primaryKey(), - // Foreign Key - DOC_NO: varchar("DOC_NO", { length: 1000 }) - .notNull() - .references(() => swpDocuments.DOC_NO, { onDelete: "cascade" }), + // Document Reference (NO FK - 외래키 제거) + DOC_NO: varchar("DOC_NO", { length: 1000 }).notNull(), // 리비전 정보 REV_NO: varchar("REV_NO", { length: 1000 }).notNull(), diff --git a/lib/swp/sync-service.ts b/lib/swp/sync-service.ts index 0a801bd8..787b28ae 100644 --- a/lib/swp/sync-service.ts +++ b/lib/swp/sync-service.ts @@ -9,7 +9,6 @@ import { type SwpDocumentInsert, type SwpDocumentRevisionInsert, type SwpDocumentFileInsert, - swpSchema, } from "@/db/schema/SWP/swp-documents"; // ============================================================================ @@ -17,63 +16,69 @@ import { // ============================================================================ export interface SwpDocumentApiResponse { + // 필수 필드 DOC_NO: string; DOC_TITLE: string; - DOC_GB: string; - DOC_TYPE: string; - OWN_DOC_NO: string; - SHI_DOC_NO: string; PROJ_NO: string; - PROJ_NM: string; - PKG_NO: string; - MAT_CD: string; - MAT_NM: string; - DISPLN: string; - CTGRY: string; - VNDR_CD: string; CPY_CD: string; CPY_NM: string; PIC_NM: string; - PIC_DEPTCD: string; PIC_DEPTNM: string; - LTST_REV_NO: string; - LTST_REV_SEQ: string; - LTST_ACTV_STAT: string; - STAGE: string; SKL_CD: string; - MOD_TYPE: string; - ACT_TYPE_NM: string; - USE_YN: string; CRTER: string; CRTE_DTM: string; CHGR: string; CHG_DTM: string; + + // 선택적 필드 (null 가능) + DOC_GB: string | null; + DOC_TYPE: string | null; + OWN_DOC_NO: string | null; + SHI_DOC_NO: string | null; + PROJ_NM: string | null; + PKG_NO: string | null; + MAT_CD: string | null; + MAT_NM: string | null; + DISPLN: string | null; + CTGRY: string | null; + VNDR_CD: string | null; + PIC_DEPTCD: string | null; + LTST_REV_NO: string | null; + LTST_REV_SEQ: string | null; + LTST_ACTV_STAT: string | null; + STAGE: string | null; + MOD_TYPE: string | null; + ACT_TYPE_NM: string | null; + USE_YN: string | null; REV_DTM: string | null; } export interface SwpFileApiResponse { + // 필수 필드 OWN_DOC_NO: string; REV_NO: string; STAGE: string; FILE_NM: string; FILE_SEQ: string; - FILE_SZ: string; - FLD_PATH: string; - ACTV_NO: string | null; - ACTV_SEQ: string; - BOX_SEQ: string; - OFDC_NO: string; - PROJ_NO: string; - PKG_NO: string; - VNDR_CD: string; - CPY_CD: string; - STAT: string; - STAT_NM: string; - IDX: string; CRTER: string; CRTE_DTM: string; CHGR: string; CHG_DTM: string; + + // 선택적 필드 (null 가능) + FILE_SZ: string | null; + FLD_PATH: string | null; + ACTV_NO: string | null; + ACTV_SEQ: string | null; + BOX_SEQ: string | null; + OFDC_NO: string | null; + PROJ_NO: string | null; + PKG_NO: string | null; + VNDR_CD: string | null; + CPY_CD: string | null; + STAT: string | null; + STAT_NM: string | null; + IDX: string | null; } // ============================================================================ @@ -220,13 +225,13 @@ async function upsertDocument( ): Promise<{ id: string; inserted: boolean; updated: boolean }> { const data: SwpDocumentInsert = { DOC_NO: doc.DOC_NO, + PROJ_NO: doc.PROJ_NO, DOC_TITLE: doc.DOC_TITLE, DOC_GB: doc.DOC_GB || null, DOC_TYPE: doc.DOC_TYPE || null, - OWN_DOC_NO: doc.OWN_DOC_NO, - SHI_DOC_NO: doc.SHI_DOC_NO, - PROJ_NO: doc.PROJ_NO, - PROJ_NM: doc.PROJ_NM, + OWN_DOC_NO: doc.OWN_DOC_NO || null, + SHI_DOC_NO: doc.SHI_DOC_NO || null, + PROJ_NM: doc.PROJ_NM || null, PKG_NO: doc.PKG_NO || null, MAT_CD: doc.MAT_CD || null, MAT_NM: doc.MAT_NM || null, @@ -256,11 +261,16 @@ async function upsertDocument( updated_at: new Date(), }; - // 기존 문서 확인 + // 기존 문서 확인 (복합키: DOC_NO + PROJ_NO) const existing = await tx .select() .from(swpDocuments) - .where(eq(swpDocuments.DOC_NO, doc.DOC_NO)) + .where( + and( + eq(swpDocuments.DOC_NO, doc.DOC_NO), + eq(swpDocuments.PROJ_NO, doc.PROJ_NO) + ) + ) .limit(1); if (existing.length > 0) { @@ -268,12 +278,17 @@ async function upsertDocument( await tx .update(swpDocuments) .set(data) - .where(eq(swpDocuments.DOC_NO, doc.DOC_NO)); - return { id: doc.DOC_NO, inserted: false, updated: true }; + .where( + and( + eq(swpDocuments.DOC_NO, doc.DOC_NO), + eq(swpDocuments.PROJ_NO, doc.PROJ_NO) + ) + ); + return { id: `${doc.DOC_NO}|${doc.PROJ_NO}`, inserted: false, updated: true }; } else { // 삽입 await tx.insert(swpDocuments).values(data); - return { id: doc.DOC_NO, inserted: true, updated: false }; + return { id: `${doc.DOC_NO}|${doc.PROJ_NO}`, inserted: true, updated: false }; } } @@ -287,13 +302,13 @@ async function upsertRevision( REV_NO: file.REV_NO, STAGE: file.STAGE, ACTV_NO: file.ACTV_NO || null, - ACTV_SEQ: file.ACTV_SEQ, - BOX_SEQ: file.BOX_SEQ, - OFDC_NO: file.OFDC_NO, - PROJ_NO: file.PROJ_NO, + ACTV_SEQ: file.ACTV_SEQ || null, + BOX_SEQ: file.BOX_SEQ || null, + OFDC_NO: file.OFDC_NO || null, + PROJ_NO: file.PROJ_NO || null, PKG_NO: file.PKG_NO || null, VNDR_CD: file.VNDR_CD || null, - CPY_CD: file.CPY_CD, + CPY_CD: file.CPY_CD || null, sync_status: "synced", last_synced_at: new Date(), updated_at: new Date(), @@ -339,11 +354,11 @@ async function upsertFile( DOC_NO: docNo, FILE_NM: file.FILE_NM, FILE_SEQ: file.FILE_SEQ, - FILE_SZ: file.FILE_SZ, - FLD_PATH: file.FLD_PATH, - STAT: file.STAT, - STAT_NM: file.STAT_NM, - IDX: file.IDX, + FILE_SZ: file.FILE_SZ || null, + FLD_PATH: file.FLD_PATH || null, + STAT: file.STAT || null, + STAT_NM: file.STAT_NM || null, + IDX: file.IDX || null, ACTV_NO: file.ACTV_NO || null, CRTER: file.CRTER, CRTE_DTM: file.CRTE_DTM, |
