You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Node.js中基于服务匹配用Round Robin轮询分配潜在客户

Node.js实现潜在客户轮询分配逻辑方案

数据库表结构

professionals表

id 
firstname
lastname

services表

id 
name
description

professional_services表(关联表)

service_id
professional_id

leads表

id 
professional_id
firstname
lastname
email
phone

需求说明

当潜在客户提交表单并选择一项或多项服务后,需完成两个核心操作:

  1. 筛选出能提供对应服务的专业人员;
  2. 通过Round Robin(轮询)算法将潜在客户依次循环分配给这些专业人员,分配示例如下:
idprofessional_id
11
22
33
41
52
63
71
82
93
101

实现方案

步骤1:筛选符合条件的专业人员

通过关联查询筛选出提供指定服务的专业人员,以下提供两种实现方式:

原生SQL查询

const getQualifiedProfessionals = async (serviceIds) => {
  const placeholders = serviceIds.map(() => '?').join(',');
  const query = `
    SELECT DISTINCT p.id, p.firstname, p.lastname
    FROM professionals p
    JOIN professional_services ps ON p.id = ps.professional_id
    WHERE ps.service_id IN (${placeholders})
  `;
  const [rows] = await db.query(query, [serviceIds]);
  return rows;
};

Sequelize ORM查询

// 前提:已定义模型关联:Professional.belongsToMany(Service, { through: 'professional_services' })
const getQualifiedProfessionals = async (serviceIds) => {
  return await Professional.findAll({
    include: [
      {
        model: Service,
        where: { id: serviceIds },
        through: { attributes: [] } // 不返回关联表冗余字段
      }
    ],
    distinct: true
  });
};

步骤2:实现Round Robin分配逻辑

核心是维护一个分配索引,每次分配后索引递增并对专业人员数量取模,确保循环分配。根据部署场景选择不同方案:

方案A:数据库维护全局索引(适合多进程/分布式场景)

通过数据库存储分配索引,避免多实例间状态不一致:

const assignLeadToProfessional = async (leadData, serviceIds) => {
  // 获取符合条件的专业人员
  const professionals = await getQualifiedProfessionals(serviceIds);
  if (professionals.length === 0) throw new Error('无符合条件的专业人员');

  // 生成服务组合唯一标识(用于区分不同服务组的分配状态)
  const serviceGroupKey = JSON.stringify(serviceIds.sort());
  
  // 加锁查询当前索引,避免并发冲突
  const [config] = await db.query(
    'SELECT current_index FROM round_robin_config WHERE service_group = ? FOR UPDATE',
    [serviceGroupKey]
  );
  let currentIndex = config ? config.current_index : 0;

  // 分配专业人员
  const assignedPro = professionals[currentIndex % professionals.length];
  leadData.professional_id = assignedPro.id;

  // 更新索引
  const newIndex = (currentIndex + 1) % professionals.length;
  if (config) {
    await db.query(
      'UPDATE round_robin_config SET current_index = ? WHERE service_group = ?',
      [newIndex, serviceGroupKey]
    );
  } else {
    await db.query(
      'INSERT INTO round_robin_config (service_group, current_index) VALUES (?, ?)',
      [serviceGroupKey, newIndex]
    );
  }

  // 保存潜在客户记录
  await Lead.create(leadData);
  return assignedPro;
};

方案B:内存维护索引(适合单进程服务)

单实例Node.js服务可直接用内存变量维护索引,无需额外存储:

// 全局对象存储不同服务组合的分配索引
const roundRobinIndexes = {};

const assignLeadToProfessional = async (leadData, serviceIds) => {
  const professionals = await getQualifiedProfessionals(serviceIds);
  if (professionals.length === 0) throw new Error('无符合条件的专业人员');

  // 生成服务组合唯一键
  const serviceKey = JSON.stringify(serviceIds.sort());
  // 初始化索引(若不存在)
  if (!roundRobinIndexes[serviceKey]) {
    roundRobinIndexes[serviceKey] = 0;
  }

  // 分配专业人员
  const assignedPro = professionals[roundRobinIndexes[serviceKey] % professionals.length];
  leadData.professional_id = assignedPro.id;

  // 更新索引
  roundRobinIndexes[serviceKey] = (roundRobinIndexes[serviceKey] + 1) % professionals.length;

  // 保存潜在客户
  await Lead.create(leadData);
  return assignedPro;
};

注意事项

  • 多进程/分布式场景必须用数据库或Redis维护全局索引,避免分配错乱;
  • 筛选专业人员时需用DISTINCT去重,防止同一专业人员因关联多个服务被重复返回;
  • 当符合条件的专业人员数量变化时(新增/移除),需重置对应服务组的分配索引,避免索引越界。

内容的提问来源于stack exchange,提问作者Prince J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 15:27:46