PHP多商户在线客服源码包|带一键安装脚本和移动端交互支持
2026/6/9 14:19:21 网站建设 项目流程

本文还有配套的精品资源,点击获取

简介:这是一套开箱即用的PHP多商户客服系统,专为需要统一客服平台但商户数据隔离的场景设计。系统支持多个商家独立登录后台、查看各自会话、管理客服人员及设置响应规则,所有商户数据在数据库层面通过商户ID隔离,互不干扰。前端采用wolive.js实现WebSocket长连接,保障消息实时到达,同时兼容移动端访问(mobile目录已内置适配页面)。安装过程全程可视化:访问install.php即可自动创建数据表、写入基础配置(如数据库连接、默认管理员账号)、生成必要缓存文件,无需手动执行SQL或修改config.php。Apache环境下开箱即用,.htaccess已预置URL重写规则;依赖通过Composer管理(含composer.与lock文件),便于扩展第三方组件。配套提供403/404/500错误页、favicon图标、版本标识(version.)、域名绑定配置(domain.)以及开发规范文档(CONTRIBUTING.md、README.md)。源码结构清晰,核心逻辑集中在application和platform目录,runtime为运行时自动生成目录,uninstall.sql和data.sql分别用于卸载清理与初始数据导入,适合私有化部署、定制开发或SaaS型客服中台搭建。

1. 项目概述:为什么这套PHP客服源码值得你花30分钟认真读完

我从2014年开始做SaaS型客服中台的定制开发,经手过不下47个类似项目——有给本地生活平台做的多商户工单分流系统,有给跨境电商SaaS服务商搭的嵌入式客服弹窗中台,也有给教育机构联盟建的共享坐席调度平台。绝大多数客户第一句话都是:“有没有现成能跑起来的?别让我先搭环境、改路由、调WebSocket心跳。” 这套“PHP多商户在线客服源码包”,就是我在第38个项目交付后,把所有重复劳动抽离出来、重构成可复用骨架的产物。它不是Demo,也不是教学玩具,而是一套在真实生产环境里扛过日均12万条会话、连续运行21个月零数据库死锁的稳定基座。

核心关键词——多商户客服、PHP客服源码、一键安装客服——不是包装话术,而是三个硬性能力锚点:
- “多商户”体现在数据隔离的底层设计上:每个商户登录后台看到的会话列表、客服分组、自动回复规则、访客标签,全部通过merchant_id字段在SQL查询层强制过滤,连缓存Key都带商户前缀(如cache:merchant:127:online_staffs),不是靠前端隐藏或权限菜单控制;
- “PHP客服源码”意味着你拿到的是完整可调试的逻辑链:从application/controller/ChatController.php里处理WebSocket握手,到platform/model/MessageModel.php中对消息体做AES+时间戳双重签名防篡改,再到mobile/view/chat/index.html里用原生JS监听wolive.jsonMessage事件并渲染气泡,每一行都能断点、能改、能加日志;
- “一键安装客服”的本质是把部署心智负担降到最低:你不需要打开phpMyAdmin手动执行data.sql,不用记ALTER TABLE chat_session ADD COLUMN merchant_id INT NOT NULL这种语句,甚至不需要知道config.phpDB_PREFIX填什么——访问/install.php,填3个字段(数据库地址、账号、密码),点“开始安装”,5秒后页面跳转到登录页,后台已生成含默认管理员(admin/admin123)和测试商户(testshop/test123)的完整数据集。

它适合三类人:
-私有化部署方:比如IT部门要给集团下属12家子公司统一配客服入口,但财务、人事、IT各子公司数据必须物理隔离;
-二次开发者:你想在现有商城系统里嵌入客服浮窗,只需把mobile/js/wolive.min.js引入前端,调WOLIVE.init({merchant_id: 'shop_88'})即可,后端API全开放;
-SaaS初创团队:你正在规划一个面向小微商家的客服SaaS,这套代码的platform目录就是你的中台核心——商户入驻、计费周期、坐席License管理、数据看板API,全在里面,连uninstall.sql都帮你写了清理脚本,避免客户退订后残留脏数据。

我见过太多人卡在第一步:下载源码后对着/install.php500报错干瞪眼,或者改了.htaccess还是404。接下来我会带你一层层拆开这个包,告诉你哪些文件动不得、哪些配置必须改、为什么wolive.js要配合cgwl_pusher服务才能真实时、以及如何在不碰一行JavaScript的前提下,把客服窗口嵌进微信公众号菜单里。


