add complete parent-bff implementation including: - GraphQL endpoint with depth/cost validation - ChildGuard越权校验 with redis cache and singleflight - parallel orchestration with partial failure fallback - three-level cache fallback strategy (Redis + LRU + downstream) - Kafka consumer for cache invalidation and notification push - opossum circuit breaker for downstream services - Prometheus metrics and SLO alerts - Helm chart for k8s deployment with multi-environment configs - Grafana dashboard for observability - complete unit and integration tests
245 lines
4.2 KiB
GraphQL
245 lines
4.2 KiB
GraphQL
# 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
|
||
}
|