generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ─── 사용자 ────────────────────────────────────────────────── model User { id String @id @default(cuid()) email String @unique password String name String role Role @default(MEMBER) department String? isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdTasks Task[] @relation("CreatedTasks") assignedTasks Task[] @relation("AssignedTasks") taskDetails TaskDetail[] uploadedFiles File[] auditLogs AuditLog[] @@map("users") } // ─── 팀 인원 (조직도 마스터) ───────────────────────────────── model TeamMember { id String @id @default(cuid()) name String rank String? // 직급 (수석연구원, 선임연구원 등) role String? // 직책 (팀장, 셀장, 팀원 등) cell String? // 셀 (HR, 총무, 리더 — 리더/빈값이면 상단 팀장 영역) contact String? photoUrl String? sortOrder Int @default(0) isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt pmTasks Task[] @relation("PmTasks") taskAssignees TaskAssignee[] milestonePmTasks Milestone[] @relation("MilestonePm") milestoneAssignees MilestoneAssignee[] @@index([cell]) @@index([isActive]) @@map("team_members") } enum Role { ADMIN MANAGER MEMBER } // ─── 업무 ──────────────────────────────────────────────────── model Task { id String @id @default(cuid()) title String description String? status TaskStatus @default(TODO) priority Priority @default(MEDIUM) quarter String // 예: "2026-Q2" category String? section String? // HR | 운영관리 tag String? // Growth | Policy | Performance | Culture | Asset | Space | Safety | Environment taskType String? // 상시업무 | 프로젝트 progress Int @default(0) issueNote String? issueEntries Json? startDate DateTime? dueDate DateTime? showDate Boolean @default(true) showDescription Boolean @default(true) showStatus Boolean @default(true) showIssue Boolean @default(true) showProgress Boolean @default(true) creatorId String assigneeId String? pmMemberId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt creator User @relation("CreatedTasks", fields: [creatorId], references: [id]) assignee User? @relation("AssignedTasks", fields: [assigneeId], references: [id]) pmMember TeamMember? @relation("PmTasks", fields: [pmMemberId], references: [id]) taskAssignees TaskAssignee[] details TaskDetail[] kpiMetrics KpiMetric[] files File[] milestones Milestone[] @@index([quarter]) @@index([status]) @@index([assigneeId]) @@index([pmMemberId]) @@index([section]) @@map("tasks") } model TaskAssignee { taskId String memberId String task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) member TeamMember @relation(fields: [memberId], references: [id], onDelete: Cascade) @@id([taskId, memberId]) @@index([memberId]) @@map("task_assignees") } enum TaskStatus { TODO IN_PROGRESS REVIEW DONE CANCELLED } enum Priority { LOW MEDIUM HIGH URGENT } // ─── 업무 상세 / 진행 기록 ──────────────────────────────────── model TaskDetail { id String @id @default(cuid()) taskId String milestoneId String? content String authorName String? // 피드백 작성자 표시명 (자유 입력) updatedBy String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) milestone Milestone? @relation(fields: [milestoneId], references: [id], onDelete: Cascade) author User @relation(fields: [updatedBy], references: [id]) @@index([taskId]) @@index([milestoneId]) @@map("task_details") } // ─── KPI 지표 ──────────────────────────────────────────────── model KpiMetric { id String @id @default(cuid()) taskId String quarter String target Float actual Float @default(0) unit String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) @@index([quarter]) @@map("kpi_metrics") } // ─── 파일 ──────────────────────────────────────────────────── model File { id String @id @default(cuid()) taskId String milestoneId String? filename String // 저장된 파일명 (UUID) originalName String // 원본 파일명 displayName String? // 표시명 (없으면 originalName 사용) sortOrder Int @default(0) mimetype String size Int path String uploadedBy String createdAt DateTime @default(now()) task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) milestone Milestone? @relation(fields: [milestoneId], references: [id], onDelete: SetNull) uploader User @relation(fields: [uploadedBy], references: [id]) @@index([taskId]) @@index([milestoneId]) @@map("files") } // ─── 마일스톤 (프로세스 단계) ───────────────────────────────── model Milestone { id String @id @default(cuid()) taskId String title String subtitle String? description String? startDate DateTime? dueDate DateTime? periodEntries Json? progress Int @default(0) links String? // JSON: [{ "label": string, "url": string }] completedAt DateTime? order Int @default(0) pmMemberId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) pmMember TeamMember? @relation("MilestonePm", fields: [pmMemberId], references: [id]) milestoneAssignees MilestoneAssignee[] details TaskDetail[] files File[] @@index([taskId]) @@index([pmMemberId]) @@map("milestones") } model MilestoneAssignee { milestoneId String memberId String milestone Milestone @relation(fields: [milestoneId], references: [id], onDelete: Cascade) member TeamMember @relation(fields: [memberId], references: [id], onDelete: Cascade) @@id([milestoneId, memberId]) @@index([memberId]) @@map("milestone_assignees") } // ─── 컬럼 설정 ─────────────────────────────────────────────── model ColumnConfig { key String @id // "HR" | "운영관리" title String titleEn String? subtitle String? cardOrder String? // JSON 배열: task id 순서 updatedAt DateTime @updatedAt @@map("column_configs") } // ─── 허브 설정 (분기 중점 과제·일정·상시 라벨) ───────────────── model HubConfig { id String @id @default("default") config Json updatedAt DateTime @updatedAt @@map("hub_configs") } // ─── 감사 로그 ─────────────────────────────────────────────── model AuditLog { id String @id @default(cuid()) userId String action String // CREATE, UPDATE, DELETE, LOGIN 등 entity String // Task, User, File 등 entityId String? details Json? ipAddress String? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) @@index([userId]) @@index([createdAt]) @@map("audit_logs") }