feat(student-portal): 实现CICD参考项目差距功能 - 错题本/请假/选课/自适应练习/公告/课案/课程计划/成绩报告卡/学情诊断增强

This commit is contained in:
SpecialX
2026-07-13 13:39:28 +08:00
parent 7c8e0f5dea
commit f13ca612e6
29 changed files with 6012 additions and 24 deletions

View File

@@ -0,0 +1,51 @@
[
{
"id": "an-001",
"title": "关于期末考试安排的通知",
"content": "各位同学:本学期期末考试将于 7 月 20 日至 7 月 25 日进行,具体科目时间安排请查看考试日程表。请同学们提前做好复习准备,注意考场纪律,携带学生证入场。",
"type": "school",
"status": "published",
"authorName": "教务处",
"publishedAt": "2026-07-10T09:00:00.000Z",
"isPinned": true,
"isReadByCurrentUser": false,
"createdAt": "2026-07-10T08:30:00.000Z",
"updatedAt": "2026-07-10T09:00:00.000Z"
},
{
"id": "an-002",
"title": "高三年级暑期辅导报名通知",
"content": "高三年级暑期辅导班即日起开始报名,涵盖数学、物理、化学等核心科目。有意参加的同学请在 7 月 18 日前到教务处登记。名额有限,先到先得。",
"type": "grade",
"status": "published",
"authorName": "年级组",
"publishedAt": "2026-07-09T14:00:00.000Z",
"isPinned": false,
"isReadByCurrentUser": true,
"createdAt": "2026-07-09T13:00:00.000Z"
},
{
"id": "an-003",
"title": "班级卫生值日调整通知",
"content": "因近期天气炎热,本周班级卫生值日表有所调整,请值日同学按时到岗。具体安排见班级公告栏。",
"type": "class",
"status": "published",
"authorName": "王老师",
"publishedAt": "2026-07-08T10:00:00.000Z",
"isPinned": false,
"isReadByCurrentUser": true,
"createdAt": "2026-07-08T09:30:00.000Z"
},
{
"id": "an-004",
"title": "校园文化艺术节活动通知",
"content": "一年一度的校园文化艺术节将于 7 月 15 日开幕,设有歌唱、舞蹈、书法、摄影等多项比赛。欢迎同学们踊跃报名参加,报名截止日期 7 月 13 日。",
"type": "school",
"status": "published",
"authorName": "校团委",
"publishedAt": "2026-07-05T15:00:00.000Z",
"isPinned": false,
"isReadByCurrentUser": false,
"createdAt": "2026-07-05T14:00:00.000Z"
}
]

View File

@@ -0,0 +1,47 @@
[
{
"id": "cp-001",
"className": "高三(2)班",
"subjectName": "数学",
"teacherName": "张老师",
"semester": "1",
"totalHours": 72,
"completedHours": 48,
"weeklyHours": 4,
"startDate": "2026-02-20",
"endDate": "2026-07-15",
"syllabus": "高三数学上学期教学计划,涵盖函数与导数、三角函数、数列等核心章节。",
"objectives": "掌握函数与导数的基本概念与应用,提升数学建模与解题能力。",
"status": "active"
},
{
"id": "cp-002",
"className": "高三(2)班",
"subjectName": "语文",
"teacherName": "李老师",
"semester": "1",
"totalHours": 64,
"completedHours": 52,
"weeklyHours": 4,
"startDate": "2026-02-20",
"endDate": "2026-07-15",
"syllabus": "高三语文上学期教学计划,涵盖现代文阅读、古代诗文、写作训练。",
"objectives": "提升阅读理解与写作能力,掌握古代诗文鉴赏方法。",
"status": "active"
},
{
"id": "cp-003",
"className": "高三(2)班",
"subjectName": "英语",
"teacherName": "刘老师",
"semester": "1",
"totalHours": 60,
"completedHours": 60,
"weeklyHours": 4,
"startDate": "2026-02-20",
"endDate": "2026-07-15",
"syllabus": "高三英语上学期教学计划,涵盖语法复习、阅读理解、写作与听力训练。",
"objectives": "系统复习语法知识,提升综合语言运用能力。",
"status": "completed"
}
]

