Files
TakeoutSaaS.TenantApi/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/CheckStoreDeliveryZoneQueryHandler.cs

65 lines
2.8 KiB
C#

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;
/// <summary>
/// 配送范围检测查询处理器。
/// </summary>
public sealed class CheckStoreDeliveryZoneQueryHandler(
IStoreRepository storeRepository,
ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
IDeliveryZoneService deliveryZoneService)
: IRequestHandler<CheckStoreDeliveryZoneQuery, StoreDeliveryCheckResultDto>
{
/// <inheritdoc />
public async Task<StoreDeliveryCheckResultDto> 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);
}