12 KiB
公告和消息模块审计报告 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 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 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 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 L128 + L149 | sendNotification 内部调用 logNotificationSendBatch,sendBatchNotifications 又调用一次 |
"代码质量规则" — 重复逻辑 |
问题分析:
- L128:
sendNotification末尾调用logNotificationSendBatch(results, ...) - L149:
sendBatchNotifications末尾再次调用logNotificationSendBatch(flatResults) - 批量发送时每条通知的日志被记录两次
后果:日志数据重复,影响监控准确性;浪费存储。
2.5 腾讯云短信 SmsSdkAppId 配置混淆(P1)
| 位置 | 代码 | 违反规则 |
|---|---|---|
| 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 L260 | Promise<ActionState<import("./types").MessageDraft[]>> |
"TypeScript 规则 — 仅用于类型的导入必须使用 import type" |
| messaging/data-access.ts L285 | import("@/modules/notifications/types").Notification[] |
同上 |
后果:可读性差,不符合 TypeScript 规范。
2.7 组件层 as 断言轻微违规(P2)
| 位置 | 代码 | 违反规则 |
|---|---|---|
| message-list.tsx L66 | setTab(v as Tab) |
"禁止 as 断言" |
| notification-list.tsx L106 | Object.keys(TYPE_ICON) as NotificationType[] |
同上 |
后果:类型不安全;应使用类型守卫。
2.8 logNotificationSend 使用 console(P2)
| 位置 | 代码 | 违反规则 |
|---|---|---|
| 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(紧急,影响类型安全与架构合规)
- 修复
saveMessageDraftAction的as断言:使用类型守卫替代formData.get() as string | null,安全处理 File 类型。 - 公告列表页
resolveAudience下沉:将受众解析逻辑迁移到announcements/data-access.ts,新增getUserAnnouncementsPageData编排函数。
P1(重要,影响模块边界与功能正确性)
- 通知偏好 Action 迁移:将
getNotificationPreferencesAction/updateNotificationPreferencesAction从messaging/actions.ts迁移到notifications/actions.ts,使用通知相关权限。 - 修复
sendBatchNotifications重复日志:移除sendBatchNotifications中的重复logNotificationSendBatch调用。 - 修复腾讯云短信
SmsSdkAppId配置:新增SMS_SDK_APP_ID环境变量和smsSdkAppId配置字段。 - 消除内联类型导入:改为顶部
import type。
P2(优化,提升代码质量)
- 组件
as断言清理:使用类型守卫替代。 - 日志服务统一:
logNotificationSend改用统一日志接口(预留接口,当前保持 console 但加注释标记为 TODO)。
五、架构图同步说明
本次审计发现以下架构图需更新:
-
§2.13 messaging:
- 移除
getNotificationPreferencesAction/updateNotificationPreferencesAction(迁移至 notifications) - 更新
actions.ts行数(减少约 60 行) - 新增
saveMessageDraftAction类型安全改进说明
- 移除
-
§2.14 notifications:
- 新增
getNotificationPreferencesAction/updateNotificationPreferencesAction - 更新
actions.ts行数(增加约 60 行) - 修复
sendBatchNotifications重复日志说明 - 新增
smsSdkAppId配置字段说明
- 新增
-
§2.16 announcements:
- 新增
getUserAnnouncementsPageData编排函数 - 更新
data-access.ts行数 - 更新用户端列表页说明(使用编排函数)
- 新增
-
附录 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/updateNotificationPreferencesActionsrc/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 typesrc/modules/messaging/data-access.ts:L285 内联 import 改为顶部import type
V3-P2-7:组件 as 断言清理
文件:
src/modules/messaging/components/message-list.tsx:L66setTab(v as Tab)改为类型守卫src/modules/notifications/components/notification-list.tsx:L106Object.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:对应节点同步更新