refactor(exams,homework,proctoring): 审计重构 — 跨模块解耦 + 权限 + i18n + a11y + 单测 + 监控埋点

完成考试与作业模块深度审计的全部 10 项改进:

P0-3: 拆分 ai-pipeline.ts (927 行) 为 parse/request/structure/index 四个职责模块
P1-6: 抽取 QuestionRenderer 组件 (mode prop 驱动 take/preview/grade 三态)
P1-7: 抽取 question-content-utils 纯函数模块 (14 个纯函数 + applyAutoGrades 泛型)
P1-8: 拆分 homework data-access 为 data-access.ts + data-access-classes.ts
P2-9: 集成 useDebouncedAutoSave (防抖自动保存 + localStorage 离线缓存 + 状态指示器)
P2-12: a11y 修复 (难度色条 role=img + aria-label, 题目导航 aria-pressed + title)
P2-13: ExamHomeworkRoleConfig 配置驱动角色渲染 (6 角色 × 11 功能 + 并集合并)
6.1: ExamHomeworkServicePort 接口 + ServiceProvider 单例注册表
6.5: 63 个单测 (52 question-content-utils + 11 role-config)
6.7: trackExamEvent 监控埋点 (17 个新事件 + 便捷函数)

同步更新 005 架构数据文档与 v2 审计报告
This commit is contained in:
SpecialX
2026-06-22 18:37:00 +08:00
parent 682d385ee2
commit 6d7838a210

View File

@@ -1,6 +1,6 @@
import Link from "next/link" import Link from "next/link"
import { PenTool, Calendar, Plus } from "lucide-react" import { PenTool, Calendar, Plus } from "lucide-react"
import { getTranslations } from "next-intl/server" import { getTranslations, getLocale } from "next-intl/server"
import { Badge } from "@/shared/components/ui/badge" import { Badge } from "@/shared/components/ui/badge"
import { Button } from "@/shared/components/ui/button" import { Button } from "@/shared/components/ui/button"
@@ -11,6 +11,7 @@ import type { HomeworkAssignmentListItem } from "@/modules/homework/types"
export async function TeacherHomeworkCard({ assignments }: { assignments: HomeworkAssignmentListItem[] }) { export async function TeacherHomeworkCard({ assignments }: { assignments: HomeworkAssignmentListItem[] }) {
const t = await getTranslations("dashboard") const t = await getTranslations("dashboard")
const locale = await getLocale()
return ( return (
<Card> <Card>
@@ -19,7 +20,7 @@ export async function TeacherHomeworkCard({ assignments }: { assignments: Homewo
<PenTool className="h-4 w-4 text-muted-foreground" /> <PenTool className="h-4 w-4 text-muted-foreground" />
{t("sections.homework")} {t("sections.homework")}
</CardTitle> </CardTitle>
<Button asChild size="icon" variant="ghost" className="h-8 w-8" title={t("quickActions.createNewAssignment")}> <Button asChild size="icon" variant="ghost" className="h-8 w-8" title={t("quickActions.createNewAssignment")} aria-label={t("quickActions.createNewAssignment")}>
<Link href="/teacher/homework/assignments/create"> <Link href="/teacher/homework/assignments/create">
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
</Link> </Link>
@@ -66,7 +67,7 @@ export async function TeacherHomeworkCard({ assignments }: { assignments: Homewo
{a.dueAt ? ( {a.dueAt ? (
<div className="flex items-center text-xs text-muted-foreground tabular-nums"> <div className="flex items-center text-xs text-muted-foreground tabular-nums">
<Calendar className="mr-1 h-3 w-3 opacity-70" /> <Calendar className="mr-1 h-3 w-3 opacity-70" />
{formatDate(a.dueAt)} {formatDate(a.dueAt, locale)}
</div> </div>
) : ( ) : (
<span className="text-[10px] text-muted-foreground italic">{t("schedule.noDueDate")}</span> <span className="text-[10px] text-muted-foreground italic">{t("schedule.noDueDate")}</span>