SMS 与消息模板
短信能力在 oak-general-business 里分成了两个层次:
- 一层是“手机号登录、验证码发送”这类账号体系能力,它更多依赖
Passport和Token; - 另一层是“系统消息模板同步和消息类型映射”,这才是本章要重点讲的内容。
主要对象
这部分主要涉及:
SmsTemplateMessageTypeMessageTypeSmsTemplate
它们的关系很清晰:
SmsTemplate保存某个系统下、某个短信渠道的模板信息;MessageType定义业务消息类型;MessageTypeSmsTemplate负责把业务消息类型映射到具体短信模板。
组件
短信相关组件并不多,但都很实用:
src/components/config/upsert/smssrc/components/passport/smssrc/components/user/login/smssrc/components/messageTypeSmsTemplate/listsrc/components/messageTypeSmsTemplate/tab
也就是说,短信模块既包含模板管理,也直接连接着系统配置、登录方式配置和登录页本身。
config/upsert/sms 常用字段
这组组件编辑的是 System.config.Sms。当前源码里最关键的字段有:
mockSenddefaultOriginali[]tencent[]ctyun[]
其中:
mockSend打开后,发送验证码不会真的调用短信厂商接口defaultOrigin决定默认短信渠道
三类厂商配置的重点字段分别是:
1. 阿里云
accessKeyIdaccessKeySecretendpointapiVersiondefaultSignName
2. 腾讯云
secretIdsecretKeysmsSdkAppIdregionendpointdefaultSignName
3. 天翼云
accessKeysecurityKeyendpointdefaultSignName
源码里这组组件还有两个实现细节很值得直接告诉开发:
- 三个渠道都是“数组配置”,可以添加多组帐号
- 但真正默认发短信走哪家,还是看
defaultOrigin
所以项目里不要只配帐号不配 defaultOrigin,否则验证码发送链路很容易因为拿不到默认渠道而失败。
messageTypeSmsTemplate/list 常用参数
这是短信模板映射里最核心的一个管理组件,关键参数只有两个:
systemIdorigin
但它内部已经做了不少事情:
ready()时先拉当前系统、当前渠道下的smsTemplate- 同时调用
features.template.getMessageType()拉业务消息类型 - 点击“同步模板”时直接调用
features.template.syncSmsTemplate(systemId, origin) - 新建映射时默认给一条
messageType + templateId - 同一种
messageType在下拉里会被禁用,避免重复绑定
也就是说,这个组件并不只是一个普通 CRUD 表,它已经把:
- 同步远端模板
- 查看现有模板
- 维护消息类型与模板映射
这些步骤合在一起了。
messageTypeSmsTemplate/tab 适合放在哪里
tab 组件的关键参数是:
systemId
它内部会按固定渠道自动分三栏:
alitencentctyun
并且每个页签里都挂一个 messageTypeSmsTemplate/list。
这组组件最适合放在:
- 系统后台页
- 系统配置页
- 平台级短信模板管理页
而不是登录页本身。
passport/sms 的真实职责
src/components/passport/sms/index.tsx 在文档里也值得单独写出来,因为它不是登录页,而是系统登录方式管理页里的一个“短信登录配置卡片”。
它当前接收的核心输入其实不是散装字段,而是三样东西:
passportchangeEnabledupdateConfig
其中 passport.config 里,这个组件真正会维护的是:
mockSenddefaultOrigintemplateNamecodeDurationdigit
也就是说,账号体系里“短信登录”这一项真正依赖的,不只是系统级 System.config.Sms,还包括 passport 自己这份登录级配置:
- 验证码模板名是什么
- 验证码有效几分钟
- 验证码是几位
从 components/passport/index.ts 的实现看,它还会在保存前主动检查配置完整性。如果启用了短信登录,但:
- 没填
templateName - 没选
defaultOrigin
组件会直接给出 warning。也就是说,项目层如果复用整套 passport/* 管理页,很多“短信登录为什么不生效”的基础配置错误其实已经能在页面上提前暴露出来。
user/login/sms 常用参数
src/components/user/login/sms/index.ts 和 web 端渲染层当前最值得写进文档的参数有:
disabledurlcallbackallowPasswordallowEmailallowWechatMpsetLoginModedigit
其中真正影响登录流程的是:
url:登录成功后跳回哪里callback:登录成功后的自定义回调digit:验证码位数校验
而 allowPassword / allowEmail / allowWechatMp / setLoginMode 更偏小程序或多登录方式切换场景,决定这块表单是否要切到别的登录模式。
这组组件内部已经直接把两条关键链路接好了:
sendCaptcha()->features.token.sendCaptcha('mobile', mobile, 'login')loginByCaptcha()->features.token.loginByMobile(mobile, captcha)
也就是说,项目层不应该再自己调用短信厂商 SDK,也不应该自己拼验证码登录接口。
还有两个很实用的源码细节:
- 发送验证码有冷却时间,开发环境默认 10 秒,非开发环境默认 60 秒
- 冷却时间戳会写到本地缓存,所以组件刷新后也不会立刻重新允许发送
一个很容易忽略的对齐点
user/login/sms 的 digit 和 passport/sms 里配置的验证码位数,本质上应该保持一致。
否则前端校验按 4 位、后台实际按 6 位生成时,就会出现:
- 前端以为验证码合法,后台却认为长度不对
- 或者前端根本不允许提交正确验证码
这也是为什么更推荐项目层直接复用公共 passport 配置页和公共登录组件,而不是把验证码位数写死在业务页里。
前端 feature 与 aspect
这一章主要依赖的前端入口是 features.template,其中和短信相关的方法有:
syncSmsTemplate(systemId, origin)getMessageType()
对应的后端 aspect 则是:
syncSmsTemplategetMessageTypesendCaptchaByMobilesendCaptchaByEmail
其中:
syncSmsTemplate会直接调用具体短信渠道实现,把远端模板同步到本地的smsTemplate对象里;- 验证码发送虽然前端统一走
features.token.sendCaptcha(...),但后端最终落到的是sendCaptchaByMobile/sendCaptchaByEmail这两个 aspect。
还有一个很容易漏掉的实现细节:
src/aspects/sms.ts里的syncSmsTemplate(...)当前会按templateCode做“存在则 update,不存在则 create”- 但源码里“删除本地已失效模板”的逻辑目前是注释掉的
这意味着同步模板的真实语义更接近:
- 增量更新
- 增量新增
而不是“远端模板全量对齐本地模板”。
endpoint / watcher
短信模板这部分当前没有单独的 endpoint,也没有单独的 watcher。
这说明它的工作方式不是“被第三方平台回调驱动”,而是更适合通过后台管理动作或定期运维脚本来主动同步。
注入点
这一章的注入点在两个地方:
- 前端通过
create(...)注入features.template; - 后端通过
ogb0Aspects注入syncSmsTemplate和getMessageType。
所以只要你的项目已经接好了 oak-general-business,短信模板同步入口其实已经具备。
公共系统页已经有现成入口
oak-general-business/src/components/system/panel/web.pc.tsx 已经把短信模板页签接进系统后台了:
smsTemplate-list页签直接包messageTypeSmsTemplate/tab
这意味着如果项目本身已经复用 system/panel,往往不需要再额外写一页“短信模板管理台”。
项目中如何接入
短信能力在项目里主要通过两条线接入:
System.config.Sms配渠道和签名;features.template、features.token负责模板同步和验证码发送。
所以项目层通常不直接调短信 SDK,而是:
- 管理台同步短信模板;
- 登录/改密等页面调用
features.token.sendCaptcha(...)。
真实项目里通常怎么组织
从当前 haina-busi、taicang 的源码来看,没有再各自重写一套独立短信模板管理界面,更多是直接吃公共包和生成域能力。
这也符合这章的定位:
- 系统配置页负责配
System.config.Sms - 公共系统页签负责模板同步和映射
- 业务登录页只负责发验证码,不直接碰短信供应商细节
这种分法是最稳的。配置、模板、登录三层职责分得很清楚。
使用示例
1. 同步短信模板
src/features/template.ts 已经提供了直接入口:
await this.features.template.syncSmsTemplate(systemId, 'tencent');
对应的后台逻辑会调用 src/aspects/sms.ts,把模板同步到 smsTemplate 表。
1.1 在系统页里直接挂模板映射组件
如果你没有复用完整的 system/panel,最省事的做法通常是自己在系统配置页里直接挂:
<MessageTypeSmsTemplateTab
oakPath={`$system-smsTemplate-${systemId}`}
systemId={systemId}
/>
这样阿里云、腾讯云、天翼云三组模板映射就会自动分栏展示。
2. 发送短信验证码
项目登录页、改密页推荐统一走:
await this.features.token.sendCaptcha('mobile', mobile, 'login');
await this.features.token.sendCaptcha('mobile', mobile, 'changePassword');
这样实际短信渠道、签名、模板映射都由系统配置控制,项目层不需要关心底层短信厂商细节。
2.1 直接复用短信登录组件
如果项目登录页只想保留手机号验证码登录,最直接的包法通常就是:
<UserLoginBySms
oakPath="#LoginBySms"
url="/frontend/index"
digit={6}
/>
如果这个页面本身只是登录弹窗,也可以不用 url,改传 callback:
<UserLoginBySms
oakPath="#LoginBySms"
callback={() => this.setState({ visible: false })}
digit={6}
/>
使用建议
最推荐的理解方式是:
- 登录验证码的短信配置,看
Passport; - 系统消息的短信模板映射,看
SmsTemplate和MessageTypeSmsTemplate; - 真正同步远端模板,走
features.template.syncSmsTemplate(...)。
再补三条特别实用的注意事项:
defaultOrigin一定要和实际配置过的ali/tencent/ctyun帐号对应上,否则验证码发送时虽然链路通了,最终还是会在 provider 选择这里出问题。mockSend只是不真的调用短信厂商,不代表验证码表不写入;开发环境下它仍然会创建captcha记录。- 模板同步当前不会自动删掉本地旧模板,所以如果厂商后台删过模板,项目侧最好结合运维规则人工检查一次映射关系。
把这三件事分开,短信模块就不会再显得混乱。