在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
需求说明
当潜在客户提交表单并选择一项或多项服务后,需完成两个核心操作:
- 筛选出能提供对应服务的专业人员;
- 通过Round Robin(轮询)算法将潜在客户依次循环分配给这些专业人员,分配示例如下:
| id | professional_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 1 |
| 5 | 2 |
| 6 | 3 |
| 7 | 1 |
| 8 | 2 |
| 9 | 3 |
| 10 | 1 |
实现方案
步骤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
相关产品推荐
相关产品推荐

