☰
朵米3.5客服系统私有化部署指南:PHP+MySQL+WebSocket实战
2026/10/6 8:30:57 网站建设 项目流程

简介:朵米3.5客服系统源码(2023正式版)是一套基于PHP+ThinkPHP构建的全功能客服解决方案,适合需要快速搭建在线客服平台的企业/开发者使用。其核心功能涵盖多渠道接入、智能分流、客户数据分析与报告,可帮助团队统一处理对话、优化响应效率。压缩包内共2000个文件,大小43.73MB,主要包含598个JavaScript文件、334个HTML页面、266个CSS样式表,以及PHP后端逻辑、SQL数据库脚本、Markdown/Word文档等,前端交互、服务端处理与部署说明一应俱全。包中还附带了《朵米3.5客服系统安装搭建配置文档v3.docx》与sh环境脚本,配合文档中的服务器环境要求(CentOS 7.6+、宝塔面板、Nginx等)即可按步骤完成部署。目前已有320人学习浏览,适合具备一定LNMP环境基础、希望减少重复开发的站长或外包团队用于商业化或二次开发。

1. 朵米3.5客服系统:一个可以私有化部署的客服源码,到底值不值得装

做客服系统的选型时,大多数人第一反应是去注册SaaS平台的免费版,但用上两周就会发现:工单数据在别人服务器上、访客聊天记录导不出来、坐席数量受限、想改个按钮样式都要提工单等排期。朵米3.5客服系统源码的定位恰恰是解决这些痛点——一套PHP + MySQL的完整客服系统,包含访客端、坐席工作台、工单管理、消息推送和统计报表,源码部署在自己服务器上,数据完全自理。适合手里有一台云服务器、希望用一套开源源码搭起私有化客服通道的团队。安装教程部分我拆成了「环境准备、数据库初始化、WebSocket配置、避坑」四段,照着走大约四十分钟可以跑通访客和坐席对话。

2. 部署前的准备:为什么先定 PHP 7.4 和 MySQL 5.7,而不是全网最新版

2.1 朵米3.5的运行环境要求:确认版本边界比急着上传源码更重要

朵米3.5是标准的PHP源码工程,整个系统由访客端页面、坐席工作台、后端API三部分组成。这类源码对运行环境最敏感的三个指标是PHP版本、MySQL版本和伪静态规则。常见做法是部署在LNMP(Linux + Nginx + MySQL + PHP)环境下,宝塔面板一键安装一套环境,再按源码要求微调版本。我在装这套系统时踩过最大的坑就是没确认PHP版本就上传,结果后台白屏,查了半小时PHP错误日志才发现是语言版本不兼容。

先看环境版本:朵米3.5正式版发布于2023年,源码里用到的语法以PHP 7.x为主要目标。推荐直接用PHP 7.4 + MySQL 5.7 + Nginx 1.18 + CentOS 7/Ubuntu 20.04的组合。不推荐一上来就装PHP 8.0以上,原因在于老源码里常见的each()、create_function()、花括号取字符串偏移$str{0}这类语法在PHP 8里已经被移除,直接触发Fatal Error,整站白屏。这不是改一行配置能解决的,需要逐文件兼容迁移,成本很高。

MySQL版本同样有边界。源码自带的SQL文件使用的是 utf8mb4 字符集,如果你的MySQL是老版本(5.5及以下),导入时会直接报错。就算强行改成utf8,中文字符在聊天记录里也会出现乱码和表情丢失。安装前先执行mysql --version确认版本,这是很多人忽略的一步。

2.2 用宝塔还是手动LNMP:两条安装路径怎么选

安装部署路径主要分两种:宝塔面板和手工LNMP环境。我的建议是——如果你只是想快速跑通这套客服系统、后续以配置和改模板为主,选宝塔;如果你本身在维护多台服务器、有大量定制化需求,选手工搭建。两者最终的效果没有本质差别,区别在于排查问题时你对自己环境有多少掌控力。

宝塔路径的操作步骤非常固定:安装宝塔面板 → 软件商店里安装 Nginx 1.18 + MySQL 5.7 + PHP 7.4 → 创建站点 → 上传源码 → 设置伪静态。其中最关键的两个步骤是「PHP版本选择」和「禁用函数移除」。宝塔默认会把putenv()、proc_open()等函数列入禁用列表,而部分客服系统的扩展模块(比如发送邮件、生成二维码)依赖这些函数,不放开会导致功能静默失败。安装完成后第一时间到PHP设置里把putenv、proc_open、symlink从禁用列表去掉。

