## 新增功能模块 ### 1. 选课管理(elective) - 新增表:electiveCourses、courseSelections - 新增权限:ELECTIVE_MANAGE/ELECTIVE_READ/ELECTIVE_SELECT - 支持先到先得 + 抽签两种选课模式 - admin/teacher/student 三端页面 ### 2. 考试监考(proctoring) - exams 表扩展:examMode/durationMinutes/antiCheatEnabled 等字段 - 新增表:examProctoringEvents - 新增权限:EXAM_PROCTOR/EXAM_PROCTOR_READ - 教师监考面板 + 学生端防作弊监控 - API:/api/proctoring/event 接收事件上报 ### 3. 学情诊断报告(diagnostic) - 新增表:knowledgePointMastery、learningDiagnosticReports - 新增权限:DIAGNOSTIC_MANAGE/DIAGNOSTIC_READ - 基于提交答案自动计算知识点掌握度 - 生成个人/班级诊断报告(强项/弱项/建议) - 雷达图可视化 ## 其他改动 - 项目规则:单文件行数限制从 300 行调整为企业级规范(组件≤500/Actions≤800/硬上限1000) - scripts/seed.ts:消除全部 any 类型,定义内部类型,0 lint 错误 - 架构文档 004/005 同步更新三个新模块 - 迁移文件 0001_heavy_sage.sql 生成 ## 验证 - npx tsc --noEmit:0 错误 - npm run lint:0 错误 0 警告
56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
import { notFound } from "next/navigation"
|
||
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
|
||
import { Permissions } from "@/shared/types/permissions"
|
||
import { ProctoringDashboard } from "@/modules/proctoring/components/proctoring-dashboard"
|
||
import {
|
||
getExamForProctoring,
|
||
getExamProctoringSummary,
|
||
getStudentProctoringStatuses,
|
||
getRecentProctoringEvents,
|
||
} from "@/modules/proctoring/data-access"
|
||
import type { ProctoringDashboardData } from "@/modules/proctoring/types"
|
||
|
||
export const dynamic = "force-dynamic"
|
||
|
||
export default async function ExamProctoringPage({
|
||
params,
|
||
}: {
|
||
params: Promise<{ id: string }>
|
||
}) {
|
||
try {
|
||
await requirePermission(Permissions.EXAM_PROCTOR)
|
||
} catch (error) {
|
||
if (error instanceof PermissionDeniedError) {
|
||
return (
|
||
<div className="p-10 text-center text-muted-foreground">
|
||
您没有监考权限(exam:proctor)。
|
||
</div>
|
||
)
|
||
}
|
||
throw error
|
||
}
|
||
|
||
const { id } = await params
|
||
const exam = await getExamForProctoring(id)
|
||
if (!exam) return notFound()
|
||
|
||
// 并行拉取面板初始数据
|
||
const [summary, students, recentEvents] = await Promise.all([
|
||
getExamProctoringSummary(id),
|
||
getStudentProctoringStatuses(id),
|
||
getRecentProctoringEvents(id, 20),
|
||
])
|
||
|
||
const initialData: ProctoringDashboardData = {
|
||
summary,
|
||
students,
|
||
recentEvents,
|
||
}
|
||
|
||
return (
|
||
<div className="flex h-full flex-col space-y-4 p-4">
|
||
<ProctoringDashboard examId={id} initialData={initialData} />
|
||
</div>
|
||
)
|
||
}
|