Files
NextEdu/docs/architecture/audit/archive/settings-profile-audit-report-v3.md

252 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 个人信息配置和设置模块审计报告 v3
> 审查日期2026-06-22
> 审查范围:`src/modules/settings/**`、`src/app/(dashboard)/settings/**`、`src/app/(dashboard)/admin/settings/**`、`src/app/(dashboard)/profile/**`
> 上一版本:`settings-profile-audit-report-v2.md`v212 项已全部完成)
> 架构图参考:`docs/architecture/004_architecture_impact_map.md` §2.23、`docs/architecture/005_architecture_data.json`
---
## 一、现有实现概要
### 1.1 文件分布
| 层 | 路径 | 文件数 | 说明 |
|----|------|--------|------|
| 路由层 - 通用设置 | `src/app/(dashboard)/settings/` | `page.tsx` + `error.tsx` + `loading.tsx` | 角色分发到 SettingsView通过 SettingsServiceProvider 注入服务 |
| 路由层 - 管理员系统设置 | `src/app/(dashboard)/admin/settings/` | `page.tsx` | 仅 admin 可访问,渲染 AdminSettingsView |
| 路由层 - 安全设置 | `src/app/(dashboard)/settings/security/` | `page.tsx` + `error.tsx` + `loading.tsx` | 独立密码修改页 |
| 路由层 - 个人资料 | `src/app/(dashboard)/profile/` | `page.tsx` + `error.tsx` + `loading.tsx` | 个人资料展示页159 行) |
| 模块层 - actions | 7 个文件 | 详见下表 | 全部使用 `requirePermission()` |
| 模块层 - data-access | 3 个文件 | 详见下表 | 全部 `server-only` |
| 模块层 - types | `types.ts`75 行) | 1 | AiProvider 类型 + SettingsService 接口 |
| 模块层 - 组件 | 12 个组件 | 详见下表 | |
| 模块层 - lib | 3 个纯函数文件 + 3 个测试 | | |
| 模块层 - config | `role-settings-config.tsx`84 行) | 1 | 配置驱动角色路由 |
| i18n | `zh-CN/settings.json` + `en/settings.json` | 2 | 完整翻译 |
### 1.2 v1/v2 完成情况回顾
v1 报告 13 项 + v2 报告 12 项改进建议已全部完成:
- ✅ i18n 命名空间创建settings.json 中英文)
- ✅ SettingsService 接口 + Context 注入(消除跨模块 action 直调)
- ✅ AdminSettingsView 接入真实数据层system_settings 表)
- ✅ 配置驱动角色路由ROLE_SETTINGS_CONFIG
- ✅ 分区 Error Boundary + Suspense 骨架屏
- ✅ Profile 页面拆分ProfileStudentOverview / ProfileTeacherOverview
- ✅ 头像上传 + 旧文件清理
- ✅ 2FA 完整 TOTP 实现(非占位)
- ✅ 通知测试按钮接入真实 dispatcher
- ✅ 会话远程登出
- ✅ AdminSettingsView dirty 检测
- ✅ 通知偏好表单 dirty 检测
- ✅ 单元测试totp / student-overview-data / security-utils
### 1.3 架构图记录情况
`004_architecture_impact_map.md` §2.23 和 `005_architecture_data.json` 的 settings 节点记录完整,包含所有 actions / data-access / components / config / lib / types 的导出、依赖关系和已知问题状态。架构图与实际代码基本一致。
---
## 二、现存问题与原因分析
### 2.1 ProfileStudentOverview / ProfileTeacherOverview 跨模块 data-access 直调P0
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [profile-student-overview.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/profile-student-overview.tsx) L8-9 | `import { getStudentClasses, getStudentSchedule } from "@/modules/classes/data-access"` / `import { getStudentDashboardGrades, getStudentHomeworkAssignments } from "@/modules/homework/data-access"` | "模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access只能通过注入的接口调用" |
| [profile-teacher-overview.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/profile-teacher-overview.tsx) L6 | `import { getTeacherClasses, getTeacherTeachingSubjects } from "@/modules/classes/data-access"` | 同上 |
| [profile-student-overview.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/profile-student-overview.tsx) L4-7 | `import { StudentGradesCard } ... from "@/modules/dashboard/components/student-dashboard/*"` | 组件层跨模块直接 import dashboard 组件,耦合度高 |
**原因**Profile 概览组件作为 Server Component 直接编排 classes/homework/dashboard 模块的数据获取和组件渲染,未通过接口抽象。
**后果**classes/homework/dashboard 模块的 data-access 签名变更会直接破坏 settings 组件settings 模块无法独立测试mock classes/homework data-access 困难);无法在不修改 settings 组件的前提下替换数据源。
### 2.2 profile/page.tsx 角色硬编码P0
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [profile/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/profile/page.tsx) L37 | `const isStudent = roles.includes("student")` | "前端权限判断统一使用 usePermission().hasPermission(),严禁出现 role === 'xxx' 硬编码" |
| 同文件 L38 | `const isTeacher = roles.includes("teacher")` | 同上 |
**原因**Profile 页面通过 `roles.includes()` 判断角色来决定渲染学生/教师概览区块,未使用权限点或配置驱动。
**后果**:新增角色(如 grade_head需修改页面代码角色与概览区块的映射关系不可配置违反项目硬编码禁令。
### 2.3 SecurityCenterCard 超出组件行数上限P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [security-center-card.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/security-center-card.tsx) | 645 行 | "React 组件:建议 ≤ 500 行(复杂表单/大型表格可放宽至 800 行)" |
**原因**:单个组件文件混合了 2FA 启用流程、2FA 关闭流程、备份码重新生成流程、最近登录历史列表、远程登出 5 个独立交互区块,以及 3 个 Dialog 的状态管理和 JSX。
**后果**:可读性下降,维护困难;难以独立测试各交互区块;修改一个流程容易影响其他流程。
**建议**:拆分为 `SecurityTwoFactorSection`2FA 启用/关闭/备份码)、`SecurityRecentLoginsSection`(登录历史 + 远程登出),主组件仅负责数据加载和组合。
### 2.4 ai-provider-settings-card.tsx 超出组件行数上限P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [ai-provider-settings-card.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/ai-provider-settings-card.tsx) | 529 行 | 同上 |
**原因**:单个组件文件混合了 Provider 列表选择、表单编辑、测试、保存、删除 5 个交互流程,以及 visibility/isDefault 表单字段。
**后果**:可读性下降,维护困难。
**建议**:将 Provider 选择器和 keyStatus 显示拆分为 `AiProviderSelector`,表单主体保留在主组件,删除确认 Dialog 拆为 `AiProviderDeleteDialog`
### 2.5 AdminSettingsView 未拆分为子组件P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [admin-settings-view.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/admin-settings-view.tsx) | 444 行4 个 Card 内联在单文件中 | v2 报告建议拆分为 `SchoolInfoCard` / `SecurityPolicyCard` / `FileUploadCard` / `NotificationConfigCard` |
**原因**v2 报告已建议拆分但未实施。
**后果**4 个 Card 的表单字段和状态更新逻辑混合在主组件中,难以独立测试和复用。
### 2.6 settings/page.tsx metadata 硬编码英文P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [settings/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/settings/page.tsx) L19-21 | `metadata = { title: "Settings" }` 硬编码英文 | "所有用户可见文本必须适配 i18n使用 next-intl提取翻译键" |
**原因**:使用静态 `metadata` 导出而非 `generateMetadata` + `getTranslations`
**后果**:中文环境下浏览器标签页显示英文 "Settings"。
### 2.7 i18n 命名空间不一致P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [avatar-upload.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/avatar-upload.tsx) L40 | `useTranslations("settings.profile.avatar")` | 命名空间为 `settings.profile.avatar` |
| [profile/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/profile/page.tsx) L22 | `getTranslations("settings.profilePage")` | 命名空间为 `settings.profilePage` |
| [profile-settings-form.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/profile-settings-form.tsx) L39 | `useTranslations("settings.profile")` | 命名空间为 `settings.profile` |
**原因**:三处使用了三种不同的 i18n 命名空间根(`settings.profile` / `settings.profilePage` / `settings.profile.avatar`v2 报告已指出但未统一。
**后果**i18n 命名空间结构混乱,维护时易混淆;翻译键分散在多个命名空间下。
**建议**:统一为 `settings.profile.*`AvatarUpload 改为 `settings.profile.avatar.*`profile/page.tsx 改为 `settings.profile.*`),将 `profilePage` 命名空间下的键合并到 `profile` 下。
### 2.8 缺少 toSettingItem 单元测试P2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| `src/modules/settings/actions-system-settings.ts` L58-75 | `toSettingItem` 纯函数无单元测试 | v2 报告建议添加 |
**原因**v2 建议未实施。
**后果**值类型转换逻辑string/number/boolean/json无回归保障。
### 2.9 profile 页 AvatarUpload 未包裹 Error BoundaryP2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [profile/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/profile/page.tsx) L53-57 | `<AvatarUpload>` 直接渲染,无 Error Boundary 包裹 | "每个独立的数据区块必须用 React Error Boundary 包裹" |
**原因**:仅学生/教师概览区块包裹了 Error BoundaryAvatarUpload 区块遗漏。
**后果**:头像上传失败(网络异常/文件服务不可用)会导致整页崩溃。
### 2.10 settings-view.tsx 直接 import next-auth/react signOutP2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [settings-view.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/settings-view.tsx) L8 | `import { signOut } from "next-auth/react"` | 模块内部组件直接耦合认证实现 |
**原因**:登出按钮直接调用 next-auth 的客户端 signOut。
**后果**settings 模块耦合 next-auth 实现;如未来更换认证方案需修改 settings 组件。
**建议**:将 `signOut` 调用封装为 settings 模块自身的 action 或通过 props 注入。
---
## 三、行业差距对比
### 3.1 与优秀 K12 产品的差距
| 维度 | 优秀实践Google Classroom / PowerSchool / Veracross | 当前状态 | 差距影响 |
|------|--------------------------------------------------------|----------|----------|
| **设置信息架构** | 统一入口,按角色动态显示分组,支持搜索 | ✅ 已统一入口,配置驱动角色路由 | 已达标 |
| **个人资料** | 头像上传 + 字段级权限可见性 | ✅ 头像上传已实现;字段级权限可见性未实现 | 学生可能看到不该看的字段(如自己的手机号由家长管理) |
| **安全中心** | 2FA、会话列表、登录历史、密码泄露检测 | ✅ 2FA TOTP + 会话登出 + 登录历史 | 已达标密码泄露检测HaveIBeenPwned未集成 |
| **通知偏好** | 按事件类型细分,支持渠道矩阵 + 免打扰 + 测试 | ✅ 全部已实现 | 已达标 |
| **主题/语言** | 主题切换 + 语言切换同页 | ✅ 已集成 | 已达标 |
| **AI 配置** | 多 Provider + 测试 + 用量统计 | ✅ 多 Provider + 测试,无用量统计 | 教育机构无法监控 AI 成本(中长期计划) |
| **空状态/骨架屏** | 每个数据区块独立骨架屏 + 空状态 | ✅ 已实现分区 Suspense + 骨架屏 | 已达标 |
| **设置搜索** | 设置项较多时支持快速搜索 | ❌ 未实现 | 设置项目前 4 个标签页,数量尚可,中长期可考虑 |
### 3.2 多角色使用习惯
| 角色 | 优秀实践 | 当前状态 |
|------|----------|----------|
| **admin** | 系统设置与个人设置在同一入口的不同分组 | ✅ `/settings` 个人设置 + `/admin/settings` 系统设置分离 |
| **teacher** | 设置页可快速跳转常用教学功能 | ✅ 有 QuickLinksCard |
| **parent** | 设置页可切换查看不同孩子的通知偏好 | ❌ 仅一套偏好,无法按孩子细分(中长期计划) |
| **student** | 设置页简洁,无系统配置 | ✅ 简洁 |
---
## 四、改进优先级建议
### P0紧急影响架构合规
1. **消除 Profile 概览组件跨模块 data-access 直调**:将 `ProfileStudentOverview` / `ProfileTeacherOverview` 改为通过 props 接收数据由页面层app 层)编排 classes/homework data-access 并注入。同时将 dashboard 组件引用改为通过 children/props 传入或抽取为 shared 组件。
2. **消除 profile/page.tsx 角色硬编码**:将 `roles.includes("student")` / `roles.includes("teacher")` 改为配置驱动或权限点判断。
### P1重要影响可维护性
3. **拆分 SecurityCenterCard**645 行 → 拆分为 `SecurityTwoFactorSection` + `SecurityRecentLoginsSection`,主组件负责数据加载和组合。
4. **拆分 ai-provider-settings-card.tsx**529 行 → 拆分 Provider 选择器和删除确认 Dialog。
5. **拆分 AdminSettingsView**4 个 Card 拆分为独立子组件。
6. **settings/page.tsx metadata i18n 化**:改为 `generateMetadata` + `getTranslations`
7. **统一 i18n 命名空间**:将 `settings.profilePage.*` 合并到 `settings.profile.*`AvatarUpload 保持 `settings.profile.avatar.*`
### P2优化提升质量
8. **添加 toSettingItem 单元测试**
9. **profile 页 AvatarUpload 包裹 Error Boundary**
10. **封装 signOut 调用**:通过 props 或 action 注入,解耦 next-auth。
---
## 五、架构图同步说明
本次审计发现架构图需补充/修改以下节点:
### 5.1 `004_architecture_impact_map.md` §2.23
- **修改"已知问题"**:新增 v3 发现的 3 项 P0/P1 问题Profile 概览跨模块 data-access 直调 / profile 角色硬编码 / SecurityCenterCard 超行数上限)
- **更新"文件清单"**新增拆分后的子组件SecurityTwoFactorSection / SecurityRecentLoginsSection / AiProviderSelector / SchoolInfoCard / SecurityPolicyCard / FileUploadCard / NotificationConfigCard
- **更新行数**SecurityCenterCard 拆分后行数变化
### 5.2 `005_architecture_data.json` settings 节点
- **`modules.settings.knownIssues`**:新增 v3 问题状态
- **`modules.settings.exports.components`**:新增拆分后的子组件
- **`dependencyMatrix`**settings → classes/homework/dashboard 的依赖类型标注为"组件层直调(待修复)"
---
## 六、验收标准
v3 完成后应满足:
1. `npm run lint` 零错误warnings 可接受)
2. `npx tsc --noEmit` 零错误
3. Profile 概览组件不直接 import classes/homework/dashboard 模块
4. profile/page.tsx 无 `roles.includes()` 硬编码
5. SecurityCenterCard 拆分后主文件 ≤ 300 行
6. ai-provider-settings-card.tsx 拆分后 ≤ 400 行
7. AdminSettingsView 拆分为 4 个子 Card 组件
8. settings/page.tsx 使用 generateMetadata
9. i18n 命名空间统一为 `settings.profile.*`
10. toSettingItem 有单元测试
11. AvatarUpload 被 Error Boundary 包裹
12. 架构图 004/005 已同步更新