47 lines
1.5 KiB
PHP
47 lines
1.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace app\inquery\utils;
|
||
|
|
|
||
|
|
use support\Redis;
|
||
|
|
use support\exception\BusinessException;
|
||
|
|
|
||
|
|
class RoundRobinAssigner
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* Redis key 前缀
|
||
|
|
* 你可以按业务分组:比如 "fb_inquiry", "website_inquiry" 等
|
||
|
|
*/
|
||
|
|
protected const REDIS_KEY_PREFIX = 'inquiry:assign:round_robin:';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 从一组业务员 ID 中按轮询规则返回“下一个业务员 ID”
|
||
|
|
*
|
||
|
|
* @param int[] $userIds 业务员 ID 列表(有顺序)
|
||
|
|
* @param string $groupKey 分配分组标识(同一个分组共享一个轮询指针)
|
||
|
|
*
|
||
|
|
* @return int 下一位业务员 ID
|
||
|
|
* @throws BusinessException
|
||
|
|
*/
|
||
|
|
public static function nextUserId(array $userIds, string $groupKey = 'default'): int
|
||
|
|
{
|
||
|
|
// 校验
|
||
|
|
$userIds = array_values(array_filter(array_map('intval', $userIds), fn($v) => $v > 0));
|
||
|
|
if (empty($userIds)) {
|
||
|
|
throw new BusinessException('轮询分配业务员失败:业务员列表不能为空');
|
||
|
|
}
|
||
|
|
|
||
|
|
$count = count($userIds);
|
||
|
|
|
||
|
|
// Redis key:同一个 groupKey 共用一条轮询链
|
||
|
|
$key = self::REDIS_KEY_PREFIX . $groupKey;
|
||
|
|
|
||
|
|
// 使用 INCR 做自增计数,天然是原子操作,适合并发环境
|
||
|
|
// 第一次调用 INCR 返回 1,我们用 (value - 1) 来做索引,从 0 开始
|
||
|
|
$val = Redis::incr($key); // 如果不存在,Redis 会先置为 0 再加 1
|
||
|
|
|
||
|
|
$index = ($val - 1) % $count;
|
||
|
|
|
||
|
|
return $userIds[$index];
|
||
|
|
}
|
||
|
|
}
|