手工搭建路径适合你已经在服务器上跑着别的服务、不想引入面板的情况。核心步骤是先装好Nginx、MySQL、PHP-FPM,再创建一个站点目录。下面这段是Ubuntu 20.04系统下从零装环境的命令流程:

# 安装基础组件与 PHP 7.4 及相关扩展 sudo apt update sudo apt install -y nginx mysql-server php7.4-fpm php7.4-mysql php7.4-curl \ php7.4-gd php7.4-mbstring php7.4-xml php7.4-zip php7.4-redis php7.4-bcmath # 启动服务并设置开机自启 sudo systemctl enable --now nginx sudo systemctl enable --now mysql sudo systemctl enable --now php7.4-fpm # 确认三大服务都已运行 sudo systemctl status nginx php7.4-fpm mysql --no-pager

这段命令的逻辑是先把PHP运行所需的扩展一次性装齐。其中php7.4-mbstring负责处理聊天记录里的中文和UTF-8字符,php7.4-curl用于调用第三方接口(如微信客服消息接口),php7.4-redis用于消息队列缓存。缺少任何一个扩展,系统都会在运行时报「类不存在」或「函数未定义」的错误。最后一行status命令用来快速确认三个核心服务都在运行状态,避免后续排查时把环境问题误判为源码问题。

2.3 选型原则:为什么这套源码放在LNMP而不是Docker容器里

很多人看到「php源码」第一反应是用Docker部署,毕竟docker run一条命令拉起容器很省事。我从事线上客服系统的运维角度看,Docker在这个场景下有一个实际问题:客服系统需要稳定的长连接(后面第四章详述),容器网络转发、端口映射、重启策略都会引入额外的排查变量。当访客说「消息发不出去」时,你不想花半小时去翻容器的Network bridge日志。

我一般建议把朵米3.5直接部署在宿主机LNMP环境里,理由有三个:一是源码涉及的PHP、MySQL、Redis、Nginx四层配置在宿主机上都有成熟的排查路径,出错时直接看日志文件;二是部署后可能要做二次开发(改样式、加机器人、对接API),宿主机的文件修改和重启流程更直接;三是源码本身没有做任何容器化适配,非要塞进Docker还得自己写Dockerfile维护基础镜像,相当于引入了额外工作量。

3. 安装落地:源码上传、SQL导入、后台初始化,三步跑通最小可用版

3.1 配置站点与伪静态规则:先让访客入口能打开

拿到源码压缩包并上传到服务器后,第一步是创建站点并指定运行目录。这里有一个大多数源码安装教程不会明说的细节:朵米3.5源码的入口文件(index.php)不一定在根目录,而是在/public子目录下。如果你直接把站点根目录指向/www/wwwroot/xxx,访问首页会显示目录结构或者403 Forbidden,这是典型的站点路径配置错误。

打开Nginx站点配置文件,把root指向源码的public目录。下面是我在宝塔环境里调试时常用的Nginx配置片段:

