You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel如何检测Notification通知是否已成功分发

Laravel Notification 发送结果判定方案

问题本质

你遇到的报错:

'void' method 'notify' result used

核心原因是Laravel自带的notify()方法本身设计为无返回值(void类型),直接拿它的返回值做if判断永远不会得到预期结果,原代码里mark_as_delivered逻辑永远不执行就是这个原因。
通知发送成功/失败的判定逻辑和你用的发送驱动(同步/队列)直接相关:

  • 同步发送:notify()执行过程不抛异常就代表发送成功,抛出异常就是发送失败
  • 异步队列发送:notify()执行只代表任务成功入队,不代表邮件实际发出去了,最终发送失败的任务会自动写入failed_jobs表

具体实现代码

同步发送场景

直接用try/catch捕获发送过程的异常即可,无异常即为发送成功:

<?php

class SendCertificates
{
    public function __invoke()
    {
        // 补全原代码缺失的证书实例获取逻辑,根据你的业务场景调整取数方式
        $certificate = Certificate::query()->find(/* 对应证书ID */);
        
        if ($this->send_certificate($certificate)) {
            $this->mark_as_delivered($certificate);
        } else {
            // 自定义失败逻辑:记日志、标记证书状态为发送失败、触发告警都可以
        }
    }

    public function send_certificate(Certificate $certificate): bool
    {
        try {
            Notification::route('mail', [
                $certificate->participant->email => $certificate->participant->name,
            ])->notify(new CertificateIssued($certificate));
            
            // 走到这行没有抛出异常,就代表发送成功
            return true;
        } catch (\Throwable $e) {
            // 按需记录错误详情
            \Log::error('证书通知发送失败', [
                'certificate_id' => $certificate->id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return false;
        }
    }
}

异步队列发送场景

如果你的CertificateIssued通知实现了ShouldQueue接口走异步队列,不要在调用notify()后立刻更新送达状态,此时邮件还没实际发送。可以选以下两种方案判定结果:

  • 方案1:监听队列事件自动更新状态
    在AppServiceProvider的boot方法中注册队列失败、成功事件回调,自动更新证书状态:
    use Illuminate\Queue\Events\JobFailed;
    use Illuminate\Queue\Events\JobProcessed;
    use Illuminate\Support\Facades\Queue;
    use App\Notifications\CertificateIssued;
    use App\Models\Certificate;
    
    public function boot()
    {
        // 队列任务执行成功:标记证书已送达
        Queue::after(function (JobProcessed $event) {
            if ($event->job->resolveName() === CertificateIssued::class) {
                $command = unserialize($event->job->payload()['data']['command']);
                Certificate::where('id', $command->certificate->id)->update(['delivered' => true]);
            }
        });
    
        // 队列任务执行失败:标记证书发送失败
        Queue::failing(function (JobFailed $event) {
            if ($event->job->resolveName() === CertificateIssued::class) {
                $command = unserialize($event->job->payload()['data']['command']);
                Certificate::where('id', $command->certificate->id)->update([
                    'delivered' => false,
                    'error_msg' => $event->exception->getMessage()
                ]);
            }
        });
    }
    
  • 方案2:定期查询failed_jobs表对账
    写定时任务扫描failed_jobs表,解析失败任务的payload内容,匹配到证书发送任务后更新对应证书的失败状态;已经从队列中移除、且不在失败表中的任务,即为发送成功。

注意事项

  • 不要尝试直接获取notify()的返回值做判断,该方法本身无返回值,所有成功失败判定都基于「是否抛出异常」的逻辑
  • 队列场景下必须等任务实际执行完成再更新状态,入队成功不代表发送成功
  • 原代码__invoke方法里直接使用$certificate变量没有提前实例化,本身就会触发变量未定义错误,需要按业务逻辑补全取数步骤

内容的提问来源于stack exchange,提问作者Pathros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 11:31:12