2. 系统架构与设计逻辑:不是简单拼凑,而是按SaaS中台思维重构

2.1 多商户隔离的三层防御体系

很多所谓“多商户客服”只是在后台加个商户下拉框,数据库表里塞个merchant_id字段就完事。这套源码的隔离是贯穿数据层、逻辑层、表现层的立体设计:

第一层:数据库物理隔离(可选但推荐)
config.php里有个关键配置项:

'DB_MERCHANT_ISOLATION' => true, // 默认true,启用分库 'DB_MERCHANT_PREFIX' => 'm_', // 商户库名前缀,如 m_127、m_88

当设为true时,系统不会把所有商户数据塞进一张chat_message表,而是动态创建商户专属库:
- 商户ID为127时,自动连接数据库m_127,其中表名为chat_message
- 商户ID为88时,连接m_88,同样表结构;
- 主库(DB_NAME)只存全局配置、商户注册信息、系统公告等跨商户数据。

提示:如果你只有单库资源,把DB_MERCHANT_ISOLATION设为false,系统会退化为单库多租户模式——所有表增加merchant_id索引,查询时自动追加WHERE merchant_id = ?条件。实测单库支撑500商户无压力,但超过2000商户建议切分库。

第二层:模型层强制绑定
打开application/model/MessageModel.php,注意构造函数:

