# teacher-bff GraphQL Schema (SDL-first, president §2.17) # 版本:v2(ARB-001 扁平命名重构,admin 字段直接挂载根 Query/Mutation) # 负责人:ai03 # 关联:contracts/teacher-bff_contract.md §1.3、workline §3.1 # # 设计原则(02-architecture-design.md §5.1): # 1. 按教学场景域组织(不按角色分),教导主任/教研组长复用同一 schema # 2. Query 为主,Mutation 谨慎(BFF 偏读多写少) # 3. N+1 防御:所有 list 字段经 DataLoader(P4 全量接入) # 4. 复杂度限制:depth ≤ 7,query cost ≤ 1000 # 5. admin 扁平命名(ARB-001 裁决:admin 字段直接挂载根 Query/Mutation) # # 错误响应格式:GraphQL errors 数组 + extensions.code = BFF_TEACHER_* + extensions.traceId # 降级模式 B(president §2.6):success=true + error=null + data 内 degraded 字段 scalar DateTime # ============ Teacher 域类型 ============ type User { id: ID! email: String! name: String! roles: [String!]! permissions: [String!]! dataScope: DataScope! } enum DataScope { SELF SUBJECT CLASS GRADE SCHOOL ALL } type ViewportItem { key: String! label: String! route: String! icon: String sortOrder: String! requiredPermission: String } type Class { id: ID! name: String! gradeId: String! exams: [Exam!]! homework: [Homework!]! } type Exam { id: ID! title: String! classId: ID! status: ExamStatus! publishedAt: DateTime grades: [Grade!]! } enum ExamStatus { DRAFT PUBLISHED IN_PROGRESS GRADING SCORED ARCHIVED } type Homework { id: ID! title: String! classId: ID! dueDate: DateTime! status: String submissions: [Submission!]! } type Submission { id: ID! studentId: ID! submittedAt: DateTime! status: SubmissionStatus! } enum SubmissionStatus { NOT_SUBMITTED SUBMITTED GRADED } type Grade { id: ID! examId: ID! studentId: ID! score: Float! rank: Int submittedAt: DateTime! } type DashboardStats { totalExams: Int pendingGrading: Int todayHomework: Int } type DashboardData { user: User classes: [Class!]! viewports: [ViewportItem!]! stats: DashboardStats } type KnowledgePath { knowledgePointId: ID! name: String! prerequisites: [KnowledgePoint!]! } type KnowledgePoint { id: ID! name: String! subject: String! } type ClassPerformance { classId: ID! averageScore: Float! passRate: Float! weaknessTopics: [String!]! } type StudentWeakness { studentId: ID! weakTopics: [String!]! recommendedExercises: [String!]! } type LearningTrend { studentId: ID! trend: [TrendPoint!]! } type TrendPoint { date: DateTime! score: Float! } type Notification { id: ID! type: NotificationType! title: String! content: String! read: Boolean! createdAt: DateTime! } enum NotificationType { SYSTEM EXAM HOMEWORK GRADE ATTENDANCE } type GeneratedQuestion { id: ID! subject: String! difficulty: String! content: String! answer: String! } # ============ Admin 域类型(ARB-001 扁平命名)============ type AdminUser { id: ID! email: String! name: String! roles: [AdminRoleRef!]! status: String! dataScope: String organizationId: ID schoolName: String lastLoginAt: DateTime createdAt: DateTime! updatedAt: DateTime! } type AdminRoleRef { id: ID! name: String! code: String! } type AdminUserPage { items: [AdminUser!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminRole { id: ID! name: String! code: String! description: String permissions: [AdminPermission!]! userCount: Int! dataScope: String isSystem: Boolean! createdAt: DateTime! updatedAt: DateTime! } type AdminPermission { id: ID! code: String! name: String! description: String resource: String! action: String! isSystem: Boolean! } type AdminViewport { id: ID! key: String! label: String! route: String! icon: String sortOrder: String! requiredPermission: String scope: String! isVisible: Boolean! } type AdminOrganization { id: ID! name: String! type: String! parentId: ID childrenCount: Int! sortOrder: Int! } type AdminClass { id: ID! name: String! gradeName: String! schoolName: String! headTeacherName: String studentCount: Int! createdAt: DateTime! } type AdminClassPage { items: [AdminClass!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminTeacher { id: ID! email: String! name: String! schoolName: String subjects: [String!]! classCount: Int! status: String! lastLoginAt: DateTime } type AdminTeacherPage { items: [AdminTeacher!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminStudent { id: ID! email: String! name: String! className: String gradeName: String schoolName: String guardianName: String guardianPhone: String status: String! } type AdminStudentPage { items: [AdminStudent!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AuditLogEntry { id: ID! eventId: String! occurredAt: DateTime! actorUserId: ID! actorName: String action: String! resourceType: String! resourceId: ID ip: String userAgent: String beforeState: String afterState: String traceId: String } type AuditLogPage { items: [AuditLogEntry!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminDashboard { totalTeachers: Int! totalStudents: Int! totalClasses: Int! totalSchools: Int! schoolAvgScore: Float! activeUsersToday: Int! auditEventsToday: Int! trend: [AdminDashboardTrendPoint!]! serviceHealth: [ServiceHealthItem!]! } type AdminDashboardTrendPoint { date: String! teachers: Int! students: Int! avgScore: Float! } type ServiceHealthItem { serviceName: String! status: String! latencyMs: Int! } type SystemSettings { schoolName: String! schoolCode: String! academicYear: String! semester: String! contactEmail: String! contactPhone: String! address: String! timezone: String! locale: String! } type AuditOverviewStats { totalEvents: Int! todayEvents: Int! uniqueActors: Int! failedActions: Int! topActions: [AuditActionCount!]! topResources: [AuditResourceCount!]! } type AuditActionCount { action: String! count: Int! } type AuditResourceCount { resource: String! count: Int! } type AuditTrendPoint { date: String! count: Int! action: String! } type DataChangeLog { id: ID! occurredAt: DateTime! actorUserId: ID! actorName: String tableName: String! action: String! recordId: ID beforeState: String afterState: String ip: String } type DataChangeLogPage { items: [DataChangeLog!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type LoginLog { id: ID! occurredAt: DateTime! userId: ID! userName: String action: String! status: String! ip: String userAgent: String failureReason: String } type LoginLogPage { items: [LoginLog!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminSchool { id: ID! name: String! code: String! address: String contactPhone: String contactEmail: String principalName: String gradeCount: Int! classCount: Int! studentCount: Int! teacherCount: Int! createdAt: DateTime! } type AdminGrade { id: ID! name: String! schoolId: ID! schoolName: String! directorName: String classCount: Int! studentCount: Int! sortOrder: Int! createdAt: DateTime! } type AdminGradeInsights { gradeId: ID! gradeName: String! schoolWideSummary: SchoolWideSummary! homeworkTimeline: [HomeworkTimelineItem!]! classRankings: [ClassRankingItem!]! } type SchoolWideSummary { totalStudents: Int! totalClasses: Int! avgScore: Float! passingRate: Float! } type HomeworkTimelineItem { homeworkId: ID! title: String! className: String! avgScore: Float! submittedCount: Int! totalCount: Int! dueDate: DateTime! } type ClassRankingItem { classId: ID! className: String! avgScore: Float! rank: Int! delta: Float! } type AdminDepartment { id: ID! name: String! schoolId: ID! schoolName: String! leaderName: String memberCount: Int! subject: String createdAt: DateTime! } type AdminAcademicYear { id: ID! name: String! startDate: DateTime! endDate: DateTime! firstSemesterStart: DateTime! firstSemesterEnd: DateTime! secondSemesterStart: DateTime! secondSemesterEnd: DateTime! isCurrent: Boolean! createdAt: DateTime! } type AdminAnnouncement { id: ID! title: String! content: String! status: String! audience: String! authorName: String isPinned: Boolean! readCount: Int! totalCount: Int! publishedAt: DateTime createdAt: DateTime! updatedAt: DateTime! } type AdminAnnouncementPage { items: [AdminAnnouncement!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminInvitationCode { id: ID! code: String! status: String! roleId: ID! roleName: String classId: ID className: String email: String batchId: String! usedBy: ID usedByName: String usedAt: DateTime createdAt: DateTime! expiresAt: DateTime! } type AdminInvitationCodePage { items: [AdminInvitationCode!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminFile { id: ID! filename: String! mimeType: String! size: Int! url: String! entityType: String entityId: ID uploadedByName: String createdAt: DateTime! } type AdminFilePage { items: [AdminFile!]! total: Int! page: Int! pageSize: Int! hasNext: Boolean! } type AdminFileStats { totalFiles: Int! totalSize: Int! byMimeType: [FileMimeTypeStat!]! byEntityType: [FileEntityTypeStat!]! } type FileMimeTypeStat { mimeType: String! count: Int! size: Int! } type FileEntityTypeStat { entityType: String! count: Int! } type AiProvider { id: ID! name: String! type: String! model: String! apiKey: String! baseUrl: String isEnabled: Boolean! createdAt: DateTime! updatedAt: DateTime! } type AiUsage { totalRequests: Int! totalTokens: Int! totalCost: Float! byProvider: [AiProviderUsage!]! byDay: [AiDayUsage!]! } type AiProviderUsage { providerId: ID! providerName: String! requests: Int! tokens: Int! cost: Float! } type AiDayUsage { date: String! requests: Int! tokens: Int! cost: Float! } type GenerateInvitationCodesResult { codes: [String!]! batchId: String! } # ============ Query(41 个:teacher 域 13 + admin 域 28)============ type Query { # ===== Teacher 域(13 Query)===== dashboard: DashboardData! viewports: [ViewportItem!]! me: User! classes: [Class!]! class(id: ID!): Class exams(classId: ID!): [Exam!]! homework(classId: ID!): [Homework!]! grades(examId: ID!): [Grade!]! knowledgePath(knowledgePointId: ID!): KnowledgePath! classPerformance(classId: ID!): ClassPerformance! studentWeakness(studentId: ID!): StudentWeakness! learningTrend(studentId: ID!, dateRange: DateRangeInput): LearningTrend! notifications(unreadOnly: Boolean): [Notification!]! # ===== P3-P5 扩展 Query(teacher-portal 期望)===== classStudents(classId: ID!): [Student!]! examDetail(id: ID!): ExamDetail homeworkDetail(id: ID!): HomeworkDetail classExams(classId: ID!): [Exam!]! classHomework(classId: ID!): [Homework!]! examGrades(examId: ID!): [Grade!]! # ===== P4 扩展 Query ===== knowledgeGraph(classId: ID, subject: String): KnowledgeGraph! classAnalytics(classId: ID!): ClassAnalytics! studentAnalytics(studentId: ID!): StudentAnalytics! # ===== P5 扩展 Query ===== myNotifications(unreadOnly: Boolean): [Notification!]! # ===== P7 扩展 Query(全部降级返回空/null)===== attendanceList(filter: AttendanceListFilterInput): AttendanceListPage! attendanceSheet(classId: ID!, date: String!): [AttendanceSheetItem!]! attendanceStats(classId: ID!, startDate: String!, endDate: String!): AttendanceStats! attendanceClassComparison(startDate: String!, endDate: String!): [AttendanceClassComparisonItem!]! attendanceWarnings(classId: ID!, startDate: String!, endDate: String!): [AttendanceWarning!]! attendanceReport(classId: ID!, reportType: AttendanceReportType!, startDate: String!, endDate: String!): AttendanceReport! classDetail(id: ID!): ClassDetail classSchedule(classId: ID): [ClassScheduleItem!]! leaveRequests(filter: LeaveRequestsFilterInput): LeaveRequestPage! scheduleChanges(page: Int, pageSize: Int): ScheduleChangePage! electiveCourses(status: String, subject: String): [ElectiveCourse!]! electiveCourseDetail(id: ID!): ElectiveCourse examRichEditor(examId: ID!): ExamRichContent proctoringStatus(examId: ID!): ProctoringStatus! # ===== P7-exams 扩展 Query ===== examBuild(examId: ID!, candidatePage: Int, candidatePageSize: Int, filter: QuestionsLibraryFilter): ExamBuild! examAnalytics(examId: ID!): ExamAnalytics! questionsLibrary(filter: QuestionsLibraryFilter): QuestionsLibraryPage! questionBatchExport(filter: QuestionsLibraryFilter): QuestionBatchExportResult! textbooks(filter: TextbooksFilter): TextbooksPage! textbookDetail(id: ID!): TextbookDetail # ===== P7-grades 扩展 Query ===== gradeEntry(examId: ID!, classId: ID!): [GradeEntryItem!]! gradeStats(classId: ID!, subject: String): GradeStats! gradeAnalytics(classId: ID!, semester: String, examType: String): GradeAnalytics! reportCard(studentId: ID!, academicYear: String, semester: String): ReportCard! homeworkSubmissions(classId: ID, status: String): [HomeworkSubmissionSummary!]! homeworkAssignmentSubmissions(homeworkId: ID!): HomeworkAssignmentSubmissions! aiBatchGrading(homeworkId: ID!): [AiBatchGradingItem!]! # ===== P7-insights 扩展 Query ===== lessonPlans(subject: String): [LessonPlan!]! lessonPlanDetail(planId: ID!): LessonPlanDetail lessonPlanLibrary(subject: String, grade: String): [LessonPlanLibraryItem!]! lessonPlanCalendar(year: Int!, month: Int!): LessonPlanCalendar! lessonPlanHeatmap: LessonPlanHeatmap! coursePlans(status: CoursePlanStatus): [CoursePlan!]! coursePlanDetail(id: ID!): CoursePlanDetail diagnosticReports(type: DiagnosticReportType, status: DiagnosticReportStatus): [DiagnosticReport!]! classDiagnostic(classId: ID!): ClassDiagnostic! studentDiagnostic(studentId: ID!): StudentDiagnostic! errorBook(subject: String!, classId: String): ErrorBook! practiceAnalytics(classId: String): PracticeAnalytics! # ===== Admin 域(28 Query,ARB-001 扁平命名)===== currentUser: User! adminUsers(filter: AdminUserFilterInput): AdminUserPage! adminUser(id: ID!): AdminUser adminRoles: [AdminRole!]! adminPermissions: [AdminPermission!]! adminViewports(scope: String): [AdminViewport!]! adminOrganization(parentId: ID): [AdminOrganization!]! adminClasses(filter: AdminClassFilterInput): AdminClassPage! adminTeachers(filter: AdminTeacherFilterInput): AdminTeacherPage! adminStudents(filter: AdminStudentFilterInput): AdminStudentPage! auditLogs(filter: AuditLogFilterInput): AuditLogPage! auditOverviewStats: AuditOverviewStats! auditTrend(days: Int): [AuditTrendPoint!]! dataChangeLogs(filter: DataChangeLogFilterInput): DataChangeLogPage! loginLogs(filter: LoginLogFilterInput): LoginLogPage! adminDashboard: AdminDashboard! systemSettings: SystemSettings! adminSchools: [AdminSchool!]! adminGrades(schoolId: ID): [AdminGrade!]! adminGradeInsights(gradeId: ID!): AdminGradeInsights! adminDepartments(schoolId: ID): [AdminDepartment!]! adminAcademicYears: [AdminAcademicYear!]! adminAnnouncements(filter: AnnouncementFilterInput): AdminAnnouncementPage! adminInvitationCodes(filter: InvitationCodeFilterInput): AdminInvitationCodePage! adminFiles(filter: FileFilterInput): AdminFilePage! adminFileStats: AdminFileStats! aiProviders: [AiProvider!]! aiUsage: AiUsage! } # ============ Mutation(20 个:teacher 域 5 + admin 域 15)============ type Mutation { # ===== Teacher 域(5 Mutation)===== createExam(input: CreateExamInput!): Exam! assignHomework(input: AssignHomeworkInput!): Homework! recordGrade(input: RecordGradeInput!): Grade! generateQuestion(input: GenerateQuestionInput!): GeneratedQuestion! markNotificationAsRead(id: ID!): Notification! # ===== P3-P5 扩展 Mutation(teacher-portal 期望)===== updateUser(id: ID, input: UpdateUserInput!): User! markAsRead(id: ID!): Notification! generateLessonPlan(input: GenerateLessonPlanInput!): GeneratedLessonPlan! generateReport(input: GenerateReportInput!): GeneratedReport! # ===== P7 扩展 Mutation(全部降级返回默认 success)===== saveAttendanceSheet(input: SaveAttendanceSheetInput!): SaveAttendanceSheetResult! approveLeaveRequest(input: ApproveLeaveRequestInput!): ApproveLeaveRequestResult! submitLeaveRequest(input: SubmitLeaveRequestInput!): SubmitLeaveRequestResult! submitScheduleChange(input: SubmitScheduleChangeInput!): SubmitScheduleChangeResult! createElectiveCourse(input: CreateElectiveCourseInput!): ElectiveCourse! updateElectiveCourse(input: UpdateElectiveCourseInput!): ElectiveCourse! publishElectiveCourse(id: ID!): ElectiveCourse! cancelElectiveCourse(id: ID!): ElectiveCourse! saveExamRichContent(input: SaveExamRichContentInput!): SaveExamRichContentResult! proctoringEvent(input: ProctoringEventInput!): ProctoringEventResult! saveScanGrading(input: SaveScanGradingInput!): SaveScanGradingResult! # ===== P7-exams 扩展 Mutation ===== saveExamBuild(input: SaveExamBuildInput!): SaveExamBuildResult! questionCreate(input: QuestionCreateInput!): Question! questionUpdate(input: QuestionUpdateInput!): Question! questionDelete(questionId: ID!): QuestionDeleteResult! questionBatchImport(input: QuestionBatchImportInput!): QuestionBatchImportResult! textbookCreate(input: TextbookCreateInput!): TextbookItem! # ===== P7-grades 扩展 Mutation ===== saveGradeEntries(input: SaveGradeEntriesInput!): SaveGradeEntriesResult! gradeSubmission(input: GradeSubmissionInput!): GradeSubmissionResult! # ===== P7-insights 扩展 Mutation ===== createLessonPlan(input: CreateLessonPlanInput!): LessonPlan! updateLessonPlan(input: UpdateLessonPlanInput!): LessonPlan! aiGenerateLessonPlan(input: AIGenerateLessonPlanInput!): AIGenerateLessonPlanResult! forkLessonPlan(planId: ID!): LessonPlan! createCoursePlan(input: CreateCoursePlanInput!): CoursePlan! updateCoursePlan(input: UpdateCoursePlanInput!): CoursePlan! # ===== Admin 域(15 Mutation,ARB-001 扁平命名)===== createUser(input: CreateUserInput!): AdminUser! toggleUserStatus(id: ID!, status: String!): AdminUser! createRole(input: CreateRoleInput!): AdminRole! updateRolePermissions(roleId: ID!, permissionCodes: [String!]!): AdminRole! updateViewport(id: ID!, input: UpdateViewportInput!): AdminViewport! updateSystemSettings(input: SystemSettingsInput!): SystemSettings! createAnnouncement(input: CreateAnnouncementInput!): AdminAnnouncement! publishAnnouncement(id: ID!): AdminAnnouncement! archiveAnnouncement(id: ID!): AdminAnnouncement! toggleAnnouncementPin(id: ID!): AdminAnnouncement! generateInvitationCodes(input: GenerateInvitationCodesInput!): GenerateInvitationCodesResult! deleteInvitationCodes(ids: [ID!]!): Boolean! deleteFiles(ids: [ID!]!): Boolean! updateAiProvider(id: ID!, input: UpdateAiProviderInput!): AiProvider! } # ============ Input 类型 ============ input DateRangeInput { start: DateTime! end: DateTime! } input CreateExamInput { classId: ID! title: String! subject: String! scheduledAt: DateTime! } input AssignHomeworkInput { classId: ID! title: String! dueDate: DateTime! } input RecordGradeInput { examId: ID! studentId: ID! score: Float! } input GenerateQuestionInput { subject: String! gradeLevel: String! difficulty: String! knowledgePointIds: [ID!]! } input AdminUserFilterInput { keyword: String status: String roleId: ID page: Int = 1 pageSize: Int = 20 } input AdminClassFilterInput { keyword: String schoolId: ID gradeId: ID page: Int = 1 pageSize: Int = 20 } input AdminTeacherFilterInput { keyword: String schoolId: ID subject: String page: Int = 1 pageSize: Int = 20 } input AdminStudentFilterInput { keyword: String schoolId: ID gradeId: ID classId: ID page: Int = 1 pageSize: Int = 20 } input AuditLogFilterInput { actorUserId: ID action: String resourceType: String resourceId: ID startDate: DateTime endDate: DateTime page: Int = 1 pageSize: Int = 20 } input DataChangeLogFilterInput { actorUserId: ID tableName: String action: String startDate: DateTime endDate: DateTime page: Int = 1 pageSize: Int = 20 } input LoginLogFilterInput { userId: ID status: String startDate: DateTime endDate: DateTime page: Int = 1 pageSize: Int = 20 } input AnnouncementFilterInput { status: String audience: String keyword: String page: Int = 1 pageSize: Int = 20 } input InvitationCodeFilterInput { status: String roleId: ID batchId: String page: Int = 1 pageSize: Int = 20 } input FileFilterInput { entityType: String entityId: ID mimeType: String page: Int = 1 pageSize: Int = 20 } input CreateUserInput { email: String! name: String! password: String! roleIds: [ID!]! schoolId: ID } input UpdateUserInput { name: String roleIds: [ID!] status: String schoolId: ID } input CreateRoleInput { name: String! code: String! description: String permissions: [String!]! } input UpdateViewportInput { label: String icon: String sortOrder: String isVisible: Boolean } input SystemSettingsInput { schoolName: String schoolCode: String academicYear: String semester: String contactEmail: String contactPhone: String address: String timezone: String locale: String } input CreateAnnouncementInput { title: String! content: String! audience: String! } input GenerateInvitationCodesInput { count: Int! roleId: ID! classId: ID email: String expiresInDays: Int } input UpdateAiProviderInput { name: String apiKey: String baseUrl: String isEnabled: Boolean } # ============ P3-P5 扩展类型 ============ type Student { id: ID! name: String! email: String! avatarUrl: String } type ExamQuestion { id: ID! examId: ID! title: String! type: String! score: Float! order: Int! } type ExamDetail { id: ID! classId: ID! title: String! description: String examDate: DateTime! duration: Int! totalScore: Float! status: ExamStatus! questions: [ExamQuestion!]! } type HomeworkSubmission { id: ID! homeworkId: ID! studentId: ID! studentName: String! status: SubmissionStatus! score: Float feedback: String submittedAt: DateTime } type HomeworkDetail { id: ID! classId: ID! title: String! description: String dueDate: DateTime! status: SubmissionStatus! submissions: [HomeworkSubmission!]! } type GeneratedLessonPlan { id: ID! content: String! summary: String! degraded: Boolean degradedReason: String } type GeneratedReport { id: ID! content: String! summary: String! recommendations: [String!]! degraded: Boolean degradedReason: String } # ============ P4 扩展类型 ============ type KnowledgeGraphNode { id: ID! name: String! subject: String! description: String masteryLevel: Int! } type KnowledgeGraphEdge { from: ID! to: ID! type: String! } type KnowledgeGraph { nodes: [KnowledgeGraphNode!]! edges: [KnowledgeGraphEdge!]! } type ClassAnalytics { classId: ID! className: String! avgScore: Float! passRate: Float! avgTrend: [Float!]! topStudents: [StudentAnalytics!]! weakPoints: [String!]! } type StudentAnalytics { studentId: ID! studentName: String! avgScore: Float! trend: [Float!]! weakPoints: [String!]! strongPoints: [String!]! masteryRate: Float! } # ============ P7-admin 扩展类型 ============ type AttendanceRecord { id: ID! classId: ID! className: String! studentId: ID! studentName: String! studentNo: String! date: String! status: String! note: String } type AttendanceListPage { items: [AttendanceRecord!]! total: Int! page: Int! pageSize: Int! totalPages: Int! } type AttendanceSheetItem { studentId: ID! studentName: String! studentNo: String! date: String! status: String note: String } type AttendanceTrendPoint { date: String! presentRate: Float! lateRate: Float! absentRate: Float! } type AttendanceStats { classId: ID! className: String! startDate: String! endDate: String! totalRecords: Int! presentCount: Int! absentCount: Int! lateCount: Int! earlyLeaveCount: Int! leaveCount: Int! presentRate: Float! lateRate: Float! earlyLeaveRate: Float! leaveRate: Float! warningCount: Int! trend: [AttendanceTrendPoint!]! } type AttendanceClassComparisonItem { classId: ID! className: String! presentRate: Float! lateRate: Float! absentRate: Float! } type AttendanceWarning { studentId: ID! studentName: String! studentNo: String! className: String! presentRate: Float! absentCount: Int! lateCount: Int! } enum AttendanceReportType { WEEKLY MONTHLY } type AttendanceReportSummary { totalRecords: Int! presentRate: Float! lateRate: Float! earlyLeaveRate: Float! leaveRate: Float! warningCount: Int! } type AttendanceReportDetail { studentId: ID! studentName: String! studentNo: String! presentCount: Int! absentCount: Int! lateCount: Int! earlyLeaveCount: Int! leaveCount: Int! presentRate: Float! } type AttendanceReport { classId: ID! className: String! reportType: AttendanceReportType! startDate: String! endDate: String! generatedAt: String! summary: AttendanceReportSummary! details: [AttendanceReportDetail!]! } type ClassDetail { id: ID! name: String! gradeId: String! subject: String! studentCount: Int! homeroomTeacher: String! room: String schoolName: String! } type ClassScheduleItem { id: ID! classId: ID! className: String! dayOfWeek: Int! period: Int! subject: String! teacherName: String! room: String! startTime: String! endTime: String! } type LeaveRequest { id: ID! applicantId: ID! applicantName: String! applicantRole: String! type: String! startDate: String! endDate: String! reason: String! status: String! submittedAt: String! reviewedAt: String reviewerId: ID reviewerName: String reviewComment: String } type LeaveRequestPage { items: [LeaveRequest!]! total: Int! page: Int! pageSize: Int! } type ScheduleChange { id: ID! requesterId: ID! requesterName: String! classId: ID! className: String! originalDate: String! originalPeriod: Int! originalSubject: String! adjustedDate: String! adjustedPeriod: Int! adjustedSubject: String! substituteTeacherId: ID substituteTeacherName: String type: String! reason: String! status: String! submittedAt: String! reviewedAt: String reviewComment: String } type ScheduleChangePage { items: [ScheduleChange!]! total: Int! page: Int! pageSize: Int! } type ElectiveStudent { studentId: ID! studentName: String! studentNo: String! enrolledAt: String! status: String! } type ElectiveCourse { id: ID! title: String! subject: String! grade: String! capacity: Int! enrolledCount: Int! teacherId: ID! teacherName: String! semester: String! status: String! description: String students: [ElectiveStudent!] createdAt: String updatedAt: String } # ============ P7-advanced 扩展类型 ============ type ExamRichContent { examId: ID! title: String! totalScore: Float! questionCount: Int! content: String! updatedAt: String! } type ProctoringSummary { examId: ID! expectedCount: Int! onlineCount: Int! submittedCount: Int! flaggedCount: Int! isProctoring: Boolean! startedAt: String } type ProctoringStudent { studentId: ID! studentName: String! studentNo: String! status: String! lastEventAt: String ipAddress: String } type ProctoringEventItem { eventId: ID! examId: ID! studentId: ID! studentName: String! studentNo: String! eventType: String! note: String createdAt: String! } type ProctoringStatus { summary: ProctoringSummary! students: [ProctoringStudent!]! recentEvents: [ProctoringEventItem!]! } type ProctoringEventResult { eventId: ID! createdAt: String! } type SaveScanGradingResult { submissionId: ID! totalScore: Float! savedAt: String! } # ============ P7-exams 扩展类型 ============ type ExamBuildNode { questionId: ID! score: Float! sortOrder: Int! content: String! type: String! difficulty: String! } type ExamBuild { examId: ID! title: String! totalScore: Float! passScore: Float! duration: Int! selected: [ExamBuildNode!]! candidates: QuestionsLibraryPage! } type SaveExamBuildResult { examId: ID! totalScore: Float! questionCount: Int! savedAt: String! } type ExamAnalyticsSummary { expectedCount: Int! attendedCount: Int! avgScore: Float! maxScore: Float! minScore: Float! passRate: Float! } type ExamScoreBand { label: String! min: Float! max: Float! count: Int! } type ExamQuestionAccuracy { questionId: ID! questionTitle: String! order: Int! correctRate: Float! avgScore: Float! maxScore: Float! } type ExamKpMastery { knowledgePoint: String! mastery: Float! } type ExamClassComparison { classId: ID! className: String! avg: Float! significant: Boolean! } type ExamHistoryTrend { examTitle: String! examDate: String! avg: Float! } type ExamStudentRank { rank: Int! studentId: ID! studentName: String! studentNo: String! totalScore: Float! level: String! } type ExamAnalytics { examId: ID! examTitle: String! summary: ExamAnalyticsSummary! distribution: [ExamScoreBand!]! questionAccuracy: [ExamQuestionAccuracy!]! knowledgeMastery: [ExamKpMastery!]! classComparison: [ExamClassComparison!]! historyTrend: [ExamHistoryTrend!]! rankings: [ExamStudentRank!]! } type Question { questionId: ID! content: String! type: String! difficulty: String! textbookId: ID textbookName: String chapterId: ID chapterName: String kpId: ID kpName: String score: Float! answer: String! analysis: String! createdAt: String! updatedAt: String! } type QuestionsLibraryPage { items: [Question!]! total: Int! page: Int! pageSize: Int! } type QuestionBatchExportResult { items: [Question!]! exportedAt: String! } type QuestionDeleteResult { questionId: ID! deleted: Boolean! } type QuestionBatchImportResult { importedCount: Int! failedCount: Int! errors: [String!]! } type TextbookItem { id: ID! title: String! subject: String! grade: String! version: String! coverUrl: String chapterCount: Int! createdAt: String! } type TextbooksPage { items: [TextbookItem!]! total: Int! page: Int! pageSize: Int! } type TextbookKp { id: ID! name: String! description: String } type TextbookChapter { id: ID! textbookId: ID! title: String! order: Int! content: String! parentId: ID knowledgePoints: [TextbookKp!]! } type TextbookDetail { id: ID! title: String! subject: String! grade: String! version: String! coverUrl: String chapters: [TextbookChapter!]! } # ============ P7-grades 扩展类型 ============ type GradeEntryItem { studentId: ID! studentName: String! studentNo: String! score: Float feedback: String } type GradeRanking { rank: Int! studentId: ID! studentName: String! studentNo: String! totalScore: Float! level: String! } type GradeStats { classId: ID! className: String! subject: String! avg: Float! median: Float! max: Float! min: Float! passRate: Float! stdDev: Float! totalCount: Int! rankings: [GradeRanking!]! } type GradeTrendPoint { month: String! avg: Float! max: Float! min: Float! } type GradeDistributionBand { label: String! min: Float! max: Float! count: Int! } type SubjectComparisonItem { subject: String! avg: Float! } type ClassComparisonItem { classId: ID! className: String! avg: Float! significant: Boolean! } type KnowledgeMasteryItem { knowledgePoint: String! mastery: Float! } type GradeAnalytics { classId: ID! className: String! trend: [GradeTrendPoint!]! distribution: [GradeDistributionBand!]! subjectComparison: [SubjectComparisonItem!]! classComparison: [ClassComparisonItem!]! knowledgeMastery: [KnowledgeMasteryItem!]! } type ReportCardSubject { subject: String! score: Float! rank: Int! teacherName: String! } type ReportCard { studentId: ID! studentName: String! studentNo: String! className: String! academicYear: String! semester: String! subjects: [ReportCardSubject!]! totalScore: Float! classRank: Int! classSize: Int! gradeRank: Int! gradeSize: Int! teacherComment: String! } type HomeworkSubmissionSummary { homeworkId: ID! title: String! classId: ID! className: String! status: String! dueDate: String! totalCount: Int! submittedCount: Int! gradedCount: Int! avgScore: Float } type SubmissionAnswer { questionId: ID! questionTitle: String! questionType: String! maxScore: Float! studentAnswer: String! correctAnswer: String! score: Float feedback: String aiSuggestion: String } type HomeworkSubmissionDetail { submissionId: ID! homeworkId: ID! homeworkTitle: String! classId: ID! className: String! studentId: ID! studentName: String! studentNo: String! status: String! submittedAt: String! totalScore: Float answers: [SubmissionAnswer!]! prevSubmissionId: ID nextSubmissionId: ID } type HomeworkAssignmentSubmissions { submissionId: ID! homeworkId: ID! homeworkTitle: String! classId: ID! className: String! studentId: ID! studentName: String! studentNo: String! status: String! submittedAt: String! totalScore: Float answers: [SubmissionAnswer!]! prevSubmissionId: ID nextSubmissionId: ID } type AiBatchGradingItem { submissionId: ID! suggestedScore: Float! suggestion: String! } type SaveGradeEntriesResult { examId: ID! classId: ID! savedCount: Int! } type GradeSubmissionResult { submissionId: ID! status: String! totalScore: Float! feedback: String } # ============ P7-insights 扩展类型 ============ type LessonPlanAIHistoryItem { id: ID! prompt: String! generatedAt: String! model: String! } type LessonPlan { id: ID! title: String! subject: String! grade: String! chapter: String! status: String! textbookId: ID textbookTitle: String classIds: [String!]! updatedAt: String! } type LessonPlanDetail { id: ID! title: String! subject: String! grade: String! chapter: String! content: String! status: String! textbookId: ID textbookTitle: String classIds: [String!]! aiHistory: [LessonPlanAIHistoryItem!]! updatedAt: String! createdAt: String! } type LessonPlanLibraryItem { id: ID! title: String! author: String! subject: String! grade: String! chapter: String! rating: Float! forkCount: Int! updatedAt: String! } type LessonPlanCalendarDay { date: String! planCount: Int! planTitles: [String!]! } type LessonPlanCalendar { year: Int! month: Int! days: [LessonPlanCalendarDay!]! } type LessonPlanHeatmapCell { textbookKp: String! planIds: [String!]! covered: Boolean! } type LessonPlanHeatmap { rows: [String!]! cols: [String!]! cells: [LessonPlanHeatmapCell!]! } enum CoursePlanStatus { DRAFT PUBLISHED ARCHIVED } type CoursePlanChapter { id: ID! title: String! hours: Int! objectives: String! keyPoints: String! activities: String! } type CoursePlan { id: ID! title: String! subject: String! grade: String! academicYear: String! semester: String! totalHours: Int! status: CoursePlanStatus! textbookId: ID updatedAt: String! } type CoursePlanDetail { id: ID! title: String! subject: String! grade: String! academicYear: String! semester: String! totalHours: Int! status: CoursePlanStatus! textbookId: ID textbookTitle: String homeworkCount: Int! chapters: [CoursePlanChapter!]! updatedAt: String! } enum DiagnosticReportType { INDIVIDUAL CLASS GRADE } enum DiagnosticReportStatus { DRAFT PUBLISHED ARCHIVED } type DiagnosticReport { id: ID! title: String! type: DiagnosticReportType! targetType: String! status: DiagnosticReportStatus! createdAt: String! } type DiagnosticKpDistribution { mastered: Int! partial: Int! weak: Int! } type DiagnosticKpMastery { knowledgePoint: String! avg: Float! median: Float! studentCount: Int! distribution: DiagnosticKpDistribution! } type DiagnosticStudentRank { rank: Int! studentId: ID! studentName: String! masteryAvg: Float! } type ClassDiagnostic { classId: ID! className: String! studentCount: Int! summary: [DiagnosticSummaryItem!]! kpMastery: [DiagnosticKpMastery!]! rankings: [DiagnosticStudentRank!]! } type DiagnosticSummaryItem { knowledgePoint: String! avg: Float! } type DiagnosticRadarPoint { axis: String! value: Float! classAvg: Float! } type StudentDiagnosticReport { id: ID! title: String! createdAt: String! status: DiagnosticReportStatus! } type StudentDiagnostic { studentId: ID! studentName: String! radar: [DiagnosticRadarPoint!]! reports: [StudentDiagnosticReport!]! } type ErrorBookSummary { totalErrors: Int! highFreqErrors: Int! weakKpCount: Int! avgErrorRate: Float! trendUp: Boolean! } type ErrorBookClassComparison { classId: ID! className: String! errorCount: Int! } type ErrorBookChapterWeakness { chapter: String! errorCount: Int! } type ErrorBookKpWeakness { knowledgePoint: String! errorRate: Float! } type ErrorBookTopError { questionId: ID! content: String! errorRate: Float! } type ErrorBookStudentGroup { studentId: ID! studentName: String! errorCount: Int! topErrors: [ErrorBookTopError!]! } type ErrorBookTopQuestion { questionId: ID! content: String! errorRate: Float! subject: String! } type ErrorBook { subject: String! classId: String! summary: ErrorBookSummary! classComparison: [ErrorBookClassComparison!]! chapterWeakness: [ErrorBookChapterWeakness!]! kpWeakness: [ErrorBookKpWeakness!]! studentGroups: [ErrorBookStudentGroup!]! topQuestions: [ErrorBookTopQuestion!]! } type PracticeSummary { totalSessions: Int! completionRate: Float! avgAccuracy: Float! weakKpCount: Int! participationRate: Float! } type PracticeClassComparison { classId: ID! className: String! totalSessions: Int! completionRate: Float! avgAccuracy: Float! participationRate: Float! weakKpCount: Int! } type PracticeTypeBreakdown { type: String! count: Int! ratio: Float! } type PracticeKpWeakness { knowledgePoint: String! accuracy: Float! } type PracticeStudentRank { rank: Int! studentId: ID! studentName: String! studentNo: String! totalSessions: Int! accuracy: Float! weakKp: String! } type PracticeInactiveStudent { studentId: ID! studentName: String! lastActiveDays: Int! } type PracticeAnalytics { classId: String! summary: PracticeSummary! classComparison: [PracticeClassComparison!]! typeBreakdown: [PracticeTypeBreakdown!]! kpWeakness: [PracticeKpWeakness!]! rankings: [PracticeStudentRank!]! inactiveStudents: [PracticeInactiveStudent!]! } type SaveAttendanceSheetResult { classId: ID! date: String! savedCount: Int! savedAt: String! } type SaveExamRichContentResult { examId: ID! updatedAt: String! } type ApproveLeaveRequestResult { requestId: ID! status: String! reviewedAt: String! reviewerId: ID! reviewerName: String! reviewComment: String } type SubmitLeaveRequestResult { requestId: ID! status: String! submittedAt: String! } type SubmitScheduleChangeResult { requestId: ID! status: String! submittedAt: String! } type AIGenerateLessonPlanResult { id: ID! content: String! summary: String! model: String! generatedAt: String! } # ============ P7 扩展 Input 类型 ============ input GenerateLessonPlanInput { classId: String! subject: String! topic: String! objectives: String! } input GenerateReportInput { classId: String! reportType: String! studentId: String } input AttendanceListFilterInput { classId: ID startDate: String endDate: String statuses: [String!] page: Int pageSize: Int } input SaveAttendanceRecordInput { studentId: ID! status: String! note: String } input SaveAttendanceSheetInput { classId: ID! date: String! records: [SaveAttendanceRecordInput!]! } input LeaveRequestsFilterInput { dataScope: String status: String page: Int pageSize: Int } input ApproveLeaveRequestInput { requestId: ID! action: String! comment: String } input SubmitLeaveRequestInput { type: String! startDate: String! endDate: String! reason: String! } input SubmitScheduleChangeInput { classId: ID! originalDate: String! originalPeriod: Int! originalSubject: String! adjustedDate: String! adjustedPeriod: Int! adjustedSubject: String! substituteTeacherId: ID type: String! reason: String! } input CreateElectiveCourseInput { title: String! subject: String! grade: String! capacity: Int! semester: String! description: String } input UpdateElectiveCourseInput { id: ID! title: String subject: String grade: String capacity: Int semester: String description: String } input SaveExamRichContentInput { examId: ID! content: String! } input ProctoringEventInput { examId: ID! studentId: ID! eventType: String! note: String } input SaveScanGradingItem { questionId: ID! score: Float! feedback: String } input SaveScanGradingInput { submissionId: ID! items: [SaveScanGradingItem!]! } input QuestionsLibraryFilter { q: String type: String difficulty: String kp: String textbook: String chapter: String page: Int pageSize: Int } input QuestionCreateInput { content: String! type: String! difficulty: String! textbookId: ID chapterId: ID kpId: ID score: Float! answer: String! analysis: String } input QuestionUpdateInput { questionId: ID! content: String type: String difficulty: String textbookId: ID chapterId: ID kpId: ID score: Float answer: String analysis: String } input QuestionBatchImportItem { content: String! type: String! difficulty: String! textbookId: ID chapterId: ID kpId: ID score: Float! answer: String! analysis: String } input QuestionBatchImportInput { items: [QuestionBatchImportItem!]! } input TextbooksFilter { q: String subject: String grade: String page: Int pageSize: Int } input TextbookCreateInput { title: String! subject: String! grade: String! version: String! coverUrl: String } input SaveExamBuildNodeInput { questionId: ID! score: Float! sortOrder: Int! } input SaveExamBuildInput { examId: ID! questions: [SaveExamBuildNodeInput!]! } input SaveGradeEntryInput { studentId: ID! score: Float! feedback: String } input SaveGradeEntriesInput { examId: ID! classId: ID! entries: [SaveGradeEntryInput!]! } input GradeSubmissionQuestionScore { questionId: ID! score: Float! feedback: String } input GradeSubmissionInput { submissionId: ID! score: Float! feedback: String aiAssisted: Boolean questionScores: [GradeSubmissionQuestionScore!] } input CreateLessonPlanInput { title: String! subject: String! grade: String! chapter: String! textbookId: ID template: String! } input UpdateLessonPlanInput { planId: ID! title: String content: String status: String textbookId: ID chapter: String classIds: [String!] } input AIGenerateLessonPlanInput { subject: String! grade: String! chapter: String! prompt: String! } input CreateCoursePlanInput { title: String! subject: String! grade: String! academicYear: String! semester: String! totalHours: Int! textbookId: ID } input UpdateCoursePlanInput { id: ID! title: String status: CoursePlanStatus chapters: [CoursePlanChapterInput!] } input CoursePlanChapterInput { id: ID title: String! hours: Int! objectives: String! keyPoints: String! activities: String! }