elearning/Backend/prisma/schema.prisma
2026-01-08 06:51:33 +00:00

354 lines
9.9 KiB
Text

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ============================================
// User Management
// ============================================
model Role {
id Int @id @default(autoincrement())
code String @unique @db.VarChar(50)
name Json // { th: "", en: "" }
description Json?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
users User[]
@@map("roles")
}
model User {
id Int @id @default(autoincrement())
username String @unique @db.VarChar(50)
email String @unique @db.VarChar(255)
password String @db.VarChar(255)
role_id Int
is_active Boolean @default(true)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
role Role @relation(fields: [role_id], references: [id])
profile Profile?
// Relations
created_courses Course[] @relation("CourseCreator")
instructor_courses CourseInstructor[]
enrollments Enrollment[]
lesson_progress LessonProgress[]
quiz_attempts QuizAttempt[]
certificates Certificate[]
@@index([email])
@@index([role_id])
@@map("users")
}
model Profile {
id Int @id @default(autoincrement())
user_id Int @unique
first_name String? @db.VarChar(100)
last_name String? @db.VarChar(100)
phone String? @db.VarChar(20)
avatar_url String? @db.VarChar(500)
bio Json? // { th: "", en: "" }
birth_date DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@map("profiles")
}
// ============================================
// Course Management
// ============================================
enum CourseStatus {
DRAFT
PENDING_APPROVAL
APPROVED
REJECTED
ARCHIVED
}
model Category {
id Int @id @default(autoincrement())
code String @unique @db.VarChar(50)
name Json // { th: "", en: "" }
description Json?
icon_url String? @db.VarChar(500)
is_active Boolean @default(true)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
courses Course[]
@@map("categories")
}
model Course {
id Int @id @default(autoincrement())
title Json // { th: "", en: "" }
description Json
thumbnail_url String? @db.VarChar(500)
price Decimal @default(0) @db.Decimal(10, 2)
is_free Boolean @default(false)
have_certificate Boolean @default(false)
status CourseStatus @default(DRAFT)
category_id Int
created_by Int
rejection_reason String? @db.Text
is_deleted Boolean @default(false)
deleted_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
category Category @relation(fields: [category_id], references: [id])
creator User @relation("CourseCreator", fields: [created_by], references: [id])
chapters Chapter[]
instructors CourseInstructor[]
enrollments Enrollment[]
@@index([category_id])
@@index([status])
@@index([created_by])
@@index([created_at])
@@map("courses")
}
model CourseInstructor {
id Int @id @default(autoincrement())
course_id Int
user_id Int
is_primary Boolean @default(false)
created_at DateTime @default(now())
course Course @relation(fields: [course_id], references: [id], onDelete: Cascade)
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@unique([course_id, user_id])
@@index([course_id])
@@index([user_id])
@@map("course_instructors")
}
model Chapter {
id Int @id @default(autoincrement())
course_id Int
title Json // { th: "", en: "" }
description Json?
order Int
created_at DateTime @default(now())
updated_at DateTime @updatedAt
course Course @relation(fields: [course_id], references: [id], onDelete: Cascade)
lessons Lesson[]
@@index([course_id])
@@index([order])
@@map("chapters")
}
// ============================================
// Lessons
// ============================================
enum LessonType {
VIDEO
TEXT
PDF
QUIZ
}
model Lesson {
id Int @id @default(autoincrement())
chapter_id Int
title Json // { th: "", en: "" }
description Json?
type LessonType
content Json? // For TEXT type
video_url String? @db.VarChar(500)
video_duration Int? // in seconds
order Int
is_preview Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
chapter Chapter @relation(fields: [chapter_id], references: [id], onDelete: Cascade)
attachments Attachment[]
prerequisites LessonPrerequisite[] @relation("LessonPrerequisites")
required_for LessonPrerequisite[] @relation("RequiredForLessons")
quiz Quiz?
progress LessonProgress[]
@@index([chapter_id])
@@index([order])
@@map("lessons")
}
model LessonPrerequisite {
id Int @id @default(autoincrement())
lesson_id Int
prerequisite_lesson_id Int
created_at DateTime @default(now())
lesson Lesson @relation("LessonPrerequisites", fields: [lesson_id], references: [id], onDelete: Cascade)
prerequisite_lesson Lesson @relation("RequiredForLessons", fields: [prerequisite_lesson_id], references: [id], onDelete: Cascade)
@@unique([lesson_id, prerequisite_lesson_id])
@@index([lesson_id])
@@index([prerequisite_lesson_id])
@@map("lesson_prerequisites")
}
model Attachment {
id Int @id @default(autoincrement())
lesson_id Int
filename String @db.VarChar(255)
original_name String @db.VarChar(255)
file_url String @db.VarChar(500)
file_size BigInt
mime_type String @db.VarChar(100)
created_at DateTime @default(now())
lesson Lesson @relation(fields: [lesson_id], references: [id], onDelete: Cascade)
@@index([lesson_id])
@@map("attachments")
}
// ============================================
// Quiz System
// ============================================
enum ScorePolicy {
HIGHEST
LATEST
AVERAGE
}
model Quiz {
id Int @id @default(autoincrement())
lesson_id Int @unique
title Json // { th: "", en: "" }
description Json?
passing_score Int @default(70)
time_limit Int? // in minutes
max_attempts Int @default(3)
cooldown_hours Int @default(24)
score_policy ScorePolicy @default(HIGHEST)
shuffle_questions Boolean @default(true)
shuffle_choices Boolean @default(true)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
lesson Lesson @relation(fields: [lesson_id], references: [id], onDelete: Cascade)
questions QuizQuestion[]
attempts QuizAttempt[]
@@map("quizzes")
}
model QuizQuestion {
id Int @id @default(autoincrement())
quiz_id Int
question Json // { th: "", en: "" }
choices Json // [{ th: "", en: "", is_correct: boolean }]
points Int @default(1)
order Int
created_at DateTime @default(now())
updated_at DateTime @updatedAt
quiz Quiz @relation(fields: [quiz_id], references: [id], onDelete: Cascade)
@@index([quiz_id])
@@index([order])
@@map("quiz_questions")
}
model QuizAttempt {
id Int @id @default(autoincrement())
quiz_id Int
user_id Int
answers Json // [{ question_id: int, choice_index: int }]
score Int
passed Boolean
time_spent Int? // in seconds
started_at DateTime
completed_at DateTime @default(now())
quiz Quiz @relation(fields: [quiz_id], references: [id], onDelete: Cascade)
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@index([quiz_id])
@@index([user_id])
@@index([completed_at])
@@map("quiz_attempts")
}
// ============================================
// Progress Tracking
// ============================================
model Enrollment {
id Int @id @default(autoincrement())
user_id Int
course_id Int
enrolled_at DateTime @default(now())
completed_at DateTime?
progress_percent Int @default(0)
last_accessed_at DateTime @default(now())
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
course Course @relation(fields: [course_id], references: [id], onDelete: Cascade)
@@unique([user_id, course_id])
@@index([user_id])
@@index([course_id])
@@map("enrollments")
}
model LessonProgress {
id Int @id @default(autoincrement())
user_id Int
lesson_id Int
is_completed Boolean @default(false)
video_progress Int @default(0) // seconds watched
completed_at DateTime?
last_watched_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @updatedAt
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
lesson Lesson @relation(fields: [lesson_id], references: [id], onDelete: Cascade)
@@unique([user_id, lesson_id])
@@index([user_id])
@@index([lesson_id])
@@map("lesson_progress")
}
model Certificate {
id Int @id @default(autoincrement())
user_id Int
course_id Int
certificate_url String @db.VarChar(500)
issued_at DateTime @default(now())
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@unique([user_id, course_id])
@@index([user_id])
@@map("certificates")
}