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

Laravel批量发送带附件通知异常问题求助

问题分析与解决方案

核心问题定位

  1. withBatchId() 调用报错:Bus::batch() 中传入的任务实例无效(返回null),导致框架无法为任务绑定批量ID
  2. 无批量任务记录 + batching() 返回false:未正确创建批量任务,通知类未关联到批量上下文,或者批量任务的创建逻辑错误

步骤1:修正CustomEmail通知类

确保Batchable trait正确集成,且通知类的队列逻辑与批量上下文关联:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Batchable;
use Illuminate\Notifications\Messages\MailMessage;

class CustomEmail extends Notification implements ShouldQueue
{
    use Queueable, Batchable;

    public $content;
    public $attachment;

    public function __construct($content, $attachment = null)
    {
        $this->content = $content;
        $this->attachment = $attachment;
    }

    public function via($notifiable)
    {
        return ['mail'];
    }

    public function toMail($notifiable)
    {
        $message = (new MailMessage())
            ->subject('自定义通知')
            ->line($this->content);

        // 添加附件(如果存在)
        if ($this->attachment) {
            $message->attach(storage_path('temp/'.$this->attachment->filename));
        }

        return $message;
    }
}

步骤2:修正EmailController中的批量任务创建逻辑

错误根源是未正确将通知发送包装为可批量的队列任务,正确写法是为每个收件人生成独立的通知任务,加入批量后再分发:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Bus;
use App\Notifications\CustomEmail;
use App\Models\User;
use App\Models\Attachment;

class EmailController extends Controller
{
    public function sendBatchedNotifications()
    {
        // 1. 处理通知内容与附件
        $content = request('content');
        $attachment = null;

        if (request()->hasFile('attachment')) {
            $file = request()->file('attachment');
            $tempPath = storage_path('temp');
            $filename = uniqid().'.'.$file->getClientOriginalExtension();
            $file->move($tempPath, $filename);

            // 创建Attachment模型,预留批量ID字段
            $attachment = Attachment::create([
                'filename' => $filename,
                'temp_path' => $tempPath,
                'batch_id' => null,
            ]);
        }

        // 2. 获取收件人列表
        $recipients = User::where('notifyable', true)->get();

        // 3. 生成批量任务数组
        $tasks = [];
        foreach ($recipients as $user) {
            // 为每个收件人创建独立的通知任务
            $tasks[] = new CustomEmail($content, $attachment);
        }

        // 4. 创建并分发批量任务
        if (!empty($tasks)) {
            $batch = Bus::batch($tasks)
                ->name('批量邮件通知')
                ->dispatch();

            // 关联附件与批量ID
            if ($attachment) {
                $attachment->update(['batch_id' => $batch->id]);
            }
        }

        return redirect()->back()->with('success', '批量通知已开始发送');
    }
}

步骤3:修正NotificationSent事件监听逻辑

确保DeleteEmailAttachments处理器能正确识别批量完成状态,并关联附件的批量ID:

<?php

namespace App\Listeners;

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

class DeleteEmailAttachments
{
    public function handle(NotificationSent $event)
    {
        $notification = $event->notification;

        // 检查当前通知所属批量是否已完成
        if ($notification->batching() && $notification->batch()->finished()) {
            $batchId = $notification->batch()->id;
            $attachment = Attachment::where('batch_id', $batchId)->first();

            if ($attachment) {
                // 删除本地文件
                @unlink($attachment->temp_path.'/'.$attachment->filename);
                // 删除数据库记录
                $attachment->delete();
            }
        }
    }
}

关键注意事项

  1. 队列驱动配置:必须使用支持批量任务的队列驱动(如database、redis),sync驱动不支持批量功能
  2. Batchable trait使用:通知类必须引入Batchable trait,不能重写batch()方法,否则会丢失批量上下文
  3. 任务实例有效性:Bus::batch()中传入的必须是有效的ShouldQueue任务实例,不能是null或非队列任务
  4. 事件监听逻辑:NotificationSent事件在单条通知发送完成后触发,需通过batch()->finished()判断整个批量是否完成

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:00:21