遇到一个苦恼的问题,没有思路

是这样的,我们公司有个工单系统,每天系统会收到工单,并保存在工单池里;
然后在每天早上八点开始分配工单到客服,每个客服有一个最大的工单接收数,超过之后就不在接收工单;

比如:A:10,B:20,C:5,D:15,四个客服依次对应的最大接收数;
工单池:50个工单,分配的时候顺序分配,ABCDABCD….一直到某个客服接收到最大,然后剔除掉,再继续分配;
现在我工单池的订单拿到了,客服的设置也拿到了,就是不知道如何顺序分配!!!
有人能提供一下思路吗,感激不尽!

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
最佳答案
// 客服容量
$staffCap = [
    'A' => 10,
    'B' => 20,
    'C' => 5,
    'D' => 15,
];
// 工单
$tickets = [
    '0001',
    '0002',
    '0003',
    '0004',
];

$seats = [];
foreach ($staffCap as $staff => $cap) {
    for ($i = 1; $i <= $cap; $i++) {
        $seats[] = $staff;
    }
}
shuffle($seats);
while (!empty($tickets)) {
    $seat = array_pop($seats);
    if (empty($seat)) {
        echo "接待满了" . PHP_EOL;
        break;
    }
    $ticket = array_pop($tickets);
    echo "$seat 接待了 $ticket" . PHP_EOL;
}

echo "还有 " . count($tickets) . " 个工单未接待" . PHP_EOL;
echo "还可接待 " . count($seats) . " 个工单" . PHP_EOL;

输出


D 接待了 0004
A 接待了 0003
B 接待了 0002
C 接待了 0001
还有 0 个工单未接待
还可接待 46 个工单
1年前 评论
讨论数量: 52
// 客服容量
$staffCap = [
    'A' => 10,
    'B' => 20,
    'C' => 5,
    'D' => 15,
];
// 工单
$tickets = [
    '0001',
    '0002',
    '0003',
    '0004',
];

$seats = [];
foreach ($staffCap as $staff => $cap) {
    for ($i = 1; $i <= $cap; $i++) {
        $seats[] = $staff;
    }
}
shuffle($seats);
while (!empty($tickets)) {
    $seat = array_pop($seats);
    if (empty($seat)) {
        echo "接待满了" . PHP_EOL;
        break;
    }
    $ticket = array_pop($tickets);
    echo "$seat 接待了 $ticket" . PHP_EOL;
}

echo "还有 " . count($tickets) . " 个工单未接待" . PHP_EOL;
echo "还可接待 " . count($seats) . " 个工单" . PHP_EOL;

输出


D 接待了 0004
A 接待了 0003
B 接待了 0002
C 接待了 0001
还有 0 个工单未接待
还可接待 46 个工单
1年前 评论

走队列分配,每个工位接完足够的单后,从队列中剔除掉

1年前 评论

不好组织语言 处理方法还是挺多的 :joy:

1年前 评论
铁牛 (楼主) 1年前
Junwind

按人员顺序,依次从工单池中随机取一个出来。
如,人员A,从50中随机一个; 人员B,从49中随机一个; 人员C,从48中随机一个;人员D,从47中随机一个;
依次循环,直到50个取完,或者4个人员分配到最大值。

1年前 评论
铁牛 (楼主) 1年前
Junwind (作者) 1年前
Junwind (作者) 1年前
小李世界 1年前
mnizfd

发表下拙见 可以按数量 比如 从0开始 给分配过的客服 标记一下 总客服数为5的话 那么标记过的人5次内不在分配 如果其中一个客服人数满了的话 那就总客服数减1 其他细节 后续优化吧 大致方案如此 还有一种是按照百分比分配发 目前还能接受工单的百分比最大的客服去接受 且百分比相等时优先分配给最大接受数多的客服 毕竟最大接受数越多 那么这个客服应该是能力强和效率快的 需求也大 希望有所帮助

1年前 评论

