453 lines
9.7 KiB
GraphQL
453 lines
9.7 KiB
GraphQL
# teacher-bff GraphQL Schema (SDL-first, president §2.17)
|
||
# 版本:v1(P2 第一版,coord 仲裁)
|
||
# 负责人: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 命名空间预留(president §5.1:P2 预留 schema,P6 实现 Resolver)
|
||
#
|
||
# 错误响应格式:GraphQL errors 数组 + extensions.code = BFF_TEACHER_* + extensions.traceId
|
||
# 降级模式 B(president §2.6):success=true + error=null + data 内 degraded 字段
|
||
#
|
||
# 阶段标注:
|
||
# P2 = 当前实现(iam gRPC,未启用字段返 null + extensions.warning = "field_unavailable_in_p2")
|
||
# P3+ = 跨阶段扩展例外(president §2.3),Resolver 内逐步补 gRPC 调用
|
||
|
||
scalar DateTime
|
||
|
||
# ============ Types ============
|
||
|
||
type User {
|
||
id: ID!
|
||
email: String!
|
||
name: String!
|
||
roles: [String!]!
|
||
permissions: [String!]!
|
||
dataScope: DataScope!
|
||
}
|
||
|
||
enum DataScope {
|
||
SELF
|
||
CLASS
|
||
GRADE
|
||
SCHOOL
|
||
DISTRICT
|
||
ALL
|
||
}
|
||
|
||
type ViewportItem {
|
||
key: String!
|
||
label: String!
|
||
route: String!
|
||
icon: String
|
||
sortOrder: String!
|
||
requiredPermission: String
|
||
}
|
||
|
||
type Class {
|
||
id: ID!
|
||
name: String!
|
||
gradeId: String!
|
||
# 延迟加载:班级下的考试(P3+ core-edu gRPC)
|
||
exams: [Exam!]!
|
||
# 延迟加载:班级下的作业(P3+ core-edu gRPC)
|
||
homework: [Homework!]!
|
||
}
|
||
|
||
type Exam {
|
||
id: ID!
|
||
title: String!
|
||
classId: ID!
|
||
status: ExamStatus!
|
||
publishedAt: DateTime
|
||
# 延迟加载:考试下的成绩(P3+ core-edu gRPC)
|
||
grades: [Grade!]!
|
||
}
|
||
|
||
enum ExamStatus {
|
||
DRAFT
|
||
PUBLISHED
|
||
IN_PROGRESS
|
||
GRADING
|
||
SCORED
|
||
ARCHIVED
|
||
}
|
||
|
||
type Homework {
|
||
id: ID!
|
||
title: String!
|
||
classId: ID!
|
||
dueDate: DateTime!
|
||
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
|
||
}
|
||
|
||
# Dashboard 聚合数据(P2: iam gRPC 用户基础信息,stats/classes 为 null + warning)
|
||
type DashboardData {
|
||
user: User
|
||
classes: [Class!]!
|
||
viewports: [ViewportItem!]!
|
||
stats: DashboardStats
|
||
}
|
||
|
||
# ============ Query ============
|
||
# P2 核心 5 Query(workline §3.2):dashboard / viewports / me / classes / class
|
||
|
||
type Query {
|
||
# 仪表盘聚合(P2: iam gRPC;P3+: + core-edu + data-ana)
|
||
# @dataScope: OWN
|
||
dashboard: DashboardData!
|
||
|
||
# 视口配置(L1 导航,iam.GetViewports)
|
||
viewports: [ViewportItem!]!
|
||
|
||
# 当前用户信息(iam.GetUserInfo + GetEffectivePermissions)
|
||
me: User!
|
||
|
||
# 教师班级列表(P2: iam 数据;P3+: core-edu.GetClassesByTeacher)
|
||
# @dataScope: OWN
|
||
classes: [Class!]!
|
||
|
||
# 单个班级详情(P3+: core-edu)
|
||
# @permission: CLASS_READ
|
||
# @dataScope: OWN
|
||
class(id: ID!): Class
|
||
|
||
# ===== P3+ 跨阶段扩展(core-edu gRPC,按 §2.3 跨阶段扩展例外新增)=====
|
||
|
||
# 班级下的考试列表(P3: core-edu.ExamService.ListExamsByClass)
|
||
# @permission: EXAM_READ
|
||
# @dataScope: OWN
|
||
exams(classId: ID!): [Exam!]!
|
||
|
||
# 班级下的作业列表(P3: core-edu.HomeworkService.ListHomeworkByClass)
|
||
# @permission: HOMEWORK_READ
|
||
# @dataScope: OWN
|
||
homework(classId: ID!): [Homework!]!
|
||
|
||
# 考试下的成绩列表(P3: core-edu.GradeService.ListGradesByExam)
|
||
# @permission: GRADE_READ
|
||
# @dataScope: OWN
|
||
grades(examId: ID!): [Grade!]!
|
||
|
||
# ===== P4+ 跨阶段扩展(content + data-ana gRPC)=====
|
||
|
||
# 知识图谱学习路径(P4: content.KnowledgeGraphService.GetLearningPath)
|
||
knowledgePath(knowledgePointId: ID!): KnowledgePath!
|
||
|
||
# 班级成绩分析(P4: data-ana.AnalyticsService.GetClassPerformance)
|
||
# @permission: ANALYTICS_TEACHER_DASHBOARD
|
||
classPerformance(classId: ID!): ClassPerformance!
|
||
|
||
# 学生薄弱点(P4: data-ana.AnalyticsService.GetStudentWeakness)
|
||
studentWeakness(studentId: ID!): StudentWeakness!
|
||
|
||
# 学习趋势(P4: data-ana.AnalyticsService.GetLearningTrend)
|
||
learningTrend(studentId: ID!, dateRange: DateRangeInput): LearningTrend!
|
||
|
||
# ===== P5+ 跨阶段扩展(ai + msg gRPC)=====
|
||
|
||
# 教师通知列表(P5: msg.NotificationService.ListNotifications)
|
||
notifications(unreadOnly: Boolean): [Notification!]!
|
||
|
||
# ===== admin 命名空间(president §5.1:P2 预留 schema,P6 实现 Resolver)=====
|
||
# admin-portal 复用 teacher-bff GraphQL endpoint,admin 操作低 QPS 无需独立 BFF
|
||
admin: AdminQuery!
|
||
}
|
||
|
||
input DateRangeInput {
|
||
start: DateTime!
|
||
end: DateTime!
|
||
}
|
||
|
||
# ============ Mutation ============
|
||
|
||
type Mutation {
|
||
# ===== P3+ 跨阶段扩展(core-edu gRPC 透传)=====
|
||
|
||
# 创建考试(P3: core-edu.ExamService.CreateExam)
|
||
# @permission: EXAM_CREATE
|
||
createExam(input: CreateExamInput!): Exam!
|
||
|
||
# 布置作业(P3: core-edu.HomeworkService.AssignHomework)
|
||
# @permission: HOMEWORK_ASSIGN
|
||
assignHomework(input: AssignHomeworkInput!): Homework!
|
||
|
||
# 录入成绩(P3: core-edu.GradeService.RecordGrade)
|
||
# @permission: GRADE_RECORD
|
||
recordGrade(input: RecordGradeInput!): Grade!
|
||
|
||
# ===== P5+ 跨阶段扩展(ai + msg gRPC)=====
|
||
|
||
# AI 出题(P5: ai.AiService.GenerateQuestion)
|
||
generateQuestion(input: GenerateQuestionInput!): GeneratedQuestion!
|
||
|
||
# 标记通知已读(P5: msg.NotificationService.MarkAsRead)
|
||
markNotificationAsRead(id: ID!): Notification!
|
||
|
||
# ===== admin 命名空间 Mutation(president §5.1:P2 预留,P6 实现)=====
|
||
admin: AdminMutation!
|
||
}
|
||
|
||
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!]!
|
||
}
|
||
|
||
# ============ P4+ 类型(content + data-ana)============
|
||
|
||
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!
|
||
}
|
||
|
||
# ============ P5+ 类型(ai + msg)============
|
||
|
||
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 命名空间(president §5.1:P2 预留 schema,P6 实现 Resolver)============
|
||
# P2: 类型声明占位,Resolver 返 null(不阻塞 admin-portal schema 内省)
|
||
# P6: ai03 实现 admin Resolver 真实数据(用户/角色/学校/组织/审计日志管理)
|
||
|
||
type AdminQuery {
|
||
# 用户管理
|
||
users(page: Int = 1, pageSize: Int = 20): AdminUserPage
|
||
user(id: ID!): AdminUser
|
||
|
||
# 角色权限管理
|
||
roles: [AdminRole!]!
|
||
role(id: ID!): AdminRole
|
||
|
||
# 学校设置
|
||
school: AdminSchool
|
||
|
||
# 组织管理
|
||
organizations: [AdminOrganization!]!
|
||
|
||
# 审计日志查询
|
||
auditLogs(page: Int = 1, pageSize: Int = 20): AuditLogPage
|
||
}
|
||
|
||
type AdminMutation {
|
||
createUser(input: AdminCreateUserInput!): AdminUser!
|
||
updateUser(id: ID!, input: AdminUpdateUserInput!): AdminUser!
|
||
deleteUser(id: ID!): Boolean!
|
||
|
||
createRole(input: AdminCreateRoleInput!): AdminRole!
|
||
updateRole(id: ID!, input: AdminUpdateRoleInput!): AdminRole!
|
||
deleteRole(id: ID!): Boolean!
|
||
|
||
updateSchool(input: AdminUpdateSchoolInput!): AdminSchool!
|
||
|
||
createOrganization(input: AdminCreateOrganizationInput!): AdminOrganization!
|
||
updateOrganization(id: ID!, input: AdminUpdateOrganizationInput!): AdminOrganization!
|
||
deleteOrganization(id: ID!): Boolean!
|
||
}
|
||
|
||
type AdminUser {
|
||
id: ID!
|
||
email: String!
|
||
name: String!
|
||
roles: [String!]!
|
||
status: String!
|
||
createdAt: DateTime!
|
||
lastLoginAt: DateTime
|
||
}
|
||
|
||
type AdminUserPage {
|
||
edges: [AdminUser!]!
|
||
totalCount: Int!
|
||
page: Int!
|
||
pageSize: Int!
|
||
}
|
||
|
||
type AdminRole {
|
||
id: ID!
|
||
name: String!
|
||
description: String
|
||
permissions: [String!]!
|
||
userCount: Int!
|
||
}
|
||
|
||
type AdminSchool {
|
||
id: ID!
|
||
name: String!
|
||
address: String
|
||
phone: String
|
||
studentCount: Int!
|
||
teacherCount: Int!
|
||
classCount: Int!
|
||
}
|
||
|
||
type AdminOrganization {
|
||
id: ID!
|
||
name: String!
|
||
type: String!
|
||
parentId: ID
|
||
createdAt: DateTime!
|
||
}
|
||
|
||
type AuditLogPage {
|
||
edges: [AuditLog!]!
|
||
totalCount: Int!
|
||
page: Int!
|
||
pageSize: Int!
|
||
}
|
||
|
||
type AuditLog {
|
||
id: ID!
|
||
actorUserId: ID!
|
||
action: String!
|
||
resourceType: String!
|
||
resourceId: ID
|
||
occurredAt: DateTime!
|
||
ip: String
|
||
}
|
||
|
||
input AdminCreateUserInput {
|
||
email: String!
|
||
name: String!
|
||
roleIds: [ID!]!
|
||
}
|
||
|
||
input AdminUpdateUserInput {
|
||
name: String
|
||
roleIds: [ID!]
|
||
status: String
|
||
}
|
||
|
||
input AdminCreateRoleInput {
|
||
name: String!
|
||
description: String
|
||
permissions: [String!]!
|
||
}
|
||
|
||
input AdminUpdateRoleInput {
|
||
name: String
|
||
description: String
|
||
permissions: [String!]
|
||
}
|
||
|
||
input AdminUpdateSchoolInput {
|
||
name: String
|
||
address: String
|
||
phone: String
|
||
}
|
||
|
||
input AdminCreateOrganizationInput {
|
||
name: String!
|
||
type: String!
|
||
parentId: ID
|
||
}
|
||
|
||
input AdminUpdateOrganizationInput {
|
||
name: String
|
||
type: String
|
||
parentId: ID
|
||
}
|