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

Laravel 10事件处理器重复执行两次问题求助

Laravel 10事件监听器重复执行问题

我在Laravel 10中执行以下代码触发事件:

$reference = "ABCD1234";
event(new \App\Events\RewardOnBooking($reference));

触发后其他功能正常,但事件对应的handle函数会执行两次,数据库中出现重复的积分记录。

我的事件与监听器配置如下:

  1. EventServiceProvider中的注册:
protected $listen = [
    // 其他事件...
    'App\Events\RewardOnBooking' => [
        'App\Listeners\AddCredit',
    ],
];
  1. RewardOnBooking事件类:
public function __construct($reference)
{
    $this->reference = $reference;
}
  1. AddCredit监听器类:
public function handle(RewardOnBooking $event): void
{
    $userCredit = Credits::where('user_id', '=', Auth::user()->id)->orderBy('id', 'desc')->first();
    if ($userCredit == null) {
        Credits::firstOrCreate(
            ['user_id' => Auth::user()->id],
            [
                'creditsAmounts' => 0,
                "description" => 'added credit for your first booking',
            ]
        )->first();
    } else {
        $amount = rand(1, 5);
        Credits::create([
            'user_id' => Auth::user()->id,
            "creditsAmounts" => $userCredit['creditsAmounts'] + $amount,
            "description" => 'Added credit for your booking reference - ' . $event->reference,
            'responce' => $amount,
        ]);
    }
}

需求:确保事件触发时,监听器的handle方法只执行一次,避免重复添加积分。


排查与解决方案

1. 检查事件是否被重复触发

在触发事件的代码前后添加日志,确认代码是否被执行两次:

Log::info('准备触发RewardOnBooking事件,reference: '.$reference);
event(new \App\Events\RewardOnBooking($reference));
Log::info('RewardOnBooking事件触发完成');

查看storage/logs/laravel.log,如果两条日志出现两次,说明触发逻辑被重复调用。

2. 检查监听器是否重复注册

  • 查看EventServiceProvider的boot方法,是否存在手动注册监听器的代码:
// 此类代码会导致重复注册
Event::listen(RewardOnBooking::class, AddCredit::class);
  • 排查其他服务提供者,确认是否有重复绑定该事件监听的逻辑。

3. 队列配置检查(若使用队列)

如果事件实现了ShouldQueue接口:

  • 清空队列重试列表,避免重复任务执行:
php artisan queue:flush
  • 检查.env中的QUEUE_CONNECTION,若为非sync驱动,确认是否有重复的队列进程在运行。

4. 路由/中间件重复触发检查

  • 确认触发事件的路由是否被定义多次,或存在中间件导致请求被重复处理(比如调试中间件、异常重定向逻辑)。

5. 幂等性优化(兜底方案)

若以上排查无问题,可在监听器中添加幂等校验,确保同一订单只处理一次:

public function handle(RewardOnBooking $event): void
{
    // 校验该订单是否已处理过积分
    $processed = Credits::where('user_id', Auth::user()->id)
        ->where('description', 'like', '%'.$event->reference.'%')
        ->exists();
    
    if ($processed) {
        return;
    }

    $userCredit = Credits::where('user_id', '=', Auth::user()->id)->orderBy('id', 'desc')->first();
    if ($userCredit == null) {
        Credits::firstOrCreate(
            ['user_id' => Auth::user()->id],
            [
                'creditsAmounts' => 0,
                "description" => 'added credit for your first booking',
            ]
        );
    } else {
        $amount = rand(1, 5);
        Credits::create([
            'user_id' => Auth::user()->id,
            "creditsAmounts" => $userCredit['creditsAmounts'] + $amount,
            "description" => 'Added credit for your booking reference - ' . $event->reference,
            'responce' => $amount,
        ]);
    }
}

建议:最好在Credits表新增reference字段存储订单编号,通过精确匹配替代模糊查询,提升校验可靠性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 17:52:42