hejianhao
2026-06-08 dc1251725e17a63de362b8ca7ee4884e44227b69
feat(robotaxi): 修改埋点问题
10个文件已修改
192 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/components/auth-bluetooth-popup/success-auth/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/constants/confirm-ride-report.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/constants/report-events.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-order-cancel.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/web-socket-handlers/create-order-status-socket-handler.ts 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/index.tsx 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Robotaxi-public-method.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/auth-bluetooth-popup/success-auth/index.tsx
@@ -48,7 +48,7 @@
      <View className='popup-content' animation={animationData}>
        <Image className='popup-icon' src={'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/bluetooth-success-auth.png'} />
        <Text className='popup-title'>已授权蓝牙,无感自动开启车辆</Text>
        <Text className='popup-desc'>授权并打开蓝牙连接,手机靠近车辆即可解锁并自动启车门</Text>
        <Text className='popup-desc'>授权并打开蓝牙连接,手机靠近车辆即可解锁并自动开启车门</Text>
        <View className='popup-confirm-btn' onClick={handleConfirm}>
          知道了
        </View>
src/robotaxi/pages/begin-route/constants/confirm-ride-report.ts
@@ -27,7 +27,9 @@
/** 预估金额币种。 */
export const CONFIRM_RIDE_FARE_CURRENCY = 'CNY';
/** 靠边状态空字符串。 */
export const CONFIRM_RIDE_EMPTY_CURB_STATUS = '';
export const CONFIRM_RIDE_EMPTY_CURB_STATUS = false;
/** 偏移距离,单位米。 */
export const CONFIRM_RIDE_DEVIATION_METERS = '';
/** 确认行程页站点埋点结构。 */
export type ConfirmRideSitePayload = {
@@ -110,7 +112,12 @@
};
/** 等待接驾取消成功埋点结构。 */
export type WaitingForPickupCancelReportPayload = WaitingForPickupReportPayload;
export type WaitingForPickupCancelReportPayload = WaitingForPickupReportPayload & {
  /** 从下单成功到取消成功的等待时长,单位秒。 */
  wait_duration_sec: number;
  /** 取消原因。 */
  cancel_reason: string;
};
/** 到达上车点后取消成功埋点结构。 */
export type PickupArrivedCancelTripReportPayload = {
@@ -118,18 +125,30 @@
  pickup: ConfirmRideSitePayload;
  /** 下车点。 */
  dropoff: ConfirmRideSitePayload;
  /** 车辆信息。 */
  vehicle_info: ConfirmRideVehicleInfoPayload;
  /** 预估信息。 */
  estimates: ConfirmRideEstimatesPayload;
  /** 停车信息。 */
  park_info: TripCompleteParkInfoPayload;
  /** 取消原因。 */
  cancel_reason: string;
};
/** 行程完成停车信息结构。 */
export type TripCompleteParkInfoPayload = {
  /** 上车点是否靠边。 */
  pickup_close_to_curb: string;
  pickup_close_to_curb: boolean;
  /** 下车点是否靠边。 */
  dropoff_close_to_curb: string;
  dropoff_close_to_curb: boolean;
  /** 停车纬度。 */
  lat: number;
  /** 停车经度。 */
  lng: number;
  /** 上车点偏移距离,单位米。 */
  pickup_deviation_meters?: string;
  /** 下车点偏移距离,单位米。 */
  dropoff_deviation_meters?: string;
};
/** 行程完成事实信息结构。 */
@@ -411,9 +430,15 @@
  vehicleInfo: any;
  /** 车辆位置。 */
  vehicleLocation?: { latitude?: number | null; longitude?: number | null } | null;
}): WaitingForPickupCancelReportPayload => (
  createWaitingForPickupReportPayload(params)
);
  /** 等待时长,单位秒。 */
  waitDurationSeconds?: number | null;
  /** 取消原因。 */
  cancelReason?: string;
}): WaitingForPickupCancelReportPayload => ({
  ...createWaitingForPickupReportPayload(params),
  wait_duration_sec: normalizeConfirmRideNumber(params.waitDurationSeconds),
  cancel_reason: String(params.cancelReason || '')
});
/** 生成到达上车点后取消成功埋点参数。 */
export const createPickupArrivedCancelTripReportPayload = (params: {
@@ -421,9 +446,35 @@
  startSite: any;
  /** 下车点。 */
  endSite: any;
  /** 车辆信息。 */
  vehicleInfo: any;
  /** 车辆位置。 */
  vehicleLocation?: { latitude?: number | null; longitude?: number | null } | null;
  /** 费用信息。 */
  fareEstimate: any;
  /** 行程时长,单位秒。 */
  routeDurationSeconds?: number | null;
  /** 行程距离,单位米。 */
  routeDistanceMeters?: number | null;
  /** 取消原因。 */
  cancelReason?: string;
}): PickupArrivedCancelTripReportPayload => ({
  pickup: createConfirmRideSitePayload(params.startSite, 'start'),
  dropoff: createConfirmRideSitePayload(params.endSite, 'end')
  dropoff: createConfirmRideSitePayload(params.endSite, 'end'),
  vehicle_info: createConfirmRideVehicleInfoPayload({
    vehicleInfo: params.vehicleInfo,
    vehicleLocation: params.vehicleLocation
  }),
  estimates: createConfirmRideEstimatesPayload({
    fareEstimate: params.fareEstimate,
    routeDurationSeconds: params.routeDurationSeconds,
    routeDistanceMeters: params.routeDistanceMeters
  }),
  park_info: createTripCompleteParkInfoPayload({
    vehicleLocation: params.vehicleLocation,
    endSite: params.endSite
  }),
  cancel_reason: String(params.cancelReason || '')
});
/** 生成行程完成停车信息。 */
@@ -433,6 +484,8 @@
  /** 下车点。 */
  endSite?: any;
}): TripCompleteParkInfoPayload => ({
  pickup_deviation_meters: CONFIRM_RIDE_DEVIATION_METERS,
  dropoff_deviation_meters: CONFIRM_RIDE_DEVIATION_METERS,
  pickup_close_to_curb: CONFIRM_RIDE_EMPTY_CURB_STATUS,
  dropoff_close_to_curb: CONFIRM_RIDE_EMPTY_CURB_STATUS,
  lat: normalizeConfirmRideNumber(
src/robotaxi/pages/begin-route/constants/report-events.ts
@@ -91,6 +91,12 @@
/** 等待接驾阶段取消成功埋点事件。 */
export const WAITING_FOR_PICKUP_CANCEL_BUTTON_EVENT_ID = 'RT_xcx_Click_WaitingFoPickup_Cancel_Button';
/** 行程中页面浏览埋点事件。 */
export const TRIP_IN_PROGRESS_PAGE_VIEW_EVENT_ID = 'RT_xcx_PageView_TripInProgress';
/** 车辆到达上车点页面浏览埋点事件。 */
export const PICKUP_ARRIVED_PAGE_VIEW_EVENT_ID = 'RT_xcx_PageView_PickupArrived';
/** 到达上车点后取消成功埋点事件。 */
export const PICKUP_ARRIVED_CANCEL_TRIP_BUTTON_EVENT_ID = 'RT_xcx_Click_PickupArrived_CancelTrip_Button';
src/robotaxi/pages/begin-route/hooks/use-order-cancel.ts
@@ -93,7 +93,9 @@
      routeDurationSeconds: deps.routeDuration,
      routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters,
      vehicleInfo: deps.vehicleInfo,
      vehicleLocation: deps.vehicleLocation
      vehicleLocation: deps.vehicleLocation,
      waitDurationSeconds: getWaitingDurationSeconds(),
      cancelReason: EMPTY_CANCEL_REASON
    }));
  };
