hejianhao
2026-05-28 395e938d07f5f4f1e8f0660a9683f5b718e7651e
feat(begin-route): 新增路线指标同步功能并修复目的地修改状态校验

- 新增 syncOrderRouteMetrics 参数控制是否同步订单路线指标
- 新增订单维度的目的地修改socket状态缓存校验,避免跨订单状态错乱
- 优化目的地修改的阈值校验逻辑,补充数值类型检查
4个文件已修改
56 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/hooks/use-modify-destination-preview-route.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-route-manager.shared.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-route-request.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-modify-destination-preview-route.ts
@@ -5,6 +5,7 @@
type RequestRouteOptions = {
  startPointId?: string | number;
  force?: boolean;
  syncOrderRouteMetrics?: boolean;
};
type Deps = {
@@ -114,7 +115,8 @@
      routeContext.destLng,
      {
        startPointId: routeContext.startPointId,
        force: true
        force: true,
        syncOrderRouteMetrics: false
      },
    );
  }, [
@@ -178,7 +180,8 @@
      routeContext.destLng,
      {
        startPointId: routeContext.startPointId,
        force: true
        force: true,
        syncOrderRouteMetrics: false
      },
    );
    Taro.showToast({
src/robotaxi/pages/begin-route/hooks/use-route-manager.shared.ts
@@ -43,6 +43,8 @@
  couponId?: string;
  /** 起点站点 ID。 */
  startPointId?: string | number;
  /** 是否同步原订单剩余里程/时长指标。 */
  syncOrderRouteMetrics?: boolean;
};
/** 步行起点定位缓存结构。 */
src/robotaxi/pages/begin-route/hooks/use-route-request.ts
@@ -335,6 +335,7 @@
      refreshMode: 'full',
      couponId: '',
      startPointId: '',
      syncOrderRouteMetrics: true,
    };
    if (typeof startPointIdOrOptions === 'boolean') {
      resolvedOptions.force = startPointIdOrOptions;
@@ -352,6 +353,7 @@
    const force = !!resolvedOptions.force;
    const refreshMode = resolvedOptions.refreshMode || 'full';
    const startPointId = resolvedOptions.startPointId ?? '';
    const syncOrderRouteMetrics = resolvedOptions.syncOrderRouteMetrics !== false;
    const now = Date.now();
    if (now - lastRouteAndFareRequestAtRef.current < 1000) {
@@ -475,6 +477,13 @@
      if (normalizedDuration !== null) {
        setRouteDuration(normalizedDuration);
      }
      if (syncOrderRouteMetrics) {
        deps.setOrderRouteMetrics?.({
          distanceMeters: Number.isFinite(Number(route?.distance)) ? Number(route?.distance) : null,
          durationSeconds: Number.isFinite(Number(route?.duration)) ? Number(route?.duration) : null,
          speedMps: normalizeSpeedMps(route?.speed)
        });
      }
      if (refreshMode === 'full') {
        const points = Array.isArray(route?.polyline) ? route.polyline : [];
src/robotaxi/pages/begin-route/index.tsx
@@ -551,19 +551,32 @@
    : DEFAULT_REQUOTE_EXPIRE_SECONDS; // 重新报价有效期(秒)
  const modifyDistanceLimitMeters = Number(distanceConfig?.xczSyDistance); // 修改目的地剩余里程限制(米)
  const modifyDurationLimitMinutes = Number(distanceConfig?.xczSyTime); // 修改目的地剩余时间限制(分钟)
  const effectiveModifyDestinationSocketStatus = useMemo(() => {
    if (typeof modifyDestinationSocketStatus === 'number') {
      return modifyDestinationSocketStatus;
    }
    const cachedSocketStatus = Taro.getStorageSync(MODIFY_DESTINATION_SOCKET_STATUS_KEY);
    const cachedOrderId = String(cachedSocketStatus?.orderId || '');
    const currentOrderId = String(orderId || '');
    if (!cachedOrderId || !currentOrderId || cachedOrderId !== currentOrderId) {
      return null;
    }
    const cachedStatus = Number(cachedSocketStatus?.status);
    return Number.isFinite(cachedStatus) ? cachedStatus : null;
  }, [modifyDestinationSocketStatus, orderId]);
  const isTripStatusAllowModifyEntry = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(tripStatus);
  const showModifyDestination = useMemo(() => {
    if (!isTripStatusAllowModifyEntry) {
      return false;
    }
    if (modifyDestinationSocketStatus === 0) {
    if (effectiveModifyDestinationSocketStatus === 0) {
      return false;
    }
    if (tripStatus !== TripStatus.InService) {
      return true;
    }
    if (modifyDestinationSocketStatus === 1) {
    if (effectiveModifyDestinationSocketStatus === 1) {
      return true;
    }
    const hasDistanceLimit = Number.isFinite(modifyDistanceLimitMeters) && modifyDistanceLimitMeters > 0;
@@ -584,7 +597,7 @@
  }, [
    isTripStatusAllowModifyEntry,
    tripStatus,
    modifyDestinationSocketStatus,
    effectiveModifyDestinationSocketStatus,
    modifyDistanceLimitMeters,
    modifyDurationLimitMinutes,
    orderRouteMetrics?.distanceMeters,
@@ -991,13 +1004,13 @@
  };
  // 预览态下如果后端推送“不可修改”,立即退出预览态并恢复原路线
  useEffect(() => {
    if (!isModifyDestinationReviewVisible || modifyDestinationSocketStatus !== 0) {
    if (!isModifyDestinationReviewVisible || effectiveModifyDestinationSocketStatus !== 0) {
      return;
    }
    handleModifyDestinationSocketDisabled();
  }, [
    isModifyDestinationReviewVisible,
    modifyDestinationSocketStatus,
    effectiveModifyDestinationSocketStatus,
    previousEndSite,
    previousFareEstimate,
    previousRoutePoints,
@@ -1006,24 +1019,29 @@
  ]);
  // 确认修改前进行阈值校验,命中限制或接口失败都展示失败弹窗
  const handleConfirmModifyDestinationWithGuard = async (selectedRoute?: any) => {
    if (modifyDestinationSocketStatus === 0) {
    if (effectiveModifyDestinationSocketStatus === 0) {
      handleModifyDestinationSocketDisabled();
      return;
    }
    const currentDistance = Number(orderRouteMetrics?.distanceMeters);
    const currentDurationSeconds = Number(orderRouteMetrics?.durationSeconds);
    const shouldBypassLocalLimit = effectiveModifyDestinationSocketStatus === 1;
    const currentDistance = orderRouteMetrics?.distanceMeters;
    const currentDurationSeconds = orderRouteMetrics?.durationSeconds;
    const distanceLimit = Number(distanceConfig?.xczSyDistance);
    const durationLimitMinutes = Number(distanceConfig?.xczSyTime);
    const isTripInService = tripStatus === TripStatus.InService;
    const hitDistanceLimit = isTripInService
    const hitDistanceLimit = !shouldBypassLocalLimit
      && isTripInService
      && Number.isFinite(distanceLimit)
      && distanceLimit > 0
      && typeof currentDistance === 'number'
      && Number.isFinite(currentDistance)
      && currentDistance < distanceLimit;
    const hitDurationLimit = isTripInService
    const hitDurationLimit = !shouldBypassLocalLimit
      && isTripInService
      && Number.isFinite(durationLimitMinutes)
      && durationLimitMinutes > 0
      && typeof currentDurationSeconds === 'number'
      && Number.isFinite(currentDurationSeconds)
      && (currentDurationSeconds / 60) < durationLimitMinutes;