PHP项目工单分配如何自动指派人员

wen PHP项目 26

本文目录导读:

PHP项目工单分配如何自动指派人员

  1. 轮询分配(Round-Robin)
  2. 最少工单分配(Least-Busy / Load Balancing)
  3. 技能匹配分配(Skill-Based)
  4. 基于权重的随机分配(Weighted Random)
  5. 高级:基于规则引擎(Rule Engine)
  6. 如何集成到现有 PHP 项目?
  7. 需要注意的关键点
  8. 推荐的开源库

在PHP项目中实现工单的自动指派,通常需要根据业务规则(如轮询、技能匹配、负载均衡等)来设计算法。

以下是几种常见的自动指派策略及其 PHP 实现思路:

轮询分配(Round-Robin)

适用场景: 团队人员能力均等,希望每个人的工单数量大致平均。

逻辑: 记录上一次被指派的人员ID,下次分配给下一个人。

PHP 实现示例:

<?php
class TicketAssigner {
    private $db;
    public function __construct($db) {
        $this->db = $db;
    }
    /**
     * 获取下一个应该被指派的人员ID(轮询)
     * @param int $teamId 团队ID
     * @return int 人员ID
     */
    public function getNextAgentRoundRobin($teamId) {
        // 1. 获取该团队所有在线/可用的客服
        $agents = $this->db->query(
            "SELECT id FROM users 
             WHERE team_id = ? AND status = 'online' 
             ORDER BY id ASC", 
            [$teamId]
        );
        if (empty($agents)) {
            throw new Exception("没有可用客服");
        }
        // 2. 获取上次分配的最后一个人(存储于 team_config 表)
        $lastAgentId = $this->db->query(
            "SELECT last_assigned_agent_id FROM team_config WHERE team_id = ?", 
            [$teamId]
        )[0]['last_assigned_agent_id'] ?? 0;
        // 3. 找到下一个
        $nextAgent = null;
        foreach ($agents as $agent) {
            if ($agent['id'] > $lastAgentId) {
                $nextAgent = $agent;
                break;
            }
        }
        // 如果到最后了,从头开始
        if (!$nextAgent) {
            $nextAgent = $agents[0];
        }
        // 4. 更新最后指派记录
        $this->db->execute(
            "UPDATE team_config SET last_assigned_agent_id = ? WHERE team_id = ?",
            [$nextAgent['id'], $teamId]
        );
        return $nextAgent['id'];
    }
}

最少工单分配(Least-Busy / Load Balancing)

适用场景: 人员处理速度不同,希望照顾处理慢的人。

逻辑: 查询当前每人未关闭工单数,指派给最少的人。

SQL 实现:

SELECT 
    u.id, 
    u.name, 
    COUNT(t.id) AS current_ticket_count
FROM users u
LEFT JOIN tickets t 
    ON t.assignee_id = u.id 
    AND t.status NOT IN ('closed', 'resolved')
WHERE u.team_id = ? AND u.status = 'active'
GROUP BY u.id
ORDER BY current_ticket_count ASC
LIMIT 1;

PHP 使用:

$agent = $db->query($sql, [$teamId]);
$assignToId = $agent[0]['id'];

技能匹配分配(Skill-Based)

适用场景: 工单有分类(技术、财务、售后),客服有专长。

数据结构设计:

  • users_skills 表:user_id, skill_id(对应工单分类ID)
  • tickets 表:category_id

算法步骤:

  1. 获取工单的 category_id
  2. 查询所有拥有该技能且在线的人员。
  3. 在这些人员中,再使用轮询或最少工单逻辑。
$categoryId = $ticketData['category_id'];
$agents = $db->query(
    "SELECT u.id, COUNT(t.id) as cnt
     FROM users u
     JOIN users_skills us ON us.user_id = u.id AND us.skill_id = ?
     LEFT JOIN tickets t ON t.assignee_id = u.id AND t.status NOT IN ('closed')
     WHERE u.status = 'online'
     GROUP BY u.id
     ORDER BY cnt ASC
     LIMIT 1",
    [$categoryId]
);

基于权重的随机分配(Weighted Random)

适用场景: 需要给老员工或高级客服更多工单,但仍保留随机性。

权重存储: users.assignment_weight (整数,越大被分配概率越高)

算法:

function weightedRandomSelect($agents) {
    $totalWeight = array_sum(array_column($agents, 'weight'));
    $random = mt_rand(1, $totalWeight);
    foreach ($agents as $agent) {
        $random -= $agent['weight'];
        if ($random <= 0) {
            return $agent['id'];
        }
    }
}

高级:基于规则引擎(Rule Engine)

对于复杂业务(如“VIP客户优先分配给组长”、“8小时未响应自动转派”),建议引入规则引擎

伪代码示例:

$rules = [
    ['condition' => $ticket['priority'] === 'urgent' && $ticket['customer_level'] === 'vip', 
     'action' => 'assignToGroupLeader'],
    ['condition' => $ticket['category'] === 'technical', 
     'action' => 'assignBySkill'],
    ['condition' => true,  // 默认
     'action' => 'roundRobin']
];
foreach ($rules as $rule) {
    if ($rule['condition']) {
        $assigner->{$rule['action']}($ticket);
        break;
    }
}

如何集成到现有 PHP 项目?

方案 A:创建定时任务(Cron Job)

  • 每分钟执行一个 PHP 脚本,查询是否有未指派的工单。
  • 调用上述分配函数,执行 UPDATE tickets SET assignee_id = ?

方案 B:实时分配(在工单创建时)

  • TicketController::store() 方法中,调用分配函数,直接将 assignee_id 写入数据库。
// 工单创建后
$assigner = new TicketAssigner($db);
$agentId = $assigner->getLeastBusyAgent($teamId);
$ticket->assignee_id = $agentId;
$ticket->save();

方案 C:使用消息队列(如 Redis / RabbitMQ)

  • 适合高并发场景,将待分配工单推入队列,Worker 进程异步处理分配。

需要注意的关键点

  1. 避免死锁/竞争:在高并发下,两个请求同时查到同一个人是“最少工单”,导致分配重叠。解决方案

    • 使用数据库行锁 SELECT ... FOR UPDATE
    • 或者在分配后立刻检查并回滚(乐观锁)。
  2. 分配记录:建议记录 ticket_assign_log 表,记录谁、什么时间、通过什么规则分配,方便回溯。

  3. 手动干预:自动分配后,应保留管理员手动改派的功能,并设置自动分配优先级低于手动指定。

  4. 休假/离线状态:分配前必须检查客服的在线状态,离线人员应跳过。


推荐的开源库

如果不想从零实现,可以考虑:

  • PHP-DI / Workflow:结合规则引擎。
  • Laravel 队列 + 事件:如果使用 Laravel,可以用 Event::listen('ticket.created') 触发分配逻辑。

最简单的起步是“最少工单 + 技能匹配”,用 SQL 一次查询即可解决;如果团队规模大且规则复杂,再引入轮询或权重随机,关键是保证公平可用性之间的平衡。

抱歉,评论功能暂时关闭!