# `src/app/(dashboard)/{announcements,dashboard,management,messages,profile,settings}` 规范核查报告 v2

> 核查日期：2026-06-18（第二轮）
> 核查范围：`src/app/(dashboard)/` 下的 announcements、dashboard、management、messages、profile、settings 子路由及其直接依赖的模块组件
> 依据文档：
> - [项目规则](../.trae/rules/project_rules.md)
> - [编码规范](../docs/standards/coding-standards.md)
> - [架构影响地图 004](../docs/architecture/004_architecture_impact_map.md)
> - [架构数据 005](../docs/architecture/005_architecture_data.json)
> 应用技能：`vercel-react-best-practices`、`web-design-guidelines`（`web-artifacts-builder` 加载失败，界面优化建议已合并至 web-design-guidelines 章节）
> 前置版本：[others_bug.md](./others_bug.md) v1

---

## 〇、v1 → v2 修复进度对比

### 已修复问题（11 项）

| 问题编号 | 描述 | 修复方式 |
|----------|------|----------|
| BUG-A01 | `announcements/page.tsx` 缺少权限校验 | ✅ 增加 `requirePermission(ANNOUNCEMENT_READ)` |
| BUG-D01 | `dashboard/page.tsx` 使用权限反推角色 | ✅ 改用 `roles.includes("admin"/"student"/"parent")` |
| BUG-M01 | `management/grade/classes/page.tsx` 缺少权限校验 | ✅ 增加 `requirePermission(GRADE_MANAGE)` |
| BUG-M02 | `management/grade/classes/page.tsx` userId 兜底空字符串 | ✅ 改用 `ctx.userId` |
| BUG-MSG01 相关 | `messages/page.tsx` 已有权限校验 | ✅ 保持 `requirePermission(MESSAGE_READ)` |
| BUG-SS01 | `settings/security/page.tsx` 缺少权限校验 | ✅ 增加 `requireAuth()` |
| BUG-S 部分 | `settings/page.tsx` 改用 `requireAuth()` | ⚠️ 部分修复（仍用权限反推角色） |
| BUG-P 部分 | `profile/page.tsx` 改用 `requireAuth()` | ⚠️ 部分修复（仍用权限反推角色） |
| BUG-NL03 | `notification-list.tsx` button 缺少 type 属性 | ✅ 已添加 `type="button"` |
| BUG-PS 部分 | `profile-settings-form.tsx` 处理 result.success | ✅ 增加 result.success 分支处理 |
| BUG-MI01 部分 | `management/grade/insights/page.tsx` 增加权限校验 | ⚠️ 使用 `requireAuth()` 而非 `requirePermission()` |

### 未修复问题（仍存在）

v1 报告中的其余 53 项问题仍未修复，详见下文。

---

## 一、核查文件清单

| 文件 | 行数 | 类型 | 用途 |
|------|------|------|------|
| [announcements/page.tsx](../src/app/(dashboard)/announcements/page.tsx) | 23 | RSC 页面 | 公告列表（普通用户） |
| [dashboard/page.tsx](../src/app/(dashboard)/dashboard/page.tsx) | 16 | RSC 页面 | 角色路由分发 |
| [management/grade/classes/page.tsx](../src/app/(dashboard)/management/grade/classes/page.tsx) | 32 | RSC 页面 | 年级班级管理 |
| [management/grade/insights/page.tsx](../src/app/(dashboard)/management/grade/insights/page.tsx) | 245 | RSC 页面 | 年级作业洞察 |
| [messages/page.tsx](../src/app/(dashboard)/messages/page.tsx) | 31 | RSC 页面 | 消息+通知列表 |
| [messages/[id]/page.tsx](../src/app/(dashboard)/messages/[id]/page.tsx) | 30 | RSC 页面 | 消息详情 |
| [messages/compose/page.tsx](../src/app/(dashboard)/messages/compose/page.tsx) | 34 | RSC 页面 | 撰写消息 |
| [profile/page.tsx](../src/app/(dashboard)/profile/page.tsx) | 304 | RSC 页面 | 个人资料（学生/教师视图） |
| [settings/page.tsx](../src/app/(dashboard)/settings/page.tsx) | 31 | RSC 页面 | 设置入口（按角色分发） |
| [settings/security/page.tsx](../src/app/(dashboard)/settings/security/page.tsx) | 48 | RSC 页面 | 安全设置 |
| [layout.tsx](../src/app/(dashboard)/layout.tsx) | 21 | RSC 布局 | Dashboard 通用布局 |
| [error.tsx](../src/app/(dashboard)/error.tsx) | 22 | 客户端组件 | 错误边界 |
| [not-found.tsx](../src/app/(dashboard)/not-found.tsx) | 23 | RSC 组件 | 404 页面 |
| [modules/announcements/components/announcement-list.tsx](../src/modules/announcements/components/announcement-list.tsx) | 108 | 客户端组件 | 公告列表（含筛选） |
| [modules/announcements/components/announcement-card.tsx](../src/modules/announcements/components/announcement-card.tsx) | 79 | 客户端组件 | 公告卡片 |
| [modules/announcements/components/announcement-detail.tsx](../src/modules/announcements/components/announcement-detail.tsx) | 206 | 客户端组件 | 公告详情 |
| [modules/messaging/components/message-list.tsx](../src/modules/messaging/components/message-list.tsx) | 117 | 客户端组件 | 消息列表 |
| [modules/messaging/components/message-detail.tsx](../src/modules/messaging/components/message-detail.tsx) | 153 | 客户端组件 | 消息详情 |
| [modules/messaging/components/message-compose.tsx](../src/modules/messaging/components/message-compose.tsx) | 146 | 客户端组件 | 撰写消息表单 |
| [modules/messaging/components/notification-list.tsx](../src/modules/messaging/components/notification-list.tsx) | 141 | 客户端组件 | 通知列表 |
| [modules/settings/components/admin-settings-view.tsx](../src/modules/settings/components/admin-settings-view.tsx) | 129 | 客户端组件 | 管理员设置视图 |
| [modules/settings/components/teacher-settings-view.tsx](../src/modules/settings/components/teacher-settings-view.tsx) | 132 | 客户端组件 | 教师设置视图 |
| [modules/settings/components/student-settings-view.tsx](../src/modules/settings/components/student-settings-view.tsx) | 120 | 客户端组件 | 学生设置视图 |
| [modules/settings/components/password-change-form.tsx](../src/modules/settings/components/password-change-form.tsx) | 180 | 客户端组件 | 修改密码表单 |
| [modules/settings/components/profile-settings-form.tsx](../src/modules/settings/components/profile-settings-form.tsx) | 202 | 客户端组件 | 资料编辑表单 |
| [modules/settings/components/notification-preferences-form.tsx](../src/modules/settings/components/notification-preferences-form.tsx) | 260 | 客户端组件 | 通知偏好表单 |
| [modules/settings/components/theme-preferences-card.tsx](../src/modules/settings/components/theme-preferences-card.tsx) | 60 | 客户端组件 | 主题偏好 |
| [modules/settings/components/ai-provider-settings-card.tsx](../src/modules/settings/components/ai-provider-settings-card.tsx) | 405 | 客户端组件 | AI Provider 配置 |
| [modules/classes/components/grade-classes-view.tsx](../src/modules/classes/components/grade-classes-view.tsx) | 455 | 客户端组件 | 年级班级管理视图 |

