backend phase 8

This commit is contained in:
hamid
2026-07-06 02:48:56 +03:30
parent 99ebf5d881
commit 2cfc082a04
55 changed files with 7480 additions and 7 deletions
@@ -0,0 +1,53 @@
#nullable enable
using Baya.Application.Features.Booking.Commands.ExpireBookingRequests;
using Mediator;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Baya.Infrastructure.Persistence.Services.Booking;
/// <summary>
/// The recurring expiry sweep for <c>booking_requests</c> (reuses the b1 in-process interval-runner seam;
/// real Hangfire/Quartz is deferred). Each tick sends <see cref="ExpireBookingRequestsCommand"/>, which
/// transitions stale rows (<c>expired_no_response</c> / <c>payment_deadline_expired</c>) in bounded,
/// idempotent batches. The interval is short because the payment window is only 30 minutes; there is no b1
/// interval config key for booking expiry, so it is a documented constant.
/// </summary>
internal sealed class BookingRequestExpiryHostedService(
IServiceScopeFactory scopeFactory,
ILogger<BookingRequestExpiryHostedService> logger) : BackgroundService
{
private static readonly TimeSpan Interval = TimeSpan.FromMinutes(1);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await SweepSafely(stoppingToken);
using var timer = new PeriodicTimer(Interval);
while (await timer.WaitForNextTickAsync(stoppingToken))
await SweepSafely(stoppingToken);
}
private async Task SweepSafely(CancellationToken cancellationToken)
{
try
{
await using var scope = scopeFactory.CreateAsyncScope();
var sender = scope.ServiceProvider.GetRequiredService<ISender>();
var result = await sender.Send(new ExpireBookingRequestsCommand(), cancellationToken);
if (result.IsSuccess && result.Result is { } counts && (counts.ExpiredNoResponse > 0 || counts.PaymentDeadlineExpired > 0))
logger.LogInformation(
"Booking-request expiry swept {NoResponse} expired-no-response and {PaymentExpired} payment-window-expired requests",
counts.ExpiredNoResponse, counts.PaymentDeadlineExpired);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
// Host is shutting down — expected, don't log as an error.
}
catch (Exception ex)
{
logger.LogError(ex, "Booking-request expiry sweep failed");
}
}
}