feat(announcements,messaging): 公告与消息模块审计重构 — i18n + Error Boundary + a11y

- 新增审计报告 docs/architecture/audit/announcements-messages-audit-report.md
- 新增中英双语 i18n 字典 announcements.json / messages.json(11/13 个命名空间)
- 重构所有 announcements 和 messaging 组件接入 next-intl(useTranslations)
- 所有页面 page.tsx 使用 generateMetadata + getTranslations 替代硬编码 metadata
- 新增 7 个 error.tsx 错误边界(4 公告 + 3 消息),统一 EmptyState + i18n + 重试
- a11y 改进:announcement-card / message-list / notification-dropdown 添加 aria-label
- 同步架构图 004 和 005:i18n.messages 清单 + 已知问题修复记录
This commit is contained in:
SpecialX
2026-06-22 16:02:07 +08:00
parent 21c1e7a286
commit fde711ce46
30 changed files with 1085 additions and 261 deletions

View File

@@ -0,0 +1,323 @@
# 公告和消息模块审计报告
> 审查日期2026-06-22
> 审查范围:`src/modules/announcements/**`、`src/modules/messaging/**`、`src/modules/notifications/**`、`src/app/(dashboard)/announcements/**`、`src/app/(dashboard)/admin/announcements/**`、`src/app/(dashboard)/messages/**`
> 架构图参考:`docs/architecture/004_architecture_impact_map.md` §2.13 / §2.14 / §2.16、`docs/architecture/005_architecture_data.json`
---
## 一、现有实现概要
### 1.1 文件分布
| 层 | 路径 | 文件数 | 说明 |
|----|------|--------|------|
| 路由层 - 用户端公告 | `src/app/(dashboard)/announcements/` | 2 个 `page.tsx` + 1 个 `loading.tsx` | 列表 + 详情,所有角色共用 |
| 路由层 - 管理端公告 | `src/app/(dashboard)/admin/announcements/` | 2 个 `page.tsx` + 1 个 `loading.tsx` | 管理列表 + 编辑 |
| 路由层 - 消息 | `src/app/(dashboard)/messages/` | 3 个 `page.tsx` + 3 个 `loading.tsx` + 1 个 `error.tsx` | 列表 + 详情 + 撰写 |
| 模块层 - announcements | `src/modules/announcements/` | 4 个核心文件 + 5 个组件 | actions(296行) / data-access(197行) / types(61行) / schema(45行) |
| 模块层 - messaging | `src/modules/messaging/` | 4 个核心文件 + 6 个组件 | actions(312行) / data-access(246行) / types(52行) / schema(44行) |
| 模块层 - notifications | `src/modules/notifications/` | 6 个核心文件 + 5 个渠道文件 | actions(159行) / data-access(174行) / dispatcher(152行) / preferences(191行) / types(153行) |
### 1.2 数据流
```
[Route] /announcements/page.tsx
└─▶ announcements/data-access.getAnnouncements (status=published, audience={gradeId,classId})
└─▶ classes/data-access.getClassGradeId / getStudentActiveClassId / getStudentActiveGradeId
[Route] /admin/announcements/page.tsx
├─▶ announcements/data-access.getAnnouncements
├─▶ school/data-access.getGrades
└─▶ classes/data-access.getAdminClasses
(页面层直接编排 3 个模块的 data-access
[Route] /messages/page.tsx
├─▶ messaging/data-access.getMessages
└─▶ notifications/data-access.getNotifications
(页面层直接编排 2 个模块的 data-access
[Route] /messages/compose/page.tsx
└─▶ messaging/data-access.getRecipients
└─▶ classes/data-access.getStudentIdsByClassIds / getTeacherIdsByClassIds / getClassesByGradeId / getStudentActiveClassId
└─▶ users/data-access.getUserNamesByIds
[Action] announcements/actions.createAnnouncementAction
└─▶ notifications.sendBatchNotifications (发布公告时批量通知)
[Action] messaging/actions.sendMessageAction
└─▶ notifications.dispatcher.sendNotification (发消息时通知收件人)
```
### 1.3 架构图记录情况
`004_architecture_impact_map.md` 对三个模块的记录较为完整:
- §2.13 messaging记录了 P0-4 / P1-5 已修复的双向依赖问题,文件清单准确
- §2.14 notifications记录了渠道抽象和从 messaging 迁移的历史
- §2.16 announcements记录了模块职责和依赖关系
**但存在以下遗漏**
- 未记录 messaging 组件目录下 `notification-dropdown.tsx``unread-message-badge.tsx` 两个组件
- 未记录 announcements 模块的 `components/` 子目录5 个组件文件未在文件清单中列出)
- 未记录消息列表的客户端搜索行为(`getMessagesAction` 在客户端被调用)
- 未记录通知下拉菜单的 30 秒轮询机制
---
## 二、现存问题与原因分析
### 2.1 国际化完全缺失P0
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [announcements/components/announcement-list.tsx](file:///e:/Desktop/CICD/src/modules/announcements/components/announcement-list.tsx) L24-29 | `"All"` / `"Published"` / `"Draft"` / `"Archived"` 硬编码 | "所有用户可见文本必须适配 i18n使用 next-intl提取翻译键" |
| [announcements/components/announcement-detail.tsx](file:///e:/Desktop/CICD/src/modules/announcements/components/announcement-detail.tsx) L29-38 | `STATUS_LABEL` / `TYPE_LABEL` 全英文硬编码 | 同上 |
| [announcements/components/announcement-card.tsx](file:///e:/Desktop/CICD/src/modules/announcements/components/announcement-card.tsx) L9-28 | `STATUS_LABEL` / `TYPE_LABEL` 重复定义且硬编码 | 同上 |
| [announcements/components/announcement-form.tsx](file:///e:/Desktop/CICD/src/modules/announcements/components/announcement-form.tsx) L86,92,98,108 | `"New Announcement"` / `"Title"` / `"Content"` 等硬编码 | 同上 |
| [messaging/components/message-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-list.tsx) L81-88 | `"Inbox"` / `"Sent"` / `"Compose"` 硬编码 | 同上 |
| [messaging/components/message-detail.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-detail.tsx) L38,74,99-106 | `"From"` / `"To"` / `"Message"` / `"New"` / `"Read"` / `"Sent"` 硬编码 | 同上 |
| [messaging/components/message-compose.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-compose.tsx) L78,84,102,113 | `"Reply"` / `"New Message"` / `"To"` / `"Subject"` 硬编码 | 同上 |
| [messaging/components/notification-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-list.tsx) L25-30,69-70 | `TYPE_LABEL` 硬编码,`"Notifications"` 标题硬编码 | 同上 |
| [messaging/components/notification-dropdown.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-dropdown.tsx) L113 | `"Notifications"` / `"Mark all read"` 硬编码 | 同上 |
| `src/shared/i18n/messages/` | **无 `announcements.json` 或 `messages.json`** | 翻译文件结构不完整 |
| [i18n/request.ts](file:///e:/Desktop/CICD/src/i18n/request.ts) L22-29 | 未加载 announcements/messages 翻译文件 | 翻译文件未注册 |
**后果**:所有用户可见文本无法切换语言,中文用户看到全英文界面,严重影响 K12 学校教师/家长/学生的使用体验。同一组件中 `STATUS_LABEL` 重复定义card 和 detail 各一份),维护成本高。
### 2.2 角色硬编码与配置驱动缺失P0
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [layout/config/navigation.ts](file:///e:/Desktop/CICD/src/modules/layout/config/navigation.ts) L39 | `NAV_CONFIG: Partial<Record<Role, NavItem[]>>` 按角色分组 | "前端权限判断统一使用 `usePermission().hasPermission()`,严禁出现 `role === 'xxx'` 硬编码" |
| 同上 L99-103, L247-251, L307-311, L343-347 | admin/teacher/student/parent 各自配置 `Announcements``Messages` 导航项 | 配置未抽象,新增角色需复制粘贴 |
**后果**:新增角色(如 `grade_head` 已存在)无法享受公告/消息导航;导航配置按角色而非权限驱动,违反"配置驱动设计"原则。
### 2.3 架构分层页面层越权编排P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [admin/announcements/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/admin/announcements/page.tsx) L33-37 | 页面层 `Promise.all` 调用 announcements/school/classes 三个模块的 data-access | "app/ 只能调用 modules/ 的 Server Actions 和 data-access" — 虽语法允许,但编排逻辑应在模块 actions 层完成 |
| [messages/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/messages/page.tsx) L17-20 | 页面层并行调用 messaging 和 notifications 两个模块的 data-access | 同上 |
| [announcements/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/announcements/page.tsx) L27-76 | `resolveAudience` 函数包含 50 行业务逻辑(根据 dataScope 解析受众) | 纯逻辑应抽为 hooks 或 data-access 层函数 |
| announcements 模块无 `getAdminAnnouncementsPageData` 编排函数 | 缺失编排层 | "模块标准结构"要求 actions.ts 承担编排职责 |
**后果**:页面层臃肿、逻辑不可复用、不可测试;多个页面需要相同数据时需复制编排逻辑。
### 2.4 模块间组件耦合P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [messaging/components/notification-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-list.tsx) L16 | 直接 `import type { Notification, NotificationType } from "@/modules/notifications/types"` | "模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access只能通过注入的接口调用" |
| [messaging/components/notification-dropdown.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-dropdown.tsx) L27 | 同上,直接 import notifications 模块类型 | 同上 |
| [messaging/components/notification-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-list.tsx) L15 | 直接 import `../actions` 中的 `markAllNotificationsAsReadAction` / `markNotificationAsReadAction` | messaging 模块的 actions re-export 了 notifications 的 actions造成职责混乱 |
| [messaging/actions.ts](file:///e:/Desktop/CICD/src/modules/messaging/actions.ts) L196-248 | messaging 模块定义了 6 个通知相关 Action`getNotificationsAction` / `markNotificationAsReadAction` 等) | 通知 Action 应由 notifications 模块提供messaging 仅负责私信 |
**后果**messaging 和 notifications 模块在 UI 层和 Action 层深度耦合无法独立替换或测试notifications 模块的 UI 组件无法复用到其他场景。
### 2.5 错误边界缺失P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| `src/app/(dashboard)/announcements/error.tsx` | **缺失** | "每个独立的数据区块必须用 React Error Boundary 包裹" |
| `src/app/(dashboard)/announcements/[id]/error.tsx` | **缺失** | 同上 |
| `src/app/(dashboard)/admin/announcements/error.tsx` | **缺失** | 同上 |
| `src/app/(dashboard)/admin/announcements/[id]/error.tsx` | **缺失** | 同上 |
| `src/app/(dashboard)/messages/[id]/error.tsx` | **缺失** | 同上 |
| `src/app/(dashboard)/messages/compose/error.tsx` | **缺失** | 同上 |
| `src/app/(dashboard)/admin/announcements/loading.tsx` | **缺失**(仅有用户端 loading | 加载骨架屏不完整 |
**后果**:数据加载失败时整页崩溃,用户体验差;无权限访问时显示原始错误而非友好提示。
### 2.6 通知轮询性能问题P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [notification-dropdown.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-dropdown.tsx) L65-68 | 每 30 秒轮询 `getNotificationsAction` + `getUnreadNotificationCountAction` | "性能:优先使用 React Server Components 获取初始数据" |
| [unread-message-badge.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/unread-message-badge.tsx) L31-33 | 每 60 秒轮询 `getUnreadMessageCountAction` | 同上 |
| 两个组件未使用 RSC 初始数据 | 客户端首次渲染无数据,需等待轮询 | "客户端组件仅负责交互" |
**后果**:多用户同时在线时,每分钟产生大量无效请求;首屏渲染时无数据,显示空状态闪烁。
### 2.7 公告表单校验不足P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [schema.ts](file:///e:/Desktop/CICD/src/modules/announcements/schema.ts) L9-10 | `targetGradeId` / `targetClassId` 为 optional未根据 `type` 做条件必填校验 | "输入使用 Zod 验证,验证失败返回结构化错误" |
| [announcement-form.tsx](file:///e:/Desktop/CICD/src/modules/announcements/components/announcement-form.tsx) L49-54 | `type === "grade"` 时不强制选择年级,`type === "class"` 时不强制选择班级 | 同上 |
| [actions.ts](file:///e:/Desktop/CICD/src/modules/announcements/actions.ts) L43-61 | `resolveTargetUserIds``type === "grade"``targetGradeId` 为空时返回空数组,公告无人接收 | 数据完整性缺失 |
**后果**:管理员可能创建无受众的公告,发布公告后无人收到通知,且无任何错误提示。
### 2.8 消息列表搜索逻辑复杂且无分页 UIP1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [message-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-list.tsx) L38-58 | 客户端 `useEffect` + `setTimeout` 防抖搜索,但未取消已发出的请求 | "可测试性:数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks" |
| 同上 L71-74 | `filtered` 在客户端再次过滤 `displayMessages`,与已搜索结果重复过滤 | 逻辑冗余 |
| 同上 L17 | 初始加载 `pageSize: 50`,但无分页 UI超过 50 条无法查看 | "明确处理空数据、无权限、网络异常等边界状态" |
| [messages/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/messages/page.tsx) L18 | 一次性加载 50 条消息,无虚拟滚动 | 性能问题 |
**后果**:消息超过 50 条时用户无法查看历史;搜索逻辑与 UI 混合,无法单独测试。
### 2.9 无权限与空状态处理不友好P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| 所有页面 | `requirePermission` 抛出 `PermissionDeniedError` 后,由上层 `error.tsx` 处理,但无专门的无权限空状态 | "明确处理空数据、无权限、网络异常等边界状态" |
| [message-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-list.tsx) L116-127 | 空状态文本硬编码且未区分"无权限"与"无数据" | 同上 |
| [notification-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-list.tsx) L80-86 | 通知空状态未提供"去设置通知偏好"等引导操作 | 用户体验不完整 |
**后果**:用户无法区分"无数据"和"无权限",无法找到下一步操作引导。
### 2.10 可访问性问题P2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [message-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-list.tsx) L104-110 | 搜索框无 `aria-label`,仅靠 `placeholder` | "可访问性a11y语义化标签、ARIA 属性、键盘导航" |
| [notification-dropdown.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-dropdown.tsx) L139-144 | `DropdownMenuItem``onSelect` 阻止默认行为后手动调用 `handleMarkRead`,键盘导航时焦点处理不明确 | 同上 |
| [announcement-card.tsx](file:///e:/Desktop/CICD/src/modules/announcements/components/announcement-card.tsx) L66-72 | 整个 Card 作为链接,但无 `aria-label` 描述跳转目标 | 同上 |
| [notification-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/notification-list.tsx) L118-124 | "Mark as read" 按钮无 `aria-label`,屏幕阅读器无法识别 | 同上 |
**后果**:视障用户无法有效使用公告和消息功能,不符合 WCAG 2.1 AA 标准。
### 2.11 监控埋点缺失P2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [announcements/actions.ts](file:///e:/Desktop/CICD/src/modules/announcements/actions.ts) | 发布/归档/删除公告无埋点 | "监控:方案中预留关键操作埋点接口" |
| [messaging/actions.ts](file:///e:/Desktop/CICD/src/modules/messaging/actions.ts) | 发送/删除消息无埋点 | 同上 |
| [notifications/data-access.ts](file:///e:/Desktop/CICD/src/modules/notifications/data-access.ts) L167-173 | 仅 `console.info` 输出发送日志,无结构化埋点 | 同上 |
**后果**:无法追踪公告阅读率、消息回复率等关键指标;通知发送失败无法告警。
### 2.12 消息软删除无事务P2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [messaging/data-access.ts](file:///e:/Desktop/CICD/src/modules/messaging/data-access.ts) L180-191 | `deleteMessage` 执行两个独立的 UPDATEsenderDeletedAt + receiverDeletedAt无事务 | "安全性:所有敏感数据查询必须在 data-access 层结合当前用户权限过滤" |
| 同上 | 两个 UPDATE 之间可能部分失败,导致数据不一致 | 数据完整性问题 |
**后果**:发送方删除后接收方可能仍可见,或反之,造成数据不一致。
### 2.13 测试覆盖不足P2
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| `tests/e2e/announcements.spec.ts` | 仅 2 个测试(未登录重定向 + 登录后可见),无管理端测试 | "可测试性" |
| `tests/e2e/` | **无 messaging 模块 E2E 测试** | 同上 |
| `src/modules/announcements/` | 无单元测试 | 同上 |
| `src/modules/messaging/` | 无单元测试 | 同上 |
| `src/modules/notifications/` | 无单元测试 | 同上 |
**后果**:重构时无回归保障,关键业务逻辑(权限过滤、受众解析、通知分发)错误无法及时发现。
---
## 三、行业差距对比
### 3.1 公告模块差距
| 功能 | 行业优秀实践 | 当前状态 | 影响 |
|------|-------------|----------|------|
| 公告分类标签 | 支持自定义标签(紧急、活动、政策),可按标签筛选 | 仅 typeschool/grade/class和 status无标签 | 教师无法快速筛选紧急公告 |
| 已读回执 | 显示已读/未读用户列表,支持提醒未读 | 无已读回执,仅通知发送 | 管理员无法知道公告是否被阅读 |
| 富文本编辑 | 支持富文本、图片、附件 | 仅纯文本 Textarea | 公告内容单调,无法插入图片 |
| 定时发布 | 支持指定时间自动发布 | `publishedAt` 字段存在但表单未暴露 | 管理员无法提前安排公告 |
| 公告置顶 | 支持置顶重要公告 | 无置顶功能 | 重要公告可能被新公告淹没 |
| 多渠道推送 | 站内 + 短信 + 邮件 + 微信 | 已实现多渠道notifications 模块) | ✅ 已达标 |
| 评论互动 | 支持公告下评论或确认收到 | 无互动功能 | 无法收集公告反馈 |
### 3.2 消息模块差距
| 功能 | 行业优秀实践 | 当前状态 | 影响 |
|------|-------------|----------|------|
| 消息分组 | 按联系人分组显示对话 | 仅按时间列表,无对话分组 | 教师与同一家长的来回消息散落各处 |
| 实时推送 | WebSocket / SSE 实时推送 | 30/60 秒轮询 | 消息延迟最高 30 秒,服务器压力大 |
| 消息草稿 | 支持草稿自动保存 | 无草稿功能 | 用户意外离开页面内容丢失 |
| 附件支持 | 支持发送文件附件 | 仅纯文本 | 无法发送作业截图等 |
| 消息星标 | 支持标记重要消息 | 无星标功能 | 重要消息无法快速找回 |
| 消息模板 | 支持常用消息模板 | 无模板 | 教师重复输入相同内容 |
| 群发消息 | 支持按班级/年级群发 | 仅支持单发 | 教师需逐个发送通知 |
| 消息搜索 | 全文搜索 + 按联系人/时间筛选 | 仅关键词搜索 subject + content | 无法按联系人筛选历史消息 |
| 已读回执 | 实时显示对方已读状态 | 仅 `readAt` 字段,无实时更新 | 发送方不知道消息是否被看到 |
### 3.3 通知模块差距
| 功能 | 行业优秀实践 | 当前状态 | 影响 |
|------|-------------|----------|------|
| 通知分类管理 | 支持按类型分组(作业/成绩/公告/消息) | 仅按时间列表,类型仅作为 Badge | 用户无法快速找到特定类型通知 |
| 通知静音 | 支持单类通知静音 | 有 `quietHours` 但仅全局免打扰 | 用户想静音作业通知但保留成绩通知无法实现 |
| 通知归档 | 支持归档已处理通知 | 仅标记已读,无归档 | 通知列表越来越长 |
| 通知优先级 | 支持高/中/低优先级 | 无优先级 | 紧急通知被普通通知淹没 |
| 桌面推送 | 支持浏览器桌面通知 | 仅站内下拉 | 用户不打开页面就收不到通知 |
### 3.4 多角色体验差距
| 角色 | 痛点 | 当前状态 | 影响 |
|------|------|----------|------|
| admin | 公告管理需切换到独立页面 | `/admin/announcements``/announcements` 分离 | 管理员查看用户视角需切换路由 |
| teacher | 消息收件人列表无法搜索 | `MessageCompose` 仅 Select 下拉 | 班级多时难以找到目标家长 |
| parent | 无法主动给教师发消息 | 依赖 `getRecipients` 返回的列表 | 家长需等待教师先发消息才能回复 |
| student | 公告无"确认收到"按钮 | 仅被动查看 | 学校无法确认学生是否看到公告 |
---
## 四、改进优先级建议
### P0紧急影响核心功能与安全
1. **i18n 全覆盖**:创建 `announcements.json``messages.json` 翻译文件,重构所有组件使用 `useTranslations` 替换硬编码文本,更新 `i18n/request.ts` 加载新文件。
2. **消除角色硬编码**:将 `NAV_CONFIG` 改为权限驱动配置,公告和消息导航项仅声明 `permission`,不按角色分组。
3. **补充错误边界**:为所有缺失的页面添加 `error.tsx`,区分"无权限"、"未找到"、"网络错误"三种状态。
### P1重要影响架构与体验
4. **解耦 messaging 与 notifications**:将通知相关组件(`notification-list.tsx``notification-dropdown.tsx`)迁移至 notifications 模块messaging 模块仅保留私信组件;通过 Context 注入数据服务接口。
5. **页面编排下沉**:在 announcements 和 messaging 模块新增 `getAdminAnnouncementsPageData` / `getMessagesPageData` 编排函数,页面层仅调用单一函数。
6. **公告表单条件校验**:使用 Zod `superRefine` 根据 `type` 强制要求 `targetGradeId` / `targetClassId`
7. **消息列表分页与虚拟滚动**:添加分页 UI超过 50 条时支持加载更多;搜索逻辑抽离为 `useMessageSearch` hook。
8. **通知实时推送**:将 30 秒轮询替换为 SSE 或 WebSocket减少无效请求首屏使用 RSC 获取初始数据。
9. **消息软删除事务化**:使用数据库事务包裹 `senderDeletedAt``receiverDeletedAt` 更新。
### P2优化提升完整性与可维护性
10. **a11y 改进**:为搜索框、按钮、链接添加 `aria-label`;确保键盘导航完整。
11. **监控埋点**:在关键 Action 中预留 `trackEvent` 接口,记录发布公告、发送消息、标记已读等操作。
12. **测试覆盖**:补充 messaging 模块 E2E 测试;为 `resolveTargetUserIds``getRecipients``selectChannels` 等纯函数添加单元测试。
13. **行业功能补齐**:公告已读回执、消息分组对话、消息草稿、通知优先级(按业务优先级逐步实施)。
14. **架构图同步**:补充 announcements 组件目录、messaging 的 notification-dropdown/unread-message-badge 组件、客户端搜索行为、轮询机制。
---
## 五、架构图同步说明
本次审计发现架构图存在以下遗漏,需补充:
### 5.1 `004_architecture_impact_map.md` 需补充
**§2.13 messaging 模块文件清单**
- 当前记录:`actions.ts` 276 行 / `data-access.ts` / `schema.ts` 41 行
- 实际状态:`actions.ts` 312 行 / `data-access.ts` 246 行 / `schema.ts` 44 行 / `types.ts` 52 行
- **遗漏组件**`components/notification-dropdown.tsx``components/unread-message-badge.tsx` 未在文件清单中列出
- **遗漏行为**`notification-dropdown.tsx` 每 30 秒轮询、`unread-message-badge.tsx` 每 60 秒轮询
**§2.16 announcements 模块文件清单**
- 当前记录:仅列出 actions/data-access/schema/types
- **遗漏组件目录**`components/` 下 5 个组件(`admin-announcements-view.tsx``announcement-card.tsx``announcement-detail.tsx``announcement-form.tsx``announcement-list.tsx`)未列出
**§2.13 messaging 依赖关系**
- **遗漏**`messaging/components/notification-list.tsx``notification-dropdown.tsx` 直接 import `@/modules/notifications/types`,存在跨模块 UI 类型依赖
### 5.2 `005_architecture_data.json` 需补充
- `modules.messaging.components` 数组缺少 `notification-dropdown.tsx``unread-message-badge.tsx` 两个节点
- `modules.announcements.components` 数组完全缺失5 个组件节点未记录)
- `modules.messaging.exports` 缺少 `UnreadMessageBadge` 组件导出
- `routes` 节点中 `/messages` 路由的 `dataAccess` 字段未记录客户端搜索行为(`getMessagesAction` 在客户端被调用)
### 5.3 无需修改的部分
- §2.14 notifications 模块记录完整准确
- P0-4 / P1-5 修复历史记录准确
- 依赖矩阵§3中 messaging → notifications 的单向依赖记录正确