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

如何让Laravel队列仅重试失败的通知渠道而非整个通知?

解决Laravel通知重试仅发送失败渠道的问题

不需要创建新通知,通过记录已成功发送的渠道,让重试时自动排除已完成的渠道即可,具体实现分两种方案(缓存/数据库),推荐数据库方案更可靠:

方案一:用数据库记录发送状态(推荐)

1. 创建发送状态记录表

生成迁移文件:

php artisan make:migration create_notification_channel_logs_table

编辑迁移文件,添加记录字段:

public function up()
{
    Schema::create('notification_channel_logs', function (Blueprint $table) {
        $table->id();
        $table->string('notification_type'); // 通知类名
        $table->unsignedBigInteger('notifiable_id'); // 接收者ID
        $table->string('notifiable_type'); // 接收者模型类名
        $table->string('channel'); // 渠道标识
        $table->timestamp('sent_at')->useCurrent();
        // 唯一索引,避免重复记录
        $table->unique(['notification_type', 'notifiable_id', 'notifiable_type', 'channel']);
    });
}

执行迁移:

php artisan migrate

2. 创建事件监听器记录发送状态

生成监听器:

php artisan make:listener MarkNotificationChannelAsSent

编辑监听器,在渠道发送成功时写入记录:

namespace App\Listeners;

use Illuminate\Notifications\Events\NotificationSent;
use App\Models\NotificationChannelLog;

class MarkNotificationChannelAsSent
{
    public function handle(NotificationSent $event)
    {
        // 写入或更新发送记录
        NotificationChannelLog::updateOrCreate(
            [
                'notification_type' => get_class($event->notification),
                'notifiable_id' => $event->notifiable->getKey(),
                'notifiable_type' => get_class($event->notifiable),
                'channel' => $event->channel,
            ],
            ['sent_at' => now()]
        );
    }
}

3. 注册事件监听

打开app/Providers/EventServiceProvider.php,添加事件与监听器的关联:

protected $listen = [
    \Illuminate\Notifications\Events\NotificationSent::class => [
        \App\Listeners\MarkNotificationChannelAsSent::class,
    ],
];

4. 修改通知类的via方法

在LineLoginNotification中,修改via方法过滤已发送渠道:

use App\Models\NotificationChannelLog;

public function via($notifiable)
{
    $allChannels = ['mail', MyChannel::class];
    
    // 查询已成功发送的渠道
    $sentChannels = NotificationChannelLog::where([
        'notification_type' => get_class($this),
        'notifiable_id' => $notifiable->getKey(),
        'notifiable_type' => get_class($notifiable),
    ])->pluck('channel')->toArray();
    
    // 只返回未发送的渠道
    return array_diff($allChannels, $sentChannels);
}

5. 确保渠道发送失败时抛出异常

修改MyChannel的send方法,发送失败时抛出异常(让队列触发重试):

public function send($notifiable, $notification)
{
    $line_id = $notifiable->line_id;
    $msg = $notification->toMyChannelMethod($notifiable);
    $messageService = new MyMessageService();

    // 根据实际业务判断发送是否失败,这里假设sendSimpleMsg失败返回false
    if (!$messageService->sendSimpleMsg($msg, $line_id)) {
        throw new \RuntimeException('MyChannel消息发送失败');
    }
}

邮件渠道默认发送失败会抛出异常,无需额外修改。

方案二:用缓存临时记录(快速实现)

如果不需要持久化记录,可改用缓存:

1. 创建事件监听器记录缓存

生成监听器后修改为:

namespace App\Listeners;

use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Support\Facades\Cache;

class MarkNotificationChannelAsSent
{
    public function handle(NotificationSent $event)
    {
        $key = $this->getCacheKey($event);
        // 缓存7天,可根据需求调整
        Cache::set($key, true, now()->addDays(7));
    }

    protected function getCacheKey(NotificationSent $event): string
    {
        return sprintf(
            'notification_sent:%s:%s:%s',
            get_class($event->notification),
            $event->notifiable->getKey(),
            $event->channel
        );
    }
}

2. 修改通知类的via方法

use Illuminate\Support\Facades\Cache;

public function via($notifiable)
{
    $allChannels = ['mail', MyChannel::class];
    
    return array_filter($allChannels, function ($channel) use ($notifiable) {
        $key = sprintf(
            'notification_sent:%s:%s:%s',
            get_class($this),
            $notifiable->getKey(),
            $channel
        );
        // 过滤掉已缓存的已发送渠道
        return !Cache::has($key);
    });
}

后续步骤同方案一的3、5。

原理说明

当通知首次执行时,via返回所有渠道,逐个发送:

  • 发送成功的渠道会触发NotificationSent事件,监听器记录已发送状态;
  • 若某渠道发送失败抛出异常,队列会重试该通知;
  • 重试时via方法会自动排除已记录的成功渠道,仅尝试发送失败的渠道。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 03:27:39