package com.ruoyi.app.flashdelivery.route; import com.ruoyi.system.domain.flash.FlashDeliveryVehicleType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; /** 路线距离服务:优先请求地图路线,异常时使用球面直线距离保证报价仍然可用。 */ @Service public class FlashDeliveryRouteService { private static final double EARTH_RADIUS_METERS = 6_371_000D; private static final Logger log = LoggerFactory.getLogger(FlashDeliveryRouteService.class); private final RouteDistanceProvider provider; public FlashDeliveryRouteService(RouteDistanceProvider provider) { this.provider = provider; } /** 按用户选择车型计算取送路线:优先地图路线,异常或无结果时降级球面直线距离。 */ public RouteDistance calculate(GeoPoint origin, GeoPoint destination, Integer vehicleType) { validate(origin); validate(destination); int selectedVehicle = FlashDeliveryVehicleType.isSupported(vehicleType) ? vehicleType : FlashDeliveryVehicleType.MOTORCYCLE; try { RouteDistance route = provider.route(origin.latitude(), origin.longitude(), destination.latitude(), destination.longitude(), selectedVehicle); if (route != null && route.distanceMeters() > 0) { return route; } } catch (RuntimeException exception) { // 不记录地址、坐标或 Key;保留车型和失败原因以定位 Google Routes 降级。 log.warn("Google Routes unavailable; use straight-line fallback. vehicleType={}, reason={}", selectedVehicle, exception.getMessage()); } // Haversine 只作为降级结果,并通过 source=STRAIGHT_LINE 明确告知调用方。 double lat1 = Math.toRadians(origin.latitude().doubleValue()); double lat2 = Math.toRadians(destination.latitude().doubleValue()); double deltaLat = lat2 - lat1; double deltaLon = Math.toRadians(destination.longitude().doubleValue() - origin.longitude().doubleValue()); double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); int meters = (int) Math.round(EARTH_RADIUS_METERS * 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a))); return new RouteDistance(Math.max(1, meters), null, "STRAIGHT_LINE"); } private void validate(GeoPoint point) { if (point == null || point.latitude() == null || point.longitude() == null || point.latitude().doubleValue() < -90D || point.latitude().doubleValue() > 90D || point.longitude().doubleValue() < -180D || point.longitude().doubleValue() > 180D) { throw new IllegalArgumentException("invalid coordinates"); } } }