Files
ladill-queue/app/Services/Qms/QueueRuleService.php
T
isaaccladandCursor cca98eefd2
Deploy Ladill Queue / deploy (push) Successful in 56s
Initial Ladill Queue release — enterprise QMS standalone app.
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>
2026-06-29 20:19:52 +00:00

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;
}
}