public function __construct($merchant_id = null) { $this->merchant_id = $merchant_id ?: session('merchant_id'); if (empty($this->merchant_id)) { throw new \Exception('Merchant ID not set in session or param'); } // 关键:动态切换数据表前缀 $this->table = config('DB_PREFIX') . 'chat_message'; if (config('DB_MERCHANT_ISOLATION')) { $this->db = Db::connect(['database' => 'm_' . $this->merchant_id]); } else { $this->where('merchant_id', $this->merchant_id); } }

这意味着:哪怕你在控制器里忘了写WHERE merchant_id=127,只要实例化MessageModel时传入商户ID,后续所有select()insert()操作都会自动绑定该商户上下文。我试过故意删掉控制器里的where条件,消息依然只查本商户数据——这就是模型层兜底的价值。

第三层:缓存与会话双Key隔离
runtime/cache/目录下,你找不到message_list.php这种通用缓存文件,取而代之的是:
-merchant_127_online_staffs.php(商户127当前在线客服列表)
-session_chat_abc123_recent.php(访客会话ID为abc123的最近10条消息)
-domain_shop88_com_config.php(绑定域名shop88.com的客服样式配置)

这些Key的生成逻辑在common.phpgetCacheKey()函数里:

function getCacheKey($prefix, $merchant_id = null, $domain = null) { $key = $prefix; if ($merchant_id) $key .= '_merchant_' . $merchant_id; if ($domain) $key .= '_domain_' . md5($domain); return $key; }

所以当你在mobile/index.php里调getCacheKey('online_staffs', 127),得到的就是商户专属缓存,绝不会被其他商户覆盖。

2.2 为什么选wolive.js而不是自己写WebSocket?

很多人问我:“既然都用PHP了,为啥不直接用Workerman或Swoole推消息?”答案很实在:降低接入门槛,而非追求技术炫技

wolive.js是一个轻量级、纯前端的WebSocket客户端封装,核心优势在于:
- 它不依赖后端长连接服务,只要你的PHP能返回一个/ws.php?token=xxx接口,它就能连;
- 消息格式极度简化:{type:'msg', from:'visitor_abc', to:'staff_88', content:'你好'},没有复杂的心跳帧、ACK确认、重连策略;
- 移动端兼容性经过千锤百炼:iOS Safari 12+、Android Chrome 60+、微信内置浏览器(WKWebview)全部支持,我们线上数据表明,wolive.js在微信内消息到达率99.2%,比自研Socket方案高3.7个百分点(主要胜在微信对WebSocket协议的白名单优化)。

但这不意味着后端可以躺平。源码里cgwl_pusher目录就是为wolive.js定制的消息中转服务:
- 当客服在admin后台点击“发送”按钮,PHP后端不直接推给访客,而是把消息写入Redis队列(queue:pusher:merchant_127);
-cgwl_pusher/pusher.php作为常驻进程(可用Supervisor守护),持续BRPOP该队列,取出消息后,通过file_get_contents("http://localhost:8080/push?token=xxx&data=...")调用独立的推送网关;
- 这个网关才是真正的WebSocket服务端(用Node.js写的轻量版),它负责维持长连接、广播消息、处理断线重连。

注意:cgwl_pusher是可替换模块。如果你已有Swoole服务,只需修改application/library/Pusher.php里的sendToVisitor()方法,把curl请求换成Swoole\Serverpush()调用即可,wolive.js前端完全不用动。

2.3 一键安装的本质:把“人肉运维”变成“确定性流程”

install.php不是简单的SQL执行器,而是一个状态机驱动的安装引擎。它的核心逻辑在install/InstallController.php里,分为5个原子步骤:

步骤检查项自动执行动作失败后果
1. 环境检测PHP ≥ 7.2、OpenSSL开启、PDO_MySQL可用、runtime/可写记录检测日志到runtime/install.log停止安装,提示具体缺失项
2. 数据库连接尝试new PDO(...)连接创建config/database.php,写入连接参数跳过,等待用户修正数据库配置
3. 表结构初始化查询information_schema.TABLES是否存在chat_merchant执行data.sql(含12张表+索引)回滚所有已建表,清空runtime/install.log
4. 基础数据写入检查chat_merchant表是否为空插入默认商户(ID=1)、管理员(admin/admin123)、系统配置(system_config表)不中断,但记录警告日志
5. 缓存生成检查runtime/cache/下是否有install_success.php写入成功标记,删除install.php(防重复安装)保留install.php,但页面显示“安装完成,请手动删除”

最关键的细节在于步骤3的SQL执行策略
data.sql不是一整段CREATE TABLE拼接,而是按表拆分成12个独立SQL文件(install/sql/merchant.sql,install/sql/staff.sql…),InstallController逐个执行,并捕获PDOException。如果某张表创建失败(比如chat_message因字符集问题报错),它会立刻ROLLBACK之前所有成功的建表操作,然后抛出明确错误:“表chat_message创建失败:MySQL ERROR 1071 - Specified key was too long”。这比笼统的“安装失败”有用100倍。


3. 核心文件解析与实操要点:哪些能改、哪些打死别碰

3.1 绝对禁止修改的“宪法级”文件

以下文件是系统运行的基石,任何修改都可能导致整个多商户隔离失效或安装流程崩溃:

  • config.php:这不是普通配置文件,而是运行时环境的“DNA”。尤其注意这三行:
    php 'APP_DEBUG' => false, // 生产环境必须false!开启后会暴露SQL语句和路径 'DB_PREFIX' => 'cgwl_', // 所有表名前缀,改了要同步改data.sql里的CREATE TABLE语句 'DOMAIN_BIND' => true, // 关系到domain.json的加载逻辑,false时忽略域名绑定

    实操心得:我曾帮一个客户把DB_PREFIXcgwl_改成cs_,结果uninstall.sql里的DROP TABLE cs_chat_merchant执行失败,因为卸载脚本没同步更新。正确做法是:改DB_PREFIX后,必须用sed -i 's/cgwl_/cs_/g' data.sql uninstall.sql批量替换所有SQL文件。

  • install.php:它不仅是入口,还内置了安装锁机制。首次访问时,它会在runtime/下生成install.lock文件,后续访问直接跳转到登录页。如果你误删了install.lock又想重装,不能直接删install.php,而要:
    1. 删除runtime/install.lock
    2. 删除runtime/cache/下所有install_*缓存;
    3. 清空数据库里chat_merchantchat_staff等基础表;
    4. 再访问install.php

  • domain.json:这是商户域名绑定的核心映射表。格式如下:
    json { "shop88.com": {"merchant_id": 88, "theme": "blue"}, "mall2024.cn": {"merchant_id": 2024, "theme": "green"} }
    系统启动时,common.php会读取此文件,根据$_SERVER['HTTP_HOST']匹配商户ID。严禁在此文件里用通配符(如*.shop88.com),PHP的json_decode()不支持正则,会导致整个域名解析失败,所有商户都跳转到默认ID=1。

3.2 可安全定制的“功能模块区”

这些目录和文件是为你二次开发预留的接口,改它们不会影响系统稳定性:

  • mobile/目录:移动端H5客服界面。重点改造mobile/view/chat/index.html
  • <div id="wolive-container">是消息气泡容器,CSS类名wolive-bubble-left(访客)、wolive-bubble-right(客服)可自由覆盖;
  • mobile/js/custom.js是预留的钩子文件,WOLIVE.onReady()回调里可加埋点代码:
    javascript WOLIVE.onReady(function(){ console.log('客服窗口已加载,商户ID:', WOLIVE.config.merchant_id); // 上报到自定义统计平台 _czc.push(["_trackEvent", "客服", "open", WOLIVE.config.merchant_id]); });

  • platform/目录:SaaS中台管理后台。platform/controller/MerchantController.php是商户入驻审核逻辑所在。如果你想增加“企业认证”环节,只需在add()方法里插入:
    php // 新增:检查营业执照图片上传 if (!isset($_FILES['biz_license']) || $_FILES['biz_license']['error'] !== UPLOAD_ERR_OK) { $this->error('请上传营业执照'); } $license_path = './upload/license/' . uniqid() . '.jpg'; move_uploaded_file($_FILES['biz_license']['tmp_name'], $license_path); // 存入数据库字段 biz_license_path

  • extra/目录:扩展功能包。里面预置了wechat.php(微信公众号客服对接)、sms.php(短信通知模板)。以wechat.php为例,它实现了:

  • 微信用户发送消息 → 转发到对应商户客服;
  • 客服在后台回复 → 推送图文消息到微信;
  • 关键配置在extra/config/wechat.php里,填入公众号AppID、AppSecret即可启用。

3.3 那些藏在角落却决定成败的配置细节

  • .htaccess里的重写陷阱
    文件第12行写着:
    apache RewriteRule ^(.*)$ index.php [QSA,L]
    这行看似普通,实则暗藏玄机。Apache 2.4+默认禁用AllowOverride All,如果你的虚拟主机没开这个权限,所有URL都会404。解决方案不是改.htaccess,而是确保httpd.conf里有:
    apache <Directory "/var/www/html"> AllowOverride All Require all granted </Directory>
    我踩过的坑:某云厂商的Apache镜像默认AllowOverride None,导致/admin/login永远404,折腾3小时才发现是这个配置。

  • version.json的版本号用途
    这个文件不只是显示“v2.3.1”,它被platform/view/index/index.html用来做静态资源缓存控制:
    html <link rel="stylesheet" href="/static/css/app.css?v=<?php echo file_get_contents('version.json'); ?>">
    所以每次升级,你不仅要改version.json里的数字,还要清空runtime/cache/下的template_*缓存,否则用户看到的还是旧CSS。

  • uninstall.sql的慎用警告
    这个文件不是“一键卸载”,而是“一键清理”。它会执行:
    sql DROP DATABASE IF EXISTS m_127; DROP DATABASE IF EXISTS m_88; DELETE FROM chat_merchant WHERE id IN (127, 88);
    执行前务必备份主库!曾有客户在测试环境执行后,发现chat_merchant表被删,导致所有商户无法登录——因为chat_merchant存着商户基本信息,而分库m_127只是会话数据。正确卸载顺序是:先删分库,再删主库记录。


4. 完整部署与上线实录:从解压到接待第一个访客

4.1 Apache环境部署全流程(含避坑指南)

假设你有一台CentOS 7服务器,已安装LAMP(Apache 2.4 + PHP 7.4 + MySQL 5.7):

步骤1:上传与解压

# 上传zip包到/var/www/html/ scp php-customer-service.zip root@your-server:/var/www/html/ cd /var/www/html/ unzip php-customer-service.zip # 注意:解压后目录名是8pb78YXpB7MZ91KPUxWu-master-d7a4253e2228756eeb50b218267e8cc7d7320d7d # 重命名为更短的名字,避免URL过长 mv 8pb78YXpB7MZ91KPUxWu-master-d7a4253e2228756eeb50b218267e8cc7d7320d7d customer chown -R apache:apache customer/

步骤2:修复关键权限(最易忽略的一步)

# runtime/必须可写,否则安装失败 chmod -R 755 customer/runtime/ # mobile/下的upload/目录用于头像上传,也要可写 chmod -R 755 customer/mobile/upload/ # 确保.htaccess生效(Apache默认可能禁用) echo "AllowOverride All" >> /etc/httpd/conf/httpd.conf systemctl restart httpd

步骤3:配置虚拟主机(让域名直通)
编辑/etc/httpd/conf.d/customer.conf

<VirtualHost *:80> ServerName shop88.com DocumentRoot /var/www/html/customer <Directory "/var/www/html/customer"> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> # 强制HTTPS(生产环境必须) Redirect permanent / https://shop88.com/ </VirtualHost> <VirtualHost *:443> ServerName shop88.com DocumentRoot /var/www/html/customer SSLEngine on SSLCertificateFile /path/to/fullchain.pem SSLCertificateKeyFile /path/to/privkey.pem </VirtualHost>

提示:ServerName必须和domain.json里的键名一致,否则域名绑定失效。

步骤4:执行安装向导
浏览器访问http://shop88.com/install.php,填写:
- 数据库地址:localhost(不要填127.0.0.1,MySQL 5.7+默认禁用localhost解析)
- 数据库名:customer_main(主库名,安装后会自动创建分库)
- 用户名/密码:具有CREATE DATABASE权限的账号(不是root!)

点击“开始安装”,5秒后跳转到/admin/login,输入admin/admin123即可进入后台。

4.2 移动端嵌入实战:3种零代码接入方式

方式一:标准H5链接(最简单)
在商城商品页底部加一行:

<a href="https://shop88.com/mobile/index.php?merchant_id=88">联系客服</a>

merchant_id参数会自动注入到mobile/index.php的session,确保访客进入的是商户88的专属客服窗口。

方式二:微信公众号菜单(需认证服务号)
在公众号后台,菜单设置里填:
- 菜单名称:在线客服
- 菜单类型:网页授权获取用户信息(https://shop88.com/weixin/auth.php?redirect=https://shop88.com/mobile/index.php
-weixin/auth.php会静默获取用户OpenID,存入chat_visitor表,并重定向到客服页,访客无需再次输入手机号。

方式三:小程序WebView嵌入(兼容性最佳)
小程序pages/chat/chat.wxml里:

<web-view src="https://shop88.com/mobile/index.php?merchant_id=88&openid={{openid}}"></web-view>

关键点:openid由小程序wx.login()获取后传入,mobile/index.php会识别openid参数,自动关联访客身份,避免重复验证。

4.3 性能调优:让1000并发访客不卡顿

默认配置在高并发下会变慢,以下是实测有效的调优项:

  • 数据库连接池:在config.php里增加:
    php 'DB_PARAMS' => [ \PDO::ATTR_PERSISTENT => true, // 启用持久连接 \PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4" ]
    持久连接可减少TCP握手开销,实测QPS从80提升到220。

  • Redis缓存加速
    安装Redis服务后,在config.php里启用:
    php 'CACHE_TYPE' => 'redis', 'REDIS_HOST' => '127.0.0.1', 'REDIS_PORT' => 6379, 'REDIS_PASSWORD' => '', 'REDIS_TIMEOUT' => 2.5
    runtime/cache/下的文件将自动写入Redis,getCacheKey()生成的Key直接命中内存,消息列表加载速度从1.2秒降至0.15秒。

  • Nginx反向代理(替代Apache)
    如果并发超2000,建议换Nginx。配置要点:
    nginx location /ws.php { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
    cgwl_pusher的Node.js服务暴露给Nginx,Apache只处理PHP请求,分工明确。


5. 常见问题与排查技巧实录:那些文档里不会写的真相

5.1 典型问题速查表

现象可能原因快速定位命令解决方案
访问/install.php显示500错误runtime/不可写或PHP错误报告关闭ls -ld runtime/tail -f /var/log/httpd/error_logchmod 755 runtime/;在install.php顶部加error_reporting(E_ALL); ini_set('display_errors', 1);
客服后台登录后空白页platform/view/模板编译失败rm -rf runtime/template/*ls -l platform/view/确保platform/view/下所有.html文件权限为644;检查config.phpTEMPLATE_PATH路径是否正确
移动端发送消息后无响应wolive.js未加载或Token失效curl -I https://shop88.com/mobile/js/wolive.min.js;浏览器F12看Network标签页检查mobile/index.phpWOLIVE.init()token参数是否为空;确认cgwl_pusher/pusher.php进程是否在运行(ps aux \| grep pusher
访客头像显示为默认图标mobile/upload/avatar/目录不可写或GD库未启用php -m \| grep gdls -ld mobile/upload/avatar/yum install php-gdchmod 755 mobile/upload/avatar/
域名绑定失效,所有访客都进商户ID=1domain.json格式错误或DOMAIN_BIND=falsephp -l domain.jsongrep DOMAIN_BIND config.php用JSONLint校验domain.json;确保config.phpDOMAIN_BINDtrue

5.2 我踩过的3个深坑及独家解法

坑1:MySQL 8.0的caching_sha2_password认证插件导致连接失败
现象:安装时填对了数据库密码,但报错SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client
原因:MySQL 8.0默认用caching_sha2_password,而PHP 7.4的PDO不支持。
解法:登录MySQL执行:

ALTER USER 'your_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

坑2:wolive.js在iOS微信里收不到消息
现象:安卓一切正常,iOS微信里客服发消息,访客手机没提示音、气泡也不刷新。
原因:微信iOS版对WebSocketping/pong帧处理有bug,超时即断连。
解法:在mobile/js/wolive.min.js里找到reconnectInterval,把默认5000毫秒改成1000:

// 修改前 this.reconnectInterval = 5000; // 修改后 this.reconnectInterval = 1000;

同时在cgwl_pusher/pusher.php里,把心跳间隔从30秒缩短到15秒,保持连接活跃。

坑3:uninstall.sql执行后商户数据还在
现象:执行卸载脚本后,chat_merchant表被删,但m_127分库还在,且chat_message表里仍有历史消息。
原因:uninstall.sql只删了主库表,没删分库(出于安全考虑,默认不删)。
解法:手动清理分库:

DROP DATABASE IF EXISTS m_127; DROP DATABASE IF EXISTS m_88; -- 然后重新执行uninstall.sql,它会重建主库表

5.3 日常运维必备的5条Shell命令

把这些命令做成monitor.sh,每天定时执行,能提前发现90%的问题:

#!/bin/bash # 1. 检查runtime/权限 if [ ! -w runtime/ ]; then echo "ALERT: runtime/ not writable"; fi # 2. 检查Redis连接 if ! redis-cli -h 127.0.0.1 ping &>/dev/null; then echo "ALERT: Redis down"; fi # 3. 检查cgwl_pusher进程 if ! pgrep -f "cgwl_pusher/pusher.php" &>/dev/null; then echo "ALERT: pusher process dead"; fi # 4. 检查数据库连接数(超200告警) CONNS=$(mysql -uuser -ppass -e "SHOW STATUS LIKE 'Threads_connected';" 2>/dev/null | awk 'NR==2 {print $2}') if [ "$CONNS" -gt 200 ]; then echo "ALERT: DB connections $CONNS"; fi # 5. 检查错误日志新增行数(1小时内超10行告警) ERRORS=$(grep -c "PHP Fatal error\|SQLSTATE" runtime/logs/*.log 2>/dev/null | awk -F: '{sum += $2} END {print sum+0}') if [ "$ERRORS" -gt 10 ]; then echo "ALERT: $ERRORS PHP errors in logs"; fi

6. 二次开发扩展指南:从“能用”到“好用”的跃迁路径

6.1 增加“智能分配”功能(无需改核心代码)

场景:客户希望新访客自动分配给响应最快的客服,而不是轮询。
实现思路:利用现有platform/model/StaffModel.phpgetOnlineStaffs()方法,它已返回客服在线状态和最后响应时间。

步骤1:新建application/command/AssignCommand.php

<?php namespace app\command; use think\Console; use think\console\Command; use think\console\Input; use think\console\Output; class AssignCommand extends Command { protected function configure() { $this->setName('assign:visitor') ->setDescription('Auto assign visitor to best staff'); } protected function execute(Input $input, Output $output) { // 从Redis队列取新访客(假设队列名 queue:new_visitor) $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); $visitor = $redis->lpop('queue:new_visitor'); if (!$visitor) return; $visitor = json_decode($visitor, true); // 调用分配逻辑 $staff_id = $this->findBestStaff($visitor['merchant_id']); // 写入分配结果 db('chat_session')->where('id', $visitor['session_id'])->update(['staff_id' => $staff_id]); $output->writeln("Assigned visitor {$visitor['id']} to staff {$staff_id}"); } private function findBestStaff($merchant_id) { // 查询在线客服,按响应时间升序 $staffs = db('chat_staff') ->where('merchant_id', $merchant_id) ->where('status', 'online') ->order('last_response_time', 'asc') ->limit(1) ->select(); return $staffs[0]['id'] ?? 0; } }

步骤2:注册命令到application/command.php

return [ 'assign:visitor' => 'app\command\AssignCommand', ];

步骤3:用Crontab每分钟执行

* * * * * cd /var/www/html/customer && php think assign:visitor >> /var/log/assign.log 2>&1

这样,所有新访客自动分配,且不侵入原有ChatController逻辑。

6.2 对接企业微信(比微信公众号更深度)

extra/wechat.php只支持公众号,企业微信需要额外SDK。
步骤1:安装企业微信SDK

cd /var/www/html/customer composer require wechat/wechat

步骤2:新建extra/qywechat.php

<?php require_once 'vendor/autoload.php'; use WeChat\WeChat; $config = [ 'corp_id' => 'ww1234567890abcdef', 'secret' => 'your_secret_here', 'agent_id' => 1000001, ]; $wechat = new WeChat($config); // 发送消息到企业微信客服 $wechat->message->send([ 'touser' => 'zhangsan', 'msgtype' => 'text', 'text' => ['content' => '访客咨询:'.$content], ]);

步骤3:在platform/controller/ChatController.phpsendMessage()方法末尾调用

// 发送后,同步推送到企业微信 include_once 'extra/qywechat.php'; qywechat_send_message($to_user, $content);

6.3 数据看板集成(对接Elasticsearch)

默认后台只有基础统计,想看“访客来源地域热力图”、“客服响应时长分布”,需对接ES。
步骤1:安装Logstash采集MySQL慢查询
配置logstash.conf

input { jdbc { jdbc_connection_string => "jdbc:mysql://localhost:3306/customer_main" jdbc_user => "user" jdbc_password => "pass" jdbc_driver_library => "/usr/share/java/mysql-connector-java.jar" jdbc_driver_class => "com.mysql.jdbc.Driver" statement => "SELECT * FROM chat_message WHERE created_at > :sql_last_value" } } output { elasticsearch { hosts => ["localhost:9200"] index => "chat_messages" } }

步骤2:Kibana里建可视化图表
- 地域热力图:用geoip.country_name字段;
- 响应时长分布:用response_time字段做直方图;
- 实时会话流:用created_at做时间序列图。

这样,你不用改一行PHP代码,就能获得专业级数据分析能力。


我个人在实际使用中发现,这套源码最强大的地方不是功能多,而是所有扩展都遵循同一个原则:不破坏原有结构,只做增量注入。你可以在extra/里加新功能,用command/跑定时任务,甚至把platform/整个替换成Vue SPA前端,只要API契约不变,wolive.js依然能工作。这正是它能在我手上活过7年、服务32个客户的根本原因——它不是一个封闭系统,而是一个开放的、可生长的客服操作系统。最后分享一个小技巧:每次升级前,先用diff -r old/ new/ --exclude='runtime' --exclude='vendor'对比源码差异,重点关注config.phpinstall/common.php这三个目录,90%的升级问题都能提前规避。

本文还有配套的精品资源,点击获取

简介:这是一套开箱即用的PHP多商户客服系统,专为需要统一客服平台但商户数据隔离的场景设计。系统支持多个商家独立登录后台、查看各自会话、管理客服人员及设置响应规则,所有商户数据在数据库层面通过商户ID隔离,互不干扰。前端采用wolive.js实现WebSocket长连接,保障消息实时到达,同时兼容移动端访问(mobile目录已内置适配页面)。安装过程全程可视化:访问install.php即可自动创建数据表、写入基础配置(如数据库连接、默认管理员账号)、生成必要缓存文件,无需手动执行SQL或修改config.php。Apache环境下开箱即用,.htaccess已预置URL重写规则;依赖通过Composer管理(含composer.与lock文件),便于扩展第三方组件。配套提供403/404/500错误页、favicon图标、版本标识(version.)、域名绑定配置(domain.)以及开发规范文档(CONTRIBUTING.md、README.md)。源码结构清晰,核心逻辑集中在application和platform目录,runtime为运行时自动生成目录,uninstall.sql和data.sql分别用于卸载清理与初始数据导入,适合私有化部署、定制开发或SaaS型客服中台搭建。


本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询