feat: tick planner, delivery job, scheduler and purge

Also: test uses disabled channels instead of deleting them (FK cascade); ChannelRegistry no longer final so tests can extend it (controller ruling).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
nikita.hohlov
2026-09-04 07:38:16 -03:00
parent 1b0a559e3c
commit affac4ce1a
13 changed files with 611 additions and 6 deletions

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use App\Enums\EventState;
use App\Models\Event;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
class PurgeCommand extends Command
{
protected $signature = 'hado:purge';
protected $description = 'Удаляет терминальные события старше HADO_RETENTION_DAYS';
public function handle(): int
{
$terminal = array_map(fn (EventState $s) => $s->value, array_filter(EventState::cases(), fn (EventState $s) => $s->isTerminal()));
$cutoff = CarbonImmutable::now()->subDays((int) config('hado.retention_days'));
$deleted = Event::whereIn('state', $terminal)->where('updated_at', '<', $cutoff)->delete();
$this->info("Удалено событий: {$deleted}");
return self::SUCCESS;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Console\Commands;
use App\Delivery\Tick;
use Illuminate\Console\Command;
class TickCommand extends Command
{
protected $signature = 'hado:tick';
protected $description = 'Один тик: переходы состояний и доставка напоминаний в открытые окна';
public function handle(Tick $tick): int
{
$tick->run();
return self::SUCCESS;
}
}