From 955c9066fb99669df5ab5050c4125bbb892ce0ce Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期五, 17 四月 2026 22:57:38 +0800
Subject: [PATCH] fix: 修复行程状态切换时定时器与地图视角的竞争条件

---
 src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts |   10 +++++
 src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts                            |   61 ++++++++++++++++++++++++++++++
 src/robotaxi/pages/begin-route/hooks/use-trip-runtime.ts                            |    2 
 src/robotaxi/pages/begin-route/index.tsx                                            |    2 
 src/robotaxi/pages/begin-route/hooks/use-map-markers.ts                             |   20 ---------
 src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts                       |    4 -
 6 files changed, 75 insertions(+), 24 deletions(-)

diff --git a/src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts b/src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts
index eec4cd5..a5844c0 100644
--- a/src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts
+++ b/src/robotaxi/pages/begin-route/components/calling-car-modules/use-trip-countdown.ts
@@ -36,6 +36,7 @@
     const pickupTimerRef = useRef<any>(null);
     const finishTimerRef = useRef<any>(null);
     const countdownFinishedRef = useRef(false);
+    const pickupTripStatusRef = useRef(tripStatus);
     const pickupEndTimeRef = useRef<number | null>(null);
     const finishEndTimeRef = useRef<number | null>(null);
     const lastFareOrderIdRef = useRef<string | null>(null);
