Files
NextEdu/docs/architecture/audit/settings-profile-audit-report-v2.md
SpecialX 27db170c0a docs: update architecture docs, audit reports, and bug tracking
- Update architecture impact map, data, feature checklist, gap audit

- Add audit reports for dashboard, exam-homework, grades-diagnostic, settings-profile, textbooks

- Update bug reports (admin, teacher, lesson-preparation, others, shared)

- Update coding standards, DR plan, design docs, and README
2026-06-23 17:36:18 +08:00

13 KiB
Raw Blame History

设置和个人信息模块审计报告 v2

审查日期2026-06-22 审查范围:src/modules/settings/**src/app/(dashboard)/settings/**src/app/(dashboard)/admin/settings/**src/app/(dashboard)/profile/** 上一版本:settings-profile-audit-report.mdv1P0/P1/P2 共 13 项已全部完成) 架构图参考:docs/architecture/004_architecture_impact_map.md §2.23、docs/architecture/005_architecture_data.json


一、v1 完成情况回顾

v1 报告中的 13 项改进建议已全部完成:

编号 优先级 标题 状态
P0-1 P0 创建 settings i18n 命名空间 已完成
P0-2 P0 消除跨模块 action 直调SettingsService 接口) 已完成
P0-3 P0 AdminSettingsView 接入真实数据层 已完成(新增 system_settings 表 + data-access + actions
P1-4 P1 配置驱动角色路由 已完成
P1-5 P1 分区 Error Boundary + Suspense 已完成
P1-6 P1 Profile 页面拆分 已完成
P1-7 P1 移除 as 断言 已完成
P2-8 P2 头像上传 已完成AvatarUpload + actions-avatar
P2-9 P2 2FA / 会话管理 已完成SecurityCenterCard + actions-security
P2-10 P2 通知测试按钮 已完成sendTestNotificationAction
P2-11 P2 语言切换集成 已完成ThemePreferencesCard 集成 LocaleSwitcher
P2-12 P2 埋点接口 已完成SettingsService.trackEvent 预留)
P2-13 P2 a11y 修复 已完成

二、v2 新发现的问题

2.1 安全中心 2FA 为纯占位实现P0

位置 问题 严重性
actions-security.ts L21-46 toggleTwoFactorAction 仅将 twoFactorEnabled 写入 system_settings 表,未接入 TOTP 密钥绑定、一次性码校验、备份码生成等真实 2FA 流程 P0
security-center-card.tsx L105-120 用户开启 2FA 后立即显示"已启用",但实际登录时不会要求二次验证,造成虚假安全感 P0
同文件 L70 注释 "占位实现,仅记录用户偏好" — 注释承认未接入真实流程 P0

后果:用户以为启用了 2FA 但实际无效;安全合规审计会失败。

建议:在 v2 中要么 (a) 完整实现 TOTP 流程(绑定 authenticator + 验证一次性码 + 备份码),要么 (b) 将开关改为"即将推出"禁用状态,避免误导。

2.2 通知测试按钮为纯占位实现P1

位置 问题 严重性
actions-notifications.ts L29-39 sendTestNotificationActionconsole.info + Promise.resolve(),未调用真实通知发送服务 P1
notification-preferences-form.tsx L119-133 点击测试按钮后总是显示"测试通知已发送",但用户不会收到任何通知 P1

后果:用户以为测试通知已发送但收不到,无法真正验证渠道配置。

建议:接入 notifications/dispatcher.ts 的真实发送逻辑,或暂时将按钮改为禁用状态并标注"功能开发中"。

2.3 头像上传未清理旧文件P1

位置 问题 严重性
actions-avatar.ts L15-34 updateUserAvatarAction 更新 users.image 字段后,旧头像文件仍留在文件存储中,无清理逻辑 P1
avatar-upload.tsx L108-124 handleRemove 调用 removeUserAvatarAction 仅清空 users.image,未删除实际文件 P1

后果:存储成本累积;孤儿文件无法回收。

建议:在 removeUserAvatarActionupdateUserAvatarAction 中,更新数据库前先记录旧 URL更新成功后异步调用 files/data-access.deleteFile 清理旧文件。

2.4 SecurityCenterCard 缺少"登出其他会话"功能P1

位置 问题 严重性
security-center-card.tsx 全文 仅展示登录历史,无法远程登出其他设备的会话 P1
actions-security.ts 全文 revokeSessionAction 或类似 Server Action P1

后果:用户发现可疑登录后无法主动处置,只能修改密码被动应对。

建议:新增 revokeSessionAction(sessionToken: string),删除 sessions 表对应记录UI 在每条登录历史旁显示"登出"按钮(当前会话除外)。

2.5 AdminSettingsView 缺少表单变更检测P1

位置 问题 严重性
admin-settings-view.tsx L107-122 handleSave 无条件保存,即使用户未修改任何字段也会触发 upsert 全部 16 个设置项 P1
同文件 L415 "Reset" 按钮直接 setValues(DEFAULT_VALUES) 而非恢复到加载时的值,会丢失未保存的服务端数据 P1

后果无谓的数据库写入Reset 语义错误。

建议:维护 dirty 状态(JSON.stringify(values) !== JSON.stringify(loadedValues)Save 按钮禁用直到 dirtyReset 恢复到 loadedValues 而非 DEFAULT_VALUES

2.6 i18n 键 settings.profile.avatarprofilePage 命名空间下缺失P2

位置 问题 严重性
profile/page.tsx 使用 <AvatarUpload> 但页面其他文本使用 settings.profilePage.* 命名空间,而 AvatarUpload 内部使用 settings.profile.avatar.*,命名空间不一致 P2

后果i18n 命名空间结构混乱,维护时易混淆。

建议:统一为 settings.profile.avatar.*settings.profilePage.avatar.*,二选一。

2.7 SecurityCenterCard 未传递 currentDeviceLabelP2

位置 问题 严重性
settings-view.tsx L182 <SecurityCenterCard /> 未传递 currentDeviceLabel prop P2
security-center-card.tsx L192-194 isCurrent 判断永远为 false"当前会话"徽章永远不会显示 P2

后果:用户无法在登录历史中识别当前会话。

建议:在 Server Component 层获取 headers().get("user-agent"),通过 props 传递到 SecurityCenterCard

2.8 头像上传未限制文件名长度P2

位置 问题 严重性
avatar-upload.tsx L49-57 validateFile 仅校验类型和大小,未校验文件名长度 P2

后果:超长文件名可能导致数据库 varchar 字段截断或存储错误。

建议:添加 file.name.length > 255 校验。

2.9 通知偏好表单未做 dirty 检测P2

位置 问题 严重性
notification-preferences-form.tsx L98-117 Save 按钮始终可点击,无 dirty 检测 P2

后果:用户误点 Save 触发不必要的 Server Action 调用。

建议:维护 dirty 状态Save 按钮在无变更时禁用。

2.10 AdminSettingsView 文件行数接近上限P2

位置 问题 严重性
admin-settings-view.tsx 425 行,接近 500 行建议上限 P2

后果:可读性下降,维护困难。

建议:将 4 个 Card 拆分为独立子组件(SchoolInfoCard / SecurityPolicyCard / FileUploadCard / NotificationConfigCard),主组件仅负责表单状态和提交逻辑。

2.11 缺少单元测试P2

位置 问题 严重性
src/modules/settings/**/*.test.ts 整个 settings 模块无任何单元测试文件 P2

