summaryrefslogtreecommitdiff
path: root/db/schema/dolce/dolce.ts
blob: 6552d61b0088be791d59283b16ecc5cea2948170 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// 유지하지만, 미사용 중. dolce 에 바로 보내기로 합의

import { pgSchema, varchar, timestamp, jsonb, text, index, boolean, uuid, integer } from "drizzle-orm/pg-core";

export const dolceSchema = pgSchema("dolce");

/**
 * 돌체 스키마: 동기화 기능을 만들기 위해, 로컬에 임시 저장을 위한 퍼시스턴스 레이어를 구성
 * 
 * 전체적인 설명:
 * 외부 시스템인 돌체는 문서관리 시스템임.
 * 우리 시스템은 돌체와 API 를 통해, 벤더가 문서를 넣어줄 수 있게 하는 역할을 함.
 * 
 * 특정 프로젝트에 B3, B4 라는 타입으로 문서를 관리함
 * B3는 일반 벤더, B4는 GTT 라는 특정 벤더에 대한 타입임.
 * 
 * 전체적인 구조는 B3, B4가 유사함
 * - 도면리스트: 외부시스템에서 관리하므로, 우리는 받기만 함
 * 
 * - 상세도면리스트: 특정 도면에 소속된 개별 도면임.
 * Revision을 관리하기 위해 생긴 레이어이며, 개별 도면에 연결된, 파일이 첨부된 실제 수발신하는 도면 단위라고 보면 됨.
 * 외부시스템(DOLCE)에서 리스트를 API로 응답함. 개별 도면별로 응답함.
 * 우리 시스템에서 벤더가 생성한다. (생성 API를 호출함) 단, 외부 시스템 측에서도 작업해 응답 결과를 다르게 줄 수도 있음.
 * 우리 시스템에서 벤더가 수정할 수 있다. (수정 API를 호출함) 수정시에도 로컬 측에 저장하고, 동기화시 수정된 내용을 외부시스템에 전송한다.
 * 우리 시스템에서 벤더가 상세도면을 생성했을 때, 우선 우리 시스템 DB에만 생성하고, API 응답 리스트에 로컬DB 임시저장값을 추가해서 보여줄 것임.
 * 
 * - 파일리스트: 외부시스템에서 관리하며, 개별 상세도면에 첨부된 파일 리스트임.
 * 외부시스템(DOLCE)에서 특정 상세도면의 파일 리스트를 API로 조회할 수 있음. 개별 상세도면에 할당된 uploadId 별로 파일리스트를 응답함.
 * 우리 시스템에서 벤더가 파일을 추가하기도 함. 이 경우, 우선 우리 시스템 DB에 추가한 파일 정보를 임시 저장하고, API 응답 리스트에 로컬DB 임시저장값을 추가해서 보여줄 것임.
 * 
 * 동기화 기능
 * - 우리 시스템에서 상세도면 추가 및 첨부파일을 추가했던 건들을 외부시스템에 전송해주는 기능임
 * - 임시 저장시 dolce에 생성/수정 요청을 할 수 있도록 필요한 정보를 저장해야 함. 이것은 스키마의 작성 기준임
 * - 동기화 하기 이전에, 우리 시스템에서 임시로 저장한 건들은 삭제할 수 있도록 함. (soft 삭제가 아니고 hard 삭제)
 * 
 * 동기화 목록 구성 방법
 * - isSynced==false 인 건들로 목록을 구성한다.
 * - 상세도면 추가 작업 건은, 상세도면+첨부파일을 하나의 동기화 건으로 구성한다.
 * - 파일 추가 작업 건은, 개별 파일들을 동기화 건으로 구성한다.
 * - B4 Bulk 업로드 건은, 개별 파일들을 동기화 건으로 구성한다.
 * - 사용자는 본인이 임시 저장한 건들만을 선택해서 동기화할 수 있도록 한다.
 * 
 * 사용자가 로컬에 파일 업로드하는 부분의 구현
 * 1. 환경변수 DOLCE_LOCAL_UPLOAD_ABSOLUTE_DIRECTORY="/evcp/data/dolce" 가 설정되어 있음. (없으면 경로 만들기)
 * 2. 로컬에서 저장할 때는 파일 메타데이터를 별도로 저장 (로컬 파일 경로는 API 호출시 사용하지 않으나, 로컬 건들도 조회 및 다운로드가 가능해야 하므로)
 * 3. 동기화되지 않아 로컬에만 있는 건들은 파일 다운로드시 로컬에서 다운로드
 * 4. 동기화 성공한 경우, 로컬에 저장된 파일은 삭제
 */

export const dolceSyncList = dolceSchema.table("dolce_sync_list", {
  id: uuid("id").primaryKey().defaultRandom(),
  
  // [필수] 작업 유형
  // "ADD_DETAIL": 상세도면 추가 (메타데이터 + 파일)
  // "MOD_DETAIL": 상세도면 수정 (메타데이터)
  // "ADD_FILE": 기존 상세도면에 파일 추가 (파일)
  // "B4_BULK": B4 일괄 업로드
  type: varchar("type", { length: 50 }).notNull(),

  // [중요] 조회 성능 및 필터링을 위한 메타데이터 컬럼 (JSONB에서 자주 쓰는 값 추출)
  projectNo: varchar("project_no", { length: 50 }).notNull(), // 프로젝트별 조회용
  drawingNo: varchar("drawing_no", { length: 100 }),          // 특정 도면 조회용
  uploadId: varchar("upload_id", { length: 100 }),            // 업로드ID 기준 조회용
  
  userId: varchar("user_id", { length: 50 }).notNull(),       // 내 작업만 보기용
  userName: varchar("user_name", { length: 100 }),            // [추가] UI 표시용
  vendorCode: varchar("vendor_code", { length: 50 }),         // [추가] 벤더별 필터링용
  
  // [데이터]
  // 실제 API 호출에 필요한 Body 데이터 + 로컬 파일 경로 정보 포함
  // 예: { meta: { ...API Body... }, files: [{ localPath: "...", originalName: "..." }] }
  payload: jsonb("payload").notNull(), 

  // [상태]
  isSynced: boolean("is_synced").default(false).notNull(),
  syncAttempts: integer("sync_attempts").default(0).notNull(),
  lastError: text("last_error"), // 실패 사유

  // [응답]
  responseCode: varchar("response_code", { length: 50 }),
  response: text("response"),

  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
}, (table) => ({
  // 인덱스 설정: 프로젝트별 미동기화 건 조회 속도 향상
  projectIdx: index("dolce_sync_project_idx").on(table.projectNo, table.isSynced),
  userIdx: index("dolce_sync_user_idx").on(table.userId, table.isSynced),
  // [추가] 벤더별 조회 인덱스
  vendorIdx: index("dolce_sync_vendor_idx").on(table.projectNo, table.vendorCode, table.isSynced),
}));