View File

@@ -0,0 +1,45 @@
[
{
"id": "dr-001",
"reportType": "individual",
"period": "2025-2026学年第一学期",
"summary": "该生本学期整体学习状态良好,数学与英语表现突出,语文阅读理解有待加强。建议保持优势科目,针对薄弱环节加强练习。",
"strengths": [
"数学逻辑思维能力强,函数与导数章节掌握扎实",
"英语词汇量丰富,听力与阅读理解正确率高"
],
"weaknesses": [
"语文文言文阅读得分率偏低",
"物理力学综合题解题速度需提升"
],
"recommendations": [
"每周增加 2 篇文言文阅读训练",
"针对物理力学综合题进行限时练习",
"保持数学与英语的日常练习频率"
],
"overallScore": 87,
"status": "published",
"createdAt": "2026-05-10T10:00:00.000Z"
},
{
"id": "dr-002",
"reportType": "individual",
"period": "2025-2026学年第二学期期中",
"summary": "期中考试后诊断显示,该生在各科均衡发展方面有所进步,数学成绩显著提升,化学实验题仍需加强。",
"strengths": [
"数学成绩从 85 分提升至 92 分,进步明显",
"英语写作能力提升,语法运用准确率提高"
],
"weaknesses": [
"化学实验设计题得分率仅 60%",
"历史材料分析题时间分配不足"
],
"recommendations": [
"加强化学实验原理理解与设计训练",
"历史答题注意时间管理,先易后难"
],
"overallScore": 89,
"status": "published",
"createdAt": "2026-05-05T14:00:00.000Z"
}
]

View File

@@ -0,0 +1,82 @@
[
{
"id": "ec-001",
"name": "创意写作工作坊",
"teacherName": "李老师",
"subjectName": "语文",
"gradeName": "高三",
"description": "通过沉浸式写作训练,提升文学创作与表达能力,涵盖散文、小说、诗歌等多种体裁。",
"capacity": 30,
"enrolledCount": 22,
"classroom": "文科楼 305",
"schedule": "每周三 16:00-17:40",
"startDate": "2026-09-01",
"endDate": "2027-01-15",
"selectionStartAt": "2026-07-01T00:00:00.000Z",
"selectionEndAt": "2026-07-20T23:59:59.000Z",
"dropDeadline": "2026-09-15T23:59:59.000Z",
"status": "open",
"selectionMode": "fcfs",
"credit": "2"
},
{
"id": "ec-002",
"name": "数学建模入门",
"teacherName": "张老师",
"subjectName": "数学",
"gradeName": "高三",
"description": "学习数学建模基本方法,结合实际问题进行建模实践,培养数学应用能力。",
"capacity": 40,
"enrolledCount": 40,
"classroom": "理科楼 201",
"schedule": "每周二 16:00-17:40",
"startDate": "2026-09-01",
"endDate": "2027-01-15",
"selectionStartAt": "2026-07-01T00:00:00.000Z",
"selectionEndAt": "2026-07-20T23:59:59.000Z",
"dropDeadline": "2026-09-15T23:59:59.000Z",
"status": "open",
"selectionMode": "lottery",
"credit": "3"
},
{
"id": "ec-003",
"name": "趣味物理实验",
"teacherName": "陈老师",
"subjectName": "物理",
"gradeName": "高三",
"description": "通过动手实验探索物理规律,涵盖力学、电磁学、光学等领域的经典实验。",
"capacity": 25,
"enrolledCount": 18,
"classroom": "物理实验室",
"schedule": "每周四 16:00-17:40",
"startDate": "2026-09-01",
"endDate": "2027-01-15",
"selectionStartAt": "2026-07-01T00:00:00.000Z",
"selectionEndAt": "2026-07-20T23:59:59.000Z",
"dropDeadline": "2026-09-15T23:59:59.000Z",
"status": "open",
"selectionMode": "fcfs",
"credit": "2"
},
{
"id": "ec-004",
"name": "英语演讲与辩论",
"teacherName": "刘老师",
"subjectName": "英语",
"gradeName": "高三",
"description": "提升英语口语表达与逻辑思辨能力,模拟辩论赛制进行实战训练。",
"capacity": 20,
"enrolledCount": 15,
"classroom": "外语楼 102",
"schedule": "每周五 16:00-17:40",
"startDate": "2026-09-01",
"endDate": "2027-01-15",
"selectionStartAt": "2026-07-01T00:00:00.000Z",
"selectionEndAt": "2026-07-20T23:59:59.000Z",
"dropDeadline": "2026-09-15T23:59:59.000Z",
"status": "open",
"selectionMode": "fcfs",
"credit": "2"
}
]

