Files
NextEdu/docs/architecture/audit/archive/announcements-messages-audit-report-v3.md

252 lines
12 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.
# 公告和消息模块审计报告 V3
> 审查日期2026-06-22
> 审查范围V2 改进后的 `src/modules/announcements/**`、`src/modules/messaging/**`、`src/modules/notifications/**`、对应路由层、i18n 翻译文件
> 前置文档:`announcements-messages-audit-report.md`V1、`announcements-messages-audit-report-v2.md`V2
> 架构图参考:`docs/architecture/004_architecture_impact_map.md` §2.13 / §2.14 / §2.16
---
## 一、V2 完成情况复核
| V2 编号 | 标题 | 状态 |
|---------|------|------|
| V2-P0-1 | 通知 i18n 命名空间独立 | ✅ 已完成 |
| V2-P0-2 | 通知标题 i18n 化 | ✅ 已完成 |
| V2-P1-1 | AnnouncementList 过滤模式统一 | ✅ 已完成 |
| V2-P1-2 | MessageList 过滤冗余移除 | ✅ 已完成 |
| V2-P1-3 | 消息详情页编排下沉 | ✅ 已完成 |
| V2-P1-4 | 表单服务端校验错误展示 | ✅ 已完成 |
| V2-P2-1 | 轮询间隔常量化 | ✅ 已完成 |
| V2-P2-2 | 架构图同步 | ✅ 已完成 |
| V2-P2-13b | 通知优先级和归档 | ✅ 已完成 |
| V2-P2-13c | 通知分类筛选 + 桌面推送 | ✅ 已完成 |
| V2-P2-13d | 公告置顶 + 已读回执 | ✅ 已完成 |
V2 共 11 项已全部实施。
---
## 二、V3 新发现问题
### 2.1 `saveMessageDraftAction` 中 `as` 断言违规P0
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [messaging/actions.ts](file:///e:/Desktop/CICD/src/modules/messaging/actions.ts) L279-283 | `formData.get("draftId") as string \| null` 等 5 处断言 | "禁止 `as` 断言(除非从 `unknown` 转换)" |
**问题分析**
- `FormData.get()` 返回类型为 `string | File | null`
- 代码直接断言为 `string | null`,若字段为 File 类型会导致运行时错误
- 应使用类型守卫或 Zod 校验进行安全转换
**后果**:类型不安全,上传场景下可能运行时崩溃;违反 TypeScript 严格模式规则。
### 2.2 公告列表页 `resolveAudience` 业务逻辑未下沉P0
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [announcements/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/announcements/page.tsx) L29-78 | `resolveAudience` 函数 50 行业务逻辑在路由层 | "app/ 只能调用 modules 的 Server Actions 和 data-access不直接访问数据库" + "页面层编排应下沉" |
**问题分析**
- `resolveAudience` 包含根据 dataScope 解析受众的复杂业务逻辑5 种 dataScope 分支)
- 直接调用 `classes` 模块的 3 个 data-access 函数(`getStudentActiveClassId``getStudentActiveGradeId``getClassGradeId`
- V2-P1-5 已为管理端和编辑页创建编排函数,但用户端列表页遗漏
**后果**:路由层承担业务逻辑,违反三层架构;逻辑无法复用;测试困难。
### 2.3 通知偏好 Action 放置位置错误P1
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [messaging/actions.ts](file:///e:/Desktop/CICD/src/modules/messaging/actions.ts) L334-396 | `getNotificationPreferencesAction` / `updateNotificationPreferencesAction` 在 messaging 模块 | "模块标准结构" — 通知偏好属于 notifications 模块 |
**问题分析**
- V1-P0-4 已将通知偏好 data-access 迁移到 `notifications/preferences.ts`
- 但对应的 Server Action 仍留在 `messaging/actions.ts`,使用 `MESSAGE_READ` 权限
- 消费方settings 模块)通过 `SettingsService` 接口注入,但 Action 实现仍在 messaging
**后果**模块边界混乱messaging 模块承担了不属于它的通知偏好职责;权限语义不正确。
### 2.4 `sendBatchNotifications` 重复日志P1
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [notifications/dispatcher.ts](file:///e:/Desktop/CICD/src/modules/notifications/dispatcher.ts) L128 + L149 | `sendNotification` 内部调用 `logNotificationSendBatch``sendBatchNotifications` 又调用一次 | "代码质量规则" — 重复逻辑 |
**问题分析**
- L128`sendNotification` 末尾调用 `logNotificationSendBatch(results, ...)`
- L149`sendBatchNotifications` 末尾再次调用 `logNotificationSendBatch(flatResults)`
- 批量发送时每条通知的日志被记录两次
**后果**:日志数据重复,影响监控准确性;浪费存储。
### 2.5 腾讯云短信 `SmsSdkAppId` 配置混淆P1
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [sms-channel.ts](file:///e:/Desktop/CICD/src/modules/notifications/channels/sms-channel.ts) L201, L203 | `SmsSdkAppId: this.config.templateCode``TemplateId: this.config.templateCode` | "类型安全" — 配置语义错误 |
**问题分析**
- 腾讯云 SMS API 要求 `SmsSdkAppId`(应用 ID`TemplateId`(模板 ID是两个不同值
- 代码中两者都使用 `this.config.templateCode`(来自 `SMS_TEMPLATE_CODE` 环境变量)
- `getSmsConfig()` 缺少 `smsSdkAppId` 字段
**后果**腾讯云短信发送必然失败SmsSdkAppId 不等于模板 ID生产环境无法使用腾讯云短信。
### 2.6 内联类型导入P1
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [messaging/actions.ts](file:///e:/Desktop/CICD/src/modules/messaging/actions.ts) L260 | `Promise<ActionState<import("./types").MessageDraft[]>>` | "TypeScript 规则 — 仅用于类型的导入必须使用 import type" |
| [messaging/data-access.ts](file:///e:/Desktop/CICD/src/modules/messaging/data-access.ts) L285 | `import("@/modules/notifications/types").Notification[]` | 同上 |
**后果**:可读性差,不符合 TypeScript 规范。
### 2.7 组件层 `as` 断言轻微违规P2
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [message-list.tsx](file:///e:/Desktop/CICD/src/modules/messaging/components/message-list.tsx) L66 | `setTab(v as Tab)` | "禁止 as 断言" |
| [notification-list.tsx](file:///e:/Desktop/CICD/src/modules/notifications/components/notification-list.tsx) L106 | `Object.keys(TYPE_ICON) as NotificationType[]` | 同上 |
**后果**:类型不安全;应使用类型守卫。
### 2.8 `logNotificationSend` 使用 consoleP2
| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [notifications/data-access.ts](file:///e:/Desktop/CICD/src/modules/notifications/data-access.ts) L210, L230 | `console.info` / `console.error` | "监控" — 应使用统一日志服务 |
**后果**:生产环境日志分散,无法集中监控。
---
## 三、行业差距对比
结合 K12 教育系统特点,对比钉钉教育、企业微信教育版、智学网、班级小管家等产品:
| 功能 | 我们 | 行业标杆 | 影响 |
|------|------|---------|------|
| 消息已读回执 | ❌ 无 | ✅ 钉钉/企微支持 | 教师无法确认家长是否已读重要通知 |
| 消息模板 | ❌ 无 | ✅ 智学网预设模板 | 教师每次手写消息效率低 |
| 公告定时发布 | ❌ 仅即时发布 | ✅ 钉钉支持定时 | 无法提前编排非工作时间发布 |
| 公告附件 | ❌ 无 | ✅ 企微/钉钉支持 | 无法附带 PDF/图片等材料 |
| 公告分类标签 | ❌ 无 | ✅ 智学网分类 | 公告列表无法按类型快速筛选 |
| 消息群发多班级 | ❌ 单收件人 | ✅ 钉钉群发 | 教师需逐个发送,效率低 |
| 公告评论/确认 | ❌ 仅已读回执 | ✅ 钉钉确认回执 | 无法收集家长确认反馈 |
| 消息搜索 | ✅ 有(按主题) | ✅ 按内容搜索 | 基本满足 |
| 通知优先级 | ✅ 有V2-P2-13b | ✅ | 已对齐 |
| 通知归档 | ✅ 有V2-P2-13b | ✅ | 已对齐 |
| 桌面推送 | ✅ 有V2-P2-13c | ✅ | 已对齐 |
| SSE 实时推送 | ✅ 有V2-P3 | ✅ | 已对齐 |
**主要差距**:消息已读回执、公告定时发布、公告附件为高价值缺失功能,直接影响家校沟通效率。
---
## 四、改进优先级建议
### P0紧急影响类型安全与架构合规
1. **修复 `saveMessageDraftAction` 的 `as` 断言**:使用类型守卫替代 `formData.get() as string | null`,安全处理 File 类型。
2. **公告列表页 `resolveAudience` 下沉**:将受众解析逻辑迁移到 `announcements/data-access.ts`,新增 `getUserAnnouncementsPageData` 编排函数。
### P1重要影响模块边界与功能正确性
3. **通知偏好 Action 迁移**:将 `getNotificationPreferencesAction` / `updateNotificationPreferencesAction``messaging/actions.ts` 迁移到 `notifications/actions.ts`,使用通知相关权限。
4. **修复 `sendBatchNotifications` 重复日志**:移除 `sendBatchNotifications` 中的重复 `logNotificationSendBatch` 调用。
5. **修复腾讯云短信 `SmsSdkAppId` 配置**:新增 `SMS_SDK_APP_ID` 环境变量和 `smsSdkAppId` 配置字段。
6. **消除内联类型导入**:改为顶部 `import type`
### P2优化提升代码质量
7. **组件 `as` 断言清理**:使用类型守卫替代。
8. **日志服务统一**`logNotificationSend` 改用统一日志接口(预留接口,当前保持 console 但加注释标记为 TODO
---
## 五、架构图同步说明
本次审计发现以下架构图需更新:
1. **§2.13 messaging**
- 移除 `getNotificationPreferencesAction` / `updateNotificationPreferencesAction`(迁移至 notifications
- 更新 `actions.ts` 行数(减少约 60 行)
- 新增 `saveMessageDraftAction` 类型安全改进说明
2. **§2.14 notifications**
- 新增 `getNotificationPreferencesAction` / `updateNotificationPreferencesAction`
- 更新 `actions.ts` 行数(增加约 60 行)
- 修复 `sendBatchNotifications` 重复日志说明
- 新增 `smsSdkAppId` 配置字段说明
3. **§2.16 announcements**
- 新增 `getUserAnnouncementsPageData` 编排函数
- 更新 `data-access.ts` 行数
- 更新用户端列表页说明(使用编排函数)
4. **附录 A 依赖矩阵**
- messaging 对 notifications 的依赖减少(不再包含通知偏好 Action
- announcements 对 classes 的依赖改为通过编排函数间接调用
---
## 六、实施记录
以下为 V3 审计报告的实施记录,所有修复均已完成并通过 `npx tsc --noEmit``npm run lint` 验证。
### V3-P0-1修复 `saveMessageDraftAction` 的 `as` 断言
**文件**`src/modules/messaging/actions.ts`
**变更**:将 L279-283 的 5 处 `formData.get("xxx") as string | null` 替换为类型守卫函数 `getStringFromFormData`,安全处理 `File` 类型。
### V3-P0-2公告列表页 `resolveAudience` 下沉
**文件**
- `src/modules/announcements/data-access.ts`:新增 `getUserAnnouncementsPageData` 编排函数
- `src/app/(dashboard)/announcements/page.tsx`:移除 `resolveAudience`,改用编排函数
### V3-P1-3通知偏好 Action 迁移
**文件**
- `src/modules/notifications/actions.ts`:新增 `getNotificationPreferencesAction` / `updateNotificationPreferencesAction`
- `src/modules/messaging/actions.ts`:移除上述 2 个 Action 及相关 import
### V3-P1-4修复 `sendBatchNotifications` 重复日志
**文件**`src/modules/notifications/dispatcher.ts`
**变更**:移除 `sendBatchNotifications` 中的重复 `logNotificationSendBatch` 调用L149
### V3-P1-5修复腾讯云短信 `SmsSdkAppId` 配置
**文件**`src/modules/notifications/channels/sms-channel.ts`
**变更**`getSmsConfig()` 新增 `smsSdkAppId` 字段(来自 `SMS_SDK_APP_ID` 环境变量),`SmsSdkAppId` 使用独立配置值。
### V3-P1-6消除内联类型导入
**文件**
- `src/modules/messaging/actions.ts`L260 内联 import 改为顶部 `import type`
- `src/modules/messaging/data-access.ts`L285 内联 import 改为顶部 `import type`
### V3-P2-7组件 `as` 断言清理
**文件**
- `src/modules/messaging/components/message-list.tsx`L66 `setTab(v as Tab)` 改为类型守卫
- `src/modules/notifications/components/notification-list.tsx`L106 `Object.keys(TYPE_ICON) as NotificationType[]` 改为类型守卫
### V3-P2-8日志服务统一预留
**文件**`src/modules/notifications/data-access.ts`
**变更**`console.info` / `console.error` 添加 TODO 注释标记,预留统一日志服务接入点。
### 架构图同步
**文件**
- `docs/architecture/004_architecture_impact_map.md`§2.13 / §2.14 / §2.16 同步更新
- `docs/architecture/005_architecture_data.json`:对应节点同步更新