---

## 二、违规问题清单（仍未修复）

### 2.1 [dashboard/page.tsx](../src/app/(dashboard)/dashboard/page.tsx) — 严重度：中

#### BUG-D02：多重 `redirect` 调用难以维护（未修复）
- **位置**：`src/app/(dashboard)/dashboard/page.tsx:12-15`
- **问题**：4 个连续 `if + redirect` 缺乏优先级文档说明，新增角色时易遗漏
- **改进建议**：抽取为 `resolveDefaultPath(roles)` 单一函数（`proxy.ts` 已有类似实现），保持单一职责

---

### 2.2 [management/grade/insights/page.tsx](../src/app/(dashboard)/management/grade/insights/page.tsx) — 严重度：高

#### BUG-MI01：权限校验不充分（部分修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:27`
- **问题**：使用 `requireAuth()` 而非 `requirePermission()`，仅校验登录状态，未校验具体权限
- **规范依据**：项目规则「Server Action 必须使用 `requirePermission()` 进行权限校验」
- **改进建议**：应使用 `requirePermission(Permissions.HOMEWORK_READ)` 或对应年级负责人权限

#### BUG-MI02：使用原生 `<select>` 而非 shadcn Select 组件（未修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:72-83`
- **问题**：使用原生 `<select>` 元素，与项目其他页面使用的 shadcn `Select` 组件风格不一致
- **规范依据**：Web Interface Guidelines — Consistency；项目组件规范
- **影响**：视觉风格不统一，无障碍特性差异，主题切换时原生 select 样式无法跟随
- **改进建议**：替换为 shadcn `Select` 组件

#### BUG-MI03：`<label>` 缺少 `htmlFor` 关联（未修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:71`
- **问题**：`<label className="text-sm font-medium">Grade</label>` 未关联到 `select` 元素
- **规范依据**：Web Interface Guidelines — Forms「Labels properly associated」
- **改进建议**：`<label htmlFor="gradeId" className="...">Grade</label>`

#### BUG-MI04：表单提交触发整页刷新（未修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:70`
- **问题**：`<form action="/management/grade/insights" method="get">` 使用原生 GET 提交，导致整页刷新
- **违反规则**：Next.js 客户端导航最佳实践
- **改进建议**：改为客户端组件 + `useRouter().push()` 或使用 `useSearchParams` 实现无刷新筛选

#### BUG-MI05：`fmt` 工具函数命名过于简短（未修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:24`
- **问题**：`const fmt = (v: number | null, digits = 1) => ...` 命名过于简短
- **改进建议**：重命名为 `formatScore` 或 `formatNumber`

---

### 2.3 [messages/[id]/page.tsx](../src/app/(dashboard)/messages/[id]/page.tsx) — 严重度：中

#### BUG-MSG02：渲染期间执行写操作（未修复）
- **位置**：`src/app/(dashboard)/messages/[id]/page.tsx:20-23`
- **问题**：在 RSC 渲染期间调用 `markMessageAsRead(id, ctx.userId)` 执行写操作
- **违反规则**：React Server Components 规范 — 渲染函数应为纯函数，不应有副作用
- **影响**：
  1. React 18+ 严格模式下渲染函数可能被调用两次，导致重复写入
  2. 流式渲染时若渲染被中断，写操作可能已执行但 UI 未更新
  3. 错误边界捕获错误后重试渲染会再次执行写操作
- **改进建议**：使用 `after()` API 延迟执行非阻塞写操作
  ```typescript
  import { after } from "next/server"

  if (!message.isRead && message.receiverId === ctx.userId) {
    after(() => markMessageAsRead(id, ctx.userId))
  }
  ```
- **规范依据**：`vercel-react-best-practices` — `server-after-nonblocking`

---

### 2.4 [messages/compose/page.tsx](../src/app/(dashboard)/messages/compose/page.tsx) — 严重度：低

#### BUG-MSG03：缺少 `metadata` 导出（未修复）
- **改进建议**：`export const metadata = { title: "Compose Message" }`

---

### 2.5 [profile/page.tsx](../src/app/(dashboard)/profile/page.tsx) — 严重度：高

#### BUG-P01：使用权限反推角色（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:46-47`
- **问题**：`isStudent = permissions.includes(HOMEWORK_SUBMIT) && !permissions.includes(EXAM_CREATE)`，`isTeacher = permissions.includes(EXAM_CREATE)`
- **规范依据**：项目规则禁止硬编码角色判断；架构文档 004 已标记
- **改进建议**：使用 `ctx.roles` 判断
  ```typescript
  const roles = ctx.roles
  const isStudent = roles.includes("student")
  const isTeacher = roles.includes("teacher")
  ```

#### BUG-P02：在 RSC 中使用 IIFE 异步块（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:49-117`
- **问题**：使用 `await (async () => { ... })()` 立即执行异步函数，将学生数据加载逻辑内联在组件中
- **影响**：
  1. 函数体过长（60+ 行），难以测试
  2. 无法被 React `cache()` 缓存
  3. 违反单一职责原则
- **改进建议**：抽取为 `data-access.ts` 中的 `getStudentProfileData(userId)` 函数

#### BUG-P03：本地 `formatDate` 函数与全局工具重复（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:26-33`
- **问题**：定义了本地 `formatDate` 函数，与 `@/shared/lib/utils.formatDate` 重复
- **影响**：日期格式不一致（本地使用 `en-US`，全局使用 `zh-CN`），维护成本增加
- **改进建议**：删除本地函数，使用全局 `formatDate`

