hejianhao
2026-06-11 a06279c2e5027a5ce3729523b7908811384ca147
feat(begin-route): 新增订单开门状态管理
2个文件已添加
13个文件已修改
482 ■■■■■ 已修改文件
src/robotaxi/constants/trip-status.ts 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/trip-action-bar.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/trip-status-header.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/release-route.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/order-door-open-storage.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-bluetooth-car-control.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-order-cancel.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-order-door-open-state.ts 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-order-payment.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-page-restore.ts 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/web-socket-handlers/create-order-status-socket-handler.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/constants/trip-status.ts
@@ -2,33 +2,54 @@
 * 页面行程状态枚举:与历史 number 值保持 1:1 对齐,避免行为变化。
 */
export enum TripStatus {
  PublishRoute = 1, // 发布行程
  Publishing = 2, // 发布中
  CallingCar = 3, // 呼叫车中
  VehicleApproaching = 4, // 车辆即将到达
  VehicleArrivedPickup = 5, // 车辆到达上车点
  InService = 6, // 车辆行驶中
  ArrivedDestination = 7, // 到达目的地
  TripFinished = 8, // 行程已结束
  PaymentSuccess = 9, // 支付成功
  CancelOnlyPickup = 10, // 取消订单时只展示上车点
  /** 发布行程 */
  PublishRoute = 1,
  /** 发布中 */
  Publishing = 2,
  /** 呼叫车中 */
  CallingCar = 3,
  /** 车辆即将到达 */
  VehicleApproaching = 4,
  /** 车辆到达上车点 */
  VehicleArrivedPickup = 5,
  /** 车辆行驶中 */
  InService = 6,
  /** 到达目的地 */
  ArrivedDestination = 7,
  /** 行程已结束 */
  TripFinished = 8,
  /** 支付成功 */
  PaymentSuccess = 9,
  /** 取消订单时只展示上车点 */
  CancelOnlyPickup = 10,
}
/**
 * socket 订单状态枚举:对应后端推送 status 字段。
 */
