☰
网页多商户客服系统 whisper-v2.1.11:私有化部署与多租户隔离实战
2026/9/28 5:35:59 网站建设 项目流程

简介:网页多商户客服系统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, keyword=None): 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 --username=super --password=你的强密码 # 创建第一个商户,并生成该商户的接入标识 php think tenant:create --name="示例商户" --domain=demo.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 src="https://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 带商户」这三步,宁可多写一行,也不留隔离隐患。希望帮到你。

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

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

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

立即咨询