后果:重构无回归保障;纯函数(toSettingItemparseUserAgentformatRelativeTime)无法独立验证。

建议:为以下纯函数添加单元测试:

  • actions-system-settings.tstoSettingItem(值类型转换)
  • security-center-card.tsxparseUserAgentformatRelativeTime
  • lib/student-overview-data.tsbuildStudentOverviewDatacomputeStudentStats

2.12 2FA 状态查询存在 N+1 问题P2

位置 问题 严重性
actions-security.ts L48-62 getTwoFactorStatus 对每个用户分别查询 3 次 system_settingsenabled / method / enabledAt共 3 次 DB 往返 P2

后果:每次加载安全中心页面额外 3 次 DB 查询。

建议:使用 getSystemSettingsByCategory("security_policy") 一次查询所有 security_policy 分类下的设置,在内存中过滤当前用户的键。


三、改进优先级建议v2

P0紧急影响安全/合规)

  1. 2FA 真实实现或禁用开关:要么完整实现 TOTP 流程,要么将开关改为"即将推出"禁用状态,避免虚假安全感。

P1重要影响功能完整性

  1. 通知测试按钮接入真实发送逻辑:调用 notifications/dispatcher.ts 发送真实通知,或暂时禁用按钮。
  2. 头像上传清理旧文件:在 removeUserAvatarActionupdateUserAvatarAction 中添加旧文件清理逻辑。
  3. 会话远程登出:新增 revokeSessionActionUI 添加"登出"按钮。
  4. AdminSettingsView 表单 dirty 检测Save 按钮在无变更时禁用Reset 恢复到加载值。