View File

@@ -0,0 +1,102 @@
{
"items": [
{
"id": "eb-001",
"questionId": "q-101",
"questionContent": "已知函数 f(x) = x² - 2x + 1求 f(x) 在区间 [0, 2] 上的最小值。",
"sourceType": "exam",
"subject": { "id": "subject-math", "name": "数学" },
"knowledgePointIds": ["kp-001"],
"knowledgePointName": "二次函数最值",
"status": "learning",
"masteryLevel": 0.4,
"nextReviewAt": "2026-07-14T08:00:00.000Z",
"reviewCount": 2,
"correctStreak": 1,
"note": "配方时容易漏掉常数项",
"errorTags": ["概念不清", "计算错误"],
"createdAt": "2026-07-01T10:00:00.000Z",
"updatedAt": "2026-07-10T14:30:00.000Z"
},
{
"id": "eb-002",
"questionId": "q-205",
"questionContent": "阅读下面文段,回答问题:\"月光如流水一般,静静地泻在这一片叶子和花上。\" 这句话运用了什么修辞手法?",
"sourceType": "homework",
"subject": { "id": "subject-chinese", "name": "语文" },
"knowledgePointIds": ["kp-201"],
"knowledgePointName": "修辞手法辨析",
"status": "new",
"masteryLevel": 0.2,
"nextReviewAt": "2026-07-13T08:00:00.000Z",
"reviewCount": 0,
"correctStreak": 0,
"note": null,
"errorTags": ["审题不清"],
"createdAt": "2026-07-08T09:00:00.000Z",
"updatedAt": "2026-07-08T09:00:00.000Z"
},
{
"id": "eb-003",
"questionId": "q-308",
"questionContent": "Choose the correct word to fill in the blank: She ___ to school every day.",
"sourceType": "exam",
"subject": { "id": "subject-english", "name": "英语" },
"knowledgePointIds": ["kp-301"],
"knowledgePointName": "一般现在时",
"status": "mastered",
"masteryLevel": 0.9,
"nextReviewAt": "2026-07-20T08:00:00.000Z",
"reviewCount": 5,
"correctStreak": 4,
"note": null,
"errorTags": [],
"createdAt": "2026-06-15T10:00:00.000Z",
"updatedAt": "2026-07-11T16:00:00.000Z"
},
{
"id": "eb-004",
"questionId": "q-410",
"questionContent": "计算:(-3)² + |-5| - √16 的值。",
"sourceType": "manual",
"subject": { "id": "subject-math", "name": "数学" },
"knowledgePointIds": ["kp-002"],
"knowledgePointName": "有理数运算",
"status": "new",
"masteryLevel": 0.1,
"nextReviewAt": "2026-07-13T08:00:00.000Z",
"reviewCount": 0,
"correctStreak": 0,
"note": "绝对值和平方容易混淆",
"errorTags": ["概念不清", "粗心大意"],
"createdAt": "2026-07-12T11:00:00.000Z",
"updatedAt": "2026-07-12T11:00:00.000Z"
},
{
"id": "eb-005",
"questionId": "q-502",
"questionContent": "简述光合作用的过程及其对生物圈的意义。",
"sourceType": "homework",
"subject": { "id": "subject-biology", "name": "生物" },
"knowledgePointIds": ["kp-501"],
"knowledgePointName": "光合作用",
"status": "learning",
"masteryLevel": 0.55,
"nextReviewAt": "2026-07-15T08:00:00.000Z",
"reviewCount": 3,
"correctStreak": 2,
"note": "光反应和暗反应阶段容易混淆",
"errorTags": ["记忆错误"],
"createdAt": "2026-06-28T14:00:00.000Z",
"updatedAt": "2026-07-09T10:00:00.000Z"
}
],
"stats": {
"totalCount": 5,
"newCount": 2,
"learningCount": 2,
"masteredCount": 1,
"dueReviewCount": 3,
"masteredRate": 0.2
}
}

