ThinkPHP实现客服系统WebSocket即时通讯与会话转接,核心是业务层(ThinkPHP)与通信层(独立WebSocket服务)解耦:前者处理用户、工单、权限等业务逻辑,后者专注连接管理、消息路由与状态同步;转接需服务端完成连接迁移、状态广播及前端重连引导,并通过Redis绑定会话ID与用户身份,确保消息精准路由与离线兜底。

ThinkPHP 实现客服系统中的 WebSocket 即时通讯与会话转接,核心不在于“能不能做”,而在于“怎么分层设计才稳”。它需要业务逻辑(ThinkPHP)和长连接通信(WebSocket 服务)解耦运行——前者管用户、工单、权限、历史记录;后者只专注连接管理、消息路由和状态同步。转接不是前端点个按钮就完事,而是服务端一次连接迁移 + 状态广播 + 前端重连引导的组合动作。
WebSocket 服务必须独立于 HTTP 请求生命周期
ThinkPHP 原生基于 HTTP 短连接,无法直接承载 WebSocket 长连接。强行在控制器里启动 Swoole 服务会导致进程阻塞、无法响应 Web 请求,且每次请求都会新建一个服务实例,完全不可用。
- 必须使用命令行方式启动独立的 WebSocket 进程,例如:
php think websocket:serve或php start.php start -d(GatewayWorker 场景) - 推荐用
topthink/think-swoole(TP6/8)或workerman/workerman(更轻量、易调试),避免直接操作底层swoole_websocket_server类 - 服务监听端口(如 9501)需在 Nginx/Apache 中反向代理,前端通过
wss://yourdomain.com/ws连接,而非裸 IP+端口(否则 HTTPS 页面无法建立 ws 连接)
消息收发与用户身份绑定是转接前提
客服系统不是群聊,每个消息必须明确归属:谁发的、发给谁、当前由谁接待。WebSocket 服务本身不存用户信息,需在连接建立时完成身份校验并绑定唯一标识(如 uid 或 session_id)。
- 客户端连接时带上 token 或加密参数:
ws://xxx.com?token=abc123,服务端解析后查数据库确认用户角色(客户 / 客服 / 管理员) - 为每个有效连接分配唯一会话 ID(
sid),并存入 Redis,结构如:sid:abc123 → {uid:1001, role:'customer', timestamp:1714471200} - 消息体建议统一格式:
{"type":"msg","from":1001,"to":2005,"content":"你好","sid":"abc123"},便于服务端识别路由逻辑
客服转接的本质是连接归属变更 + 状态通知
转接不是把消息转发给另一个客服,而是把当前会话的“接待权”从 A 客服移交到 B 客服,并确保双方客户端实时感知变化。
立即学习“PHP免费学习笔记(深入)”;
- 服务端收到转接请求(如 POST /api/transfer)后,更新 Redis 中该会话的
current_agent_id字段,并推送事件:{"type":"transfer","sid":"abc123","from":2001,"to":2005,"timestamp":...} - 原客服连接收到通知,前端自动禁用输入框、显示“已转出”提示;新客服连接收到通知,前端自动展开该会话窗口、加载历史消息
- 后续该会话所有消息,服务端只推送给新客服的连接(根据
sid查找目标fd),不再广播或误推给原客服 - 若新客服不在线,可先存离线消息,待其上线后拉取,并触发桌面提醒
Nginx 反向代理与 HTTPS 兼容配置不能省
生产环境几乎全是 HTTPS,但 WebSocket 的 ws:// 协议会被浏览器拦截。必须用 wss://,这依赖 Nginx 正确透传升级头并维持长连接。
- Nginx 配置关键段:
proxy_pass http://127.0.0.1:9501;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
</pre>
-
proxy_read_timeout必须设大(如 24 小时),否则空闲连接会被 Nginx 主动断开,导致频繁重连 - 宝塔等面板需额外放行对应端口(如 9501),并关闭“强制 HTTPS”对
/ws路径的干扰



















