| | |
| | | import { useEffect, useRef } from 'react'; |
| | | import Taro from '@tarojs/taro'; |
| | | import wsManager from '../../../../utils/websocket'; |
| | | import { queryVehicleByOrderId } from '../../../api/begin-route'; |
| | | import { clearOrderTripStorage, safeParse } from '../../../../utils/Robotaxi-public-method'; |
| | | import { normalizeDistanceKm, normalizeDurationSeconds } from '../../../../robotaxi/utils/format'; |
| | | import { getOrderId } from '../../../utils/order'; |
| | | import { toTripStatus, TripStatus } from '../constants/trip-status'; |
| | | import { PENDING_CANCEL_CONTEXT_TTL_MS, PendingCancelContext } from './use-order-actions.shared'; |
| | | import { normalizeSocketRouteSnapshot, normalizeSpeedMps } from './runtime-sync'; |
| | | import { MODIFY_DESTINATION_SOCKET_STATUS_KEY } from '../constants/modify-destination'; |
| | | import { clearPassedRouteCache, writePassedRouteCache } from './passed-route-cache'; |
| | | import { resolveVehiclePoint } from './trip-motion-utils'; |
| | | import { |
| | | createOpenTrunkSocketHandler, |
| | | createOrderReassignSocketHandler, |
| | | createOrderUpdatePriceSocketHandler, |
| | | createUpdateEndAddressStatusSocketHandler |
| | | } from './web-socket-handlers/create-order-meta-socket-handlers'; |
| | | import { createOrderRouteSocketHandler } from './web-socket-handlers/create-order-route-socket-handler'; |
| | | import { createOrderStatusSocketHandler } from './web-socket-handlers/create-order-status-socket-handler'; |
| | | import { type WebSocketDepsRef } from './web-socket-handlers/web-socket-order-guard'; |
| | | import { TripStatus } from '../constants/trip-status'; |
| | | import { type PendingCancelContext } from './use-order-actions.shared'; |
| | | |
| | | type UseWebSocketEventsDeps = { |
| | | export type UseWebSocketEventsDeps = { |
| | | tripStatusRef: React.MutableRefObject<TripStatus>; |
| | | pendingCancelContextRef: React.MutableRefObject<PendingCancelContext | null>; |
| | | modifyDestinationPickupSuccessAtRef: React.MutableRefObject<number>; |
| | |
| | | resetSmoothRouteMotion: () => void; |
| | | }; |
| | | |
| | | const resolveOrderId = (payload: any, fallbackOrderId: string) => |
| | | getOrderId(payload?.content?.orderId || payload?.orderId || fallbackOrderId); |
| | | type BusinessSocketEventName = |
| | | | 'order_status' |
| | | | 'order_route' |
| | | | 'open_trunk' |
| | | | 'order_update_price' |
| | | | 'order_reassign' |
| | | | 'update_end_address_status'; |
| | | |
| | | // 解析“上一单乘客下车点”坐标,兼容顶层字段、content 字段与历史大小写差异。 |
| | | const parseBeforeUserEndSite = (payload: any) => { |
| | | const longitude = Number( |
| | | payload?.endLongitude |
| | | ?? payload?.data?.endLongitude |
| | | ?? payload?.content?.endLongitude |
| | | ); |
| | | const latitude = Number( |
| | | payload?.endLatitude |
| | | ?? payload?.endlatitude |
| | | ?? payload?.data?.endLatitude |
| | | ?? payload?.data?.endlatitude |
| | | ?? payload?.content?.endLatitude |
| | | ?? payload?.content?.endlatitude |
| | | ); |
| | | if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) { |
| | | return null; |
| | | } |
| | | return { longitude, latitude }; |
| | | }; |
| | | type SocketPayloadHandler = (payload: any) => void; |
| | | |
| | | const isSameOrder = (incomingOrderId: string, currentOrderId: string) => { |
| | | // 当前页面已无活跃订单时,不消费缺失 orderId 的兜底消息,避免手动取消后又被旧 socket 状态污染回接驾态。 |
| | | if (!incomingOrderId && !currentOrderId) return false; |
| | | if (!incomingOrderId || !currentOrderId) return false; |
| | | return String(incomingOrderId) === String(currentOrderId); |
| | | }; |
| | | const BUSINESS_SOCKET_EVENTS: BusinessSocketEventName[] = [ |
| | | 'order_status', |
| | | 'order_route', |
| | | 'open_trunk', |
| | | 'order_update_price', |
| | | 'order_reassign', |
| | | 'update_end_address_status' |
| | | ]; |
| | | |
| | | /** |
| | | * 获取仍在有效期内的待确认取消上下文;过期则立即清空,避免脏上下文影响后续订单。 |
| | | * 创建默认业务事件绑定标记。 |
| | | */ |
| | | const getValidPendingCancelContext = (deps: UseWebSocketEventsDeps) => { |
| | | const pendingCancelContext = deps.pendingCancelContextRef.current; |
| | | if (!pendingCancelContext) { |
| | | return null; |
| | | } |
| | | if (Date.now() - pendingCancelContext.createdAt > PENDING_CANCEL_CONTEXT_TTL_MS) { |
| | | deps.pendingCancelContextRef.current = null; |
| | | return null; |
| | | } |
| | | return pendingCancelContext; |
| | | }; |
| | | const createBusinessEventBoundMap = (): Record<BusinessSocketEventName, boolean> => ({ |
| | | order_status: false, |
| | | order_route: false, |
| | | open_trunk: false, |
| | | order_update_price: false, |
| | | order_reassign: false, |
| | | update_end_address_status: false |
| | | }); |
| | | |
| | | /** |
| | | * 判断当前 socket 取消消息是否可复用本地记录的取消前上下文。 |
| | | * 创建默认业务处理器映射。 |
| | | */ |
| | | const isPendingCancelContextMatched = (payload: any, pendingCancelContext: PendingCancelContext | null) => { |
| | | if (!pendingCancelContext) { |
| | | return false; |
| | | } |
| | | const socketStatus = Number(payload?.status); |
| | | if (socketStatus !== 0) { |
| | | return false; |
| | | } |
| | | const incomingOrderId = String(payload?.content?.orderId || payload?.orderId || ''); |
| | | if (!incomingOrderId || !pendingCancelContext.orderId) { |
| | | return true; |
| | | } |
| | | return incomingOrderId === pendingCancelContext.orderId; |
| | | }; |
| | | |
| | | const resetRouteRuntimeState = (deps: UseWebSocketEventsDeps) => { |
| | | deps.setRoutePoints([]); |
| | | deps.setPolyline([]); |
| | | deps.setVehicleMoveAlongPoints([]); |
| | | clearPassedRouteCache(); |
| | | deps.polylineKeyRef.current = ''; |
| | | deps.lastPolylineUpdateAtRef.current = 0; |
| | | deps.lastRoutePointRef.current = null; |
| | | deps.lastRouteUpdateAtRef.current = 0; |
| | | deps.lastVehiclePointRef.current = null; |
| | | deps.lastVehicleUpdateAtRef.current = 0; |
| | | deps.currentFullRouteRef.current = []; |
| | | deps.lastErasedIndexRef.current = 0; |
| | | deps.orderRouteInitializedRef.current = false; |
| | | deps.resetSmoothRouteMotion(); |
| | | }; |
| | | |
| | | const syncTripStatus = (payload: any, deps: UseWebSocketEventsDeps) => { |
| | | const incomingTripStatus = Number(payload?.tripStatus); |
| | | if (!Number.isFinite(incomingTripStatus)) { |
| | | return null; |
| | | } |
| | | const currentTripStatus = deps.tripStatusRef.current; |
| | | const mappedTripStatus = toTripStatus(incomingTripStatus, currentTripStatus); |
| | | const modifyDestinationPickupSuccessAt = Number(deps.modifyDestinationPickupSuccessAtRef.current || 0); |
| | | const shouldGuardModifyDestinationPickupStatus = mappedTripStatus === TripStatus.InService |
| | | && [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(currentTripStatus) |
| | | && modifyDestinationPickupSuccessAt > 0 |
| | | && Date.now() - modifyDestinationPickupSuccessAt <= deps.modifyDestinationPickupStatusGuardMs; |
| | | if (shouldGuardModifyDestinationPickupStatus) { |
| | | deps.modifyDestinationPickupSuccessAtRef.current = 0; |
| | | return currentTripStatus; |
| | | } |
| | | // 行程进行关键态不允许被回退,避免 4/6 被延迟状态覆盖导致路线链路重置 |
| | | if ([TripStatus.VehicleApproaching, TripStatus.InService].includes(currentTripStatus) && mappedTripStatus < currentTripStatus) { |
| | | return currentTripStatus; |
| | | } |
| | | if (currentTripStatus === mappedTripStatus) { |
| | | return mappedTripStatus; |
| | | } |
| | | deps.setTripStatus(mappedTripStatus); |
| | | deps.tripStatusRef.current = mappedTripStatus; |
| | | Taro.setStorageSync('tripStatus', mappedTripStatus); |
| | | return mappedTripStatus; |
| | | }; |
| | | |
| | | const syncVehicleInfo = ( |
| | | effectiveOrderId: string, |
| | | deps: UseWebSocketEventsDeps, |
| | | lastFetchedVehicleOrderIdRef: any |
| | | ) => { |
| | | if (!effectiveOrderId) return; |
| | | const shouldFetchVehicle = lastFetchedVehicleOrderIdRef.current !== effectiveOrderId; |
| | | |
| | | if (deps.orderId !== effectiveOrderId) { |
| | | deps.setOrderId(effectiveOrderId); |
| | | Taro.setStorageSync('orderId', effectiveOrderId); |
| | | } |
| | | if (!shouldFetchVehicle) return; |
| | | lastFetchedVehicleOrderIdRef.current = effectiveOrderId; |
| | | const requestedOrderId = String(effectiveOrderId); |
| | | queryVehicleByOrderId({ orderId: effectiveOrderId }) |
| | | .then((res: any) => { |
| | | const latestOrderId = String(Taro.getStorageSync('orderId') || ''); |
| | | if (latestOrderId !== requestedOrderId) { |
| | | return; |
| | | } |
| | | deps.setVehicleInfo(res); |
| | | }) |
| | | .catch(() => { }); |
| | | }; |
| | | |
| | | const handleCancelPopupPush = ( |
| | | payload: any, |
| | | changeStatusOld: TripStatus, |
| | | deps: UseWebSocketEventsDeps, |
| | | pendingCancelContext: PendingCancelContext | null |
| | | ) => { |
| | | if (!payload?.showCancelPopup) return; |
| | | const matchedPendingCancelContext = isPendingCancelContextMatched(payload, pendingCancelContext); |
| | | if (matchedPendingCancelContext) { |
| | | deps.setCancelType(pendingCancelContext?.cancelType || 4); |
| | | deps.pendingCancelContextRef.current = null; |
| | | } else if (changeStatusOld === TripStatus.VehicleApproaching || changeStatusOld === TripStatus.VehicleArrivedPickup) { |
| | | deps.setOrderId(''); |
| | | deps.setVehicleInfo({}); |
| | | clearOrderTripStorage(); |
| | | Taro.removeStorageSync('pickupEndTime'); |
| | | Taro.removeStorageSync('waitingTimerStartAt'); |
| | | Taro.removeStorageSync('waitingTimerElapsed'); |
| | | deps.setCancelType(4); |
| | | } else { |
| | | deps.setCancelType(5); |
| | | } |
| | | deps.setTripStatus(TripStatus.CancelOnlyPickup); |
| | | deps.setShowCancelPopup(true); |
| | | }; |
| | | |
| | | const handleStatusSideEffects = (mappedTripStatus: TripStatus | null, changeStatusOld: TripStatus, deps: UseWebSocketEventsDeps) => { |
| | | if (mappedTripStatus === TripStatus.InService) { |
| | | deps.setShowAnnouncementsPopup(false); |
| | | } |
| | | if (mappedTripStatus === TripStatus.VehicleArrivedPickup && changeStatusOld !== TripStatus.VehicleArrivedPickup) { |
| | | resetRouteRuntimeState(deps); |
| | | } |
| | | if (mappedTripStatus === TripStatus.VehicleApproaching && changeStatusOld !== TripStatus.VehicleApproaching) { |
| | | resetRouteRuntimeState(deps); |
| | | } |
| | | }; |
| | | |
| | | const handleOrderStatusPayload = ( |
| | | payload: any, |
| | | deps: UseWebSocketEventsDeps, |
| | | lastFetchedVehicleOrderIdRef: any |
| | | ) => { |
| | | const changeStatusOld = deps.tripStatusRef.current; |
| | | const pendingCancelContext = getValidPendingCancelContext(deps); |
| | | const currentOrderId = getOrderId(deps.orderId) || pendingCancelContext?.orderId || ''; |
| | | const incomingOrderId = payload?.content?.orderId || payload?.orderId; |
| | | if (!isSameOrder(incomingOrderId, currentOrderId)) { |
| | | return; |
| | | } |
| | | |
| | | const mappedTripStatus = syncTripStatus(payload, deps); |
| | | const effectiveOrderId = resolveOrderId(payload, currentOrderId); |
| | | syncVehicleInfo(effectiveOrderId, deps, lastFetchedVehicleOrderIdRef); |
| | | handleStatusSideEffects(mappedTripStatus, changeStatusOld, deps); |
| | | handleCancelPopupPush(payload, changeStatusOld, deps, pendingCancelContext); |
| | | |
| | | if (payload?.showPassengerNotice) { |
| | | deps.checkAndShowPassengerNotice(); |
| | | } |
| | | }; |
| | | |
| | | // 统一解析方向值 |
| | | const resolveDirectionValue = (payload: any) => { |
| | | const rawDirection = payload?.content?.direction; |
| | | const direction = Number(rawDirection); |
| | | if (!Number.isFinite(direction)) { |
| | | return null; |
| | | } |
| | | return ((direction % 360) + 360) % 360; |
| | | }; |
| | | |
| | | const updateRouteSummary = (payload: any, deps: UseWebSocketEventsDeps) => { |
| | | const normalizedDirection = resolveDirectionValue(payload); |
| | | if (typeof normalizedDirection === 'number') { |
| | | if (deps.lastSocketDirectionAtRef) { |
| | | deps.lastSocketDirectionAtRef.current = Date.now(); |
| | | } |
| | | deps.setVehicleDirection(normalizedDirection); |
| | | } |
| | | const route = safeParse(payload?.content?.route, {}); |
| | | const distanceMeters = Number(route?.distance); |
| | | const durationSecondsRaw = Number(route?.duration); |
| | | const speedMps = normalizeSpeedMps(route?.speed); |
| | | const distanceKm = normalizeDistanceKm(distanceMeters); |
| | | if (distanceKm !== null) { |
| | | deps.setRouteDistance(distanceKm); |
| | | } |
| | | const durationSeconds = normalizeDurationSeconds(durationSecondsRaw); |
| | | if (durationSeconds !== null) { |
| | | deps.setRouteDuration(durationSeconds); |
| | | } |
| | | deps.setOrderRouteMetrics?.({ |
| | | distanceMeters: Number.isFinite(distanceMeters) ? distanceMeters : null, |
| | | durationSeconds: Number.isFinite(durationSecondsRaw) ? durationSecondsRaw : null, |
| | | speedMps |
| | | }); |
| | | }; |
| | | |
| | | const syncVehiclePointOnly = (routeSnapshot: ReturnType<typeof normalizeSocketRouteSnapshot>, deps: UseWebSocketEventsDeps) => { |
| | | const targetPoint = resolveVehiclePoint( |
| | | routeSnapshot.points, |
| | | routeSnapshot.vehicleMoveAlongPoints, |
| | | routeSnapshot.normalVehiclePointMode |
| | | ); |
| | | if (!targetPoint) { |
| | | return; |
| | | } |
| | | deps.updateVehicleLocationThrottled(targetPoint.latitude, targetPoint.longitude, true); |
| | | }; |
| | | |
| | | const handleOrderRoutePayload = (payload: any, deps: UseWebSocketEventsDeps) => { |
| | | const beforeUserSite = parseBeforeUserEndSite(payload); |
| | | deps.setBeforeUserEndSite(beforeUserSite); |
| | | const currentTripStatus = deps.tripStatusRef.current; |
| | | // socket 路线拦截只跟随真实的“修改目的地预览态”,不要被本地终点覆盖态误伤。 |
| | | const isModifyDestinationPreviewActive = Boolean(deps.isModifyDestinationReviewVisible); |
| | | const shouldKeepModifyPreviewSummary = isModifyDestinationPreviewActive; |
| | | if (!shouldKeepModifyPreviewSummary) { |
| | | updateRouteSummary(payload, deps); |
| | | } |
| | | // 行程结束态仅更新概要信息,不再消费路线点,避免视口反复重算造成跳动 |
| | | if ([TripStatus.TripFinished, TripStatus.PaymentSuccess].includes(currentTripStatus)) { |
| | | return; |
| | | } |
| | | const routeSnapshot = normalizeSocketRouteSnapshot(payload); |
| | | const shouldOnlySyncModifyPreviewVehicle = isModifyDestinationPreviewActive && currentTripStatus === TripStatus.InService; |
| | | const shouldFreezeModifyPreviewRoute = isModifyDestinationPreviewActive && currentTripStatus !== TripStatus.InService; |
| | | if (isModifyDestinationPreviewActive) { |
| | | deps.updatePreviousRoutePoints?.(routeSnapshot.points || []); |
| | | } |
| | | if (shouldFreezeModifyPreviewRoute) { |
| | | return; |
| | | } |
| | | if (shouldOnlySyncModifyPreviewVehicle) { |
| | | syncVehiclePointOnly(routeSnapshot, deps); |
| | | return; |
| | | } |
| | | const currentOrderId = getOrderId(deps.orderId); |
| | | if (routeSnapshot.vehicleMoveAlongPoints.length > 1) { |
| | | writePassedRouteCache(currentOrderId, routeSnapshot.vehicleMoveAlongPoints); |
| | | } |
| | | deps.applySmoothRouteSnapshot(routeSnapshot); |
| | | if (routeSnapshot.forceFullRedraw && typeof deps.onOrderUpdateLineRendered === 'function') { |
| | | Taro.nextTick(() => { |
| | | deps.onOrderUpdateLineRendered?.(routeSnapshot.points || []); |
| | | }); |
| | | } |
| | | }; |
| | | const createBusinessHandlerMap = (): Record<BusinessSocketEventName, SocketPayloadHandler | null> => ({ |
| | | order_status: null, |
| | | order_route: null, |
| | | open_trunk: null, |
| | | order_update_price: null, |
| | | order_reassign: null, |
| | | update_end_address_status: null |
| | | }); |
| | | |
| | | export default function useWebSocketEvents(deps: UseWebSocketEventsDeps) { |
| | | // 持有最新依赖,避免渲染过程反复解绑/重绑监听 |
| | | const depsRef = useRef(deps); |
| | | const depsRef = useRef(deps) as WebSocketDepsRef; |
| | | useEffect(() => { |
| | | depsRef.current = deps; |
| | | }, [deps]); |
| | | |
| | | // 各事件是否已绑定标记 |
| | | const authSuccessBound = useRef(false); |
| | | const messageBound = useRef(false); |
| | | const orderStatusBound = useRef(false); |
| | | const orderRouteBound = useRef(false); |
| | | const openTrunkBound = useRef(false); |
| | | const orderUpdatePriceBound = useRef(false); |
| | | const orderReassignBound = useRef(false); |
| | | const updateEndAddressStatusBound = useRef(false); |
| | | const businessEventBoundRef = useRef(createBusinessEventBoundMap()); |
| | | |
| | | const authSuccessHandlerRef = useRef<(data: any) => void>(); |
| | | const messageHandlerRef = useRef<(data: any) => void>(); |
| | | const orderStatusHandlerRef = useRef<(data: any) => void>(); |
| | | const orderRouteHandlerRef = useRef<(data: any) => void>(); |
| | | const openTrunkHandlerRef = useRef<(data: any) => void>(); |
| | | const orderUpdatePriceHandlerRef = useRef<(data: any) => void>(); |
| | | const orderReassignHandlerRef = useRef<(data: any) => void>(); |
| | | const updateEndAddressStatusHandlerRef = useRef<(data: any) => void>(); |
| | | const businessHandlerMapRef = useRef(createBusinessHandlerMap()); |
| | | const socketOpenHandlerRef = useRef<(() => void) | null>(null); |
| | | const lastFetchedVehicleOrderIdRef = useRef<string | null>(null); |
| | | |
| | | // 仅初始化一次事件处理器与解绑逻辑 |
| | | /** |
| | | * 绑定业务事件监听。 |
| | | */ |
| | | const bindSocketListeners = () => { |
| | | if (!authSuccessBound.current && authSuccessHandlerRef.current) { |
| | | wsManager.on('auth_success', authSuccessHandlerRef.current); |
| | | authSuccessBound.current = true; |
| | | } |
| | | BUSINESS_SOCKET_EVENTS.forEach((eventName) => { |
| | | const handler = businessHandlerMapRef.current[eventName]; |
| | | if (!businessEventBoundRef.current[eventName] && handler) { |
| | | wsManager.on(eventName, handler); |
| | | businessEventBoundRef.current[eventName] = true; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | businessHandlerMapRef.current = { |
| | | order_status: createOrderStatusSocketHandler(depsRef, lastFetchedVehicleOrderIdRef), |
| | | order_route: createOrderRouteSocketHandler(depsRef), |
| | | open_trunk: createOpenTrunkSocketHandler(depsRef), |
| | | order_update_price: createOrderUpdatePriceSocketHandler(depsRef), |
| | | order_reassign: createOrderReassignSocketHandler(depsRef, lastFetchedVehicleOrderIdRef), |
| | | update_end_address_status: createUpdateEndAddressStatusSocketHandler(depsRef) |
| | | }; |
| | | authSuccessHandlerRef.current = () => { |
| | | if (!messageBound.current && messageHandlerRef.current) { |
| | | wsManager.on('message', messageHandlerRef.current); |
| | |
| | | messageHandlerRef.current = (data) => { |
| | | console.log('ws message:', data); |
| | | }; |
| | | openTrunkHandlerRef.current = (payload) => { |
| | | const currentDeps = depsRef.current; |
| | | const isOpen = Number(payload?.isOpen); |
| | | if (isOpen === 1) { |
| | | currentDeps.setFinishCountdownEnabled(true); |
| | | } |
| | | }; |
| | | orderUpdatePriceHandlerRef.current = (payload) => { |
| | | const currentDeps = depsRef.current; |
| | | const incomingOrderId = getOrderId(payload?.orderId || payload?.content?.orderId); |
| | | const currentOrderId = getOrderId(currentDeps.orderId); |
| | | if (!isSameOrder(incomingOrderId, currentOrderId)) { |
| | | return; |
| | | } |
| | | currentDeps.setPriceUpdateVersion((v: number) => v + 1); |
| | | }; |
| | | orderReassignHandlerRef.current = (payload) => { |
| | | const currentDeps = depsRef.current; |
| | | const incomingOrderId = getOrderId(payload?.orderId || payload?.content?.orderId); |
| | | const currentOrderId = getOrderId(currentDeps.orderId); |
| | | if (!isSameOrder(incomingOrderId, currentOrderId)) { |
| | | return; |
| | | } |
| | | lastFetchedVehicleOrderIdRef.current = null; |
| | | const requestedOrderId = String(currentOrderId || ''); |
| | | queryVehicleByOrderId({ orderId: currentOrderId }) |
| | | .then((res: any) => { |
| | | const latestOrderId = getOrderId(depsRef.current.orderId); |
| | | if (!requestedOrderId || latestOrderId !== requestedOrderId) { |
| | | return; |
| | | } |
| | | currentDeps.setVehicleInfo(res); |
| | | }) |
| | | .catch(() => { }); |
| | | }; |
| | | updateEndAddressStatusHandlerRef.current = (payload) => { |
| | | const currentDeps = depsRef.current; |
| | | const incomingOrderId = getOrderId(payload?.content?.orderId); |
| | | const currentOrderId = getOrderId(currentDeps.orderId); |
| | | if (!isSameOrder(incomingOrderId, currentOrderId)) { |
| | | return; |
| | | } |
| | | const nextStatus = Number(payload?.status); |
| | | const normalizedStatus = Number.isFinite(nextStatus) ? nextStatus : null; |
| | | currentDeps.setModifyDestinationSocketStatus(normalizedStatus); |
| | | if (normalizedStatus === null) { |
| | | Taro.removeStorageSync(MODIFY_DESTINATION_SOCKET_STATUS_KEY); |
| | | return; |
| | | } |
| | | Taro.setStorageSync(MODIFY_DESTINATION_SOCKET_STATUS_KEY, { |
| | | orderId: String(currentOrderId || incomingOrderId || ''), |
| | | status: normalizedStatus |
| | | }); |
| | | }; |
| | | orderStatusHandlerRef.current = (payload) => { |
| | | handleOrderStatusPayload(payload, depsRef.current, lastFetchedVehicleOrderIdRef); |
| | | }; |
| | | orderRouteHandlerRef.current = (payload) => { |
| | | handleOrderRoutePayload(payload, depsRef.current); |
| | | }; |
| | | // 处理重进页面时 useDidShow 先于 handler 初始化的场景,确保已连接状态下也能完成监听绑定 |
| | | if (wsManager.isConnectedStatus()) { |
| | | bindSocketListeners(); |
| | | } |
| | | |
| | | return () => { |
| | | if (authSuccessBound.current && authSuccessHandlerRef.current) { |
| | | wsManager.off('auth_success', authSuccessHandlerRef.current); |
| | |
| | | wsManager.off('message', messageHandlerRef.current); |
| | | messageBound.current = false; |
| | | } |
| | | if (orderStatusBound.current && orderStatusHandlerRef.current) { |
| | | wsManager.off('order_status', orderStatusHandlerRef.current); |
| | | orderStatusBound.current = false; |
| | | } |
| | | if (orderRouteBound.current && orderRouteHandlerRef.current) { |
| | | wsManager.off('order_route', orderRouteHandlerRef.current); |
| | | orderRouteBound.current = false; |
| | | } |
| | | if (openTrunkBound.current && openTrunkHandlerRef.current) { |
| | | wsManager.off('open_trunk', openTrunkHandlerRef.current); |
| | | openTrunkBound.current = false; |
| | | } |
| | | if (orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) { |
| | | wsManager.off('order_update_price', orderUpdatePriceHandlerRef.current); |
| | | orderUpdatePriceBound.current = false; |
| | | } |
| | | if (orderReassignBound.current && orderReassignHandlerRef.current) { |
| | | wsManager.off('order_reassign', orderReassignHandlerRef.current); |
| | | orderReassignBound.current = false; |
| | | } |
| | | if (updateEndAddressStatusBound.current && updateEndAddressStatusHandlerRef.current) { |
| | | wsManager.off('update_end_address_status', updateEndAddressStatusHandlerRef.current); |
| | | updateEndAddressStatusBound.current = false; |
| | | } |
| | | BUSINESS_SOCKET_EVENTS.forEach((eventName) => { |
| | | const handler = businessHandlerMapRef.current[eventName]; |
| | | if (businessEventBoundRef.current[eventName] && handler) { |
| | | wsManager.off(eventName, handler); |
| | | businessEventBoundRef.current[eventName] = false; |
| | | } |
| | | }); |
| | | if (socketOpenHandlerRef.current) { |
| | | wsManager.off('open', socketOpenHandlerRef.current); |
| | | socketOpenHandlerRef.current = null; |
| | |
| | | }; |
| | | }, []); |
| | | |
| | | // 绑定业务事件监听 |
| | | const bindSocketListeners = () => { |
| | | if (!authSuccessBound.current && authSuccessHandlerRef.current) { |
| | | wsManager.on('auth_success', authSuccessHandlerRef.current); |
| | | authSuccessBound.current = true; |
| | | } |
| | | if (!orderStatusBound.current && orderStatusHandlerRef.current) { |
| | | wsManager.on('order_status', orderStatusHandlerRef.current); |
| | | orderStatusBound.current = true; |
| | | } |
| | | if (!orderRouteBound.current && orderRouteHandlerRef.current) { |
| | | wsManager.on('order_route', orderRouteHandlerRef.current); |
| | | orderRouteBound.current = true; |
| | | } |
| | | if (!openTrunkBound.current && openTrunkHandlerRef.current) { |
| | | wsManager.on('open_trunk', openTrunkHandlerRef.current); |
| | | openTrunkBound.current = true; |
| | | } |
| | | if (!orderUpdatePriceBound.current && orderUpdatePriceHandlerRef.current) { |
| | | wsManager.on('order_update_price', orderUpdatePriceHandlerRef.current); |
| | | orderUpdatePriceBound.current = true; |
| | | } |
| | | if (!orderReassignBound.current && orderReassignHandlerRef.current) { |
| | | wsManager.on('order_reassign', orderReassignHandlerRef.current); |
| | | orderReassignBound.current = true; |
| | | } |
| | | if (!updateEndAddressStatusBound.current && updateEndAddressStatusHandlerRef.current) { |
| | | wsManager.on('update_end_address_status', updateEndAddressStatusHandlerRef.current); |
| | | updateEndAddressStatusBound.current = true; |
| | | } |
| | | }; |
| | | |
| | | // 确保连接可用并完成监听绑定 |
| | | /** |
| | | * 确保连接可用并完成监听绑定。 |
| | | */ |
| | | const ensureSocketListeners = (host: string) => { |
| | | if (wsManager.isConnectedStatus()) { |
| | | bindSocketListeners(); |