CRM/backend/app/Services/AutomationDispatcher.php

59 خطوط
2.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\AutomationRule;
use Illuminate\Database\Eloquent\Model;
class AutomationDispatcher
{
public function __construct(private AutomationEngine $engine) {}
public function dispatch(string $trigger, Model $subject, string $eventKey, array $context = []): void
{
$teamId = $subject->getAttribute('team_id');
$rules = AutomationRule::where('trigger', $trigger)->where('is_active', true)
->where(fn ($query) => $query->whereNull('team_id')->when($teamId, fn ($team) => $team->orWhere('team_id', $teamId)))
->get();
foreach ($rules as $rule) {
if (! $this->matches($rule->conditions ?? [], $subject, $context)) {
continue;
}
$runs = $rule->runs()->where('subject_type', $subject::class)->where('subject_id', $subject->getKey())->count();
if ($runs >= $rule->max_runs_per_record) {
continue;
}
try {
$this->engine->run($rule, $subject, "automation:{$rule->id}:{$eventKey}", $context);
} catch (\Throwable) {
// The run log contains the failure; business lifecycle must remain available.
}
}
}
private function matches(array $conditions, Model $subject, array $context): bool
{
foreach ($conditions as $condition) {
$field = $condition['field'] ?? null;
$operator = $condition['operator'] ?? 'equals';
$expected = $condition['value'] ?? null;
if (! $field) {
return false;
}
$actual = $context[$field] ?? $subject->getAttribute($field);
$matches = match ($operator) {
'not_equals' => $actual != $expected,
'greater_than' => is_numeric($actual) && $actual > $expected,
'less_than' => is_numeric($actual) && $actual < $expected,
'contains' => is_string($actual) && str_contains($actual, (string) $expected),
default => $actual == $expected,
};
if (! $matches) {
return false;
}
}
return true;
}
}