hejianhao
2026-06-01 61b6c47e19b2452cddbca54b7844790a5ef4ee16
refactor(robotaxi): 集中管理埋点事件常量并完善上报逻辑

新建埋点事件ID常量文件,统一管理所有埋点事件标识
替换多模块内硬编码的埋点事件ID为导入的常量
新增toUnixSeconds工具函数用于安全转换Unix秒时间戳
为站点搜索页新增地图选点和历史上车点点击的埋点上报逻辑
1个文件已添加
5个文件已修改
186 ■■■■■ 已修改文件
src/pages/robotaxi/home/components/privacy-popup/index.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-navigation.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/constants/report-events.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-order-submit.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/site-search/index.tsx 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Robotaxi-public-method.ts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/components/privacy-popup/index.tsx
@@ -6,6 +6,7 @@
import { submitAgreement } from '../../../../../robotaxi/api/home'
import { getNormalizedLoginAgreementList, type LoginAgreementItem } from '../../../../../robotaxi/utils/login-agreement'
import { reportEvent } from '../../../../../utils/Robotaxi-public-method'
import { PROTOCOL_CLICK_EVENT_ID } from '../../../../../robotaxi/pages/begin-route/constants/report-events'
type Props = {
  show?: boolean;
@@ -78,7 +79,7 @@
  }, [isAgree]);
  const handleClose = () => {
    reportEvent('RT_xcx_protocol_click', {
    reportEvent(PROTOCOL_CLICK_EVENT_ID, {
      status_code: 0
    })
    privicyShowManager.setHasShow(false)
@@ -97,7 +98,7 @@
        return Number.isNaN(idNumber) ? key : idNumber;
      });
    submitAgreement({ type: selectedType }).then((res: any) => {
      reportEvent('RT_xcx_protocol_click', {
      reportEvent(PROTOCOL_CLICK_EVENT_ID, {
        status_code: 1
      })
      privicyShowManager.setHasShow(true)
src/pages/robotaxi/home/hooks/use-navigation.ts
@@ -10,6 +10,11 @@
import { jumpTo } from '../../../../utils/jump';
import { getGeofence } from '../../../../robotaxi/api/home';
import { GeofenceType } from '../../../../robotaxi/constants/geofence';
import {
  RIDE_HOME_EDIT_DROPOFF_EVENT_ID,
  RIDE_HOME_EDIT_PICKUP_EVENT_ID,
  SEARCH_CLICK_EVENT_ID
} from '../../../../robotaxi/pages/begin-route/constants/report-events';
import { resolveHomeStartSiteDisplaySite } from './use-home-start-site-display';
type Deps = {
@@ -60,12 +65,6 @@
  queryPointerByCenter: (lat: number, lng: number) => Promise<void>;
};
/** 修改上车点按钮埋点事件标识。 */
const EDIT_PICKUP_EVENT_ID = 'RT_xcx_Click_RideHome_EditPickup_Button';
/** 修改下车点按钮埋点事件标识。 */
const EDIT_DROPOFF_EVENT_ID = 'RT_xcx_Click_RideHome_EditDropoff_Button';
/**
 * 首页导航与页面操作编排。
 */
@@ -109,7 +108,7 @@
   * 上报首页修改上下车点埋点。
   */
  const reportEditStationEvent = (type: string) => {
    const eventId = type === 'end' ? EDIT_DROPOFF_EVENT_ID : EDIT_PICKUP_EVENT_ID;
    const eventId = type === 'end' ? RIDE_HOME_EDIT_DROPOFF_EVENT_ID : RIDE_HOME_EDIT_PICKUP_EVENT_ID;
    reportEvent(eventId, {
      user_cur_loc: createUserCurrentLocationPayload(),
      recommended_pickup_station: createRecommendedPickupStationPayload(),
@@ -189,7 +188,7 @@
      reportEditStationEvent(type);
      Taro.navigateTo({
        url: '/robotaxi/pages/select-map-site/index?pageType=' + type,
        success: () => reportEvent('RT_xcx_search_click', { element_type: 'start' })
        success: () => reportEvent(SEARCH_CLICK_EVENT_ID, { element_type: 'start' })
      });
      return;
    }
@@ -205,7 +204,7 @@
      Taro.navigateTo({
        url: `/robotaxi/pages/site-search/index?pageType=${type}&electronicAreaId=${effectiveStartSite.electronicAreaId}&longitude=${effectiveStartSite.longitude}&latitude=${effectiveStartSite.latitude}`,
        success: () => {
          reportEvent('RT_xcx_search_click', { element_type: type == 'start' ? 'start' : 'dest' });
          reportEvent(SEARCH_CLICK_EVENT_ID, { element_type: type == 'start' ? 'start' : 'dest' });
        }
      });
      return;
@@ -214,7 +213,7 @@
    Taro.navigateTo({
      url: `/robotaxi/pages/site-search/index?pageType=${type}&electronicAreaId=${deps.electronicAreaId}&longitude=${deps.currentSite.longitude}&latitude=${deps.currentSite.latitude}`,
      success: () => {
        reportEvent('RT_xcx_search_click', { element_type: type == 'start' ? 'start' : 'dest' });
        reportEvent(SEARCH_CLICK_EVENT_ID, { element_type: type == 'start' ? 'start' : 'dest' });
      }
    });
  };
src/robotaxi/pages/begin-route/constants/report-events.ts
New file
@@ -0,0 +1,26 @@
/** 协议弹窗点击埋点事件。 */
export const PROTOCOL_CLICK_EVENT_ID = 'RT_xcx_protocol_click';
/** 首页修改上车点点击埋点事件。 */
export const RIDE_HOME_EDIT_PICKUP_EVENT_ID = 'RT_xcx_Click_RideHome_EditPickup_Button';
/** 首页修改下车点点击埋点事件。 */
export const RIDE_HOME_EDIT_DROPOFF_EVENT_ID = 'RT_xcx_Click_RideHome_EditDropoff_Button';
/** 首页搜索入口点击埋点事件。 */
export const SEARCH_CLICK_EVENT_ID = 'RT_xcx_search_click';
/** 下单提交埋点事件。 */
export const ORDER_SUBMIT_EVENT_ID = 'RT_xcx_order_submit';
/** 支付发起埋点事件。 */
export const PAY_START_EVENT_ID = 'RT_xcx_pay_start';
/** 支付结果埋点事件。 */
export const PAY_RESULT_EVENT_ID = 'RT_xcx_pay_result';
/** 站点搜索页地图选点点击埋点事件。 */
export const SELECT_PICKUP_LOCATION_PIN_PICKUP_LOCATION_BUTTON_EVENT_ID = 'RT_xcx_Click_SelectPickupLocation_PinPickupLocation_Button';
/** 站点搜索页历史上车点点击埋点事件。 */
export const SELECT_PICKUP_LOCATION_HISTORY_PICKUP_ITEM_EVENT_ID = 'RT_xcx_Click_SelectPickupLocation_HistoryPickup_Item';
src/robotaxi/pages/begin-route/hooks/use-order-submit.ts
@@ -7,6 +7,11 @@
import { requestOrderSuccessSubscribeOnTap, subscribeAfterOrderSuccess } from '../../../../utils/subscribe';
import { TripStatus } from '../constants/trip-status';
import {
  ORDER_SUBMIT_EVENT_ID,
  PAY_RESULT_EVENT_ID,
  PAY_START_EVENT_ID
} from '../constants/report-events';
import {
  HOME_PAGE_URL,
  HOME_RELAUNCH_DELAY_MS,
  LOGIN_PAGE_URL,
@@ -39,12 +44,6 @@
/** 协议接口约定:true=已同意,false=未同意。 */
const resolveAgreementStatus = (result: any) => result === true;
/** 下单发起埋点事件标识。 */
const PAY_START_EVENT_ID = 'RT_xcx_pay_start';
/** 下单结果埋点事件标识。 */
const PAY_RESULT_EVENT_ID = 'RT_xcx_pay_result';
/** 下单结果埋点状态。 */
type PayResultStatus =
@@ -161,7 +160,7 @@
    const response = await saveOrder(payload);
    const statusCode = response?.orderId ? 1 : 0;
    reportEvent('RT_xcx_order_submit', {
    reportEvent(ORDER_SUBMIT_EVENT_ID, {
      total_amount: deps.fareEstimate.originalFare,
      payable_amount: deps.fareEstimate.finalFare,
      coupon_id: cachedCouponId,
src/robotaxi/pages/site-search/index.tsx
@@ -2,7 +2,7 @@
import { useState, useEffect, useRef } from 'react';
import { View, Text, Image, Input } from '@tarojs/components';
import './index.less';
import { getDistance } from '../../../utils/Robotaxi-public-method';
import { getDistance, reportEvent, toUnixSeconds } from '../../../utils/Robotaxi-public-method';
import { getOrderDistance } from '../../api/select-map-site';
import { checkOtp } from '../../../utils/tools';
import TipPopup from '../select-map-site/components/tip-popup';
@@ -21,6 +21,10 @@
  MODIFY_DESTINATION_SKIP_AC_QUERY_ON_RETURN_KEY,
  MODIFY_DESTINATION_SESSION_KEY
} from '../begin-route/constants/modify-destination';
import {
  SELECT_PICKUP_LOCATION_HISTORY_PICKUP_ITEM_EVENT_ID,
  SELECT_PICKUP_LOCATION_PIN_PICKUP_LOCATION_BUTTON_EVENT_ID
} from '../begin-route/constants/report-events';
import {
  HOME_START_SITE_SOURCE_MANUAL_SELECTED,
  writeHomeStartSiteDisplayState
@@ -173,8 +177,85 @@
    setShowAllRecommend(!showAllRecommend)
  }
  /**
   * 构造地图选点埋点中的用户当前位置字段。
   */
  const createUserCurrentLocationPayload = async () => {
    const selectedCity = Taro.getStorageSync('selectedCity');
    try {
      const location = await Taro.getLocation({ type: 'gcj02' });
      return {
        lat: Number(location?.latitude || router.params?.latitude || 0),
        lng: Number(location?.longitude || router.params?.longitude || 0),
        poi_id: '',
        poi_name: '',
        city: String(selectedCity?.name || '')
      };
    } catch (error) {
      return {
        lat: Number(router.params?.latitude || 0),
        lng: Number(router.params?.longitude || 0),
        poi_id: '',
        poi_name: '',
        city: String(selectedCity?.name || '')
      };
    }
  };
  const toSelectSite = () => {
  /**
   * 点击地图选点时上报埋点。
   */
  const reportSelectPickupLocationEvent = async () => {
    const userCurrentLocation = await createUserCurrentLocationPayload();
    reportEvent(SELECT_PICKUP_LOCATION_PIN_PICKUP_LOCATION_BUTTON_EVENT_ID, {
      user_cur_loc: userCurrentLocation,
      nearby_pickup_stations: Array.isArray(nearbySiteList) ? nearbySiteList : []
    });
  };
  /**
   * 点击历史记录站点时上报埋点。
   */
  const reportHistoryPickupSelectEvent = (item: any, listIndex: number, totalResults: number) => {
    if (pageType !== 'start') {
      return;
    }
    const selectedCity = Taro.getStorageSync('selectedCity');
    const historyPickupStationPayload = {
      id: String(item?.id || item?.pointId || ''),
      saved_at_timestamp: toUnixSeconds(
        item?.saved_at_timestamp
        ?? item?.savedAtTimestamp
        ?? item?.savedAt
        ?? item?.updateTime
        ?? item?.createTime
        ?? item?.createdAt
      ),
      usage_cnt: Number(
        item?.usage_cnt
        ?? item?.usageCount
        ?? item?.useCount
        ?? item?.count
        ?? 0
      ),
      list_idx: Number(listIndex),
      total_results: Number(totalResults),
      station: {
        poi_id: String(item?.id || item?.pointId || ''),
        poi_name: String(item?.name || ''),
        distance: Number.isFinite(Number(item?.distanceRaw))
          ? Number(item?.distanceRaw)
          : Number(item?.distance || 0),
        city: String(item?.city || selectedCity?.name || '')
      }
    };
    reportEvent(SELECT_PICKUP_LOCATION_HISTORY_PICKUP_ITEM_EVENT_ID, {
      histroy_pickup_stations: historyPickupStationPayload
    });
  };
  const toSelectSite = async () => {
    await reportSelectPickupLocationEvent();
    Taro.navigateTo({
      url: '/robotaxi/pages/select-map-site/index?pageType=' + pageType,
    })
@@ -277,7 +358,7 @@
  const renderSiteItems = (
    list: any[],
    onSelect: (item: any) => void,
    onSelect: (item: any, index?: number) => void,
    showDistance = true,
    showStationChildren = false
  ) => { // 复用列表项渲染
@@ -290,7 +371,7 @@
        starSrc='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-star.png'
        noStarSrc='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/no-star.png'
        showDistance={showDistance}
        onSelect={onSelect}
        onSelect={(selectedItem: any) => onSelect(selectedItem, index)}
        renderHighlightedText={renderHighlightedText}
        showStationChildren={showStationChildren}
      />
@@ -356,7 +437,10 @@
          <View className='card-header'>历史记录</View>
          <View className='card-content list-content' id='history-card-content'>
            {
              renderSiteItems(visibleHistory, (item) => onChangeSelectSite(item), true, true)
              renderSiteItems(visibleHistory, (item, index = 0) => {
                reportHistoryPickupSelectEvent(item, index, visibleHistory.length);
                onChangeSelectSite(item);
              }, true, true)
            }
          </View>
          {sortedHistorySite.length > MAX_SHOW_COUNT && (
src/utils/Robotaxi-public-method.ts
@@ -1095,6 +1095,12 @@
/** 来源页路径兜底值。 */
const DEFAULT_REFERRER_PATH = '';
/** Unix 秒时间戳的毫秒阈值。 */
const UNIX_SECONDS_MILLISECOND_THRESHOLD = 1000000000000;
/** 毫秒转秒的换算系数。 */
const MILLISECONDS_PER_SECOND = 1000;
/** 标准化埋点页面路径。 */
const normalizeReportPagePath = (route: any, fallbackPath: string) => {
  const normalizedRoute = String(route || '').trim();
@@ -1115,6 +1121,29 @@
  };
};
/** 将任意时间字段安全转换为 Unix 秒时间戳。 */
export const toUnixSeconds = (value: any) => {
  if (typeof value === 'number' && Number.isFinite(value)) {
    return value > UNIX_SECONDS_MILLISECOND_THRESHOLD
      ? Math.floor(value / MILLISECONDS_PER_SECOND)
      : Math.floor(value);
  }
  if (typeof value === 'string') {
    const normalizedValue = value.trim();
    const numericValue = Number(normalizedValue);
    if (normalizedValue && Number.isFinite(numericValue)) {
      return numericValue > UNIX_SECONDS_MILLISECOND_THRESHOLD
        ? Math.floor(numericValue / MILLISECONDS_PER_SECOND)
        : Math.floor(numericValue);
    }
    const parsedTime = Date.parse(value);
    if (!Number.isNaN(parsedTime)) {
      return Math.floor(parsedTime / MILLISECONDS_PER_SECOND);
    }
  }
  return 0;
};
/** 判断埋点字段是否为需要自动 JSON 化的结构化数据。 */
const isStructuredReportValue = (value: any) => {
  if (Array.isArray(value)) {