Notification Starter
受众:开发者 摘要:统一通知 Facade。
log/memory/database/sms/websocket/webhook/
何时使用
| 场景 | 建议 |
|---|---|
| 站内信(落库 + 后台运维) | Notification.send("database", ...) |
| 实时在线推送(在线用户) | Notification.send("websocket", ...) |
| 邮件(统一发送日志) | Notification.send("mail", ...) |
| 短信(统一发送日志、重试) | Notification.send("sms", ...) |
| IM 机器人(钉钉 / 飞书 / Slack / Telegram / 自托管) | Notification.send("webhook", ...) |
| 微信公众号模板消息 / 客服消息、小程序订阅消息 | Notification.send("wechat", ...) |
| 后台 / 调试 | log / memory |
| 业务连续消息流(私聊、群聊、客服) | 用 Conversation API,不用 Notification |
| 直接短信(不入发送日志) | 直接 Sms.send(...),看 sms.md |
业务代码只调 Notification Facade,不直接绑定具体通道实现。
快速开始
Notification.send("database", NotificationMessage.builder()
.subject("通知")
.content("你的任务已经处理完成")
.recipient(NotificationRecipient.user(1L))
.build());多通道:
List<NotificationSendResult> results = Notification.send(
List.of("database", "websocket"),
NotificationMessage.builder()
.subject("订单状态变更")
.content("订单已支付")
.recipient(NotificationRecipient.user(1L))
.build());多通道发送会逐个通道尝试并返回每通道结果;某通道失败不阻断后续。单通道失败仍抛异常。
Facade API
// 单通道
Notification.send(String channel, NotificationMessage message);
// 多通道
Notification.send(List<String> channels, NotificationMessage message);NotificationMessage 关键字段:
| 字段 | 说明 |
|---|---|
subject | 标题 |
content | 正文(可走 i18n key) |
template | 模板编码(template) |
variable(k, v) / variables(map) | 模板变量 |
recipient(...) | 接收人(可多次) |
attribute(k, v) | 通道扩展属性(如 webhook.provider) |
NotificationRecipient 类型:
| 类型 | 构造 |
|---|---|
| 用户 | user(userId) |
| 邮箱 | email(email) |
| 手机 | mobile(mobile) |
| WebSocket 用户 | websocket(userId) |
| WebSocket session | websocketSession(sessionId) |
| WebSocket 分组 | websocketGroup(group) |
| WebSocket 访客 | websocketGuest() |
| 微信 openid | NotificationRecipient.of("wechat-openid", openid) |
| 主题 | topic(topic) |
配置项
spring:
open:
notification:
enabled: true
default-channel: log
channels:
log:
enabled: true
memory:
enabled: true
capacity: 1000
websocket:
enabled: true
mail:
enabled: true
sms:
enabled: true
database:
enabled: true
webhook:
enabled: false
default-provider: telegram
message-type: text
signing-enabled: true
providers:
telegram:
endpoint: ""
generic:
endpoint: ""
wechat:
enabled: false
template:
validate-before-send: true
fail-on-missing-variables: false| 配置 | 默认值 | 说明 |
|---|---|---|
enabled | true | 启用开关 |
default-channel | log | 默认通道 |
template.validate-before-send | true | 发送前模板变量校验 |
template.fail-on-missing-variables | false | 缺失变量是否阻断发送 |
channels.<x>.enabled | 按通道 | 启停通道 |
channels.webhook.default-provider | telegram | 默认机器人 |
channels.webhook.signing-enabled | true | 签名 |
channels.wechat.enabled | false | 启用微信通知通道 |
Provider
Channel
| Channel | 说明 |
|---|---|
log | 默认,写日志 |
memory | 测试,内存保留 |
database | 站内信,写 notification_message |
websocket | 在线用户实时推送 |
mail | 委托 Mail starter |
sms | 委托 SMS starter |
webhook | 机器人 / 第三方推送 |
wechat | 委托 微信生态 core,发送公众号模板 / 客服消息和小程序订阅消息 |
扩展:实现 NotificationChannel SPI Bean 即可新增通道。
WeChat 通道
wechat 通道由 spring-open-core-wechat 提供,默认关闭,启用后复用 微信生态 中的 WechatOfficialService 和 WechatMiniappService;不自研微信协议,也不在 Notification 模块保存微信密钥。
spring:
open:
notification:
channels:
wechat:
enabled: true消息类型:
wechat.message-type | 能力 | 底层服务 |
|---|---|---|
official-template | 公众号模板消息 | WechatOfficialService#sendTemplateMessage |
official-kefu-text | 公众号客服文本消息 | WechatOfficialService#sendTextKefuMessage |
miniapp-subscribe | 小程序订阅消息 | WechatMiniappService#sendSubscribeMessage |
常用 attribute:
| Attribute | 必填 | 说明 |
|---|---|---|
wechat.account-code | 是 | 微信账号 code,对应 spring.open.wechat 或 wechat_account 中的账号 |
wechat.message-type | 是 | 上表中的消息类型 |
wechat.to-user | 否 | 微信 openid;未传时读取第一个 wechat-openid 接收人 |
wechat.template-id | 模板 / 订阅消息必填 | 微信模板 ID;未传时回退 NotificationMessage.template |
wechat.url | 否 | 公众号模板消息跳转 URL |
wechat.client-message-id | 否 | 公众号模板消息 client msg id,未传时回退消息 ID |
wechat.kf-account | 否 | 指定公众号客服账号 |
wechat.page | 否 | 小程序订阅消息跳转页面 |
wechat.miniprogram-state | 否 | 小程序状态,如 developer / trial / formal |
wechat.lang | 否 | 小程序订阅消息语言;未传时使用消息 locale |
variables 会作为微信模板 / 订阅消息 data 值传入,值会转成字符串。微信发送结果仍写入 wechat_message_log 出站日志;Notification 发送日志记录通道级成败,便于统一重试和审计。
公众号模板消息:
Notification.send("wechat", NotificationMessage.builder()
.template("tpl_xxx")
.recipient(NotificationRecipient.of("wechat-openid", "openid_xxx"))
.variable("first", "订单已支付")
.variable("keyword1", "SO202606080001")
.attribute("wechat.account-code", "official-main")
.attribute("wechat.message-type", "official-template")
.attribute("wechat.url", "https://example.com/orders/SO202606080001")
.build());小程序订阅消息:
Notification.send("wechat", NotificationMessage.builder()
.template("subscribe_tpl_xxx")
.recipient(NotificationRecipient.of("wechat-openid", "openid_xxx"))
.variable("thing1", "订单已发货")
.attribute("wechat.account-code", "mini-main")
.attribute("wechat.message-type", "miniapp-subscribe")
.attribute("wechat.page", "pages/order/detail?id=1001")
.attribute("wechat.miniprogram-state", "formal")
.build());公众号客服文本:
Notification.send("wechat", NotificationMessage.builder()
.content("您好,客服已收到您的请求。")
.recipient(NotificationRecipient.of("wechat-openid", "openid_xxx"))
.attribute("wechat.account-code", "official-main")
.attribute("wechat.message-type", "official-kefu-text")
.build());后台发送元数据接口 GET /api/v1/notifications/send-metadata 会返回 wechat 通道、微信消息类型和字段清单;wechat.to-user 按敏感字段展示,避免在前端表单和日志中扩散 openid。
Webhook Provider
| Provider | 成功判断 |
|---|---|
generic | HTTP 状态码 |
dingtalk | HTTP 成功 + errcode/code == 0,支持 query 签名 |
wecom | HTTP 成功 + errcode/code == 0 |
feishu / lark | HTTP 成功 + code/errcode == 0,支持 payload 签名 |
slack | HTTP 成功 + 空响应或 ok |
discord | HTTP 成功(204 No Content) |
bark | HTTP 成功 + code=200 |
gotify | HTTP 成功 |
ntfy | HTTP 成功 |
push-plus | HTTP 成功 + code=200 |
server-chan | HTTP 成功 + code=0 |
telegram | HTTP 成功 + ok=true |
teams | HTTP 成功 |
mattermost | HTTP 成功 + 空响应或 ok |
签名密钥 / endpoint token / 请求头不写入失败原因。失败结果记录 provider / 第三方业务 code / 第三方消息 / 框架 hint。
Provider 单次覆盖
通过消息属性单次覆盖:
Notification.send("webhook", NotificationMessage.builder()
.subject("部署通知")
.content("生产环境发布完成")
.attribute("webhook.provider", "dingtalk")
.attribute("webhook.endpoint", "https://example.com/webhook")
.build());常见 attribute:webhook.provider / webhook.endpoint / webhook.message-type / webhook.secret / webhook.payload(完整覆盖)。
Provider 特定属性:
| Provider | 常用属性 |
|---|---|
| Bark | webhook.bark.device-key / group / level / sound / url / icon |
| Gotify | webhook.gotify.priority / extras |
| ntfy | webhook.ntfy.topic / priority / tags / click / attach / filename / actions / markdown |
| PushPlus | webhook.push-plus.token / topic / template / channel / option / to / callback-url |
| ServerChan | webhook.server-chan.channel / open-id |
| Telegram | webhook.telegram.chat-id / parse-mode(Markdown / MarkdownV2 / HTML) |
Webhook 配置示例:
spring:
open:
notification:
channels:
webhook:
enabled: true
default-provider: feishu
message-type: markdown
providers:
dingtalk:
endpoint: https://oapi.dingtalk.com/robot/send?access_token=your-token
secret: ${SPRING_OPEN_NOTIFICATION_DINGTALK_SECRET:}
feishu:
endpoint: https://open.feishu.cn/open-apis/bot/v2/hook/your-token
secret: ${SPRING_OPEN_NOTIFICATION_FEISHU_SECRET:}
wecom:
endpoint: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key
slack:
endpoint: https://hooks.slack.com/services/your/webhook/url
telegram:
endpoint: https://api.telegram.org/bot<bot-token>/sendMessage后台数据库配置可覆盖同名 key,优先级高于本地文件。敏感 endpoint 和 secret 建议放后台并标记敏感。
数据库配置常用 key
spring.open.notification.channels.webhook.enabled
spring.open.notification.channels.webhook.default-provider
spring.open.notification.channels.webhook.message-type
spring.open.notification.channels.webhook.signing-enabled
spring.open.notification.channels.webhook.providers.<provider>.enabled
spring.open.notification.channels.webhook.providers.<provider>.endpoint
spring.open.notification.channels.webhook.providers.<provider>.secret
spring.open.notification.channels.webhook.providers.<provider>.message-type
spring.open.notification.channels.wechat.enabled模板
Notification.send("database", NotificationMessage.builder()
.template("order.paid")
.variable("orderNo", "SO202605190001")
.recipient(NotificationRecipient.user(1L))
.build());默认检查缺失变量但不阻断。严格模式 template.fail-on-missing-variables: true。
按 code + channel + locale 匹配(详见 template.md)。通道 channel 优先,回退 all 通道模板。
业务事件集成
业务模块先发布领域事件,应用装配层调 Notification Facade,避免业务模块直接绑定通知实现。
| 模块 | 事件 | 默认通道 | 配置 |
|---|---|---|---|
| Mall | 订单状态、售后状态、虚拟交付库存预警、售后待审核规则告警、订单待履约规则告警 | database | spring.open.mall.notifications / Notification 规则 |
| Blog | 文章发布规则告警 | database | Notification 规则 |
| CMS | 内容审核驳回规则告警 | database / mail | Notification 规则 |
| Forum | 主题回复、点赞、收藏、回复点赞、举报处理进度 | database | spring.open.forum.notifications |
| Forum | 主题收到新回复规则告警 | database | Notification 规则 |
| Forum | 主题收到点赞规则告警 | database | Notification 规则 |
| Forum | 回复收到点赞规则告警 | database | Notification 规则 |
| Forum | 主题收到收藏规则告警 | database | Notification 规则 |
| Forum | 举报处理结果规则告警 | database | Notification 规则 |
| Short Link | 风险提示访问规则告警 | database / mail | Notification 规则 |
| Live Code | 分流兜底使用规则告警 | database / mail | Notification 规则 |
| Install | 安装完成 / 执行失败规则告警 | database / mail | Notification 规则 |
Forum 联动只在应用装配层完成,Forum 模块本身只发布事件。关闭:
spring:
open:
forum:
notifications:
enabled: falseForum 主题新回复、主题点赞、主题收藏、回复点赞和举报处理结果有两条装配链路:普通互动通知默认发给内容作者或举报人,受 spring.open.forum.notifications 控制;Notification 规则分别使用 forum.reply.created / forum.topic.liked / forum.topic.favorited / forum.reply.liked / forum.report.status-changed 事件编码,可在 /api/v1/notification-rules 配置运营接收人、通道、去重和静默窗口,未配置接收人时静默跳过。举报处理结果规则只处理非 pending 状态,避免和待审核提醒重复。
通知规则 Contract
Notification starter 只负责把消息发到指定通道;“什么事件达到阈值后该通知谁、是否去重、是否静默、是否升级”由 Notification 核心模块的通知规则 contract 承接。
可复制的最小用法:
NotificationRule rule = new NotificationRule(
"open-platform.balance-low",
true,
3,
Duration.ofMinutes(5),
Duration.ofMinutes(10),
Duration.ofMinutes(30),
List.of("database"),
List.of(1L),
new NotificationRuleEscalation(
true,
Duration.ofHours(1),
Duration.ofHours(2),
List.of("mail", "webhook"),
List.of(9L)));
NotificationRuleEvent event = new NotificationRuleEvent(
"open-platform.balance-low",
"app:1001",
Instant.now(),
3,
Duration.ofMinutes(5),
lastSentAt,
lastEscalatedAt,
Map.of("appId", 1001L));
NotificationRuleDecision decision = ruleResolver.resolve(rule, event);
if (decision.shouldSend()) {
dispatchService.send(new SendNotificationCommand(
decision.channels(),
decision.recipientUserIds(),
"open-platform.balance-low",
"余额不足",
"应用余额不足,请及时充值。",
"zh-CN",
event.dedupeKey(),
Map.of("appId", 1001L),
Map.of("dedupeKey", decision.dedupeKey())), dataScope);
}解析语义:
| 能力 | 说明 |
|---|---|
| 阈值 | thresholdCount 约束统计窗口内最小事件次数,thresholdWindow 用于校验调用方传入的统计窗口 |
| 去重 | dedupeWindow 内同一 dedupeKey 不重复发送 |
| 静默 | silenceWindow 内同一 dedupeKey 不再打扰接收人 |
| 升级 | escalation.after 到期后追加升级通道和升级接收人,escalation.dedupeWindow 防止升级通知刷屏 |
| 通道 | channels 为空默认 database,重复通道会去重并归一小写 |
| 接收人 | recipientUserIds 和升级接收人会去重;具体数据范围仍由发送 Service 校验 |
规则配置持久化由 spring-open-core-notification 承接,表名为 notification_rule。后台 API 前缀为 /api/v1/notification-rules,权限码为 notification:rule:view|create|update|delete。
| API | 说明 |
|---|---|
GET /api/v1/notification-rules | 分页查询规则配置,支持按启用状态和关键字筛选 |
GET /api/v1/notification-rules/templates | 查询内置规则模板,返回推荐编码、阈值、通道和升级策略 |
GET /api/v1/notification-rules/ops-summary | 查询规则总数、启用 / 停用、升级策略完整性、接收人配置、内置模板总体覆盖、按模板分类覆盖、待配置模板编码和升级缺口规则编码 |
POST /api/v1/notification-rules/export | 按当前筛选条件创建通知规则配置 CSV 导出异步任务,成功后通过任务产物下载接口获取文件 |
GET /api/v1/notification-rules/{id} | 查询规则详情 |
POST /api/v1/notification-rules | 创建规则配置 |
PUT /api/v1/notification-rules/{id} | 更新规则配置 |
POST /api/v1/notification-rules/{id}/enable | 启用规则 |
POST /api/v1/notification-rules/{id}/disable | 停用规则 |
DELETE /api/v1/notification-rules/{id} | 删除规则 |
DELETE /api/v1/notification-rules | 批量删除规则 |
规则配置字段会归一化为运行时 NotificationRule:通道默认 database,通道和接收人去重,阈值最小为 1,窗口秒数小于等于 0 时视为未配置。模板 API 只返回推荐配置,不创建或修改规则;运营摘要 API 只读聚合现有规则和内置模板,不修改规则状态。导出 API 会复用 Core Async Task 底座,创建 notification-rule.export 任务并写入 private CSV 产物,下载入口沿用异步任务的 artifact/download-url。升级策略完整性按“启用规则且启用升级”统计,存在升级通道或升级接收人任一配置即视为完整,否则返回 escalationIncompleteRuleCodes 供运维定位。Admin 后台 /#/notification-rules 已接入该 contract,支持规则分页筛选、新增 / 修改、启用 / 停用、删除 / 批量删除、通道 / 接收人配置、升级策略配置、从内置模板填充表单、顶部运营摘要、模板分类覆盖预览和升级缺口提示。当前 contract 已服务 Open Platform 余额不足、套餐耗尽、Key 异常、Provider 故障、限流、Queue 失败任务、Outbox 投递失败、补偿命令失败、Web3 扫描 / 结算失败、Scheduler 执行失败、异步任务失败、内容安全待复核和复核拒绝、Install 安装完成 / 执行失败、Mall 虚拟交付低库存和缺货、Mall 售后待审核、Mall 订单待履约、Blog 文章发布、CMS 内容审核驳回、Forum 主题新回复、Short Link 风险提示访问、Live Code 分流兜底使用等告警规则决策;更多业务事件源、更复杂升级策略和深度告警运营看板按后续功能簇推进。
后台接口
| API | 说明 |
|---|---|
GET /api/v1/notifications | 通知管理分页 |
GET /api/v1/notifications/inbox | 当前用户收件箱 |
GET /api/v1/notifications/inbox/unread-count | 未读数 |
GET /api/v1/notifications/preferences | 当前用户通知偏好 |
PUT /api/v1/notifications/preferences | 保存通知偏好 |
GET /api/v1/notifications/send-metadata | 后台发送页面元数据 |
POST /api/v1/notifications/send | 后台发送 |
POST /api/v1/notifications/template-preview | 发送前模板预览 |
PUT /api/v1/notifications/inbox/{id}/read | 标记已读 |
PUT /api/v1/notifications/inbox/{id}/unread | 标记未读 |
PUT /api/v1/notifications/inbox/read-batch | 批量已读 |
PUT /api/v1/notifications/inbox/unread-batch | 批量未读 |
PUT /api/v1/notifications/inbox/read-all | 全部已读 |
DELETE /api/v1/notifications/inbox/{id} | 删除自己的站内信 |
DELETE /api/v1/notifications/inbox | 批量删除自己的站内信 |
GET /api/v1/notification-send-logs | 发送日志;支持 retryableOnly=true 只看失败且可人工重试的日志 |
GET /api/v1/notification-send-logs/summary | 发送统计(总 / 成功 / 失败 / 可重试 / 已重试 / 近 24h 失败) |
POST /api/v1/notification-send-logs/{id}/retry | 失败重试 |
POST /api/v1/notification-send-logs/retry-batch | 批量失败重试 |
GET /api/v1/notification-message-attachments | 站内信附件 |
GET /api/v1/notification-read-receipts | 阅读回执 |
后台发送示例
POST /api/v1/notifications/send{
"channels": ["database"],
"recipientUserIds": [1, 2],
"template": "welcome",
"variables": { "username": "admin" }
}channels 为空默认 database。recipientUserIds 去重并按数据范围校验。模板、subject、content、variables 复用渲染和发送前校验。
批量重试
{ "ids": [1, 2, 3] }先校验所有日志都存在、失败、载荷可恢复,再逐条重试,避免部分发送后才发现某条不可重试。
发送日志列表和统计接口都支持 retryableOnly=true。当前 Notification 发送日志的“可重试”语义与人工重试 contract 一致:失败日志才进入可重试候选;成功日志不会出现在该筛选结果中。
短信发送运维
| 方式 | 是否进 Notification 发送日志 |
|---|---|
Sms.send(...) | 不进 |
Notification.send("sms", ...) | 进 |
需要后台查看 / 失败重试时用 Notification sms 通道:
GET /api/v1/notification-send-logs?channel=smsPOST /api/v1/auth/verify/sms-codes 验证码接口走 Notification sms 通道。
通知偏好
iam_user_preference 按 notification 分组保存。当前默认支持:
- 通道开关:站内信 / 邮件 / 短信 / WebSocket
- 业务分类开关:系统 / 账号 / 待办 / 聊天 / 博客 / 论坛 / 电商 / 营销
NotificationPreferenceResolver 同时判断通道和业务分类。已接入:
- 站内信实时推送尊重
websocketEnabled:关闭后不再推送notification.*WebSocket 事件 - 用户关系通知同时尊重
databaseEnabled+accountEnabled:关闭后好友申请、通过 / 拒绝、关注提醒不写收件箱 - 会话新消息尊重
databaseEnabled+chatEnabled+ 成员muted:免打扰仍进会话未读,但不写收件箱 database通道按消息属性module自动匹配业务分类(forum→forumEnabled,mall→mallEnabled)
其他业务应优先走 Notification Facade。确实需要直接写库时必须显式复用同一偏好解析器,并提供可观测日志、重试入口或明确降级策略,不在业务监听器静默吞异常。
通知跳转 Contract
站内信 attributes 携带稳定跳转目标。后端解析后写入 VO,并在 notification.* WebSocket 事件返回,三端不自行解析原始 JSON。
| 字段 | 说明 |
|---|---|
module | 业务模块(system / forum / mall) |
event / eventType | 事件编码(reply.created / order.status-changed) |
sourceType / sourceId | 触发来源对象 |
targetType / targetId | 点击后查看的目标对象 |
targetRoute | 语义化前端路由(/forum/topics/10) |
targetUserId | 用户关系类目标用户 |
dedupeKey | 业务幂等键 |
内置联动:
- 用户关系:好友申请、通过 / 拒绝、关注 → 对方资料
- Conversation:会话新消息 →
/chat/conversations/{id},带conversationType/messagePreview/ 发送者信息 - Forum:回复、点赞、收藏 → 主题;举报处理 → 举报记录
- Mall:订单状态 → 订单;售后 → 售后单
targetRoute 是跨端语义路由,不强制等于前端框架实际文件路径。App / PC / Admin 按自己路由表映射;REST 收件箱详情仍是事实来源。
站内信标题正文本地化
接口同时返回原始 subject / content 和 displaySubject / displayContent(当前语言)。显式 i18n key(messages.notification.demo.subject 或 {messages.notification.demo.subject})自动解析,无翻译时回退原文。
站内信附件
只保存元数据和 Storage 身份,不保存二进制内容,不依赖 file_record 关联:
{
"messageId": 1,
"disk": "local",
"path": "notifications/manual.pdf",
"name": "manual.pdf",
"contentType": "application/pdf",
"size": 1024
}返回时按当前 Storage 配置解析 disk + path 访问 URL。外部地址可直接传 url 兜底。
会话消息边界
| 用途 | 选 Notification 还是 Conversation |
|---|---|
| 通知 / 站内信 / 邮件 / 短信 / 机器人 | Notification |
| 用户连续消息流(私聊、群聊、客服) | Conversation(conversation_*) |
| 实时加速 | WebSocket,不作历史消息来源 |
会话 API 见 view-app.md / view-pc.md 中的 Conversation 用户端契约。会话开关:
spring:
open:
conversation:
enabled: true扩展点与源码骨架
Notification starter 的核心链路保持“业务 Facade → Manager 编排 → Channel SPI 发送 → Recorder 观测”:
| 类型 | 源码 | 说明 |
|---|---|---|
| 自动配置 | NotificationAutoConfiguration / NotificationMailAutoConfiguration / NotificationSmsAutoConfiguration / NotificationWebSocketAutoConfiguration | 注册默认 log / memory / webhook 通道、可选 mail / sms / websocket 集成、Manager 和发送记录器 |
| Facade | Notification | 面向业务代码的短名静态入口,只转发到 NotificationManager |
| Manager | NotificationManager / DefaultNotificationManager | 负责默认通道、多通道发送、模板渲染、失败语义和发送记录 |
| 消息 contract | NotificationMessage | 承载标题、正文、模板、接收人、变量、locale 和通道扩展属性 |
| 事件载荷 | NotificationEventPayload | 把业务领域事件归一成通知属性,不决定具体通道 |
| 通道 SPI | NotificationChannel | 新增通道时实现该接口,并注册为 Spring Bean |
| 记录 SPI | NotificationSendRecorder | 写入内存、数据库、审计或后续重试观测 |
| 配置解析 | NotificationConfigResolver / ConfigManagerNotificationRuntimeConfigSource | 读取本地配置和后台 ConfigManager 运行时覆盖配置 |
发送语义:
- 单通道发送失败会抛出异常,适合关键通知。
- 多通道发送按传入顺序逐个尝试,单个通道失败只返回失败结果,不阻断后续通道。
- 配置 Template starter 后,Manager 会按
code + channel + locale渲染;未配置模板或模板编码为空时直接使用subject/content。 attributes是通道私有信息、跳转目标、业务事件和发送诊断的扩展区,可能进入发送记录,不要放密钥、token 或明文密码。- 业务模块优先发布领域事件,由应用装配层转换为
NotificationMessage并选择通道,避免业务模块直接绑定某个通知实现。
开发约定
- 通知内容长期保存用
database通道 - 实时在线推送用
websocket,离线消息仍落库 - 第三方机器人密钥放环境变量或后台配置,不写入代码
- 各通道发送记录统一走
NotificationSendRecorder扩展点 - 业务模块只发领域事件,应用装配层调 Facade
- 直接写库时必须显式复用偏好解析器,不静默吞异常
- 跨端跳转用
targetRoute语义路由,不写死前端路径
验证命令
./mvnw -pl spring-open-starters/spring-open-starter-notification -am test -DskipITs
./mvnw -pl spring-open-core/spring-open-core-notification -am -Dtest='*Notification*Test' -Dsurefire.failIfNoSpecifiedTests=false test -DskipITs
./mvnw -pl spring-open-application -am test -DskipITs -DskipFrontend相关
- 模板与变量:template.md
- 邮件:mail.md
- 短信:sms.md
- WebSocket:websocket.md
- Notification 站内信与 Conversation 会话:本页、view-app.md、view-pc.md
- 多语言:i18n.md
- Provider 扩展模型:provider.md