chore(student-bff): merge student-bff full implementation into main
This commit is contained in:
805
packages/shared-ts/contracts/graphql/student-bff.schema.graphql
Normal file
805
packages/shared-ts/contracts/graphql/student-bff.schema.graphql
Normal file
@@ -0,0 +1,805 @@
|
||||
# student-bff GraphQL Schema (v1)
|
||||
#
|
||||
# 负责人: 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)
|
||||
# 起草与仲裁流程: ai04 起草 → coord 在批次 2 启动前仲裁第一版 → ai14 (student-portal) 消费
|
||||
#
|
||||
# 设计规范 (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
|
||||
|
||||
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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 作业 (下游: 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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 成绩 (下游: 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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 考勤 (下游: core-edu AttendanceService, P3 预留)
|
||||
# ============================================================================
|
||||
|
||||
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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 教材与章节 (下游: 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 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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 通知 (下游: 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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 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!]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Mutation 结果
|
||||
# ============================================================================
|
||||
|
||||
type SubmitHomeworkResult {
|
||||
success: Boolean!
|
||||
submissionId: ID
|
||||
homeworkId: ID!
|
||||
submittedAt: DateTime
|
||||
status: HomeworkStatus
|
||||
error: MutationError
|
||||
}
|
||||
|
||||
type MarkNotificationReadResult {
|
||||
success: Boolean!
|
||||
notificationId: ID!
|
||||
readAt: DateTime
|
||||
error: MutationError
|
||||
}
|
||||
|
||||
type MutationError {
|
||||
code: String! # BFF_STUDENT_* 前缀
|
||||
message: String!
|
||||
traceId: String
|
||||
i18nKey: String
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Query 根类型
|
||||
# ============================================================================
|
||||
|
||||
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!
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Mutation 根类型
|
||||
# ============================================================================
|
||||
|
||||
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!
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 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 越权防御)
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 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!
|
||||
}
|
||||
Reference in New Issue
Block a user