外观
消息中心
字数
8212 字
预计阅读
32 分钟
系统给人发消息有五种方式:
| 方式 | 发给谁 | 收到的人在哪里看 |
|---|---|---|
| 通知公告 | 所有登录用户 | 右上角铃铛的"公告"页签 |
| 站内信 | 系统里的某个用户 | 铃铛的"站内信"页签、"我的消息"页面 |
| 邮件 | 用户的邮箱 | 自己的邮箱 |
| 短信 | 用户的手机号 | 手机 |
| 微信订阅消息(默认关闭) | 在小程序里订阅过提醒的用户 | 微信的"服务通知" |
公告由管理员手动撰写、发布。站内信、邮件和短信则是系统在发生某件事时自动发出的,比如有了新的审批待办。它们的内容来自模板:一段预先写好的文字,里面留着几个空位(占位符),发送时再填上实际的值。微信订阅消息跟着站内信走,用的是微信的模板,见微信订阅消息。
所有管理页面都在 系统管理 → 消息中心 下面。
菜单一览
| 菜单 | 作用 | 权限前缀 |
|---|---|---|
| 通知公告 | 撰写、发布、撤回公告,查看谁读过 | messaging.bulletin |
| 站内信模板 | 站内信的标题和正文 | messaging.inboxTemplate |
| 站内信 | 所有发出的站内信(只读) | messaging.inbox |
| 邮件账号 | 用来发信的邮箱服务器 | messaging.mailAccount |
| 邮件模板 | 邮件的主题和正文 | messaging.mailTemplate |
| 邮件记录 | 每封邮件的发送结果(只读) | messaging.mailRecord |
| 短信渠道 | 短信服务商(阿里云、腾讯云)的账号 | messaging.smsChannel |
| 短信模板 | 短信正文和服务商模板 | messaging.smsTemplate |
| 短信记录 | 每条短信的发送结果和回执(只读) | messaging.smsRecord |
完整的权限点是"前缀 + 动作",比如 messaging.mailTemplate.modify。动作有 browse(浏览)、view(查看)、create(新增)、modify(修改)、remove(删除),通知公告另有 publish(发布)。站内信和两个记录页面只有浏览和查看。在 系统管理 → 角色管理 中分配。
测试也需要"修改"权限
"测试发送"和"测试连接"会真的发出消息、连接外部服务器,所以要有对应页面的"修改"权限,每次测试都记入操作日志。列表里的启用开关同样需要"修改"权限。
用户这边
铃铛
页头右上角的铃铛上的数字 = 未读公告 + 未读站内信,超过 99 显示 99+。点开后有两个页签:
- 公告:最新 5 条已发布的公告;
- 站内信:最新 8 条站内信,底部有"查看全部消息"。
点一条,弹框显示内容,同时标记为已读。"全部已读"只对当前页签生效。
收到新站内信、公告发布或撤回时,铃铛立即更新,不用刷新页面,原理见实时推送。
我的消息
点铃铛里的"查看全部消息",或者首页"工作概览"中的"未读消息",进入 我的消息 页面。每个登录用户都能用,不需要分配菜单。
- 可以按阅读状态(全部、仅未读)和分类(系统、业务)筛选;
- 列表显示状态、标题、分类、发送人、接收时间。模板没有填发送方名称时,发送人显示"系统";
- 点"查看"阅读并标记已读;"全部已读"把剩下的一次标记完;
- 只能看到发给自己的消息。拿别人的消息编号去访问,返回 404,和消息不存在时一样。
站内信按纯文本显示:换行会保留,但内容不会被当作网页代码执行。
移动端的"消息"页也有公告入口和站内信列表,同样可以全部已读。收到新站内信时,移动端"消息"页签的角标也会通过实时推送跟着更新,见实时推送 · 移动端。
通知公告
在 系统管理 → 消息中心 → 通知公告 中管理:
- 公告有标题、类型(通知、公告)和富文本正文(可以设置格式的正文)。正文保存时会经过安全清洗,去掉脚本这类危险内容,见富文本;
- 新建的公告是草稿。打开列表里的"已发布"开关才算发布,关掉就是撤回,需要
messaging.bulletin.publish权限。发布、撤回或删除已发布的公告时,所有在线用户的铃铛立即刷新; - 公告发给所有登录用户,不能只发给某个部门或某些人;
- "已读用户"列出读过这条公告的人,只显示你的数据范围内的用户;
- 公告只有一种语言,不跟着界面语言切换。作者按读者的语言撰写即可。
模板
站内信、邮件、短信各有一个模板页面。一个模板靠模板编码识别,比如 wf.task.assigned。业务代码只认编码,所以管理员改了模板内容,不需要改代码。
占位符
在标题、主题和正文中,用 {参数名} 标出要填值的位置。比如审批待办的标题是 待审批:{initiator}的{model},发送时 {initiator} 换成发起人的名字,{model} 换成流程的名称。
- 参数名只能由英文字母、数字和下划线组成,不能用中文(写成
{发起人}不会被替换); - 发送时没有提供的参数,会原样保留
{参数名}; - 邮件正文是 HTML(网页格式)。填进去的值会被转义(当作普通文字处理),不会变成额外的网页代码;
- 时间按收件人的时区显示(
YYYY-MM-DD HH:mm),流程名称、字典值这类可以翻译的内容按收件人的语言显示。
多语言
同一个编码可以有多行,每种语言(简体中文、English)一行,"编码 + 语言"不能重复。发送时按收件人的语言选择;没有这种语言就用简体中文的,再没有就用这个编码任意一个启用的模板。所以要让某个渠道不再发这个编码,必须停用这个渠道下它的所有语言行。站内信模板可以用"更多 → 添加翻译"快速复制出另一种语言。详细规则见国际化 · 消息模板和国际化 · 时区。
停用模板和增加渠道
某个编码在某个渠道下没有启用的模板,这个渠道就不发。利用这一点,不改代码也能调整通知方式:
- 停用邮件模板
wf.task.assigned的全部语言行(预置了简体中文和 English 两行):有新待办时不再发邮件,站内信照发。只停用其中一行的话,收件人会改收另一种语言的邮件; - 新建一个编码为
wf.task.assigned的短信模板:有新待办时还会发短信(记得先在服务商那里申请好模板,见下文短信)。
只有"任务超时告警"固定只发站内信,"身份验证短信验证码"固定只发短信,加别的渠道的模板也不会发。
测试发送
模板列表中的"测试发送"会用这一行模板真的发一次,停用的模板也可以测:
- 弹框按模板里的占位符列出参数输入框(最多 20 个,每个值最多 500 字)。参数原样填入,不做翻译和时间格式化;
- 站内信只发给你自己(入口在"更多"菜单里);邮件要填"收件邮箱",短信要填"接收手机号";
- 邮件和短信发完会提示成功还是失败,并给出记录编号(如「测试邮件已发送,记录 #12」),可以到邮件记录、短信记录里查看详情;站内信只提示「测试消息已发送」或「测试消息推送失败」,不给编号。为了安全,页面上不显示服务商返回的原始错误。
三种模板的区别
| 站内信模板 | 邮件模板 | 短信模板 | |
|---|---|---|---|
| 标题 | 标题,最多 200 字 | 主题,最多 255 字 | 无 |
| 正文 | 纯文本 | 富文本(HTML) | 纯文本,最多 500 字 |
| 其他字段 | 分类(系统、业务)、发送方名称 | 邮件账号、发件人名称 | 短信渠道、用途、服务商模板 ID |
- 站内信的分类和发送方名称会显示在用户的消息列表里;
- 站内信模板的编码只能用小写字母、数字和下划线,以字母开头,并用点分成至少两段,比如
biz.order.shipped; - 邮件的发件人名称是收件人看到的名字,为空时用邮件账号的名称;
- 短信的用途(验证码、通知、推广)只用来分类和筛选。
邮件正文不能用内联样式
邮件模板正文和公告用同一套安全清洗规则,保存时会去掉 style 属性和脚本。所以排版要靠段落、标题、列表这些普通格式,不能靠内联样式。
邮件
邮件账号
发邮件用的是 SMTP(发送邮件的通用协议)。在 系统管理 → 消息中心 → 邮件账号 中添加发信用的邮箱服务器:
| 字段 | 说明 |
|---|---|
| 账号名称 | 不能重复 |
| 发件地址 | 收件人看到的发件邮箱 |
| 登录名、密码 | 登录 SMTP 服务器用。很多邮箱要用"授权码"代替登录密码 |
| 服务器地址、端口 | 比如 smtp.example.com 和 465 |
| 连接安全 | 和服务器之间怎样加密:SSL、STARTTLS(默认)、无加密 |
- 行上的"测试连接"会连接服务器并登录,只告诉你成功还是失败;
- 密码加密后保存,页面上永远读不回来。编辑时密码框留空,表示不修改;
- 修改服务器地址、端口、登录名或连接安全时,必须重新输入密码。这样,存好的密码只会发给它原本对应的服务器;
- 为了防止 SSRF(借服务器之手访问内网的攻击),服务器地址不能是内网、本机或云厂商元数据服务的地址,端口默认只允许 25、465、587。需要其他端口时,由部署人员在环境变量
OUTBOUND_SMTP_PORTS中添加,见环境变量 · 安全; - 正在被邮件模板使用的账号不能删除。
邮件模板用哪个账号
邮件模板的"邮件账号"可以留空,列表中显示为"默认账号",意思是使用最早添加的那个启用的账号。模板指定的账号被停用后,这个模板的邮件会发送失败(no_account),不会自动改用其他账号。
点邮件模板的名称可以查看详情,正文在一个隔离的预览框(沙箱 iframe)里显示,里面的脚本不会执行。
短信
短信渠道
一个渠道就是短信服务商的一个账号。在 系统管理 → 消息中心 → 短信渠道 中添加。"驱动"决定对接哪家服务商,有三种:
| 驱动 | 需要填写 |
|---|---|
| 阿里云 | 短信签名、API Key(即 AccessKey ID)、API Secret(即 AccessKey Secret);区域可以不填,默认 cn-hangzhou |
| 腾讯云 | 短信签名、应用 ID(即 SdkAppId)、API Key(即 SecretId)、API Secret(即 SecretKey);区域可以不填,默认 ap-guangzhou |
| 调试(写日志) | 什么都不用填。不会真的发短信,只在服务端日志里写一行,适合开发和演示 |
- 缺少必填项时,保存会提示"短信渠道缺少服务商必填配置";
- API Secret 和回执密钥加密后保存,页面上读不回来,编辑时留空表示不修改;
- 行上的"测试连接"用这个账号调用一次服务商的查询接口,只告诉你成功还是失败。调试驱动总是成功;
- 腾讯云的手机号没有带国家码(
+开头)时,自动按中国大陆号码(+86)发送; - 正在被短信模板使用的渠道不能删除。
不要随意更换 APP_SECRET
邮件账号的密码、短信渠道的 API Secret 和回执密钥,都用环境变量 APP_SECRET 派生出的密钥加密。更换 APP_SECRET 之后,已经保存的这些密钥就解不开了,要逐个重新填写。
其他服务商
目前只内置阿里云和腾讯云。华为云、七牛云等没有内置,需要时可以照着这两个驱动增加一个。
服务商模板
国内的短信服务商要求每条短信都使用事先审核通过的模板,所以短信模板分两部分:
- 正文:写在本系统里,用于发送记录和调试驱动;
- 服务商模板 ID:在阿里云或腾讯云控制台申请到的模板编号。手机实际收到的内容由服务商的模板决定。
两家传参数的方式不一样:
- 阿里云按名字传:正文里的参数名要和阿里云模板里的变量名一致;
- 腾讯云按顺序传:顺序就是正文里各个占位符第一次出现的顺序,要和腾讯云模板里
{1}、{2}…… 的顺序对上。
短信模板的"短信渠道"可以留空,列表中显示为"默认渠道",意思是使用最早添加的、启用的、并且没有被验证码模板使用的渠道。找不到这样的渠道时不发送。
验证码模板(编码以 auth. 开头)例外:它们从不使用默认渠道。没选渠道时,列表和详情里用警告色显示"未配置(验证码不会发送)",而不是"默认渠道";表单里的下拉框用这句话作占位文字,下面的提示"验证码模板必须选择已启用的渠道;为安全起见不会回退到默认渠道"会变成警告色。见短信验证码。
回执
回执是服务商事后告诉我们"这条短信到没到对方手机"。配置方法:
- 在短信渠道中填写回执密钥,自己定一个足够长的随机字符串,保存;
- 再打开这个渠道的编辑框。回执密钥下面显示"服务商回执推送地址",形如
https://你的域名/api/messaging/sms/receipt/渠道编号?token=,渠道编号已经填好。回执密钥读不回来,所以要自己在末尾接上,得到完整地址https://你的域名/api/messaging/sms/receipt/渠道编号?token=回执密钥; - 在服务商控制台,把短信状态报告的推送地址设为这个完整地址;
- 之后短信记录中的"回执状态"会变成"已送达"或"送达失败",并记下回执时间。
新建渠道时还没有编号,这里只提示"保存后在此显示服务商的回执推送地址。"。
地址的开头取自你打开后台时用的网址。如果那是内网地址或 localhost,要换成服务商能访问到的公网域名。
没有填回执密钥、密钥不对或者渠道已停用时,这个地址一律返回 404。同一个来源 IP 每分钟最多推送 120 次(各渠道的回执地址合计计数),超出返回 429。
短信验证码
短信登录、短信找回密码、在个人中心更换手机号,都使用编码为 auth.sms_code 的短信模板(名称"身份验证短信验证码")。要让它们可用:
- 新建一个启用的短信渠道;
- 编辑模板
auth.sms_code:选择这个渠道,填写服务商模板 ID(阿里云模板里的变量名要是code,腾讯云模板里用{1}放验证码),保持启用。
验证码只走这个模板指定的渠道,不会回退到"默认渠道"。这是有意的安全设计,原因见下面的提示。所以验证码模板必须选择一个启用的渠道,否则验证码不会发出:
- 没选渠道:列表和详情里,短信渠道一栏用警告色显示"未配置(验证码不会发送)"。表单里的下拉框用这句话作占位文字,下面还有一行提示"验证码模板必须选择已启用的渠道;为安全起见不会回退到默认渠道",没选渠道时这行提示变成警告色;
- 选中的渠道后来被停用:界面上不会提示。停用渠道前请先检查验证码模板。
验证码没有发出时,登录页照样提示"已发送"。这也是有意的:不管手机号有没有注册、短信有没有发出,页面的回应都一样,别人就无法借此探测哪些手机号注册过。验证码的有效期、发送次数限制等见登录与账号 · 短信登录和相关参数。
只有超级管理员能改验证码的发送路径
能改验证码模板的人,就能把别人的登录验证码引到自己的服务商账号上,再从那里读出来。所以:
- 编码以
auth.开头(不区分大小写)的短信模板,以及它们使用的渠道,只有超级管理员能新建、修改、删除、启停和测试发送; - 其他人也不能让别的短信模板使用这些渠道,或者使用验证码模板的服务商模板 ID;
- 违反时返回 403。
验证码不会写进短信记录:记录里的验证码一律显示为 ******。
微信订阅消息
有新的审批待办时,可以用微信的一次性订阅消息提醒审批人:提醒出现在微信的"服务通知"里,点开进入小程序里这条审批的详情。这项功能默认关闭。
它跟着站内信走:一条站内信推送出去之后,下面几条都满足,就再发一条微信订阅消息:
- 参数
notify.wx_subscribe.enabled为true; - 服务端配置了小程序的 AppID 和 AppSecret(和微信小程序登录共用);
- 这条通知的模板编码在参数
notify.wx_subscribe.templates里配了模板 ID; - 收件人在这个小程序上绑定了微信,并且没有解绑。
只发一次:失败不重试,也不留发送记录(发送记录里没有微信这一项)。微信调用失败不影响站内信本身。AppSecret 和微信的访问凭证不会写进日志,也不会出现在任何接口的返回里。
用户怎么订阅
一次性订阅消息要用户自己同意。在小程序 我的 页点 新待办微信提醒,微信弹窗询问是否允许:
- 这一项只在微信小程序里显示,并且要满足:已经绑定微信、开关已打开、已配置模板。H5 和 App 上没有;
- 允许后提示「已开启,下一条新待办将通过微信提醒你」;拒绝或者没有成功时提示「未开启微信提醒」;
- 点一次,弹窗里允许的每个模板各对应以后的一条提醒(只配了新待办一项时,就是下一条新待办)。想继续收,就再点。在微信弹窗里勾选"总是保持以上选择"之后,再点不会弹窗;
- 剩余的次数由微信保存,系统不记录。没有次数时微信拒绝发送,系统直接跳过,不算失败。
待办被转办或者改派给别人时,新的办理人会收到新待办通知,所以也会再发一次微信提醒(如果他订阅过)。
启用步骤
- 在微信公众平台的"订阅消息"里选一个公共模板(比如"待办事项提醒"),记下模板 ID 和各个字段的键(比如
thing1、time2); - 确认服务端已经配置了
WX_MP_APPID和WX_MP_SECRET,见微信小程序登录。小程序后台开了 API 的 IP 白名单时,把服务器的出口 IP 加进去; - 在 系统管理 → 参数设置 中修改
notify.wx_subscribe.templates(名称"微信订阅消息模板(按通知编码)"),写法见下一节; - 把
notify.wx_subscribe.enabled(名称"微信订阅消息提醒")改为true。
开关关闭时,系统完全不请求微信,小程序里也不显示入口。
模板配置
notify.wx_subscribe.templates 是一段 JSON,按通知的模板编码各配一项。种子只预置了新待办 wf.task.assigned 这一项,它的模板 ID 是空的,所以开关打开前还要先填好 ID。每一项有这些内容:
| 项 | 说明 |
|---|---|
id | 微信的模板 ID。为空表示这个编码不发 |
page | 点开提醒时打开的小程序页面。预置的是这条审批的详情页 |
data | 模板每个字段填什么。键改成所选模板真实的字段键;值里可以用占位符,见下文。预置的是 thing1 填 {title}、time2 填 {time} |
state | 可以不填。点开提醒时打开哪个版本的小程序:developer(开发版)、trial(体验版)、formal(正式版,默认) |
- 可以用的占位符:
{title}(站内信的标题)、{time}(通知的时间,按收件人的时区显示为YYYY-MM-DD HH:mm),以及这条通知自己的参数(只限文字和数字,比如{instanceId}、{initiator}); - 微信对每类字段有长度限制,超出的部分自动截掉:thing 20 个字,name 10 个字,phrase 5 个字,character_string 和 letter 32 个字,symbol 5 个字;
- 其他通知(比如退回给发起人
wf.instance.sent_back)按同样的格式再加一项即可。小程序一次最多申请 3 个模板:去掉空的和重复的模板 ID 之后,只有按配置顺序排在前面的 3 个会出现在订阅弹窗里,后面的那些通知用户订阅不到,也就收不到微信提醒; - 字段键配错时,微信拒绝发送,服务端日志里记一条警告。
保存时不检查格式
参数页保存时不检查这段 JSON。格式不对时,所有微信提醒都不发,服务端日志里记一条警告。改完后留意一下日志。
上线前在真机上试一次
模板自带的自动测试用的是模拟的微信接口,只能证明请求的格式和开关是对的。真实的发送要用你自己的 AppID、AppSecret 和订阅模板,在小程序真机上走一遍:点"新待办微信提醒"并允许,再给这个用户派一条新待办,看微信"服务通知"里是否收到,点开能否进入审批详情。
发送记录
三个只读页面记录每一条消息的结果:站内信、邮件记录、短信记录。都可以按模板编码、状态和创建时间筛选,短信记录还可以按手机号和回执状态筛选。点开详情能看到填好参数后的完整内容,邮件正文同样在隔离的预览框里显示。
状态
| 页面 | 状态 |
|---|---|
| 站内信 | 待推送、推送中、已推送、推送失败 |
| 邮件记录、短信记录 | 待发送、发送中、已发送、发送失败、已跳过 |
站内信的"推送"指的是实时提醒。站内信本身在业务操作完成时就已经存进了收件人的"我的消息",推送失败也不影响查看。
什么时候发、失败了怎么办
- 业务操作成功提交之后才发送。操作失败回滚了,消息也不会发出;
- 发送失败不会让业务操作失败,只会记在记录里;
- 定时任务"派发待发送通知"(
notify.dispatch,每分钟一次)会补发还没发出和发送失败的消息,每条最多尝试 3 次(邮件记录、短信记录里的"发送尝试次数",站内信详情里的"发送次数")。原理见消息通知 · 什么时候真正发出去。
邮件和短信失败时,"错误信息"一栏是一个简短的代码:
| 错误信息 | 含义 |
|---|---|
no_address | 收件用户没有填邮箱或手机号,状态为"已跳过" |
no_account | 没有可用的邮件账号:一个启用的都没有,或者模板指定的账号已停用 |
no_channel | 没有可用的短信渠道 |
refused | 邮件服务器的地址或端口被安全规则拒绝 |
send_failed | 邮件服务器或短信服务商返回失败 |
interrupted | 发送途中服务停止,并且 3 次机会已经用完 |
没配邮件账号,邮件记录全是"发送失败"?
审批通知默认会发站内信和邮件。还没有添加邮件账号时,邮件记录会显示发送失败(no_account)。不打算发邮件的话,在 邮件模板 中把这些编码的简体中文和 English 两行都停用。
保留时间
站内信、邮件记录、短信记录和短信验证码,超过参数 audit.retention_days(默认 180 天)后,由定时任务"清理过期日志"彻底删除,见定时任务 · 内置任务。注意这也包括用户"我的消息"里的旧站内信。
哪些事件会发消息
| 事件 | 模板 | 发给谁 | 预置的渠道 |
|---|---|---|---|
| 有新的审批待办 | 流程待审批通知 wf.task.assigned | 新的办理人 | 站内信、邮件 |
| 待办不再需要你处理(流程被撤销、终止,任务被撤回、退回、转办等) | 流程待办取消通知 wf.task.canceled | 原来的办理人 | 站内信、邮件 |
| 退回给发起人修改 | 流程退回发起人通知 wf.instance.sent_back | 发起人 | 站内信、邮件 |
| 审批通过 | 流程已通过通知 wf.instance.approved | 发起人 | 站内信、邮件 |
| 审批驳回 | 流程已驳回通知 wf.instance.rejected | 发起人 | 站内信、邮件 |
| 抄送 | 流程抄送通知 wf.cc | 被抄送的人 | 站内信、邮件 |
| 催办 | 流程催办通知 wf.task.urged | 当前所有待办的办理人 | 站内信、邮件 |
| 待办超时 | 流程待办超时提醒 wf.task.overdue | 办理人 | 站内信、邮件 |
| 待办超时后由系统自动处理 | 流程待办超时处理通知 wf.task.timeout | 发起人、流程管理员、原来的办理人 | 站内信、邮件 |
| 定时任务执行超时 | 任务超时告警 scheduler.job.timeout | 所有启用的超级管理员 | 只发站内信 |
| 申请短信验证码 | 身份验证短信验证码 auth.sms_code | 填写的手机号 | 只发短信 |
- 审批通知不会发给触发这件事的人自己,比如你把待办转办给别人,你自己不会收到"待办取消"的通知;
- 审批没有预置短信模板,需要时按停用模板和增加渠道自己加;
- 微信订阅消息不在表里:它默认关闭,打开后跟着站内信发送,预置的只有"有新的审批待办",见微信订阅消息;
- 待办超时提醒和超时自动处理都由定时任务"发送待办超时提醒"每 5 分钟检查一次。节点的"到期后"选了自动通过、自动驳回或转给上级时,到期后直接处理并发"流程待办超时处理通知",不再给办理人发超时提醒;没人可转或处理失败时,改为提醒办理人,"流程待办超时处理通知"只发给发起人和流程管理员。设置方法见工作流 · 超时自动处理;
- 强制下线不是消息:它只通过实时推送让对方的页面马上退出,不会写站内信,见实时推送。
没有"按人关闭通知"的设置
目前用户不能自己选择不接收某类通知。要整体关闭某类通知的某个渠道,停用对应的模板(每种语言的那一行都要停用,否则会改用另一种语言发送)。微信订阅消息是例外:用户自己在小程序里订阅了才会收到。收件人的语言和时区来自他的账号,见国际化。
开发指南
- 消息通知:在代码里发站内信、邮件和短信,用种子预置模板,事务提交后才发送的原理
- 富文本:公告和邮件模板正文的编辑与安全清洗
- 实时推送:铃铛为什么能立即更新
- 相关功能:工作流 · 通知、登录与账号、定时任务、国际化、安全基线