做了七八年业务系统的人,几乎都绕不开发短信这件事。登录要验证码、下单要通知、账单要提醒、活动要触达,产品经理一句“加个短信通知”,活儿就落到后端身上。可真动手的时候,很多人第一次都会被卡在同一个地方:代码写完了,一调用就报签名不合法、模板不存在、余额不足,来回折腾一整天。我自己第一次接阿里云短信服务的时候,光是签名被驳回就重提了三次,模板变量写错格式又卡了半天,那种“明明照文档抄的却跑不通”的憋屈感印象很深。
这篇东西想干的事很朴素:把阿里云短信服务从开通到发出第一条短信的全流程拆开讲透,包括那些官方文档里写在角落、但真正会卡住你的细节。行文我会尽量往简单里说,把签名理解成信封上的落款、把模板理解成提前报备过的信件格式、把 AccessKey 理解成自家信箱的钥匙,没接触过云服务的人顺着读也能跟上;已经写过几版短信代码、但总在审核和错误码上栽跟头的朋友,可以直接跳到第三、第五部分看参数拆解和踩坑表。整条链路涉及账号开通、签名申请、模板申请、密钥管理、SDK 调用、流控设计和成本核算七个环节,我一环一环往下说。
1. 先搞清楚短信服务在解决什么问题
1.1 短信不是“发一条消息”那么简单
很多人对发短信的想象停留在这个层面:拿到对方手机号,调个接口,内容一传,短信就飞出去了。真做过就知道,中间隔着一整套审核与合规机制,这套机制不是平台故意为难人,而是被现实逼出来的。早年间短信通道管理松散的时候,垃圾短信、诈骗短信泛滥到什么程度,经历过那个阶段的人都有记忆。所以现在的短信服务,本质上是一个“带前置审查的定向投递系统”,你能发什么、发给谁、发多快,全都有约束。
理解这一点非常关键,因为它决定了你的开发顺序。新手最容易犯的错是先写代码后申请资质,代码写得漂漂亮亮,一跑就报错,然后回头去补签名和模板,中间还要等审核,白白浪费一两天。正确的顺序应该反过来:先把签名和模板这两个“通行证”拿到手,再动代码。这就像寄挂号信,你得先去邮局登记,而不是先在家里把信封糊好。
从技术定位上看,短信服务属于典型的“旁路能力”:它不参与你的核心业务链路,但几乎所有核心链路都需要它兜底。登录、支付、改密、注销,这些环节的短信是安全验证手段;订单、物流、账单,这些环节的短信是信息触达手段。两类场景对短信的要求完全不同,前者要求到达率和时效性极高、内容极短,后者可以稍慢但信息量更大。搞混这两类,后面模板设计和流控策略都会走偏。
1.2 四块积木:签名、模板、密钥、接口
把整套体系拆到最简,其实就是四块积木,缺一不可。
签名是短信开头那个方括号里的内容,比如【某某科技】。它的作用是告诉收信人“这条短信是谁发的”,同时也是平台追溯责任主体的依据。签名必须提前申请并审核通过,不能临时拼凑。你可以把它理解成信封上的落款和公章,没有落款的信件,收信人第一反应就是扔进垃圾箱。
模板是预先报备过的短信正文格式,里面可以留占位符,比如“您的验证码是${code},5分钟内有效”。为什么要报备?因为平台需要知道你发的内容长什么样,避免有人拿通道去发营销垃圾或者诈骗话术。模板审核通过后,你只能按这个格式发,变量部分可以由程序动态填充。这相当于邮局提前审阅了你的信件模板,之后你只能往里填名字和数字,不能改正文。
AccessKey是调用接口的身份凭证,一对 ID 和 Secret,作用等同于账号密码,但权限更大。它必须妥善保管,绝不能写进前端代码或者提交到公开仓库。我在实际项目里见过把 Secret 硬编码在 JS 里的,等于把家门钥匙挂在门把手上。
接口就是最后那一下调用,把手机号、签名、模板编号、变量值四个参数递过去,平台负责投递。四块积木的关系是:密钥证明你是谁,签名证明发给谁看,模板决定发什么内容,接口负责把这一切撮合起来。
1.3 哪些场景适合用,哪些别硬上
短信的能力边界得心里有数,不然方案选型就会拧巴。
适合的场景很明确:强时效 + 强触达 + 内容极短。验证码是典型代表,用户就在等着,晚十秒体验就崩。订单状态变更、支付结果、物流节点通知也属于这一类,用户对这类信息的容忍度高,打开率也高。还有一类是安全提醒,比如异地登录、密码修改,这类短信本身不承载业务,但承载信任。
不太适合的场景同样明确。大段内容推送别用短信,一条短信超过 70 个字就按多条计费,一条 200 字的通知成本是普通短信的三倍,而且阅读体验极差。营销推广要慎用,推广类短信的模板审核更严,需要额外的资质证明,而且用户反感度高,容易被投诉,投诉率一高账号就会被限流。高频实时通知也要考虑,比如每笔流水都发一条,用户一天收十几条,最后的结果就是把你拉黑。
我个人的判断标准是这样的:如果这条信息用户不看会导致业务无法继续,那用短信;如果只是“知道了更好”,那用站内信或者推送更划算。这个判断看着简单,但真到需求评审的时候,能省下不少预算。
2. 开通与准备:地基打牢再动手
2.1 账号开通与前期准备
第一步是把账号准备好。按平台引导完成注册和认证流程,个人主体和企业主体能申请的能力范围不一样,企业主体能申请的签名类型更多、模板类型也更全,如果项目是商用,建议直接走企业主体,省得后面来回折腾。
账号准备好之后,在产品列表里找到短信服务,点击开通。开通本身是免费的,不产生费用,只有实际发送才计费。这一点很多人误解,以为开通就要充值,其实不是。开通后进入控制台,你会看到国内消息、国际/港澳台消息、套餐包管理、发送记录这几个模块,国内业务主要用第一个。
接下来是计费方式的选择。默认是按量付费,发一条算一条的钱,适合刚起步、量不确定的阶段。如果业务量稳定,可以提前买套餐包,单价会更低。我的建议是前期一律按量付费,等跑满一个月,看清楚了真实的月发送量和失败率,再决定要不要买包。上来就囤一堆套餐包,结果业务没起来,包还有有效期,纯属浪费。
提示:开通后先把控制台的“发送量统计”和“费用中心”两个页面收藏起来,后面排查问题、核对账单全靠它们,比翻文档快得多。
还有一个容易被忽略的前置动作:把要接收短信的手机号先加进测试白名单(如果平台提供这个能力)。部分场景下新账号有发送限制,先小范围验证通道是否通畅,比一上来就群发稳妥得多。
2.2 签名申请:为什么你的总被驳回
签名是整个流程里驳回率最高的环节,我见过的驳回原因能列一长串,但归结起来就几类。
第一类是签名内容和主体对不上。签名必须能证明是你或你所在的组织,常见的证明方式是提供应用商店链接、公众号、小程序、已备案的网站,或者商标注册证明。很多个人开发者随手写个“XX助手”“XX服务”,又拿不出任何证明材料,必然被驳回。签名的本质是责任绑定,平台必须确认这个签名背后的主体真实存在。
第二类是签名踩了通用词的雷。纯数字不行,纯英文单词不行,“短信”“通知”“验证码”“客服”这类通用词汇也不行,因为这些词无法区分主体。有人图省事直接写【通知】,想着通用一点,结果就是被拒。签名长度一般要求 2 到 12 个字符,太长了展示不下,太短了没法识别。
第三类是签名涉及敏感或违规内容。这个不用多说,涉及金融、医疗、教育等特殊行业的签名,审核会更严格,需要补充对应的资质。如果项目属于这类行业,提前把资质材料准备好,能省一轮来回。
实操建议:签名尽量用公司全称或品牌全称,别用缩写和自造词。我见过一个项目签名写的是拼音缩写,审核员根本没法判断对应哪个主体,直接驳回。改成品牌全称后,一次就过了。另外,签名申请提交后一般几个小时内有结果,工作日的通过速度明显快于周末,赶项目的话别卡在周五下午提交,否则只能干等到下周一。
2.3 模板申请:变量写错一个字符都过不了
模板的审核比签名更细,因为它直接决定短信正文长什么样。这里有几个硬性规则必须记住。
模板内容必须包含变量,且变量格式严格。变量的写法是${变量名},花括号、美元符号、变量名三者缺一不可,中间不能有空格。我见过有人写成$ {code}或者${ code },肉眼几乎看不出差别,但审核系统识别不了,直接驳回。变量名建议用有意义的英文单词,比如 code、name、orderNo,别用 a、b、c,可读性差,后期维护也痛苦。
模板内容长度和变量数量都有上限。正文一般控制在 500 字符以内,变量数量别贪多,我实际操作下来,超过 5 个变量的模板可读性就很差了,用户看到一堆填充内容也懵。变量的替换值长度通常也有约束,太长会被截断或者直接报错,验证码这类内容控制在 6 位数字最稳妥。
模板类型要和内容匹配。验证码模板就是纯验证码,通知模板可以带业务信息,推广模板需要额外的退订方式和资质。很多人的模板被驳回,是因为把推广内容伪装成通知,比如“您的账户有一笔优惠待领取”,这种一眼就能看出是营销,审核不会放行。
下面这张表是我整理的模板类型对照,选模板时可以直接对着看:
| 模板类型 | 典型内容 | 变量示例 | 审核严格度 | 适用场景 |
|---|---|---|---|---|
| 验证码 | 您的验证码是 123456,5 分钟内有效 | code | 中 | 登录、支付、改密 |
| 通知 | 您的订单 20240001 已发货 | orderNo | 中 | 物流、账单、状态变更 |
| 推广 | 活动信息加退订方式 | name | 高 | 营销触达(需资质) |
| 公告 | 系统维护通知 | time | 中 | 服务变更告知 |
模板申请通过后,平台会给你一个模板编号,形如 SMS_一串数字,这个编号是调用接口时的必传参数,务必记下来。我的习惯是把它写进配置文件,而不是硬编码在代码里,因为不同环境(测试、生产)可能用不同模板,硬编码会把自己坑死。
2.4 密钥管理:这一环出错后果最严重
AccessKey 的创建很简单,控制台点几下就有,但它的管理是整条链路上风险最高的地方。
能创建子账号就绝不使用主账号密钥。主账号密钥权限是全量的,一旦泄露,等于把整个云上资产交出去。正确做法是创建一个子账号,只授予短信发送相关的权限,然后用子账号的 AccessKey 调用接口。这样即使密钥泄露,损失也被限制在短信范围内。这个操作多花十分钟,但能省掉未来可能的巨大麻烦。
密钥绝不能出现在三个地方:前端代码、公开代码仓库、日志输出。我见过最离谱的案例是某开发者调试的时候把 Secret 打印到日志里,日志又上传到了日志平台,等于公开放送。正确的做法是放在环境变量或者配置中心,代码里只读取变量名。
密钥要定期轮换。这不是形式主义,长期不换的密钥一旦在某个环节被记录过,风险就一直存在。轮换的做法是先创建新密钥,代码切换到新密钥,观察没问题后禁用旧密钥,整个过程可以做到零停机。
注意:如果发现密钥可能泄露,第一动作是立即在控制台禁用该密钥,而不是先改代码。代码改动需要时间和发布流程,禁用是一秒钟的事,先把门关上再处理别的。
3. 代码实操:从零跑通第一条短信
3.1 环境准备与 SDK 安装
准备工作做完,终于可以写代码了。这里我以 Java 和 Python 两种最常用的语言为例,其他语言的思路完全一致,照着 SDK 文档替换包名即可。
Java 项目用 Maven 引入依赖,核心是两个包:一个是短信服务的 SDK,一个是底层的网关 SDK。版本号建议直接去控制台文档页复制最新的,不要凭记忆写,版本对不上会出现类找不到的情况。
<dependency> <groupId>com.aliyun</groupId> <artifactId>dysmsapi20170525</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>tea-openapi</artifactId> <version>0.2.6</version> </dependency>Python 项目用 pip 装两个包,注意包名和导入名不太一样,这是新手容易踩的坑:
pip install alibabacloud_dysmsapi20170525 pip install alibabacloud_tea_openapi装完之后先别急着写业务代码,写一个最小的测试脚本,把第一条短信发出去。这个最小验证的价值非常高,它能把 SDK 依赖、网络、密钥、签名、模板这五个变量一次性验证到位。如果一上来就集成到业务框架里,出了问题根本分不清是框架的问题还是短信配置的问题。
3.2 核心参数逐个拆解
调用接口的核心参数就那么几个,但每个都有讲究,我逐个说。
Endpoint(服务接入点)是国内短信服务的固定地址dysmsapi.aliyuncs.com。这个一般不用改,但要注意区分国内和国际,发国际短信的地址不一样,写错了会一直报错。
PhoneNumbers(手机号)支持单个号码,也支持多个号码用逗号分隔批量发送,但批量发送有数量上限,一般一次不超过 1000 个。号码格式必须是纯数字的 11 位手机号,带加号、带横线、带空格都不行。我见过有人从 Excel 里复制号码,前面带了个不可见的空格字符,肉眼看不出来,接口直接报号码格式错误,排查了半小时。
SignName(签名)是你审核通过的那个签名,必须和申请的一字不差,包括方括号里的内容。注意,传参的时候不要带方括号,只传签名文字本身。这个细节坑过不少人,带上括号就会被判定为签名不合法。
TemplateCode(模板编号)是审核通过后平台给的那串编号,格式是 SMS_ 开头。同样必须一字不差。
TemplateParam(模板变量)是一个 JSON 字符串,键名必须和模板里的变量名完全对应。比如模板里写的是${code},那这个 JSON 就应该是{"code":"123456"}。如果模板里有两个变量,JSON 里必须两个都给,少给一个会导致短信内容里出现未替换的占位符,虽然有时能发出去,但内容很怪,用户看到会以为是诈骗。
下面是 Python 版的完整可运行示例,我把关键参数都加上了注释:
from alibabacloud_dysmsapi20170525.client import Client as DysmsClient from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_dysmsapi20170525 import models as dysms_models import json def create_client(key_id, key_secret): config = open_api_models.Config( access_key_id=key_id, access_key_secret=key_secret ) config.endpoint = "dysmsapi.aliyuncs.com" return DysmsClient(config) def send_code(client, phone, sign, template_code, code): request = dysms_models.SendSmsRequest( phone_numbers=phone, sign_name=sign, template_code=template_code, template_param=json.dumps({"code": code}, ensure_ascii=False) ) response = client.send_sms(request) return response.bodyJava 版的写法结构相似,核心是构造 Client 和 Request 两个对象:
Config config = new Config() .setAccessKeyId(System.getenv("SMS_KEY_ID")) .setAccessKeySecret(System.getenv("SMS_KEY_SECRET")); config.endpoint = "dysmsapi.aliyuncs.com"; Client client = new Client(config); SendSmsRequest request = new SendSmsRequest() .setPhoneNumbers("13800000000") .setSignName("某某科技") .setTemplateCode("SMS_123456789") .setTemplateParam("{\"code\":\"123456\"}"); SendSmsResponse response = client.sendSms(request); System.out.println(response.getBody().getCode());注意 Java 版本里密钥是从环境变量读的,不是硬编码,这是必须坚持的习惯。
3.3 返回结果怎么读,别把提交成功当成送达
接口返回的 JSON 里有几个关键字段,很多人只看 Code 是不是 OK,这是不够的。
| 字段 | 含义 | 怎么用 |
|---|---|---|
| Code | 请求处理结果 | 等于 OK 表示提交成功,非 OK 要查错误码表 |
| Message | 结果描述 | 排查时看这个,比 Code 更具体 |
| BizId | 发送回执编号 | 查发送记录、对账、排查到达问题的唯一凭据 |
| RequestId | 请求追踪编号 | 提工单时平台会要,一定要留 |
这里有个非常重要的认知差:Code 等于 OK 只代表平台接收了你的请求,不代表短信已经到达用户手机。真正的到达结果要等回执,而回执是异步的,可能几秒后到,也可能几十秒后到。所以做业务设计的时候,不能把“接口返回 OK”当作“用户已收到验证码”,只能当作“已提交成功”。
正确的做法是:接口返回 OK 后,把 BizId 存下来,前端提示“验证码已发送”,然后等用户输入。用户输入错误时,你可以通过 BizId 去查这条短信的实际状态,判断是用户输错了还是压根没收到。这个设计在客服排查的时候价值极高,没有 BizId 就只能让用户“再等等”或者“再发一次”。
另外提醒一句,手机号在日志里要脱敏。中间四位打码是基本操作,我见过把完整号码和验证码一起打进日志的,安全意识太薄。验证码更不能记日志,一旦日志泄露,等于所有账号都能被登录。
3.4 字数、条数与成本怎么算
短信计费是按条算的,而一条的长度是有严格定义的:包含签名在内的所有字符,70 个字以内算一条。超过 70 个字就会被拆成多条长短信,每 67 个字算一条。注意这个 67 和 70 的差别,长短信需要额外的拼接信息,所以单条容量反而变小了。
举个实际的算例。签名是【某某科技】共 6 个字符(含两个方括号),正文是“您的订单已发货,单号 SF1234567890,请注意查收”,数一下大约 28 个字符,加起来 34 个字符,远在 70 以内,一条搞定。如果正文写成了 80 个字符,加上签名 86 个字符,按 67 字一条拆,就要算两条,成本直接翻倍。
这个账看起来小,放到量上就很可观。假设一天发 10 万条通知,一条多算一毛钱,一天就是一万块,一个月三十万。所以我在做模板的时候有个硬规矩:正文控制在 50 字以内,签名控制在 8 字以内。这样怎么填充变量都不会超标。
变量填充长度也要计入总字数。一个变量看着只占几个字符,但实际替换后可能很长,比如订单号 18 位,商品名可能二三十个字。模板里出现商品名这类长度不可控的变量,特别容易触发长短信。我的做法是给变量做截断,超过一定长度就省略,宁可不完整也别翻倍计费。
提示:做模板时先在纸上把签名、正文、最长变量值都写出来,总字符数加起来数一遍,比上线后再改模板省事得多。
4. 上线前的加固:别让接口裸奔
4.1 验证码场景的三道防线
代码跑通只是起点,直接上线的短信接口基本等于给攻击者送钱。我见过最典型的事故是这样:接口没有频率限制,某个脚本一晚上刷了几万条验证码,账单直接五位数。
第一道防线是号码维度的频率控制。平台本身有默认的流控策略,对同一号码、同一签名、同一模板有分钟级、小时级、日级的发送次数限制,具体数值各账号可能不同,以控制台展示为准。但这个默认限制只能兜底,不能当成你的业务策略。你的业务层也应该有一层控制,比如同一手机号 60 秒内只允许发送一次,一天最多十次。
第二道防线是人机校验。发验证码之前先过图形验证或者行为验证,这一层能把绝大多数脚本挡在门外。成本很低,收益极高。有些项目为了体验把这一层省了,结果就是被刷到怀疑人生。
第三道防线是设备与 IP 维度的限制。同一 IP 或同一设备指纹在短时间内请求大量不同手机号,这基本可以判定是异常行为,直接拦截。这一层需要结合你的业务日志做,实现成本稍高,但对有规模的业务来说是必要的。
三道防线是层层递进的关系,不是三选一。只做号码限制,攻击者换号就绕过了;只做人机校验,遇到打码平台也能破。三层叠加,攻击成本才会被抬到不划算的程度。
4.2 错误处理与重试策略
短信接口的错误码分两类,处理方式完全不同,这个区分很多人没做对。
一类是“别重试”的错误,比如签名不合法、模板不存在、手机号格式错误、余额不足。这类错误重试一百次结果都一样,纯属浪费资源,正确做法是记录日志、告警、人工介入。余额不足这种更是要提前监控,别等用户投诉才发现欠费停机。
另一类是“可以重试”的错误,比如系统繁忙、网络超时。这类错误一般重试一到两次就能成功。但重试要加退避,比如第一次失败等 200 毫秒,第二次等 500 毫秒,不要连着猛刷,那样只会加重服务端压力,反而更容易失败。
重试还有个大坑:幂等性。如果第一次请求其实已经成功了,只是响应超时没收到,你重试一次就会给用户发两条一模一样的短信。用户收到两条验证码会懵,收到两条扣款通知会慌。解决办法是请求时带一个业务侧的流水号,或者发送前先在本地记录“已请求”,重试时先查一下状态再决定。这个细节不做,线上一定会出问题。
4.3 日志、回执与效果监控
短信这块最容易被忽略的是可观测性。出了问题时,如果你只有一句“用户说没收到”,那排查基本靠猜。
发送日志要记全:手机号(脱敏)、签名、模板编号、BizId、请求时间、返回 Code、耗时。这些字段在排查时一个都不能少。特别是耗时,如果某个时段耗时突然拉长,可能是网络或者服务端有波动,提前发现能避免大面积投诉。
回执要接。平台支持把短信的最终状态(成功、失败、未知)回调给你指定的地址,接上这个回调,你才能真正知道到达率。只看接口返回的 OK 是没用的,那是一厢情愿。
监控要设阈值。我会盯三个指标:发送量环比突增(可能是被刷)、失败率突增(可能是配置或余额问题)、到达率下滑(可能是通道质量或内容被拦截)。三个指标任何一个异常都值得看一眼,很多事故在发生前都有征兆。
5. 踩坑实录与常见问题速查
5.1 错误码对照表
这张表是我这些年攒下来的,遇到问题先对着查,能省掉一大半的搜索时间。
| 错误码关键字 | 大致含义 | 处理方向 |
|---|---|---|
| Code 返回 OK | 请求提交成功 | 不等同于送达,继续看回执 |
| 签名不合法 | 签名未审核通过或参数带括号 | 核对签名文字,去掉方括号 |
| 模板不合法 | 模板编号错误或未通过审核 | 核对 SMS_ 编号,确认审核状态 |
| 手机号不合法 | 格式错误或非大陆号码 | 检查是否 11 位纯数字,有无隐藏字符 |
| 触发流控 | 同一号码发送过于频繁 | 检查业务层是否缺频率控制 |
| 余额不足 | 账户欠费 | 及时充值,配置余额告警 |
| 业务停机 | 账号状态异常 | 查控制台账户状态与开通情况 |
| 系统繁忙 | 服务端临时问题 | 退避重试一到两次 |
表格里最常被问到的是“签名不合法”和“模板不合法”,这两个八成都是参数对不上。核对的时候建议直接复制控制台里的签名和模板编号,粘贴到代码里,不要手打。手打错一个字符的排查成本极高,眼睛盯着看十遍都看不出来。
5.2 审核类的坑怎么绕
审核类的坑最磨人,因为卡的是时间,不是技术。
签名驳回最常见的理由是证明材料不匹配。你提交的签名是品牌名,但给的证明材料是另一个产品的链接,审核员没法确认关联性,必然驳回。解决办法是提供的材料要和签名强相关,能直接看到签名文字出现在材料里。
模板驳回最常见的原因是内容里没有变量。很多人以为纯通知类短信不需要变量,写了个固定内容的模板,结果被拒。规则是模板必须包含变量,哪怕你只是加一个日期变量${date},也能通过。这个规则看着奇怪,其实是防止有人用固定模板群发广告,加了变量就没法完全固定内容了。
审核时长要有心理预期。一般几个小时,工作日快、周末慢,遇到需要补充材料的情况会更久。所以我给所有项目的建议都是:短信资质提前三天办。别等到上线前一天才想起来申请签名,那只能干着急。
5.3 成本上的坑
除了长短信翻倍计费,还有几个隐性成本值得注意。
失败也计费的情况要问清楚。不同场景的计费口径不完全一样,发出去但用户手机停机导致未送达的,通常仍会计费。所以到达率低的时候,你付的是有效成本,但拿到的是无效结果,这时候就该优化号码质量了。
套餐包有有效期。买的时候便宜,用不完过期作废,等于白买。按量付费虽然单价高一点,但胜在灵活,量不稳定的时候反而更省。
测试环境的消耗要隔离。开发调试期间反复发送,量看着不大,累计起来也不少。我的做法是把测试号码集中管理,调试时只发给固定的两三个号,避免全组人各自测试造成浪费。
注意:给所有短信接口加一个总开关配置。遇到极端情况(比如被恶意刷量、通道异常),能一键停掉所有发送,这个开关的价值在事故面前会被无限放大。
6. 几个我自己的实践体会
最后聊几个不成体系但很实用的经验,都是踩过之后才想明白的。
验证码别用自己的随机数生成器直接生成六位数。用普通的随机函数生成,重复率和可预测性都不理想。用密码学安全的随机源,位数控制在六位,有效期设五分钟,验证一次即失效,这三个参数是长期验证下来最平衡的组合。有效期设太短,用户输到一半就过期了,体验很差;设太长,被撞库的风险就上来了。
发送失败时,前端提示别写“发送失败”。用户看到这四个字只会反复点,加重流控。更好的提示是“发送过于频繁,请稍后再试”或者“未收到请 60 秒后重试”,既说明了情况,也引导了行为。这个细节看着小,能显著降低客服工单量。
把短信当成一条业务链路,而不是一个工具函数。我见过太多项目把发送逻辑封装成一个静态方法到处调,没有日志、没有重试、没有监控,出问题时找不到入口。正确的做法是封装成一个独立的服务模块,统一处理参数校验、频率控制、错误映射、日志记录,业务侧只调一个方法。这样后面无论换通道、加监控还是做灰度,都只需要改一个地方。
最后说一个排查思路:短信发不出去的时候,按“账号状态、签名模板、参数格式、频率限制、余额”这五步顺序查,八成问题在前三步。这个顺序是从高频到低频排的,比漫无目的地翻文档快得多。真遇到查不出来的,把 RequestId 记下来提工单,平台侧能查到比你多得多的信息,别自己硬扛。