
简介网页多商户客服系统whisper-v2.1.11是一套面向多商户在线客服场景的完整源码包适合需要搭建多租户客服平台的中高级PHP开发者、站长及二次开发团队。系统以多商户隔离为核心可支撑商户独立接入、会话分配与后台管理等典型需求帮助解决多商户客服统一部署与权限划分的问题。压缩包共1890个文件约27.85MB以801个php业务代码为主体辅以215个js交互脚本、59个css与123个png、269个gif等前端与图标资源另有97个html模板、81个asciidoc接口文档、26个json配置及sql、db数据库文件结构完整、层次分明。目前已有1138人学习下载热度较高。包内附带的asciidoc文档覆盖客户端、索引、集群、连接池、搜索等模块说明配合完整源码与数据库脚本读者可快速理解系统架构、接口调用与部署思路并在此基础上进行功能扩展与二次开发具备较高的参考与复用价值。1. 网页多商户客服系统 whisper-v2.1.11一套能私有化部署的多租户工单与在线沟通底座如果你手上同时跑着好几个品牌站、独立站或者 SaaS 子站每个站都要挂在线客服又不想给每个站单独买一套 SaaS 坐席那 whisper-v2.1.11 这套网页多商户客服系统就是冲这个场景来的。它的核心定位是「一套后端、多商户隔离、网页端接入」商户各自管理自己的客服、会话、工单和访客平台方统一运维。很多人搜「whisper 是什么」其实这里说的不是语音识别那个 Whisper而是这套客服系统的项目代号v2.1.11 是它的一个迭代版本。它适合中小团队自建客服中台也适合做二次开发接进已有业务系统。下面我按「它怎么隔离商户 → 怎么跑起来 → 怎么接站 → 坑在哪 → 怎么验证」的顺序拆一遍。2. 多商户隔离怎么落地租户模型、数据边界与坐席分配2.1 先想清楚「商户」在这套系统里到底是什么多商户客服系统最容易翻车的地方不是功能少而是隔离没做干净。whisper-v2.1.11 里一个商户tenant本质上是一个逻辑容器它下面挂着四类资源坐席账号、访客会话、工单记录、渠道配置。平台超管能看到所有商户商户管理员只能看到自己名下的数据。这个边界如果只靠前端隐藏菜单来实现那基本等于没隔离所以真正要盯的是后端每一次查询有没有带上 tenant_id 条件。常见做法是所有业务表都冗余一个 tenant_id 字段并且在 DAO 层做统一拦截而不是在每个接口里手写 where。我一般会先确认三件事登录态里有没有绑定当前商户、跨商户查询有没有被中间件拦住、文件上传目录是不是按商户分目录存放。这三点决定了这套系统能不能真的拿去给多个客户用。2.2 租户、坐席、会话三张核心表的关系理解数据模型后面配置和排错都会顺很多。下面这张表是我梳理出来的核心关系字段名以实际库表为准这里给的是通用结构方便你对照自己的部署。资源关键字段隔离方式说明商户 tenantid、name、status主键平台级容器停用后其下坐席无法登录坐席 agentid、tenant_id、roletenant_id 过滤同一账号不建议跨商户复用会话 sessionid、tenant_id、visitor_id、agent_idtenant_id 过滤访客身份按商户维度独立工单 ticketid、tenant_id、status、assigneetenant_id 过滤状态流转要防跨商户指派渠道 channelid、tenant_id、type、configtenant_id 过滤网页接入代码按商户生成坐席分配这块whisper-v2.1.11 支持按商户配置接待规则。常见的有轮询、空闲优先、指定分组。这里有个细节轮询计数如果存在全局缓存里而不是按商户维度存多商户同时接待时会互相干扰表现为「A 商户的访客被分给了 B 商户刚空闲的坐席」。排查时优先看分配器的 key 有没有拼 tenant_id。2.3 用一段伪代码确认隔离是否生效部署完别急着接站先用一段查询验证隔离。下面是我常用的自检思路语言用 Python 示意重点是看 tenant_id 有没有贯穿。# 伪代码验证多商户数据隔离是否生效 def get_sessions(current_agent, keywordNone): tenant_id current_agent.tenant_id # 登录态必须带商户 assert tenant_id, 坐席未绑定商户隔离失效 query Session.query.filter(Session.tenant_id tenant_id) # 强制过滤 if keyword: query query.filter(Session.content.like(f%{keyword}%)) return query.all() # 反例只按 agent_id 查跨商户会串数据 def bad_get_sessions(agent_id): return Session.query.filter(Session.agent_id agent_id).all()逻辑说明第一段函数把 tenant_id 作为强制条件无论前端传什么参数都绕不过去第二段是典型的错误写法只按坐席查一旦坐席被复用或者 ID 规则不严就会读到别的商户会话。参数上tenant_id 应该来自服务端登录态而不是前端传参这点是隔离能不能守住的关键。你可以在测试环境建两个商户各造几条会话然后用 A 商户的账号去搜 B 商户的关键词搜不到才算过关。3. 把 whisper-v2.1.11 跑起来环境、初始化与网页接入3.1 部署前的环境清单与依赖确认这套系统是网页端客服后端通常是常见的 Web 技术栈前端是管理后台加访客聊天窗。部署前先把环境对齐能省掉一半玄学问题。我一般按这个清单过一遍运行环境确认语言运行时版本与项目要求一致版本差一个大版本经常出兼容问题数据库MySQL 或同类关系库字符集用 utf8mb4否则访客昵称里的特殊字符会乱码缓存Redis 之类用于坐席在线状态和会话分配计数Web 服务Nginx 反代注意 WebSocket 的 upgrade 头要透传目录权限上传目录、日志目录、缓存目录要给写权限提示先把数据库字符集和 WebSocket 反代配好再导入初始化数据顺序反了后面改起来很麻烦。3.2 初始化数据库与创建第一个商户环境就绪后导入初始化脚本然后创建平台超管和第一个商户。命令行示意如下# 导入初始化结构以实际脚本名为准 mysql -u root -p whisper install/whisper_schema.sql # 执行初始化创建平台超管 php think init:admin --usernamesuper --password你的强密码 # 创建第一个商户并生成该商户的接入标识 php think tenant:create --name示例商户 --domaindemo.example.com逻辑说明第一条导入表结构注意库名和字符集第二条创建平台级超管这个账号能跨商户密码要强第三条创建商户命令会返回一个商户标识tenant key后面网页接入代码要用到它。参数上domain 用于区分不同商户的接入来源如果你打算用同一域名下不同路径区分商户那这里可以留空改由接入代码里的 tenant key 决定。执行完登录后台确认能看到商户列表再进商户详情确认坐席、渠道菜单都在。3.3 网页端接入把聊天窗挂到你的站点接入是这套系统对外的门面。whisper-v2.1.11 一般提供一段 JS 引入代码按商户生成。典型接入方式如下!-- 在需要客服的页面 body 末尾引入 -- script window.whisperConfig { tenantKey: 你的商户标识, // 决定会话归属哪个商户 apiBase: https://kf.example.com, // 客服系统服务地址 autoOpen: false, // 是否自动弹出聊天窗 theme: #2b6cb0 // 聊天窗主色按品牌调 }; /script script srchttps://kf.example.com/static/whisper.js async/script逻辑说明tenantKey 是最关键的参数它决定这个访客会话落到哪个商户填错就会「访客发消息另一个商户收到」。apiBase 指向客服系统地址跨域时要在服务端配好允许来源。autoOpen 建议先设 false调试阶段手动点开避免一进页面就弹窗干扰排查。theme 只是外观不影响功能。接入后打开浏览器控制台看 whisper.js 是否加载成功、WebSocket 是否连上再发一条测试消息去对应商户后台确认能收到。3.4 坐席接待规则与工单流转配置接入通了之后配接待规则。进商户后台的客服管理建坐席、分组然后设分配策略。工单这块whisper-v2.1.11 支持状态流转和指派常见状态是待处理、处理中、已解决、已关闭。配置时注意两点一是工单指派要限制在本商户坐席内二是状态流转最好加权限别让普通坐席随便关闭别人的工单。这两点配好多商户协作才不会乱。4. 避坑与排查多商户客服系统最容易翻车的五个点4.1 访客消息串到别的商户现象A 商户的访客发消息B 商户后台收到了。原因接入代码里的 tenantKey 填错或者后端会话创建时没从 tenantKey 解析商户而是用了默认商户。解决先核对页面里的 tenantKey再查后端创建会话的入口确认 tenant_id 来源是接入标识而不是兜底值。测试时用两个商户各接一个页面交叉发消息验证。4.2 坐席在线状态显示不准现象坐席明明在线访客却分配不进来或者显示离线。原因在线状态存在缓存里WebSocket 断线后没有及时清理或者多实例部署时缓存没共享。解决确认缓存是共享的检查心跳和断线回调有没有更新状态多实例场景下别用本地内存存在线状态。4.3 WebSocket 连不上消息只能刷新才看到现象聊天窗能打开但消息不实时刷新页面才出现。原因Nginx 反代没透传 Upgrade 和 Connection 头或者超时时间太短。解决在反代配置里补上 upgrade 相关头把 proxy_read_timeout 调大确认服务端 WebSocket 端口可达。4.4 文件上传跨商户可访问现象A 商户上传的图片拿到链接后 B 商户也能打开。原因上传目录没有按商户隔离或者文件访问没有鉴权。解决上传路径按 tenant_id 分目录访问时校验当前登录态是否有权访问该商户文件敏感文件不要用纯静态直链。4.5 升级版本后历史会话丢失现象从旧版本升到 v2.1.11 后部分会话查不到。原因升级脚本改了表结构或加了 tenant_id 字段历史数据没回填。解决升级前备份升级后检查回填脚本是否执行确认老数据的 tenant_id 有默认归属别让历史会话变成孤儿数据。5. 验证与进阶用压测和日志把多商户系统盯住5.1 用并发测试验证隔离与分配功能通了不代表扛得住。我一般会做一轮并发验证模拟两个商户各若干访客同时发消息观察分配是否串商户、坐席状态是否准确、消息有没有丢。可以用简单的脚本压重点看三个指标会话归属正确率、消息到达延迟、坐席分配是否均衡。下面是个压测思路示意# 用并发工具模拟多商户访客示意工具按实际选 for tenant in tenantA tenantB; do for i in $(seq 1 50); do curl -s -X POST https://kf.example.com/api/session \ -H Content-Type: application/json \ -d {\tenantKey\:\$tenant\,\visitor\:\v$i\} done done wait逻辑说明这段并发创建会话tenantKey 分别用两个商户跑完去后台核对会话归属。参数上并发数按你预期峰值调别一上来就压满。观察点在于有没有会话落到错误商户、有没有创建失败。压完再看日志里有没有 tenant_id 为空的记录那通常就是隔离漏点。5.2 日志与监控要按商户维度切多商户系统排错最怕日志混在一起。建议在日志里统一带上 tenant_id出问题时能按商户过滤。监控上重点盯每个商户的会话量、坐席在线数、消息积压。如果某个商户突然会话暴涨可能是被刷或者接入代码被滥用这时候按商户限流比全局限流更精准。5.3 二次开发的边界如果你要基于 whisper-v2.1.11 做二次开发记住一条任何新增查询都要带 tenant_id任何新增缓存 key 都要拼商户维度。我见过太多项目在加功能时忘了这条上线后才发现数据串了。从那以后我每次加接口都强制走一遍「登录态取 tenant_id → 查询强制过滤 → 缓存 key 带商户」这三步宁可多写一行也不留隔离隐患。希望帮到你。本文还有配套的精品资源点击获取