15 KiB
个人信息配置和设置模块审计报告 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(v2,12 项已全部完成) 架构图参考: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 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 L6 | import { getTeacherClasses, getTeacherTeachingSubjects } from "@/modules/classes/data-access" |
同上 |
| 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 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 | 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 | 529 行 | 同上 |
原因:单个组件文件混合了 Provider 列表选择、表单编辑、测试、保存、删除 5 个交互流程,以及 visibility/isDefault 表单字段。
后果:可读性下降,维护困难。
建议:将 Provider 选择器和 keyStatus 显示拆分为 AiProviderSelector,表单主体保留在主组件,删除确认 Dialog 拆为 AiProviderDeleteDialog。
2.5 AdminSettingsView 未拆分为子组件(P1)
| 位置 | 问题 | 违反规则 |
|---|---|---|
| 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 L19-21 | metadata = { title: "Settings" } 硬编码英文 |
"所有用户可见文本必须适配 i18n(使用 next-intl),提取翻译键" |
原因:使用静态 metadata 导出而非 generateMetadata + getTranslations。
后果:中文环境下浏览器标签页显示英文 "Settings"。
2.7 i18n 命名空间不一致(P1)
| 位置 | 问题 | 违反规则 |
|---|---|---|
| avatar-upload.tsx L40 | useTranslations("settings.profile.avatar") |
命名空间为 settings.profile.avatar |
| profile/page.tsx L22 | getTranslations("settings.profilePage") |
命名空间为 settings.profilePage |
| 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 Boundary(P2)
| 位置 | 问题 | 违反规则 |
|---|---|---|
| profile/page.tsx L53-57 | <AvatarUpload> 直接渲染,无 Error Boundary 包裹 |
"每个独立的数据区块必须用 React Error Boundary 包裹" |
原因:仅学生/教师概览区块包裹了 Error Boundary,AvatarUpload 区块遗漏。
后果:头像上传失败(网络异常/文件服务不可用)会导致整页崩溃。
2.10 settings-view.tsx 直接 import next-auth/react signOut(P2)
| 位置 | 问题 | 违反规则 |
|---|---|---|
| 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(紧急,影响架构合规)
- 消除 Profile 概览组件跨模块 data-access 直调:将
ProfileStudentOverview/ProfileTeacherOverview改为通过 props 接收数据,由页面层(app 层)编排 classes/homework data-access 并注入。同时将 dashboard 组件引用改为通过 children/props 传入或抽取为 shared 组件。 - 消除 profile/page.tsx 角色硬编码:将
roles.includes("student")/roles.includes("teacher")改为配置驱动或权限点判断。
P1(重要,影响可维护性)
- 拆分 SecurityCenterCard:645 行 → 拆分为
SecurityTwoFactorSection+SecurityRecentLoginsSection,主组件负责数据加载和组合。 - 拆分 ai-provider-settings-card.tsx:529 行 → 拆分 Provider 选择器和删除确认 Dialog。
- 拆分 AdminSettingsView:4 个 Card 拆分为独立子组件。
- settings/page.tsx metadata i18n 化:改为
generateMetadata+getTranslations。 - 统一 i18n 命名空间:将
settings.profilePage.*合并到settings.profile.*,AvatarUpload 保持settings.profile.avatar.*。
P2(优化,提升质量)
- 添加 toSettingItem 单元测试。
- profile 页 AvatarUpload 包裹 Error Boundary。
- 封装 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 完成后应满足:
npm run lint零错误(warnings 可接受)npx tsc --noEmit零错误- Profile 概览组件不直接 import classes/homework/dashboard 模块
- profile/page.tsx 无
roles.includes()硬编码 - SecurityCenterCard 拆分后主文件 ≤ 300 行
- ai-provider-settings-card.tsx 拆分后 ≤ 400 行
- AdminSettingsView 拆分为 4 个子 Card 组件
- settings/page.tsx 使用 generateMetadata
- i18n 命名空间统一为
settings.profile.* - toSettingItem 有单元测试
- AvatarUpload 被 Error Boundary 包裹
- 架构图 004/005 已同步更新