#### BUG-P04：`toWeekday` 类型断言不必要（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:23`
- **问题**：`(day === 0 ? 7 : day) as 1 | 2 | 3 | 4 | 5 | 6 | 7` 使用 `as` 断言
- **规范依据**：编码规范 4.2.3「禁止 `as` 断言（除非从 `unknown` 转换）」
- **改进建议**：使用类型守卫
  ```typescript
  const toWeekday = (d: Date): 1 | 2 | 3 | 4 | 5 | 6 | 7 => {
    const day = d.getDay()
    const result = day === 0 ? 7 : day
    if (result < 1 || result > 7) throw new Error("Invalid weekday")
    return result
  }
  ```

#### BUG-P05：缩进不一致（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:156,166,184,204-206`
- **问题**：多处缩进不一致（如 156 行 `               <div` 比 155 行多一个空格）
- **规范依据**：`.prettierrc` 配置 `tabWidth: 2`
- **改进建议**：运行 `npx prettier --write` 统一格式

#### BUG-P06：缺少 `metadata` 导出（未修复）
- **改进建议**：`export const metadata = { title: "Profile" }`

---

### 2.6 [settings/page.tsx](../src/app/(dashboard)/settings/page.tsx) — 严重度：中

#### BUG-S01：使用权限反推角色（未修复）
- **位置**：`src/app/(dashboard)/settings/page.tsx:24,27`
- **问题**：同 BUG-P01，使用 `permissions.includes(HOMEWORK_SUBMIT) && !permissions.includes(EXAM_CREATE)` 判断学生
- **改进建议**：使用 `ctx.roles` 判断

#### BUG-S02：缺少 `metadata` 导出（未修复）
- **改进建议**：`export const metadata = { title: "Settings" }`

---

### 2.7 [layout.tsx](../src/app/(dashboard)/layout.tsx) — 严重度：中

#### BUG-L01：跳过链接样式冗长（未修复）
- **位置**：`src/app/(dashboard)/layout.tsx:12`
- **问题**：`focus:absolute focus:z-50 focus:p-4 focus:bg-background focus:text-foreground focus:border focus:border-border focus:rounded-md focus:m-2` 类名过长且重复
- **改进建议**：抽取为 `skip-link` 类名或独立组件

#### BUG-L02：`<main>` 元素缺少显式 `role="main"`（未修复）
- **位置**：`src/app/(dashboard)/layout.tsx:16`
- **问题**：`<main id="main-content">` 已有 `id`，但部分屏幕阅读器需要显式 `role="main"`
- **改进建议**：添加 `role="main"`（虽然 HTML5 规范中 `<main>` 隐式 `role="main"`，但为兼容性建议显式）

---

### 2.8 [error.tsx](../src/app/(dashboard)/error.tsx) — 严重度：低

#### BUG-E01：未使用 `error.digest` 信息（未修复）
- **位置**：`src/app/(dashboard)/error.tsx:7`
- **问题**：`error` 参数包含 `digest` 字段（用于错误追踪），但未展示给用户或上报
- **改进建议**：在描述中包含 `digest` 或提供「复制错误码」按钮

---

### 2.9 [not-found.tsx](../src/app/(dashboard)/not-found.tsx) — 严重度：低

#### BUG-NF01：使用原生 `<a>` 样式而非 Button 组件（未修复）
- **位置**：`src/app/(dashboard)/not-found.tsx:15-20`
- **问题**：`<Link className="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex h-9 ...">` 手动拼接 Button 样式
- **规范依据**：项目组件规范「使用 `cn()` 工具函数管理条件类名」
- **改进建议**：使用 `<Button asChild><Link href="/dashboard">...</Link></Button>`

---

### 2.10 [announcement-list.tsx](../src/modules/announcements/components/announcement-list.tsx) — 严重度：中

#### BUG-AL01：使用 `<a href>` 而非 `<Link>`（未修复）
- **位置**：`src/modules/announcements/components/announcement-list.tsx:76`
- **问题**：`<a href={createHref}>` 使用原生 `<a>` 标签，导致全页刷新
- **违反规则**：`vercel-react-best-practices` — Next.js 客户端导航最佳实践
- **改进建议**：使用 `next/link` 的 `<Link>` 组件

#### BUG-AL02：`handleFilterChange` 未使用 `useCallback`（未修复）
- **位置**：`src/modules/announcements/components/announcement-list.tsx:51-57`
- **问题**：`handleFilterChange` 每次渲染创建新引用
- **违反规则**：`rerender-functional-setstate`、`rerender-memo`
- **改进建议**：使用 `useCallback` 包裹

---

### 2.11 [announcement-card.tsx](../src/modules/announcements/components/announcement-card.tsx) — 严重度：低

#### BUG-AC01：`useMemo` 包裹整个 JSX（过度优化，未修复）
- **位置**：`src/modules/announcements/components/announcement-card.tsx:38-68`
- **问题**：使用 `useMemo` 包裹整个卡片 JSX，依赖项为 `[announcement]`（对象）
- **违反规则**：`rerender-simple-expression-in-memo` — 简单表达式不需要 memo
- **影响**：`announcement` 是对象，每次父组件传入新引用时 memo 失效，无实际优化效果
- **改进建议**：移除 `useMemo`，直接渲染 JSX；如需优化应使用 `React.memo` 包裹组件

---

### 2.12 [announcement-detail.tsx](../src/modules/announcements/components/announcement-detail.tsx) — 严重度：中

#### BUG-AD01：使用 `<a href>` 而非 `<Link>`（未修复）
- **位置**：`src/modules/announcements/components/announcement-detail.tsx:123,146`
- **问题**：`backHref` 和 `editHref` 使用原生 `<a>` 标签
- **改进建议**：替换为 `next/link`

#### BUG-AD02：三个处理函数未 `useCallback`（未修复）
- **位置**：`src/modules/announcements/components/announcement-detail.tsx:64-115`
- **问题**：`handlePublish`、`handleArchive`、`handleDelete` 每次渲染创建新引用
- **违反规则**：`rerender-functional-setstate`
- **改进建议**：使用 `useCallback` 包裹

---

### 2.13 [message-list.tsx](../src/modules/messaging/components/message-list.tsx) — 严重度：中

