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
59 lines
1.7 KiB
PHP
59 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Delivery;
|
|
|
|
use App\Channels\ChannelRegistry;
|
|
use App\Enums\DeliveryResult;
|
|
use App\Models\Delivery;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Queue\Queueable;
|
|
use Throwable;
|
|
|
|
final class DeliverJob implements ShouldQueue
|
|
{
|
|
use Queueable;
|
|
|
|
public int $tries = 3;
|
|
|
|
public function __construct(public readonly int $deliveryId) {}
|
|
|
|
/** @return list<int> */
|
|
public function backoff(): array
|
|
{
|
|
return [30, 120, 600];
|
|
}
|
|
|
|
public function handle(ChannelRegistry $registry): void
|
|
{
|
|
$delivery = Delivery::with(['event.user', 'event.source', 'channel'])->findOrFail($this->deliveryId);
|
|
if ($delivery->result !== DeliveryResult::Pending) {
|
|
return;
|
|
}
|
|
|
|
if ($delivery->event->isTerminal()) {
|
|
$delivery->update(['result' => DeliveryResult::Failed, 'error' => 'skipped: событие уже закрыто']);
|
|
|
|
return;
|
|
}
|
|
|
|
$outcome = $registry->for($delivery->channel->type)
|
|
->deliver($delivery->event, $delivery->event->user, $delivery->channel->config, $delivery);
|
|
|
|
if ($outcome->ok) {
|
|
$delivery->update(['result' => DeliveryResult::Ok, 'sent_at' => now(), 'error' => null, 'meta' => $outcome->meta]);
|
|
|
|
return;
|
|
}
|
|
|
|
$delivery->update(['error' => $outcome->error]);
|
|
throw new DeliveryFailed($outcome->error ?? 'delivery failed');
|
|
}
|
|
|
|
public function failed(Throwable $e): void
|
|
{
|
|
Delivery::whereKey($this->deliveryId)
|
|
->where('result', DeliveryResult::Pending->value)
|
|
->update(['result' => DeliveryResult::Failed, 'error' => $e->getMessage()]);
|
|
}
|
|
}
|