init Backend
This commit is contained in:
parent
08a4e0d8fa
commit
924000b084
29 changed files with 10080 additions and 13 deletions
354
Backend/prisma/schema.prisma
Normal file
354
Backend/prisma/schema.prisma
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
// 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")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue