chore(teacher-bff): merge teacher-bff full implementation into main

This commit is contained in:
SpecialX
2026-07-10 19:18:16 +08:00
37 changed files with 2588 additions and 467 deletions

View File

@@ -0,0 +1,452 @@
# teacher-bff GraphQL Schema (SDL-first, president §2.17)
# 版本v1P2 第一版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 字段经 DataLoaderP4 全量接入)
# 4. 复杂度限制depth ≤ 7query cost ≤ 1000
# 5. admin 命名空间预留president §5.1P2 预留 schemaP6 实现 Resolver
#
# 错误响应格式GraphQL errors 数组 + extensions.code = BFF_TEACHER_* + extensions.traceId
# 降级模式 Bpresident §2.6success=true + error=null + data 内 degraded 字段
#
# 阶段标注:
# P2 = 当前实现iam gRPC未启用字段返 null + extensions.warning = "field_unavailable_in_p2"
# P3+ = 跨阶段扩展例外president §2.3Resolver 内逐步补 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 Queryworkline §3.2dashboard / viewports / me / classes / class
type Query {
# 仪表盘聚合P2: iam gRPCP3+: + 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.1P2 预留 schemaP6 实现 Resolver=====
# admin-portal 复用 teacher-bff GraphQL endpointadmin 操作低 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 命名空间 Mutationpresident §5.1P2 预留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.1P2 预留 schemaP6 实现 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
}