export enum SocketOrderStatus {
  Canceled = 0, // 已取消
  PendingAccept = 1, // 待接单
  PendingDeparture = 2, // 待出发
  PickupOnTheWay = 3, // 接驾中
  WaitingPassenger = 4, // 待客上车
  PassengerOnboard = 5, // 乘客已上车
  InProgress = 6, // 服务中
  WaitingDropoff = 7, // 待客下车
  PendingPayment = 8, // 待付款
  PendingComment = 9, // 待评价
  Completed = 10, // 已完成
  /** 已取消 */
  Canceled = 0,
  /** 待接单 */
  PendingAccept = 1,
  /** 待出发 */
  PendingDeparture = 2,
  /** 接驾中 */
  PickupOnTheWay = 3,
  /** 待客上车 */
  WaitingPassenger = 4,
  /** 乘客已上车 */
  PassengerOnboard = 5,
  /** 服务中 */
  InProgress = 6,
  /** 待客下车 */
  WaitingDropoff = 7,
  /** 待付款 */
  PendingPayment = 8,
  /** 待评价 */
  PendingComment = 9,
  /** 已完成 */
  Completed = 10,
}
const TRIP_STATUS_SET = new Set<number>(Object.values(TripStatus).filter((value) => typeof value === 'number') as number[]); // 行程状态值集合
src/robotaxi/pages/begin-route/components/calling-car-modules/trip-action-bar.tsx
@@ -7,6 +7,7 @@
    handleModifyDestination?: () => void;
    handleShareTrip?: () => void;
    showModifyDestination?: boolean;
    showCancelTrip?: boolean;
};
export default function TripActionBar({
@@ -14,7 +15,8 @@
    handleCancelTrip,
    handleModifyDestination,
    handleShareTrip,
    showModifyDestination = true
    showModifyDestination = true,
    showCancelTrip = tripStatus === TripStatus.VehicleApproaching
}: Props) {
    // 按行程状态控制按钮集合,避免在 JSX 中堆叠过多条件判断
    const actionList = [
@@ -22,7 +24,7 @@
            key: "cancel",
            label: "取消行程",
            icon: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/cancel-trip.png',
            visible: tripStatus === TripStatus.VehicleApproaching,
            visible: showCancelTrip,
            onClick: handleCancelTrip
        },
        {
src/robotaxi/pages/begin-route/components/calling-car-modules/trip-status-header.tsx
@@ -18,6 +18,7 @@
    durationDisplay: { value: string | number; unit: string };
    isArriveSoon: boolean;
    isPickupCountdownSoon: boolean;
    hasDoorOpenedInCurrentOrder: boolean;
    pickupCountdownSeconds: number | null;
    finishCountdownEnabled: boolean;
    finishCountdownSeconds: number | null;
@@ -33,6 +34,7 @@
    durationDisplay,
    isArriveSoon,
    isPickupCountdownSoon,
    hasDoorOpenedInCurrentOrder,
    pickupCountdownSeconds,
    finishCountdownEnabled,
    finishCountdownSeconds,
@@ -100,7 +102,9 @@
        [TripStatus.VehicleApproaching]: isArriveSoon
            ? <>车辆即将到达,请准备上车</>
            : <>车辆还有<View className="time">{durationDisplay.value}</View>{durationDisplay.unit}到达</>,
        [TripStatus.VehicleArrivedPickup]: <>{isPickupCountdownSoon ? '车辆已到达,请尽快上车' : '车辆已到达上车点'}<View className="time" style={isPickupCountdownSoon ? { color: '#f58900' } : undefined}>{pickupCountdownSeconds !== null ? formatCountdown(pickupCountdownSeconds) : '--:--'}</View></>,
        [TripStatus.VehicleArrivedPickup]: hasDoorOpenedInCurrentOrder
            ? <>车辆已到达,请尽快上车</>
            : <>{isPickupCountdownSoon ? '车辆已到达,请尽快上车' : '车辆已到达上车点'}<View className="time" style={isPickupCountdownSoon ? { color: '#f58900' } : undefined}>{pickupCountdownSeconds !== null ? formatCountdown(pickupCountdownSeconds) : '--:--'}</View></>,
        [TripStatus.InService]: <>预计<View className="time">{inServiceDurationDisplay.value}</View>{inServiceDurationDisplay.unit}抵达目的地</>,
        [TripStatus.ArrivedDestination]: (finishCountdownEnabled || finishCountdownSeconds !== null)
            ? <>已到达目的地<View className="time">{finishCountdownSeconds !== null ? formatCountdown(finishCountdownSeconds) : '--:--'}</View></>
@@ -113,7 +117,9 @@
    const descMap = {
        [TripStatus.CallingCar]: { text: '正全力为您叫车中,请耐心等待...', className: 'calling-car-content-desc' },
        [TripStatus.VehicleApproaching]: limitMinutes ? { text: `此处限停${limitMinutes}分钟,请尽快前往上车点`, className: 'calling-car-content-warning' } : null,
        [TripStatus.VehicleArrivedPickup]: { text: isPickupCountdownSoon ? `车辆到达后最多等待${limitMinutes}分钟,超时自动取消` : '此处限停,请在倒计时结束前到达上车点', className: 'calling-car-content-desc' },
        [TripStatus.VehicleArrivedPickup]: hasDoorOpenedInCurrentOrder
            ? { text: '车门已解锁,已停止等待倒计时', className: 'calling-car-content-desc' }
            : { text: isPickupCountdownSoon ? `车辆到达后最多等待${limitMinutes}分钟,超时自动取消` : '此处限停,请在倒计时结束前到达上车点', className: 'calling-car-content-desc' },
        [TripStatus.InService]: { text: '行程已开始,请全程系好安全带', className: 'calling-car-content-desc' },
        [TripStatus.ArrivedDestination]: { text: '倒计时结束时车辆将自动驶离,请带好物品安全下车', className: 'calling-car-content-desc' },
        [TripStatus.TripFinished]: { text: '订单待支付,请核对后完成支付', className: 'calling-car-content-desc' }
src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts
@@ -12,6 +12,7 @@
    orderId?: string;
    estimatedWaitMinutes?: number;
    finishCountdownEnabled: boolean;
    hasDoorOpenedInCurrentOrder: boolean;
    handleCancelTimeout?: () => void;
    handleFinishTimeout?: () => void;
    priceUpdateVersion?: number;
@@ -27,6 +28,7 @@
    orderId,
    estimatedWaitMinutes,
    finishCountdownEnabled,
    hasDoorOpenedInCurrentOrder,
    handleCancelTimeout,
    handleFinishTimeout,
    priceUpdateVersion
@@ -41,6 +43,7 @@
    const finishTimerRef = useRef<any>(null);
    const countdownFinishedRef = useRef(false);
    const pickupTripStatusRef = useRef(tripStatus);
    const pickupDoorOpenedRef = useRef(hasDoorOpenedInCurrentOrder);
    const pickupEndTimeRef = useRef<number | null>(null);
    const finishEndTimeRef = useRef<number | null>(null);
    const lastFareRequestKeyRef = useRef<string | null>(null); // 费用明细请求去重键(订单+优惠券)
@@ -138,8 +141,26 @@
    }, [tripStatus]);
    useEffect(() => {
        pickupDoorOpenedRef.current = hasDoorOpenedInCurrentOrder;
    }, [hasDoorOpenedInCurrentOrder]);
    const stopPickupCountdown = useCallback((clearPersistedEndTime = false) => {
        clearPickupTimer();
        countdownFinishedRef.current = false;
        pickupEndTimeRef.current = null;
        setPickupCountdownSeconds(null);
        if (clearPersistedEndTime) {
            Taro.removeStorageSync(PICKUP_END_TIME_KEY);
        }
    }, [clearPickupTimer]);
    useEffect(() => {
        if (tripStatus !== TripStatus.VehicleArrivedPickup) {
            clearPickupTimer();
            return;
        }
        if (hasDoorOpenedInCurrentOrder) {
            stopPickupCountdown(true);
            return;
        }
@@ -147,6 +168,10 @@
            // 防止状态已切换后,旧定时回调仍触发取消逻辑
            if (pickupTripStatusRef.current !== TripStatus.VehicleArrivedPickup) {
                clearPickupTimer();
                return;
            }
            if (pickupDoorOpenedRef.current) {
                stopPickupCountdown(true);
                return;
            }
            const remaining = getRemainingSeconds(pickupEndTimeRef.current);
@@ -200,7 +225,7 @@
        return () => {
            clearPickupTimer();
        };
    }, [tripStatus, orderId, handleCancelTimeout, clearPickupTimer]);
    }, [tripStatus, orderId, hasDoorOpenedInCurrentOrder, handleCancelTimeout, clearPickupTimer, stopPickupCountdown]);
    useEffect(() => {
        if (tripStatus !== TripStatus.ArrivedDestination) {
src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -40,8 +40,12 @@
    handlePay?: () => void;
    /** 打开详细评价弹窗 */
    handleOpenEvaluatePopup?: () => void;
    /** 当前订单是否已开过门 */
    hasDoorOpenedInCurrentOrder?: boolean;
    /** 取消行程 */
    handleCancelTrip?: () => void;
    /** 是否展示取消行程 */
    showCancelTrip?: boolean;
    /** 修改目的地 */
    handleModifyDestination?: () => void;
    /** 是否展示修改目的地 */
@@ -92,7 +96,9 @@
    onToggleDetailFade,
    handlePay,
    handleOpenEvaluatePopup,
    hasDoorOpenedInCurrentOrder = false,
    handleCancelTrip,
    showCancelTrip = tripStatus === TripStatus.VehicleApproaching,
    handleModifyDestination,
    showModifyDestination = true,
    handleShareTrip,
@@ -129,6 +135,7 @@
        orderId,
        estimatedWaitMinutes,
        finishCountdownEnabled,
        hasDoorOpenedInCurrentOrder,
        handleCancelTimeout,
        handleFinishTimeout,
        priceUpdateVersion
@@ -153,6 +160,7 @@
                durationDisplay={durationDisplay}
                isArriveSoon={isArriveSoon}
                isPickupCountdownSoon={isPickupCountdownSoon}
                hasDoorOpenedInCurrentOrder={hasDoorOpenedInCurrentOrder}
                pickupCountdownSeconds={pickupCountdownSeconds}
                finishCountdownEnabled={finishCountdownEnabled}
                finishCountdownSeconds={finishCountdownSeconds}
@@ -217,6 +225,7 @@
                    <TripActionBar
                        tripStatus={tripStatus}
                        handleCancelTrip={handleCancelTrip}
                        showCancelTrip={showCancelTrip}
                        handleModifyDestination={handleModifyDestination}
                        showModifyDestination={showModifyDestination}
                        handleShareTrip={handleShareTrip}
src/robotaxi/pages/begin-route/components/release-route.tsx
@@ -233,7 +233,7 @@
                        <View className="route-content">
                            <View className="route-content-top">
                                <View className="route-content-txt">预估等待{fareEstimate.waitingTime || '-'}分钟</View>
                                <View style={{ margin: '0 4px' }}>|</View>
                                <View className="route-content-txt" style={{ margin: '0 4px' }}>|</View>
                                <View className="route-content-txt">限乘{fareEstimate.maxPeopleNumber || '-'}人</View>
                            </View>
                            <View className="route-content-bottom">
src/robotaxi/pages/begin-route/hooks/order-door-open-storage.ts
New file
@@ -0,0 +1,48 @@
import Taro from '@tarojs/taro';
/** 订单开门标记缓存键前缀。 */
const ORDER_DOOR_OPENED_STORAGE_KEY_PREFIX = 'ROBOTAXI_ORDER_DOOR_OPENED_';
/**
 * 生成订单开门标记缓存键。
 */
const getOrderDoorOpenedStorageKey = (orderId?: string) => {
  const normalizedOrderId = String(orderId || '').trim();
  if (!normalizedOrderId) {
    return '';
  }
  return `${ORDER_DOOR_OPENED_STORAGE_KEY_PREFIX}${normalizedOrderId}`;
};
/**
 * 读取当前订单是否已经开过门。
 */
export const getOrderDoorOpenedFlag = (orderId?: string) => {
  const storageKey = getOrderDoorOpenedStorageKey(orderId);
  if (!storageKey) {
    return false;
  }
  return Boolean(Taro.getStorageSync(storageKey));
};
/**
 * 标记当前订单已经开过门。
 */
export const setOrderDoorOpenedFlag = (orderId?: string) => {
  const storageKey = getOrderDoorOpenedStorageKey(orderId);
  if (!storageKey) {
    return;
  }
  Taro.setStorageSync(storageKey, true);
};
/**
 * 清理当前订单的开门标记。
 */
export const clearOrderDoorOpenedFlag = (orderId?: string) => {
  const storageKey = getOrderDoorOpenedStorageKey(orderId);
  if (!storageKey) {
    return;
  }
  Taro.removeStorageSync(storageKey);
};
src/robotaxi/pages/begin-route/hooks/use-bluetooth-car-control.ts
@@ -7,6 +7,7 @@
type Deps = {
  orderId: string;
  tripStatus: TripStatus;
  onBluetoothDoorUnlockSuccess?: () => void;
};
type BluetoothTokenPayload = {
@@ -49,7 +50,7 @@
/**
 * 蓝牙车控编排:负责凭证获取、蓝牙连接和 RtAuth 鉴权。
 */
export default function useBluetoothCarControl({ orderId, tripStatus }: Deps) {
export default function useBluetoothCarControl({ orderId, tripStatus, onBluetoothDoorUnlockSuccess }: Deps) {
  const managerRef = useRef<any>(null); // 蓝牙管理器实例引用
  const lastStartKeyRef = useRef(''); // 最近一次启动鉴权的参数签名
  const startAuthTaskRef = useRef<Promise<void> | null>(null); // 当前进行中的鉴权任务,防止并发重复请求
@@ -75,6 +76,24 @@
    });
  }; // 统一展示蓝牙解锁结果提示
  latestTripStatusRef.current = tripStatus;
  /**
   * 收口蓝牙真实解锁成功后的状态与提示。
   */
  const handleBluetoothDoorUnlockSuccess = () => {
    if (unlockFinishedRef.current) {
      return;
    }
    unlockFinishedRef.current = true;
    setAuthError('');
    onBluetoothDoorUnlockSuccess?.();
    const storageKey = `ROBOTAXI_UNLOCK_TOAST_SHOWN_${orderId}`;
    if (!Taro.getStorageSync(storageKey)) {
      showUnlockToast(UNLOCK_SUCCESS_MESSAGE);
      Taro.setStorageSync(storageKey, true);
    }
    closeConnectionRef.current();
  };
  /**
   * 判断当前蓝牙异步回调是否仍属于有效轮次。
@@ -173,25 +192,11 @@
        if (result?.type === 'pollingUnlock') {
          const isAlreadyUnlocked = Number(result?.data?.result) === 2; // RtNotification 挂载在 data,result=2 表示车辆已解锁
          if (isAlreadyUnlocked) {
            unlockFinishedRef.current = true;
            setAuthError('');
            const storageKey = `ROBOTAXI_UNLOCK_TOAST_SHOWN_${orderId}`;
            if (!Taro.getStorageSync(storageKey)) {
              showUnlockToast(UNLOCK_SUCCESS_MESSAGE);
              Taro.setStorageSync(storageKey, true);
            }
            closeConnectionRef.current();
            handleBluetoothDoorUnlockSuccess();
            return;
          }
          if (result?.success) {
            unlockFinishedRef.current = true;
            setAuthError('');
            const storageKey = `ROBOTAXI_UNLOCK_TOAST_SHOWN_${orderId}`;
            if (!Taro.getStorageSync(storageKey)) {
              showUnlockToast(UNLOCK_SUCCESS_MESSAGE);
              Taro.setStorageSync(storageKey, true);
            }
            closeConnectionRef.current();
            handleBluetoothDoorUnlockSuccess();
          } else {
            setAuthError(RETRY_UNLOCK_MESSAGE);
            showUnlockToast(RETRY_UNLOCK_MESSAGE);
src/robotaxi/pages/begin-route/hooks/use-order-cancel.ts
@@ -14,6 +14,7 @@
  createPickupArrivedCancelTripReportPayload
} from '../../../constants/confirm-ride-report';
import { OrderActionsDeps } from './use-order-actions.shared';
import { clearOrderDoorOpenedFlag } from './order-door-open-storage';
/** 等待计时开始时间缓存键。 */
const WAITING_TIMER_START_STORAGE_KEY = 'waitingTimerStartAt';
@@ -46,6 +47,7 @@
   * 清理活跃订单运行时缓存。
   */
  const clearActiveOrderRuntime = () => {
    clearOrderDoorOpenedFlag(deps.orderId);
    deps.setOrderId('');
    deps.setVehicleInfo({});
    clearOrderTripStorage();
src/robotaxi/pages/begin-route/hooks/use-order-door-open-state.ts
New file
@@ -0,0 +1,99 @@
import { useCallback, useEffect, useState } from 'react';
import { TripStatus } from '../../../constants/trip-status';
import { getOrderId } from '../../../utils/order';
import {
  clearOrderDoorOpenedFlag,
  getOrderDoorOpenedFlag,
  setOrderDoorOpenedFlag
} from './order-door-open-storage';
/** Hook 入参。 */
type Deps = {
  /** 当前订单 ID。 */
  orderId: string;
  /** 当前行程状态。 */
  tripStatus: TripStatus;
  /** 页面展示版本号。 */
  pageDidShowVersion: number;
};
/** Hook 返回值。 */
type UseOrderDoorOpenStateResult = {
  /** 当前订单是否已开过门。 */
  hasDoorOpenedInCurrentOrder: boolean;
  /** 标记当前订单已开门。 */
  markCurrentOrderDoorOpened: (targetOrderId?: string) => void;
  /** 清理当前订单开门标记。 */
  clearCurrentOrderDoorOpened: (targetOrderId?: string) => void;
};
/**
 * 管理当前订单“是否已经开过门”的页面级业务状态。
 */
export default function useOrderDoorOpenState({
  orderId,
  tripStatus,
  pageDidShowVersion
}: Deps): UseOrderDoorOpenStateResult {
  const [hasDoorOpenedInCurrentOrder, setHasDoorOpenedInCurrentOrder] = useState(false); // 当前订单是否已开过门
  /**
   * 按订单同步当前“已开门”标记,支持退出页面再进入后的状态恢复。
   */
  const syncOrderDoorOpenedState = useCallback((targetOrderId?: string) => {
    const resolvedOrderId = getOrderId(targetOrderId || orderId);
    if (!resolvedOrderId) {
      setHasDoorOpenedInCurrentOrder(false);
      return;
    }
    setHasDoorOpenedInCurrentOrder(getOrderDoorOpenedFlag(resolvedOrderId));
  }, [orderId]);
  /**
   * 标记当前订单已经开过门。
   */
  const markCurrentOrderDoorOpened = useCallback((targetOrderId?: string) => {
    const resolvedOrderId = getOrderId(targetOrderId || orderId);
    if (!resolvedOrderId) {
      return;
    }
    setOrderDoorOpenedFlag(resolvedOrderId);
    setHasDoorOpenedInCurrentOrder(true);
  }, [orderId]);
  /**
   * 清理当前订单的开门标记。
   */
  const clearCurrentOrderDoorOpened = useCallback((targetOrderId?: string) => {
    const resolvedOrderId = getOrderId(targetOrderId || orderId);
    if (!resolvedOrderId) {
      setHasDoorOpenedInCurrentOrder(false);
      return;
    }
    clearOrderDoorOpenedFlag(resolvedOrderId);
    setHasDoorOpenedInCurrentOrder(false);
  }, [orderId]);
  /**
   * 页面展示或订单切换后,重新同步一次开门标记,避免页面重进丢失。
   */
  useEffect(() => {
    syncOrderDoorOpenedState(orderId);
  }, [orderId, pageDidShowVersion, syncOrderDoorOpenedState]);
  /**
   * 订单进入终态后,主动清理本单开门标记,避免缓存长期残留。
   */
  useEffect(() => {
    if (![TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(tripStatus)) {
      return;
    }
    clearCurrentOrderDoorOpened(orderId);
  }, [tripStatus, orderId, clearCurrentOrderDoorOpened]);
  return {
    hasDoorOpenedInCurrentOrder,
    markCurrentOrderDoorOpened,
    clearCurrentOrderDoorOpened
  };
}
src/robotaxi/pages/begin-route/hooks/use-order-payment.ts
@@ -13,6 +13,7 @@
import { TRIP_COMPLETED_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_PAY_START_BUTTON_EVENT_ID } from '../../../constants/report-events';
import { SocketOrderStatus, TripStatus } from '../../../constants/trip-status';
import { OrderActionsDeps } from './use-order-actions.shared';
import { clearOrderDoorOpenedFlag } from './order-door-open-storage';
/** 支付 Hook 返回值。 */
type UseOrderPaymentResult = {
@@ -39,6 +40,7 @@
   * 同步支付成功态。
   */
  const syncPaymentSuccessStatus = () => {
    clearOrderDoorOpenedFlag(deps.orderId);
    deps.setTripStatus(TripStatus.PaymentSuccess);
    Taro.setStorageSync('tripStatus', TripStatus.PaymentSuccess);
  };
@@ -47,6 +49,7 @@
   * 同步行程结束待支付态。
   */
  const syncTripFinishedStatus = () => {
    clearOrderDoorOpenedFlag(deps.orderId);
    deps.setTripStatus(TripStatus.TripFinished);
    Taro.setStorageSync('tripStatus', TripStatus.TripFinished);
  };
src/robotaxi/pages/begin-route/hooks/use-page-restore.ts
@@ -15,6 +15,13 @@
  force?: boolean;
};
type RestoredPageState = {
  orderId: string;
  tripStatus: TripStatus;
  startSite: any;
  endSite: any;
};
type Deps = {
  wsHost: string;
  ensureSocketListeners: (wsHost: string) => void;
@@ -126,41 +133,73 @@
  /**
   * 优先使用活跃订单回填上下车点与行程状态。
   */
  const syncSitesFromActiveOrder = (cachedTripStatusStorage: any, s: any, e: any) => {
  const syncSitesFromActiveOrder = async (
    cachedTripStatusStorage: any,
    cachedOrderId: string,
    cachedTripStatus: TripStatus,
    s: any,
    e: any
  ): Promise<RestoredPageState> => {
    const fallbackState: RestoredPageState = {
      orderId: cachedOrderId,
      tripStatus: cachedTripStatus,
      startSite: s,
      endSite: e
    };
    if (!(cachedTripStatusStorage && cachedTripStatusStorage != 1)) {
      if (s) deps.setStartSite(s);
      if (e) deps.setEndSite(e);
      return;
      return fallbackState;
    }
    getOrderActive()
      .then((res: any) => {
        if (!res) return;
        deps.setTrunkStatus(![0, 2].includes(res.openTrunk));
        const nextStartSite = {
          id: res.startPointId || '',
          name: res.startPointName || '',
          latitude: res.startLat || 0,
          longitude: res.startLon || 0,
          electronicAreaId: res.xpElectronicFenceAreaId || '',
          time: res.startParkingTimeLimit || 0,
        };
        const nextEndSite = {
          id: res.endPointId || '',
          name: res.endPointName || '',
          latitude: res.endLat || 0,
          longitude: res.endLon || 0,
          electronicAreaId: res.xpElectronicFenceAreaId || '',
          time: res.endParkingTimeLimit || 0,
        };
        const nextStatus = toTripStatus(mapOrderStatusToTripStatus(res.status || 1));
        Taro.setStorageSync('tripStatus', nextStatus);
        deps.setTripStatus(nextStatus);
        Taro.setStorageSync('startSite', nextStartSite);
        Taro.setStorageSync('endSite', nextEndSite);
        deps.setStartSite(nextStartSite);
        deps.setEndSite(nextEndSite);
      })
      .catch(() => { });
    try {
      const res: any = await getOrderActive();
      if (!res) {
        if (s) deps.setStartSite(s);
        if (e) deps.setEndSite(e);
        return fallbackState;
      }
      deps.setTrunkStatus(![0, 2].includes(res.openTrunk));
      const nextStartSite = {
        id: res.startPointId || '',
        name: res.startPointName || '',
        latitude: res.startLat || 0,
        longitude: res.startLon || 0,
        electronicAreaId: res.xpElectronicFenceAreaId || '',
        time: res.startParkingTimeLimit || 0,
      };
      const nextEndSite = {
        id: res.endPointId || '',
        name: res.endPointName || '',
        latitude: res.endLat || 0,
        longitude: res.endLon || 0,
        electronicAreaId: res.xpElectronicFenceAreaId || '',
        time: res.endParkingTimeLimit || 0,
      };
      const nextStatus = toTripStatus(mapOrderStatusToTripStatus(res.status || 1));
      const nextOrderId = String(res.id || cachedOrderId || '');
      Taro.setStorageSync('tripStatus', nextStatus);
      deps.setTripStatus(nextStatus);
      Taro.setStorageSync('startSite', nextStartSite);
      Taro.setStorageSync('endSite', nextEndSite);
      if (nextOrderId) {
        Taro.setStorageSync('orderId', nextOrderId);
        deps.setOrderId(nextOrderId);
      }
      deps.setStartSite(nextStartSite);
      deps.setEndSite(nextEndSite);
      return {
        orderId: nextOrderId,
        tripStatus: nextStatus,
        startSite: nextStartSite,
        endSite: nextEndSite
      };
    } catch (error) {
      if (s) deps.setStartSite(s);
      if (e) deps.setEndSite(e);
      return fallbackState;
    }
  };
  /**
@@ -215,18 +254,28 @@
    const cachedTripStatusStorage = Taro.getStorageSync('tripStatus');
    const cachedTripStatus = toTripStatus(cachedTripStatusStorage, deps.getCurrentTripStatus());
    deps.setTripStatus(cachedTripStatus);
    if ([TripStatus.VehicleApproaching, TripStatus.InService].includes(cachedTripStatus)) {
      deps.setVehicleMoveAlongPoints(readPassedRouteCache(String(cachedOrderId || '')));
    const restoredPageState = await syncSitesFromActiveOrder(
      cachedTripStatusStorage,
      cachedOrderId,
      cachedTripStatus,
      s,
      e
    );
    const resolvedOrderId = restoredPageState.orderId;
    const resolvedTripStatus = restoredPageState.tripStatus;
    const resolvedStartSite = restoredPageState.startSite;
    const resolvedEndSite = restoredPageState.endSite;
    if ([TripStatus.VehicleApproaching, TripStatus.InService].includes(resolvedTripStatus)) {
      deps.setVehicleMoveAlongPoints(readPassedRouteCache(String(resolvedOrderId || '')));
    } else {
      deps.setVehicleMoveAlongPoints([]);
    }
    syncCachedOrderVehicle(cachedOrderId);
    syncSitesFromActiveOrder(cachedTripStatusStorage, s, e);
    const shouldRestorePreviewRoute = isPreviewRouteRestoreActive(cachedOrderId, cachedTripStatus);
    if (!shouldRestorePreviewRoute && cachedOrderId && [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService, TripStatus.ArrivedDestination, TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(cachedTripStatus)) {
      deps.routeRequestOrderRoute(cachedOrderId, cachedTripStatus);
    syncCachedOrderVehicle(resolvedOrderId);
    const shouldRestorePreviewRoute = isPreviewRouteRestoreActive(resolvedOrderId, resolvedTripStatus);
    if (!shouldRestorePreviewRoute && resolvedOrderId && [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService, TripStatus.ArrivedDestination, TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(resolvedTripStatus)) {
      deps.routeRequestOrderRoute(resolvedOrderId, resolvedTripStatus);
    }
    await syncLocationAndInitRoute(s, e, cachedOrderId, cachedTripStatus);
    await syncLocationAndInitRoute(resolvedStartSite, resolvedEndSite, resolvedOrderId, resolvedTripStatus);
  };
  /**
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -99,6 +99,10 @@
  handlePay: () => void;
  /** 打开评价 */
  handleOpenEvaluatePopup: () => void;
  /** 是否展示取消行程 */
  showCancelTrip: boolean;
  /** 当前订单是否已开过门 */
  hasDoorOpenedInCurrentOrder: boolean;
  /** 详情展开回调 */
  handleToggleDetailFade: (expanded: boolean, hasFare: boolean) => void;
  /** 修改目的地 */
@@ -434,6 +438,7 @@
          vehicleInfo={deps.vehicleInfo}
          handleOpenCancelPopup={(displayInfo) => deps.handleOpenCancelPopupByTripStatus(deps.tripStatus, displayInfo)}
          handleCancelTrip={() => deps.handleOpenCancelPopupByTripStatus(deps.tripStatus, true)}
          showCancelTrip={deps.showCancelTrip}
          handleModifyDestination={deps.handleModifyDestination}
          showModifyDestination={deps.showModifyDestination}
          handleShareTrip={deps.handleShareTrip}
@@ -447,6 +452,7 @@
          handlePay={deps.handlePay}
          onOpenCouponSelect={deps.handleOpenCouponSelect}
          handleOpenEvaluatePopup={deps.handleOpenEvaluatePopup}
          hasDoorOpenedInCurrentOrder={deps.hasDoorOpenedInCurrentOrder}
          estimatedWaitMinutes={deps.fareEstimate?.waitingTime}
          routeDurationSeconds={deps.routeDuration}
          onToggleDetailFade={deps.handleToggleDetailFade}
src/robotaxi/pages/begin-route/hooks/web-socket-handlers/create-order-status-socket-handler.ts
@@ -10,6 +10,7 @@
import { PENDING_CANCEL_CONTEXT_TTL_MS, type PendingCancelContext } from '../use-order-actions.shared';
import type { UseWebSocketEventsDeps } from '../use-web-socket-events';
import { isSameOrder, resolveCurrentOrderId, type WebSocketDepsRef } from './web-socket-order-guard';
import { clearOrderDoorOpenedFlag } from '../order-door-open-storage';
/**
 * 解析 payload 中用于同步订单的有效 orderId。
@@ -150,6 +151,7 @@
    deps.setCancelType(pendingCancelContext?.cancelType || 4);
    deps.pendingCancelContextRef.current = null;
  } else if (changeStatusOld === TripStatus.VehicleApproaching || changeStatusOld === TripStatus.VehicleArrivedPickup) {
    clearOrderDoorOpenedFlag(deps.orderId);
    deps.setOrderId('');
    deps.setVehicleInfo({});
    clearOrderTripStorage();
src/robotaxi/pages/begin-route/index.tsx
@@ -52,6 +52,7 @@
import useBluetoothAccess from './hooks/use-bluetooth-access';
// 蓝牙车控连接与鉴权编排
import useBluetoothCarControl from './hooks/use-bluetooth-car-control';
import useOrderDoorOpenState from './hooks/use-order-door-open-state';
import { toTripStatus, TripStatus } from '../../constants/trip-status';
import { MODIFY_DESTINATION_SOCKET_STATUS_KEY } from '../../constants/modify-destination';
import { getDistance, reportEvent } from '../../../utils/Robotaxi-public-method';
@@ -777,6 +778,15 @@
    getCurrentLatitude: () => latitude,
    getCurrentVehicleLocation: () => vehicleLocation
  }); // 页面恢复与初始化编排
  const {
    hasDoorOpenedInCurrentOrder,
    markCurrentOrderDoorOpened,
    clearCurrentOrderDoorOpened
  } = useOrderDoorOpenState({
    orderId,
    tripStatus,
    pageDidShowVersion
  }); // 当前订单开门状态编排
  const router = Taro.useRouter();
  const tripRuntime = useTripRuntime({
    waitingTimerStartKey: WAITING_TIMER_START_KEY,
@@ -948,9 +958,17 @@
   * 支付成功后的统一收口。
   */
  const handlePaymentSuccess = useCallback(() => {
    clearCurrentOrderDoorOpened(orderId);
    setTripStatus(TripStatus.PaymentSuccess);
    Taro.setStorageSync('tripStatus', TripStatus.PaymentSuccess);
  }, []);
  }, [orderId, clearCurrentOrderDoorOpened]);
  /**
   * 蓝牙真实解锁成功后,写入当前订单开门标记。
   */
  const handleBluetoothDoorUnlockSuccess = useCallback(() => {
    markCurrentOrderDoorOpened();
  }, [markCurrentOrderDoorOpened]);
  const { queryPaymentResult: queryOrderPaymentResult } = useOrderPaymentResultQuery({
    orderId,
@@ -1040,8 +1058,16 @@
    markManualDoorUnlock
  } = useBluetoothCarControl({
    orderId,
    tripStatus
    tripStatus,
    onBluetoothDoorUnlockSuccess: handleBluetoothDoorUnlockSuccess
  });
  /**
   * 手动点击开门成功后,既要记录本单已开门,也要停止后续蓝牙自动解锁流程。
   */
  const handleManualDoorUnlockSuccess = useCallback(() => {
    markCurrentOrderDoorOpened();
    markManualDoorUnlock();
  }, [markCurrentOrderDoorOpened, markManualDoorUnlock]);
  const {
    bluetoothStatusReady,
    bluetoothCardDescription,
@@ -1073,6 +1099,11 @@
    title: `我正在使用小鹏Robotaxi,车牌号[${vehicleInfo?.plateNumber || ''}]`,
    path: `/robotaxi/pages/share-trip/index?orderId=${shareOrderId}`
  });
  /** 接驾阶段取消行程按钮展示态。 */
  const showCancelTrip = useMemo(() => {
    return [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(tripStatus) && !hasDoorOpenedInCurrentOrder;
  }, [tripStatus, hasDoorOpenedInCurrentOrder]);
  useShareAppMessage(onShareAppMessage);
@@ -1444,6 +1475,8 @@
    handleOpenOverRoutePopup,
    handlePay,
    handleOpenEvaluatePopup,
    showCancelTrip,
    hasDoorOpenedInCurrentOrder,
    handleToggleDetailFade,
    handleModifyDestination,
    showModifyDestination,
@@ -1469,7 +1502,7 @@
    handleCloseSuccessAuthPopup,
    handleAuthorizeBluetooth,
    bluetoothAuthSuccess,
    onDoorUnlockSuccess: markManualDoorUnlock,
    onDoorUnlockSuccess: handleManualDoorUnlockSuccess,
    pageDidShowVersion,
    queuePollingTimeSeconds
  }); // 视图渲染委托