feat(teacher-portal): 完成参考项目差距闭环 P3-P7 全量实现

- P3 考试/作业/成绩 mutation + 详情页 + 批改界面 + 乐观更新 + 多 Tab 同步
- P4 知识图谱 SVG 可视化 + 学情分析仪表盘 + parent-portal Remote
- P5 WebSocket 通知中心 + AI 出题(SSE) + AI 教案 + AI 学情报告
- P6 可观测性硬化:Sentry + WebVitals + OTel + A11y + 性能配置 + Cookie 迁移
- P7 参考项目差距闭环:新增 35 个页面覆盖 13 个缺失模块
  - attendance(考勤 4 页)/questions(题库)/textbooks(教材 2 页)
  - classes/[id] 详情 + classes/schedule 课表
  - course-plans(2 页)/diagnostic(2 页)/error-book/practice
  - exams/[id]/build 组卷 + exams/[id]/analytics 考后分析
  - exams/[id]/edit-rich 富文本编辑 + exams/[id]/proctoring 监考
  - grades/entry 批量录入 + grades/stats 统计 + grades/analytics 分析 + grades/report-card 报告卡
  - homework/submissions 列表 + assignments/[id]/submissions 批量批改
  - homework/submissions/[submissionId] 单份批改 + scan-grading 扫描批改
  - lesson-plans 编辑器 + library + calendar + heatmap 5 页
  - elective 选修课 3 页 /leave 请假 /schedule-changes 调课
- P7 基础设施:61 GraphQL operations + 5 handlers + 13 fixtures + 11 viewports
- 集成 browser.ts/server.ts 注册所有 p7 handlers(fallthrough 顺序)
- viewports.ts 扩展 11 个新导航项
- 验证:tsc --noEmit 零错误 + eslint 零错误
- 文档:workline.md 新增 §5 P7 参考项目差距闭环(含完整文件清单)
This commit is contained in:
SpecialX
2026-07-13 14:27:04 +08:00
parent f13ca612e6
commit d49d211425
117 changed files with 30867 additions and 108 deletions

View File

@@ -0,0 +1,90 @@
"use client";
/**
* 可观测性 Provider 组件P6 硬化)
*
* 整合 Sentry + Web Vitals + OTel 初始化,在应用启动时调用各 init 函数。
* - "use client" 客户端组件
* - 在 useEffect 中按需初始化各可观测性模块(动态 import 避免未安装包问题)
* - 不渲染任何可见 UI返回 children
* - 条件初始化:根据环境变量决定是否启用各模块
*
* 关联02-architecture-design.md §12 可观测性 / project_rules §12 可观测性规范
*/
import { useEffect, type ReactNode } from "react";
/**
* 可观测性 Provider。
*
* 在客户端挂载时初始化 Sentry / Web Vitals / OTel。
* 所有初始化均为条件性:未配置对应环境变量时跳过,未安装包时降级。
*
* 不渲染任何可见 UI仅透传 children。
*/
export function ObservabilityProvider({
children,
}: {
children: ReactNode;
}): ReactNode {
useEffect(() => {
// 并行初始化各可观测性模块(互不依赖)
const initObservability = async (): Promise<void> => {
// 1. Sentry 错误追踪条件NEXT_PUBLIC_SENTRY_DSN 配置时)
try {
const { initSentry } = await import(
"@/lib/observability/sentry"
);
await initSentry();
} catch (err) {
if (typeof console !== "undefined") {
console.warn("[teacher-portal] Sentry 初始化失败", err);
}
}
// 2. Web Vitals RUM 采集(生产环境启用)
if (process.env.NODE_ENV === "production") {
try {
const { initWebVitals } = await import(
"@/lib/observability/web-vitals"
);
await initWebVitals();
} catch (err) {
if (typeof console !== "undefined") {
console.warn("[teacher-portal] Web Vitals 初始化失败", err);
}
}
}
// 3. OTel browser SDK条件NEXT_PUBLIC_OTEL_ENABLED=true 时)
try {
const { initOTel } = await import("@/lib/observability/otel");
await initOTel();
} catch (err) {
if (typeof console !== "undefined") {
console.warn("[teacher-portal] OTel 初始化失败", err);
}
}
// 4. Cookie 迁移条件NEXT_PUBLIC_COOKIE_MIGRATION_ENABLED=true 时)
// 实际迁移在 iam refresh cookie 端点就绪后启用,此处仅做准备
try {
const { migrateTokenToCookie } = await import(
"@/lib/observability/cookie-migration"
);
// 后台执行迁移,不阻塞应用渲染
void migrateTokenToCookie();
} catch {
// 迁移失败不影响应用功能,静默忽略
}
};
// 后台初始化,不阻塞渲染
void initObservability();
}, []);
// 不渲染任何可见 UI仅透传 children
return children;
}
export default ObservabilityProvider;