comment(Inspiring::quote()); })->purpose('Display an inspiring quote'); /** * Betriebs-Alert bei fehlgeschlagenem Scheduler-Task (R5.3): meldet den Task * per Log + Best-Effort-Mail, damit ein stiller Ausfall (z. B. Rechnungslauf, * Kontingent-Reset) nicht unbemerkt bleibt. */ $alertOnFailure = fn (string $task) => function (Event $event) use ($task): void { OperationalAlert::send("Geplanter Task fehlgeschlagen: {$task}", [ 'exit_code' => $event->exitCode, ]); }; // ======================================== // Tägliche Bereinigungen // ======================================== // Magic-Links: Abgelaufene / verbrauchte Tokens entfernen (täglich 03:00) Schedule::command(PurgeMagicLinks::class, ['--days=30']) ->dailyAt('03:00') ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('magic-links:purge')); // Meldeverfahren: Schwärzungs-Snapshots nach Löschfrist entfernen (täglich 03:10) Schedule::command(PurgeRedactionSnapshots::class) ->dailyAt('03:10') ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('snapshots:purge')); // PM-Entwürfe: Zombie-Drafts nach 180 Tagen Inaktivität archivieren (wöchentlich) Schedule::command(PurgeExpiredPressReleaseDrafts::class, ['--days=180']) ->weekly() ->sundays() ->at('04:00') ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('press-releases:purge-drafts')); // ======================================== // Geplante PM-Veröffentlichung // ======================================== // PM mit scheduled_at <= now & Status review automatisch veröffentlichen. // Läuft alle 5 Min — passt zum FormRule "scheduled_at min. 5 Min in Zukunft". Schedule::command(PublishScheduledPressReleases::class) ->everyFiveMinutes() ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('press-releases:publish-scheduled')); // ======================================== // Manueller Rechnungskreis (MAN-) — Legacy-Zahlungen // ======================================== // Fällige Rechnungen für laufende Legacy-Zahlungsvereinbarungen ausstellen // (Periodenende erreicht), wie im Altsystem. Neue Abschlüsse laufen über // Stripe (STR-Kreis) und werden hier nicht angefasst. Schedule::command(GenerateManualInvoices::class) ->dailyAt('04:30') ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('billing:generate-manual-invoices')); // ======================================== // PM-Kontingent (Kalendermonats-Reset des Tarif-Modells) // ======================================== // Monatlicher Reset des verbrauchten PM-Kontingents (am 1. um 00:05). Schedule::command(ResetMonthlyPressReleaseQuota::class) ->monthlyOn(1, '00:05') ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('press-releases:reset-monthly-quota')); // ======================================== // Börsendaten für den Web-Ticker // ======================================== // Kurse (DAX, EUR/USD, Brent) alle 15 Minuten aktualisieren; die Views lesen // nur aus dem Cache — ein API-Ausfall leert die Anzeige nicht sofort. Schedule::command(RefreshMarketData::class) ->everyFifteenMinutes() ->withoutOverlapping() ->runInBackground() ->onFailure($alertOnFailure('market:refresh'));