1649 lines
38 KiB
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!
|
|
}
|