feat(exams,homework,parent): V3 审计深度修复 — 批量批改/考试分析/提交反馈/家长视图/移动端优化
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 架构文档
This commit is contained in:
80
src/instrumentation.ts
Normal file
80
src/instrumentation.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user