@@ -103,7 +105,13 @@
  const reportPickupArrivedCancelTripEvent = () => {
    reportEvent(PICKUP_ARRIVED_CANCEL_TRIP_BUTTON_EVENT_ID, createPickupArrivedCancelTripReportPayload({
      startSite: deps.startSite,
      endSite: deps.endSite
      endSite: deps.endSite,
      fareEstimate: deps.fareEstimate,
      routeDurationSeconds: deps.routeDuration,
      routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters,
      vehicleInfo: deps.vehicleInfo,
      vehicleLocation: deps.vehicleLocation,
      cancelReason: EMPTY_CANCEL_REASON
    }));
  };
src/robotaxi/pages/begin-route/hooks/use-web-socket-events.ts
@@ -14,6 +14,8 @@
export type UseWebSocketEventsDeps = {
    tripStatusRef: React.MutableRefObject<TripStatus>;
    pickupArrivedReportedOrderIdRef: React.MutableRefObject<string>;
    tripInProgressReportedOrderIdRef: React.MutableRefObject<string>;
    arrivedDestinationReportedOrderIdRef: React.MutableRefObject<string>;
    pendingCancelContextRef: React.MutableRefObject<PendingCancelContext | null>;
    modifyDestinationPickupSuccessAtRef: React.MutableRefObject<number>;
src/robotaxi/pages/begin-route/hooks/web-socket-handlers/create-order-status-socket-handler.ts
@@ -4,7 +4,7 @@
import { clearOrderTripStorage, reportEvent } from '../../../../../utils/Robotaxi-public-method';
import { getOrderId } from '../../../../utils/order';
import { TripStatus, toTripStatus } from '../../constants/trip-status';
import { ARRIVED_DROPOFF_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_COMPLETE_TRIP_BUTTON_EVENT_ID } from '../../constants/report-events';
import { ARRIVED_DROPOFF_PAGE_VIEW_EVENT_ID, PICKUP_ARRIVED_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_PAGE_VIEW_EVENT_ID, TRIP_IN_PROGRESS_COMPLETE_TRIP_BUTTON_EVENT_ID } from '../../constants/report-events';
import { createTripInProgressCompleteTripReportPayload } from '../../constants/confirm-ride-report';
import { clearPassedRouteCache } from '../passed-route-cache';
import { PENDING_CANCEL_CONTEXT_TTL_MS, type PendingCancelContext } from '../use-order-actions.shared';
@@ -184,6 +184,86 @@
};
/**
 * 行程中(状态为 6)上报页面浏览埋点。
 */
