c435319025
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 7m53s
- Added payment_status field to GetOrderByIdResponse and OrderSummaryDto in proto - Proto version bumped to 0.0.179 - Added PaymentStatus mapping in DiscountOrderService gRPC responses - Created ExpirePendingOrdersService: expires pending orders after 30 min, releases inventory - Registered background service in ConfigureServices
117 lines
4.3 KiB
C#
117 lines
4.3 KiB
C#
using CMSMicroservice.Application.Common.Interfaces;
|
|
using CMSMicroservice.Domain.Enums;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace CMSMicroservice.Infrastructure.BackgroundServices;
|
|
|
|
/// <summary>
|
|
/// سرویس پسزمینه برای منقضی کردن سفارشات تخفیفی که بیش از ۳۰ دقیقه در وضعیت Pending ماندهاند.
|
|
/// موجودی رزرو شده آزاد و وضعیت پرداخت به Reject تغییر میکند.
|
|
/// </summary>
|
|
public class ExpirePendingOrdersService : BackgroundService
|
|
{
|
|
private readonly IServiceScopeFactory _scopeFactory;
|
|
private readonly ILogger<ExpirePendingOrdersService> _logger;
|
|
|
|
/// <summary>
|
|
/// مدت زمان انقضا (۳۰ دقیقه)
|
|
/// </summary>
|
|
private static readonly TimeSpan ExpirationTime = TimeSpan.FromMinutes(30);
|
|
|
|
/// <summary>
|
|
/// هر ۵ دقیقه چک میشود
|
|
/// </summary>
|
|
private static readonly TimeSpan CheckInterval = TimeSpan.FromMinutes(5);
|
|
|
|
public ExpirePendingOrdersService(
|
|
IServiceScopeFactory scopeFactory,
|
|
ILogger<ExpirePendingOrdersService> logger)
|
|
{
|
|
_scopeFactory = scopeFactory;
|
|
_logger = logger;
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_logger.LogInformation("ExpirePendingOrdersService started — checking every {Interval} min, expiring after {Expiry} min",
|
|
CheckInterval.TotalMinutes, ExpirationTime.TotalMinutes);
|
|
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
await ExpireOldPendingOrders(stoppingToken);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error in ExpirePendingOrdersService");
|
|
}
|
|
|
|
await Task.Delay(CheckInterval, stoppingToken);
|
|
}
|
|
}
|
|
|
|
private async Task ExpireOldPendingOrders(CancellationToken cancellationToken)
|
|
{
|
|
using var scope = _scopeFactory.CreateScope();
|
|
var context = scope.ServiceProvider.GetRequiredService<IApplicationDbContext>();
|
|
var inventoryService = scope.ServiceProvider.GetRequiredService<IInventoryService>();
|
|
|
|
var cutoff = DateTime.UtcNow - ExpirationTime;
|
|
|
|
// پیدا کردن سفارشات Pending قدیمی
|
|
var expiredOrders = await context.DiscountOrders
|
|
.Include(o => o.OrderDetails)
|
|
.Where(o => o.PaymentStatus == PaymentStatus.Pending && o.Created < cutoff)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
if (!expiredOrders.Any()) return;
|
|
|
|
_logger.LogInformation("Found {Count} expired pending orders to clean up", expiredOrders.Count);
|
|
|
|
foreach (var order in expiredOrders)
|
|
{
|
|
try
|
|
{
|
|
// آزادسازی رزرو موجودی
|
|
foreach (var detail in order.OrderDetails)
|
|
{
|
|
await inventoryService.ReleaseReservationAsync(
|
|
detail.ProductId,
|
|
ProductType.DiscountProduct,
|
|
detail.Count,
|
|
order.Id,
|
|
cancellationToken);
|
|
}
|
|
|
|
// تغییر وضعیت به Reject
|
|
order.PaymentStatus = PaymentStatus.Reject;
|
|
|
|
// آپدیت تراکنش مربوطه
|
|
if (order.TransactionId.HasValue)
|
|
{
|
|
var transaction = await context.Transactions
|
|
.FirstOrDefaultAsync(t => t.Id == order.TransactionId.Value, cancellationToken);
|
|
if (transaction != null)
|
|
{
|
|
transaction.PaymentStatus = PaymentStatus.Reject;
|
|
}
|
|
}
|
|
|
|
_logger.LogInformation(
|
|
"Expired order #{OrderId} (created {Created:u}) — inventory released, status set to Reject",
|
|
order.Id, order.Created);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to expire order #{OrderId}", order.Id);
|
|
}
|
|
}
|
|
|
|
await context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
}
|