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

263 lines
13 KiB
Markdown
Raw 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.
# 设置和个人信息模块审计报告 v2
> 审查日期2026-06-22
> 审查范围:`src/modules/settings/**`、`src/app/(dashboard)/settings/**`、`src/app/(dashboard)/admin/settings/**`、`src/app/(dashboard)/profile/**`
> 上一版本:`settings-profile-audit-report.md`v1P0/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](file:///e:/Desktop/CICD/src/modules/settings/actions-security.ts) L21-46 | `toggleTwoFactorAction` 仅将 `twoFactorEnabled` 写入 system_settings 表,未接入 TOTP 密钥绑定、一次性码校验、备份码生成等真实 2FA 流程 | P0 |
| [security-center-card.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/security-center-card.tsx) L105-120 | 用户开启 2FA 后立即显示"已启用",但实际登录时不会要求二次验证,造成虚假安全感 | P0 |
| 同文件 L70 注释 | "占位实现,仅记录用户偏好" — 注释承认未接入真实流程 | P0 |
**后果**:用户以为启用了 2FA 但实际无效;安全合规审计会失败。
**建议**:在 v2 中要么 (a) 完整实现 TOTP 流程(绑定 authenticator + 验证一次性码 + 备份码),要么 (b) 将开关改为"即将推出"禁用状态,避免误导。
### 2.2 通知测试按钮为纯占位实现P1
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [actions-notifications.ts](file:///e:/Desktop/CICD/src/modules/settings/actions-notifications.ts) L29-39 | `sendTestNotificationAction``console.info` + `Promise.resolve()`,未调用真实通知发送服务 | P1 |
| [notification-preferences-form.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/notification-preferences-form.tsx) L119-133 | 点击测试按钮后总是显示"测试通知已发送",但用户不会收到任何通知 | P1 |
**后果**:用户以为测试通知已发送但收不到,无法真正验证渠道配置。
**建议**:接入 `notifications/dispatcher.ts` 的真实发送逻辑,或暂时将按钮改为禁用状态并标注"功能开发中"。
### 2.3 头像上传未清理旧文件P1
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [actions-avatar.ts](file:///e:/Desktop/CICD/src/modules/settings/actions-avatar.ts) L15-34 | `updateUserAvatarAction` 更新 `users.image` 字段后,旧头像文件仍留在文件存储中,无清理逻辑 | P1 |
| [avatar-upload.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/avatar-upload.tsx) L108-124 | `handleRemove` 调用 `removeUserAvatarAction` 仅清空 `users.image`,未删除实际文件 | P1 |
**后果**:存储成本累积;孤儿文件无法回收。
**建议**:在 `removeUserAvatarAction``updateUserAvatarAction` 中,更新数据库前先记录旧 URL更新成功后异步调用 `files/data-access.deleteFile` 清理旧文件。
### 2.4 SecurityCenterCard 缺少"登出其他会话"功能P1
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [security-center-card.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/security-center-card.tsx) 全文 | 仅展示登录历史,无法远程登出其他设备的会话 | P1 |
| [actions-security.ts](file:///e:/Desktop/CICD/src/modules/settings/actions-security.ts) 全文 | 无 `revokeSessionAction` 或类似 Server Action | P1 |
**后果**:用户发现可疑登录后无法主动处置,只能修改密码被动应对。
**建议**:新增 `revokeSessionAction(sessionToken: string)`,删除 `sessions` 表对应记录UI 在每条登录历史旁显示"登出"按钮(当前会话除外)。
### 2.5 AdminSettingsView 缺少表单变更检测P1
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [admin-settings-view.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/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.avatar` 在 `profilePage` 命名空间下缺失P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [profile/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/profile/page.tsx) | 使用 `<AvatarUpload>` 但页面其他文本使用 `settings.profilePage.*` 命名空间,而 AvatarUpload 内部使用 `settings.profile.avatar.*`,命名空间不一致 | P2 |
**后果**i18n 命名空间结构混乱,维护时易混淆。
**建议**:统一为 `settings.profile.avatar.*``settings.profilePage.avatar.*`,二选一。
### 2.7 SecurityCenterCard 未传递 `currentDeviceLabel`P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [settings-view.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/settings-view.tsx) L182 | `<SecurityCenterCard />` 未传递 `currentDeviceLabel` prop | P2 |
| [security-center-card.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/security-center-card.tsx) L192-194 | `isCurrent` 判断永远为 `false`"当前会话"徽章永远不会显示 | P2 |
**后果**:用户无法在登录历史中识别当前会话。
**建议**:在 Server Component 层获取 `headers().get("user-agent")`,通过 props 传递到 `SecurityCenterCard`
### 2.8 头像上传未限制文件名长度P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [avatar-upload.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/avatar-upload.tsx) L49-57 | `validateFile` 仅校验类型和大小,未校验文件名长度 | P2 |
**后果**:超长文件名可能导致数据库 `varchar` 字段截断或存储错误。
**建议**:添加 `file.name.length > 255` 校验。
### 2.9 通知偏好表单未做 dirty 检测P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [notification-preferences-form.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/notification-preferences-form.tsx) L98-117 | Save 按钮始终可点击,无 dirty 检测 | P2 |
**后果**:用户误点 Save 触发不必要的 Server Action 调用。
**建议**:维护 dirty 状态Save 按钮在无变更时禁用。
### 2.10 AdminSettingsView 文件行数接近上限P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [admin-settings-view.tsx](file:///e:/Desktop/CICD/src/modules/settings/components/admin-settings-view.tsx) | 425 行,接近 500 行建议上限 | P2 |
**后果**:可读性下降,维护困难。
**建议**:将 4 个 Card 拆分为独立子组件(`SchoolInfoCard` / `SecurityPolicyCard` / `FileUploadCard` / `NotificationConfigCard`),主组件仅负责表单状态和提交逻辑。
### 2.11 缺少单元测试P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| `src/modules/settings/**/*.test.ts` | 整个 settings 模块无任何单元测试文件 | P2 |
**后果**:重构无回归保障;纯函数(`toSettingItem``parseUserAgent``formatRelativeTime`)无法独立验证。
**建议**:为以下纯函数添加单元测试:
- `actions-system-settings.ts``toSettingItem`(值类型转换)
- `security-center-card.tsx``parseUserAgent``formatRelativeTime`
- `lib/student-overview-data.ts``buildStudentOverviewData``computeStudentStats`
### 2.12 2FA 状态查询存在 N+1 问题P2
| 位置 | 问题 | 严重性 |
|------|------|--------|
| [actions-security.ts](file:///e:/Desktop/CICD/src/modules/settings/actions-security.ts) L48-62 | `getTwoFactorStatus` 对每个用户分别查询 3 次 `system_settings`enabled / method / enabledAt共 3 次 DB 往返 | P2 |
**后果**:每次加载安全中心页面额外 3 次 DB 查询。
**建议**:使用 `getSystemSettingsByCategory("security_policy")` 一次查询所有 security_policy 分类下的设置,在内存中过滤当前用户的键。
---
## 三、改进优先级建议v2
### P0紧急影响安全/合规)
1. **2FA 真实实现或禁用开关**:要么完整实现 TOTP 流程,要么将开关改为"即将推出"禁用状态,避免虚假安全感。
### P1重要影响功能完整性
2. **通知测试按钮接入真实发送逻辑**:调用 `notifications/dispatcher.ts` 发送真实通知,或暂时禁用按钮。
3. **头像上传清理旧文件**:在 `removeUserAvatarAction``updateUserAvatarAction` 中添加旧文件清理逻辑。
4. **会话远程登出**:新增 `revokeSessionAction`UI 添加"登出"按钮。
5. **AdminSettingsView 表单 dirty 检测**Save 按钮在无变更时禁用Reset 恢复到加载值。
### P2优化提升质量
6. **统一 i18n 命名空间**`settings.profile.avatar``settings.profilePage` 二选一。
7. **SecurityCenterCard 传递 currentDeviceLabel**Server Component 层获取 user-agent 传入。
8. **头像上传文件名长度校验**:添加 `file.name.length > 255` 校验。
9. **通知偏好表单 dirty 检测**Save 按钮在无变更时禁用。
10. **AdminSettingsView 拆分子组件**4 个 Card 拆分为独立组件。
11. **添加单元测试**:为纯函数添加测试覆盖。
12. **2FA 状态查询优化**:合并 3 次 DB 查询为 1 次。
---
## 四、v2 实施计划
### 4.1 P02FA 真实实现或禁用
**方案选择**:考虑到完整 TOTP 实现需要额外的库(`otplib`)和 UIQR 码扫描、备份码展示v2 阶段先将开关改为"即将推出"禁用状态,避免虚假安全感。完整 TOTP 实现留待 v3。
**改动范围**
- `security-center-card.tsx`Switch 添加 `disabled` 属性,显示"即将推出"徽章
- i18n添加 `twoFactor.comingSoon`
### 4.2 P1通知测试按钮接入真实逻辑
**方案选择**:调用 `notifications/dispatcher.ts``dispatchNotification` 函数发送真实通知。
**改动范围**
- `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` 状态,计算 `isDirty`Save 按钮禁用逻辑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 状态
- `dependencyMatrix`settings → 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 已同步更新