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

基于Laravel Echo实现连接超时后关闭客服聊天的方案咨询

解决Laravel Echo + Pusher.js客服聊天的离线会话关闭需求

我之前做过类似的客服聊天系统,刚好碰到过几乎一样的问题,给你分享几个经过验证的可行方案:

方案一:前端监听连接状态+延迟冷却(快速实现)

Pusher.js本身没有直接提供“连接超时回调URL”的配置,但我们可以监听它的连接状态变化事件,结合延迟定时器来避免误判,再主动调用自定义接口关闭会话。

具体代码示例

// 监听Pusher连接状态切换
Echo.connector.pusher.connection.bind('state_change', (states) => {
  const { previous, current } = states;

  // 仅当连接从在线变为断开/不可用时触发
  if (previous === 'connected' && ['disconnected', 'unavailable', 'failed'].includes(current)) {
    // 设置10秒冷却时间(可根据业务调整),避免短暂网络波动误判
    const offlineTimer = setTimeout(() => {
      // 用fetch发送关闭会话请求(也可以用axios)
      fetch('/api/chat/close-session', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
        },
        body: JSON.stringify({
          user_id: '你的用户MD5唯一标识' // 替换成实际用户ID
        })
      }).then(res => {
        if (res.ok) {
          console.log('会话已自动关闭');
        }
      }).catch(err => {
        console.error('关闭会话失败:', err);
      });
    }, 10000); // 10秒冷却

    // 如果冷却期内重新连接,立即取消关闭任务
    Echo.connector.pusher.connection.bind('connected', () => {
      clearTimeout(offlineTimer);
    });
  }
});

// 额外处理浏览器关闭/页面卸载的情况(更可靠)
window.addEventListener('beforeunload', () => {
  // 使用sendBeacon,确保页面卸载时请求能发送成功
  const formData = new FormData();
  formData.append('user_id', '你的用户MD5唯一标识');
  formData.append('_token', document.querySelector('meta[name="csrf-token"]').content);
  navigator.sendBeacon('/api/chat/close-session', formData);
});

方案二:后端结合Presence事件+延迟队列(更可靠)

前端的延迟处理可能存在漏洞(比如用户直接断电),可以结合Presence通道的member_removed事件,在后端用延迟队列做二次验证,确保用户确实离线后再关闭会话。

步骤1:监听Presence通道的成员移除事件

在Laravel中注册事件监听,捕获用户离开Presence通道的事件:

// app/Providers/EventServiceProvider.php
protected $listen = [
    'Illuminate\Broadcasting\Events\BroadcastLogout' => [
        'App\Listeners\HandleUserOffline',
    ],
];

步骤2:编写延迟关闭会话的逻辑

// app/Listeners/HandleUserOffline.php
namespace App\Listeners;

use Illuminate\Broadcasting\Events\BroadcastLogout;
use App\Jobs\CloseChatSession;

class HandleUserOffline
{
    public function handle(BroadcastLogout $event)
    {
        $userId = $event->user->id; // 替换成你的用户MD5标识
        // 延迟15秒执行关闭任务,给用户留重连时间
        CloseChatSession::dispatch($userId)->delay(now()->addSeconds(15));
    }
}

步骤3:编写队列任务,二次验证用户在线状态

// app/Jobs/CloseChatSession.php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Pusher\Pusher;

class CloseChatSession implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $userId;

    public function __construct($userId)
    {
        $this->userId = $userId;
    }

    public function handle()
    {
        // 通过Pusher API查询Presence通道的在线成员
        $pusher = new Pusher(
            config('broadcasting.connections.pusher.key'),
            config('broadcasting.connections.pusher.secret'),
            config('broadcasting.connections.pusher.app_id'),
            config('broadcasting.connections.pusher.options')
        );

        $channelMembers = $pusher->getChannelMembers('chat');
        $isOnline = collect($channelMembers->members)->contains(function ($member) {
            return $member->id === $this->userId;
        });

        // 确认用户确实离线,再关闭会话
        if (!$isOnline) {
            \App\Models\ChatSession::where('user_id', $this->userId)
                ->update(['status' => 'closed', 'closed_at' => now()]);
        }
    }
}

关键注意事项

  • 冷却时间建议设置在10-30秒之间,平衡用户体验和会话准确性。
  • 后端延迟任务可以配合Laravel的队列系统(比如Redis),确保任务可靠执行。
  • 如果用户在冷却期内重新上线,前端要主动通知后端取消未执行的关闭任务(可以通过新增接口实现)。

内容的提问来源于stack exchange,提问作者Patrik Krehák

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:48:26