ThinkPHP 5.1 实现定时短信的核心是“稳、准、可追溯”,需通过命令行Command+Crontab调度、封装SmsService类处理发送与异常、模板变量替换、幂等锁及失败告警机制保障可靠性。

ThinkPHP 5.1 做定时任务发短信,核心不是“能不能发”,而是“怎么稳、怎么准、怎么可追溯”。它本身不内置短信能力,必须靠定时任务触发业务逻辑,再调用第三方短信接口(如阿里云、腾讯云、容联云等)。关键在任务调度可靠 + 短信调用健壮 + 异常有兜底。
定时任务主体:命令行类 + Crontab 调度
这是最推荐、最稳定的组合。TP5.1 的 think\console\Command 提供了清晰的入口,配合 Linux 的 crontab 实现系统级调度,不依赖 Web 请求,避免超时和中断。
- 在
application/command/下新建一个类,比如SmsNotify.php,继承think\console\Command -
configure()中设好命令名(如sms:notify)和描述,便于后期维护 -
execute()里写真实逻辑:查出待通知数据(如超时未处理工单、到期提醒用户)、组装参数、调用短信 SDK - 记得在
application/command.php中注册该类,否则php think sms:notify会报错 - 服务器上用
crontab -e添加规则,例如:*/10 * * * * /usr/bin/php /var/www/project/think sms:notify >> /tmp/sms.log 2>&1(每10分钟执行一次)
短信发送:封装服务类 + 异常捕获 + 日志记录
别把短信逻辑直接塞进 execute()。应抽成独立服务类(如 app\service\SmsService),统一管理配置、签名、模板 ID 和重试策略。
- 构造函数注入配置(从
config/sms.php读取 access_key、secret、sign_name 等),避免硬编码 - 发送方法返回结构化结果:
['success' => true, 'msg_id' => 'xxx', 'error' => ''],方便后续追踪 - 必须 try-catch 第三方 SDK 抛出的异常,并记录完整错误日志(含请求参数、响应体、时间戳)
- 对失败情况做简单重试(如最多 2 次,间隔 1 秒),但不要无限循环阻塞任务
- 成功后建议写一条
sms_log表记录:手机号、模板 ID、发送时间、状态、message_id(用于回执查询)
消息内容与上下文:变量替换 + 多模板支持
短信不是群发广告,得带业务上下文。比如工单超时提醒,要包含工单号、责任人、剩余时间;生日祝福要带姓名。硬拼字符串容易出错,建议用模板引擎式管理。
立即学习“PHP免费学习笔记(深入)”;
- 建一张
sms_templates表,字段包括:code(如ticket_overdue)、content(含占位符如{ticket_no} 已超时 {minutes} 分钟,请及时处理)、status - 调用时传入实际值数组:
$params = ['ticket_no' => 'T20260806001', 'minutes' => 15] - 用
strtr($template, $params)或正则替换完成填充,再校验长度(国内短信通常 ≤ 70 字) - 不同业务场景用不同模板 code,避免混用导致信息错乱
可靠性增强:幂等控制 + 执行锁 + 失败告警
定时任务可能重复执行(如 crontab 规则误配、服务器重启触发多实例),短信重复发送会引发投诉。必须加防护。
- 任务开始前先检查是否已在运行:用缓存键
sms:notify:lock+Cache::has()判断,存在则直接退出 - 设置锁有效期(如 10 分钟),防止进程崩溃导致锁残留
- 关键操作加数据库唯一约束或业务幂等标识(如工单通知只发一次,状态标记为
sms_sent=1) - 若连续 N 次发送失败(如 3 次),自动触发飞书/邮件告警给运维,而不是静默丢弃
- 日志中明确区分“无数据可发”、“发送成功”、“发送失败(网络)”、“发送失败(余额不足)”等状态,便于快速定位



