我的理解是 循环工单 然后利用循环的key除人数拿余数分配下标,分配满的就把这个客服从数组中剔除掉,直到客服数组为空终止

1年前 评论

优先队列按权重进行给工单,给过工单的客服权重降低,保证公平性。已经饱和的客服剔除优先队列

1年前 评论

我的想法是循环工单,然后用工单的index % 客服的数组长度,分配到对应index的客服,然后客服的可接单数量减一,减到0就把客服从数组剔除

1年前 评论

这里有个点需要注意,每个客服的饱和度是不一样的,A = 10 ,B = 20, 当客户总数量是 15,A 分配应该是 5,B 应该分配是 10

1年前 评论
铁牛 (楼主) 1年前
mnizfd 1年前

工单量够多的话直接按最大接收数占总最大接收数的比例分配每个客服的总工单数量 :see_no_evil: 直接一把梭哈 :see_no_evil: :see_no_evil: :see_no_evil:

客服工单总数量 = 总工单数量 *(客服最大接收数 / 全部客服最大接收数和)

1年前 评论
白小二

两层数组迭代,外层订单,内层客服,分配订单跟接满踢人都在内层迭代处理。很简单的写法

1年前 评论
铁牛 (楼主) 1年前
白小二 (作者) 1年前

感觉随机分配比较好 :joy:

1年前 评论

例如客服数组个数为:3,遍历工单数组,工单 index % 3 得到分配给客服数组 index;中途 A 满了,把 A 从数组中剔除,客服数组个数变为:2,工单 index % 2 ,以此类推

1年前 评论

队列长度4,初始值 ABCD ,拿出A分配任务,如果没达到最大值,把A再放入队列 变成 BCDA,如果达到了最大值,就是BCD,如果A的工单有关闭的时,再把A丢进队列。

ABCD BCDA CDB DBCA

1年前 评论
Complicated

最直接办法就是:每次给客服分配了就累计下分配的数量,每次分配前检查一下这个客服是否到达最大值,到了就滤过,不到就分配呗

1年前 评论
// 客服容量
$staffCap = [
    'A' => 10,
    'B' => 20,
    'C' => 5,
    'D' => 15,
];
// 工单
$tickets = [
    '0001',
    '0002',
    '0003',
    '0004',
];

$seats = [];
foreach ($staffCap as $staff => $cap) {
    for ($i = 1; $i <= $cap; $i++) {
        $seats[] = $staff;
    }
}
shuffle($seats);
while (!empty($tickets)) {
    $seat = array_pop($seats);
    if (empty($seat)) {
        echo "接待满了" . PHP_EOL;
        break;
    }
    $ticket = array_pop($tickets);
    echo "$seat 接待了 $ticket" . PHP_EOL;
}

echo "还有 " . count($tickets) . " 个工单未接待" . PHP_EOL;
echo "还可接待 " . count($seats) . " 个工单" . PHP_EOL;

输出


D 接待了 0004
A 接待了 0003
B 接待了 0002
C 接待了 0001
还有 0 个工单未接待
还可接待 46 个工单
1年前 评论

我做过一个类似的需求,有关商机的自动分配。

分配时间:非节假日,8:3021:00
分配对象:特定部门成员(可以多个部门)
在自动分配的同时可以手动分配,可以增减部门成员,可以修改部门成员的每日分配上限。
部门员工可以开启或关闭自动分配功能,开启自动分配有离线和在线状态。
未开启自动分配,开启员工离线状态不可分配。
自动分配存在优先级,但是也需要均匀,要部门内的员工轮询着来。
解决方案:定时脚本+redis+rabbitmq
定时脚本 获取数据
redis 
  list 维护部门成员信息轮询 
  hash 成员是否可分配  
  hash 每日分配上限 
  zset每日分配统计
mq  消费数据
1年前 评论

把客服理解成数组,键是 A B C D,值是 10 20 5 15

分别对应这个客服剩余可以接坐席数,10就代表10个空余,按照数字从大到小排序

