hejianhao
2026-06-17 654e1f4555009ff177196b7a3d78d6d0962ddead
refactor(robotaxi): 重构订单距离校验逻辑,统一常量与工具方法
2个文件已添加
8个文件已修改
366 ■■■■■ 已修改文件
src/pages/robotaxi/home/hooks/use-quick-capsule.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/components/tip-popup/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/constants/order-distance.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/fare-detail-card.tsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-order-distance-config.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/pricing-rules/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/utils/order-distance.ts 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-quick-capsule.ts
@@ -1,11 +1,18 @@
import Taro from '@tarojs/taro';
import { useEffect, useMemo, useRef, useState } from 'react';
import { getUserQuickCapsule } from '../../../../robotaxi/api/home';
import { getOrderDistance } from '../../../../robotaxi/api/select-map-site';
import {
  RECOMMENDED_CONFIRM_RIDE_SOURCE_TYPE,
  completeConfirmRideSelectionSession,
  writeConfirmRideSiteSourceType
} from '../../../../robotaxi/constants/confirm-ride-report';
import { ORDER_DISTANCE_MIN_DISTANCE_TOAST } from '../../../../robotaxi/constants/order-distance';
import {
  ensureOrderDistanceConfig,
  isWithinStartEndMinDistance,
  OrderDistanceConfig
} from '../../../../robotaxi/utils/order-distance';
type CapsuleSite = {
  id: string | number;
@@ -32,6 +39,7 @@
export default function useQuickCapsule(deps: Deps) {
  const [capsuleSite, setCapsuleSite] = useState<CapsuleSite | null>(null); // 快捷胶囊站点
  const requestSeqRef = useRef(0); // 快捷胶囊请求序号,避免旧请求覆盖
  const distanceConfigRef = useRef<OrderDistanceConfig>({}); // 快捷胶囊下单距离配置缓存
  /**
   * 获取用于请求的上车点 ID(仅使用当前选中站点,避免滑动过程误用旧缓存)。
@@ -63,6 +71,17 @@
  const clearQuickCapsule = () => {
    setCapsuleSite(null);
  };
  /**
   * 确保快捷胶囊点击时具备距离配置,避免漏掉起终点最短距离校验。
   */
  const ensureDistanceConfigReady = async () => ensureOrderDistanceConfig({
    currentConfig: distanceConfigRef.current,
    fetchConfig: getOrderDistance,
    onConfigResolved: (config) => {
      distanceConfigRef.current = config;
    }
  });
  /**
   * 归一化快捷胶囊站点结构,兼容不同后端字段。
@@ -130,6 +149,15 @@
      Taro.showToast({ title: '暂无快捷目的地', icon: 'none' });
      return;
    }
    const distanceConfig = await ensureDistanceConfigReady();
    if (!isWithinStartEndMinDistance({
      startSite: targetStartSite,
      endSite: capsuleSite,
      minDistance: distanceConfig?.minDistance
    })) {
      Taro.showToast({ title: ORDER_DISTANCE_MIN_DISTANCE_TOAST, icon: 'none' });
      return;
    }
    Taro.setStorageSync('endSite', capsuleSite);
    Taro.setStorageSync('startSite', targetStartSite);
    writeConfirmRideSiteSourceType('start', RECOMMENDED_CONFIRM_RIDE_SOURCE_TYPE);
src/robotaxi/components/tip-popup/index.tsx
@@ -1,6 +1,11 @@
import Taro from '@tarojs/taro';
import { View, Text, Image } from '@tarojs/components';
import { useEffect, useState } from 'react';
import {
    ORDER_DISTANCE_MAX_DISTANCE_TITLE,
    ORDER_DISTANCE_MIN_DISTANCE_TITLE,
    ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE
} from '../../constants/order-distance';
import './index.less'
type Props = {
@@ -53,7 +58,9 @@
            <View className='tip-mask'></View>
            <View className='tip-content' animation={animationData}>
                <Image onClick={toHome} className='close-icon' src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/close-icon.png" />
                <View className='tip-title'>{tipType === '1' ? '上车点与目的地距离较近,建议步行前往' : '当前位置距上车点较远,暂不支持叫车,请重新选择上车点'}</View>
                <View className='tip-title'>
                    {tipType === ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE ? ORDER_DISTANCE_MIN_DISTANCE_TITLE : ORDER_DISTANCE_MAX_DISTANCE_TITLE}
                </View>
                <View onClick={onClose} className='reselect-btn'>重新选点</View>
            </View>
        </View>
src/robotaxi/constants/order-distance.ts
New file
@@ -0,0 +1,14 @@
/** 起终点距离过近提示类型。 */
export const ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE = '1';
/** 用户距离起点过远提示类型。 */
export const ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE = '2';
/** 起终点距离过近弹窗文案。 */
export const ORDER_DISTANCE_MIN_DISTANCE_TITLE = '上车点与目的地距离较近,建议步行前往';
/** 用户距离起点过远弹窗文案。 */
export const ORDER_DISTANCE_MAX_DISTANCE_TITLE = '当前位置距上车点较远,暂不支持叫车,请重新选择上车点';
/** 首页快捷胶囊距离过近提示文案。 */
export const ORDER_DISTANCE_MIN_DISTANCE_TOAST = '上车点与目的地距离较近,请重新选择';
src/robotaxi/pages/begin-route/components/calling-car-modules/fare-detail-card.tsx
@@ -174,11 +174,16 @@
                        </View>
                    }
                </View>
                <View className="car-card-end-cost" onClick={toMoneyDetail}>
                    <View className="car-card-end-cost-money">
                        全程{formatTripDistance(fareBreakdown?.kilometer)}|{formatTripDurationFromSeconds(fareBreakdown?.time)}
                <View className="car-card-end-cost">
                    <View className="car-card-end-cost-label">
                        行程费用
                    </View>
                    <Image src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/ccc-icon-right.png" />
                    <View className="car-card-end-cost-content">
                        <View className="car-card-end-cost-money" onClick={toMoneyDetail}>
                            全程{formatTripDistance(fareBreakdown?.kilometer)}|{formatTripDurationFromSeconds(fareBreakdown?.time)}
                        </View>
                        <Image src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/ccc-icon-right.png" />
                    </View>
                </View>
                {
                    (fareBreakdown?.operateFree != 1 && [TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(tripStatus)) && Number(fareBreakdown?.redPacketMoney + fareBreakdown?.couponMoney) > 0 &&
@@ -200,7 +205,7 @@
                    {
                        fareBreakdown?.operateFree != 1 ?
                            <View className="pay-box-money"><View>{formatMoney(
                               fareBreakdown?.payMoney <= 0
                                fareBreakdown?.payMoney <= 0
                                    ? 0 : fareBreakdown?.payMoney)}</View><View className="unit">元</View></View>
                            :
                            <View className="pay-box-free">载客测试免费</View>
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
@@ -384,20 +384,31 @@
        margin-top: 16px;
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 4px;
        justify-content: space-between;
        &-money {
        &-label {
          font-size: 24px;
          line-height: 44px;
          letter-spacing: normal;
          color: rgba(0, 0, 0, 0.4);
          color: rgba(0, 0, 0, 0.9);
        }
        image {
          margin-left: 4px;
          width: 32px;
          height: 32px;
        &-content {
          display: flex;
          align-items: center;
          gap: 4px;
          &-money {
            font-size: 24px;
            line-height: 44px;
            letter-spacing: normal;
            color: rgba(0, 0, 0, 0.4);
          }
          image {
            margin-left: 4px;
            width: 32px;
            height: 32px;
          }
        }
      }
@@ -756,4 +767,4 @@
      height: 17.5px;
    }
  }
}
}
src/robotaxi/pages/begin-route/hooks/use-order-distance-config.ts
@@ -3,6 +3,10 @@
import { getOrderDistance } from '../../../api/select-map-site';
import { getDistance } from '../../../../utils/Robotaxi-public-method';
import { ensureSelectedCityByLocation } from '../../../../robotaxi/utils/city';
import {
  ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE,
  ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE
} from '../../../constants/order-distance';
type OrderDistanceConfig = {
  maxDistance?: number;
@@ -73,7 +77,7 @@
        const loc = await Taro.getLocation({ type: 'gcj02' });
        const distance = getDistance(loc.latitude, loc.longitude, startSite.latitude, startSite.longitude);
        if (distance > maxDistance) {
          deps.setTipType('2');
          deps.setTipType(ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE);
          deps.setShowTipPopup(true);
          return false;
        }
@@ -83,7 +87,7 @@
    if (minDistance) {
      const distance = getDistance(startSite.latitude, startSite.longitude, deps.endSite.latitude, deps.endSite.longitude);
      if (distance < minDistance) {
        deps.setTipType('1');
        deps.setTipType(ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE);
        deps.setShowTipPopup(true);
        return false;
      }
src/robotaxi/pages/pricing-rules/index.tsx
@@ -343,7 +343,7 @@
    waitingFeePerMinute !== null &&
    waitingFeePerMinute !== undefined &&
    waitingFeePerMinute !== ''
      ? `司机到达预约地点${waitingFreeMinutes}分钟后开始收取等待费,为${formatMoney(waitingFeePerMinute)}元/分钟,不足一分钟按一分钟计算`
      ? `司机到达预约地点${waitingFreeMinutes}分钟后开始收取等待费,为${formatMoney(waitingFeePerMinute)}元/分钟,不足1分钟按1分钟计算`
      : '';
  const longDistanceStep1 = basicPricing?.num7;
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts
@@ -15,6 +15,12 @@
} from '../../../../utils/Robotaxi-public-method';
import { getGeofence } from '../../../api/home';
import { getOrderDistance } from '../../../api/select-map-site';
import {
  ensureOrderDistanceConfig,
  isWithinPickupMaxDistance,
  isWithinStartEndMinDistance,
  normalizeOrderDistanceConfig
} from '../../../utils/order-distance';
import { collectPoint } from '../../../api/site-search';
import { checkOtp } from '../../../../utils/tools';
import { jumpTo } from '../../../../utils/jump';
@@ -41,6 +47,10 @@
  writeConfirmRideSiteSourceType
} from '../../../constants/confirm-ride-report';
import { GeofenceType } from '../../../constants/geofence';
import {
  ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE,
  ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE
} from '../../../constants/order-distance';
const AUTO_SCALE_MAX = 17; // 自动缩放最大等级
const AUTO_SCALE_MIN = 11; // 自动缩放最小等级
@@ -617,6 +627,15 @@
  };
  /**
   * 获取距离配置,避免用户快速确认时使用到未初始化完成的配置。
   */
  const ensureDistanceConfigReady = async () => ensureOrderDistanceConfig({
    currentConfig: state.distanceConfig,
    fetchConfig: getOrderDistance,
    onConfigResolved: state.setDistanceConfig
  });
  /**
   * 确认选择站点(包含起终点距离与电子围栏校验)。
   */
  const confirmSite = async () => {
@@ -629,18 +648,30 @@
      return;
    }
    if (state.pageType === 'start') {
      const distanceConfig = await ensureDistanceConfigReady();
      const endSiteCache = Taro.getStorageSync('endSite');
      if (endSiteCache?.electronicAreaId && state.selectedSite?.electronicAreaId && endSiteCache.electronicAreaId !== state.selectedSite.electronicAreaId) {
        Taro.showToast({ title: '请在同一运营区内选择', icon: 'none' });
        return;
      }
      if (state.userLatitude && state.userLongitude) {
        const distance = getDistance(state.userLatitude, state.userLongitude, state.selectedSite.latitude, state.selectedSite.longitude);
        if (distance > state.distanceConfig.maxDistance) {
          state.setTipType('2');
          state.setShowTipPopup(true);
          return;
        }
      if (!isWithinPickupMaxDistance({
        userLatitude: state.userLatitude,
        userLongitude: state.userLongitude,
        pickupSite: state.selectedSite,
        maxDistance: distanceConfig?.maxDistance
      })) {
        state.setTipType(ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE);
        state.setShowTipPopup(true);
        return;
      }
      if (!isWithinStartEndMinDistance({
        startSite: state.selectedSite,
        endSite: endSiteCache,
        minDistance: distanceConfig?.minDistance
      })) {
        state.setTipType(ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE);
        state.setShowTipPopup(true);
        return;
      }
      reportConfirmMapSiteEvent();
      Taro.setStorageSync('startSite', state.selectedSite);
@@ -659,18 +690,20 @@
      jumpTo({ url: '/pages/user/login?hideBack=1' });
      return;
    }
    const distanceConfig = await ensureDistanceConfigReady();
    const startSiteCache = Taro.getStorageSync('startSite');
    if (startSiteCache?.electronicAreaId && state.selectedSite?.electronicAreaId && startSiteCache.electronicAreaId !== state.selectedSite.electronicAreaId) {
      Taro.showToast({ title: '请在同一运营区内选择', icon: 'none' });
      return;
    }
    if (startSiteCache && startSiteCache.longitude && startSiteCache.latitude) {
      const distance = getDistance(startSiteCache.latitude, startSiteCache.longitude, state.selectedSite.latitude, state.selectedSite.longitude);
      if (distance < state.distanceConfig.minDistance) {
        state.setTipType('1');
        state.setShowTipPopup(true);
        return;
      }
    if (!isWithinStartEndMinDistance({
      startSite: startSiteCache,
      endSite: state.selectedSite,
      minDistance: distanceConfig?.minDistance
    })) {
      state.setTipType(ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE);
      state.setShowTipPopup(true);
      return;
    }
    reportConfirmMapSiteEvent();
    const modifySession = Taro.getStorageSync(MODIFY_DESTINATION_SESSION_KEY); // 修改目的地会话中将终点写入候选缓存,返回行程页进入预览态
@@ -795,11 +828,7 @@
    getOrderDistance().then((res: any) => {
      if (res) {
        state.setDistanceConfig({
          maxDistance: res.maxDistance,
          minDistance: res.minDistance,
          adsorptionDistance: res.adsorptionDistance
        });
        state.setDistanceConfig(normalizeOrderDistanceConfig(res));
      }
    }).catch((err) => {
      console.error('获取距离配置失败', err);
src/robotaxi/pages/site-search/index.tsx
@@ -2,8 +2,14 @@
import { useState, useEffect, useRef } from 'react';
import { View, Text, Image, Input } from '@tarojs/components';
import './index.less';
import { getDistance, reportEvent, toUnixSeconds } from '../../../utils/Robotaxi-public-method';
import { reportEvent, toUnixSeconds } from '../../../utils/Robotaxi-public-method';
import { getOrderDistance } from '../../api/select-map-site';
import {
  ensureOrderDistanceConfig,
  isWithinPickupMaxDistance,
  isWithinStartEndMinDistance,
  normalizeOrderDistanceConfig
} from '../../utils/order-distance';
import { checkOtp } from '../../../utils/tools';
import TipPopup from '../../components/tip-popup';
import SiteListItem from './components/site-list-item';
@@ -42,6 +48,10 @@
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  writeHomeStartSiteDisplayState
} from '../../hooks/use-home-start-site-display';
import {
  ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE,
  ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE
} from '../../constants/order-distance';
import {
  HISTORY_CONFIRM_RIDE_SOURCE_TYPE,
  RECOMMENDED_CONFIRM_RIDE_SOURCE_TYPE,
@@ -94,20 +104,26 @@
  useEffect(() => {
    getOrderDistance().then((res: any) => {
      if (res) {
        setDistanceConfig({
          maxDistance: res.maxDistance,
          minDistance: res.minDistance,
          adsorptionDistance: res.adsorptionDistance
        });
        setDistanceConfig(normalizeOrderDistanceConfig(res));
      }
    }).catch(() => { });
  }, []);
  /**
   * 确保搜索页距离配置可用,避免快速确认时漏掉校验。
   */
  const ensureDistanceConfigReady = async () => ensureOrderDistanceConfig({
    currentConfig: distanceConfig,
    fetchConfig: getOrderDistance,
    onConfigResolved: setDistanceConfig
  });
  // 处理终点选择后的业务校验与跳转
  const handleEndSiteSelection = async (
    item: any,
    sourceType = UNKNOWN_CONFIRM_RIDE_SOURCE_TYPE
  ) => {
    const resolvedDistanceConfig = await ensureDistanceConfigReady();
    const startSiteCache = Taro.getStorageSync('startSite');
    if (startSiteCache?.electronicAreaId && item?.electronicAreaId && startSiteCache.electronicAreaId !== item.electronicAreaId) {
      Taro.showToast({
@@ -117,29 +133,14 @@
      return
    }
    try {
      let minDistance = distanceConfig?.minDistance;
      if (!distanceConfig?.minDistance) {
        const res = await getOrderDistance();
        if (res) {
          minDistance = res.minDistance;
          setDistanceConfig({
            maxDistance: res.maxDistance,
            minDistance: res.minDistance,
            adsorptionDistance: res.adsorptionDistance,
            geo_lat: item.latitude,
            geo_lng: item.longitude,
            odd_grid_id: item.electronicAreaId,
            timestamp: Date.now(),
          });
        }
      }
      if (startSiteCache && startSiteCache.longitude && startSiteCache.latitude) {
        const distance = getDistance(startSiteCache.latitude, startSiteCache.longitude, item.latitude, item.longitude);
        if (minDistance && distance < minDistance) {
          setTipType('1');
          setShowTipPopup(true);
          return;
        }
      if (!isWithinStartEndMinDistance({
        startSite: startSiteCache,
        endSite: item,
        minDistance: resolvedDistanceConfig?.minDistance
      })) {
        setTipType(ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE);
        setShowTipPopup(true);
        return;
      }
    } catch { }
    const modifySession = Taro.getStorageSync(MODIFY_DESTINATION_SESSION_KEY);
@@ -345,6 +346,7 @@
      return
    }
    if (pageType == 'start') {
      const resolvedDistanceConfig = await ensureDistanceConfigReady();
      const endSiteCache = Taro.getStorageSync('endSite');
      if (endSiteCache?.electronicAreaId && item?.electronicAreaId && endSiteCache.electronicAreaId !== item.electronicAreaId) {
        Taro.showToast({
@@ -354,20 +356,23 @@
        return
      }
      try {
        if (!distanceConfig?.maxDistance) {
          const res = await getOrderDistance();
          if (res) {
            setDistanceConfig({
              maxDistance: res.maxDistance,
              minDistance: res.minDistance,
              adsorptionDistance: res.adsorptionDistance
            });
          }
        }
        const loc = await Taro.getLocation({ type: 'gcj02' });
        const distance = getDistance(loc.latitude, loc.longitude, item.latitude, item.longitude);
        if (distanceConfig?.maxDistance && distance > distanceConfig.maxDistance) {
          setTipType('2');
        if (!isWithinPickupMaxDistance({
          userLatitude: loc.latitude,
          userLongitude: loc.longitude,
          pickupSite: item,
          maxDistance: resolvedDistanceConfig?.maxDistance
        })) {
          setTipType(ORDER_DISTANCE_TIP_TYPE_MAX_DISTANCE);
          setShowTipPopup(true);
          return;
        }
        if (!isWithinStartEndMinDistance({
          startSite: item,
          endSite: endSiteCache,
          minDistance: resolvedDistanceConfig?.minDistance
        })) {
          setTipType(ORDER_DISTANCE_TIP_TYPE_MIN_DISTANCE);
          setShowTipPopup(true);
          return;
        }
src/robotaxi/utils/order-distance.ts
New file
@@ -0,0 +1,103 @@
import { getDistance } from '../../utils/Robotaxi-public-method';
/** 下单距离配置。 */
export type OrderDistanceConfig = {
  /** 用户到起点的最大距离。 */
  maxDistance?: number;
  /** 起终点之间的最小距离。 */
  minDistance?: number;
  /** 点位吸附距离。 */
  adsorptionDistance?: number;
};
type CoordinateSite = {
  latitude?: number | string;
  longitude?: number | string;
};
/**
 * 统一将下单距离配置归一化为数字,避免字符串配置影响校验。
 */
export const normalizeOrderDistanceConfig = (config: any): OrderDistanceConfig => ({
  maxDistance: Number.isFinite(Number(config?.maxDistance)) ? Number(config.maxDistance) : undefined,
  minDistance: Number.isFinite(Number(config?.minDistance)) ? Number(config.minDistance) : undefined,
  adsorptionDistance: Number.isFinite(Number(config?.adsorptionDistance)) ? Number(config.adsorptionDistance) : undefined
});
/**
 * 确保下单距离配置可用,必要时拉取最新配置并回写页面状态。
 */
export const ensureOrderDistanceConfig = async (params: {
  currentConfig: any;
  fetchConfig: () => Promise<any>;
  onConfigResolved?: (config: OrderDistanceConfig) => void;
}) => {
  const normalizedCurrentConfig = normalizeOrderDistanceConfig(params.currentConfig);
  if (
    Number.isFinite(normalizedCurrentConfig.maxDistance)
    && Number.isFinite(normalizedCurrentConfig.minDistance)
  ) {
    return normalizedCurrentConfig;
  }
  const response = await params.fetchConfig();
  const normalizedNextConfig = normalizeOrderDistanceConfig(response);
  params.onConfigResolved?.(normalizedNextConfig);
  return normalizedNextConfig;
};
/**
 * 判断站点是否具备有效经纬度。
 */
export const hasValidSiteCoordinate = (site: CoordinateSite | null | undefined) => (
  Number.isFinite(Number(site?.latitude)) && Number.isFinite(Number(site?.longitude))
);
/**
 * 校验用户当前位置到起点的最大允许距离。
 */
export const isWithinPickupMaxDistance = (params: {
  userLatitude?: number | null;
  userLongitude?: number | null;
  pickupSite?: CoordinateSite | null;
  maxDistance?: number;
}) => {
  if (
    !Number.isFinite(Number(params.userLatitude))
    || !Number.isFinite(Number(params.userLongitude))
    || !hasValidSiteCoordinate(params.pickupSite)
    || !Number.isFinite(Number(params.maxDistance))
  ) {
    return true;
  }
  const distance = getDistance(
    Number(params.userLatitude),
    Number(params.userLongitude),
    Number(params.pickupSite?.latitude),
    Number(params.pickupSite?.longitude)
  );
  return distance <= Number(params.maxDistance);
};
/**
 * 校验起终点是否满足最短下单距离。
 */
export const isWithinStartEndMinDistance = (params: {
  startSite?: CoordinateSite | null;
  endSite?: CoordinateSite | null;
  minDistance?: number;
}) => {
  if (
    !hasValidSiteCoordinate(params.startSite)
    || !hasValidSiteCoordinate(params.endSite)
    || !Number.isFinite(Number(params.minDistance))
  ) {
    return true;
  }
  const distance = getDistance(
    Number(params.startSite?.latitude),
    Number(params.startSite?.longitude),
    Number(params.endSite?.latitude),
    Number(params.endSite?.longitude)
  );
  return distance >= Number(params.minDistance);
};