## 新增功能 ### 1. 屏幕阅读器兼容性增强(a11y) - 无障碍工具库:src/shared/lib/a11y.ts - aria-live Hook:src/shared/hooks/use-aria-live.ts - a11y 组件:skip-link/visually-hidden/focus-trap/aria-status - 增强 UI:table.tsx 系统性 ARIA role,dialog.tsx aria-modal - 审计文档:docs/accessibility/a11y-audit.md(WCAG 2.1 AA 清单) ### 2. 视觉回归测试 - 测试套件:tests/visual/(homepage + 3 个 dashboard) - 3 视口(desktop/tablet/mobile)× 2 主题(light/dark) - 动态元素遮罩,避免误报 - playwright.config.ts 新增 visual-chromium 项目 - 文档:docs/testing/visual-regression.md ### 3. 短信/微信推送渠道集成 - 新模块:src/modules/notifications/ - 4 个渠道:SMS(阿里云/腾讯云)、WeChat(公众号)、Email(SMTP)、In-App - 分发器按用户偏好并行多渠道发送 - 外部 SDK 动态 import,Mock 模式开发可用 - 文档:docs/notifications/channels.md ### 4. 漏洞扫描 CI 集成 - CI security-scan job:npm audit + Snyk + Trivy FS + OWASP ZAP - 独立工作流 security.yml:每周一深度扫描 + 容器镜像扫描 - 配置:suppressions.json + .trivyignore - 本地脚本:security-scan.sh/ps1 - 文档:docs/security/scanning.md(SLA 分级) ### 5. 灾备方案 - 脚本:backup-verify/backup-offsite-sync/dr-drill/failover/health-check - CI 增强:备份后校验+异地同步,每周灾备演练 - 独立工作流 dr-drill.yml:每周一凌晨 4 点自动演练 - 文档:docs/dr/dr-plan.md(RTO 4h/RPO 24h)+ dr-runbook.md(6 故障场景) ## 验证 - npx tsc --noEmit:0 错误 - npm run lint:0 错误 0 警告
7.8 KiB
通知渠道集成文档
本模块(src/modules/notifications)为系统提供多渠道通知发送能力,支持站内消息、短信、微信公众号模板消息和邮件四种渠道。
架构概览
调用方 (Server Action / 其他模块)
│
▼
dispatcher.ts ── 读取用户通知偏好 (notification_preferences)
│ ── 读取用户联系方式 (users.phone / users.email)
│
├── in_app (站内消息,总是启用)
├── sms (短信,smsEnabled && phone)
├── wechat (微信模板消息,pushEnabled && openId)
└── email (邮件,emailEnabled && email)
│
▼
data-access.ts ── logNotificationSend (console 日志)
模块结构
| 文件 | 职责 |
|---|---|
types.ts |
通知渠道类型定义(NotificationPayload, ChannelSendResult 等) |
channels/types.ts |
渠道发送者接口(NotificationChannelSender, ChannelRecipient) |
channels/sms-channel.ts |
短信渠道(阿里云/腾讯云/Mock) |
channels/wechat-channel.ts |
微信公众号模板消息渠道 |
channels/email-channel.ts |
邮件渠道(Nodemailer SMTP) |
channels/in-app-channel.ts |
站内消息渠道(复用 messaging data-access) |
dispatcher.ts |
通知分发器(按偏好选择渠道、并行发送) |
data-access.ts |
通知数据访问(偏好查询、联系方式查询、日志记录) |
actions.ts |
Server Actions(sendNotificationAction, sendClassNotificationAction) |
index.ts |
模块统一导出 |
渠道配置
1. 站内消息(in_app)
- 默认启用,无需配置。
- 复用现有
messaging模块的createNotification,写入message_notifications表。 - 用户可在站内通知中心查看。
2. 短信(SMS)
支持三种 Provider,通过 SMS_PROVIDER 环境变量选择:
| Provider | 说明 | 环境变量 |
|---|---|---|
mock(默认) |
开发环境模拟,仅记录日志 | 无需其他配置 |
aliyun |
阿里云短信 | SMS_ACCESS_KEY_ID, SMS_ACCESS_KEY_SECRET, SMS_SIGN_NAME, SMS_TEMPLATE_CODE |
tencent |
腾讯云短信 | 同上(复用相同变量名) |
模板变量替换:将 payload.title / payload.content 填入模板变量 title / content。
3. 微信公众号模板消息(wechat)
通过微信公众号 API 发送模板消息:
- 获取 access_token:
GET https://api.weixin.qq.com/cgi-bin/token(带缓存,提前 5 分钟刷新) - 发送模板消息:
POST https://api.weixin.qq.com/cgi-bin/message/template/send
环境变量:
| 变量 | 说明 |
|---|---|
WECHAT_APP_ID |
公众号 AppID |
WECHAT_APP_SECRET |
公众号 AppSecret |
WECHAT_TEMPLATE_ID |
模板消息 ID |
模板数据映射:
keyword1←payload.titlekeyword2←payload.contentkeyword3←payload.type
可通过 payload.metadata.wechatKeywords 自定义覆盖。
注意:当前
users表无wechat_open_id字段,微信渠道暂不会实际触发。扩展 schema 后在data-access.ts的getUserContactInfo中补充查询即可。
4. 邮件(email)
使用 Nodemailer 通过 SMTP 发送,支持 HTML 邮件模板(根据通知类型显示不同颜色)。
环境变量:
| 变量 | 默认值 | 说明 |
|---|---|---|
EMAIL_HOST |
- | SMTP 主机(配置后启用真实发送) |
EMAIL_PORT |
587 |
SMTP 端口(465 使用 SSL) |
EMAIL_USER |
- | SMTP 用户名 |
EMAIL_PASS |
- | SMTP 密码 |
EMAIL_FROM |
noreply@example.com |
发件人地址 |
Mock 模式(开发环境)
所有渠道均提供 Mock 实现,无需任何外部服务即可运行:
- SMS:
SMS_PROVIDER=mock(默认)→ 仅console.info记录 - WeChat: 未配置
WECHAT_APP_ID等 → 自动使用 Mock - Email: 未配置
EMAIL_HOST→ 自动使用 Mock - 站内消息: 始终真实写入数据库(无 Mock)
生产环境配置
阿里云短信示例
SMS_PROVIDER=aliyun
SMS_ACCESS_KEY_ID=LTAI5tXXXXXXXXXXXX
SMS_ACCESS_KEY_SECRET=XXXXXXXXXXXXXXXXXXXXXXXX
SMS_SIGN_NAME=智慧教务
SMS_TEMPLATE_CODE=SMS_123456789
微信公众号示例
WECHAT_APP_ID=wx1234567890abcdef
WECHAT_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WECHAT_TEMPLATE_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
邮件 SMTP 示例
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_USER=notification@example.com
EMAIL_PASS=xxxxxxxx
EMAIL_FROM=智慧教务 <notification@example.com>
使用方式
1. 通过 Server Action 调用
import { sendNotificationAction, sendClassNotificationAction } from "@/modules/notifications/actions"
// 发送给单个用户
await sendNotificationAction({
userId: "user-xxx",
title: "作业提醒",
content: "您有一份新作业待提交",
type: "info",
actionUrl: "/homework/123",
})
// 发送给班级所有学生(教师权限)
await sendClassNotificationAction("class-xxx", {
title: "考试通知",
content: "明天下午 2 点期中考试",
type: "warning",
actionUrl: "/exams/456",
})
2. 直接调用分发器(服务端)
import { sendNotification } from "@/modules/notifications"
await sendNotification({
userId: "user-xxx",
title: "成绩发布",
content: "您的数学成绩为 95 分",
type: "success",
})
渠道选择逻辑
分发器根据用户通知偏好(notification_preferences 表)和联系方式决定启用渠道:
| 渠道 | 启用条件 |
|---|---|
| in_app | pushEnabled(默认 true),总是兜底 |
| sms | smsEnabled && 用户有手机号 |
emailEnabled && 用户有邮箱 |
|
pushEnabled && 用户有 wechatOpenId |
通知偏好中的
homeworkNotifications/gradeNotifications等按通知类别控制,由调用方在构造 payload 前决定是否调用发送。
扩展新渠道
- 在
channels/下创建新文件,实现NotificationChannelSender接口:
import "server-only"
import type { NotificationChannelSender, ChannelRecipient } from "./types"
import type { NotificationPayload, ChannelSendResult, NotificationChannel } from "../types"
const channel: NotificationChannel = "your_channel" as NotificationChannel
class YourChannelSender implements NotificationChannelSender {
readonly channel = channel
async send(payload: NotificationPayload, recipient: ChannelRecipient): Promise<ChannelSendResult> {
// 实现发送逻辑
}
async sendBatch(items) { /* ... */ }
}
export function createYourSender(): NotificationChannelSender {
return new YourChannelSender()
}
- 在
types.ts的NotificationChannel类型中添加新渠道:
export type NotificationChannel = "in_app" | "email" | "sms" | "wechat" | "your_channel"
-
在
dispatcher.ts的SenderRegistry和selectChannels中注册新渠道。 -
在
index.ts中导出新的发送器工厂。
权限说明
sendNotificationAction: 需要MESSAGE_SEND权限sendClassNotificationAction: 需要MESSAGE_SEND权限,且教师只能给自己所教班级发送
项目无独立
NOTIFICATION_SEND权限点,复用MESSAGE_SEND(教师/管理员/年级主任均拥有)。
外部 SDK 依赖
所有外部 SDK 均使用动态 import,避免增加构建体积:
| 渠道 | SDK | 安装命令 |
|---|---|---|
| 阿里云短信 | @alicloud/dysmsapi20170525, @alicloud/openapi-client, @alicloud/credentials |
npm i @alicloud/dysmsapi20170525 @alicloud/openapi-client @alicloud/credentials |
| 腾讯云短信 | tencentcloud-sdk-nodejs |
npm i tencentcloud-sdk-nodejs |
| 邮件 | nodemailer |
npm i nodemailer @types/nodemailer |
Mock 模式无需安装任何 SDK,开发环境开箱即用。生产环境按需安装对应 SDK。