159 lines
5.8 KiB
Plaintext
159 lines
5.8 KiB
Plaintext
// 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")
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(uuid())
|
||
phone String? @unique
|
||
appleId String? @unique @map("apple_id")
|
||
nickname String?
|
||
avatar String?
|
||
agreementAccepted Boolean @default(false) @map("agreement_accepted")
|
||
isPro Boolean @default(false) @map("is_pro") // 是否为付费会员
|
||
proExpireDate DateTime? @map("pro_expire_date") // 会员过期时间(可选,预留给订阅制)
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
settings UserSettings?
|
||
learningProgress UserLearningProgress[]
|
||
achievements UserAchievement[]
|
||
courses UserCourse[]
|
||
|
||
@@map("users")
|
||
}
|
||
|
||
model UserSettings {
|
||
userId String @id @map("user_id")
|
||
pushNotification Boolean @default(true) @map("push_notification")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@map("user_settings")
|
||
}
|
||
|
||
model Course {
|
||
id String @id @default(uuid())
|
||
title String
|
||
subtitle String? // 课程副标题
|
||
description String?
|
||
coverImage String? @map("cover_image")
|
||
type String @default("system") // ✅ 新增:system | single
|
||
status String @default("published") // ✅ 新增:published | draft
|
||
deletedAt DateTime? @map("deleted_at") // ✅ 新增:软删除时间戳
|
||
totalNodes Int @default(0) @map("total_nodes")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
chapters CourseChapter[]
|
||
nodes CourseNode[]
|
||
userCourses UserCourse[]
|
||
|
||
@@map("courses")
|
||
}
|
||
|
||
model CourseChapter {
|
||
id String @id @default(uuid())
|
||
courseId String @map("course_id")
|
||
title String
|
||
orderIndex Int @map("order_index")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
||
nodes CourseNode[]
|
||
|
||
@@unique([courseId, orderIndex])
|
||
@@map("course_chapters")
|
||
}
|
||
|
||
model CourseNode {
|
||
id String @id @default(uuid())
|
||
courseId String @map("course_id")
|
||
chapterId String? @map("chapter_id") // 可选,支持无章节的节点
|
||
title String
|
||
subtitle String?
|
||
orderIndex Int @map("order_index")
|
||
duration Int? // 预估时长(分钟)
|
||
unlockCondition String? @map("unlock_condition") // 解锁条件
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
||
chapter CourseChapter? @relation(fields: [chapterId], references: [id], onDelete: SetNull)
|
||
slides NodeSlide[]
|
||
learningProgress UserLearningProgress[]
|
||
|
||
@@unique([courseId, orderIndex])
|
||
@@map("course_nodes")
|
||
}
|
||
|
||
model NodeSlide {
|
||
id String @id @default(uuid())
|
||
nodeId String @map("node_id")
|
||
slideType String @map("slide_type") // text | image | quiz | interactive
|
||
orderIndex Int @map("order_index")
|
||
content Json // 存储卡片内容(灵活结构)
|
||
effect String? // fade_in | typewriter | slide_up
|
||
interaction String? // tap_to_reveal | zoom | parallax
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
node CourseNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
||
|
||
@@map("node_slides")
|
||
}
|
||
|
||
model UserLearningProgress {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
nodeId String @map("node_id")
|
||
status String @default("not_started") // not_started | in_progress | completed
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
totalStudyTime Int @default(0) @map("total_study_time") // 总学习时长(秒)
|
||
currentSlide Int @default(0) @map("current_slide") // 当前学习到的幻灯片位置
|
||
completionRate Int @default(0) @map("completion_rate") // 完成度(%)
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
node CourseNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([userId, nodeId])
|
||
@@map("user_learning_progress")
|
||
}
|
||
|
||
model UserAchievement {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
achievementType String @map("achievement_type") // lesson_completed | course_completed
|
||
achievementData Json @map("achievement_data") // 存储成就详情
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@map("user_achievements")
|
||
}
|
||
|
||
model UserCourse {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
courseId String @map("course_id")
|
||
lastOpenedAt DateTime? @map("last_opened_at") // ✅ 新增:最近打开时间,用于排序
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([userId, courseId])
|
||
@@map("user_courses")
|
||
}
|