结束一个坐席,相应客服对应数字 +1,接收到一个坐席相应数字 -1

每次有任意人坐席结束或分配坐席时判断数组内数组相加是否 > 0 ,> 0 则进行分配坐席

最开始的时候会分配不均,当数据足够多的时候保证每个人都是满的,后面可以加一个调拨功能

调拨功能是判断数组内是否存在差值特别大的两条数据,两个人数据相加除以2,然后进行平均分配,如果超出上限则对气他几个人进行分配,然后再次调拨再次分配

1年前 评论

可以使用工作饱和度分配,饱和度一致时空余数大的优先,比如如下数据:

四个客服最大接收数:1020515
工单总数:分配数 # 工作饱和度
工单数00000  # 0%0%0%0%
工单数10100  # 0%5%0%0%
工单数20101  # 0%5%0%6%
工单数31101  # 10%5%0%6%
工单数41111  # 10%5%20%6%
工单数51211  # 10%10%20%6%
工单数61212  # 10%10%20%12%
工单数71312  # 10%15%20%12%
工单数82312  # 20%15%20%12%
...
1年前 评论

我做个一个需求 (当前接收工数/总分配工单数)/份额=当前占比, 因为我是来一个分一个,每人的份额还不一样,得到占比最小的然后进行分配,一直循环,因为我这个和绩效挂勾,绩效好的份额高

1年前 评论

一堆人讲的和楼主的需求没一毛钱的关系。

分配的时候顺序分配,ABCDABCD…. 一直到某个客服接收到最大,然后剔除掉,再继续分配;

你们做功能都不要需求的么

1年前 评论
qietugou 1年前
令龙小道 1年前
铁牛 (楼主) 1年前

我本来写了一长串数据库化的方案的,结果不小心删除了,这里重新简单说一下; 1、我想的是数据库化,动态分配,每天早上分配积累单子,但是可能快的中午就处理完了,如果有新单还是需要继续分配的,所以,要记录一个每个客服当前时间的动态单子数; 2、数据库化后,可以查询动态查询客服的剩余处理量,而且客服数量可以随意添加,每个人的最大数量可以随意变更;

实现: 1、建表 用户ID user_id,最大分配数量max_count,当前单子量。最后分配时间last_time,客服排序规则(可以自定义或者直接user_id排序,建议用户id固定排序,不然加一个客服就要去设置一下权重) 2、缓存最后分配的user_id, 可以利用这个id 根据排序找到下一个人该分配给谁;如果缓存中没有这个ID可以利用表中的last_time 去确定最后分配的id,这个就是last_time的价值所在,初始执行或者数据迁移没有缓存时可以使用;

查询: //没有缓存 var last_id=getLastIdFromCache(); if(!last_id)
${last_id} = select user_id from xxx order by last_time desc limit 1 //缓存起来

#查询下一个分配人:当前单量小于最大分配量的 && 大于 上次last_id 的权重的 select user_id from xxx where current_count${last_Id} order by 自定义的权重(order by user_id desc)

#分配成功,注意修改当前量和最后分配时间

#客服处理完一单后,业务修改current_count 当前单子数;

1年前 评论
铁牛 (楼主) 1年前
zhenming 1年前
forwzb (作者) 1年前
forwzb (作者) 1年前

需求有问题吧。。。 工单是不断进入工单池的,需要不断消费,如果一股脑消费完,还有什么先后顺序问题? 如果是需要不停消费的,那给工作未饱和的人员建立一个顺序队列,来一个工单出列一个工作人员,队列空了就重新建队列,这样不就解决了?

1年前 评论
laisxn

放一个队列跑工单任务分配;
记录每个对象对应的任务数量;
记录下一次工单来领分配的对象,按分配顺序(提前排序号)记录下一个分配对象(此处就要判断对象是不是满负荷了),第一次就是A;

1年前 评论

是不是像银行排队一样的, 是的话就不用预分配吧,有一个总的排队就行了,哪个客服空闲就分给哪个客服。否则的话,按顺序不就是

