// 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" } // generator zod { // provider = "zod-prisma-types" // output = "../src/generated" // (default) the directory where generated zod schemas will be saved // createModelTypes = true // createRelationValuesTypes = true // writeNullishInModelTypes = true // createPartialTypes = false // useMultipleFiles = false // useTypeAssertions = true // } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model Taxonomy { id String @id @default(cuid()) name String @unique slug String @unique @map("slug") deletedAt DateTime? @map("deleted_at") createdAt DateTime @default(now()) @map("created_at") terms Term[] objectType String[] @map("object_type") order Float? @map("order") @@index([order, deletedAt]) @@map("taxonomy") } model Term { id String @id @default(cuid()) name String taxonomy Taxonomy? @relation(fields: [taxonomyId], references: [id]) taxonomyId String? @map("taxonomy_id") order Float? @map("order") description String? parentId String? @map("parent_id") parent Term? @relation("ChildParent", fields: [parentId], references: [id], onDelete: Cascade) children Term[] @relation("ChildParent") ancestors TermAncestry[] @relation("DescendantToAncestor") descendants TermAncestry[] @relation("AncestorToDescendant") domainId String? @map("domain_id") domain Department? @relation("TermDom", fields: [domainId], references: [id]) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") createdBy String? @map("created_by") depts Department[] @relation("department_term") hasChildren Boolean? @default(false) @map("has_children") courses Course[] @relation("course_term") @@index([name]) // 对name字段建立索引,以加快基于name的查找速度 @@index([parentId]) // 对parentId字段建立索引,以加快基于parentId的查找速度 @@map("term") } model TermAncestry { id String @id @default(cuid()) ancestorId String? @map("ancestor_id") descendantId String @map("descendant_id") relDepth Int @map("rel_depth") ancestor Term? @relation("AncestorToDescendant", fields: [ancestorId], references: [id]) descendant Term @relation("DescendantToAncestor", fields: [descendantId], references: [id]) // 索引建议 @@index([ancestorId]) // 针对祖先的查询 @@index([descendantId]) // 针对后代的查询 @@index([ancestorId, descendantId]) // 组合索引,用于查询特定的祖先-后代关系 @@index([relDepth]) // 根据关系深度的查询 @@map("term_ancestry") } model Staff { id String @id @default(cuid()) showname String? @map("showname") username String @unique @map("username") avatar String? @map("avatar") password String? @map("password") phoneNumber String? @unique @map("phone_number") domainId String? @map("domain_id") deptId String? @map("dept_id") domain Department? @relation("DomainStaff", fields: [domainId], references: [id]) department Department? @relation("DeptStaff", fields: [deptId], references: [id]) order Float? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") enabled Boolean? @default(true) deletedAt DateTime? @map("deleted_at") officerId String? @map("officer_id") watchedPost Post[] @relation("post_watch_staff") visits Visit[] posts Post[] sentMsgs Message[] @relation("message_sender") receivedMsgs Message[] @relation("message_receiver") enrollments Enrollment[] courseReviews CourseReview[] teachedCourses CourseInstructor[] @@index([officerId]) @@index([deptId]) @@index([domainId]) @@index([username]) @@index([order]) @@map("staff") } model Department { id String @id @default(cuid()) name String order Float? ancestors DeptAncestry[] @relation("DescendantToAncestor") descendants DeptAncestry[] @relation("AncestorToDescendant") parentId String? @map("parent_id") parent Department? @relation("ChildParent", fields: [parentId], references: [id]) children Department[] @relation("ChildParent") domainId String? @map("domain_id") domainTerms Term[] @relation("TermDom") deletedAt DateTime? @map("deleted_at") isDomain Boolean? @default(false) @map("is_domain") domainStaffs Staff[] @relation("DomainStaff") deptStaffs Staff[] @relation("DeptStaff") terms Term[] @relation("department_term") watchedPost Post[] @relation("post_watch_dept") hasChildren Boolean? @default(false) @map("has_children") @@index([parentId]) @@index([isDomain]) @@index([name]) @@index([order]) @@map("department") } model DeptAncestry { id String @id @default(cuid()) ancestorId String? @map("ancestor_id") descendantId String @map("descendant_id") relDepth Int @map("rel_depth") ancestor Department? @relation("AncestorToDescendant", fields: [ancestorId], references: [id]) descendant Department @relation("DescendantToAncestor", fields: [descendantId], references: [id]) // 索引建议 @@index([ancestorId]) // 针对祖先的查询 @@index([descendantId]) // 针对后代的查询 @@index([ancestorId, descendantId]) // 组合索引,用于查询特定的祖先-后代关系 @@index([relDepth]) // 根据关系深度的查询 @@map("dept_ancestry") } model RoleMap { id String @id @default(cuid()) objectId String @map("object_id") roleId String @map("role_id") domainId String? @map("domain_id") objectType String @map("object_type") role Role @relation(fields: [roleId], references: [id]) @@index([domainId]) @@index([objectId]) @@map("rolemap") } model Role { id String @id @default(cuid()) name String @unique @map("name") permissions String[] @default([]) @map("permissions") roleMaps RoleMap[] system Boolean? @default(false) @map("system") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") @@map("role") } model AppConfig { id String @id @default(cuid()) slug String @unique title String? description String? meta Json? @@map("app_config") } model Post { id String @id @default(cuid()) type String? title String? content String? author Staff? @relation(fields: [authorId], references: [id]) authorId String? domainId String? referenceId String? attachments String[] @default([]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt visits Visit[] watchableStaffs Staff[] @relation("post_watch_staff") watchableDepts Department[] @relation("post_watch_dept") parentId String? parent Post? @relation("PostChildren", fields: [parentId], references: [id]) children Post[] @relation("PostChildren") deletedAt DateTime? @map("deleted_at") Lecture Lecture? @relation(fields: [lectureId], references: [id]) lectureId String? // 复合索引 @@index([type, domainId]) // 类型和域组合查询 @@index([authorId, type]) // 作者和类型组合查询 @@index([referenceId, type]) // 引用ID和类型组合查询 @@index([parentId, type]) // 父级帖子和创建时间索引 // 时间相关索引 @@index([createdAt]) // 按创建时间倒序索引 @@index([updatedAt]) // 按更新时间倒序索引 } model Message { id String @id @default(cuid()) url String? intent String? option Json? senderId String? @map("sender_id") messageType String? sender Staff? @relation(name: "message_sender", fields: [senderId], references: [id]) title String? content String? receivers Staff[] @relation("message_receiver") visits Visit[] createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime? @updatedAt @map("updated_at") @@index([messageType, createdAt]) @@map("message") } model Visit { id String @id @default(cuid()) visitType String? @map("visit_type") visitorId String @map("visitor_id") visitor Staff @relation(fields: [visitorId], references: [id]) postId String? @map("post_id") post Post? @relation(fields: [postId], references: [id]) message Message? @relation(fields: [messageId], references: [id]) messageId String? @map("message_id") views Int @default(1) createdAt DateTime? @default(now()) @map("created_at") updatedAt DateTime? @updatedAt @map("updated_at") sourceIP String? @map("source_ip") @@index([postId, visitType, visitorId]) @@index([messageId, visitType, visitorId]) @@map("visit") } model Course { id String @id @default(cuid()) // 课程唯一标识符 title String // 课程标题 subTitle String? // 课程副标题(可选) description String // 课程详细描述 thumbnail String? // 课程封面图片URL(可选) level String // 课程难度等级 // 课程内容组织结构 terms Term[] @relation("course_term") // 课程学期 instructors CourseInstructor[] // 课程讲师团队 sections Section[] // 课程章节结构 enrollments Enrollment[] // 学生报名记录 reviews CourseReview[] // 学员课程评价 // 课程规划与目标设定 requirements String[] // 课程学习前置要求 objectives String[] // 具体的学习目标 skills String[] // 课程结束后可掌握的技能 audiences String[] // 目标受众群体描述 // 课程统计指标 totalDuration Int @default(0) // 课程总时长(分钟) totalLectures Int @default(0) // 总课时数 averageRating Float @default(0) // 平均评分(1-5分) numberOfReviews Int @default(0) // 评价总数 numberOfStudents Int @default(0) // 学习人数 completionRate Float @default(0) // 完课率(0-100%) // 课程状态管理 status String // 课程状态(如:草稿/已发布/已归档) isFeatured Boolean @default(false) // 是否为精选推荐课程 // 生命周期时间戳 createdAt DateTime @default(now()) // 创建时间 updatedAt DateTime @updatedAt // 最后更新时间 publishedAt DateTime? // 发布时间 archivedAt DateTime? // 归档时间 deletedAt DateTime? // 软删除时间 // 数据库索引优化 @@index([status]) // 课程状态索引,用于快速筛选 @@index([level]) // 难度等级索引,用于分类查询 @@index([isFeatured]) // 精选标记索引,用于首页推荐 } model Section { id String @id @default(cuid()) // 章节唯一标识符 title String // 章节标题 description String? // 章节描述(可选) objectives String[] // 本章节的具体学习目标 order Float? @default(0) // 章节排序权重 // 关联关系 course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) courseId String // 所属课程ID lectures Lecture[] // 包含的所有课时 // 章节统计数据 totalDuration Int @default(0) // 本章节总时长(分钟) totalLectures Int @default(0) // 本章节课时总数 // 时间管理 createdAt DateTime @default(now()) // 创建时间 updatedAt DateTime @updatedAt // 更新时间 deletedAt DateTime? // 软删除时间 @@index([courseId, order]) // 复合索引:用于按课程ID和顺序快速查询 } model Lecture { id String @id @default(cuid()) // 课时唯一标识符 title String // 课时标题 description String? // 课时描述(可选) order Float? @default(0) // 课时排序权重 duration Int // 学习时长(分钟) type String // 课时类型(video/article) // 课时内容 content String? // Markdown格式文章内容 videoUrl String? // 视频URL地址 videoThumbnail String? // 视频封面图URL // 关联内容 resources Resource[] // 课时附属资源 section Section @relation(fields: [sectionId], references: [id], onDelete: Cascade) sectionId String // 所属章节ID comments Post[] // 课时评论 progress LectureProgress[] // 学习进度记录 // 时间管理 publishedAt DateTime? // 发布时间 createdAt DateTime @default(now()) // 创建时间 updatedAt DateTime @updatedAt // 更新时间 deletedAt DateTime? // 软删除时间 @@index([sectionId, order]) // 章节内课时排序索引 @@index([type, publishedAt]) // 课时类型和发布时间复合索引 } model Enrollment { id String @id @default(cuid()) // 报名记录唯一标识符 status String // 报名状态(如:进行中/已完成/已过期) // 关联关系 student Staff @relation(fields: [studentId], references: [id]) studentId String // 学员ID course Course @relation(fields: [courseId], references: [id]) courseId String // 课程ID progress LectureProgress[] // 课时学习进度记录 // 学习数据统计 completionRate Float @default(0) // 课程完成度(0-100%) lastAccessedAt DateTime? // 最后访问时间 // 时间管理 createdAt DateTime @default(now()) // 报名时间 updatedAt DateTime @updatedAt // 更新时间 completedAt DateTime? // 完课时间 @@unique([studentId, courseId]) // 确保学员不会重复报名同一课程 @@index([status]) // 报名状态索引 @@index([completedAt]) // 完课时间索引 } model LectureProgress { id String @id @default(cuid()) // 进度记录唯一标识符 progress Float @default(0) // 完成进度(0-100%) isCompleted Boolean @default(false) // 是否完成 // 关联关系 enrollment Enrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade) enrollmentId String // 报名记录ID lecture Lecture @relation(fields: [lectureId], references: [id], onDelete: Cascade) lectureId String // 课时ID // 学习数据 lastPosition Int @default(0) // 视频播放位置(秒) viewCount Int @default(0) // 观看次数 readCount Int @default(0) // 阅读次数 totalWatchTime Int @default(0) // 总观看时长(秒) // 时间记录 lastWatchedAt DateTime? // 最后观看时间 createdAt DateTime @default(now()) // 创建时间 updatedAt DateTime @updatedAt // 更新时间 @@unique([enrollmentId, lectureId]) // 确保每个报名只有一条课时进度 @@index([isCompleted]) // 完成状态索引 @@index([lastWatchedAt]) // 最后观看时间索引 } model CourseInstructor { course Course @relation(fields: [courseId], references: [id]) courseId String // 课程ID instructor Staff @relation(fields: [instructorId], references: [id]) instructorId String // 讲师ID role String // 讲师角色 createdAt DateTime @default(now()) // 创建时间 order Float? @default(0) // 讲师显示顺序 @@id([courseId, instructorId]) // 联合主键 } model CourseReview { id String @id @default(cuid()) // 评价唯一标识符 rating Int // 评分(1-5星) content String? // 评价内容 student Staff @relation(fields: [studentId], references: [id]) studentId String // 评价学员ID course Course @relation(fields: [courseId], references: [id]) courseId String // 课程ID helpfulCount Int @default(0) // 评价点赞数 createdAt DateTime @default(now()) // 评价时间 updatedAt DateTime @updatedAt // 更新时间 @@unique([studentId, courseId]) // 确保学员对同一课程只能评价一次 @@index([rating]) // 评分索引 } model Resource { id String @id @default(cuid()) // 资源唯一标识符 title String // 资源标题 description String? // 资源描述 type String // 资源类型 url String // 资源URL fileType String? // 文件MIME类型 fileSize Int? // 文件大小(bytes) lecture Lecture @relation(fields: [lectureId], references: [id], onDelete: Cascade) lectureId String // 所属课时ID downloadCount Int @default(0) // 下载次数 createdAt DateTime @default(now()) // 创建时间 updatedAt DateTime @updatedAt // 更新时间 @@index([lectureId, type]) // 课时资源类型复合索引 } model Node { id String @id @default(cuid()) title String description String? type String // 节点之间的关系 sourceEdges NodeEdge[] @relation("from_node") targetEdges NodeEdge[] @relation("to_node") style Json? position Json? // 存储节点在画布中的位置 {x: number, y: number} data Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } // 节点之间的关系 model NodeEdge { id String @id @default(cuid()) // 关系的起点和终点 source Node @relation("from_node", fields: [sourceId], references: [id], onDelete: Cascade) sourceId String target Node @relation("to_node", fields: [targetId], references: [id], onDelete: Cascade) targetId String // 关系属性 type String? label String? description String? // 自定义边的样式(可选) style Json? // 存储边的样式,如 {color: string, strokeWidth: number} createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([sourceId, targetId, type]) @@index([sourceId]) @@index([targetId]) }