// prisma/schema.prisma - SQLite版本(快速启动) generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } // ==================== 租户 ==================== 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[] @@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()) name String description String? pictureBookId Int? @map("picture_book_id") pictureBookName String? @map("picture_book_name") // 封面图片 - 存储文件路径(不再是 base64) coverImagePath String? @map("cover_image_path") // 数字资源 - 存储多文件路径(JSON数组 [{path, name}]) ebookPaths String? @map("ebook_paths") audioPaths String? @map("audio_paths") videoPaths String? @map("video_paths") otherResources String? @map("other_resources") // JSON数组存储多个资源 {path, name} // 教学材料 - 存储文件路径(不再是 base64) pptPath String? @map("ppt_path") pptName String? @map("ppt_name") posterPaths String? @map("poster_paths") // JSON数组存储多个挂图 {path, name} // 实体教具和学生材料 tools String? @map("tools") // JSON数组存储教具列表 [{name, quantity}] studentMaterials String? @map("student_materials") // 学生材料文本 // 课堂计划 lessonPlanData String? @map("lesson_plan_data") // JSON存储课堂计划数据 // 延伸活动 activitiesData String? @map("activities_data") // JSON存储延伸活动数据 // 测评工具 assessmentData String? @map("assessment_data") // JSON存储测评工具数据 gradeTags String @default("[]") @map("grade_tags") domainTags String @default("[]") @map("domain_tags") duration Int @default(25) status String @default("DRAFT") // DRAFT, PENDING, REJECTED, PUBLISHED, ARCHIVED version String @default("1.0") // 审核相关字段 submittedAt DateTime? @map("submitted_at") // 提交审核时间 submittedBy Int? @map("submitted_by") // 提交人ID reviewedAt DateTime? @map("reviewed_at") // 审核时间 reviewedBy Int? @map("reviewed_by") // 审核人ID reviewComment String? @map("review_comment") // 审核意见 reviewChecklist String? @map("review_checklist") // 审核检查项结果 JSON // 版本相关字段 parentId Int? @map("parent_id") // 父版本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") resources CourseResource[] scripts CourseScript[] activities CourseActivity[] lessons Lesson[] tenantCourses TenantCourse[] versions CourseVersion[] tasks Task[] taskTemplates TaskTemplate[] schedulePlans SchedulePlan[] scheduleTemplates ScheduleTemplate[] @@index([status]) @@map("courses") } // ==================== 课程版本历史 ==================== model CourseVersion { id Int @id @default(autoincrement()) courseId Int @map("course_id") version String // 版本号 snapshotData String // JSON快照(完整课程内容) 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 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) @@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") // 关联资源ID列表,JSON数组 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 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") // STUDENT, CLASS title String content String? images String? @map("images") // JSON: ["path1", "path2"] 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") // READING, ACTIVITY, HOMEWORK targetType String @map("target_type") // CLASS, STUDENT 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") // DRAFT, PUBLISHED, ARCHIVED 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") // PENDING, IN_PROGRESS, COMPLETED 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") // READING, ACTIVITY, HOMEWORK // 关联课程(可选) relatedCourseId Int? @map("related_course_id") // 默认时间设置 defaultDuration Int @default(7) @map("default_duration") // 默认任务天数 // 模板状态 isDefault Boolean @default(false) @map("is_default") status String @default("ACTIVE") // ACTIVE, ARCHIVED // 创建者 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") // PICTURE_BOOK, MATERIAL, TEMPLATE description String? coverImage String? @map("cover_image") tenantId Int? @map("tenant_id") // NULL = 公共资源 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") // IMAGE, PDF, VIDEO, AUDIO, PPT, OTHER filePath String @map("file_path") fileSize Int? @map("file_size") tags String? // JSON: ["tag1", "tag2"] 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 // FATHER, MOTHER, GRANDFATHER, GRANDMOTHER, OTHER 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") // TEACHER, SCHOOL, PARENT recipientId Int @map("recipient_id") title String content String notificationType String @map("notification_type") // SYSTEM, TASK, LESSON, GROWTH 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") // MAIN主班, ASSIST配班, CARE保育员 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") // 操作人(教师ID),可选 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") // 时间段 "09:00-09:30" weekDay Int? @map("week_day") // 周几 (0-6),用于重复排课 // 重复规则 repeatType String @default("NONE") @map("repeat_type") // NONE, DAILY, WEEKLY repeatEndDate DateTime? @map("repeat_end_date") // 排课来源 source String @default("SCHOOL") // SCHOOL(学校排课), TEACHER(教师预约) createdBy Int @map("created_by") // 创建人ID // 状态 status String @default("ACTIVE") // ACTIVE, CANCELLED // 备注 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") // 时间段 "09:00-09:30" weekDay Int? @map("week_day") // 周几 (0-6) 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") // SCHOOL, TEACHER, PARENT, ADMIN action String // CREATE, UPDATE, DELETE, LOGIN, etc. module String // 教师管理, 学生管理, 排课管理, etc. description String targetId Int? @map("target_id") // 操作对象ID oldValue String? @map("old_value") // JSON格式 newValue String? @map("new_value") // JSON格式 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") }