P2优化提升质量

  1. 统一 i18n 命名空间settings.profile.avatarsettings.profilePage 二选一。
  2. SecurityCenterCard 传递 currentDeviceLabelServer Component 层获取 user-agent 传入。
  3. 头像上传文件名长度校验:添加 file.name.length > 255 校验。
  4. 通知偏好表单 dirty 检测Save 按钮在无变更时禁用。
  5. AdminSettingsView 拆分子组件4 个 Card 拆分为独立组件。
  6. 添加单元测试:为纯函数添加测试覆盖。
  7. 2FA 状态查询优化:合并 3 次 DB 查询为 1 次。

四、v2 实施计划

4.1 P02FA 真实实现或禁用

方案选择:考虑到完整 TOTP 实现需要额外的库(otplib)和 UIQR 码扫描、备份码展示v2 阶段先将开关改为"即将推出"禁用状态,避免虚假安全感。完整 TOTP 实现留待 v3。

改动范围

  • security-center-card.tsxSwitch 添加 disabled 属性,显示"即将推出"徽章
  • i18n添加 twoFactor.comingSoon

4.2 P1通知测试按钮接入真实逻辑

方案选择:调用 notifications/dispatcher.tsdispatchNotification 函数发送真实通知。

改动范围

  • actions-notifications.ts:导入 dispatchNotification,根据 channel 调用对应渠道
  • 失败时返回具体错误信息

4.3 P1头像上传清理旧文件

改动范围

  • actions-avatar.ts:在更新前记录旧 image URL更新成功后调用 files/data-access.deleteFileByUrl 清理
  • 需要先确认 files/data-access 是否有 deleteFileByUrl 函数,若无则新增

4.4 P1会话远程登出

改动范围

  • actions-security.ts:新增 revokeSessionAction(sessionToken: string)
  • security-center-card.tsx:每条登录历史旁添加"登出"按钮(当前会话除外)
  • i18n添加 recentLogins.revoke / revokeSuccess / revokeFailure

4.5 P1AdminSettingsView dirty 检测

改动范围

  • admin-settings-view.tsx:维护 loadedValues 状态,计算 isDirtySave 按钮禁用逻辑Reset 恢复到 loadedValues

4.6 P2其他优化项

逐项实施,每项改动范围较小,详见各小节。


五、架构图同步说明

v2 改动完成后需同步更新:

5.1 004_architecture_impact_map.md §2.23

  • 更新"已知问题":标注 v2 新增/修复项
  • 更新"文件清单":新增测试文件、拆分后的子组件

5.2 005_architecture_data.json

  • modules.settings.exports:新增 revokeSessionAction
  • modules.settings.knownIssues:更新 v2 状态
  • dependencyMatrixsettings → notifications 依赖(通知测试真实发送)

六、验收标准

v2 完成后应满足:

  1. npm run lint 零错误warnings 可接受)
  2. npx tsc --noEmit 零错误
  3. 2FA 开关为禁用状态或完整 TOTP 实现(二选一)
  4. 通知测试按钮发送真实通知或禁用(二选一)
  5. 头像更换/删除后旧文件被清理
  6. 安全中心可远程登出其他会话
  7. AdminSettingsView Save 按钮在无变更时禁用
  8. 至少 3 个纯函数有单元测试
  9. 架构图 004/005 已同步更新