# student-bff GraphQL Schema (v2) # # 负责人: ai04 # 仲裁依据: coord-final-decisions §2 B1-B8 + president-final-rulings §2.2 # 存放路径: packages/shared-ts/contracts/graphql/student-bff.schema.graphql (president §2.2.1) # # 设计规范 (president §2.2.5): # - Query/Mutation 用 camelCase # - 分页采用 Relay Cursor Connections 规范 ({ edges, pageInfo, totalCount }) # - 错误响应: GraphQL errors 数组 + extensions.code + extensions.traceId # - 权限点标注: # @permission: _ # - DataScope 标注: # @dataScope: OWN (学生数据隔离 SELF) # - Type 用 PascalCase, 字段用 camelCase, 枚举用 UPPER_SNAKE_CASE # - 必填字段用 !, 可空字段不标 ! (避免破坏性变更) # # 降级模式 (president §2.6 方案 B): # - 下游不可用时 success=true + error=null + data 内 degraded=true # - 降级字段返回 null, 父对象加 degraded/degradedReason/degradedFields # # v2 变更: # - 补全 22 个扩展 Query (examDetail/homeworkDetail/serverTime/mySchedule/...) # - 补全 21 个扩展 Mutation (submitExam/saveExamDraft/updateProfile/...) # - startPracticeSession 从 Query 移到 Mutation (语义为创建会话) # - 总计 36 Query + 23 Mutation + 1 Subscription = 60 operations scalar DateTime scalar JSON # ============================================================================ # Relay Cursor Connections 规范 # ============================================================================ interface Node { id: ID! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } # ============================================================================ # 错误扩展 (GraphQL errors 数组 + extensions) # ============================================================================ """ GraphQL errors 数组中 extensions 字段规范 (president §2.2.3 + G8 裁决). 由 GlobalErrorFilter 注入, 不在 schema 中显式暴露. """ type GraphQLErrorExtension { code: String! # BFF_STUDENT_* 前缀 (B5 裁决) traceId: String! # 全链路追踪 ID (Gateway 注入 X-Request-Id) i18nKey: String! # i18n key: error.bffStudent. (F4 裁决) severity: String! # error / warning / info } # ============================================================================ # 通用类型 # ============================================================================ """ 降级标记 (president §2.6 方案 B). 当下游服务不可用但需返回部分数据时, 父对象包含此接口字段. """ interface Degradable { degraded: Boolean! degradedReason: String degradedFields: [String!] } type DegradationInfo { degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 用户与权限 (下游: iam) # ============================================================================ type UserProfile { id: ID! email: String! name: String! avatar: String roles: [String!]! permissions: [String!]! } type ViewportItem { key: String! label: String! route: String! icon: String sortOrder: Int! requiredPermission: String } type ViewportConfig { navigation: [ViewportItem!]! dataScope: StudentDataScope! } type StudentDataScope { showHistoryGrades: Boolean! showClassRanking: Boolean! enableAIChat: Boolean! } type CurrentUserPayload implements Degradable { user: UserProfile viewports: ViewportConfig effectivePermissions: [String!]! dataScope: String # SELF (学生固定) degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 班级 (下游: core-edu ClassService) # ============================================================================ type StudentClass { id: ID! name: String! gradeId: String! gradeName: String subjects: [String!]! homeroomTeacher: TeacherBrief } type TeacherBrief { id: ID! name: String! avatar: String } type StudentClassConnection { edges: [StudentClassEdge!]! pageInfo: PageInfo! totalCount: Int! } type StudentClassEdge { node: StudentClass! cursor: String! } type StudentClassPayload implements Degradable { classes: [StudentClass!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 考试 (下游: core-edu ExamService) # ============================================================================ enum ExamStatus { DRAFT PUBLISHED IN_PROGRESS COMPLETED CANCELLED ARCHIVED } type Exam { id: ID! classId: ID! title: String! description: String examDate: DateTime! duration: Int! # 考试时长(分钟) totalScore: Float! status: ExamStatus! createdBy: ID! createdAt: DateTime! updatedAt: DateTime! daysLeft: Int # 距考试天数(负数表示已过) } type ExamConnection { edges: [ExamEdge!]! pageInfo: PageInfo! totalCount: Int! } type ExamEdge { node: Exam! cursor: String! } type ExamListPayload implements Degradable { exams: [Exam!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 考试详情 (含题目与学生提交) type ExamDetailPayload implements Degradable { exam: JSON questions: [JSON!] mySubmission: JSON degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 作业 (下游: core-edu HomeworkService) # ============================================================================ enum HomeworkStatus { ASSIGNED SUBMITTED GRADED OVERDUE RETURNED } type Homework { id: ID! classId: ID! title: String! description: String dueDate: DateTime! status: HomeworkStatus! createdBy: ID! createdAt: DateTime! updatedAt: DateTime! submission: HomeworkSubmission # 当前学生的提交 } type HomeworkSubmission { id: ID! homeworkId: ID! studentId: ID! status: HomeworkStatus! submittedAt: DateTime gradedAt: DateTime score: Float feedback: String } type HomeworkConnection { edges: [HomeworkEdge!]! pageInfo: PageInfo! totalCount: Int! } type HomeworkEdge { node: Homework! cursor: String! } type HomeworkListPayload implements Degradable { homework: [Homework!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 作业详情 type HomeworkDetailPayload implements Degradable { homework: JSON degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 成绩 (下游: core-edu GradeService) # ============================================================================ type Grade { id: ID! studentId: ID! examId: ID homeworkId: ID examTitle: String homeworkTitle: String subject: String score: Float! totalScore: Float! feedback: String gradedBy: ID! gradedAt: DateTime! createdAt: DateTime! updatedAt: DateTime! } type GradeConnection { edges: [GradeEdge!]! pageInfo: PageInfo! totalCount: Int! } type GradeEdge { node: Grade! cursor: String! } type GradeListPayload implements Degradable { grades: [Grade!]! averageScore: Float totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 成绩报告卡 type ReportCardPayload implements Degradable { reportCard: JSON degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 考勤 (下游: core-edu AttendanceService) # ============================================================================ enum AttendanceStatus { PRESENT ABSENT LATE EARLY_LEAVE EXCUSED } type AttendanceRecord { id: ID! studentId: ID! classId: ID! date: DateTime! status: AttendanceStatus! remark: String recordedBy: ID! createdAt: DateTime! } type AttendanceConnection { edges: [AttendanceEdge!]! pageInfo: PageInfo! totalCount: Int! } type AttendanceEdge { node: AttendanceRecord! cursor: String! } type AttendanceListPayload implements Degradable { records: [AttendanceRecord!]! presentCount: Int! absentCount: Int! lateCount: Int! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 课表 / 请假 / 选课 / 课案 / 课程计划 (下游: core-edu + content) # ============================================================================ # 服务器时间 type ServerTimePayload { serverTime: String! timezone: String! timestamp: Int! } # 课表 type ScheduleItem { id: ID! classId: ID! className: String subject: String! date: DateTime! startTime: DateTime! endTime: DateTime! teacher: TeacherBrief room: String } type SchedulePayload implements Degradable { items: [ScheduleItem!]! weekStart: String degraded: Boolean! degradedReason: String degradedFields: [String!] } # 请假记录 type LeaveRequest { id: ID! studentId: ID! type: String! startDate: DateTime! endDate: DateTime! reason: String! status: String! attachments: [String!] createdAt: DateTime! updatedAt: DateTime! } type LeaveRequestsPayload implements Degradable { requests: [LeaveRequest!]! totalCount: Int! pendingCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 选课记录 type ElectiveSelection { id: ID! studentId: ID! courseId: ID! courseName: String! status: String! selectedAt: DateTime! } type ElectiveSelectionsPayload implements Degradable { selections: [ElectiveSelection!]! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 可选课程 type ElectiveCourse { id: ID! title: String! subject: String! description: String capacity: Int! enrolledCount: Int! teacher: TeacherBrief } type AvailableElectiveCoursesPayload implements Degradable { courses: [ElectiveCourse!]! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 课案 type LessonPlan { id: ID! title: String! subject: String! description: String objectives: [String!] content: JSON createdBy: ID! createdAt: DateTime! updatedAt: DateTime! } type LessonPlansPayload implements Degradable { plans: [LessonPlan!]! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } type LessonPlanDetailPayload implements Degradable { plan: LessonPlan degraded: Boolean! degradedReason: String degradedFields: [String!] } # 课程计划 type CoursePlan { id: ID! title: String! subject: String! description: String startDate: DateTime! endDate: DateTime! objectives: [String!] createdBy: ID! createdAt: DateTime! updatedAt: DateTime! } type CoursePlansPayload implements Degradable { plans: [CoursePlan!]! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } type CoursePlanDetailPayload implements Degradable { plan: CoursePlan degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 教材与章节 (下游: content, P4) # ============================================================================ type Textbook { id: ID! title: String! subjectId: ID! subjectName: String gradeId: ID! gradeName: String version: String! coverImage: String chapters: [Chapter!]! } type Chapter { id: ID! textbookId: ID! title: String! description: String sortOrder: Int! parentId: ID # 父章节(支持章节树) knowledgePoints: [KnowledgePoint!]! } type KnowledgePoint { id: ID! title: String! description: String mastery: Float # 0-1 掌握度 } type TextbookConnection { edges: [TextbookEdge!]! pageInfo: PageInfo! totalCount: Int! } type TextbookEdge { node: Textbook! cursor: String! } type TextbookListPayload implements Degradable { textbooks: [Textbook!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } type ChapterListPayload implements Degradable { chapters: [Chapter!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 学习路径 (下游: content KnowledgeGraphService, P4) # ============================================================================ type LearningPath { studentId: ID! subjectId: ID! points: [KnowledgePoint!]! recommendedOrder: [String!]! # 知识点 id 顺序 estimatedHours: Float } type LearningPathPayload implements Degradable { path: LearningPath degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 学情分析 (下游: data-ana, P4) # ============================================================================ type StudentDashboard { studentId: ID! averageScore: Float classRank: Int totalStudents: Int pendingHomeworkCount: Int! upcomingExamCount: Int! unreadNotificationCount: Int! lastGrade: Grade weakness: WeakPointSummary # P4 data-ana 启用后填充 trend: LearningTrendSummary # P4 data-ana 启用后填充 } type WeakPointSummary { knowledgePointId: ID! title: String! mastery: Float! subject: String } type StudentWeakness { studentId: ID! subjectId: ID weakPoints: [WeakPoint!]! } type WeakPoint { knowledgePointId: ID! title: String! mastery: Float! subject: String lastAssessedAt: DateTime } type LearningTrendSummary { studentId: ID! subjectId: ID points: [TrendPoint!]! } type LearningTrend { studentId: ID! subjectId: ID points: [TrendPoint!]! } type TrendPoint { date: DateTime! score: Float! subject: String } type StudentDashboardPayload implements Degradable { dashboard: StudentDashboard degraded: Boolean! degradedReason: String degradedFields: [String!] } type WeaknessPayload implements Degradable { weakness: StudentWeakness degraded: Boolean! degradedReason: String degradedFields: [String!] } type TrendPayload implements Degradable { trend: LearningTrend degraded: Boolean! degradedReason: String degradedFields: [String!] } # 学生成长曲线 type StudentGrowthPayload implements Degradable { studentId: ID! subjectId: ID points: [TrendPoint!]! averageScore: Float classAverage: Float growthRate: Float degraded: Boolean! degradedReason: String degradedFields: [String!] } # 作业分析 type AssignmentAnalysisPayload implements Degradable { analysis: JSON degraded: Boolean! degradedReason: String degradedFields: [String!] } # 个人资料 type MyProfilePayload implements Degradable { profile: JSON degraded: Boolean! degradedReason: String degradedFields: [String!] } # 掌握度概览 type MasterySummaryPayload implements Degradable { summary: JSON degraded: Boolean! degradedReason: String degradedFields: [String!] } # 诊断报告 type DiagnosticReportsPayload implements Degradable { reports: [JSON!]! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 错题本 type ErrorBookItem { id: ID! questionId: ID! subjectId: ID! knowledgePointId: ID! myAnswer: String correctAnswer: String! note: String tags: [String!] mastered: Boolean! createdAt: DateTime! updatedAt: DateTime! } type ErrorBookPayload implements Degradable { items: [ErrorBookItem!]! totalCount: Int! subjectStats: [JSON!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 练习会话 type PracticeSession { id: ID! studentId: ID! subjectId: ID! status: String! totalQuestions: Int! answeredCount: Int! startedAt: DateTime! completedAt: DateTime } type PracticeSessionsPayload implements Degradable { sessions: [PracticeSession!]! totalCount: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # 通知 / 公告 (下游: msg, P5) # ============================================================================ enum NotificationType { HOMEWORK_ASSIGNED HOMEWORK_GRADED EXAM_PUBLISHED EXAM_UPDATED GRADE_RECORDED SYSTEM ANNOUNCEMENT } enum NotificationChannel { IN_APP EMAIL SMS PUSH } type Notification { id: ID! userId: ID! type: NotificationType! title: String! content: String! channel: NotificationChannel! isRead: Boolean! createdAt: DateTime! readAt: DateTime metadata: JSON } type NotificationConnection { edges: [NotificationEdge!]! pageInfo: PageInfo! totalCount: Int! } type NotificationEdge { node: Notification! cursor: String! } type NotificationListPayload implements Degradable { notifications: [Notification!]! degraded: Boolean! degradedReason: String degradedFields: [String!] } type UnreadCountPayload implements Degradable { count: Int! degraded: Boolean! degradedReason: String degradedFields: [String!] } # 公告 type Announcement { id: ID! title: String! content: String! authorId: ID! status: String! category: String targetAudience: String publishedAt: DateTime createdAt: DateTime! updatedAt: DateTime! } type AnnouncementConnection { edges: [AnnouncementEdge!]! pageInfo: PageInfo! totalCount: Int! } type AnnouncementEdge { node: Announcement! cursor: String! } type AnnouncementDetailPayload implements Degradable { announcement: Announcement degraded: Boolean! degradedReason: String degradedFields: [String!] } # ============================================================================ # AI 答疑 (下游: ai, P5) # ============================================================================ type AIChatResponse { content: String! model: String! usage: AIUsage! } type AIUsage { promptTokens: Int! completionTokens: Int! totalTokens: Int! } type AIChatPayload implements Degradable { response: AIChatResponse degraded: Boolean! degradedReason: String degradedFields: [String!] } input AIChatInput { messages: [AIChatMessageInput!]! model: String = "gpt-4o-mini" context: AIChatContextInput } # ============================================================================ # Mutation 结果 # ============================================================================ type MutationError { code: String! # BFF_STUDENT_* 前缀 message: String! traceId: String i18nKey: String } type SubmitHomeworkResult { success: Boolean! submissionId: ID homeworkId: ID! submittedAt: DateTime status: HomeworkStatus error: MutationError } type MarkNotificationReadResult { success: Boolean! notificationId: ID! readAt: DateTime error: MutationError } # 通用 Mutation 结果 type GenericMutationResult { success: Boolean! error: MutationError } # 通知相关 Mutation 结果 type MarkAllAsReadResult { success: Boolean! markedCount: Int! error: MutationError } # 考试相关 Mutation 结果 type SubmitExamResult { success: Boolean! submissionId: ID examId: ID! submittedAt: DateTime score: Float status: String error: MutationError } type SaveExamDraftResult { success: Boolean! draftId: ID examId: ID! savedAt: DateTime error: MutationError } type RecordExamViolationResult { success: Boolean! violationId: ID examId: ID! violationType: String! recordedAt: DateTime error: MutationError } # 个人资料相关 Mutation 结果 type UpdateProfileResult { success: Boolean! profile: JSON error: MutationError } # 作业延期申请 type RequestExtensionResult { success: Boolean! requestId: ID homeworkId: ID! status: String! error: MutationError } # 班级相关 Mutation 结果 type JoinClassResult { success: Boolean! classId: ID! joinedAt: DateTime error: MutationError } type LeaveClassResult { success: Boolean! classId: ID! leftAt: DateTime error: MutationError } # 错题相关 Mutation 结果 type ErrorBookItemResult { success: Boolean! itemId: ID error: MutationError } # 请假相关 Mutation 结果 type CreateLeaveRequestResult { success: Boolean! requestId: ID status: String! error: MutationError } # 选课相关 Mutation 结果 type SelectElectiveResult { success: Boolean! selectionId: ID courseId: ID! status: String! error: MutationError } # 练习相关 Mutation 结果 type SubmitPracticeAnswerResult { success: Boolean! questionId: ID! isCorrect: Boolean correctAnswer: String explanation: String error: MutationError } type StartPracticeSessionResult { success: Boolean! session: JSON questions: [JSON!]! error: MutationError } # ============================================================================ # Query 根类型 (36 Queries) # ============================================================================ type Query { """当前学生信息 + 权限 + 视口 (聚合 iam.GetUserInfo + GetEffectivePermissions + GetViewports)""" # @permission: AUTH_READ # @dataScope: OWN currentUser: CurrentUserPayload! """我的班级列表 (core-edu.ClassService.GetClass + ListStudentsByClass)""" # @permission: CLASS_READ # @dataScope: OWN myClasses( after: String first: Int = 20 before: String last: Int ): StudentClassConnection! """我的考试列表 (core-edu.ExamService.ListExamsByClass)""" # @permission: EXAM_READ # @dataScope: OWN myExams( after: String first: Int = 20 before: String last: Int status: ExamStatus ): ExamConnection! """我的作业列表 (core-edu.HomeworkService.ListHomeworkByClass)""" # @permission: HOMEWORK_READ # @dataScope: OWN myHomework( after: String first: Int = 20 before: String last: Int status: HomeworkStatus ): HomeworkConnection! """我的成绩列表 (core-edu.GradeService.ListGradesByStudent, B4 强制 userId 比对)""" # @permission: GRADE_READ # @dataScope: OWN myGrades( after: String first: Int = 20 before: String last: Int subject: String startDate: DateTime endDate: DateTime ): GradeConnection! """我的考勤记录 (core-edu.AttendanceService.ListAttendanceByStudent)""" # @permission: ATTENDANCE_READ # @dataScope: OWN myAttendance( after: String first: Int = 20 before: String last: Int startDate: DateTime endDate: DateTime ): AttendanceConnection! """教材列表 (content.TextbookService.ListTextbooks)""" # @permission: TEXTBOOK_READ # @dataScope: OWN textbooks( after: String first: Int = 20 before: String last: Int subjectId: ID gradeId: ID ): TextbookConnection! """章节列表 (content.ChapterService.ListChapters)""" # @permission: CHAPTER_READ # @dataScope: OWN chapters( after: String first: Int = 20 before: String last: Int textbookId: ID! ): ChapterListPayload! """学习路径推荐 (content.KnowledgeGraphService.GetLearningPath)""" # @permission: LEARNING_PATH_READ # @dataScope: OWN learningPath(subjectId: ID!): LearningPathPayload! """学生仪表盘 (data-ana.AnalyticsService.GetStudentDashboard)""" # @permission: DASHBOARD_VIEW # @dataScope: OWN studentDashboard: StudentDashboardPayload! """我的薄弱点 (data-ana.AnalyticsService.GetStudentWeakness)""" # @permission: WEAKNESS_READ # @dataScope: OWN myWeakness(subjectId: ID): WeaknessPayload! """学习趋势 (data-ana.AnalyticsService.GetLearningTrend)""" # @permission: TREND_READ # @dataScope: OWN myTrend( subjectId: ID startDate: DateTime endDate: DateTime ): TrendPayload! """我的通知列表 (msg.NotificationService.ListNotifications)""" # @permission: NOTIFICATION_READ # @dataScope: OWN myNotifications( after: String first: Int = 20 before: String last: Int onlyUnread: Boolean = false ): NotificationConnection! """通知未读数 (msg.NotificationService.GetUnreadCount)""" # @permission: NOTIFICATION_READ # @dataScope: OWN myNotificationUnreadCount: UnreadCountPayload! """AI 答疑 (同步, ai.Chat). 实际为写操作但 schema 设计为 Query 便于前端 GET 缓存""" # @permission: STUDENT_AI_CHAT # @dataScope: OWN aiChat(input: AIChatInput!): AIChatPayload! # ===== 扩展 Query (v2 新增, P3-P5) ===== """考试详情 (含题目与学生提交, core-edu.ExamService.GetExam)""" # @permission: EXAM_READ # @dataScope: OWN examDetail(examId: ID!): ExamDetailPayload! """作业详情 (core-edu.HomeworkService.GetHomework)""" # @permission: HOMEWORK_READ # @dataScope: OWN homeworkDetail(homeworkId: ID!): HomeworkDetailPayload! """服务器时间 (BFF 本地, 无下游)""" serverTime: ServerTimePayload! """我的课表 (周视图, core-edu.ScheduleService.GetScheduleByStudent)""" # @permission: SCHEDULE_READ # @dataScope: OWN mySchedule(weekStart: DateTime): SchedulePayload! """学生成长曲线 (data-ana.GetStudentGrowth)""" # @permission: ANALYTICS_READ # @dataScope: OWN studentGrowth( subjectId: ID startDate: DateTime endDate: DateTime ): StudentGrowthPayload! """作业分析 (data-ana.GetAssignmentAnalysis)""" # @permission: ANALYTICS_READ # @dataScope: OWN assignmentAnalysis(homeworkId: ID): AssignmentAnalysisPayload! """个人资料 (iam.GetUserProfile)""" # @permission: AUTH_READ # @dataScope: OWN myProfile: MyProfilePayload! """掌握度概览 (data-ana.GetMasterySummary)""" # @permission: ANALYTICS_READ # @dataScope: OWN myMasterySummary(subjectId: ID): MasterySummaryPayload! """诊断报告列表 (data-ana.ListDiagnosticReports)""" # @permission: ANALYTICS_READ # @dataScope: OWN myDiagnosticReports: DiagnosticReportsPayload! """错题本 (data-ana.ListErrorBookItems)""" # @permission: ANALYTICS_READ # @dataScope: OWN myErrorBook( subjectId: ID mastered: Boolean page: Int = 1 pageSize: Int = 20 ): ErrorBookPayload! """公告列表 (msg.ListAnnouncements)""" # @permission: ANNOUNCEMENT_READ # @dataScope: OWN announcements( first: Int = 20 category: String ): AnnouncementConnection! """公告详情 (msg.GetAnnouncement)""" # @permission: ANNOUNCEMENT_READ # @dataScope: OWN announcementDetail(announcementId: ID!): AnnouncementDetailPayload! """我的请假记录 (core-edu.ListLeaveRequestsByStudent)""" # @permission: LEAVE_REQUEST_READ # @dataScope: OWN myLeaveRequests: LeaveRequestsPayload! """我的选课记录 (content.ListElectiveSelectionsByStudent)""" # @permission: ELECTIVE_READ # @dataScope: OWN myElectiveSelections: ElectiveSelectionsPayload! """可选课程 (content.ListAvailableElectiveCourses)""" # @permission: ELECTIVE_READ # @dataScope: OWN availableElectiveCourses: AvailableElectiveCoursesPayload! """我的课案 (content.ListLessonPlansByStudent)""" # @permission: LESSON_PLAN_READ # @dataScope: OWN myLessonPlans: LessonPlansPayload! """课案详情 (content.GetLessonPlan)""" # @permission: LESSON_PLAN_READ # @dataScope: OWN lessonPlanDetail(lessonPlanId: ID!): LessonPlanDetailPayload! """我的课程计划 (content.ListCoursePlansByStudent)""" # @permission: COURSE_PLAN_READ # @dataScope: OWN myCoursePlans: CoursePlansPayload! """课程计划详情 (content.GetCoursePlan)""" # @permission: COURSE_PLAN_READ # @dataScope: OWN coursePlanDetail(coursePlanId: ID!): CoursePlanDetailPayload! """成绩报告卡 (core-edu.GradeService.GetReportCard)""" # @permission: GRADE_READ # @dataScope: OWN myReportCard( examId: ID term: String academicYear: String ): ReportCardPayload! """我的练习会话列表 (data-ana.ListPracticeSessionsByStudent)""" # @permission: ANALYTICS_READ # @dataScope: OWN myPracticeSessions: PracticeSessionsPayload! } # ============================================================================ # Mutation 根类型 (23 Mutations) # ============================================================================ type Mutation { """提交作业 (core-edu.HomeworkService.SubmitHomework, B4 强制 userId 比对)""" # @permission: HOMEWORK_SUBMIT # @dataScope: OWN submitHomework( input: SubmitHomeworkInput! ): SubmitHomeworkResult! """标记通知已读 (msg.NotificationService.MarkAsRead)""" # @permission: NOTIFICATION_UPDATE # @dataScope: OWN markNotificationAsRead( input: MarkNotificationReadInput! ): MarkNotificationReadResult! # ===== 扩展 Mutation (v2 新增, P3-P5) ===== """标记通知已读 (别名, msg.MarkAsRead)""" # @permission: NOTIFICATION_UPDATE # @dataScope: OWN markAsRead(input: MarkAsReadInput!): MarkNotificationReadResult! """全部通知标记已读 (msg.MarkAllNotificationsAsRead)""" # @permission: NOTIFICATION_UPDATE # @dataScope: OWN markAllAsRead(input: MarkAllAsReadInput!): MarkAllAsReadResult! """更新通知偏好 (msg.UpdateNotificationPreferences)""" # @permission: NOTIFICATION_UPDATE # @dataScope: OWN updateNotificationPreference( input: UpdateNotificationPreferenceInput! ): GenericMutationResult! """提交考试 (含幂等键, core-edu.ExamService.SubmitExam)""" # @permission: EXAM_SUBMIT # @dataScope: OWN submitExam(input: SubmitExamInput!): SubmitExamResult! """保存考试草稿 (core-edu.ExamService.SaveExamDraft)""" # @permission: EXAM_SUBMIT # @dataScope: OWN saveExamDraft(input: SaveExamDraftInput!): SaveExamDraftResult! """记录考试违规 (防作弊, core-edu.ExamService.RecordExamViolation)""" # @permission: EXAM_RECORD_VIOLATION # @dataScope: OWN recordExamViolation(input: RecordViolationInput!): RecordExamViolationResult! """记录粘贴事件 (防作弊, core-edu.ExamService.RecordPasteEvent)""" # @permission: EXAM_SUBMIT # @dataScope: OWN recordPasteEvent(input: RecordPasteEventInput!): RecordExamViolationResult! """更新个人资料 (iam.UpdateUserProfile)""" # @permission: AUTH_UPDATE # @dataScope: OWN updateProfile(input: UpdateProfileInput!): UpdateProfileResult! """修改密码 (iam.ChangePassword)""" # @permission: AUTH_UPDATE # @dataScope: OWN changePassword(input: ChangePasswordInput!): GenericMutationResult! """作业延期申请 (core-edu.RequestHomeworkExtension)""" # @permission: HOMEWORK_UPDATE # @dataScope: OWN requestExtension(input: RequestExtensionInput!): RequestExtensionResult! """加入班级 (core-edu.JoinClass)""" # @permission: CLASS_JOIN # @dataScope: OWN joinClass(input: JoinClassInput!): JoinClassResult! """退出班级 (core-edu.LeaveClass)""" # @permission: CLASS_LEAVE # @dataScope: OWN leaveClass(input: LeaveClassInput!): LeaveClassResult! """添加错题 (data-ana.AddErrorBookItem)""" # @permission: ANALYTICS_UPDATE # @dataScope: OWN addErrorBookItem(input: AddErrorBookItemInput!): ErrorBookItemResult! """更新错题 (data-ana.UpdateErrorBookItem)""" # @permission: ANALYTICS_UPDATE # @dataScope: OWN updateErrorBookItem(input: UpdateErrorBookItemInput!): ErrorBookItemResult! """删除错题 (data-ana.DeleteErrorBookItem)""" # @permission: ANALYTICS_UPDATE # @dataScope: OWN deleteErrorBookItem(input: DeleteErrorBookItemInput!): ErrorBookItemResult! """标记公告已读 (msg.MarkAnnouncementAsRead)""" # @permission: ANNOUNCEMENT_READ # @dataScope: OWN markAnnouncementRead(input: MarkAnnouncementReadInput!): GenericMutationResult! """创建请假申请 (core-edu.CreateLeaveRequest)""" # @permission: LEAVE_REQUEST_CREATE # @dataScope: OWN createLeaveRequest(input: CreateLeaveRequestInput!): CreateLeaveRequestResult! """取消请假申请 (core-edu.CancelLeaveRequest)""" # @permission: LEAVE_REQUEST_UPDATE # @dataScope: OWN cancelLeaveRequest(input: CancelLeaveRequestInput!): CreateLeaveRequestResult! """选择课程 (content.SelectElectiveCourse)""" # @permission: ELECTIVE_SELECT # @dataScope: OWN selectElectiveCourse(input: SelectElectiveInput!): SelectElectiveResult! """退选课程 (content.DropElectiveCourse)""" # @permission: ELECTIVE_DROP # @dataScope: OWN dropElectiveCourse(input: DropElectiveInput!): SelectElectiveResult! """启动练习会话 (data-ana.StartPracticeSession, 从 Query 移到 Mutation)""" # @permission: ANALYTICS_UPDATE # @dataScope: OWN startPracticeSession(input: StartPracticeSessionInput!): StartPracticeSessionResult! """提交练习答案 (data-ana.SubmitPracticeAnswer)""" # @permission: ANALYTICS_UPDATE # @dataScope: OWN submitPracticeAnswer(input: SubmitPracticeAnswerInput!): SubmitPracticeAnswerResult! } # ============================================================================ # Input 类型 # ============================================================================ input SubmitHomeworkInput { homeworkId: ID! studentId: ID! # 必须与 x-user-id 一致 (B4 越权防御) answers: [HomeworkAnswerInput!]! } input HomeworkAnswerInput { questionId: ID! content: String! # 最大 5000 字符 attachments: [String!] # URL 列表, 最多 5 个 } input MarkNotificationReadInput { notificationId: ID! userId: ID! # 必须与 x-user-id 一致 (B4 越权防御) } input MarkAsReadInput { notificationId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input MarkAllAsReadInput { studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input UpdateNotificationPreferenceInput { channel: NotificationChannel! enabled: Boolean! categories: [String!] } input SubmitExamInput { examId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 answers: [ExamAnswerInput!]! idempotencyKey: String! } input ExamAnswerInput { questionId: ID! content: String! attachments: [String!] } input SaveExamDraftInput { examId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 answers: [ExamAnswerInput!]! draftId: ID } input RecordViolationInput { examId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 violationType: ViolationType! severity: ViolationSeverity! details: String timestamp: DateTime! } enum ViolationType { TAB_SWITCH COPY_PASTE WINDOW_BLUR FULLSCREEN_EXIT } enum ViolationSeverity { LOW MEDIUM HIGH } input RecordPasteEventInput { examId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 questionId: ID! pastedContent: String! timestamp: DateTime! } input UpdateProfileInput { name: String avatar: String phone: String address: String bio: String birthday: String gender: String } input ChangePasswordInput { currentPassword: String! newPassword: String! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input RequestExtensionInput { homeworkId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 reason: String! requestedDays: Int! } input JoinClassInput { classCode: String! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input LeaveClassInput { classId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 reason: String } input AddErrorBookItemInput { questionId: ID! subjectId: ID! knowledgePointId: ID! myAnswer: String correctAnswer: String! note: String tags: [String!] } input UpdateErrorBookItemInput { itemId: ID! note: String tags: [String!] mastered: Boolean } input DeleteErrorBookItemInput { itemId: ID! } input MarkAnnouncementReadInput { announcementId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input CreateLeaveRequestInput { type: LeaveRequestType! startDate: DateTime! endDate: DateTime! reason: String! attachments: [String!] studentId: ID # 可选, 若提供必须与 x-user-id 一致 } enum LeaveRequestType { SICK PERSONAL FAMILY OTHER } input CancelLeaveRequestInput { requestId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input SelectElectiveInput { courseId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input DropElectiveInput { courseId: ID! studentId: ID # 可选, 若提供必须与 x-user-id 一致 } input StartPracticeSessionInput { subjectId: ID! knowledgePointIds: [ID!] difficulty: PracticeDifficulty } enum PracticeDifficulty { EASY MEDIUM HARD ADAPTIVE } input SubmitPracticeAnswerInput { sessionId: ID! questionId: ID! answer: String! timeSpent: Int } # ============================================================================ # AI 流式答疑 (SSE Subscription, P5) # ============================================================================ type AIStreamChunk { content: String! # 本次分块内容 done: Boolean! # 是否结束 model: String usage: AIUsage # done=true 时填充 } input AIStreamChatInput { messages: [AIChatMessageInput!]! model: String = "gpt-4o-mini" context: AIChatContextInput } input AIChatMessageInput { role: String! # user / assistant content: String! } input AIChatContextInput { subject: String knowledgePointId: ID } # ============================================================================ # Subscription 根类型 (SSE 传输, GraphQL Yoga 原生支持) # ============================================================================ type Subscription { """AI 答疑流式响应 (ai.StreamChat, SSE 传输)""" # @permission: STUDENT_AI_CHAT # @dataScope: OWN aiStreamChat(input: AIStreamChatInput!): AIStreamChunk! }