Initial Ladill Queue release — enterprise QMS standalone app.
Deploy Ladill Queue / deploy (push) Successful in 56s
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>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user