后端重构:
- 添加统一响应格式 ResultDto<T> 和 PageResultDto<T>
- 添加分页查询 DTO 基类 PageQueryDto
- 添加响应转换拦截器 TransformInterceptor
- 添加公共工具函数(JSON 解析、分页计算)
- 配置 Swagger/OpenAPI 文档(访问路径:/api-docs)
- Tenant 模块 DTO 规范化示例(添加 @ApiProperty 装饰器)
- CourseLesson 控制器重构 - 移除类级路径参数,修复 Orval 验证错误
- 后端 DTO 规范化 - 为 Course、Lesson、TeacherCourse、SchoolCourse 控制器添加完整的 Swagger 装饰器
前端重构:
- 配置 Orval 从后端 OpenAPI 自动生成 API 客户端
- 生成 API 客户端代码(带完整参数定义)
- 创建 API 客户端统一入口 (src/api/client.ts)
- 创建 API 适配层 (src/api/teacher.adapter.ts)
- 配置文件路由 (unplugin-vue-router)
- 课程模块迁移到新 API 客户端
- 修复 PrepareModeView.vue API 调用错误
- 教师模块迁移到新 API 客户端
- 修复 school-course.ts 类型错误
- 清理 teacher.adapter.ts 未使用导入
- 修复 client.ts API 客户端结构
- 创建文件路由目录结构
Bug 修复:
- 修复路由配置问题 - 移除 top-level await,改用手动路由配置
- 修复响应拦截器 - 正确解包 { code, message, data } 格式的响应
- 清理 teacher.adapter.ts 未使用导入
- 修复 client.ts API 客户端结构
- 创建文件路由目录结构
系统测试:
- 后端 API 测试通过 (7/7)
- 前端路由测试通过 (4/4)
- 数据库完整性验证通过
- Orval API 客户端验证通过
- 超管端功能测试通过 (97.8% 通过率)
新增文件:
- reading-platform-backend/src/common/dto/result.dto.ts
- reading-platform-backend/src/common/dto/page-query.dto.ts
- reading-platform-backend/src/common/interceptors/transform.interceptor.ts
- reading-platform-backend/src/common/utils/json.util.ts
- reading-platform-backend/src/common/utils/pagination.util.ts
- reading-platform-frontend/orval.config.ts
- reading-platform-frontend/src/api/generated/mutator.ts
- reading-platform-frontend/src/api/client.ts
- reading-platform-frontend/src/api/teacher.adapter.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
905 lines
38 KiB
Plaintext
905 lines
38 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "sqlite"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model Tenant {
|
|
id Int @id @default(autoincrement())
|
|
name String
|
|
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")
|
|
loginAccount String? @unique @map("login_account")
|
|
passwordHash String? @map("password_hash")
|
|
classes Class[]
|
|
growthRecords GrowthRecord[]
|
|
lessons Lesson[]
|
|
notifications Notification[]
|
|
operationLogs OperationLog[]
|
|
parents Parent[]
|
|
schedulePlans SchedulePlan[]
|
|
scheduleTemplates ScheduleTemplate[]
|
|
schoolCourses SchoolCourse[]
|
|
students Student[]
|
|
settings SystemSettings?
|
|
taskTemplates TaskTemplate[]
|
|
tasks Task[]
|
|
teachers Teacher[]
|
|
tenantCourses TenantCourse[]
|
|
tenantPackages TenantPackage[]
|
|
|
|
@@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")
|
|
classTeachers ClassTeacher[]
|
|
classes Class[]
|
|
feedbacks LessonFeedback[]
|
|
lessons Lesson[]
|
|
schedulePlans SchedulePlan[]
|
|
scheduleTemplates ScheduleTemplate[]
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
classTeachers ClassTeacher[]
|
|
teacher Teacher? @relation(fields: [teacherId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
growthRecords GrowthRecord[]
|
|
lessons Lesson[]
|
|
schedulePlans SchedulePlan[]
|
|
scheduleTemplates ScheduleTemplate[]
|
|
studentHistory StudentClassHistory[] @relation("ToClass")
|
|
fromHistory StudentClassHistory[] @relation("FromClass")
|
|
students Student[]
|
|
|
|
@@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")
|
|
growthRecords GrowthRecord[]
|
|
parents ParentStudent[]
|
|
classHistory StudentClassHistory[]
|
|
records StudentRecord[]
|
|
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
taskCompletions TaskCompletion[]
|
|
|
|
@@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")
|
|
environmentConstruction String? @map("environment_construction")
|
|
activities CourseActivity[]
|
|
courseLessons CourseLesson[]
|
|
packageCourses CoursePackageCourse[]
|
|
resources CourseResource[]
|
|
scripts CourseScript[]
|
|
versions CourseVersion[]
|
|
theme Theme? @relation(fields: [themeId], references: [id])
|
|
lessons Lesson[]
|
|
schedulePlans SchedulePlan[]
|
|
scheduleTemplates ScheduleTemplate[]
|
|
schoolCourses SchoolCourse[]
|
|
taskTemplates TaskTemplate[]
|
|
tasks Task[]
|
|
tenantCourses TenantCourse[]
|
|
|
|
@@index([status])
|
|
@@map("courses")
|
|
}
|
|
|
|
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 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")
|
|
pages CourseScriptPage[]
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
resourceIds String? @map("resource_ids")
|
|
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")
|
|
completedLessonIds String? @default("[]") @map("completed_lesson_ids")
|
|
currentLessonId Int? @map("current_lesson_id")
|
|
currentStepId Int? @map("current_step_id")
|
|
lessonIds String? @default("[]") @map("lesson_ids")
|
|
progressData String? @map("progress_data")
|
|
feedbacks LessonFeedback[]
|
|
schedulePlan SchedulePlan? @relation(fields: [schedulePlanId], references: [id])
|
|
course Course @relation(fields: [courseId], references: [id])
|
|
class Class @relation(fields: [classId], references: [id])
|
|
teacher Teacher @relation(fields: [teacherId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
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")
|
|
teacher Teacher @relation(fields: [teacherId], references: [id])
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
student Student @relation(fields: [studentId], references: [id])
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
tenant Tenant @relation(fields: [tenantId], 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
|
|
class Class? @relation(fields: [classId], references: [id])
|
|
student Student @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@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
|
|
completions TaskCompletion[]
|
|
targets TaskTarget[]
|
|
course Course? @relation(fields: [relatedCourseId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@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())
|
|
student Student @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
|
task Task @relation(fields: [taskId], 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
|
|
course Course? @relation(fields: [relatedCourseId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
children ParentStudent[]
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
student Student @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
|
parent Parent @relation(fields: [parentId], 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")
|
|
teacher Teacher @relation(fields: [teacherId], references: [id], onDelete: Cascade)
|
|
class Class @relation(fields: [classId], 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")
|
|
toClass Class @relation("ToClass", fields: [toClassId], references: [id])
|
|
fromClass Class? @relation("FromClass", fields: [fromClassId], references: [id])
|
|
student Student @relation(fields: [studentId], 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")
|
|
lessons Lesson[]
|
|
teacher Teacher? @relation(fields: [teacherId], references: [id])
|
|
course Course @relation(fields: [courseId], references: [id])
|
|
class Class @relation(fields: [classId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
|
|
@@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")
|
|
teacher Teacher? @relation(fields: [teacherId], references: [id])
|
|
class Class? @relation(fields: [classId], references: [id])
|
|
course Course @relation(fields: [courseId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@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])
|
|
|
|
@@index([tenantId, userId])
|
|
@@index([tenantId, createdAt])
|
|
@@index([action, module])
|
|
@@map("operation_logs")
|
|
}
|
|
|
|
model Theme {
|
|
id Int @id @default(autoincrement())
|
|
name String @unique
|
|
description String?
|
|
sortOrder Int @default(0) @map("sort_order")
|
|
status String @default("ACTIVE")
|
|
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")
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
package CoursePackage @relation(fields: [packageId], 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")
|
|
package CoursePackage @relation(fields: [packageId], references: [id], onDelete: Cascade)
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([tenantId, packageId])
|
|
@@index([tenantId, status])
|
|
@@map("tenant_packages")
|
|
}
|
|
|
|
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")
|
|
stepResources LessonStepResource[]
|
|
lesson CourseLesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
|
|
@@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")
|
|
resource CourseResource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
|
|
step LessonStep @relation(fields: [stepId], 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")
|
|
usageCount Int @default(0) @map("usage_count")
|
|
status String @default("ACTIVE")
|
|
saveLocation String @default("PERSONAL") @map("save_location")
|
|
reviewStatus String @default("PENDING") @map("review_status")
|
|
reviewedBy Int? @map("reviewed_by")
|
|
reviewedAt DateTime? @map("reviewed_at")
|
|
reviewComment String? @map("review_comment")
|
|
themeId Int? @map("theme_id")
|
|
gradeTags String @default("[]") @map("grade_tags")
|
|
domainTags String @default("[]") @map("domain_tags")
|
|
duration Int @default(25)
|
|
coverImagePath String? @map("cover_image_path")
|
|
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")
|
|
environmentConstruction String? @map("environment_construction")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
lessons SchoolCourseLesson[]
|
|
reservations SchoolCourseReservation[]
|
|
sourceCourse Course @relation(fields: [sourceCourseId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([tenantId])
|
|
@@index([tenantId, saveLocation])
|
|
@@index([tenantId, reviewStatus])
|
|
@@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")
|
|
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?
|
|
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])
|
|
@@index([schoolCourseId])
|
|
@@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")
|
|
schoolCourse SchoolCourse @relation(fields: [schoolCourseId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([schoolCourseId, scheduledDate])
|
|
@@map("school_course_reservations")
|
|
}
|