#### BUG-ML01：使用字符串拼接动态类名（未修复）
- **位置**：`src/modules/messaging/components/message-list.tsx:82,91`
- **问题**：`` className={`transition-colors hover:bg-accent/50 ${unread ? "border-primary/40" : ""}`} `` 使用模板字符串拼接类名
- **规范依据**：项目规则「使用 `cn()` 工具函数管理条件类名」
- **改进建议**：
  ```typescript
  className={cn(
    "transition-colors hover:bg-accent/50",
    unread && "border-primary/40"
  )}
  ```

#### BUG-ML02：`usePermission` 在客户端组件中导致 hydration 风险（未修复）
- **位置**：`src/modules/messaging/components/message-list.tsx:30-31`
- **问题**：`usePermission()` 依赖 `useSession()`，服务端渲染时返回空权限，客户端首次渲染后才有权限，导致「Compose」按钮在 hydration 后闪烁
- **违反规则**：Web Interface Guidelines — Hydration Safety
- **改进建议**：将 `canSend` 作为 prop 从 RSC 父组件传入

---

### 2.14 [message-detail.tsx](../src/modules/messaging/components/message-detail.tsx) — 严重度：中

#### BUG-MD01：使用 `<a href>` 而非 `<Link>`（未修复）
- **位置**：`src/modules/messaging/components/message-detail.tsx:79`
- **问题**：`<a href={backHref}>` 使用原生 `<a>`
- **改进建议**：替换为 `next/link`

#### BUG-MD02：`replyHref` 为 `undefined` 时仍渲染 Link（未修复）
- **位置**：`src/modules/messaging/components/message-detail.tsx:68,87-92`
- **问题**：当 `canSend` 为 false 时 `replyHref` 为 `undefined`，但代码使用 `<Link href={replyHref ?? "#"}>` 仍渲染可点击链接，点击后跳转到 `#`
- **改进建议**：`canSend` 为 false 时不渲染 Reply 按钮（当前已有 `{canSend ? ... : null}` 包裹，但内部仍用 `?? "#"` 兜底，应直接使用 `replyHref!` 或移除兜底）

#### BUG-MD03：URL 参数未编码（未修复）
- **位置**：`src/modules/messaging/components/message-detail.tsx:69-71`
- **问题**：`subject=${encodeURIComponent(...)}` 已编码 subject，但 `parentId` 和 `receiverId` 未编码
- **改进建议**：使用 `URLSearchParams` 构建查询字符串

---

### 2.15 [message-compose.tsx](../src/modules/messaging/components/message-compose.tsx) — 严重度：中

#### BUG-MC01：使用 `<a href>` 而非 `<Link>`（未修复）
- **位置**：`src/modules/messaging/components/message-compose.tsx:73`
- **问题**：返回按钮使用原生 `<a>`
- **改进建议**：替换为 `next/link`

#### BUG-MC02：隐藏 input 与 `formData.set` 重复（未修复）
- **位置**：`src/modules/messaging/components/message-compose.tsx:46,97`
- **问题**：`handleSubmit` 中 `formData.set("receiverId", receiverId)`，同时 JSX 中又有 `<input type="hidden" name="receiverId" value={receiverId} />`，两者重复
- **改进建议**：移除隐藏 input，仅使用 `formData.set`

#### BUG-MC03：`handleSubmit` 未 `useCallback`（未修复）
- **位置**：`src/modules/messaging/components/message-compose.tsx:41-66`
- **改进建议**：使用 `useCallback` 包裹

---

### 2.16 [notification-list.tsx](../src/modules/messaging/components/notification-list.tsx) — 严重度：中

#### BUG-NL01：使用字符串拼接动态类名（未修复）
- **位置**：`src/modules/messaging/components/notification-list.tsx:94,102`
- **问题**：`` className={`transition-colors ${!n.isRead ? "border-primary/40 bg-primary/5" : ""}`} ``
- **规范依据**：项目规则「使用 `cn()` 工具函数管理条件类名」
- **改进建议**：使用 `cn()`

#### BUG-NL02：`handleMarkRead` 未 `useCallback`（未修复）
- **位置**：`src/modules/messaging/components/notification-list.tsx:54-63`
- **改进建议**：使用 `useCallback`

---

### 2.17 [password-change-form.tsx](../src/modules/settings/components/password-change-form.tsx) — 严重度：高

#### BUG-PC01：使用字符串拼接动态类名（严重违规，未修复）
- **位置**：`src/modules/settings/components/password-change-form.tsx:133`
- **问题**：`` className={`h-2 [&>div]:${meta.color}`} `` 动态拼接 Tailwind 类名
- **规范依据**：项目规则「**禁止**字符串拼接动态类名（`bg-${color}-500`）」
- **影响**：Tailwind JIT 无法识别动态拼接的类名，`bg-red-500`、`bg-yellow-500`、`bg-green-500` 可能被 tree-shaking 移除，导致生产环境进度条无颜色
- **改进建议**：使用映射对象 + `cn()`
  ```typescript
  const STRENGTH_BAR_CLASS: Record<PasswordStrength, string> = {
    weak: "h-2 [&>div]:bg-red-500",
    medium: "h-2 [&>div]:bg-yellow-500",
    strong: "h-2 [&>div]:bg-green-500",
  }

  <Progress value={meta.value} className={STRENGTH_BAR_CLASS[strength]} />
  ```

#### BUG-PC02：使用 `document.getElementById` 操作 DOM（未修复）
- **位置**：`src/modules/settings/components/password-change-form.tsx:62-63`
- **问题**：`const form = document.getElementById("password-change-form") as HTMLFormElement | null` 直接操作 DOM
- **规范依据**：React 最佳实践 — 避免直接 DOM 操作
- **改进建议**：使用 `useRef<HTMLFormElement>` 或受控组件重置表单

#### BUG-PC03：`as` 断言使用（未修复）
- **位置**：`src/modules/settings/components/password-change-form.tsx:62`
- **问题**：`as HTMLFormElement | null` 使用类型断言
- **规范依据**：编码规范 4.2.3「禁止 `as` 断言」
- **改进建议**：使用 `useRef` 后通过 ref.current 的类型推导

---

### 2.18 [profile-settings-form.tsx](../src/modules/settings/components/profile-settings-form.tsx) — 严重度：高

#### BUG-PS01：使用 `as any` 类型断言（严重违规，未修复）
- **位置**：`src/modules/settings/components/profile-settings-form.tsx:35`
- **问题**：`resolver: zodResolver(profileFormSchema) as any` 使用 `as any`
- **规范依据**：项目规则「**禁止 `any`**」「**禁止 `as` 断言**」
- **改进建议**：修复 `zodResolver` 类型不匹配问题
  ```typescript
  import type { Resolver } from "react-hook-form"
  const resolver: Resolver<ProfileFormValues> = zodResolver(profileFormSchema)
  ```