@@ -108,12 +109,21 @@
     }, [tripStatus]);
 
     useEffect(() => {
+        pickupTripStatusRef.current = tripStatus;
+    }, [tripStatus]);
+
+    useEffect(() => {
         if (tripStatus !== 5) {
             clearPickupTimer();
             return;
         }
 
         const runPickupTick = () => {
+            // 防止状态已切换后,旧定时回调仍触发取消逻辑
+            if (pickupTripStatusRef.current !== 5) {
+                clearPickupTimer();
+                return;
+            }
             const remaining = getRemainingSeconds(pickupEndTimeRef.current);
             if (remaining !== null && remaining <= 0) {
                 if (!countdownFinishedRef.current) {
diff --git a/src/robotaxi/pages/begin-route/hooks/use-map-markers.ts b/src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
index 45d4c3e..ff7e315 100644
--- a/src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
+++ b/src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
@@ -159,26 +159,8 @@
       ].join('|'))
       .join(';');
     const hasDropoffMarker = markersRef.current.some((m: any) => m.id === 2);
-    const hasCarMarker = markersRef.current.some((m: any) => m.id === deps.vehicleMarkerId);
-    const currentCarMarker = markersRef.current.find((m: any) => m.id === deps.vehicleMarkerId);
-    const currentRotate = Number(currentCarMarker?.rotate);
-    const targetRotate = Number(deps.vehicleDirection);
-    const angleDiff = (a: number, b: number) => {
-      const diff = Math.abs(((a - b + 540) % 360) - 180);
-      return diff;
-    };
-    const isArrivedAngleSynced = Number.isFinite(currentRotate)
-      && Number.isFinite(targetRotate)
-      && angleDiff(currentRotate, targetRotate) < 1;
     const shouldHoldMarkerByTranslate = [4, 6].includes(deps.tripStatus) && carMarkerInitialized.current && deps.statusRule.showCar && (!deps.statusRule.showDropoff || hasDropoffMarker);
-    const shouldHoldMarkerWhenArrived = deps.tripStatus === 7
-      && deps.arrivedTooltipReady
-      && carMarkerInitialized.current
-      && effectiveShowCar
-      && hasCarMarker
-      && isArrivedAngleSynced
-      && (!deps.statusRule.showDropoff || hasDropoffMarker);
-    const shouldSkipUpdate = (shouldHoldMarkerByTranslate && (deps.isAndroid || tripStatus6MarkerRefreshedRef.current)) || shouldHoldMarkerWhenArrived;
+    const shouldSkipUpdate = shouldHoldMarkerByTranslate && (deps.isAndroid || tripStatus6MarkerRefreshedRef.current);
     applyMarkerState(nextMarkers, nextMarkersSignature, shouldSkipUpdate, carMarker);
   }, [deps.tripStatus, deps.startSite?.name, deps.startSite?.latitude, deps.startSite?.longitude, deps.endSite?.name, deps.endSite?.latitude, deps.endSite?.longitude, deps.vehicleLocation, deps.vehicleDirection, deps.vehicleMarkerId, deps.isAndroid, deps.statusRule.showPickup, deps.statusRule.showCar, deps.statusRule.showDropoff]);
 
diff --git a/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts b/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts
index f725a89..b278b54 100644
--- a/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts
+++ b/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts
@@ -49,7 +49,10 @@
 
     const mapRectRef = useRef<any>(null);
     const viewportKeyRef = useRef<string>('');
+    const orderDetailViewportLockedRef = useRef(false);
     const tripStatus6ViewportLockedRef = useRef(false);
+    const orderDetailEnterSignatureRef = useRef('');
+    const orderDetailRouteUpdatedAfterEnterRef = useRef(false);
 
     const isAndroid = useMemo(() => {
         try {
@@ -183,6 +186,22 @@
         return { points, keyParts };
     };
 
+    const buildOrderDetailRouteSignature = (pickupSite: any, dropoffSite: any) => {
+        const firstPoint = deps.routePoints[0];
+        const lastPoint = deps.routePoints[deps.routePoints.length - 1];
+        return [
+            deps.routePoints.length,
+            firstPoint?.latitude ?? '',
+            firstPoint?.longitude ?? '',
+            lastPoint?.latitude ?? '',
+            lastPoint?.longitude ?? '',
+            pickupSite?.latitude ?? '',
+            pickupSite?.longitude ?? '',
+            dropoffSite?.latitude ?? '',
+            dropoffSite?.longitude ?? '',
+        ].join('|');
+    };
+
     const updateViewportByPayload = (viewportType: string, points: any[]) => {
         const mapCtx = Taro.createMapContext('map');
         const bottomPadding = computeBottomPadding(points, viewportType);
@@ -274,11 +293,48 @@
     }, [deps.tripStatus, deps.statusRule?.viewport, deps.endSite?.latitude, deps.endSite?.longitude, mapScale]);
 
     useEffect(() => {
+        if (![8, 9].includes(deps.tripStatus)) {
+            orderDetailViewportLockedRef.current = false;
+            orderDetailRouteUpdatedAfterEnterRef.current = false;
+            orderDetailEnterSignatureRef.current = '';
+            return;
+        }
+        const { pickupSite, dropoffSite } = resolveViewportSites();
+        orderDetailEnterSignatureRef.current = buildOrderDetailRouteSignature(pickupSite, dropoffSite);
+        orderDetailRouteUpdatedAfterEnterRef.current = false;
+        orderDetailViewportLockedRef.current = false;
+        viewportKeyRef.current = '';
+        setMapScale(14);
+    }, [deps.tripStatus]);
+
+    useEffect(() => {
+        if (![8, 9].includes(deps.tripStatus)) return;
+        const { pickupSite, dropoffSite } = resolveViewportSites();
+        const currentSignature = buildOrderDetailRouteSignature(pickupSite, dropoffSite);
+        if (!orderDetailEnterSignatureRef.current) {
+            orderDetailEnterSignatureRef.current = currentSignature;
+            return;
+        }
+        if (currentSignature === orderDetailEnterSignatureRef.current) return;
+        orderDetailRouteUpdatedAfterEnterRef.current = true;
+        orderDetailViewportLockedRef.current = false;
+        viewportKeyRef.current = '';
+    }, [deps.tripStatus, deps.routePoints, deps.startSite?.latitude, deps.startSite?.longitude, deps.endSite?.latitude, deps.endSite?.longitude]);
+
+    useEffect(() => {
         const { pickupSite, dropoffSite } = resolveViewportSites();
         const viewportType = deps.statusRule.viewport;
         if (viewportType === 'dropoff') return;
         const { points, keyParts } = buildViewportPayload(viewportType, pickupSite, dropoffSite);
         if (points.length === 0) return;
+        if (
+            [8, 9].includes(deps.tripStatus) &&
+            viewportType === 'order_detail' &&
+            orderDetailViewportLockedRef.current &&
+            orderDetailRouteUpdatedAfterEnterRef.current
+        ) {
+            return;
+        }
         if (deps.tripStatus === 6 && viewportType === 'route' && tripStatus6ViewportLockedRef.current) return;
         const nextKey = keyParts.join('|');
         if (nextKey === viewportKeyRef.current) return;
@@ -286,6 +342,10 @@
         updateViewportByPayload(viewportType, points);
         if (deps.tripStatus === 6 && viewportType === 'route') {
             tripStatus6ViewportLockedRef.current = true;
+        }
+        if ([8, 9].includes(deps.tripStatus) && viewportType === 'order_detail') {
+            const hasOrderDetailRouteAfterEnter = orderDetailRouteUpdatedAfterEnterRef.current && deps.routePoints.length > 0;
+            orderDetailViewportLockedRef.current = hasOrderDetailRouteAfterEnter;
         }
     }, [deps.tripStatus, deps.statusRule, deps.routePoints, deps.walkingPoints, deps.startSite?.latitude, deps.startSite?.longitude, deps.endSite?.latitude, deps.endSite?.longitude, deps.vehicleLocation, deps.longitude, deps.latitude, mapBottomPadding]);
 
@@ -314,6 +374,7 @@
         setShowLocationFlag,
         mapRectRef,
         viewportKeyRef,
+        orderDetailViewportLockedRef,
         tripStatus6ViewportLockedRef,
         isAndroid,
         resolveSafeBottomInset,
diff --git a/src/robotaxi/pages/begin-route/hooks/use-trip-runtime.ts b/src/robotaxi/pages/begin-route/hooks/use-trip-runtime.ts
index 7095372..8285eed 100644
--- a/src/robotaxi/pages/begin-route/hooks/use-trip-runtime.ts
+++ b/src/robotaxi/pages/begin-route/hooks/use-trip-runtime.ts
@@ -231,7 +231,7 @@
   useEffect(() => {
     const wasHold = [4, 6].includes(deps.lastTripStatusRef.current);
     const isHold = [4, 6].includes(deps.tripStatus);
-    if (deps.tripStatus != 4) Taro.removeStorageSync('pickupEndTime');
+    if (deps.tripStatus != 5) Taro.removeStorageSync('pickupEndTime');
     if (deps.tripStatus != 7) Taro.removeStorageSync('finishEndTime');
     if (!wasHold && isHold) {
       deps.orderRouteInitializedRef.current = false;
diff --git a/src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts b/src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts
index a72ecc0..384199c 100644
--- a/src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts
+++ b/src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts
@@ -202,12 +202,10 @@
     const currentTripStatus = Number(deps.tripStatusRef.current);
     if (currentTripStatus === 7) {
         const lastPoint = deps.lastVehiclePointRef.current;
-        const lastUpdateAt = Number(deps.lastVehicleUpdateAtRef.current || 0);
-        const now = Date.now();
         const latDiff = Math.abs(Number(lastPoint?.latitude || 0) - targetPoint.latitude);
         const lngDiff = Math.abs(Number(lastPoint?.longitude || 0) - targetPoint.longitude);
         const almostSamePoint = latDiff < 0.000003 && lngDiff < 0.000003;
-        if (almostSamePoint || (now - lastUpdateAt < 1200)) {
+        if (almostSamePoint) {
             return;
         }
     }
diff --git a/src/robotaxi/pages/begin-route/index.tsx b/src/robotaxi/pages/begin-route/index.tsx
index afda77b..95989f3 100644
--- a/src/robotaxi/pages/begin-route/index.tsx
+++ b/src/robotaxi/pages/begin-route/index.tsx
@@ -110,7 +110,7 @@
     4: { showPickup: true, showDropoff: false, showCar: true, showRoute: true, showWalking: false, viewport: 'route', translateCar: true },
     5: { showPickup: true, showDropoff: false, showCar: true, showRoute: false, showWalking: false, viewport: 'car_user', translateCar: true },
     6: { showPickup: false, showDropoff: true, showCar: true, showRoute: true, showWalking: false, viewport: 'route', translateCar: true },
-    7: { showPickup: false, showDropoff: true, showCar: true, showRoute: false, showWalking: false, viewport: 'dropoff', translateCar: false },
+    7: { showPickup: false, showDropoff: true, showCar: true, showRoute: false, showWalking: false, viewport: 'car_user', translateCar: true },
     8: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: false, viewport: 'order_detail', translateCar: false },
     9: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: false, viewport: 'order_detail', translateCar: false },
     10: { showPickup: true, showDropoff: false, showCar: false, showRoute: false, showWalking: false, viewport: 'pickup', translateCar: false },

--
Gitblit v1.7.1