V3-5: exam-actions.tsx 集成 useExamHomeworkFeatures hook,按角色控制菜单项可见性 V3-7: 批量批改 — 新增 batchAutoGradeSubmissions data-access + Server Action + HomeworkBatchGradingView 组件 V3-8: 考试分析仪表盘 — 新增 getExamAnalytics stats-service + ExamAnalyticsDashboard 组件 + /teacher/exams/[id]/analytics 路由 V3-9: 提交后即时反馈页 — 新增 HomeworkSubmissionResult 组件 + /student/learning/assignments/[id]/result 路由 V3-11: 家长考试详情 — 新增 ChildExamDetail 组件 + getStudentExamResults data-access + child-detail-panel exams Tab V3-12: 移动端触控优化 — 题目导航与考试操作按钮 44px 最小触控目标 修复: instrumentation.ts 适配器补全 questionCount/averageScore/overdueCount 字段 修复: exam-homework-port.ts 类型导入对齐 ExamWithQuestionsForHomework 修复: trend-line-chart.tsx 数据类型允许 undefined(classAverage 可选场景) 同步更新 004/005 架构文档
81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
/**
|
||
* Next.js Instrumentation 钩子
|
||
*
|
||
* 在应用启动时执行一次性初始化操作。
|
||
* 文档:https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
|
||
*
|
||
* V3-3: 注册 ExamHomeworkServicePort 实现
|
||
* 将 modules 层的 data-access 函数注入到 shared 层的 ServicePort,
|
||
* 使 app 层可以通过 EXAM_HOMEWORK_SERVICE_PROVIDER.get() 调用,
|
||
* 而不直接依赖 modules 内部实现。
|
||
*/
|
||
|
||
import { registerExamHomeworkService } from "@/shared/services/exam-homework-port"
|
||
import { getExamById, getExams, getExamCreatorId, getExamTitleById } from "@/modules/exams/data-access"
|
||
import {
|
||
getHomeworkAssignmentById,
|
||
getHomeworkAssignments,
|
||
getAssignmentMaxScoreById,
|
||
} from "@/modules/homework/data-access"
|
||
import { getExamWithQuestionsForHomework } from "@/modules/exams/data-access"
|
||
import type { DataScope } from "@/shared/types/permissions"
|
||
import type { Exam } from "@/modules/exams/types"
|
||
import type { HomeworkAssignmentListItem } from "@/modules/homework/types"
|
||
|
||
/**
|
||
* 适配器:将 getExamById 的返回值补全为 Exam 类型
|
||
* data-access 返回的对象包含 questions 数组但缺少 questionCount 字段
|
||
*/
|
||
const adaptExam = (raw: Awaited<ReturnType<typeof getExamById>>): Exam | null => {
|
||
if (!raw) return null
|
||
return {
|
||
...raw,
|
||
questionCount: raw.questions?.length ?? 0,
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 适配器:将 getHomeworkAssignmentById 的返回值补全为 HomeworkAssignmentListItem 类型
|
||
* data-access 返回的对象缺少 averageScore 和 overdueCount 字段
|
||
*/
|
||
const adaptAssignment = (raw: Awaited<ReturnType<typeof getHomeworkAssignmentById>>): HomeworkAssignmentListItem | null => {
|
||
if (!raw) return null
|
||
return {
|
||
id: raw.id,
|
||
sourceExamId: raw.sourceExamId,
|
||
sourceExamTitle: raw.sourceExamTitle,
|
||
title: raw.title,
|
||
status: raw.status,
|
||
availableAt: raw.availableAt,
|
||
dueAt: raw.dueAt,
|
||
allowLate: raw.allowLate,
|
||
lateDueAt: raw.lateDueAt,
|
||
maxAttempts: raw.maxAttempts,
|
||
createdAt: raw.createdAt,
|
||
updatedAt: raw.updatedAt,
|
||
targetCount: raw.targetCount,
|
||
submittedCount: raw.submittedCount,
|
||
gradedCount: raw.gradedCount,
|
||
averageScore: null,
|
||
overdueCount: 0,
|
||
}
|
||
}
|
||
|
||
export async function register(): Promise<void> {
|
||
registerExamHomeworkService({
|
||
// 考试
|
||
getExamById: async (id: string, scope?: DataScope) => adaptExam(await getExamById(id, scope)),
|
||
getExams,
|
||
getExamCreatorId,
|
||
getExamTitleById,
|
||
|
||
// 作业
|
||
getHomeworkAssignmentById: async (id: string, scope?: DataScope) => adaptAssignment(await getHomeworkAssignmentById(id, scope)),
|
||
getHomeworkAssignments,
|
||
getAssignmentMaxScoreByIds: getAssignmentMaxScoreById,
|
||
|
||
// 跨模块
|
||
getExamWithQuestionsForHomework,
|
||
})
|
||
}
|