#### BUG-PS02：`console.error` 残留（未修复）
- **位置**：`src/modules/settings/components/profile-settings-form.tsx:64`
- **问题**：`console.error(error)` 在生产代码中残留
- **规范依据**：编码规范 — 生产代码不应包含 `console.*`
- **改进建议**：移除或替换为日志服务

#### BUG-PS03：`onSubmit` 未 `useCallback`（未修复）
- **位置**：`src/modules/settings/components/profile-settings-form.tsx:47-67`
- **改进建议**：使用 `useCallback`

#### BUG-PS04：`age` 字段使用 `z.coerce.number()` 但未处理 NaN（未修复）
- **位置**：`src/modules/settings/components/profile-settings-form.tsx:25`
- **问题**：`age: z.coerce.number().min(0).optional()` 当输入为空字符串时会转换为 `0`，而非 `undefined`
- **改进建议**：使用 `z.preprocess` 处理空值

---

### 2.19 [notification-preferences-form.tsx](../src/modules/settings/components/notification-preferences-form.tsx) — 严重度：中

#### BUG-NPF01：Switch 与隐藏 checkbox 状态同步问题（未修复）
- **位置**：`src/modules/settings/components/notification-preferences-form.tsx:186-201,233-248`
- **问题**：同时使用隐藏 `<input type="checkbox">` 和 `<Switch>`，两者都调用 `toggleChannel`/`toggleCategory`，可能导致双重切换
- **影响**：用户点击 Switch 时，`onCheckedChange` 触发；同时隐藏 checkbox 的 `onChange` 也触发，导致状态切换两次回到原点
- **改进建议**：移除隐藏 checkbox，仅使用 Switch + 隐藏 input（`type="hidden"`）提交表单
  ```typescript
  <input type="hidden" name={item.key} value={checked ? "true" : "false"} />
  <Switch
    checked={checked}
    onCheckedChange={() => toggleChannel(item.key)}
    aria-label={item.label}
  />
  ```

#### BUG-NPF02：本地状态与服务器状态可能不同步（未修复）
- **位置**：`src/modules/settings/components/notification-preferences-form.tsx:122-133`
- **问题**：`useState` 初始化自 `preferences` prop，但 prop 变化时状态不更新
- **违反规则**：`rerender-derived-state-no-effect`
- **改进建议**：使用 `key` prop 重置组件，或使用受控组件

#### BUG-NPF03：中文注释混合英文代码（未修复）
- **位置**：`src/modules/settings/components/notification-preferences-form.tsx:161,186,209`
- **问题**：`{/* 通知渠道 */}`、`{/* 隐藏的 checkbox 用于表单提交 */}`、`{/* 通知类别 */}` 中文注释
- **规范依据**：项目代码一致性（其他文件使用英文注释）
- **改进建议**：统一为英文注释

---

### 2.20 [theme-preferences-card.tsx](../src/modules/settings/components/theme-preferences-card.tsx) — 严重度：低

#### BUG-TP01：`"use client"` 后缺少空行（未修复）
- **位置**：`src/modules/settings/components/theme-preferences-card.tsx:1-2`
- **问题**：`"use client"` 紧跟 `import` 无空行
- **规范依据**：`.prettierrc` 格式规范
- **改进建议**：运行 `npx prettier --write`

#### BUG-TP02：`setTheme` 参数类型不安全（未修复）
- **位置**：`src/modules/settings/components/theme-preferences-card.tsx:31`
- **问题**：`onValueChange={(v) => setTheme(v)}` 中 `v` 为 `string`，但 `setTheme` 期望特定类型
- **改进建议**：使用类型守卫或 next-themes 提供的类型

---

### 2.21 [ai-provider-settings-card.tsx](../src/modules/settings/components/ai-provider-settings-card.tsx) — 严重度：高

#### BUG-AI01：中英文混合 UI（严重一致性违规，未修复）
- **位置**：`src/modules/settings/components/ai-provider-settings-card.tsx:298,306,325,352,367`
- **问题**：
  - FormLabel 使用中文「品牌方」「设为默认」
  - FormDescription 使用中文「填写基础地址，不要包含 /chat/completions。」「不会回显历史 Key，留空表示不更新。」
  - SelectItem 使用中文「智谱」
- **规范依据**：Web Interface Guidelines — Consistency；项目其他 UI 均为英文
- **影响**：用户在英文界面中突然看到中文，体验割裂
- **改进建议**：统一为英文
  ```typescript
  <FormLabel>Provider</FormLabel>
  <FormDescription>Enter base URL without /chat/completions suffix.</FormDescription>
  <FormLabel>Set as default</FormLabel>
  <FormDescription>Existing key won't be displayed. Leave blank to keep current.</FormDescription>
  <SelectItem value="zhipu">Zhipu</SelectItem>
  ```

#### BUG-AI02：`useEffect` 依赖项过多导致重复执行（未修复）
- **位置**：`src/modules/settings/components/ai-provider-settings-card.tsx:108-136`
- **问题**：`useEffect` 依赖 `[form, selectedId, onProvidersChanged, initialMode, resetToNew]`，但使用 `loadedRef` 防止重复执行
- **违反规则**：`rerender-dependencies` — 应使用原始依赖
- **改进建议**：将初始化逻辑移至 `useEffect` 内部，依赖项仅为 `[]`（仅执行一次）

#### BUG-AI03：`handleSelectChange` 未 `useCallback`（未修复）
- **位置**：`src/modules/settings/components/ai-provider-settings-card.tsx:138-156`
- **改进建议**：使用 `useCallback`

#### BUG-AI04：文件行数 405 行，接近上限（未修复）
- **位置**：`src/modules/settings/components/ai-provider-settings-card.tsx`
- **问题**：文件 405 行，项目规则建议 React 组件 ≤ 500 行，但复杂度较高
- **改进建议**：考虑拆分为 `AiProviderSelect`、`AiProviderForm`、`AiProviderTestButton` 子组件

---

### 2.22 [admin-settings-view.tsx](../src/modules/settings/components/admin-settings-view.tsx) — 严重度：低