View File

@@ -0,0 +1,58 @@
[
{
"id": "lv-001",
"leaveType": "sick",
"startDate": "2026-07-10",
"endDate": "2026-07-11",
"reason": "感冒发烧,需在家休息恢复。",
"status": "approved",
"reviewComment": "注意休息,祝早日康复。",
"reviewedAt": "2026-07-09T14:30:00.000Z",
"reviewerName": "王老师",
"className": "高三(2)班",
"attendanceSynced": true,
"createdAt": "2026-07-09T08:15:00.000Z"
},
{
"id": "lv-002",
"leaveType": "personal",
"startDate": "2026-07-15",
"endDate": "2026-07-15",
"reason": "家中有事需处理,请假一天。",
"status": "pending",
"reviewComment": null,
"reviewedAt": null,
"reviewerName": null,
"className": "高三(2)班",
"attendanceSynced": false,
"createdAt": "2026-07-13T09:00:00.000Z"
},
{
"id": "lv-003",
"leaveType": "family",
"startDate": "2026-06-20",
"endDate": "2026-06-22",
"reason": "参加亲属婚礼,需请假三天。",
"status": "rejected",
"reviewComment": "临近期末考试,建议调整时间。",
"reviewedAt": "2026-06-19T16:00:00.000Z",
"reviewerName": "王老师",
"className": "高三(2)班",
"attendanceSynced": false,
"createdAt": "2026-06-18T10:00:00.000Z"
},
{
"id": "lv-004",
"leaveType": "other",
"startDate": "2026-07-05",
"endDate": "2026-07-05",
"reason": "参加市级学科竞赛培训。",
"status": "cancelled",
"reviewComment": null,
"reviewedAt": null,
"reviewerName": null,
"className": "高三(2)班",
"attendanceSynced": false,
"createdAt": "2026-07-04T11:20:00.000Z"
}
]

View File

@@ -0,0 +1,46 @@
[
{
"id": "lp-001",
"title": "二次函数的图像与性质",
"subjectName": "数学",
"gradeName": "高三",
"chapterTitle": "第一章 函数与导数",
"creatorName": "张老师",
"status": "published",
"publishedAt": "2026-07-08T08:00:00.000Z",
"updatedAt": "2026-07-08T08:00:00.000Z"
},
{
"id": "lp-002",
"title": "文言文阅读:赤壁赋",
"subjectName": "语文",
"gradeName": "高三",
"chapterTitle": "第三章 古代散文",
"creatorName": "李老师",
"status": "published",
"publishedAt": "2026-07-07T10:00:00.000Z",
"updatedAt": "2026-07-07T10:00:00.000Z"
},
{
"id": "lp-003",
"title": "牛顿运动定律的应用",
"subjectName": "物理",
"gradeName": "高三",
"chapterTitle": "第二章 力学",
"creatorName": "陈老师",
"status": "published",
"publishedAt": "2026-07-06T09:00:00.000Z",
"updatedAt": "2026-07-06T09:00:00.000Z"
},
{
"id": "lp-004",
"title": "化学平衡常数与计算",
"subjectName": "化学",
"gradeName": "高三",
"chapterTitle": "第四章 化学反应原理",
"creatorName": "赵老师",
"status": "published",
"publishedAt": "2026-07-05T14:00:00.000Z",
"updatedAt": "2026-07-05T14:00:00.000Z"
}
]

View File

