Files
Edu/apps/teacher-portal/src/lib/graphql.ts
SpecialX 1eacd1ed87 feat(teacher-portal): 完整实现 teacher-portal 微前端
包含 settings/students/api、graphql、mocks、ui-tokens 设计令牌等
2026-07-10 19:10:20 +08:00

303 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* GraphQL client + queries/mutationsARB-001 P2 第一版 schema
*
* 维护者ai13teacher-portal
* 关联coord.md §1 ARB-001、02-architecture-design.md §4
*
* schema 来源packages/shared-ts/contracts/graphql/teacher-bff.graphqlai03 维护)
* P2 第一版 Query 范围ARB-001 §1.2dashboard / viewports / me / classes / class5 个)
* P2 不包含 MutationP3+ 扩展)
*
* 错误处理ARB-001 §1.3
* - BFF 始终返回 ActionState 信封success/errors/data
* - GraphQL errors[].extensions.code = BFF_TEACHER_*G14 裁决)
* - 前端按前缀路由 i18n keyF11: error.teacher_bff.<code_snake>
*/
import { createClient, cacheExchange, fetchExchange } from "urql";
import { gql } from "urql";
import type { Client } from "urql";
// ============ Types对齐 ARB-001 §1.2 schema ============
export type DataScope =
"SELF" | "CLASS" | "GRADE" | "SCHOOL" | "DISTRICT" | "ALL";
export type ExamStatus =
"DRAFT" | "PUBLISHED" | "IN_PROGRESS" | "GRADING" | "SCORED" | "ARCHIVED";
export type SubmissionStatus = "NOT_SUBMITTED" | "SUBMITTED" | "GRADED";
export interface User {
id: string;
email: string;
name: string;
roles: string[];
dataScope: DataScope;
}
export interface ViewportItem {
key: string;
label: string;
route: string;
icon: string | null;
sortOrder: string;
requiredPermission: string | null;
}
export interface Class {
id: string;
name: string;
gradeId: string;
studentCount: number;
}
export interface DashboardStats {
totalExams: number;
pendingGrading: number;
todayHomework: number;
}
export interface DashboardData {
user: User;
classes: Class[];
viewports: ViewportItem[];
stats: DashboardStats;
}
// ============ urql client 单例(总裁 §2.17 方案 A ============
/**
* 创建 urql client 单例
*
* Shellteacher-portal调用此函数创建 client通过 GraphQLProvider 注入给所有 Remote。
* token 从 localStorage 读取F12: P2 用 localStorageP6 迁移 httpOnly cookie
*/
export function createGraphQLClient(): Client {
const url =
process.env.NEXT_PUBLIC_TEACHER_BFF_GRAPHQL_URL ||
"/api/v1/teacher/graphql";
return createClient({
url,
fetchOptions: () => {
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
// F12: P2 token 存 localStorage
if (typeof window !== "undefined") {
const token = window.localStorage.getItem("edu_access_token");
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
}
return { headers };
},
exchanges: [cacheExchange, fetchExchange],
});
}
// ============ P2 QueryARB-001 §1.25 个 Must Have ============
/** 仪表盘聚合查询并行iam.me + iam.viewports + iam.permissions + classes.byTeacher */
export const DashboardQuery = gql`
query Dashboard {
dashboard {
user {
id
email
name
roles
dataScope
}
classes {
id
name
gradeId
studentCount
}
viewports {
key
label
route
icon
sortOrder
requiredPermission
}
stats {
totalExams
pendingGrading
todayHomework
}
}
}
`;
/** 视口配置查询(导航) */
export const ViewportsQuery = gql`
query Viewports {
viewports {
key
label
route
icon
sortOrder
requiredPermission
}
}
`;
/** 当前用户信息查询 */
export const MeQuery = gql`
query Me {
me {
id
email
name
roles
dataScope
}
}
`;
/** 班级列表查询(按教师 dataScope 过滤) */
export const ClassesQuery = gql`
query Classes {
classes {
id
name
gradeId
studentCount
}
}
`;
/** 班级详情查询 */
export const ClassQuery = gql`
query Class($id: ID!) {
class(id: $id) {
id
name
gradeId
studentCount
}
}
`;
// ============ P3 Query/MutationMSW mock待 teacher-bff P3+ 实现) ============
// 以下查询对应 workline.md §3 P3-P5 交付物schema 尚未在 ARB-001 P2 第一版中定义。
// 开发期通过 MSW 拦截返回 mock 响应;上游就绪后由 ai03 在 teacher-bff.graphql 扩展。
/** 学生信息iam 数据,经 teacher-bff 聚合) */
export interface Student {
id: string;
name: string;
email: string;
avatarUrl: string | null;
}
/** 班级学生名单查询P3 扩展MSW mock */
export const ClassStudentsQuery = gql`
query ClassStudents($classId: ID!) {
classStudents(classId: $classId) {
id
name
email
avatarUrl
}
}
`;
/** 考试项core-edu 域) */
export interface ExamItem {
id: string;
classId: string;
title: string;
description: string | null;
examDate: string;
duration: number;
totalScore: number;
status: ExamStatus;
}
/** 班级考试列表查询P3 扩展MSW mock */
export const ClassExamsQuery = gql`
query ClassExams($classId: ID!) {
classExams(classId: $classId) {
id
classId
title
description
examDate
duration
totalScore
status
}
}
`;
/** 作业项core-edu 域) */
export interface HomeworkItem {
id: string;
classId: string;
title: string;
description: string | null;
dueDate: string;
status: SubmissionStatus;
}
/** 班级作业列表查询P3 扩展MSW mock */
export const ClassHomeworkQuery = gql`
query ClassHomework($classId: ID!) {
classHomework(classId: $classId) {
id
classId
title
description
dueDate
status
}
}
`;
/** 成绩项core-edu 域) */
export interface GradeItem {
id: string;
studentId: string;
studentName: string;
examId: string | null;
homeworkId: string | null;
score: number;
feedback: string | null;
}
/** 考试成绩列表查询P3 扩展MSW mock */
export const ExamGradesQuery = gql`
query ExamGrades($examId: ID!) {
examGrades(examId: $examId) {
id
studentId
studentName
examId
homeworkId
score
feedback
}
}
`;
/** 更新用户信息 MutationP3 扩展MSW mock */
export const UpdateUserMutation = gql`
mutation UpdateUser($input: UpdateUserInput!) {
updateUser(input: $input) {
id
email
name
roles
dataScope
}
}
`;