Deploy Ladill Queue / deploy (push) Successful in 56s
Phases 1–6: tickets, counters, displays, appointments, workflows, rules, analytics, reports, feedback, admin, device API, and Gitea deploy workflow for queue.ladill.com. Co-authored-by: Cursor <cursoragent@cursor.com>
146 lines
4.7 KiB
PHP
146 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Qms;
|
|
|
|
use App\Models\QueueRule;
|
|
use App\Models\ServiceQueue;
|
|
use App\Models\Ticket;
|
|
|
|
class QueueRuleService
|
|
{
|
|
/**
|
|
* Apply active rules and return possibly modified queue + attributes.
|
|
*
|
|
* @param array<string, mixed> $attributes
|
|
* @return array{0: ServiceQueue, 1: array<string, mixed>}
|
|
*/
|
|
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<string, mixed> $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<string, mixed> $attributes
|
|
* @return array{0: ServiceQueue, 1: array<string, mixed>}
|
|
*/
|
|
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<string, mixed> $attributes
|
|
* @return array{0: ServiceQueue, 1: array<string, mixed>}
|
|
*/
|
|
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<string, mixed> $attributes
|
|
* @return array{0: ServiceQueue, 1: array<string, mixed>}
|
|
*/
|
|
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;
|
|
}
|
|
}
|