fix(admin): dedup pending maintenance notifications (updated_at claim + staleness threshold)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/portal-design
parent
493b81aadf
commit
ab5ba2b169
|
|
@ -20,6 +20,9 @@ use Livewire\Component;
|
|||
#[Layout('layouts.admin')]
|
||||
class Maintenance extends Component
|
||||
{
|
||||
/** A pending notification is presumed failed (retryable) after this long. */
|
||||
private const RETRY_AFTER_MIN = 60;
|
||||
|
||||
#[Validate('required|string|max:255')]
|
||||
public string $title = '';
|
||||
|
||||
|
|
@ -188,11 +191,20 @@ class Maintenance extends Component
|
|||
['maintenance_window_id' => $window->id, 'customer_id' => $customer->id, 'event' => $event],
|
||||
['email' => $customer->email],
|
||||
);
|
||||
// Delivered rows (sent_at set by the MessageSent listener) are final; a
|
||||
// row that is still unconfirmed (sent_at null) is retryable — re-queue it.
|
||||
|
||||
// Delivered rows (sent_at set by the MessageSent listener) are final.
|
||||
if ($delivery->sent_at !== null) {
|
||||
return;
|
||||
}
|
||||
// Otherwise the row is pending. Re-queue only when this is a fresh claim
|
||||
// or the previous attempt is stale enough to be presumed failed — so a
|
||||
// resend while the job is still in flight does not duplicate the email.
|
||||
// updated_at is the last-attempt marker (touched on every dispatch).
|
||||
if (! $delivery->wasRecentlyCreated && $delivery->updated_at->gt(now()->subMinutes(self::RETRY_AFTER_MIN))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$delivery->forceFill(['updated_at' => now()])->save();
|
||||
$mail->notificationId = $delivery->id;
|
||||
Mail::to($customer->email)
|
||||
->locale($customer->locale ?: config('app.locale'))
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ it('publishes a window and emails affected customers once (idempotent)', functio
|
|||
expect(fn () => MaintenanceNotification::query()->create([
|
||||
'maintenance_window_id' => $window->id, 'customer_id' => $customer->id, 'event' => 'announcement', 'email' => $customer->email,
|
||||
]))->toThrow(Illuminate\Database\UniqueConstraintViolationException::class);
|
||||
|
||||
// Resending while the first mail is still pending must NOT duplicate.
|
||||
Livewire::actingAs(operator('Owner'))->test(Maintenance::class)->call('resend', $window->uuid);
|
||||
Mail::assertQueued(MaintenanceAnnouncementMail::class, 1);
|
||||
});
|
||||
|
||||
it('emails a cancellation to customers who were announced', function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue