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; } }