比如 12 % 4 = 0, 13 % 4 = 1, 14 % 4 = 2, 这样算按顺序均分?

1年前 评论
铁牛 (楼主) 1年前

我也分享一下,不知道符不符合楼主的想法,
1.订单按权重排序(需要优先处理的)
2.匹配客服在线限人数,(哪个客服在线,就优先分配订单)
3.在未达到峰值订单前,订单都是随机分配
如现有50条订单
a客服最大处理10条订单,
b客服最大处理20条订单,
c客服最大处理15条订单,
d客服最大处理30条订单,
当c客服不在线
按照订单优先级 随机分配给abd客服
当a已经处理10条后就踢出,把剩下订单交予bd。当bd也满了后,最后剩下订单都随机交予未处理过的客服

1年前 评论

做过类似的需求,下面是大概的思路,当然也有其他的办法,根据实际情况来就行

总体概况就是,让用户队伍成为一条队伍,最前面的领完了就到后面重新排队领

$order_list = [订单列表...];
$user_limit = [
    A => [
        'max_num' => '10', // 最大分配
        'now_num' => '0', // 当前分配
    ]
    ....
];
$user_list = [A,B,C,D];
$user_count = count($user_list);
foreach ($order_list as $value) {
    if ($user_count == 0) {
        // 没有用户可以分配了
        break;
    }
    while ($count > 0) {
        // 当前用户
        $user_info = current($user_list);
        if ($user_limit[$user_info]['max_num'] == ++$user_limit[$user_info]['now_num']) {
            // 已经领满了移除自身
            unset($user_list[key($user_list)]);
            --$user_count;
        }else{
            // 移除自身 重新进队
            unset($user_list[key($user_list)]);
            $user_list[] = $user_info;
        }
        ...业务逻辑
    }
}
1年前 评论
<?php


//获取当前订单可以接待的客服
function getUser($pre_user){
    //该数据存在缓存或者数据库(查询出来之前应该按照 ABCD的坐班顺序)
    $user_limit = [
        A => [
            'name' => 'A', // 客服名称
            'max_num' => '10', // 最大分配
            'now_num' => '0', // 当前分配
        ],
        B => [
            'name' => 'B', // 客服名称
            'max_num' => '20', // 最大分配
            'now_num' => '20', // 当前分配
        ],
        C => [
            'name' => 'C', // 客服名称
            'max_num' => '15', // 最大分配
            'now_num' => '0', // 当前分配
        ],
    ];

    //获取得到当前有空闲席位的客服
    $allow_user_pre = [];
    $allow_user_next = [];
    $flag = false;
    $allow_user = [];
    foreach ($user_limit as $key => $value) {
        if($key == $flag){
            $flag = true;
        }
        //剔除掉已经排满的客服
        if($value['max_num']<=$value['now_num']){
            continue;
        }
        if($flag){
            $allow_user_next[] = $value;
        }else{
            $allow_user_pre[] = $value;
        }

    }

    if(isset($allow_user_next[0])){
        return $allow_user_next[0]['name']
    }

    if(isset($allow_user_pre[0])){
        return $allow_user_pre[0]['name']
    }

    return false;
}


//记录上一个订单接待客服的人(缓存或者数据库)
$pre_user = A;


$next_user = getUser($pre_user);

if(false !==$next_user){
    $pre_user = $next_user;

    //TODO更新当前客服的接待人数

    //TODO其他业务逻辑
}


1年前 评论
carter (作者) 1年前
UKNOW

订单放在队列里面,然后循环 客服数组,每次从队列取出一个订单给客服,并记录分配给每个客服的工单数量,满了就不分配给该客服 就行了吧

1年前 评论

<?php

namespace App\Http\Service;

class DispatchService
{
    public array $setting = [
        'A' => [
            'max'        => 15,
            'permission' => [],
        ],
        'B' => [
            'max'        => 5,
            'permission' => [],
        ],
        'C' => [
            'max'        => 10,
            'permission' => [],
        ],
        'D' => [
            'max'        => 20,
            'permission' => [],
        ],
    ];