#### BUG-AS01：Tab 图标语义错误（未修复）
- **位置**：`src/modules/settings/components/admin-settings-view.tsx:50-53`
- **问题**：`appearance` Tab 使用 `<Shield />` 图标（盾牌通常表示安全），应使用 `<Palette />` 或 `<Monitor />`
- **规范依据**：Web Interface Guidelines — Iconography
- **改进建议**：`<TabsTrigger value="appearance"><Palette /></TabsTrigger>`（注意：`student-settings-view.tsx` 和 `teacher-settings-view.tsx` 已正确使用 `Palette`，仅 admin 视图未修复）

#### BUG-AS02：`signOut` 直接调用未确认（未修复）
- **位置**：`src/modules/settings/components/admin-settings-view.tsx:120`
- **问题**：`onClick={() => signOut({ callbackUrl: "/login" })}` 直接登出，无确认对话框
- **规范依据**：Web Interface Guidelines — Destructive Actions
- **改进建议**：增加确认对话框

---

### 2.23 [teacher-settings-view.tsx](../src/modules/settings/components/teacher-settings-view.tsx) — 严重度：低

#### BUG-TS01：与 admin-settings-view.tsx 大量重复代码（未修复）
- **位置**：`src/modules/settings/components/teacher-settings-view.tsx`
- **问题**：与 `admin-settings-view.tsx`、`student-settings-view.tsx` 90% 代码重复，仅「Back to dashboard」链接和「Quick links」不同
- **规范依据**：DRY 原则
- **改进建议**：抽取为 `SettingsLayout` 共享组件

---

### 2.24 [student-settings-view.tsx](../src/modules/settings/components/student-settings-view.tsx) — 严重度：低

#### BUG-ST01：同 BUG-TS01，代码重复（未修复）
- **改进建议**：同 BUG-TS01

---

### 2.25 [grade-classes-view.tsx](../src/modules/classes/components/grade-classes-view.tsx) — 严重度：高

#### BUG-GC01：文件 455 行，接近 500 行建议上限（未修复）
- **位置**：`src/modules/classes/components/grade-classes-view.tsx`
- **问题**：单文件 455 行，包含列表、创建对话框、编辑对话框、删除确认对话框
- **规范依据**：项目规则「React 组件：建议 ≤ 500 行」
- **改进建议**：拆分为：
  - `grade-classes-view.tsx`（主视图，< 100 行）
  - `grade-class-create-dialog.tsx`
  - `grade-class-edit-dialog.tsx`
  - `grade-class-delete-dialog.tsx`

#### BUG-GC02：`useEffect` 依赖项导致不必要重渲染（未修复）
- **位置**：`src/modules/classes/components/grade-classes-view.tsx:62-78`
- **问题**：两个 `useEffect` 依赖 `managedGrades` 数组引用，父组件每次传入新数组都会触发
- **违反规则**：`rerender-dependencies`
- **改进建议**：依赖 `managedGrades[0]?.id` 而非整个数组

#### BUG-GC03：中英文混合 UI（未修复）
- **位置**：`src/modules/classes/components/grade-classes-view.tsx:183-184,283,370,389`
- **问题**：表头「班主任」「任课老师」使用中文，其他列使用英文
- **规范依据**：Web Interface Guidelines — Consistency
- **改进建议**：统一为英文 `Homeroom Teacher`、`Subject Teachers`

#### BUG-GC04：`formatSubjectTeachers` 在每次渲染时重新创建（未修复）
- **位置**：`src/modules/classes/components/grade-classes-view.tsx:140-146`
- **问题**：函数在组件内定义，每次渲染创建新引用
- **改进建议**：移至模块级别（不依赖组件状态）

---

## 三、React 性能优化（应用 `vercel-react-best-practices` 技能）

### 3.1 重渲染优化

#### PERF-01：`usePermission` 返回的回调未 memoize（未修复）
- **位置**：`src/shared/hooks/use-permission.ts:11-25`
- **问题**：`hasPermission`、`hasAnyPermission`、`hasAllPermissions`、`hasRole` 每次渲染创建新函数引用
- **违反规则**：`rerender-functional-setstate`、`rerender-memo`
- **影响**：`message-list.tsx`、`message-detail.tsx` 中使用 `usePermission()` 的组件每次渲染都创建新 `canSend`/`canDelete` 值
- **改进建议**：使用 `useCallback` 包裹所有回调

#### PERF-02：`AnnouncementCard` 的 `useMemo` 无效（未修复）
- **位置**：`src/modules/announcements/components/announcement-card.tsx:38-68`
- **问题**：`useMemo` 依赖 `[announcement]`（对象），父组件每次渲染传入新引用，memo 失效
- **违反规则**：`rerender-simple-expression-in-memo`
- **改进建议**：移除 `useMemo`，使用 `React.memo` 包裹组件

#### PERF-06：`ai-provider-settings-card.tsx` 使用 `loadedRef` 防止重复加载（未修复）
- **位置**：`src/modules/settings/components/ai-provider-settings-card.tsx:66,109-110`
- **问题**：使用 `loadedRef` 而非空依赖 `useEffect`
- **违反规则**：`rerender-dependencies`
- **改进建议**：使用空依赖数组 `[]` + 清理函数

### 3.2 服务端性能

#### PERF-08：`profile/page.tsx` 数据加载未使用 `cache()`（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:49-117`
- **问题**：学生数据加载逻辑内联在组件中，无法被 React `cache()` 去重
- **违反规则**：`server-cache-react`
- **改进建议**：抽取为 `data-access.ts` 中的 `cache()` 包裹函数

#### PERF-09：`messages/[id]/page.tsx` 渲染期间写操作（未修复）
- **位置**：`src/app/(dashboard)/messages/[id]/page.tsx:20-23`
- **问题**：渲染期间调用 `markMessageAsRead` 执行写操作
- **违反规则**：`server-after-nonblocking`
- **改进建议**：使用 `after()` API

---

## 四、Web 界面规范审查（应用 `web-design-guidelines` 技能）

### 4.1 Hydration Safety

#### UI-01：`usePermission` 导致 hydration 闪烁（未修复）
- **位置**：`src/modules/messaging/components/message-list.tsx:30-31`、`src/modules/messaging/components/message-detail.tsx:41-43`
- **问题**：`usePermission()` 依赖 `useSession()`，服务端渲染时无权限，客户端 hydration 后权限相关 UI（Compose、Reply、Delete 按钮）闪烁出现
- **违反规则**：Web Interface Guidelines — Hydration Safety
- **改进建议**：将权限判断结果作为 prop 从 RSC 父组件传入

