// prisma/schema-v2.prisma - 重构版数据模型 // 创建时间:2026-02-27 // 基于设计文档:21-数据模型重构设计.md // // 迁移说明: // 1. 本文件为新版数据模型,与原 schema.prisma 共存 // 2. 采用渐进式迁移策略,保留旧表以确保向后兼容 // 3. 迁移完成后可将旧表标记为废弃 generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } // ==================== 主题字典(新增) ==================== model Theme { id Int @id @default(autoincrement()) name String @unique description String? sortOrder Int @default(0) @map("sort_order") status String @default("ACTIVE") // ACTIVE, ARCHIVED createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") courses Course[] @@index([sortOrder]) @@map("themes") } // ==================== 课程套餐(新增) ==================== model CoursePackage { id Int @id @default(autoincrement()) name String description String? price Int @default(0) discountPrice Int? @map("discount_price") discountType String? @map("discount_type") gradeLevels String @default("[]") @map("grade_levels") status String @default("DRAFT") submittedAt DateTime? @map("submitted_at") submittedBy Int? @map("submitted_by") reviewedAt DateTime? @map("reviewed_at") reviewedBy Int? @map("reviewed_by") reviewComment String? @map("review_comment") courseCount Int @default(0) @map("course_count") tenantCount Int @default(0) @map("tenant_count") createdBy Int? @map("created_by") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") publishedAt DateTime? @map("published_at") courses CoursePackageCourse[] tenantPackages TenantPackage[] @@index([status]) @@map("course_packages") } // ==================== 套餐-课程包关联(新增) ==================== model CoursePackageCourse { id Int @id @default(autoincrement()) packageId Int @map("package_id") courseId Int @map("course_id") gradeLevel String @map("grade_level") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") package CoursePackage @relation(fields: [packageId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@unique([packageId, courseId]) @@index([packageId, gradeLevel]) @@map("course_package_courses") } // ==================== 租户-套餐授权(新增) ==================== model TenantPackage { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") packageId Int @map("package_id") startDate String @map("start_date") endDate String @map("end_date") status String @default("ACTIVE") pricePaid Int @default(0) @map("price_paid") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) package CoursePackage @relation(fields: [packageId], references: [id], onDelete: Cascade) @@unique([tenantId, packageId]) @@index([tenantId, status]) @@map("tenant_packages") } // ==================== 租户 ==================== model Tenant { id Int @id @default(autoincrement()) name String loginAccount String? @unique @map("login_account") passwordHash String? @map("password_hash") address String? contactPerson String? @map("contact_person") contactPhone String? @map("contact_phone") logoUrl String? @map("logo_url") packageType String @default("STANDARD") @map("package_type") teacherQuota Int @default(20) @map("teacher_quota") studentQuota Int @default(200) @map("student_quota") storageQuota BigInt @default(5368709120) @map("storage_quota") startDate String @map("start_date") expireDate String @map("expire_date") teacherCount Int @default(0) @map("teacher_count") studentCount Int @default(0) @map("student_count") storageUsed BigInt @default(0) @map("storage_used") status String @default("ACTIVE") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") teachers Teacher[] students Student[] classes Class[] lessons Lesson[] tenantCourses TenantCourse[] growthRecords GrowthRecord[] tasks Task[] taskTemplates TaskTemplate[] parents Parent[] notifications Notification[] settings SystemSettings? schedulePlans SchedulePlan[] scheduleTemplates ScheduleTemplate[] operationLogs OperationLog[] // 新增关联 packages TenantPackage[] schoolCourses SchoolCourse[] @@map("tenants") } // ==================== 教师 ==================== model Teacher { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") name String phone String email String? loginAccount String @unique @map("login_account") passwordHash String @map("password_hash") classIds String? @default("[]") @map("class_ids") status String @default("ACTIVE") lessonCount Int @default(0) @map("lesson_count") feedbackCount Int @default(0) @map("feedback_count") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") lastLoginAt DateTime? @map("last_login_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) classes Class[] lessons Lesson[] feedbacks LessonFeedback[] classTeachers ClassTeacher[] schedulePlans SchedulePlan[] scheduleTemplates ScheduleTemplate[] @@map("teachers") } // ==================== 班级 ==================== model Class { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") name String grade String teacherId Int? @map("teacher_id") studentCount Int @default(0) @map("student_count") lessonCount Int @default(0) @map("lesson_count") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) teacher Teacher? @relation(fields: [teacherId], references: [id]) students Student[] lessons Lesson[] growthRecords GrowthRecord[] classTeachers ClassTeacher[] studentHistory StudentClassHistory[] @relation("ToClass") fromHistory StudentClassHistory[] @relation("FromClass") schedulePlans SchedulePlan[] scheduleTemplates ScheduleTemplate[] @@map("classes") } // ==================== 学生 ==================== model Student { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") classId Int @map("class_id") name String gender String? birthDate DateTime? @map("birth_date") parentPhone String? @map("parent_phone") parentName String? @map("parent_name") readingCount Int @default(0) @map("reading_count") lessonCount Int @default(0) @map("lesson_count") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) class Class @relation(fields: [classId], references: [id], onDelete: Cascade) records StudentRecord[] growthRecords GrowthRecord[] taskCompletions TaskCompletion[] parents ParentStudent[] classHistory StudentClassHistory[] @@map("students") } // ==================== 课程包(重构) ==================== model Course { id Int @id @default(autoincrement()) // 新增字段 themeId Int? @map("theme_id") coreContent String? @map("core_content") introSummary String? @map("intro_summary") introHighlights String? @map("intro_highlights") introGoals String? @map("intro_goals") introSchedule String? @map("intro_schedule") introKeyPoints String? @map("intro_key_points") introMethods String? @map("intro_methods") introEvaluation String? @map("intro_evaluation") introNotes String? @map("intro_notes") scheduleRefData String? @map("schedule_ref_data") hasCollectiveLesson Boolean @default(false) @map("has_collective_lesson") // 保留字段 name String description String? pictureBookId Int? @map("picture_book_id") pictureBookName String? @map("picture_book_name") coverImagePath String? @map("cover_image_path") ebookPaths String? @map("ebook_paths") audioPaths String? @map("audio_paths") videoPaths String? @map("video_paths") otherResources String? @map("other_resources") pptPath String? @map("ppt_path") pptName String? @map("ppt_name") posterPaths String? @map("poster_paths") tools String? @map("tools") studentMaterials String? @map("student_materials") lessonPlanData String? @map("lesson_plan_data") activitiesData String? @map("activities_data") assessmentData String? @map("assessment_data") gradeTags String @default("[]") @map("grade_tags") domainTags String @default("[]") @map("domain_tags") duration Int @default(25) status String @default("DRAFT") version String @default("1.0") submittedAt DateTime? @map("submitted_at") submittedBy Int? @map("submitted_by") reviewedAt DateTime? @map("reviewed_at") reviewedBy Int? @map("reviewed_by") reviewComment String? @map("review_comment") reviewChecklist String? @map("review_checklist") parentId Int? @map("parent_id") isLatest Boolean @default(true) usageCount Int @default(0) @map("usage_count") teacherCount Int @default(0) @map("teacher_count") avgRating Float @default(0) @map("avg_rating") createdBy Int? @map("created_by") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") publishedAt DateTime? @map("published_at") // 关联 theme Theme? @relation(fields: [themeId], references: [id]) packageCourses CoursePackageCourse[] courseLessons CourseLesson[] schoolCourses SchoolCourse[] resources CourseResource[] scripts CourseScript[] activities CourseActivity[] lessons Lesson[] tenantCourses TenantCourse[] versions CourseVersion[] tasks Task[] taskTemplates TaskTemplate[] schedulePlans SchedulePlan[] scheduleTemplates ScheduleTemplate[] @@index([themeId]) @@index([status]) @@map("courses") } // ==================== 课程(新增) ==================== model CourseLesson { id Int @id @default(autoincrement()) courseId Int @map("course_id") lessonType String @map("lesson_type") name String description String? duration Int @default(25) videoPath String? @map("video_path") videoName String? @map("video_name") pptPath String? @map("ppt_path") pptName String? @map("ppt_name") pdfPath String? @map("pdf_path") pdfName String? @map("pdf_name") objectives String? preparation String? extension String? reflection String? assessmentData String? @map("assessment_data") useTemplate Boolean @default(false) @map("use_template") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) steps LessonStep[] @@unique([courseId, lessonType]) @@index([courseId]) @@map("course_lessons") } // ==================== 教学环节(新增) ==================== model LessonStep { id Int @id @default(autoincrement()) lessonId Int @map("lesson_id") name String content String? duration Int @default(5) objective String? resourceIds String? @map("resource_ids") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") lesson CourseLesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) stepResources LessonStepResource[] @@index([lessonId]) @@map("lesson_steps") } // ==================== 环节资源关联(新增) ==================== model LessonStepResource { id Int @id @default(autoincrement()) stepId Int @map("step_id") resourceId Int @map("resource_id") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") step LessonStep @relation(fields: [stepId], references: [id], onDelete: Cascade) resource CourseResource @relation(fields: [resourceId], references: [id], onDelete: Cascade) @@unique([stepId, resourceId]) @@map("lesson_step_resources") } // ==================== 校本课程包(新增) ==================== model SchoolCourse { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") sourceCourseId Int @map("source_course_id") name String description String? createdBy Int @map("created_by") changesSummary String? @map("changes_summary") changesData String? @map("changes_data") usageCount Int @default(0) @map("usage_count") status String @default("ACTIVE") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) sourceCourse Course @relation(fields: [sourceCourseId], references: [id]) lessons SchoolCourseLesson[] reservations SchoolCourseReservation[] @@index([tenantId]) @@index([sourceCourseId]) @@map("school_courses") } // ==================== 校本课程(新增) ==================== model SchoolCourseLesson { id Int @id @default(autoincrement()) schoolCourseId Int @map("school_course_id") sourceLessonId Int @map("source_lesson_id") lessonType String @map("lesson_type") objectives String? preparation String? extension String? reflection String? changeNote String? @map("change_note") stepsData String? @map("steps_data") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") schoolCourse SchoolCourse @relation(fields: [schoolCourseId], references: [id], onDelete: Cascade) @@unique([schoolCourseId, lessonType]) @@map("school_course_lessons") } // ==================== 校本课程预约(新增) ==================== model SchoolCourseReservation { id Int @id @default(autoincrement()) schoolCourseId Int @map("school_course_id") teacherId Int @map("teacher_id") classId Int @map("class_id") scheduledDate String @map("scheduled_date") scheduledTime String? @map("scheduled_time") status String @default("PENDING") note String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") schoolCourse SchoolCourse @relation(fields: [schoolCourseId], references: [id], onDelete: Cascade) @@index([schoolCourseId, scheduledDate]) @@map("school_course_reservations") } // ==================== 课程资源 ==================== model CourseResource { id Int @id @default(autoincrement()) courseId Int @map("course_id") resourceType String @map("resource_type") resourceName String @map("resource_name") fileUrl String @map("file_url") fileSize Int? @map("file_size") mimeType String? @map("mime_type") metadata String? sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) stepResources LessonStepResource[] @@map("course_resources") } // ==================== 课程脚本(废弃但保留) ==================== model CourseScript { id Int @id @default(autoincrement()) courseId Int @map("course_id") stepIndex Int @map("step_index") stepName String @map("step_name") stepType String @map("step_type") duration Int objective String? teacherScript String? @map("teacher_script") interactionPoints String? @map("interaction_points") resourceIds String? @map("resource_ids") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) pages CourseScriptPage[] @@unique([courseId, stepIndex]) @@map("course_scripts") } // ==================== 逐页配置(废弃但保留) ==================== model CourseScriptPage { id Int @id @default(autoincrement()) scriptId Int @map("script_id") pageNumber Int @map("page_number") questions String? interactionComponent String? @map("interaction_component") teacherNotes String? @map("teacher_notes") resourceIds String? @map("resource_ids") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") script CourseScript @relation(fields: [scriptId], references: [id], onDelete: Cascade) @@unique([scriptId, pageNumber]) @@map("course_script_pages") } // ==================== 延伸活动(废弃但保留) ==================== model CourseActivity { id Int @id @default(autoincrement()) courseId Int @map("course_id") name String domain String? domainTagId Int? @map("domain_tag_id") activityType String @map("activity_type") duration Int? onlineMaterials String? @map("online_materials") offlineMaterials String? activityGuide String? objectives String? sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@map("course_activities") } // ==================== 课程版本历史 ==================== model CourseVersion { id Int @id @default(autoincrement()) courseId Int @map("course_id") version String snapshotData String changeLog String? publishedAt DateTime @default(now()) @map("published_at") publishedBy Int @map("published_by") course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@index([courseId]) @@map("course_versions") } // ==================== 授课记录 ==================== model Lesson { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") teacherId Int @map("teacher_id") classId Int @map("class_id") courseId Int @map("course_id") schedulePlanId Int? @map("schedule_plan_id") plannedDatetime DateTime? @map("planned_datetime") startDatetime DateTime? @map("start_datetime") endDatetime DateTime? @map("end_datetime") actualDuration Int? @map("actual_duration") status String @default("PLANNED") overallRating String? @map("overall_rating") participationRating String? @map("participation_rating") completionNote String? @map("completion_note") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id]) teacher Teacher @relation(fields: [teacherId], references: [id]) class Class @relation(fields: [classId], references: [id]) course Course @relation(fields: [courseId], references: [id]) schedulePlan SchedulePlan? @relation(fields: [schedulePlanId], references: [id]) feedbacks LessonFeedback[] records StudentRecord[] @@map("lessons") } // ==================== 课程反馈 ==================== model LessonFeedback { id Int @id @default(autoincrement()) lessonId Int @map("lesson_id") teacherId Int @map("teacher_id") designQuality Int? @map("design_quality") participation Int? goalAchievement Int? @map("goal_achievement") stepFeedbacks String? @map("step_feedbacks") pros String? suggestions String? activitiesDone String? @map("activities_done") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) teacher Teacher @relation(fields: [teacherId], references: [id]) @@unique([lessonId, teacherId]) @@map("lesson_feedbacks") } // ==================== 学生测评记录 ==================== model StudentRecord { id Int @id @default(autoincrement()) lessonId Int @map("lesson_id") studentId Int @map("student_id") focus Int? participation Int? interest Int? understanding Int? domainAchievements String? notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) student Student @relation(fields: [studentId], references: [id]) @@unique([lessonId, studentId]) @@map("student_records") } // ==================== 标签体系 ==================== model Tag { id Int @id @default(autoincrement()) level Int code String @unique name String parentId Int? @map("parent_id") description String? metadata String? sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") @@map("tags") } // ==================== 租户课程授权 ==================== model TenantCourse { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") courseId Int @map("course_id") authorized Boolean @default(true) authorizedAt DateTime? @map("authorized_at") createdAt DateTime @default(now()) @map("created_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@unique([tenantId, courseId]) @@map("tenant_courses") } // ==================== 成长档案 ==================== model GrowthRecord { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") studentId Int @map("student_id") classId Int? @map("class_id") recordType String @map("record_type") title String content String? images String? @map("images") recordDate DateTime @map("record_date") createdBy Int @map("created_by") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) class Class? @relation(fields: [classId], references: [id], onDelete: SetNull) @@index([tenantId, studentId]) @@index([tenantId, classId]) @@map("growth_records") } // ==================== 阅读任务 ==================== model Task { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") title String description String? taskType String @map("task_type") targetType String @map("target_type") relatedCourseId Int? @map("related_course_id") createdBy Int @map("created_by") startDate DateTime @map("start_date") endDate DateTime @map("end_date") status String @default("PUBLISHED") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) course Course? @relation(fields: [relatedCourseId], references: [id], onDelete: SetNull) targets TaskTarget[] completions TaskCompletion[] @@index([tenantId, status]) @@map("tasks") } model TaskTarget { id Int @id @default(autoincrement()) taskId Int @map("task_id") classId Int? @map("class_id") studentId Int? @map("student_id") task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) @@index([taskId, classId]) @@index([taskId, studentId]) @@map("task_targets") } model TaskCompletion { id Int @id @default(autoincrement()) taskId Int @map("task_id") studentId Int @map("student_id") status String @default("PENDING") completedAt DateTime? @map("completed_at") feedback String? parentFeedback String? @map("parent_feedback") createdAt DateTime @default(now()) task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) @@unique([taskId, studentId]) @@map("task_completions") } // ==================== 任务模板 ==================== model TaskTemplate { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") name String description String? taskType String @map("task_type") relatedCourseId Int? @map("related_course_id") defaultDuration Int @default(7) @map("default_duration") isDefault Boolean @default(false) @map("is_default") status String @default("ACTIVE") createdBy Int @map("created_by") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) course Course? @relation(fields: [relatedCourseId], references: [id], onDelete: SetNull) @@index([tenantId, status]) @@map("task_templates") } // ==================== 资源库 ==================== model ResourceLibrary { id Int @id @default(autoincrement()) name String libraryType String @map("library_type") description String? coverImage String? @map("cover_image") tenantId Int? @map("tenant_id") createdBy Int @map("created_by") status String @default("PUBLISHED") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt items ResourceItem[] @@index([libraryType, status]) @@map("resource_libraries") } model ResourceItem { id Int @id @default(autoincrement()) libraryId Int @map("library_id") title String description String? fileType String @map("file_type") filePath String @map("file_path") fileSize Int? @map("file_size") tags String? sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) library ResourceLibrary @relation(fields: [libraryId], references: [id], onDelete: Cascade) @@index([libraryId]) @@map("resource_items") } // ==================== 系统设置 ==================== model SystemSettings { id Int @id @default(autoincrement()) tenantId Int @unique @map("tenant_id") schoolName String? @map("school_name") schoolLogo String? @map("school_logo") address String? notifyOnLesson Boolean @default(true) @map("notify_on_lesson") notifyOnTask Boolean @default(true) @map("notify_on_task") notifyOnGrowth Boolean @default(false) @map("notify_on_growth") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@map("system_settings") } // ==================== 家长 ==================== model Parent { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") name String phone String email String? loginAccount String @unique @map("login_account") passwordHash String @map("password_hash") status String @default("ACTIVE") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") lastLoginAt DateTime? @map("last_login_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) children ParentStudent[] @@map("parents") } // ==================== 家长-学生关联 ==================== model ParentStudent { id Int @id @default(autoincrement()) parentId Int @map("parent_id") studentId Int @map("student_id") relationship String createdAt DateTime @default(now()) @map("created_at") parent Parent @relation(fields: [parentId], references: [id], onDelete: Cascade) student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) @@unique([parentId, studentId]) @@map("parent_students") } // ==================== 通知 ==================== model Notification { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") recipientType String @map("recipient_type") recipientId Int @map("recipient_id") title String content String notificationType String @map("notification_type") relatedType String? @map("related_type") relatedId Int? @map("related_id") isRead Boolean @default(false) @map("is_read") readAt DateTime? @map("read_at") createdAt DateTime @default(now()) @map("created_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@index([tenantId, recipientType, recipientId]) @@map("notifications") } // ==================== 班级教师关联 ==================== model ClassTeacher { id Int @id @default(autoincrement()) classId Int @map("class_id") teacherId Int @map("teacher_id") role String @default("MAIN") isPrimary Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") class Class @relation(fields: [classId], references: [id], onDelete: Cascade) teacher Teacher @relation(fields: [teacherId], references: [id], onDelete: Cascade) @@unique([classId, teacherId]) @@map("class_teachers") } // ==================== 学生调班历史 ==================== model StudentClassHistory { id Int @id @default(autoincrement()) studentId Int @map("student_id") fromClassId Int? @map("from_class_id") toClassId Int @map("to_class_id") reason String? operatedBy Int? @map("operated_by") createdAt DateTime @default(now()) @map("created_at") student Student @relation(fields: [studentId], references: [id]) fromClass Class? @relation("FromClass", fields: [fromClassId], references: [id]) toClass Class @relation("ToClass", fields: [toClassId], references: [id]) @@map("student_class_history") } // ==================== 排课计划 ==================== model SchedulePlan { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") classId Int @map("class_id") courseId Int @map("course_id") teacherId Int? @map("teacher_id") scheduledDate DateTime? @map("scheduled_date") scheduledTime String? @map("scheduled_time") weekDay Int? @map("week_day") repeatType String @default("NONE") @map("repeat_type") repeatEndDate DateTime? @map("repeat_end_date") source String @default("SCHOOL") createdBy Int @map("created_by") status String @default("ACTIVE") note String? reminderSent Boolean @default(false) @map("reminder_sent") reminderSentAt DateTime? @map("reminder_sent_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id]) class Class @relation(fields: [classId], references: [id]) course Course @relation(fields: [courseId], references: [id]) teacher Teacher? @relation(fields: [teacherId], references: [id]) lessons Lesson[] @@index([tenantId, classId]) @@index([tenantId, teacherId]) @@index([tenantId, scheduledDate]) @@map("schedule_plans") } // ==================== 排课模板 ==================== model ScheduleTemplate { id Int @id @default(autoincrement()) tenantId Int @map("tenant_id") name String courseId Int @map("course_id") classId Int? @map("class_id") teacherId Int? @map("teacher_id") scheduledTime String? @map("scheduled_time") weekDay Int? @map("week_day") duration Int @default(25) isDefault Boolean @default(false) @map("is_default") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id]) class Class? @relation(fields: [classId], references: [id]) teacher Teacher? @relation(fields: [teacherId], references: [id]) @@index([tenantId]) @@index([tenantId, courseId]) @@map("schedule_templates") } // ==================== 操作日志 ==================== model OperationLog { id Int @id @default(autoincrement()) tenantId Int? @map("tenant_id") userId Int @map("user_id") userType String @map("user_type") action String module String description String targetId Int? @map("target_id") oldValue String? @map("old_value") newValue String? @map("new_value") ipAddress String? @map("ip_address") userAgent String? @map("user_agent") createdAt DateTime @default(now()) @map("created_at") tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull) @@index([tenantId, userId]) @@index([tenantId, createdAt]) @@index([action, module]) @@map("operation_logs") }