@@ -0,0 +1,49 @@
{
"studentId": "stu-001",
"studentName": "陈同学",
"averageMastery": 76,
"totalKnowledgePoints": 42,
"confidenceLevel": "high",
"mastery": [
{
"knowledgePointId": "kp-math-01",
"knowledgePointName": "函数与导数",
"masteryLevel": 92,
"totalQuestions": 35,
"correctQuestions": 32,
"lastAssessedAt": "2026-07-10T14:00:00.000Z"
},
{
"knowledgePointId": "kp-math-02",
"knowledgePointName": "三角函数",
"masteryLevel": 78,
"totalQuestions": 28,
"correctQuestions": 22,
"lastAssessedAt": "2026-07-09T10:00:00.000Z"
},
{
"knowledgePointId": "kp-chinese-01",
"knowledgePointName": "文言文阅读",
"masteryLevel": 58,
"totalQuestions": 20,
"correctQuestions": 12,
"lastAssessedAt": "2026-07-08T15:00:00.000Z"
},
{
"knowledgePointId": "kp-english-01",
"knowledgePointName": "阅读理解",
"masteryLevel": 85,
"totalQuestions": 40,
"correctQuestions": 34,
"lastAssessedAt": "2026-07-11T09:00:00.000Z"
},
{
"knowledgePointId": "kp-physics-01",
"knowledgePointName": "力学综合",
"masteryLevel": 68,
"totalQuestions": 25,
"correctQuestions": 17,
"lastAssessedAt": "2026-07-07T11:00:00.000Z"
}
]
}

View File

@@ -0,0 +1,55 @@
{
"sessions": [
{
"id": "ps-001",
"practiceType": "error_variant",
"status": "completed",
"totalQuestions": 10,
"answeredQuestions": 10,
"correctCount": 8,
"accuracy": 0.8,
"startedAt": "2026-07-10T14:00:00.000Z",
"completedAt": "2026-07-10T14:25:00.000Z"
},
{
"id": "ps-002",
"practiceType": "knowledge_point",
"status": "completed",
"totalQuestions": 8,
"answeredQuestions": 8,
"correctCount": 6,
"accuracy": 0.75,
"startedAt": "2026-07-11T19:00:00.000Z",
"completedAt": "2026-07-11T19:18:00.000Z"
},
{
"id": "ps-003",
"practiceType": "weak_chapter",
"status": "in_progress",
"totalQuestions": 12,
"answeredQuestions": 5,
"correctCount": 3,
"accuracy": 0.6,
"startedAt": "2026-07-13T10:00:00.000Z",
"completedAt": null
},
{
"id": "ps-004",
"practiceType": "ai_recommended",
"status": "abandoned",
"totalQuestions": 10,
"answeredQuestions": 2,
"correctCount": 1,
"accuracy": 0.5,
"startedAt": "2026-07-08T16:00:00.000Z",
"completedAt": null
}
],
"stats": {
"totalSessions": 4,
"completedSessions": 2,
"totalQuestionsAnswered": 25,
"totalCorrect": 18,
"overallAccuracy": 0.72
}
}

View File

@@ -0,0 +1,101 @@
{
"studentId": "stu-001",
"studentName": "陈同学",
"classId": "cls-001",
"className": "高三(2)班",
"classTeacherName": "王老师",
"academicYearName": "2025-2026学年",
"semester": "1",
"subjects": [
{
"subjectId": "subject-math",
"subjectName": "数学",
"averageScore": 88.5,
"rankInSubject": 5,
"totalStudentsInSubject": 45,
"fullScoreTotal": 442.5,
"records": [
{
"id": "gr-001",
"examName": "月考一",
"score": 85,
"maxScore": 100,
"grade": "良好",
"rank": 6,
"submittedAt": "2026-03-15T10:00:00.000Z"
},
{
"id": "gr-002",
"examName": "期中考试",
"score": 92,
"maxScore": 100,
"grade": "优秀",
"rank": 3,
"submittedAt": "2026-04-20T10:00:00.000Z"
}
]
},
{
"subjectId": "subject-chinese",
"subjectName": "语文",
"averageScore": 82.0,
"rankInSubject": 8,
"totalStudentsInSubject": 45,
"fullScoreTotal": 410.0,
"records": [
{
"id": "gr-003",
"examName": "月考一",
"score": 80,
"maxScore": 100,
"grade": "良好",
"rank": 10,
"submittedAt": "2026-03-16T10:00:00.000Z"
},
{
"id": "gr-004",
"examName": "期中考试",
"score": 84,
"maxScore": 100,
"grade": "良好",
"rank": 7,
"submittedAt": "2026-04-21T10:00:00.000Z"
}
]
},
{
"subjectId": "subject-english",
"subjectName": "英语",
"averageScore": 90.5,
"rankInSubject": 3,
"totalStudentsInSubject": 45,
"fullScoreTotal": 452.5,
"records": [
{
"id": "gr-005",
"examName": "月考一",
"score": 89,
"maxScore": 100,
"grade": "良好",
"rank": 4,
"submittedAt": "2026-03-17T10:00:00.000Z"
},
{
"id": "gr-006",
"examName": "期中考试",
"score": 92,
"maxScore": 100,
"grade": "优秀",
"rank": 2,
"submittedAt": "2026-04-22T10:00:00.000Z"
}
]
}
],
"overallAverage": 87.0,
"overallRank": 4,
"classTotalStudents": 45,
"overallPassRate": 0.96,
"overallExcellentRate": 0.33,
"generatedAt": "2026-07-13T12:00:00.000Z"
}