#### UI-02：`theme-preferences-card.tsx` 已使用 `suppressHydrationWarning`（已修复 ✅）
- **位置**：`src/modules/settings/components/theme-preferences-card.tsx:32`
- **现状**：✅ 已正确处理主题切换的 hydration 问题

### 4.2 Navigation & State

#### UI-03：使用 `<a href>` 导致全页刷新（未修复）
- **位置**：多处（BUG-AL01、BUG-AD01、BUG-MD01、BUG-MC01）
- **问题**：使用原生 `<a>` 而非 `<Link>`，破坏 SPA 导航
- **违反规则**：Web Interface Guidelines — Navigation
- **改进建议**：全部替换为 `next/link`

#### UI-04：`announcement-list.tsx` 筛选状态未反映在 URL（未修复）
- **位置**：`src/modules/announcements/components/announcement-list.tsx:51-57`
- **问题**：`handleFilterChange` 使用 `router.replace(qs ? ?${qs} : ?)` 更新 URL ✅，但初始 `filter` 状态来自 `initialStatus` prop 而非 URL
- **改进建议**：使用 `useSearchParams` 读取 URL 状态

#### UI-05：`message-detail.tsx` 回复链接 URL 参数构建不严谨（未修复）
- **位置**：`src/modules/messaging/components/message-detail.tsx:69-71`
- **问题**：手动拼接 URL 参数，未使用 `URLSearchParams`
- **改进建议**：见 BUG-MD03

### 4.3 Forms

#### UI-06：`management/grade/insights/page.tsx` label 未关联 select（未修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:71`
- **问题**：`<label>` 缺少 `htmlFor`
- **违反规则**：Web Interface Guidelines — Forms
- **改进建议**：见 BUG-MI03

#### UI-08：`message-compose.tsx` 表单提交使用 `formData.set` 而非受控组件（未修复）
- **位置**：`src/modules/messaging/components/message-compose.tsx:46-49`
- **问题**：混合使用受控（`receiverId` state）和非受控（FormData）模式
- **改进建议**：统一使用受控组件或完全使用 FormData

### 4.4 Content & Copy

#### UI-09：中英文混合 UI（未修复）
- **位置**：
  - `ai-provider-settings-card.tsx`：BUG-AI01
  - `grade-classes-view.tsx`：BUG-GC03
  - `notification-preferences-form.tsx`：BUG-NPF03（注释）
- **违反规则**：Web Interface Guidelines — Consistency
- **改进建议**：统一为英文

#### UI-10：错误消息缺少修复步骤（未修复）
- **位置**：`src/app/(dashboard)/error.tsx:13`
- **问题**：`"We apologize for the inconvenience. An unexpected error occurred."` 未提供下一步操作
- **违反规则**：Web Interface Guidelines — Content & Copy
- **改进建议**：增加「联系管理员」链接或错误码展示

#### UI-11：`admin-settings-view.tsx` Tab 图标语义错误（未修复）
- **位置**：`src/modules/settings/components/admin-settings-view.tsx:50-53`
- **问题**：Appearance Tab 使用 Shield 图标
- **违反规则**：Web Interface Guidelines — Iconography
- **改进建议**：见 BUG-AS01

### 4.5 Accessibility

#### UI-12：`notification-list.tsx` icon 按钮缺少 `aria-label`（未修复）
- **位置**：`src/modules/messaging/components/notification-list.tsx:118-124`
- **问题**：「Mark as read」按钮文本存在，但图标按钮模式未统一
- **改进建议**：确保所有图标按钮有 `aria-label`

#### UI-13：`layout.tsx` 跳过链接样式冗长（未修复）
- **位置**：`src/app/(dashboard)/layout.tsx:12`
- **问题**：跳过链接使用大量 `focus:` 前缀类名，难以维护
- **改进建议**：抽取为独立样式或组件

### 4.6 Performance

#### UI-14：`management/grade/insights/page.tsx` 表单提交整页刷新（未修复）
- **位置**：`src/app/(dashboard)/management/grade/insights/page.tsx:70`
- **问题**：原生 form GET 提交导致整页刷新
- **违反规则**：Web Interface Guidelines — Performance
- **改进建议**：见 BUG-MI04

#### UI-15：`profile/page.tsx` 内联数据处理逻辑（未修复）
- **位置**：`src/app/(dashboard)/profile/page.tsx:59-108`
- **问题**：在组件内执行数组排序、过滤等耗时操作
- **改进建议**：移至 data-access 层

---

## 五、架构文档同步问题

### 5.1 [004_architecture_impact_map.md](../docs/architecture/004_architecture_impact_map.md)

#### DOC-01：announcements 模块未记录页面缺少权限校验（已过时 ✅）
- **位置**：004 文档 2.16 节
- **问题**：v1 报告中标记的「`app/(dashboard)/announcements/page.tsx` 完全缺少权限校验」已修复
- **改进建议**：更新架构文档，移除「缺少权限校验」的已知问题，标记为 ✅ 已修复

#### DOC-02：management 模块未在架构文档中独立记录（未修复）
- **位置**：004 文档
- **问题**：`app/(dashboard)/management/grade/` 路由未在架构文档中记录其依赖关系
- **改进建议**：补充 management 路由的模块依赖（classes、school）

#### DOC-03：settings 模块文件清单过期（未修复）
- **位置**：004 文档 2.23 节
- **问题**：记录 `components/* | 8 文件`，但实际有 8 个文件 ✅，需核对行数
- **改进建议**：核对并更新各文件行数

### 5.2 [005_architecture_data.json](../docs/architecture/005_architecture_data.json)

#### DOC-04：缺少 management 路由记录（未修复）
- **改进建议**：在 `routes` 数组中补充 management 路由

---

## 六、问题汇总统计

### v2 总体统计