    public array $service = [];

    public function dispatch()
    {
        $pools = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52];

        foreach ($pools as $pool) {
            $staff                                 = $this->getIdx();
            $this->setting[$staff]['permission'][] = $pool;
        }
        foreach ($this->setting as $key => $item) {
            echo $key . " : \n";
            echo ' item ' . implode(',', $item['permission']) . "\n";
        }
    }

    public function getIdx()
    {
        $this->service = [];
        foreach ($this->setting as $key => $item) {
            if (count($item['permission']) < $item['max']) {
                $this->service [] = $key;
            }
        }
        $min    = 0;
        $minIdx = null;
        foreach ($this->service as $item) {
            if ($minIdx == null) {
                $min    = count($this->setting[$item]['permission']);
                $minIdx = $item;
                continue;
            }
            if (count($this->setting[$item]['permission']) < $min) {
                $min    = count($this->setting[$item]['permission']);
                $minIdx = $item;
            }
        }

        return $minIdx;
    }

}

$dispatch = new DispatchService();
$dispatch->dispatch();

1年前 评论
class User{
    protected $id ;
    protected $maxNum ;
    protected $dealNum = 0;
    //waiting ,dealing,offline
    protected $status;
    protected $weight;
    public function __get($key){
        if($key == 'weight'){
            return $this->getWeight();
        }
        return $this->key;
    }
    public function getWeight(){
        return $this->dealNum / $this->maxNum ;
    }
    public function deal(){
        $this->dealNum++;
        if($this->dealNum == $this->maxNum){
            $this->status='offline';
        }
    }
}

//收到工单时触发分配,
$allUserList = collect([$user1,$user2,...$userN]);

$canWorkUserList = $allUserList->where('status','waiting');
$minWeight = $canWorkUserList->min('weight');
$minDealNum = $canWorkUserList->min('dealNum');
$toUser = $canWorkUserList
    //->where('dealNum',$minDealNum) 按处理量
    ->where('weight',$minWeight) // 按权重
    //->random(1) 随机获取权重相同的一个人
    ->first(); //按顺序获取
$toUser->deal() ;
1年前 评论
<?php

$user = [
    'A' => 10,
    'B' => 20,
    'C' => 5,
    'D' => 15,
];

$work = [
    'A' => 0,
    'B' => 0,
    'C' => 0,
    'D' => 0,
];

$count = count($user);


$task = 100;

$i = 0;
while ($i < $task) {
    if($count > 0){
        foreach ($user as $name => $max) {
            if($work[$name] < $max){
                $work[$name]++;
                echo $name." 接单成功, 目前手上:".$work[$name]." 单, 还能接:".($max - $work[$name])." 单".PHP_EOL;
                $i++;
            }else{
                unset($user[$name]);
                $count--;
            }
        }
    }else{
        echo "爆单了".$i.PHP_EOL;
        $i++;
    }
}
1年前 评论

有个算法叫 加权轮询

1年前 评论
铁牛 (楼主) 1年前
 $jobs = [
            "job001",
            "job002",
            "job003",
            "job004",
            "job005",
            "job006",
            "job007",
            "job008",
            "job009",
            "job010",
        ];
        $workers = [
            ['name' => 'A', 'number' => 15],
            ['name' => 'B', 'number' => 20],
            ['name' => 'C', 'number' => 5],
            ['name' => 'D', 'number' => 15],
        ];

        do {
            foreach ($workers as $k => $worker) {
                $job = array_shift($jobs);
                if ($job) {
                    if ($worker['number'] <= 0) {
                        continue;
                    }
                    $workers[$k]['number'] -= 1;
                    echo sprintf("客服%s处理了工单%s\n", $worker['name'], $job);
                }
            }
        } while (!empty($jobs));


        echo '处理完成';
1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!