Files
Edu/packages/shared-ts/contracts/graphql/parent-bff.graphql

722 lines
13 KiB
GraphQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# parent-bff GraphQL SchemaSDL 契约,对齐 02-architecture-design.md §4.2
#
# 本文件是 parent-bff GraphQL 端点的契约唯一源single source of truth
# - 前端 parent-portal 引用本文件生成 TypeScript 类型
# - 后端 parent-bff 引用本文件加载为 GraphQLSchema
# - CI 强制检测 breaking change与 main 分支对比)
#
# 仲裁依据:
# - U3GraphQL P2 立即引入parent-bff P4 直接用 GraphQL不走 REST 过渡)
# - C1错误码前缀 BFF_PARENT_
# - U4BFF 豁免 @RequirePermission仅校验 x-user-id + ChildGuard
# - 02 §9 #5depth ≤ 7 / cost ≤ 1000
#
# 版本v232 Query + 6 Mutation对齐 parent-portal 全部 operations
scalar DateTime
scalar JSON
# ============ Legacy Types ============
type Parent {
id: ID!
email: String!
name: String!
avatar: String
roles: [String!]!
permissions: [String!]
dataScope: DataScope!
schoolId: ID
}
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!
}
# ============ Extended Typesv2 新增) ============
type ChildBrief {
id: ID!
name: String!
grade: String!
classId: ID
className: String
avatar: String
}
type ChildSummary {
childId: ID!
avgScore: Float
classRank: Int
classSize: Int
attendanceRate: Float
pendingHomeworkCount: Int!
recentGradeTrend: Float
recentScores: [ChildGrade!]!
upcomingEvents: [UpcomingEvent!]!
}
type UpcomingEvent {
id: ID!
type: String!
title: String!
dueDate: DateTime!
}
type ChildDetail {
childId: ID!
basicInfo: ChildBasicInfo!
todaySchedule: [JSON!]!
weeklySchedule: [JSON!]!
homeworkSummary: HomeworkSummary!
gradeSummary: GradeSummary!
examResults: ExamResultSummary!
}
type ChildBasicInfo {
name: String!
avatar: String
grade: String!
className: String!
schoolName: String!
relation: String!
}
type HomeworkSummary {
pendingCount: Int!
overdueCount: Int!
submittedCount: Int!
gradedCount: Int!
}
type GradeSummary {
avgScore: Float!
classRank: Int
classSize: Int
trend: Float
}
type ExamResultSummary {
upcoming: Int!
completed: Int!
avgScore: Float
}
type ChildGrade {
examId: ID!
examName: String!
examDate: DateTime!
subject: String!
studentScore: Float!
classAverage: Float
classMax: Float
classMin: Float
gradeLevel: String
}
type ChildHomework {
id: ID!
title: String!
subject: String!
className: String!
assignedDate: DateTime!
dueDate: DateTime!
status: String!
score: Float
maxScore: Float
feedback: String
}
type ChildExam {
id: ID!
name: String!
subject: String!
status: String!
startsAt: DateTime!
expiresAt: DateTime
durationSeconds: Int
questionCount: Int
totalScore: Float
submittedAt: DateTime
}
type AttendanceRecord {
id: ID!
date: DateTime!
status: String!
checkInTime: DateTime
checkOutTime: DateTime
note: String
}
type ExamResult {
examId: ID!
childId: ID!
score: Float
rank: Int
subjectScores: [SubjectScore!]!
feedback: String
}
type SubjectScore {
subject: String!
score: Float!
fullScore: Float!
}
type ChildClass {
id: ID!
name: String!
homeroomTeacher: String
studentCount: Int
grade: String!
year: String
}
type ReportCard {
childId: ID!
academicYearId: ID!
semester: Int!
subjects: [ReportCardSubject!]!
overallComment: String
classRank: Int
}
type ReportCardSubject {
subject: String!
score: Float!
grade: String!
teacherComment: String
}
type GrowthArchive {
childId: ID!
subject: String
dataPoints: [GrowthDataPoint!]!
}
type GrowthDataPoint {
date: DateTime!
category: String!
title: String!
description: String!
evidence: String
}
type WeaknessItem {
id: ID!
knowledgePoint: String!
masteryLevel: Float!
subject: String!
recommendation: String
}
type ChildTrend {
childId: ID!
period: TrendPeriod!
dataPoints: [TrendDataPoint!]!
}
type TrendDataPoint {
date: DateTime!
score: Float!
subject: String
}
type LearningPathItem {
id: ID!
title: String!
subject: String!
order: Int!
masteryLevel: Float!
resources: [String!]!
}
type ErrorBookStats {
childId: ID!
totalCount: Int!
newCount: Int!
learningCount: Int!
masteredCount: Int!
dueReviewCount: Int!
masteredRate: Float!
}
type WrongQuestion {
id: ID!
questionId: ID!
subject: String!
content: String!
wrongAnswer: String!
correctAnswer: String!
addedAt: DateTime!
status: String!
}
type WeakKp {
id: ID!
knowledgePoint: String!
subject: String!
masteryLevel: Float!
recommendation: String
}
type MasterySummary {
childId: ID!
overallMastery: Float!
subjectMastery: [SubjectMastery!]!
totalKps: Int!
masteredKps: Int!
}
type SubjectMastery {
subject: String!
mastery: Float!
totalKps: Int!
masteredKps: Int!
}
type DiagnosticReport {
id: ID!
childId: ID!
subject: String!
reportDate: DateTime!
summary: String!
recommendations: [String!]!
}
type PracticeStats {
childId: ID!
totalSessions: Int!
completedSessions: Int!
totalQuestionsAnswered: Int!
overallAccuracy: Float!
}
type PracticeSession {
id: ID!
childId: ID!
subject: String!
startedAt: DateTime!
completedAt: DateTime
questionCount: Int!
correctCount: Int!
accuracy: Float!
}
type CoursePlan {
id: ID!
childId: ID!
subject: String!
title: String!
startDate: DateTime!
endDate: DateTime!
progress: Float!
}
type CoursePlanDetail {
id: ID!
childId: ID!
subject: String!
title: String!
startDate: DateTime!
endDate: DateTime!
progress: Float!
lessons: [CoursePlanLesson!]!
}
type CoursePlanLesson {
id: ID!
title: String!
date: DateTime!
completed: Boolean!
}
type LessonPlan {
id: ID!
childId: ID!
subject: String!
title: String!
date: DateTime!
teacherName: String!
}
type LessonPlanDetail {
id: ID!
childId: ID!
subject: String!
title: String!
date: DateTime!
teacherName: String!
objectives: [String!]!
content: String!
homework: String
}
type ElectiveCourse {
id: ID!
childId: ID!
name: String!
subject: String!
teacher: String!
schedule: String!
selected: Boolean!
}
type LeaveRequestItem {
id: ID!
childId: ID!
childName: String
className: String
type: String!
startDate: DateTime!
endDate: DateTime!
reason: String!
status: String!
submittedAt: DateTime!
reviewedAt: DateTime
reviewerName: String
reviewComment: String
}
type AcademicYear {
id: ID!
name: String!
startDate: DateTime!
endDate: DateTime!
isCurrent: Boolean!
}
type MyNotification {
id: ID!
childId: ID
eventType: String!
title: String!
body: String!
read: Boolean!
createdAt: DateTime!
actionUrl: String
pinned: Boolean!
}
type MyNotificationPreferences {
parentId: ID!
preferences: JSON!
defaults: NotificationPreferencesDefaults!
updatedAt: DateTime
}
type NotificationPreferencesDefaults {
channels: [NotificationChannel!]!
eventTypes: NotificationEventTypes!
}
type MarkAsReadResult {
id: ID!
read: Boolean!
}
type MarkAllAsReadResult {
count: Int!
}
type SwitchChildResult {
childId: ID!
childName: String!
selectedAt: DateTime!
}
type ExportChildGradesResult {
downloadUrl: String!
expiresAt: DateTime!
}
type UpdateNotificationPreferencesResult {
parentId: ID!
updatedAt: DateTime!
}
# ============ Query32 个) ============
type Query {
# Legacy11 个)
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!
# Extended21 个)
currentUser: Parent!
myChildren: [ChildBrief!]!
childSummary(childId: ID!): ChildSummary
childDetail(childId: ID!): ChildDetail
childAttendance(
childId: ID!
startDate: DateTime!
endDate: DateTime!
): [AttendanceRecord!]!
childExamResult(childId: ID!, examId: ID!): ExamResult
childClasses(childId: ID!): [ChildClass!]!
childReportCard(
childId: ID!
academicYearId: ID!
semester: Int!
): ReportCard
childGrowthArchive(childId: ID!, subject: String): GrowthArchive!
childWeakness(childId: ID!): [WeaknessItem!]!
childTrend(childId: ID!, period: TrendPeriod!): ChildTrend!
childLearningPath(childId: ID!): [LearningPathItem!]!
childErrorBookStats(childId: ID!): ErrorBookStats!
childTopWrongQuestions(childId: ID!, limit: Int): [WrongQuestion!]!
childWeakKps(childId: ID!, limit: Int): [WeakKp!]!
childMasterySummary(childId: ID!): MasterySummary!
childDiagnosticReports(childId: ID!): [DiagnosticReport!]!
childPracticeStats(childId: ID!): PracticeStats!
childPracticeSessions(childId: ID!, limit: Int): [PracticeSession!]!
childCoursePlans(childId: ID!): [CoursePlan!]!
childCoursePlanDetail(childId: ID!, planId: ID!): CoursePlanDetail
childLessonPlans(childId: ID!, subject: String): [LessonPlan!]!
childLessonPlanDetail(childId: ID!, planId: ID!): LessonPlanDetail
childElective(childId: ID!): [ElectiveCourse!]!
childLeaveRequests(childId: ID!): [LeaveRequestItem!]!
academicYears: [AcademicYear!]!
myNotifications(unreadOnly: Boolean, limit: Int): [MyNotification!]!
myNotificationPreferences: MyNotificationPreferences!
}
# ============ Mutation6 个) ============
type Mutation {
# Legacy3 个)
selectChild(childId: ID!): SelectChildResult!
markNotificationRead(notificationId: ID!): Notification!
updateNotificationPreferences(
input: UpdateNotificationPreferencesInput!
): NotificationPreferences!
# Extended6 个新 mutation覆盖前端全部操作
markAsRead(notificationId: ID!): MarkAsReadResult!
markAllAsRead: MarkAllAsReadResult!
switchChild(childId: ID!): SwitchChildResult!
updateMyNotificationPreferences(
parentId: ID!
preferences: JSON!
defaults: JSON
): UpdateNotificationPreferencesResult!
createLeaveRequest(input: LeaveRequestInput!): LeaveRequestItem!
exportChildGrades(childId: ID!, subject: String): ExportChildGradesResult!
}
# ============ Inputs ============
input DateRangeInput {
start: DateTime!
end: DateTime!
}
enum TrendPeriod {
WEEK
MONTH
SEMESTER
YEAR
}
input UpdateNotificationPreferencesInput {
channels: [NotificationChannel!]!
eventTypes: NotificationEventTypesInput!
}
input NotificationEventTypesInput {
gradeReleased: Boolean
homeworkGraded: Boolean
examPublished: Boolean
attendanceAlert: Boolean
schoolAnnouncement: Boolean
}
input LeaveRequestInput {
childId: ID!
type: String!
startDate: DateTime!
endDate: DateTime!
reason: String!
}
type SelectChildResult {
childId: ID!
selectedAt: DateTime!
audited: Boolean!
}