| 严重度 | 数量 | 问题编号 |
|--------|------|----------|
| 高 | 7 | BUG-MI01, BUG-P01, BUG-P02, BUG-PC01, BUG-PS01, BUG-AI01, BUG-GC01 |
| 中 | 13 | BUG-D02, BUG-MI02, BUG-MI03, BUG-MI04, BUG-MSG02, BUG-P01(中), BUG-S01, BUG-L01, BUG-AL01, BUG-AD01, BUG-ML01, BUG-ML02, BUG-MD01, BUG-MD02, BUG-MC01, BUG-NL01, BUG-NPF01, BUG-AI02 |
| 低 | 12 | BUG-MSG03, BUG-P03, BUG-P04, BUG-P05, BUG-P06, BUG-S02, BUG-L02, BUG-E01, BUG-NF01, BUG-AC01, BUG-AD02, BUG-MC02, BUG-MC03, BUG-NL02, BUG-PC02, BUG-PC03, BUG-PS02, BUG-PS03, BUG-PS04, BUG-NPF02, BUG-NPF03, BUG-TP01, BUG-TP02, BUG-AI03, BUG-AI04, BUG-AS01, BUG-AS02, BUG-TS01, BUG-ST01, BUG-GC02, BUG-GC03, BUG-GC04 |
| 性能 | 5 | PERF-01, PERF-02, PERF-06, PERF-08, PERF-09 |
| 界面 | 12 | UI-01, UI-03, UI-04, UI-05, UI-06, UI-08, UI-09, UI-10, UI-11, UI-12, UI-13, UI-14, UI-15 |
| 文档 | 3 | DOC-02, DOC-03, DOC-04 |
| **合计** | **52** | |

### v1 → v2 修复进度

| 类别 | v1 数量 | v2 已修复 | v2 未修复 | 修复率 |
|------|---------|-----------|-----------|--------|
| 高严重度 | 9 | 2 | 7 | 22% |
| 中严重度 | 14 | 1 | 13 | 7% |
| 低严重度 | 13 | 1 | 12 | 8% |
| 性能 | 9 | 4 | 5 | 44% |
| 界面 | 15 | 3 | 12 | 20% |
| 文档 | 4 | 1 | 3 | 25% |
| **合计** | **64** | **12** | **52** | **19%** |

---

## 七、修复优先级建议（v2）

### P0（立即修复 — 影响安全与正确性，仍未修复）
1. **BUG-MI01**：`management/grade/insights/page.tsx` 权限校验升级为 `requirePermission()`
2. **BUG-PC01**：`password-change-form.tsx` 修复动态类名拼接（生产环境进度条无颜色）
3. **BUG-PS01**：`profile-settings-form.tsx` 移除 `as any`
4. **BUG-AI01**：`ai-provider-settings-card.tsx` 统一 UI 语言为英文
5. **BUG-P01**：`profile/page.tsx` 使用 `ctx.roles` 判断角色
6. **BUG-P02**：`profile/page.tsx` 抽取数据加载逻辑到 data-access
7. **BUG-GC01**：`grade-classes-view.tsx` 拆分组件

### P1（本迭代修复 — 影响可维护性与性能）
8. **BUG-S01**：`settings/page.tsx` 使用 `ctx.roles` 判断角色
9. **BUG-MSG02**：`messages/[id]/page.tsx` 使用 `after()` 延迟写操作
10. **BUG-AL01、BUG-AD01、BUG-MD01、BUG-MC01**：替换 `<a>` 为 `<Link>`
11. **BUG-ML01、BUG-NL01**：使用 `cn()` 替换字符串拼接
12. **PERF-01**：`usePermission` 回调 memoize
13. **UI-01**：权限相关 UI 改为 RSC prop 传入
14. **BUG-NPF01**：`notification-preferences-form.tsx` 修复 Switch/checkbox 双重切换
15. **BUG-PS02**：移除 `console.error`

### P2（下迭代修复 — 增强健壮性）
16. **BUG-MI02、BUG-MI03、BUG-MI04**：`management/grade/insights` 改用 shadcn Select
17. **BUG-PC02、BUG-PC03**：`password-change-form.tsx` 使用 `useRef` 替代 `document.getElementById`
18. **BUG-TS01、BUG-ST01**：抽取 `SettingsLayout` 共享组件
19. **BUG-AS01**：修复 admin Tab 图标语义
20. **UI-10**：错误页增加修复步骤
21. **BUG-GC03**：统一 `grade-classes-view.tsx` UI 语言
22. **BUG-NPF03**：统一注释语言

### P3（文档同步）
23. **DOC-01**：更新架构文档，标记 announcements 权限校验已修复
24. **DOC-02、DOC-04**：补充 management 路由记录
25. **DOC-03**：核对 settings 模块文件行数

---

## 八、验证命令

修复完成后应运行以下命令确保零错误：

```bash
npm run lint
npx tsc --noEmit
npm run test:unit
```

针对特定模块的端到端验证：

```bash
# 验证权限校验
curl -I http://localhost:3000/management/grade/insights  # 应返回 302 重定向到 /login
curl -I http://localhost:3000/profile  # 应返回 302
curl -I http://localhost:3000/settings  # 应返回 302

# 验证 hydration
# 在浏览器控制台检查无 hydration warning

# 验证 Tailwind 类名（BUG-PC01 修复后）
# 检查密码强度进度条在生产环境显示正确颜色
```

---

## 九、v2 新增发现

### 9.1 新增问题

#### NEW-01：`profile-settings-form.tsx` 错误处理改进但仍不完整
- **位置**：`src/modules/settings/components/profile-settings-form.tsx:57-61`
- **问题**：v1 中 `onSubmit` 仅 `toast.success`，v2 已增加 `result.success` 分支处理 ✅，但仍未处理 `result.errors`（字段级错误）
- **改进建议**：使用 react-hook-form 的 `setError` 设置字段级错误

### 9.2 修复质量评估

#### GOOD-01：`dashboard/page.tsx` 角色判断修复质量良好 ✅
- **位置**：`src/app/(dashboard)/dashboard/page.tsx:10-15`
- **评估**：v2 使用 `roles.includes("admin"/"student"/"parent")` 替代权限反推，逻辑清晰，优先级明确

#### GOOD-02：`management/grade/classes/page.tsx` 权限校验修复质量良好 ✅
- **位置**：`src/app/(dashboard)/management/grade/classes/page.tsx:9-10`
- **评估**：v2 使用 `requirePermission(GRADE_MANAGE)` 并通过 `ctx.userId` 获取用户 ID，消除了空字符串隐患

#### GOOD-03：`notification-list.tsx` button type 修复 ✅
- **位置**：`src/modules/messaging/components/notification-list.tsx:119`
- **评估**：v2 已添加 `type="button"`，防止意外表单提交

---

> 报告生成人：AI Agent（GLM-5.2）
> 核查方法：人工逐行审查 + 架构图比对 + 技能规则匹配 + v1 对比
> 应用技能：`vercel-react-best-practices`（65 条规则）、`web-design-guidelines`（Web Interface Guidelines）
> 前置版本：[others_bug.md](./others_bug.md) v1
> 修复进度：12/64（19%），其中高严重度修复 2/9（22%）
