Files
Edu/packages/shared-ts/contracts/graphql/student-bff.schema.graphql

1649 lines
38 KiB
GraphQL

# student-bff GraphQL Schema (v2)
#
# 负责人: ai04
# 仲裁依据: coord-final-decisions §2 B1-B8 + president-final-rulings §2.2
# 存放路径: packages/shared-ts/contracts/graphql/student-bff.schema.graphql (president §2.2.1)
#
# 设计规范 (president §2.2.5):
# - Query/Mutation 用 camelCase
# - 分页采用 Relay Cursor Connections 规范 ({ edges, pageInfo, totalCount })
# - 错误响应: GraphQL errors 数组 + extensions.code + extensions.traceId
# - 权限点标注: # @permission: <RESOURCE>_<ACTION>
# - DataScope 标注: # @dataScope: OWN (学生数据隔离 SELF)
# - Type 用 PascalCase, 字段用 camelCase, 枚举用 UPPER_SNAKE_CASE
# - 必填字段用 !, 可空字段不标 ! (避免破坏性变更)
#
# 降级模式 (president §2.6 方案 B):
# - 下游不可用时 success=true + error=null + data 内 degraded=true
# - 降级字段返回 null, 父对象加 degraded/degradedReason/degradedFields
#
# v2 变更:
# - 补全 22 个扩展 Query (examDetail/homeworkDetail/serverTime/mySchedule/...)
# - 补全 21 个扩展 Mutation (submitExam/saveExamDraft/updateProfile/...)
# - startPracticeSession 从 Query 移到 Mutation (语义为创建会话)
# - 总计 36 Query + 23 Mutation + 1 Subscription = 60 operations
scalar DateTime
scalar JSON
# ============================================================================
# Relay Cursor Connections 规范
# ============================================================================
interface Node {
id: ID!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# ============================================================================
# 错误扩展 (GraphQL errors 数组 + extensions)
# ============================================================================
"""
GraphQL errors 数组中 extensions 字段规范 (president §2.2.3 + G8 裁决).
GlobalErrorFilter 注入, 不在 schema 中显式暴露.
"""
type GraphQLErrorExtension {
code: String! # BFF_STUDENT_* 前缀 (B5 裁决)
traceId: String! # 全链路追踪 ID (Gateway 注入 X-Request-Id)
i18nKey: String! # i18n key: error.bffStudent.<code_snake> (F4 裁决)
severity: String! # error / warning / info
}
# ============================================================================
# 通用类型
# ============================================================================
"""
降级标记 (president §2.6 方案 B).
当下游服务不可用但需返回部分数据时, 父对象包含此接口字段.
"""
interface Degradable {
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type DegradationInfo {
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 用户与权限 (下游: iam)
# ============================================================================
type UserProfile {
id: ID!
email: String!
name: String!
avatar: String
roles: [String!]!
permissions: [String!]!
}
type ViewportItem {
key: String!
label: String!
route: String!
icon: String
sortOrder: Int!
requiredPermission: String
}
type ViewportConfig {
navigation: [ViewportItem!]!
dataScope: StudentDataScope!
}
type StudentDataScope {
showHistoryGrades: Boolean!
showClassRanking: Boolean!
enableAIChat: Boolean!
}
type CurrentUserPayload implements Degradable {
user: UserProfile
viewports: ViewportConfig
effectivePermissions: [String!]!
dataScope: String # SELF (学生固定)
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 班级 (下游: core-edu ClassService)
# ============================================================================
type StudentClass {
id: ID!
name: String!
gradeId: String!
gradeName: String
subjects: [String!]!
homeroomTeacher: TeacherBrief
}
type TeacherBrief {
id: ID!
name: String!
avatar: String
}
type StudentClassConnection {
edges: [StudentClassEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type StudentClassEdge {
node: StudentClass!
cursor: String!
}
type StudentClassPayload implements Degradable {
classes: [StudentClass!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 考试 (下游: core-edu ExamService)
# ============================================================================
enum ExamStatus {
DRAFT
PUBLISHED
IN_PROGRESS
COMPLETED
CANCELLED
ARCHIVED
}
type Exam {
id: ID!
classId: ID!
title: String!
description: String
examDate: DateTime!
duration: Int! # 考试时长(分钟)
totalScore: Float!
status: ExamStatus!
createdBy: ID!
createdAt: DateTime!
updatedAt: DateTime!
daysLeft: Int # 距考试天数(负数表示已过)
}
type ExamConnection {
edges: [ExamEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type ExamEdge {
node: Exam!
cursor: String!
}
type ExamListPayload implements Degradable {
exams: [Exam!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 考试详情 (含题目与学生提交)
type ExamDetailPayload implements Degradable {
exam: JSON
questions: [JSON!]
mySubmission: JSON
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 作业 (下游: core-edu HomeworkService)
# ============================================================================
enum HomeworkStatus {
ASSIGNED
SUBMITTED
GRADED
OVERDUE
RETURNED
}
type Homework {
id: ID!
classId: ID!
title: String!
description: String
dueDate: DateTime!
status: HomeworkStatus!
createdBy: ID!
createdAt: DateTime!
updatedAt: DateTime!
submission: HomeworkSubmission # 当前学生的提交
}
type HomeworkSubmission {
id: ID!
homeworkId: ID!
studentId: ID!
status: HomeworkStatus!
submittedAt: DateTime
gradedAt: DateTime
score: Float
feedback: String
}
type HomeworkConnection {
edges: [HomeworkEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type HomeworkEdge {
node: Homework!
cursor: String!
}
type HomeworkListPayload implements Degradable {
homework: [Homework!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 作业详情
type HomeworkDetailPayload implements Degradable {
homework: JSON
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 成绩 (下游: core-edu GradeService)
# ============================================================================
type Grade {
id: ID!
studentId: ID!
examId: ID
homeworkId: ID
examTitle: String
homeworkTitle: String
subject: String
score: Float!
totalScore: Float!
feedback: String
gradedBy: ID!
gradedAt: DateTime!
createdAt: DateTime!
updatedAt: DateTime!
}
type GradeConnection {
edges: [GradeEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type GradeEdge {
node: Grade!
cursor: String!
}
type GradeListPayload implements Degradable {
grades: [Grade!]!
averageScore: Float
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 成绩报告卡
type ReportCardPayload implements Degradable {
reportCard: JSON
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 考勤 (下游: core-edu AttendanceService)
# ============================================================================
enum AttendanceStatus {
PRESENT
ABSENT
LATE
EARLY_LEAVE
EXCUSED
}
type AttendanceRecord {
id: ID!
studentId: ID!
classId: ID!
date: DateTime!
status: AttendanceStatus!
remark: String
recordedBy: ID!
createdAt: DateTime!
}
type AttendanceConnection {
edges: [AttendanceEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type AttendanceEdge {
node: AttendanceRecord!
cursor: String!
}
type AttendanceListPayload implements Degradable {
records: [AttendanceRecord!]!
presentCount: Int!
absentCount: Int!
lateCount: Int!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 课表 / 请假 / 选课 / 课案 / 课程计划 (下游: core-edu + content)
# ============================================================================
# 服务器时间
type ServerTimePayload {
serverTime: String!
timezone: String!
timestamp: Int!
}
# 课表
type ScheduleItem {
id: ID!
classId: ID!
className: String
subject: String!
date: DateTime!
startTime: DateTime!
endTime: DateTime!
teacher: TeacherBrief
room: String
}
type SchedulePayload implements Degradable {
items: [ScheduleItem!]!
weekStart: String
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 请假记录
type LeaveRequest {
id: ID!
studentId: ID!
type: String!
startDate: DateTime!
endDate: DateTime!
reason: String!
status: String!
attachments: [String!]
createdAt: DateTime!
updatedAt: DateTime!
}
type LeaveRequestsPayload implements Degradable {
requests: [LeaveRequest!]!
totalCount: Int!
pendingCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 选课记录
type ElectiveSelection {
id: ID!
studentId: ID!
courseId: ID!
courseName: String!
status: String!
selectedAt: DateTime!
}
type ElectiveSelectionsPayload implements Degradable {
selections: [ElectiveSelection!]!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 可选课程
type ElectiveCourse {
id: ID!
title: String!
subject: String!
description: String
capacity: Int!
enrolledCount: Int!
teacher: TeacherBrief
}
type AvailableElectiveCoursesPayload implements Degradable {
courses: [ElectiveCourse!]!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 课案
type LessonPlan {
id: ID!
title: String!
subject: String!
description: String
objectives: [String!]
content: JSON
createdBy: ID!
createdAt: DateTime!
updatedAt: DateTime!
}
type LessonPlansPayload implements Degradable {
plans: [LessonPlan!]!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type LessonPlanDetailPayload implements Degradable {
plan: LessonPlan
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 课程计划
type CoursePlan {
id: ID!
title: String!
subject: String!
description: String
startDate: DateTime!
endDate: DateTime!
objectives: [String!]
createdBy: ID!
createdAt: DateTime!
updatedAt: DateTime!
}
type CoursePlansPayload implements Degradable {
plans: [CoursePlan!]!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type CoursePlanDetailPayload implements Degradable {
plan: CoursePlan
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 教材与章节 (下游: content, P4)
# ============================================================================
type Textbook {
id: ID!
title: String!
subjectId: ID!
subjectName: String
gradeId: ID!
gradeName: String
version: String!
coverImage: String
chapters: [Chapter!]!
}
type Chapter {
id: ID!
textbookId: ID!
title: String!
description: String
sortOrder: Int!
parentId: ID # 父章节(支持章节树)
knowledgePoints: [KnowledgePoint!]!
}
type KnowledgePoint {
id: ID!
title: String!
description: String
mastery: Float # 0-1 掌握度
}
type TextbookConnection {
edges: [TextbookEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type TextbookEdge {
node: Textbook!
cursor: String!
}
type TextbookListPayload implements Degradable {
textbooks: [Textbook!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type ChapterListPayload implements Degradable {
chapters: [Chapter!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 学习路径 (下游: content KnowledgeGraphService, P4)
# ============================================================================
type LearningPath {
studentId: ID!
subjectId: ID!
points: [KnowledgePoint!]!
recommendedOrder: [String!]! # 知识点 id 顺序
estimatedHours: Float
}
type LearningPathPayload implements Degradable {
path: LearningPath
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 学情分析 (下游: data-ana, P4)
# ============================================================================
type StudentDashboard {
studentId: ID!
averageScore: Float
classRank: Int
totalStudents: Int
pendingHomeworkCount: Int!
upcomingExamCount: Int!
unreadNotificationCount: Int!
lastGrade: Grade
weakness: WeakPointSummary # P4 data-ana 启用后填充
trend: LearningTrendSummary # P4 data-ana 启用后填充
}
type WeakPointSummary {
knowledgePointId: ID!
title: String!
mastery: Float!
subject: String
}
type StudentWeakness {
studentId: ID!
subjectId: ID
weakPoints: [WeakPoint!]!
}
type WeakPoint {
knowledgePointId: ID!
title: String!
mastery: Float!
subject: String
lastAssessedAt: DateTime
}
type LearningTrendSummary {
studentId: ID!
subjectId: ID
points: [TrendPoint!]!
}
type LearningTrend {
studentId: ID!
subjectId: ID
points: [TrendPoint!]!
}
type TrendPoint {
date: DateTime!
score: Float!
subject: String
}
type StudentDashboardPayload implements Degradable {
dashboard: StudentDashboard
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type WeaknessPayload implements Degradable {
weakness: StudentWeakness
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type TrendPayload implements Degradable {
trend: LearningTrend
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 学生成长曲线
type StudentGrowthPayload implements Degradable {
studentId: ID!
subjectId: ID
points: [TrendPoint!]!
averageScore: Float
classAverage: Float
growthRate: Float
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 作业分析
type AssignmentAnalysisPayload implements Degradable {
analysis: JSON
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 个人资料
type MyProfilePayload implements Degradable {
profile: JSON
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 掌握度概览
type MasterySummaryPayload implements Degradable {
summary: JSON
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 诊断报告
type DiagnosticReportsPayload implements Degradable {
reports: [JSON!]!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 错题本
type ErrorBookItem {
id: ID!
questionId: ID!
subjectId: ID!
knowledgePointId: ID!
myAnswer: String
correctAnswer: String!
note: String
tags: [String!]
mastered: Boolean!
createdAt: DateTime!
updatedAt: DateTime!
}
type ErrorBookPayload implements Degradable {
items: [ErrorBookItem!]!
totalCount: Int!
subjectStats: [JSON!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 练习会话
type PracticeSession {
id: ID!
studentId: ID!
subjectId: ID!
status: String!
totalQuestions: Int!
answeredCount: Int!
startedAt: DateTime!
completedAt: DateTime
}
type PracticeSessionsPayload implements Degradable {
sessions: [PracticeSession!]!
totalCount: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# 通知 / 公告 (下游: msg, P5)
# ============================================================================
enum NotificationType {
HOMEWORK_ASSIGNED
HOMEWORK_GRADED
EXAM_PUBLISHED
EXAM_UPDATED
GRADE_RECORDED
SYSTEM
ANNOUNCEMENT
}
enum NotificationChannel {
IN_APP
EMAIL
SMS
PUSH
}
type Notification {
id: ID!
userId: ID!
type: NotificationType!
title: String!
content: String!
channel: NotificationChannel!
isRead: Boolean!
createdAt: DateTime!
readAt: DateTime
metadata: JSON
}
type NotificationConnection {
edges: [NotificationEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type NotificationEdge {
node: Notification!
cursor: String!
}
type NotificationListPayload implements Degradable {
notifications: [Notification!]!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
type UnreadCountPayload implements Degradable {
count: Int!
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# 公告
type Announcement {
id: ID!
title: String!
content: String!
authorId: ID!
status: String!
category: String
targetAudience: String
publishedAt: DateTime
createdAt: DateTime!
updatedAt: DateTime!
}
type AnnouncementConnection {
edges: [AnnouncementEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type AnnouncementEdge {
node: Announcement!
cursor: String!
}
type AnnouncementDetailPayload implements Degradable {
announcement: Announcement
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
# ============================================================================
# AI 答疑 (下游: ai, P5)
# ============================================================================
type AIChatResponse {
content: String!
model: String!
usage: AIUsage!
}
type AIUsage {
promptTokens: Int!
completionTokens: Int!
totalTokens: Int!
}
type AIChatPayload implements Degradable {
response: AIChatResponse
degraded: Boolean!
degradedReason: String
degradedFields: [String!]
}
input AIChatInput {
messages: [AIChatMessageInput!]!
model: String = "gpt-4o-mini"
context: AIChatContextInput
}
# ============================================================================
# Mutation 结果
# ============================================================================
type MutationError {
code: String! # BFF_STUDENT_* 前缀
message: String!
traceId: String
i18nKey: String
}
type SubmitHomeworkResult {
success: Boolean!
submissionId: ID
homeworkId: ID!
submittedAt: DateTime
status: HomeworkStatus
error: MutationError
}
type MarkNotificationReadResult {
success: Boolean!
notificationId: ID!
readAt: DateTime
error: MutationError
}
# 通用 Mutation 结果
type GenericMutationResult {
success: Boolean!
error: MutationError
}
# 通知相关 Mutation 结果
type MarkAllAsReadResult {
success: Boolean!
markedCount: Int!
error: MutationError
}
# 考试相关 Mutation 结果
type SubmitExamResult {
success: Boolean!
submissionId: ID
examId: ID!
submittedAt: DateTime
score: Float
status: String
error: MutationError
}
type SaveExamDraftResult {
success: Boolean!
draftId: ID
examId: ID!
savedAt: DateTime
error: MutationError
}
type RecordExamViolationResult {
success: Boolean!
violationId: ID
examId: ID!
violationType: String!
recordedAt: DateTime
error: MutationError
}
# 个人资料相关 Mutation 结果
type UpdateProfileResult {
success: Boolean!
profile: JSON
error: MutationError
}
# 作业延期申请
type RequestExtensionResult {
success: Boolean!
requestId: ID
homeworkId: ID!
status: String!
error: MutationError
}
# 班级相关 Mutation 结果
type JoinClassResult {
success: Boolean!
classId: ID!
joinedAt: DateTime
error: MutationError
}
type LeaveClassResult {
success: Boolean!
classId: ID!
leftAt: DateTime
error: MutationError
}
# 错题相关 Mutation 结果
type ErrorBookItemResult {
success: Boolean!
itemId: ID
error: MutationError
}
# 请假相关 Mutation 结果
type CreateLeaveRequestResult {
success: Boolean!
requestId: ID
status: String!
error: MutationError
}
# 选课相关 Mutation 结果
type SelectElectiveResult {
success: Boolean!
selectionId: ID
courseId: ID!
status: String!
error: MutationError
}
# 练习相关 Mutation 结果
type SubmitPracticeAnswerResult {
success: Boolean!
questionId: ID!
isCorrect: Boolean
correctAnswer: String
explanation: String
error: MutationError
}
type StartPracticeSessionResult {
success: Boolean!
session: JSON
questions: [JSON!]!
error: MutationError
}
# ============================================================================
# Query 根类型 (36 Queries)
# ============================================================================
type Query {
"""当前学生信息 + 权限 + 视口 (聚合 iam.GetUserInfo + GetEffectivePermissions + GetViewports)"""
# @permission: AUTH_READ
# @dataScope: OWN
currentUser: CurrentUserPayload!
"""我的班级列表 (core-edu.ClassService.GetClass + ListStudentsByClass)"""
# @permission: CLASS_READ
# @dataScope: OWN
myClasses(
after: String
first: Int = 20
before: String
last: Int
): StudentClassConnection!
"""我的考试列表 (core-edu.ExamService.ListExamsByClass)"""
# @permission: EXAM_READ
# @dataScope: OWN
myExams(
after: String
first: Int = 20
before: String
last: Int
status: ExamStatus
): ExamConnection!
"""我的作业列表 (core-edu.HomeworkService.ListHomeworkByClass)"""
# @permission: HOMEWORK_READ
# @dataScope: OWN
myHomework(
after: String
first: Int = 20
before: String
last: Int
status: HomeworkStatus
): HomeworkConnection!
"""我的成绩列表 (core-edu.GradeService.ListGradesByStudent, B4 强制 userId 比对)"""
# @permission: GRADE_READ
# @dataScope: OWN
myGrades(
after: String
first: Int = 20
before: String
last: Int
subject: String
startDate: DateTime
endDate: DateTime
): GradeConnection!
"""我的考勤记录 (core-edu.AttendanceService.ListAttendanceByStudent)"""
# @permission: ATTENDANCE_READ
# @dataScope: OWN
myAttendance(
after: String
first: Int = 20
before: String
last: Int
startDate: DateTime
endDate: DateTime
): AttendanceConnection!
"""教材列表 (content.TextbookService.ListTextbooks)"""
# @permission: TEXTBOOK_READ
# @dataScope: OWN
textbooks(
after: String
first: Int = 20
before: String
last: Int
subjectId: ID
gradeId: ID
): TextbookConnection!
"""章节列表 (content.ChapterService.ListChapters)"""
# @permission: CHAPTER_READ
# @dataScope: OWN
chapters(
after: String
first: Int = 20
before: String
last: Int
textbookId: ID!
): ChapterListPayload!
"""学习路径推荐 (content.KnowledgeGraphService.GetLearningPath)"""
# @permission: LEARNING_PATH_READ
# @dataScope: OWN
learningPath(subjectId: ID!): LearningPathPayload!
"""学生仪表盘 (data-ana.AnalyticsService.GetStudentDashboard)"""
# @permission: DASHBOARD_VIEW
# @dataScope: OWN
studentDashboard: StudentDashboardPayload!
"""我的薄弱点 (data-ana.AnalyticsService.GetStudentWeakness)"""
# @permission: WEAKNESS_READ
# @dataScope: OWN
myWeakness(subjectId: ID): WeaknessPayload!
"""学习趋势 (data-ana.AnalyticsService.GetLearningTrend)"""
# @permission: TREND_READ
# @dataScope: OWN
myTrend(
subjectId: ID
startDate: DateTime
endDate: DateTime
): TrendPayload!
"""我的通知列表 (msg.NotificationService.ListNotifications)"""
# @permission: NOTIFICATION_READ
# @dataScope: OWN
myNotifications(
after: String
first: Int = 20
before: String
last: Int
onlyUnread: Boolean = false
): NotificationConnection!
"""通知未读数 (msg.NotificationService.GetUnreadCount)"""
# @permission: NOTIFICATION_READ
# @dataScope: OWN
myNotificationUnreadCount: UnreadCountPayload!
"""AI 答疑 (同步, ai.Chat). 实际为写操作但 schema 设计为 Query 便于前端 GET 缓存"""
# @permission: STUDENT_AI_CHAT
# @dataScope: OWN
aiChat(input: AIChatInput!): AIChatPayload!
# ===== 扩展 Query (v2 新增, P3-P5) =====
"""考试详情 (含题目与学生提交, core-edu.ExamService.GetExam)"""
# @permission: EXAM_READ
# @dataScope: OWN
examDetail(examId: ID!): ExamDetailPayload!
"""作业详情 (core-edu.HomeworkService.GetHomework)"""
# @permission: HOMEWORK_READ
# @dataScope: OWN
homeworkDetail(homeworkId: ID!): HomeworkDetailPayload!
"""服务器时间 (BFF 本地, 无下游)"""
serverTime: ServerTimePayload!
"""我的课表 (周视图, core-edu.ScheduleService.GetScheduleByStudent)"""
# @permission: SCHEDULE_READ
# @dataScope: OWN
mySchedule(weekStart: DateTime): SchedulePayload!
"""学生成长曲线 (data-ana.GetStudentGrowth)"""
# @permission: ANALYTICS_READ
# @dataScope: OWN
studentGrowth(
subjectId: ID
startDate: DateTime
endDate: DateTime
): StudentGrowthPayload!
"""作业分析 (data-ana.GetAssignmentAnalysis)"""
# @permission: ANALYTICS_READ
# @dataScope: OWN
assignmentAnalysis(homeworkId: ID): AssignmentAnalysisPayload!
"""个人资料 (iam.GetUserProfile)"""
# @permission: AUTH_READ
# @dataScope: OWN
myProfile: MyProfilePayload!
"""掌握度概览 (data-ana.GetMasterySummary)"""
# @permission: ANALYTICS_READ
# @dataScope: OWN
myMasterySummary(subjectId: ID): MasterySummaryPayload!
"""诊断报告列表 (data-ana.ListDiagnosticReports)"""
# @permission: ANALYTICS_READ
# @dataScope: OWN
myDiagnosticReports: DiagnosticReportsPayload!
"""错题本 (data-ana.ListErrorBookItems)"""
# @permission: ANALYTICS_READ
# @dataScope: OWN
myErrorBook(
subjectId: ID
mastered: Boolean
page: Int = 1
pageSize: Int = 20
): ErrorBookPayload!
"""公告列表 (msg.ListAnnouncements)"""
# @permission: ANNOUNCEMENT_READ
# @dataScope: OWN
announcements(
first: Int = 20
category: String
): AnnouncementConnection!
"""公告详情 (msg.GetAnnouncement)"""
# @permission: ANNOUNCEMENT_READ
# @dataScope: OWN
announcementDetail(announcementId: ID!): AnnouncementDetailPayload!
"""我的请假记录 (core-edu.ListLeaveRequestsByStudent)"""
# @permission: LEAVE_REQUEST_READ
# @dataScope: OWN
myLeaveRequests: LeaveRequestsPayload!
"""我的选课记录 (content.ListElectiveSelectionsByStudent)"""
# @permission: ELECTIVE_READ
# @dataScope: OWN
myElectiveSelections: ElectiveSelectionsPayload!
"""可选课程 (content.ListAvailableElectiveCourses)"""
# @permission: ELECTIVE_READ
# @dataScope: OWN
availableElectiveCourses: AvailableElectiveCoursesPayload!
"""我的课案 (content.ListLessonPlansByStudent)"""
# @permission: LESSON_PLAN_READ
# @dataScope: OWN
myLessonPlans: LessonPlansPayload!
"""课案详情 (content.GetLessonPlan)"""
# @permission: LESSON_PLAN_READ
# @dataScope: OWN
lessonPlanDetail(lessonPlanId: ID!): LessonPlanDetailPayload!
"""我的课程计划 (content.ListCoursePlansByStudent)"""
# @permission: COURSE_PLAN_READ
# @dataScope: OWN
myCoursePlans: CoursePlansPayload!
"""课程计划详情 (content.GetCoursePlan)"""
# @permission: COURSE_PLAN_READ
# @dataScope: OWN
coursePlanDetail(coursePlanId: ID!): CoursePlanDetailPayload!
"""成绩报告卡 (core-edu.GradeService.GetReportCard)"""
# @permission: GRADE_READ
# @dataScope: OWN
myReportCard(
examId: ID
term: String
academicYear: String
): ReportCardPayload!
"""我的练习会话列表 (data-ana.ListPracticeSessionsByStudent)"""
# @permission: ANALYTICS_READ
# @dataScope: OWN
myPracticeSessions: PracticeSessionsPayload!
}
# ============================================================================
# Mutation 根类型 (23 Mutations)
# ============================================================================
type Mutation {
"""提交作业 (core-edu.HomeworkService.SubmitHomework, B4 强制 userId 比对)"""
# @permission: HOMEWORK_SUBMIT
# @dataScope: OWN
submitHomework(
input: SubmitHomeworkInput!
): SubmitHomeworkResult!
"""标记通知已读 (msg.NotificationService.MarkAsRead)"""
# @permission: NOTIFICATION_UPDATE
# @dataScope: OWN
markNotificationAsRead(
input: MarkNotificationReadInput!
): MarkNotificationReadResult!
# ===== 扩展 Mutation (v2 新增, P3-P5) =====
"""标记通知已读 (别名, msg.MarkAsRead)"""
# @permission: NOTIFICATION_UPDATE
# @dataScope: OWN
markAsRead(input: MarkAsReadInput!): MarkNotificationReadResult!
"""全部通知标记已读 (msg.MarkAllNotificationsAsRead)"""
# @permission: NOTIFICATION_UPDATE
# @dataScope: OWN
markAllAsRead(input: MarkAllAsReadInput!): MarkAllAsReadResult!
"""更新通知偏好 (msg.UpdateNotificationPreferences)"""
# @permission: NOTIFICATION_UPDATE
# @dataScope: OWN
updateNotificationPreference(
input: UpdateNotificationPreferenceInput!
): GenericMutationResult!
"""提交考试 (含幂等键, core-edu.ExamService.SubmitExam)"""
# @permission: EXAM_SUBMIT
# @dataScope: OWN
submitExam(input: SubmitExamInput!): SubmitExamResult!
"""保存考试草稿 (core-edu.ExamService.SaveExamDraft)"""
# @permission: EXAM_SUBMIT
# @dataScope: OWN
saveExamDraft(input: SaveExamDraftInput!): SaveExamDraftResult!
"""记录考试违规 (防作弊, core-edu.ExamService.RecordExamViolation)"""
# @permission: EXAM_RECORD_VIOLATION
# @dataScope: OWN
recordExamViolation(input: RecordViolationInput!): RecordExamViolationResult!
"""记录粘贴事件 (防作弊, core-edu.ExamService.RecordPasteEvent)"""
# @permission: EXAM_SUBMIT
# @dataScope: OWN
recordPasteEvent(input: RecordPasteEventInput!): RecordExamViolationResult!
"""更新个人资料 (iam.UpdateUserProfile)"""
# @permission: AUTH_UPDATE
# @dataScope: OWN
updateProfile(input: UpdateProfileInput!): UpdateProfileResult!
"""修改密码 (iam.ChangePassword)"""
# @permission: AUTH_UPDATE
# @dataScope: OWN
changePassword(input: ChangePasswordInput!): GenericMutationResult!
"""作业延期申请 (core-edu.RequestHomeworkExtension)"""
# @permission: HOMEWORK_UPDATE
# @dataScope: OWN
requestExtension(input: RequestExtensionInput!): RequestExtensionResult!
"""加入班级 (core-edu.JoinClass)"""
# @permission: CLASS_JOIN
# @dataScope: OWN
joinClass(input: JoinClassInput!): JoinClassResult!
"""退出班级 (core-edu.LeaveClass)"""
# @permission: CLASS_LEAVE
# @dataScope: OWN
leaveClass(input: LeaveClassInput!): LeaveClassResult!
"""添加错题 (data-ana.AddErrorBookItem)"""
# @permission: ANALYTICS_UPDATE
# @dataScope: OWN
addErrorBookItem(input: AddErrorBookItemInput!): ErrorBookItemResult!
"""更新错题 (data-ana.UpdateErrorBookItem)"""
# @permission: ANALYTICS_UPDATE
# @dataScope: OWN
updateErrorBookItem(input: UpdateErrorBookItemInput!): ErrorBookItemResult!
"""删除错题 (data-ana.DeleteErrorBookItem)"""
# @permission: ANALYTICS_UPDATE
# @dataScope: OWN
deleteErrorBookItem(input: DeleteErrorBookItemInput!): ErrorBookItemResult!
"""标记公告已读 (msg.MarkAnnouncementAsRead)"""
# @permission: ANNOUNCEMENT_READ
# @dataScope: OWN
markAnnouncementRead(input: MarkAnnouncementReadInput!): GenericMutationResult!
"""创建请假申请 (core-edu.CreateLeaveRequest)"""
# @permission: LEAVE_REQUEST_CREATE
# @dataScope: OWN
createLeaveRequest(input: CreateLeaveRequestInput!): CreateLeaveRequestResult!
"""取消请假申请 (core-edu.CancelLeaveRequest)"""
# @permission: LEAVE_REQUEST_UPDATE
# @dataScope: OWN
cancelLeaveRequest(input: CancelLeaveRequestInput!): CreateLeaveRequestResult!
"""选择课程 (content.SelectElectiveCourse)"""
# @permission: ELECTIVE_SELECT
# @dataScope: OWN
selectElectiveCourse(input: SelectElectiveInput!): SelectElectiveResult!
"""退选课程 (content.DropElectiveCourse)"""
# @permission: ELECTIVE_DROP
# @dataScope: OWN
dropElectiveCourse(input: DropElectiveInput!): SelectElectiveResult!
"""启动练习会话 (data-ana.StartPracticeSession, Query 移到 Mutation)"""
# @permission: ANALYTICS_UPDATE
# @dataScope: OWN
startPracticeSession(input: StartPracticeSessionInput!): StartPracticeSessionResult!
"""提交练习答案 (data-ana.SubmitPracticeAnswer)"""
# @permission: ANALYTICS_UPDATE
# @dataScope: OWN
submitPracticeAnswer(input: SubmitPracticeAnswerInput!): SubmitPracticeAnswerResult!
}
# ============================================================================
# Input 类型
# ============================================================================
input SubmitHomeworkInput {
homeworkId: ID!
studentId: ID! # 必须与 x-user-id 一致 (B4 越权防御)
answers: [HomeworkAnswerInput!]!
}
input HomeworkAnswerInput {
questionId: ID!
content: String! # 最大 5000 字符
attachments: [String!] # URL 列表, 最多 5 个
}
input MarkNotificationReadInput {
notificationId: ID!
userId: ID! # 必须与 x-user-id 一致 (B4 越权防御)
}
input MarkAsReadInput {
notificationId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input MarkAllAsReadInput {
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input UpdateNotificationPreferenceInput {
channel: NotificationChannel!
enabled: Boolean!
categories: [String!]
}
input SubmitExamInput {
examId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
answers: [ExamAnswerInput!]!
idempotencyKey: String!
}
input ExamAnswerInput {
questionId: ID!
content: String!
attachments: [String!]
}
input SaveExamDraftInput {
examId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
answers: [ExamAnswerInput!]!
draftId: ID
}
input RecordViolationInput {
examId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
violationType: ViolationType!
severity: ViolationSeverity!
details: String
timestamp: DateTime!
}
enum ViolationType {
TAB_SWITCH
COPY_PASTE
WINDOW_BLUR
FULLSCREEN_EXIT
}
enum ViolationSeverity {
LOW
MEDIUM
HIGH
}
input RecordPasteEventInput {
examId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
questionId: ID!
pastedContent: String!
timestamp: DateTime!
}
input UpdateProfileInput {
name: String
avatar: String
phone: String
address: String
bio: String
birthday: String
gender: String
}
input ChangePasswordInput {
currentPassword: String!
newPassword: String!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input RequestExtensionInput {
homeworkId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
reason: String!
requestedDays: Int!
}
input JoinClassInput {
classCode: String!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input LeaveClassInput {
classId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
reason: String
}
input AddErrorBookItemInput {
questionId: ID!
subjectId: ID!
knowledgePointId: ID!
myAnswer: String
correctAnswer: String!
note: String
tags: [String!]
}
input UpdateErrorBookItemInput {
itemId: ID!
note: String
tags: [String!]
mastered: Boolean
}
input DeleteErrorBookItemInput {
itemId: ID!
}
input MarkAnnouncementReadInput {
announcementId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input CreateLeaveRequestInput {
type: LeaveRequestType!
startDate: DateTime!
endDate: DateTime!
reason: String!
attachments: [String!]
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
enum LeaveRequestType {
SICK
PERSONAL
FAMILY
OTHER
}
input CancelLeaveRequestInput {
requestId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input SelectElectiveInput {
courseId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input DropElectiveInput {
courseId: ID!
studentId: ID # 可选, 若提供必须与 x-user-id 一致
}
input StartPracticeSessionInput {
subjectId: ID!
knowledgePointIds: [ID!]
difficulty: PracticeDifficulty
}
enum PracticeDifficulty {
EASY
MEDIUM
HARD
ADAPTIVE
}
input SubmitPracticeAnswerInput {
sessionId: ID!
questionId: ID!
answer: String!
timeSpent: Int
}
# ============================================================================
# AI 流式答疑 (SSE Subscription, P5)
# ============================================================================
type AIStreamChunk {
content: String! # 本次分块内容
done: Boolean! # 是否结束
model: String
usage: AIUsage # done=true 时填充
}
input AIStreamChatInput {
messages: [AIChatMessageInput!]!
model: String = "gpt-4o-mini"
context: AIChatContextInput
}
input AIChatMessageInput {
role: String! # user / assistant
content: String!
}
input AIChatContextInput {
subject: String
knowledgePointId: ID
}
# ============================================================================
# Subscription 根类型 (SSE 传输, GraphQL Yoga 原生支持)
# ============================================================================
type Subscription {
"""AI 答疑流式响应 (ai.StreamChat, SSE 传输)"""
# @permission: STUDENT_AI_CHAT
# @dataScope: OWN
aiStreamChat(input: AIStreamChatInput!): AIStreamChunk!
}