const reportTripInProgressEventIfNeeded = (
  mappedTripStatus: TripStatus | null,
  changeStatusOld: TripStatus,
  effectiveOrderId: string,
  deps: UseWebSocketEventsDeps
) => {
  const shouldReportTripInProgress = (
    mappedTripStatus === TripStatus.InService
    && changeStatusOld !== TripStatus.InService
    && Boolean(effectiveOrderId)
  );
  if (!shouldReportTripInProgress) {
    return;
  }
  if (deps.tripInProgressReportedOrderIdRef.current === effectiveOrderId) {
    return;
  }
  deps.tripInProgressReportedOrderIdRef.current = effectiveOrderId;
  getOrderDetail(effectiveOrderId)
    .then((orderDetail: any) => {
      const payload = createTripInProgressCompleteTripReportPayload({
        startSite: deps.startSite,
        endSite: deps.endSite,
        vehicleInfo: deps.vehicleInfo,
        vehicleLocation: deps.vehicleLocation,
        fareEstimate: deps.fareEstimate,
        routeDurationSeconds: deps.routeDuration,
        routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters,
        orderDetail
      });
      reportEvent(TRIP_IN_PROGRESS_PAGE_VIEW_EVENT_ID, payload, 'pageView');
    })
    .catch(() => {
      deps.tripInProgressReportedOrderIdRef.current = '';
    });
};
/**
 * 车辆到达上车点后上报页面浏览埋点。
 */
const reportPickupArrivedEventIfNeeded = (
  mappedTripStatus: TripStatus | null,
  changeStatusOld: TripStatus,
  effectiveOrderId: string,
  deps: UseWebSocketEventsDeps
) => {
  const shouldReportPickupArrived = (
    mappedTripStatus === TripStatus.VehicleArrivedPickup
    && changeStatusOld !== TripStatus.VehicleArrivedPickup
    && Boolean(effectiveOrderId)
  );
  if (!shouldReportPickupArrived) {
    return;
  }
  if (deps.pickupArrivedReportedOrderIdRef.current === effectiveOrderId) {
    return;
  }
  deps.pickupArrivedReportedOrderIdRef.current = effectiveOrderId;
  getOrderDetail(effectiveOrderId)
    .then((orderDetail: any) => {
      const payload = createTripInProgressCompleteTripReportPayload({
        startSite: deps.startSite,
        endSite: deps.endSite,
        vehicleInfo: deps.vehicleInfo,
        vehicleLocation: deps.vehicleLocation,
        fareEstimate: deps.fareEstimate,
        routeDurationSeconds: deps.routeDuration,
        routeDistanceMeters: deps.orderRouteMetrics?.distanceMeters,
        orderDetail
      });
      reportEvent(PICKUP_ARRIVED_PAGE_VIEW_EVENT_ID, payload, 'pageView');
    })
    .catch(() => {
      deps.pickupArrivedReportedOrderIdRef.current = '';
    });
};
/**
 * 到达目的地后上报完成行程埋点。
 */
