基于ThinkPHP的多商户在线客服系统架构解析

一、多商户架构设计核心原则

多商户在线客服系统的核心挑战在于实现数据隔离与权限控制的平衡。系统需支持不同商户独立管理客服团队、会话记录及配置规则,同时保证底层框架的统一性。采用”单实例多租户”模式可有效降低运维成本,每个商户拥有独立的数据库前缀或Schema,通过中间件实现数据路由。

权限控制体系需实现三级隔离:系统管理员>商户管理员>客服人员。建议采用RBAC(基于角色的访问控制)模型,结合ThinkPHP的权限中间件实现细粒度控制。例如商户管理员可配置客服排班规则,但无法访问其他商户数据。

  1. // 权限中间件示例
  2. class MerchantAuth
  3. {
  4. public function handle($request, Closure $next)
  5. {
  6. $merchantId = session('merchant_id');
  7. $requiredMerchant = $request->route('merchant_id');
  8. if ($merchantId != $requiredMerchant) {
  9. throw new \Exception('无权访问其他商户数据');
  10. }
  11. return $next($request);
  12. }
  13. }

二、核心模块技术实现

1. 实时通信层构建

WebSocket是构建在线客服的核心技术,推荐使用Workerman或Swoole扩展提升并发能力。系统需支持三种通信模式:访客与客服单聊、客服组内协作、管理员广播通知。消息协议设计应包含:

  • 消息类型标识(文本/图片/文件)
  • 发送方身份指纹
  • 时间戳与消息ID
  • 商户专属标识
  1. // 消息协议封装示例
  2. class ChatMessage {
  3. const TYPE_TEXT = 1;
  4. const TYPE_IMAGE = 2;
  5. public function __construct($type, $content, $sender) {
  6. $this->packet = [
  7. 'type' => $type,
  8. 'content' => $content,
  9. 'sender' => $sender,
  10. 'timestamp' => time(),
  11. 'merchant_id' => session('merchant_id')
  12. ];
  13. }
  14. public function encode() {
  15. return json_encode($this->packet);
  16. }
  17. }

2. 智能路由算法设计

会话分配需综合考虑客服负载、技能标签、访客来源等因素。推荐实现加权轮询算法,基础权重由管理员配置,动态权重根据实时负载调整:

  1. 最终权重 = 基础权重 × (1 - 当前会话数/最大会话数)

数据库设计应包含客服状态表(online_status),字段包括:

  • 客服ID
  • 当前会话数
  • 最后活动时间
  • 技能标签数组
  • 商户ID

3. 数据隔离实现方案

数据库层面建议采用”分表不分库”策略,为每个商户创建独立的前缀表。例如访客表结构为:

  1. CREATE TABLE merchant_1_visitor (
  2. id INT AUTO_INCREMENT,
  3. session_id VARCHAR(64),
  4. entry_url VARCHAR(255),
  5. -- 其他字段
  6. );
  7. CREATE TABLE merchant_2_visitor (
  8. -- 同结构
  9. );

模型层通过重写基类方法实现自动表名替换:

  1. class BaseModel extends Model
  2. {
  3. protected function getTableName()
  4. {
  5. $merchantId = session('merchant_id');
  6. $prefix = 'merchant_' . $merchantId . '_';
  7. return $prefix . parent::getTableName();
  8. }
  9. }

三、性能优化与扩展设计

1. 连接管理优化

采用Redis实现WebSocket连接的心跳检测与状态同步。建议设置三级缓存:

  • 内存缓存(5秒过期):活跃连接列表
  • Redis缓存(30秒过期):连接详细信息
  • 数据库持久化:会话结束时写入
  1. // 连接管理示例
  2. class ConnectionManager {
  3. protected $redis;
  4. public function __construct() {
  5. $this->redis = new Redis();
  6. $this->redis->connect('127.0.0.1', 6379);
  7. }
  8. public function addConnection($connId, $merchantId) {
  9. $key = "conn:{$merchantId}:{$connId}";
  10. $this->redis->setex($key, 30, json_encode([
  11. 'last_active' => time(),
  12. 'customer_id' => session('customer_id')
  13. ]));
  14. }
  15. }

2. 横向扩展架构

当商户数量超过500或并发连接超过1万时,建议采用分片部署方案。按商户ID哈希值分配服务节点,通过Nginx的stream模块实现TCP层负载均衡。配置示例:

  1. stream {
  2. upstream websocket_backend {
  3. server 10.0.0.1:2345;
  4. server 10.0.0.2:2345;
  5. hash $binary_remote_addr consistent;
  6. }
  7. server {
  8. listen 8080;
  9. proxy_pass websocket_backend;
  10. proxy_http_version 1.1;
  11. proxy_set_header Upgrade $http_upgrade;
  12. proxy_set_header Connection "Upgrade";
  13. }
  14. }

四、部署与运维建议

  1. 环境隔离:为不同商户提供独立的PHP-FPM池,通过systemd管理进程
  2. 监控体系:集成Prometheus监控连接数、消息延迟等关键指标
  3. 备份策略:每日全量备份+实时binlog增量备份
  4. 升级方案:采用蓝绿部署,通过商户ID路由实现无缝切换

建议配置参数优化:

  1. ; php.ini优化
  2. max_execution_time = 300
  3. memory_limit = 512M
  4. ; swoole.ini配置
  5. worker_num = cpu核心数 * 2
  6. task_worker_num = cpu核心数

该架构方案已在多个中大型项目中验证,可支撑单实例万级并发连接,商户扩展无需修改核心代码。实际部署时建议先进行压力测试,根据QPS指标调整连接池和缓存策略。对于超大规模部署,可考虑引入消息队列解耦各模块,使用Kafka实现会话记录的异步持久化。