# parent-bff GraphQL Schema(SDL 契约,对齐 02-architecture-design.md §4.2) # # 本文件是 parent-bff GraphQL 端点的契约唯一源(single source of truth)。 # - 前端 parent-portal 引用本文件生成 TypeScript 类型 # - 后端 parent-bff 引用本文件加载为 GraphQLSchema # - CI 强制检测 breaking change(与 main 分支对比) # # 仲裁依据: # - U3:GraphQL P2 立即引入(parent-bff P4 直接用 GraphQL,不走 REST 过渡) # - C1:错误码前缀 BFF_PARENT_ # - U4:BFF 豁免 @RequirePermission,仅校验 x-user-id + ChildGuard # - 02 §9 #5:depth ≤ 7 / cost ≤ 1000 scalar DateTime # ============ Types ============ type Parent { id: ID! email: String! name: String! avatar: String roles: [String!]! dataScope: DataScope! } enum DataScope { SELF CHILDREN CLASS GRADE SCHOOL DISTRICT ALL } type ViewportItem { key: String! label: String! route: String! icon: String sortOrder: String! requiredPermission: String } type Child { id: ID! name: String! grade: String! class: ClassInfo! lastGrade: Grade grades(page: Int = 1, pageSize: Int = 20, subject: String): GradePage! homework: [Homework!]! exams: [Exam!]! analytics: ChildAnalytics! } type ClassInfo { id: ID! name: String! gradeId: String! } type Grade { id: ID! examId: ID! examTitle: String! subject: String! score: Float! rank: Int gradedAt: DateTime! } type GradePage { items: [Grade!]! pagination: Pagination! } type Pagination { page: Int! pageSize: Int! total: Int! } type Homework { id: ID! title: String! classId: ID! dueDate: DateTime! status: HomeworkStatus! submittedAt: DateTime } enum HomeworkStatus { NOT_SUBMITTED SUBMITTED GRADED OVERDUE } type Exam { id: ID! title: String! classId: ID! status: ExamStatus! examDate: DateTime! publishedAt: DateTime } enum ExamStatus { DRAFT PUBLISHED IN_PROGRESS GRADING SCORED ARCHIVED } type ChildAnalytics { childId: ID! weakness: [WeaknessTopic!]! trend: [TrendPoint!]! classRank: Int classAverage: Float } type WeaknessTopic { knowledgePointId: ID! name: String! subject: String! masteryRate: Float! } type TrendPoint { date: DateTime! score: Float! subject: String } type Notification { id: ID! type: NotificationType! title: String! content: String! read: Boolean! childId: ID createdAt: DateTime! } enum NotificationType { SYSTEM EXAM HOMEWORK GRADE ATTENDANCE ANNOUNCEMENT } type NotificationPreferences { channels: [NotificationChannel!]! eventTypes: NotificationEventTypes! } enum NotificationChannel { APP SMS EMAIL WECHAT } type NotificationEventTypes { gradeReleased: Boolean! homeworkGraded: Boolean! examPublished: Boolean! attendanceAlert: Boolean! schoolAnnouncement: Boolean! } type DashboardData { parent: Parent children: [Child!]! viewports: [ViewportItem!]! unreadNotifications: Int! degraded: Boolean! } # ============ Query ============ type Query { dashboard: DashboardData! viewports: [ViewportItem!]! me: Parent! children: [Child!]! child(childId: ID!): Child childGrades( childId: ID! page: Int = 1 pageSize: Int = 20 subject: String ): GradePage! childHomework(childId: ID!): [Homework!]! childExams(childId: ID!): [Exam!]! childAnalytics(childId: ID!, dateRange: DateRangeInput): ChildAnalytics! notifications( unreadOnly: Boolean page: Int = 1 pageSize: Int = 20 ): [Notification!]! notificationPreferences: NotificationPreferences! } input DateRangeInput { start: DateTime! end: DateTime! } # ============ Mutation ============ type Mutation { selectChild(childId: ID!): SelectChildResult! markNotificationRead(notificationId: ID!): Notification! updateNotificationPreferences( input: UpdateNotificationPreferencesInput! ): NotificationPreferences! } type SelectChildResult { childId: ID! selectedAt: DateTime! audited: Boolean! } input UpdateNotificationPreferencesInput { channels: [NotificationChannel!]! eventTypes: NotificationEventTypesInput! } input NotificationEventTypesInput { gradeReleased: Boolean homeworkGraded: Boolean examPublished: Boolean attendanceAlert: Boolean schoolAnnouncement: Boolean }