const reportTripCompleteEventIfNeeded = (
@@ -242,6 +322,8 @@
  const mappedTripStatus = syncTripStatus(payload, currentDeps);
  const effectiveOrderId = resolveEffectiveOrderId(payload, currentOrderId);
  syncVehicleInfo(effectiveOrderId, currentDeps, lastFetchedVehicleOrderIdRef);
  reportPickupArrivedEventIfNeeded(mappedTripStatus, changeStatusOld, effectiveOrderId, currentDeps);
  reportTripInProgressEventIfNeeded(mappedTripStatus, changeStatusOld, effectiveOrderId, currentDeps);
  reportTripCompleteEventIfNeeded(mappedTripStatus, changeStatusOld, effectiveOrderId, currentDeps);
  handleStatusSideEffects(mappedTripStatus, changeStatusOld, currentDeps);
  handleCancelPopupPush(payload, changeStatusOld, currentDeps, pendingCancelContext);
src/robotaxi/pages/begin-route/index.tsx
@@ -212,6 +212,8 @@
  const lastSocketDirectionAtRef = useRef(0); // 最近一次由 socket 写入车辆朝向的时间戳
  const beforeBackRef = useRef<() => void>(() => { }); // 左上角返回前的兜底清理,避免预览路线残留到下次进页
  const pendingCancelContextRef = useRef<PendingCancelContext | null>(null); // 待确认取消上下文,避免本地状态重置污染后续 socket 取消弹窗类型判断
  const pickupArrivedReportedOrderIdRef = useRef(''); // 到达上车点页面浏览埋点去重
  const tripInProgressReportedOrderIdRef = useRef(''); // 行程中页面浏览埋点去重
  const arrivedDestinationReportedOrderIdRef = useRef(''); // 到达目的地完成行程埋点去重,避免同一订单重复 socket 上报
  const lastPreviewPageViewDidShowVersionRef = useRef(0); // 确认行程预览态页面浏览埋点最近一次上报的页面展示版本
  const lastWaitingForDispatchPageViewOrderIdRef = useRef(''); // 等待派单页页面浏览埋点最近一次上报的订单 ID
@@ -888,6 +890,8 @@
  const { bindSocketListeners, ensureSocketListeners } = useWebSocketEvents({
    tripStatusRef,
    pickupArrivedReportedOrderIdRef,
    tripInProgressReportedOrderIdRef,
    arrivedDestinationReportedOrderIdRef,
    pendingCancelContextRef,
    startSite,
src/robotaxi/pages/select-map-site/hooks/use-select-map-site.ts
@@ -83,11 +83,13 @@
      return;
    }
    reportEvent(eventId, {
      has_nearby_stations_shown: state.nearbySites.length > 0 ? 1 : 0,
      has_nearby_stations_shown: state.nearbySites.length > 0 ? true : false,
      nearby_station_count: Number(state.nearbySites.length || 0),
      confirmed_pickup: {
        station: createConfirmedPickupStationPayload(),
        type: state.pickupConfirmTypeRef.current
        type: state.pickupConfirmTypeRef.current,
        lat: Number(state.selectedSite?.latitude || state.selectedSite?.lat || 0),
        lng: Number(state.selectedSite?.longitude || state.selectedSite?.lng || 0)
      },
      map_zoom_level: Number(state.mapScale || 0),
      pin_loc: {
src/robotaxi/pages/site-search/index.tsx
@@ -334,7 +334,7 @@
    const selectedCity = Taro.getStorageSync('selectedCity');
    reportEvent(eventId, {
      search_selected_station: {
        query_key: String(searchValue || '').trim(),
        query_keyword: String(searchValue || '').trim(),
        list_idx: Number(listIndex),
        total_results: Number(totalResults),
        station: createReportStationPayload(item, String(selectedCity?.name || ''))
src/utils/Robotaxi-public-method.ts
@@ -11,6 +11,7 @@
  GEOFENCE_TYPE_OPERATING,
  GEOFENCE_TYPE_STATION
} from '../robotaxi/constants/geofence';
import { APPID } from '../constants/app'
/**
 * 安全的 JSON 解析函数
@@ -21,7 +22,7 @@
  if (typeof str !== 'string') {
    return str || defaultVal;
  }
  const trimmed = str.trim();
  // 简单的格式预判:JSON 对象或数组字符串通常以 { 或 [ 开头
  // 如果不是这两种开头,大概率不是我们需要解析的 JSON,直接返回原值或默认值,避免引发 JSON.parse 的异常和错误日志
@@ -1242,10 +1243,12 @@
        type,  // 埋点操作类型
        user_id: uid || '-1',  // 用户ID
        masked_device_id: openId,  // 设备ID
        device_id: openId,  // 设备ID
        event_time: Date.now(),  // 事件时间
        city: Taro.getStorageSync('selectedCity').name || '',  // 城市
        orderId: Taro.getStorageSync('orderId') || '',  // 订单ID
        os: Taro.getSystemInfoSync().system || '',  // 操作系统
        app_id: APPID,  // 微信小程序appid
        app_ver: appVersion,  // 微信小程序版本
        session_id: '',  // 会话ID
      },