فهرست منبع

feat(KIP-17900): 埋点初始化

john 9 ماه پیش
والد
کامیت
1845fd3f71

+ 1 - 1
.prettierrc.js

@@ -1,6 +1,6 @@
 module.exports = {
   singleQuote: true,
-  printWidth: 280,
+  printWidth: 80,
   arrowParens: 'always',
   tabWidth: 2,
 };

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "temporay-parking",
-  "version": "3.19.0",
+  "version": "3.20.0",
   "private": true,
   "scripts": {
     "serve": "cross-env NODE_ENV=dev vue-cli-service serve",

+ 10 - 0
src/environment/index.js

@@ -6,6 +6,8 @@ export function devInit() {
   window.wechatOfficialAccountId = 'wxb81a622ed6d60adf';
   window.QR_CODE_BASE_URL = 'https://pd.kerryplus.com';
   window.QUESTIONNAIRE_BASE_URL = 'https://dev-questionnaire.kerryonvip.com';
+  window.trackingBaseUrl = 'https://tq.kerryprops.com.cn/sa.gif';
+  window.trackingRemarkPre = 'dev';
 }
 
 export function qaInit() {
@@ -16,6 +18,8 @@ export function qaInit() {
   window.wechatOfficialAccountId = 'wxb81a622ed6d60adf';
   window.QR_CODE_BASE_URL = 'https://pq.kerryplus.com';
   window.QUESTIONNAIRE_BASE_URL = 'https://qa-questionnaire.kerryplus.com';
+  window.trackingBaseUrl = 'https://tq.kerryprops.com.cn/sa.gif';
+  window.trackingRemarkPre = 'qa';
 }
 
 export function slInit() {
@@ -26,6 +30,8 @@ export function slInit() {
   window.wechatOfficialAccountId = 'wxb150c7d193e8662d';
   window.QR_CODE_BASE_URL = 'https://p.kerryplus.com';
   window.QUESTIONNAIRE_BASE_URL = 'https://sl-questionnaire.kerryplus.com';
+  window.trackingBaseUrl = 'https://sl-t.kerryplus.com/sa.gif';
+  window.trackingRemarkPre = 'sl';
 }
 
 export function ltInit() {
@@ -36,6 +42,8 @@ export function ltInit() {
   window.wechatOfficialAccountId = 'wx2bd99ca94d6acd7e';
   window.QR_CODE_BASE_URL = 'https://p.kerryplus.com';
   window.QUESTIONNAIRE_BASE_URL = 'https://lt-questionnaire.kerryplus.com';
+  window.trackingBaseUrl = 'https://tq.kerryprops.com.cn/sa.gif';
+  window.trackingRemarkPre = 'lt';
 }
 
 export function prodInit() {
@@ -46,4 +54,6 @@ export function prodInit() {
   window.wechatOfficialAccountId = 'wxb150c7d193e8662d';
   window.QR_CODE_BASE_URL = 'https://p.kerryplus.com';
   window.QUESTIONNAIRE_BASE_URL = 'https://questionnaire.kerryplus.com';
+  window.trackingBaseUrl = 'https://t.kerryplus.com/sa.gif';
+  window.trackingRemarkPre = 'online';
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
src/lib/Sensorsdata-UniPlugin-JS/sensorsdata.es6.min.js


+ 8 - 0
src/plugins/AppInit.js

@@ -25,6 +25,9 @@ import {
   ETCP_APP_INVOICE_PATH,
 } from '@/constants.js';
 
+/* 埋点 */
+import { TrackFactory, trackingInit, getUrlParams } from '@/utils';
+
 export default {
   install(app) {
     app.config.globalProperties.$md5 = md5;
@@ -69,5 +72,10 @@ export default {
     app.config.globalProperties.isAlipayClient = window.isAlipayClient;
     app.config.globalProperties.$theme = theme;
     // window.wx = wx
+    // 埋点参数设置及公共字段初始化
+    const webViewParams = getUrlParams(location.search);
+    // 埋点参数设置及公共字段初始化
+    trackingInit(webViewParams);
+    app.config.globalProperties.trackFactory = TrackFactory;
   }
 };

+ 52 - 1
src/store/index.js

@@ -62,6 +62,25 @@ const store = createStore({
       refreshPageKey: 1,
       version: 360, // h5版本
       appVersion: 100, // 小程序版本
+      /* 埋点相关 */
+      initTracking: false,
+      vipCode: '',
+      kipUserId: '',
+      sessionId: '',
+      format: 'RETAIL',
+      runtime: 'MP-WEIXIN-WEBVIEW',
+      brandName: '',
+      applicationType: '',
+      profileId: '',
+      unionId: '',
+      distinctId: '',
+      building_unit: '',
+      company: '',
+      position: '',
+      is_auth_mobile: 0,
+      is_auth_identity: 0,
+      initTracking: false,
+      /* 埋点 */
     }
   },
   mutations: {
@@ -189,7 +208,39 @@ const store = createStore({
     },
     set_APP_VERSION(state, payload) {
       state.appVersion = payload;
-    }
+    },
+    // by https://git.kerryprops.com.cn/tron/monthly-parking-frontend/pull/128
+    SET_INIT_TRACKING(state, payload) {
+      state.initTracking = payload;
+    },
+    SET_GLOBAL_CONFIG(state, config) {
+      if (config.runtime) {
+        state.runtime = config.runtime;
+      }
+      /* k+ */
+      state.appId = config.appId ? config.appId : '';
+      state.vipCode = config.vipCode;
+      state.kipUserId = config.kipUserId;
+      state.openId = config.openId ? config.openId : '';
+      state.sessionId = config.sessionId;
+      state.format = config.format;
+      state.lbsId = config.mallId;
+      state.brandId = config.groupId;
+      state.lbsName = config.lbsName;
+      state.projectId = config.projectId;
+      state.themeName = config.themeName;
+      state.applicationType = config.applicationType;
+      state.brandName = config?.groupName ?? '';
+      state.profileId = config?.profileId ?? '';
+      state.unionId = config?.unionId ?? '';
+      state.building_unit = config?.building_unit ?? '';
+      state.company = config?.company ?? '';
+      state.position = config?.position ?? '';
+      state.is_auth_mobile = config?.is_auth_mobile ?? 0;
+      state.is_auth_identity = config?.is_auth_identity ?? 0;
+      state.distinctId = config?.distinctId ?? '';
+      /* CRM */
+    },
   },
   actions: {
     async baseInit({ commit, dispatch }, { options, callback }) {

+ 2 - 1
src/utils/index.js

@@ -70,4 +70,5 @@ export * from './common/function.js'
 export * from './common/lbsIdCommon.js'
 export * from './common/contactManage.js'
 export * from './common/CrossPlatformManage.js'
-export * from './alipayClient/index.js'
+export * from './alipayClient/index.js'
+export * from './trackingh5.js'

+ 377 - 0
src/utils/trackingh5.js

@@ -0,0 +1,377 @@
+/**
+ * 小程序埋点,接口使用神策SDK sensors
+ */
+
+import sensors from '@/lib/Sensorsdata-UniPlugin-JS/sensorsdata.es6.min.js';
+import store from '@/store';
+import {getGroupIdAndMallIdByLsbId} from '@/utils'
+
+/**
+ * 上传埋点数据至神策(第三方埋点平台)
+ * param eventName   string  埋点事件名
+ * param properites  object  埋点数据 (包含action, current event duration)
+ * param eventEnable boolean 是否允许上传埋点数据,默认为true
+ * param owner       string  埋点负责人,默认为undefined
+ *  */
+const normalTrack = (
+  eventName,
+  properites = {},
+  trackingEnable = true
+) => {
+  if (trackingEnable) {
+    sensors.track(eventName, properites);
+  }
+};
+
+// 设置全埋点事件及埋点公共字段
+const trackingInit = (webViewParams) => {
+  // // 当前H5存在其他直接进入页面的形式,不会携带global对象,不需要初始化
+  // if (!webViewParams?.global) {
+  //   return;
+  // }
+
+  const globalObj = webViewParams;
+  // 如果是非kerry+平台进入的方式,不进行埋点初始化
+  /*if (globalObj?.applicationType !== 'KERRY') {
+    return;
+  }*/
+  
+  /* 设置项目名称 */
+  let project = 'kerryplus';
+  if (/PUDONG|JINGAN/.test(globalObj.source)) {
+    project = 'kerryon'
+  }
+  if (/CRM/.test(globalObj.source)) {
+    project = globalObj.source
+  }
+
+  /* 设置lbsid */
+  let lbsId = globalObj?.lbsId || '';
+  if (!/CRM|PUDONG|JINGAN|ALIPAY/.test(globalObj.source)) {
+    const groupIdAndMallId = getGroupIdAndMallIdByLsbId(globalObj.lbsId);
+    groupId = groupIdAndMallId.groupId;
+    mallId = groupIdAndMallId.mallId;
+    lbsId = mallId
+  }
+
+  // eslint-disable-next-line no-console
+  console.log(
+    'url=',
+    `${window.trackingBaseUrl}?project=${project}&remark=${window.trackingRemarkPre}_${lbsId}`
+  );
+
+  store.commit('SET_INIT_TRACKING', true);
+  store.commit('SET_INIT_TRACKING', true);
+
+
+  store.commit('SET_GLOBAL_CONFIG', globalObj);
+  store.commit('SET_THEME', globalObj);
+
+  
+  
+  //设置全埋点事件和其他参数
+  sensors.init({
+    server_url: `${window.trackingBaseUrl}?project=${project}&remark=${window.trackingRemarkPre}_${lbsId}`,
+    //数据接收地址
+    //其中lbs为用户当前的 环境_lbs。其中生产都报给t remark统一online_lbs
+    //qa和开发环境,统一报给 tq。其中发了qa的,remark使用qa_lbs作为区分。
+    //开发的remark用dev前缀+一个自定义的标签就行。这个主要是方便开发在后续开发自己测试埋点的时候,使用这个remark把自己埋的筛出来。
+    //项目名:嘉里中心小程序报crm,KerryON小程序报kerryon,Kerry+小程序报kerryplus,如果不是从webview拉起,而是浏览器进入H5,报h5service。
+
+    is_track_single_page: true, // 单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发 $pageview 事件
+    use_client_time: true,
+    send_type: 'image', //这个地方与原版不同,没有采用beacon方式。因为不同的移动端对beacon的支持不一致
+    batch_send: {
+      datasend_timeout: 6000,
+      send_interval: 6000,
+      storage_length: 200,
+    },
+    heatmap: {
+      //是否开启点击图,default 表示开启,自动采集 $WebClick 事件,可以设置 'not_collect' 表示关闭。
+      clickmap: 'default',
+      //是否开启触达图,not_collect 表示关闭,不会自动采集 $WebStay 事件,可以设置 'default' 表示开启。
+      scroll_notice_map: 'default',
+    },
+    show_log: true,
+    source_channel: ['tpName', 'sa_utm'],
+    //子配置项 true 表示采集,false 表示不采集,未设置的参数取默认值。
+    preset_properties: {
+      //是否采集 $latest_utm 最近一次广告系列相关参数,默认值 true。
+      latest_utm: true,
+      //是否采集 $latest_traffic_source_type 最近一次流量来源类型,默认值 true。
+      latest_traffic_source_type: true,
+      //是否采集 $latest_search_keyword 最近一次搜索引擎关键字,默认值 true。
+      latest_search_keyword: true,
+      //是否采集 $latest_referrer 最近一次前向地址,默认值 true。
+      latest_referrer: true,
+      //是否采集 $latest_referrer_host 最近一次前向地址,1.14.8 以下版本默认是true,1.14.8 及以上版本默认是 false,需要手动设置为 true 开启。
+      latest_referrer_host: true,
+      //是否采集 $latest_landing_page 最近一次落地页地址,默认值 true。
+      latest_landing_page: true,
+      //是否采集 $url 页面地址作为公共属性,1.16.5 以下版本默认是 false,1.16.5 及以上版本默认是 true。
+      url: true,
+      //是否采集 $title 页面标题作为公共属性,1.16.5 以下版本默认是 false,1.16.5 及以上版本默认是 true。
+      title: true,
+    },
+  });
+  //对于所有事件都需要添加的属性,可在初始化 SDK 前,调用 registerApp() 将属性注册为公共属性
+  if(project === 'kerryplus') {
+    sensors.registerPage({
+      saglobal_product_name: project,
+      saglobal_digital_brand_id: globalObj?.groupId,
+      saglobal_digital_brand_name: globalObj?.groupName,
+      saglobal_lbs_id: globalObj.mallId,
+      saglobal_lbs_name: globalObj.lbsName,
+      saglobal_open_id: globalObj.openId,
+      saglobal_union_id: globalObj.unionId,
+      saglobal_profile_id: globalObj.profileId,
+      saglobal_project: globalObj.projectId,
+      saglobal_building_unit: globalObj.building_unit,
+      saglobal_company: globalObj.company,
+      saglobal_position: globalObj.position,
+      saglobal_is_auth_mobile: globalObj.is_auth_mobile,
+      saglobal_is_auth_identity: globalObj.is_auth_identity,
+    });
+    const distinctID = globalObj.distinctId;
+    sensors.identify(distinctID, true);
+  } else {
+    sensors.registerPage({
+      saglobal_product_name: project,
+      saglobal_digital_brand_id: globalObj?.groupId,
+      saglobal_digital_brand_name: globalObj?.groupName,
+      saglobal_lbs_id: lbsId,
+      saglobal_lbs_name: globalObj.lbsName,
+      saglobal_open_id: globalObj.openId,
+      saglobal_union_id: globalObj.unionId,
+      saglobal_profile_id: globalObj.kipUserId,
+      saglobal_vipcode: globalObj.vipCode,
+    });
+  }
+  // eslint-disable-next-line no-console
+  console.log('sensors', sensors);
+  // eslint-disable-next-line no-console
+  console.log('globalObj', globalObj);
+};
+
+sensors.quick('autoTrack'); //用于采集 $pageview 事件。
+
+class TrackFactory {
+   eventData = {};
+
+  isUpload = true;
+  eventName = '';
+   timeStart = new Date().getTime();
+   apiTimeStart = new Date().getTime();
+   static instanceRecorder = {};
+
+  /**
+   * 获取埋点事件实例
+   * param eventName       string  埋点事件名
+   * param eventData   object  埋点数据
+   * param isForce     boolean 是否强制实例化
+   * param isUpload    boolean 是否允许上传埋点数据,默认为true
+   * param owner       string  埋点负责人,默认为undefined
+   * param autoTrack1  boolean 初始化时自动上报一次 action 1,默认为 true
+   *
+   * return object
+   *  */
+  static getInstance(
+    eventName = "",
+    eventData = '',
+    isForce = false,
+    isUpload,
+    owner,
+    autoTrack1
+  ) {
+    // 判断埋点事件名是否被注册,利用instanceRecorder记录被注册埋点事件实例
+    // 如果已经实例化过,就直接返回实例,无需再new
+    // 如果要强制实例化,可以把forceInit设为true
+    if (
+      !Object.prototype.hasOwnProperty.call(this.instanceRecorder, eventName) ||
+      isForce
+    ) {
+      eventData = eventData || {};
+      let autoTrack1_temp = true;
+      if (autoTrack1 === false) {
+        autoTrack1_temp = false;
+      }
+      this.instanceRecorder[eventName] = new TrackFactory(
+        eventName,
+        eventData,
+        isUpload,
+        owner,
+        autoTrack1_temp
+      );
+    }
+    return this.instanceRecorder[eventName];
+  }
+
+  /**
+   * 销毁埋点事件实例
+   * param eventName       string  埋点事件名
+   *  */
+  static destory(eventName) {
+    delete this.instanceRecorder[eventName];
+  }
+
+  /**
+   * 埋点数据初始化
+   * param eventName      string  埋点事件名
+   * param eventData   object  埋点数据
+   * param isUpload boolean 是否允许上传埋点数据,默认为true
+   * param owner       string  埋点负责人,默认为undefined
+   * param autoTrack1  boolean 初始化时自动上报一次 action 1,默认为 true
+   *  */
+  constructor(
+    eventName = '',
+    eventData = '',
+    isUpload = true,
+    owner = "",
+    autoTrack1 = true
+  ) {
+    eventData = eventData || {};
+    eventData['owner'] = owner;
+    this.isUpload = isUpload;
+    this.eventName = eventName;
+    this.eventData = eventData;
+    if (autoTrack1) {
+      // 记录页面展示action
+      this.track(1);
+    }
+  }
+
+  /**
+   * 记录埋点的action
+   * param action     number  action code
+   * param onceData   object  针对指定action的埋点数据,具体见埋点需求
+   *  */
+  track = (action, onceData = {}) => {
+    if (action == 100 || action == 600) {
+      this.apiTimeStart = new Date().getTime();
+      this.eventData['user_mainstory_timecost'] =
+        new Date().getTime() - this.timeStart;
+    }
+
+    const trackData = { ...this.eventData, ...onceData };
+    trackData['action'] = action;
+    trackData['current_event_duration'] = new Date().getTime() - this.timeStart;
+    if (action == 200 || action == 400 || action == 700 || action == 800)
+      trackData['api_timecost'] = new Date().getTime() - this.apiTimeStart;
+    if (this.isUpload) {
+      normalTrack(this.eventName, trackData);
+    }
+  };
+}
+
+/**
+ * 曝光监测选项
+ */
+// interface VisibilityObserverOption {
+//   /** 要监听的节点的选择器,默认值 .watch-me  */
+//   selector: string;
+//   /** 阈值,默认为 0.5 */
+//   threshold: number;
+//   /** 在 viewport 中停留多少秒才算曝光。默认为 1 */
+//   delayInSecond: number;
+//   /** 用来扩展(或收缩)参照节点布局区域的边界。默认值为 { bottom: 0 } */
+//   margin: Partial<{
+//     left: number;
+//     right: number;
+//     top: number;
+//     bottom: number;
+//   }>;
+// }
+
+/**
+ * 创建并返回一个 IntersectionObserver 对象实例。
+ * 该实例会观测 option.selector 节点。
+ * 当节点在 viewport 的观测区域停留 option.delayInSecond 秒,
+ * 就认为该节点已曝光,会触发 visibilityCb。
+ *
+ * 注意:
+ * 1. 节点多次曝光会触发多次 visibilityCb
+ * 2. 待观测的 option.selector 节点必须有唯一的 id
+ * 3. 调用该方法时,待观测的节点必须在已经在页面上渲染完成。一般在 onReady / mounted 方法中调用。
+ * 4. 在 onUnload / beforeDestroy 需要执行 observer.disconnect() 来释放资源。
+ * 5. observer 不能作为 vue 实例的响应式数据。
+ *
+ * https://uniapp.dcloud.net.cn/api/ui/intersection-observer.html#createintersectionobserver
+ *
+ * @param elContainer 自定义组件实例,一般传入 this
+ * @param visibilityCb 回调函数
+ * @param option
+ * @returns IntersectionObserver 对象实例。
+ */
+const getVisibilityObserver = (
+  elContainer,
+  visibilityCb,
+  option
+) => {
+  const opt = Object.assign(
+    {},
+    {
+      selector: '.watch-me',
+      threshold: 0.5,
+      delayInSecond: 1,
+      margin: {
+        bottom: 0,
+      },
+    },
+    option ? option : {}
+  );
+
+  let timer = null;
+  const inMap = {};
+  const outMap = {};
+  const delay = opt.delayInSecond * 1000;
+
+  const observer = uni.createIntersectionObserver(elContainer, {
+    observeAll: true,
+    thresholds: [opt.threshold],
+    initialRatio: opt.threshold,
+  });
+  observer.relativeToViewport(opt.margin).observe(opt.selector, (res) => {
+    // eslint-disable-next-line no-console
+    // console.log(res);
+
+    // res 身上除了 UniApp.ObserveResult 中列明的属性外
+    // 还有两个属性:
+    // - id:string,节点的 id
+    // - dataset:object,存放节点上面的 data-* 数据
+    // 所以我们可以用 id 来做 inMap 和 outMap 的 key。
+    const key = res.id;
+    // 进入 viewpoint
+    if (res.intersectionRatio > opt.threshold) {
+      inMap[key] = res;
+      if (outMap[key]) {
+        delete outMap[key];
+      }
+    } else {
+      // 离开 viewpoint
+      outMap[key] = res;
+      if (inMap[key]) {
+        // 计算停留时间
+        if (outMap[key].time - inMap[key].time >= delay) {
+          visibilityCb(key, outMap[key]);
+          delete outMap[key];
+        }
+        delete inMap[key];
+      }
+    }
+
+    // 处理那些一直停留在 viewpoint 内的节点
+    if (timer) {
+      clearTimeout(timer);
+    }
+    timer = setTimeout(() => {
+      Object.keys(inMap).forEach((id) => {
+        visibilityCb(id, inMap[id]);
+        delete inMap[id];
+      });
+    }, delay);
+  });
+
+  return observer;
+};
+
+export { trackingInit, TrackFactory, getVisibilityObserver };

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است