View File

@@ -40,6 +40,17 @@ import myScheduleFixture from "./fixtures/mySchedule.json";
import studentGrowthFixture from "./fixtures/studentGrowth.json";
import assignmentAnalysisFixture from "./fixtures/assignmentAnalysis.json";
import myProfileFixture from "./fixtures/myProfile.json";
// 批次 A 扩展 fixture
import errorBookFixture from "./fixtures/errorBook.json";
import leaveRequestsFixture from "./fixtures/leaveRequests.json";
import electiveCoursesFixture from "./fixtures/electiveCourses.json";
import practiceSessionsFixture from "./fixtures/practiceSessions.json";
import announcementsFixture from "./fixtures/announcements.json";
import lessonPlansFixture from "./fixtures/lessonPlans.json";
import coursePlansFixture from "./fixtures/coursePlans.json";
import reportCardFixture from "./fixtures/reportCard.json";
import diagnosticReportsFixture from "./fixtures/diagnosticReports.json";
import masterySummaryFixture from "./fixtures/masterySummary.json";
// ---------------------------------------------------------------------------
// GraphQL 请求/响应类型
@@ -275,6 +286,181 @@ function handleQuery(
case "myProfile":
return graphqlOk("myProfile", ok(myProfileFixture));
// === 批次 A 扩展查询 ===
case "myErrorBook":
return graphqlOk("myErrorBook", ok(errorBookFixture));
case "myLeaveRequests":
return graphqlOk("myLeaveRequests", ok(leaveRequestsFixture));
case "myElectiveSelections": {
// 从可选课程中派生选课记录
const selections = electiveCoursesFixture.slice(0, 2).map((c) => ({
id: `sel-${c.id}`,
courseId: c.id,
courseName: c.name,
status: "enrolled" as const,
selectedAt: "2026-07-02T10:00:00.000Z",
enrolledAt: "2026-07-03T08:00:00.000Z",
droppedAt: null,
dropReason: null,
course: {
id: c.id,
name: c.name,
teacherName: c.teacherName,
subjectName: c.subjectName,
capacity: c.capacity,
enrolledCount: c.enrolledCount,
classroom: c.classroom,
schedule: c.schedule,
credit: c.credit,
status: c.status,
},
}));
return graphqlOk("myElectiveSelections", ok(selections));
}
case "availableElectiveCourses":
return graphqlOk(
"availableElectiveCourses",
ok(electiveCoursesFixture),
);
case "myPracticeSessions":
return graphqlOk("myPracticeSessions", ok(practiceSessionsFixture));
case "startPracticeSession": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const practiceType = (input["practiceType"] as string) ?? "error_variant";
const session = {
id: `ps-${Date.now()}`,
practiceType,
status: "in_progress" as const,
totalQuestions: 10,
answeredQuestions: 0,
correctCount: 0,
accuracy: 0,
startedAt: new Date().toISOString(),
currentQuestionIndex: 0,
questions: [
{
id: "pq-001",
questionId: "q-001",
content: "已知函数 f(x) = x² - 2x + 1求 f(x) 的最小值。",
type: "short-answer",
options: [],
maxScore: 5,
status: "pending" as const,
},
{
id: "pq-002",
questionId: "q-002",
content: "下列哪个是二次函数的标准形式?",
type: "single-choice",
options: [
{ id: "opt-a", text: "y = ax + b" },
{ id: "opt-b", text: "y = ax² + bx + c (a≠0)" },
{ id: "opt-c", text: "y = a/x" },
{ id: "opt-d", text: "y = √x" },
],
maxScore: 5,
status: "pending" as const,
},
],
};
return graphqlOk("startPracticeSession", ok({ session }));
}
case "announcements":
return graphqlOk("announcements", ok(announcementsFixture));
case "announcementDetail": {
const announcementId =
(variables["announcementId"] as string) ?? "an-001";
const detail =
announcementsFixture.find((a) => a.id === announcementId) ??
announcementsFixture[0];
return graphqlOk("announcementDetail", ok(detail));
}
case "myLessonPlans":
return graphqlOk("myLessonPlans", ok(lessonPlansFixture));
case "lessonPlanDetail": {
const planId = (variables["planId"] as string) ?? "lp-001";
const item =
lessonPlansFixture.find((l) => l.id === planId) ?? lessonPlansFixture[0];
const detail = {
...item,
content: {
objectives:
"1. 理解核心概念与基本原理\n2. 掌握解题方法与技巧\n3. 能够综合运用解决实际问题",
keyPoints: "重点:核心公式的推导与应用\n难点综合题型的分析与解题策略",
summary: "本节课围绕核心知识点展开,通过例题讲解与课堂练习巩固理解。",
homework: "完成课后练习第 1-5 题,预习下一节内容。",
blackboard: "板书要点:公式推导流程图 + 例题解题步骤",
},
};
return graphqlOk("lessonPlanDetail", ok(detail));
}
case "myCoursePlans":
return graphqlOk("myCoursePlans", ok(coursePlansFixture));
case "coursePlanDetail": {
const planId = (variables["planId"] as string) ?? "cp-001";
const item =
coursePlansFixture.find((c) => c.id === planId) ?? coursePlansFixture[0];
const detail = {
...item,
items: [
{
id: "cpi-001",
week: 1,
topic: "函数的基本概念",
content: "函数定义、定义域、值域、表示方法",
hours: 4,
textbookChapter: "第 1.1 节",
notes: null,
isCompleted: true,
completedAt: "2026-02-23T16:00:00.000Z",
},
{
id: "cpi-002",
week: 2,
topic: "函数的单调性与奇偶性",
content: "单调性判定、奇偶性判定、综合应用",
hours: 4,
textbookChapter: "第 1.2-1.3 节",
notes: "注意数形结合",
isCompleted: true,
completedAt: "2026-03-02T16:00:00.000Z",
},
{
id: "cpi-013",
week: 13,
topic: "期末复习与综合训练",
content: "全册知识点梳理、模拟试卷讲评",
hours: 4,
textbookChapter: "复习章节",
notes: null,
isCompleted: false,
completedAt: null,
},
],
};
return graphqlOk("coursePlanDetail", ok(detail));
}
case "myReportCard":
return graphqlOk("myReportCard", ok(reportCardFixture));
case "myDiagnosticReports":
return graphqlOk("myDiagnosticReports", ok(diagnosticReportsFixture));
case "myMasterySummary":
return graphqlOk("myMasterySummary", ok(masterySummaryFixture));
default: {
// 未覆盖的查询操作名,返回错误
return graphqlError(
@@ -433,6 +619,127 @@ function handleMutation(
return graphqlOk("leaveClass", ok(result));
}
// === 批次 A 扩展变更 ===
case "addErrorBookItem": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const result = {
id: `eb-${Date.now()}`,
status: "new" as const,
createdAt: new Date().toISOString(),
};
void input;
return graphqlOk("addErrorBookItem", ok(result));
}
case "updateErrorBookItem": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const id = (input["id"] as string) ?? "";
const reviewResult = input["reviewResult"] as
| "again"
| "hard"
| "good"
| "easy"
| undefined;
const masteryMap: Record<string, number> = {
again: 0.2,
hard: 0.4,
good: 0.7,
easy: 0.9,
};
const mastery = reviewResult ? masteryMap[reviewResult] ?? 0.5 : 0.5;
const nextReview = new Date();
nextReview.setDate(nextReview.getDate() + (reviewResult === "again" ? 1 : reviewResult === "hard" ? 3 : 7));
const result = {
id,
status: mastery >= 0.8 ? ("mastered" as const) : ("learning" as const),
masteryLevel: mastery,
nextReviewAt: nextReview.toISOString(),
reviewCount: 1,
};
return graphqlOk("updateErrorBookItem", ok(result));
}
case "deleteErrorBookItem": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const id = (input["id"] as string) ?? "";
return graphqlOk("deleteErrorBookItem", ok({ id, deleted: true }));
}
case "createLeaveRequest": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const result = {
id: `lv-${Date.now()}`,
status: "pending" as const,
createdAt: new Date().toISOString(),
};
void input;
return graphqlOk("createLeaveRequest", ok(result));
}
case "cancelLeaveRequest": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const id = (input["id"] as string) ?? "";
return graphqlOk(
"cancelLeaveRequest",
ok({ id, status: "cancelled" as const }),
);
}
case "selectElectiveCourse": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const courseId = (input["courseId"] as string) ?? "";
const result = {
id: `sel-${Date.now()}`,
courseId,
status: "enrolled" as const,
selectedAt: new Date().toISOString(),
};
return graphqlOk("selectElectiveCourse", ok(result));
}
case "dropElectiveCourse": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const courseId = (input["courseId"] as string) ?? "";
const result = {
id: `sel-${courseId}`,
courseId,
status: "dropped" as const,
droppedAt: new Date().toISOString(),
};
return graphqlOk("dropElectiveCourse", ok(result));
}
case "submitPracticeAnswer": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const questionId = (input["questionId"] as string) ?? "";
const isCorrect = questionId === "q-002" && input["answer"] === "opt-b";
const result = {
answerId: `ans-${Date.now()}`,
isCorrect,
score: isCorrect ? 5 : 0,
maxScore: 5,
correctAnswer: questionId === "q-002" ? "opt-b" : "x=1",
sessionCompleted: false,
session: {
id: (input["sessionId"] as string) ?? "",
answeredQuestions: 1,
correctCount: isCorrect ? 1 : 0,
accuracy: isCorrect ? 1 : 0,
status: "in_progress" as const,
},
};
return graphqlOk("submitPracticeAnswer", ok(result));
}
case "markAnnouncementRead": {
const input = (variables["input"] ?? {}) as Record<string, unknown>;
const announcementId = (input["announcementId"] as string) ?? "";
return graphqlOk(
"markAnnouncementRead",
ok({ announcementId, read: true }),
);
}
default: {
// 未覆盖的变更操作名,返回错误
return graphqlError(
@@ -469,6 +776,21 @@ const QUERY_NAMES = new Set<QueryOperationName>([
"studentGrowth",
"assignmentAnalysis",
"myProfile",
"myErrorBook",
"myLeaveRequests",
"myElectiveSelections",
"availableElectiveCourses",
"myPracticeSessions",
"startPracticeSession",
"announcements",
"announcementDetail",
"myLessonPlans",
"lessonPlanDetail",
"myCoursePlans",
"coursePlanDetail",
"myReportCard",
"myDiagnosticReports",
"myMasterySummary",
]);
/** 变更操作名集合 */
@@ -571,6 +893,16 @@ export const fixtures = {
studentGrowth: studentGrowthFixture,
assignmentAnalysis: assignmentAnalysisFixture,
myProfile: myProfileFixture,
errorBook: errorBookFixture,
leaveRequests: leaveRequestsFixture,
electiveCourses: electiveCoursesFixture,
practiceSessions: practiceSessionsFixture,
announcements: announcementsFixture,
lessonPlans: lessonPlansFixture,
coursePlans: coursePlansFixture,
reportCard: reportCardFixture,
diagnosticReports: diagnosticReportsFixture,
masterySummary: masterySummaryFixture,
};
/** 导出工具函数供测试使用 */