using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Inventory.Commands;
using TakeoutSaaS.Domain.Inventory.Enums;
using TakeoutSaaS.Domain.Inventory.Repositories;
namespace TakeoutSaaS.Application.App.Inventory.Handlers;
///
/// 释放过期锁定处理器。
///
public sealed class ReleaseExpiredInventoryLocksCommandHandler(
IInventoryRepository inventoryRepository,
ILogger logger)
: IRequestHandler
{
///
public async Task Handle(ReleaseExpiredInventoryLocksCommand request, CancellationToken cancellationToken)
{
// 1. 查询过期锁
var now = DateTime.UtcNow;
var expiredLocks = await inventoryRepository.FindExpiredLocksAsync(null, now, cancellationToken);
if (expiredLocks.Count == 0)
{
return 0;
}
// 2. 释放锁对应库存
var affected = 0;
foreach (var lockRecord in expiredLocks)
{
var item = await inventoryRepository.GetForUpdateAsync(lockRecord.TenantId, lockRecord.StoreId, lockRecord.ProductSkuId, cancellationToken);
if (item is null)
{
continue;
}
if (lockRecord.IsPresale)
{
item.PresaleLocked = Math.Max(0, item.PresaleLocked - lockRecord.Quantity);
}
else
{
item.QuantityReserved = Math.Max(0, item.QuantityReserved - lockRecord.Quantity);
}
item.IsSoldOut = item.QuantityOnHand - item.QuantityReserved - item.PresaleLocked <= (item.SafetyStock ?? 0);
await inventoryRepository.UpdateItemAsync(item, cancellationToken);
await inventoryRepository.MarkLockStatusAsync(lockRecord, InventoryLockStatus.Released, cancellationToken);
affected++;
}
await inventoryRepository.SaveChangesAsync(cancellationToken);
logger.LogInformation("释放过期库存锁定 {Count} 条", affected);
return affected;
}
}