using MediatR; using Microsoft.AspNetCore.Http; using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Services; 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 CheckStoreDeliveryZoneQueryHandler( IStoreRepository storeRepository, ITenantProvider tenantProvider, IHttpContextAccessor httpContextAccessor, IDeliveryZoneService deliveryZoneService) : IRequestHandler { /// public async Task Handle(CheckStoreDeliveryZoneQuery request, CancellationToken cancellationToken) { // 1. 校验门店存在 var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId(); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); if (store is null) { throw new BusinessException(ErrorCodes.NotFound, "门店不存在"); } // 2. (空行后) 执行配送范围判断 var zones = await storeRepository.GetDeliveryZonesAsync(request.StoreId, tenantId, cancellationToken); var result = deliveryZoneService.CheckPointInZones(zones, request.Longitude, request.Latitude); // 3. (空行后) 计算距离 if (store.Longitude.HasValue && store.Latitude.HasValue) { var distance = CalculateDistanceKm(store.Latitude.Value, store.Longitude.Value, request.Latitude, request.Longitude); result = result with { Distance = (decimal)Math.Round(distance, 2, MidpointRounding.AwayFromZero) }; } return result; } private static double CalculateDistanceKm(double latitude1, double longitude1, double latitude2, double longitude2) { const double earthRadius = 6371000d; var latRad1 = DegreesToRadians(latitude1); var latRad2 = DegreesToRadians(latitude2); var deltaLat = DegreesToRadians(latitude2 - latitude1); var deltaLon = DegreesToRadians(longitude2 - longitude1); var sinLat = Math.Sin(deltaLat / 2); var sinLon = Math.Sin(deltaLon / 2); var a = sinLat * sinLat + Math.Cos(latRad1) * Math.Cos(latRad2) * sinLon * sinLon; var c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a))); return earthRadius * c / 1000d; } private static double DegreesToRadians(double degrees) => degrees * (Math.PI / 180d); }