using MediatR; using Microsoft.Extensions.Logging; using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Services; using TakeoutSaaS.Domain.Stores.Entities; using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Stores.Handlers; /// /// 更新配送区域处理器。 /// public sealed class UpdateStoreDeliveryZoneCommandHandler( IStoreRepository storeRepository, ITenantProvider tenantProvider, IGeoJsonValidationService geoJsonValidationService, ILogger logger) : IRequestHandler { /// public async Task Handle(UpdateStoreDeliveryZoneCommand request, CancellationToken cancellationToken) { // 1. 读取区域 var tenantId = tenantProvider.GetCurrentTenantId(); var existing = await storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken); if (existing is null) { return null; } // 2. 校验门店归属 if (existing.StoreId != request.StoreId) { throw new BusinessException(ErrorCodes.ValidationFailed, "配送区域不属于该门店"); } // 3. (空行后) 校验 GeoJSON var validation = geoJsonValidationService.ValidatePolygon(request.PolygonGeoJson); if (!validation.IsValid) { throw new BusinessException(ErrorCodes.ValidationFailed, validation.ErrorMessage ?? "配送范围格式错误"); } // 4. (空行后) 更新字段 existing.ZoneName = request.ZoneName.Trim(); existing.PolygonGeoJson = (validation.NormalizedGeoJson ?? request.PolygonGeoJson).Trim(); existing.MinimumOrderAmount = request.MinimumOrderAmount; existing.DeliveryFee = request.DeliveryFee; existing.EstimatedMinutes = request.EstimatedMinutes; existing.SortOrder = request.SortOrder; // 5. (空行后) 持久化 await storeRepository.UpdateDeliveryZoneAsync(existing, cancellationToken); await storeRepository.SaveChangesAsync(cancellationToken); logger.LogInformation("更新配送区域 {DeliveryZoneId} 对应门店 {StoreId}", existing.Id, existing.StoreId); // 6. (空行后) 返回 DTO return StoreMapping.ToDto(existing); } }