server { listen 80; server_name kefu.example.com; root /www/wwwroot/dumi/public; index index.php index.html; # 伪静态规则:将所有非真实文件的请求转发给 index.php location / { if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?s=$1 last; } } location ~ \.php$ { fastcgi_pass unix:/tmp/php-cgi-74.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }

配置的关键在于伪静态那段if块。朵米的URL格式是index.php?s=/admin/login这种PATH_INFO风格,如果Nginx不做URL重写,访问路由时直接报404。把运行目录设到public、并加上这段重写规则后,访客端首页和坐席后台登录页才能正常路由到PHP控制器。如果你用的是宝塔面板,站点设置里自带「伪静态」下拉菜单,选择「ThinkPHP」规则即可,效果和上面手写的rewrite一致。

3.2 数据库导入与配置文件指向:把代码和MySQL连起来

站点能打开后,紧接着就是数据库初始化。这套源码的安装包内通常自带一个dumi.sql文件,需要手动在MySQL中创建库并导入。数据库名、用户名、密码这三项后面都要写进源码的配置文件,建议用英文小写命名(比如dumi_db),减少编码问题。

# 登录MySQL并创建独立的数据库账号 mysql -uroot -p # 在MySQL命令行内执行 CREATE DATABASE dumi_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'dumi_user'@'localhost' IDENTIFIED BY '你的强密码'; GRANT ALL PRIVILEGES ON dumi_db.* TO 'dumi_user'@'localhost'; FLUSH PRIVILEGES; # 退出后通过命令行导入SQL文件 mysql -udumi_user -p dumi_db < /www/wwwroot/dumi/dumi.sql

导入这段SQL的后半部分是一整张数据表结构加初始数据,覆盖管理员账号、客服坐席、系统配置项、会话记录表等。需要注意utf8mb4_unicode_ci这个排序规则——如果建库时用了utf8_general_ci,后续访客昵称里的emoji表情会直接变成问号。导入完成后,打开源码根目录下的config/database.php文件,把database、username、password三个字段改成实际值。改完这一步,PHP代码就有了完整的数据库连接能力,下一步可以进后台完成最终初始化。

3.3 后台初始化:管理员账号、坐席创建与访客接入代码

数据库导入完成后,浏览器访问http://你的域名/index.php/admin进入后台。首次打开会要求设置管理员账号和密码,这一步是在admin_user表里写入初始记录,后续登录全部走这里。完成初始化后,后台左侧菜单里有「坐席管理」和「客服接入」两个核心配置项:坐席管理用来添加客服人员账号并分配权限;客服接入则会生成一段JavaScript代码,复制到你的网站页面底部即可唤起访客聊天窗口。

接入代码一般是这样的结构,以JavaScript形式引入:

<script type="text/javascript"> // 初始化朵米访客端,from_url 填当前页面的地址 var dumi = new DumiChat({ api_url: "https://kefu.example.com/index.php", from_url: window.location.href, // 自动分配给客服组,组ID以后台「客服组列表」里显示的数字为准 group_id: 1, // 开启自动弹窗,访客进入页面后3秒出现邀请文案 auto_popup: true }); </script>

这段代码的作用是把你的业务网站和客服系统连接起来。api_url必须和前面Nginx配置的域名保持完全一致,否则访客端向错误的接口发起请求,浏览器控制台会报跨域或404。group_id决定了新访客被分配给哪一组坐席,建议在后台先把坐席账号分组建好,再来改这段代码。auto_popup: true这个开关在实际使用中建议先关闭,等测试通过再开,避免访客一进页面就被弹窗打扰。

3.4 安装完成后的功能自检清单

初始化完成后,不要急着把接入代码部署到生产环境。我习惯先做一个四步自检:第一步,在访客端页面打开聊天窗口,发送一条「测试消息」;第二步,用坐席账号登录后台,确认能看到这条消息并回复;第三步,在访客端确认收到坐席回复,完成一次双向对话;第四步,刷新页面重新进入,确认聊天历史记录被正确加载。这四步全部通过,说明PHP执行、数据库读写、消息推送链路都已打通。只要任何一步失败,就按链路倒推排查——先看这一步调用的接口返回了什么,再查对应日志。

4. 消息推送与WebSocket长连接:客服系统最容易翻车的环节

4.1 为什么客服系统的消息不能纯靠请求轮询

很多初次接触客服源码的人以为「访客发消息 → 请求后端接口 → 坐席刷新页面看到」就够了,实际用起来会发现根本不行。因为客服场景要求消息延迟进入秒级,坐席不可能一直手动刷新页面。HTTP协议本身是单向的——浏览器发出请求,服务器返回响应,连接就结束了。想要实现「坐席在线时访客消息立即冒出来」,只有两条路:短轮询或长连接。

短轮询能做到,代价是每两三秒发起一次HTTP请求。10个坐席在线时,每个坐席对应多个会话页面,瞬间几十个请求打到Nginx和PHP-FPM上,服务器负载直接飙高。更麻烦的是消息有延迟——访客发完消息到坐席看到,平均要多等一次轮询周期。朵米3.5的处理方式是使用WebSocket长连接:访客端和坐席端都通过WS与服务器建立持久连接,服务端有新消息时直接推给对应的连接。这套机制下,消息到达坐席页面基本是实时的,服务器只在真正有新事件时才推送数据。安装时WebSocket服务没起来,系统看起来「能登录但收发消息断断续续」,九成是长连接建立失败。

4.2 在Nginx中为WebSocket做反向代理:配置与参数说明

朵米3.5的WebSocket服务由系统内置的Swoole或Workerman进程提供(取决于2023正式版的打包方式),监听一个独立的TCP端口(常见为2346或8282)。浏览器默认只能发起HTTP/HTTPS请求,无法直接连接非80端口的WS服务,所以需要Nginx做反向代理:把wss://kefu.example.com/ws转发到内网的WS服务端口。

# Nginx WebSocket 反向代理配置,放在 http 块内 map $http_upgrade $connection_upgrade { default upgrade; '' close; } # 在 server 块中增加一段 location,指向 WS 服务 location /ws { proxy_pass http://127.0.0.1:2346; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_read_timeout 60s; proxy_send_timeout 60s; }

这段配置里的map指令是关键。WebSocket连接建立时有一个HTTP Upgrade握手过程,Nginx需要正确识别Upgrade: websocket这个请求头并把它透传给后端,否则握手失败,前端页面一直报「WebSocket is closed before the connection is established」。proxy_read_timeout和proxy_send_timeout用来控制长连接的空闲保持时间。客服场景下,坐席可能三五分钟不发消息但依然保持在线,默认60秒超时会导致连接被Nginx掐断,需要调长到300秒以上才稳定。

4.3 后端服务进程、防火墙端口与协议路径的统一

反向代理配置完成后,还要确认两件事:后台有没有启动WS服务进程,以及服务器防火墙是否放行了这个端口。如果服务没启动,代理转发过去连接直接拒绝;如果端口没放行,云服务器安全组和本机iptables都会拦掉流量。手动启动WS服务并确认监听端口:

# 进入源码根目录,启动 WebSocket 服务进程(不同打包方式命令略有差异) cd /www/wwwroot/dumi php think worker:start -d # 检查端口监听状态,2346 为默认端口,以源码配置为准 netstat -tlnp | grep 2346

启动命令里的-d参数表示以守护进程方式后台运行,关闭终端后连接不掉。worker:start是这套系统封装好的管理命令,底层调用了Workerman/Swoole的进程管理逻辑。跑完netstat能确认Nginx代理的端口与后端监听端口完全一致。常见的一个隐蔽坑是:代码配置文件里写的WS端口是2346,但你启动服务时用了另一个端口,两者不一致时前端界面表现是「连接中」转几秒后失败。检查时先看后端端口,再看Nginx的proxy_pass指向,最后回到源码配置文件确认路径。

5. 朵米3.5安装避坑:现象、原因、解决,五条血泪经验

5.1 首页访问返回404,站点根目录明显不对

现象:浏览器访问站点域名直接出现404 Not Found,或显示出文件的目录索引结构,而不是客服系统的登录页。

原因:Nginx的站点配置里root指向了源码根目录,而朵米的入口文件在/public子目录里,Nginx找不到对应的index.php,于是走了404规则。

解决:打开Nginx站点配置,把root改为站点目录/public,同时补上ThinkPHP风格的伪静态规则。如果用的是宝塔,在站点设置里直接选择「ThinkPHP」伪静态规则保存。改完重启Nginx再访问一次,首页能出来说明路径问题解决了。顺带确认一下源码目录里是否还存在一个index.php的二级入口,有些源码包把后台入口单独拆出来了,站点根目录设错时会互相干扰。

5.2 消息发不出去、前端一直处于「连接中」状态

现象:访客端能打开聊天窗口,输入消息点击发送后气泡一直停在「发送中」,或者页面顶部提示连接已断开。

原因:多数情况下是WebSocket连接没建立成功,要么是WS服务进程没启动,要么是Nginx没有正确代理WS握手请求。少数情况是浏览器通过wss://访问,但Nginx只监听了HTTP的80端口,证书未配置导致握手失败。

解决:先按第四章的方法确认后端WS进程在运行且端口监听正常;接着确认Nginx配置里写的是wss://域名对应的443端口;最后打开浏览器开发者工具切到Network面板,找到WS连接请求,看Handshake是成功还是失败。如果看到403,检查Nginx配置里proxy_set_header Host $host;是否携带了正确的域名头。这套链路我排查过不下十次,大部分是启动进程的端口和Nginx代理端口不一致,其次是代理头缺失。

5.3 数据库导入后聊天记录里的中文变成乱码

现象:系统能正常登录,但访客昵称、聊天消息里所有中文字符都显示成菱形问号「�」。

原因:建库时指定的字符集不是utf8mb4,或数据库连接层使用的字符集与表结构不一致。MySQL在连接建立时会按默认字符集传输数据,如果默认是latin1,UTF-8编码的中文会直接损坏。

解决:把数据库文件用下面这条命令重新建库导入。如果已经导入过数据,需要改库默认字符集再重建表数据:

ALTER DATABASE dumi_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE dumi_chat_msg CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

另一处容易忽略的是PHP连接串里的charset。朵米的数据库配置文件config/database.php里有一项charset,确保它是utf8mb4而不是utf8。改完重启PHP-FPM,重新插入一条中文消息测试。之前遇到过一次类似情况,改了库没改PHP连接串,问题照样存在,因为PHP和MySQL握手时使用的是配置里的字符集参数,它会把查询指令里的中文参数按错误编码传给服务端。

5.4 PHP版本升至8.0后后台直接白屏

现象:拿到源码后不知情的运维把PHP版本切换成8.0或8.1,打开后台页面直接出现白色空白页,浏览器控制台没有任何报错。

原因:PHP 8.0移除了部分老函数,源码中调用到这些函数时直接导致Fatal Error,而PHP-FPM默认错误显示是关闭的,页面上自然是一片空白。

解决:第一步在PHP配置文件php.ini里打开display_errors = On,重启PHP-FPM,再刷新页面看到具体报错位置;第二步从报错文件里定位到具体函数,把each()改成foreach替代写法,create_function()改成匿名函数function() use ($args)。如果你只是要跑通系统、不想改代码,最稳妥的方案是把PHP版本锁回7.4,这也是我在前面建议不要用最新版的原因。源码毕竟是2023年发布的正式版,经过线上验证的版本边界比追求新版本更可靠。

5.5 坐席离线后访客消息没有邮件/短信提醒

现象:坐席(客服人员)退出登录,访客发出新消息,坐席没有收到任何通知,直到重新登录才在历史会话里看到。

原因:离线消息通知依赖定时任务(crontab)按固定周期扫描待办会话并触发通知。源码安装教程里如果没提这一条,大多数团队会漏掉配置。

解决:在服务器上添加crontab定时任务,确保每隔一分钟检查一次离线消息并触发通知:

# 编辑定时任务表 crontab -e # 追加一行:每分钟进入源码目录执行消息通知扫描 */1 * * * * cd /www/wwwroot/dumi && php think noticemessage >> /var/log/dumi_cron.log 2>&1

这条定时任务的逻辑由源码里think noticemessage命令实现,主要扫描chat_session中最后一条消息超过设定时间仍未回复的会话。日志重定向到独立文件里,方便后续排查任务有没有执行成功。我在真实环境里遇到过一次通知不触发的问题,最后定位到定时任务执行时PHP环境变量缺失,在crontab -e里加一行export PATH=/usr/local/php/bin:$PATH解决了。这个坑比较隐蔽,建议新装定时任务后先手动执行一次命令确认不会报错,再交给cron接管。

6. 进阶:日志排错、自动备份与二次开发的第一个起点

先把「出了问题去哪看」这步做好,运维这套系统才不会心慌。朵米基于ThinkPHP框架,运行日志在runtime/log目录下按日期归档,PHP错误会写到php.ini中error_log指定的文件。排查问题时打开这些日志文件,能省掉大半瞎猜的时间。其次建议写一个简单的定时备份脚本,每天凌晨自动备份数据库和源码目录,这里备份脚本的核心是mysqldump与tar的组合:

#!/bin/bash # 数据库备份:排除缓存表,压缩保存到指定目录 mysqldump -udumi_user -p'密码' dumi_db --ignore-table=dumi_db.dumi_cache \ | gzip > /data/backup/dumi_db_$(date +%F).sql.gz # 源码目录备份:排除 runtime 日志和缓存 tar czf /data/backup/dumi_src_$(date +%F).tar.gz \ --exclude='runtime' --exclude='.git' /www/wwwroot/dumi # 保留最近30天备份,删除更早文件 find /data/backup -mtime +30 -name "*.gz" -delete

最后说一句二次开发的起点。这套源码自由度最高的修改点是访客端模板,目录通常在public/static或application/index/view里,找chat_window.html这类文件名改即可。想改聊天窗口配色、按钮位置、欢迎语,直接改模板和CSS,不用碰后端逻辑。我个人的习惯是先备份原文件再改,改一步刷新一次页面,CSS样式翻车很容易,有了备份就有后悔药。

这整套客服系统跑通之后,后续的维护重点其实不是功能,而是连接稳定性和数据安全。定期查看WS进程存活状态、每天确认备份文件大小正常、每季度清理一次积压的会话日志,做到这三点基本不会出大事。希望帮到你。

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

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

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

立即咨询