feat: state, window and quiet rules

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 06:39:23 -03:00
parent e48e48aa4a
commit c2fa8aa00d
8 changed files with 420 additions and 1 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Scheduling;
use App\Enums\AfterDue;
use App\Enums\EventState;
use App\Models\Event;
use App\Models\User;
use Carbon\CarbonImmutable;
/**
* Состояние, которое следует из дат события и «сейчас». Используется и кроном
* (переходы), и upsert-ом (пересчёт). Терминальные Done/Withdrawn ставятся
* только действиями, сюда не входят.
*/
final class StateResolver
{
public function __construct(private readonly DueResolver $due) {}
public function resolve(Event $event, User $user, CarbonImmutable $now): EventState
{
$due = $this->due->resolve($event, $user);
if ($now->gte($due->moment)) {
return $event->after_due === AfterDue::Keep ? EventState::Overdue : EventState::Expired;
}
if ($now->gte($this->due->todayStart($due, $user))) {
return EventState::Today;
}
$fireStart = $this->due->fireStart($event, $user);
if ($fireStart->lt($due->day) && $now->gte($fireStart)) {
return EventState::Preparing;
}
return EventState::Scheduled;
}
}