$attributes * @return array{0: ServiceQueue, 1: array} */ public function apply(ServiceQueue $queue, array $attributes): array { $rules = QueueRule::owned($queue->owner_ref) ->where('service_queue_id', $queue->id) ->where('is_active', true) ->orderBy('sort_order') ->get(); foreach ($rules as $rule) { match ($rule->rule_type) { 'priority_boost' => $this->applyPriorityBoost($rule, $attributes), 'overflow' => [$queue, $attributes] = $this->applyOverflow($queue, $rule, $attributes), 'routing' => [$queue, $attributes] = $this->applyRouting($queue, $rule, $attributes), 'capacity' => [$queue, $attributes] = $this->applyCapacity($queue, $rule, $attributes), default => null, }; } return [$queue, $attributes]; } /** * @param array $attributes */ protected function applyPriorityBoost(QueueRule $rule, array &$attributes): void { $boostTo = data_get($rule->actions, 'priority'); $when = data_get($rule->conditions, 'source'); if ($boostTo && (! $when || ($attributes['source'] ?? null) === $when)) { $attributes['priority'] = $boostTo; } } /** * @param array $attributes * @return array{0: ServiceQueue, 1: array} */ protected function applyOverflow(ServiceQueue $queue, QueueRule $rule, array $attributes): array { $threshold = (int) data_get($rule->conditions, 'waiting_threshold', 0); $overflowQueueId = data_get($rule->actions, 'overflow_queue_id'); if (! $overflowQueueId || $threshold <= 0) { return [$queue, $attributes]; } if ($this->waitingCount($queue) >= $threshold) { $overflow = $this->activeQueue($overflowQueueId); if ($overflow) { return [$overflow, $attributes]; } } return [$queue, $attributes]; } /** * @param array $attributes * @return array{0: ServiceQueue, 1: array} */ protected function applyRouting(ServiceQueue $queue, QueueRule $rule, array $attributes): array { $targetId = data_get($rule->actions, 'target_queue_id'); if (! $targetId) { return [$queue, $attributes]; } $whenSource = data_get($rule->conditions, 'source'); $whenPriority = data_get($rule->conditions, 'priority'); if ($whenSource && ($attributes['source'] ?? null) !== $whenSource) { return [$queue, $attributes]; } if ($whenPriority && ($attributes['priority'] ?? null) !== $whenPriority) { return [$queue, $attributes]; } $target = $this->activeQueue((int) $targetId); return $target ? [$target, $attributes] : [$queue, $attributes]; } /** * @param array $attributes * @return array{0: ServiceQueue, 1: array} */ protected function applyCapacity(ServiceQueue $queue, QueueRule $rule, array $attributes): array { $maxWaiting = (int) data_get($rule->conditions, 'max_waiting', 0); if ($maxWaiting <= 0) { return [$queue, $attributes]; } if ($this->waitingCount($queue) < $maxWaiting) { return [$queue, $attributes]; } $whenFull = data_get($rule->actions, 'when_full', 'reject'); if ($whenFull === 'reject') { throw new \RuntimeException('Queue has reached the configured waiting limit.'); } $overflowId = data_get($rule->actions, 'overflow_queue_id'); if ($overflowId) { $overflow = $this->activeQueue((int) $overflowId); if ($overflow) { return [$overflow, $attributes]; } } throw new \RuntimeException('Queue has reached the configured waiting limit.'); } protected function waitingCount(ServiceQueue $queue): int { return Ticket::query() ->where('service_queue_id', $queue->id) ->where('status', 'waiting') ->count(); } protected function activeQueue(int $queueId): ?ServiceQueue { $target = ServiceQueue::find($queueId); return ($target && $target->is_active && ! $target->is_paused) ? $target : null; } }