@tmsfe/tms-core 0.0.33 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmsfe/tms-core",
3
- "version": "0.0.33",
3
+ "version": "0.0.34",
4
4
  "description": "tms运行时框架",
5
5
  "repository": {
6
6
  "type": "git",
@@ -7,7 +7,6 @@
7
7
 
8
8
  import syncApi from './syncfnmanager';
9
9
  import md5 from './md5';
10
- import CloudReport from './cloudReport';
11
10
  import { rpxToPx } from './rpx';
12
11
  import { serialize } from './objUtils';
13
12
  import * as stringUtils from './stringUtils';
@@ -40,7 +39,6 @@ function invoke(obj, funcName, args) {
40
39
  function initProxy(appObj, options) {
41
40
  app = appObj;
42
41
  initOptions = options;
43
- CloudReport.init(options);
44
42
  }
45
43
 
46
44
  function awaitTMS() {
@@ -75,8 +73,7 @@ asyncFuncNames.forEach((funcName) => {
75
73
  // 返回对象的函数,并且对象的所有接口都可以以promise的形式返回
76
74
  const objFuncs = {};
77
75
  const objFuncNames = [
78
- 'getLogManager', 'getRealtimeLogManager', 'getReporter',
79
- 'getFastReporter', 'getEventDispatcher',
76
+ 'getLogManager', 'getRealtimeLogManager', 'getReporter', 'getEventDispatcher',
80
77
  ];
81
78
  objFuncNames.forEach((funcName) => {
82
79
  objFuncs[funcName] = function (...args1) {
@@ -203,23 +200,10 @@ function getHomePage() {
203
200
  return initOptions.homePage;
204
201
  }
205
202
 
206
- function getCloudReport() {
207
- if (tms) {
208
- return tms.getCloudReport();
209
- }
210
- return CloudReport;
211
- }
212
-
213
- wx.onAppShow((options) => {
214
- // 避免vendor分包加载太慢而错过onAppShow
215
- wx.tmsAppShowOptions = options;
216
- });
217
-
218
203
  const api = {
219
204
  isProxy: true, // 方便定位问题时判断是否proxy
220
205
  initProxy,
221
206
  md5,
222
- getCloudReport,
223
207
  getCarManager,
224
208
  getLocationManager,
225
209
  rpxToPx,
package/src/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import Request from './request';
2
- import FastReport from './fastreport';
3
- import CloudReport from './cloudReport';
2
+ import Reporter from './report/index';
4
3
  import { getConfig } from './config';
5
4
  import syncApi from './syncfnmanager';
6
5
  import nav from './navigator';
@@ -8,7 +7,6 @@ import { getLogManager, getRealtimeLogManager } from './log';
8
7
  import { setEnvInfo, getEnvInfo, setAuthInfo, setAppPagePaths, isAppPageExist, getHomePage } from './env';
9
8
  import md5 from './md5';
10
9
  import { callCloudFunc } from './cloudService';
11
- import { cache as report, startListenApp } from './report';
12
10
  import EventDispatcher from './eventDispatcher';
13
11
  import { serialize } from './objUtils';
14
12
  import { rpxToPx } from './rpx';
@@ -66,18 +64,7 @@ const createRequest = (config = {}) => new Request(config);
66
64
  * @example
67
65
  * getReporter().report(reportData, reportNow);
68
66
  */
69
- const getReporter = () => ({ report });
70
-
71
- /**
72
- * @description 埋点上报(快速上报,不依赖用户userId标识)
73
- * @returns {Class} [FastReporter类](#class-fastreport)
74
- */
75
- const getFastReporter = () => FastReport;
76
-
77
- /**
78
- * @description 云函数埋点上报
79
- */
80
- const getCloudReport = () => CloudReport;
67
+ const getReporter = () => Reporter;
81
68
 
82
69
  /**
83
70
  * @description 自定义事件机制
@@ -103,7 +90,6 @@ const getLocationBaseClass = () => LocationBase;
103
90
  * @returns {undefined} 无返回值.
104
91
  */
105
92
  const init = (options = {}) => {
106
- startListenApp();
107
93
  const { appVersion, wxAppId, client, defaultHost, cloudEnvId, appEnv, appPagePaths, homePage } = options;
108
94
  const envInfo = {
109
95
  wxAppId,
@@ -114,7 +100,7 @@ const init = (options = {}) => {
114
100
  };
115
101
  setEnvInfo(envInfo);
116
102
  setAppPagePaths(appPagePaths, homePage);
117
- CloudReport.init(envInfo);
103
+ Reporter.init(envInfo);
118
104
  Request.defaultHost = defaultHost;
119
105
  // 初始化云环境
120
106
  wx.cloud.init({ env: cloudEnvId });
@@ -140,8 +126,6 @@ const api = {
140
126
  getRealtimeLogManager,
141
127
  md5,
142
128
  getReporter,
143
- getCloudReport,
144
- getFastReporter,
145
129
  getLocationManager,
146
130
  getLocationBaseClass,
147
131
  getEventDispatcher,
@@ -0,0 +1,92 @@
1
+ /**
2
+ * 格式化旧埋点
3
+ */
4
+
5
+ // / <reference path='./types.ts'/>
6
+ import helper from './helper';
7
+
8
+ /**
9
+ * 获取埋点的基础字段
10
+ */
11
+ function getBaseData(deviceData: IDeviceData): DataItem[] {
12
+ const arr = new Array<string>(helper.dataArrLen);
13
+ const { networkType, location } = deviceData;
14
+ const { appVersion, client } = helper.getInitOptions();
15
+ // todo: 如何区分新旧埋点?新:f20不为空,旧:f20为空
16
+ // ++++++++++++++++++++++++++字段列表++++++++++++++++++++++++++
17
+ // 0: log_time,日志入库时间
18
+ // 1: access_time,用户点击时间,服务端统一处理
19
+ // 2: user_ip,前端无需赋值
20
+ // 3: qimei,灯塔中的用户ID
21
+ // 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
22
+ // 5: user_id,用户ID
23
+ // 6: qq_no,登录QQ的openid,
24
+ // 7: wechat_id,登录微信的openid
25
+ // 8: phone_no,手机号
26
+ // 9: platform,客户端请求来源,小程序是2
27
+ arr[9] = '2';
28
+ // 10: app_version,客户端|小程序版本号
29
+ arr[10] = appVersion;
30
+ // 11: channel,外部推广渠道或车辆所属渠道
31
+ // 12: net_type,wifi、3G、4G等
32
+ arr[12] = networkType;
33
+ // 13: product_id,手图APP、车联网等
34
+ arr[13] = '2';
35
+ // 14: busi_type,1: 定位、2: 检索、3: 导航、4: 车主服务、5: 小程序
36
+ arr[14] = '5';
37
+ // 15: request_id,请求ID,由接入层填充
38
+ // 16: session_id,这里表示渠道公共参数
39
+ arr[16] = helper.getLaunchFrom();
40
+ // 17: f17,province - 省份
41
+ arr[17] = location.province;
42
+ // 18: f18,city - 城市
43
+ arr[18] = location.cityName;
44
+ // 19: f19,当前小程序运行的宿主环境
45
+ arr[19] = helper.getSystemInfo().host;
46
+ // 28: f28,sinan、mycar等
47
+ arr[28] = client;
48
+ // 29: f29,小程序场景值
49
+ arr[29] = helper.getAppScene();
50
+ // 33: f33,系统信息
51
+ arr[33] = helper.getSystemInfoString();
52
+ // 36: f36,小程序启动时的参数
53
+ arr[36] = helper.getLaunchOptionsString();
54
+ // --------------------------字段列表--------------------------
55
+ return arr;
56
+ }
57
+
58
+ /**
59
+ * 拼接埋点数组
60
+ */
61
+ function jointData(data: IOldParams, deviceData: IDeviceData): DataItem[] {
62
+ const arr = getBaseData(deviceData);
63
+ const keys = Object.keys(data) as any as number[];
64
+ for (const key of keys) {
65
+ arr[key] = helper.convert2String(data[key]);
66
+ }
67
+ return arr;
68
+ }
69
+
70
+ /**
71
+ * 格式化普通埋点数据
72
+ */
73
+ function formatData(data: IOldParams): Promise<DataItem[]> {
74
+ return new Promise<DataItem[]>((resolve) => {
75
+ helper.getDeviceData().then((deviceData: IDeviceData) => {
76
+ const arr = jointData(data, deviceData);
77
+ resolve(arr);
78
+ });
79
+ });
80
+ }
81
+
82
+ /**
83
+ * 格式化快速上报埋点数据,不依赖用户位置
84
+ */
85
+ function formatFastData(data: IOldParams): DataItem[] {
86
+ return jointData(data, helper.defaultDevice);
87
+ }
88
+
89
+ export default {
90
+ formatData,
91
+ formatFastData,
92
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * 格式化新埋点
3
+ */
4
+
5
+ // / <reference path='./types.ts'/>
6
+ import helper from './helper';
7
+
8
+ /**
9
+ * 获取埋点的基础字段
10
+ */
11
+ function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: number } {
12
+ const page = helper.getPageInfo() as IPage;
13
+ const { networkType, location } = deviceData;
14
+ const { appVersion, client } = helper.getInitOptions();
15
+ const arr = new Array<string>(helper.dataArrLen);
16
+ // todo: 如何区分新旧埋点?新:f20不为空,旧:f20为空
17
+ // ++++++++++++++++++++++++++字段列表++++++++++++++++++++++++++
18
+ // 0: log_time,日志入库时间
19
+ // 1: access_time,用户点击时间,服务端统一处理
20
+ // 2: user_ip,前端无需赋值
21
+ // 3: qimei,灯塔中的用户ID
22
+ // 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
23
+ // 5: user_id,用户ID
24
+ // 6: qq_no,登录QQ的openid,
25
+ // 7: wechat_id,登录微信的openid
26
+ // 8: phone_no,手机号
27
+ // 9: platform,客户端请求来源,小程序是2
28
+ arr[9] = '2';
29
+ // 10: app_version,客户端|小程序版本号
30
+ arr[10] = appVersion;
31
+ // 11: channel,外部推广渠道或车辆所属渠道
32
+ // 12: net_type,wifi、3G、4G等
33
+ arr[12] = networkType;
34
+ // 13: product_id,手图APP、车联网等
35
+ arr[13] = '2';
36
+ // 14: busi_type,1: 定位、2: 检索、3: 导航、4: 车主服务、5: 小程序
37
+ arr[14] = '5';
38
+ // 15: request_id,请求ID,由接入层填充
39
+ // 16: session_id,标识用户一次访问过程的ID
40
+ // 17: f17,province - 省份
41
+ arr[17] = location.province;
42
+ // 18: f18,city - 城市
43
+ arr[18] = location.cityName;
44
+ // 19: f19,系统信息
45
+ arr[19] = helper.getSystemInfoString();
46
+ // 20: f20,sinan、mycar等
47
+ arr[20] = client;
48
+ // 21: f21,小程序场景值
49
+ arr[21] = helper.getAppScene();
50
+ // 22: f22,小程序启动时的参数
51
+ arr[22] = helper.getLaunchOptionsString();
52
+ // 23: f23,当前页面的url
53
+ arr[23] = page.route;
54
+ // 24: f24,当前页面的query
55
+ arr[24] = JSON.stringify(page.options);
56
+ // 25 ~ 30: 预留字典给后续扩展使用
57
+ // 31 ~ 40: 提供给开发自定义
58
+ // --------------------------字段列表--------------------------
59
+ return { arr, nextIndex: 31 };
60
+ }
61
+
62
+ /**
63
+ * 拼接埋点数组
64
+ */
65
+ function jointData(data: any[], deviceData: IDeviceData): DataItem[] {
66
+ const { arr, nextIndex } = getBaseData(deviceData);
67
+ let index = nextIndex;
68
+ for (const item of data) {
69
+ if (index >= arr.length) {
70
+ console.error('埋点参数个数超出上限而被截断', data);
71
+ break;
72
+ }
73
+ arr[index] = helper.convert2String(item);
74
+ index += 1;
75
+ }
76
+ return arr;
77
+ }
78
+
79
+ /**
80
+ * 格式化上报埋点数据
81
+ */
82
+ function formatData(data: any[]): Promise<DataItem[]> {
83
+ return new Promise<DataItem[]>((resolve) => {
84
+ helper.getDeviceData().then((deviceData: IDeviceData) => {
85
+ const arr = jointData(data, deviceData);
86
+ resolve(arr);
87
+ });
88
+ });
89
+ }
90
+
91
+ /**
92
+ * 格式化快速上报埋点数据,不依赖用户位置
93
+ */
94
+ function formatFastData(data: any[]): DataItem[] {
95
+ return jointData(data, helper.defaultDevice);
96
+ }
97
+
98
+ export default {
99
+ formatData,
100
+ formatFastData,
101
+ };
@@ -0,0 +1,217 @@
1
+ /**
2
+ * 埋点辅助函数
3
+ */
4
+
5
+ // / <reference path='./types.ts'/>
6
+ import syncApi from '../syncfnmanager';
7
+
8
+ function getTms(): any {
9
+ // 如果是在app.js的onLaunch中调用,则没有getApp().tms为空
10
+ return getApp()?.tms || wx.tms;
11
+ }
12
+
13
+ let initOptions: IInitOptions;
14
+
15
+ /**
16
+ * 初始化
17
+ */
18
+ function init(options: IInitOptions): void {
19
+ if (!initOptions) {
20
+ initOptions = options;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 获取初始化时的参数
26
+ */
27
+ function getInitOptions(): IInitOptions {
28
+ return initOptions;
29
+ }
30
+
31
+ let systemInfo: ISystemInfo | null = null;
32
+
33
+ /**
34
+ * 获取系统信息
35
+ */
36
+ function getSystemInfo(): ISystemInfo {
37
+ if (systemInfo === null) {
38
+ const system = syncApi.getSystemInfoSync() as any;
39
+ // eslint-disable-next-line
40
+ const { model = '', version: wxVersion = '', platform = '', SDKVersion = '', host = '' } = system;
41
+ systemInfo = { model, wxVersion, platform, SDKVersion, host };
42
+ }
43
+ return systemInfo;
44
+ }
45
+
46
+ let systemString: string | null = null;
47
+
48
+ /**
49
+ * 获取系统信息并格式化成字符串
50
+ */
51
+ function getSystemInfoString(): string {
52
+ if (systemString === null) {
53
+ systemString = JSON.stringify(getSystemInfo());
54
+ }
55
+ return systemString;
56
+ }
57
+
58
+ /**
59
+ * 把值/对象转成字符串
60
+ * @param value
61
+ */
62
+ function convert2String(value: any): string {
63
+ const type = typeof value;
64
+ if (type === 'string') {
65
+ return value;
66
+ }
67
+ if (type === 'object') {
68
+ return JSON.stringify(value);
69
+ }
70
+ return String(value);
71
+ }
72
+
73
+ /**
74
+ * 获取当前页面信息
75
+ */
76
+ function getPageInfo(): IPage | null {
77
+ const pages = getCurrentPages();
78
+ // 首页未渲染
79
+ if (pages.length === 0) {
80
+ const launch = syncApi.getLaunchOptionsSync() as any;
81
+ return { route: launch.path, options: launch.query };
82
+ }
83
+ const page = pages.pop();
84
+ // 插件页
85
+ if (!page) {
86
+ return null;
87
+ }
88
+ return page;
89
+ }
90
+
91
+ let launchOptions: string | null = null;
92
+
93
+ /**
94
+ * 获取小程序启动参数
95
+ */
96
+ function getLaunchOptionsString(): string {
97
+ if (launchOptions === null) {
98
+ const obj = syncApi.getLaunchOptionsSync() as any;
99
+ launchOptions = obj.query ? JSON.stringify(obj.query) : '';
100
+ }
101
+ return launchOptions;
102
+ }
103
+
104
+ /**
105
+ * 获取小程序启动时的from参数
106
+ */
107
+ function getLaunchFrom(): string {
108
+ const obj = syncApi.getLaunchOptionsSync() as any;
109
+ return obj.query?.from as string || '';
110
+ }
111
+
112
+ /**
113
+ * 获取小程序启动场景值
114
+ */
115
+ function getAppScene(): string {
116
+ const { scene = -1 } = syncApi.getLaunchOptionsSync() as any;
117
+ return scene.toString();
118
+ }
119
+
120
+ // 是否爬虫
121
+ let isCrawler: boolean | null = null;
122
+
123
+ /**
124
+ * 判断当前是否需要发埋点(爬虫和插件页不发)
125
+ */
126
+ function canReport(): boolean {
127
+ if (isCrawler === null) {
128
+ const scene = getAppScene();
129
+ isCrawler = scene === '1129' || scene === '1030';
130
+ }
131
+ // 小程序爬虫,不上报
132
+ if (isCrawler) {
133
+ return false;
134
+ }
135
+ // 插件页不上报埋点
136
+ return getPageInfo() !== null;
137
+ }
138
+
139
+ /**
140
+ * 埋点数组的长度
141
+ */
142
+ const dataArrLen = 41;
143
+
144
+ const defaultDevice = {
145
+ networkType: null,
146
+ location: {
147
+ province: null,
148
+ cityName: null,
149
+ },
150
+ } as any as IDeviceData;
151
+
152
+ let networkPromise: Promise<string> | null = null;
153
+
154
+ function getNetworkType(): Promise<string> {
155
+ if (networkPromise === null) {
156
+ networkPromise = new Promise((resolve) => {
157
+ wx.getNetworkType({
158
+ success: (res: any) => resolve(res.networkType),
159
+ fail: () => resolve('unknown'),
160
+ });
161
+ });
162
+ }
163
+ return networkPromise;
164
+ }
165
+
166
+ let locPromise: Promise<ILocation> | null = null;
167
+
168
+ function getLocation(): Promise<ILocation> {
169
+ if (locPromise === null) {
170
+ locPromise = new Promise<ILocation>((resolve) => {
171
+ const LOC = getTms().getLocationManager();
172
+ LOC.getIpLocation()
173
+ .then((res: any) => {
174
+ const ad = res.ad_info;
175
+ resolve({ province: ad.province, cityName: ad.city });
176
+ })
177
+ .catch(() => {
178
+ resolve({ province: '', cityName: '' });
179
+ });
180
+ });
181
+ }
182
+ return locPromise;
183
+ }
184
+
185
+ let devicePromise: Promise<IDeviceData> | null = null;
186
+
187
+ /**
188
+ * 获取设备信息(网络状况,位置信息)
189
+ */
190
+ function getDeviceData(): Promise<IDeviceData> {
191
+ if (devicePromise === null) {
192
+ devicePromise = new Promise<IDeviceData>((resolve) => {
193
+ Promise.all([getNetworkType(), getLocation()]).then((res) => {
194
+ const [networkType, location] = res;
195
+ resolve({ networkType, location });
196
+ });
197
+ });
198
+ }
199
+ return devicePromise;
200
+ }
201
+
202
+ export default {
203
+ init,
204
+ getTms,
205
+ getInitOptions,
206
+ getSystemInfo,
207
+ getSystemInfoString,
208
+ convert2String,
209
+ getPageInfo,
210
+ getLaunchOptionsString,
211
+ getLaunchFrom,
212
+ getAppScene,
213
+ canReport,
214
+ dataArrLen,
215
+ defaultDevice,
216
+ getDeviceData,
217
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 埋点
3
+ */
4
+
5
+ // / <reference path='./types.ts'/>
6
+ import helper from './helper';
7
+ import sender from './sender';
8
+ import formatV1 from './formatV1';
9
+ import formatV2 from './formatV2';
10
+
11
+ /**
12
+ * 初始化
13
+ */
14
+ function init(options: IInitOptions): void {
15
+ helper.init(options);
16
+ }
17
+
18
+ /**
19
+ * 旧埋点
20
+ * @param data { 27: xxxx, 34: xxx }
21
+ */
22
+ function report(data: IOldParams = {}): void {
23
+ if (helper.canReport()) {
24
+ formatV1.formatData(data).then(arr => sender.queue(arr));
25
+ }
26
+ }
27
+
28
+ /**
29
+ * 旧埋点,快速上报,不依赖用户位置
30
+ * @param data { 27: xxxx, 34: xxx }
31
+ */
32
+ function fastReport(data: IOldParams = {}): void {
33
+ if (helper.canReport()) {
34
+ const arr = formatV1.formatFastData(data);
35
+ sender.send(arr);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * 新埋点
41
+ */
42
+ function report2(...data: any[]): void {
43
+ if (helper.canReport()) {
44
+ formatV2.formatData(data).then(arr => sender.queue(arr));
45
+ }
46
+ }
47
+
48
+ /**
49
+ * 新埋点,快速上报,不依赖用户位置
50
+ */
51
+ function fastReport2(...data: any[]): void {
52
+ if (helper.canReport()) {
53
+ const arr = formatV2.formatFastData(data);
54
+ sender.send(arr);
55
+ }
56
+ }
57
+
58
+ export default {
59
+ init,
60
+ report,
61
+ fastReport,
62
+ report2,
63
+ fastReport2,
64
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * 负责发送埋点
3
+ */
4
+
5
+ // / <reference path='./types.ts'/>
6
+ import helper from './helper';
7
+
8
+ // 缓存队列
9
+ const cacheArr = new Array<DataItem[]>();
10
+ const max = 50; // 超过最大限制就马上发送
11
+ const delay = 3000; // 延迟N毫秒再聚合发送
12
+ let timer = 0; // 计时器
13
+
14
+ // 检查队列埋点,是否超过上限需要马上发送或者启动计时器
15
+ function checkQueue(isSendFail: boolean): void {
16
+ if (!isSendFail && cacheArr.length >= max) {
17
+ batchSendData();
18
+ } else {
19
+ startTimer();
20
+ }
21
+ }
22
+
23
+ // 启动计时器
24
+ function startTimer(): void {
25
+ if (timer !== 0 || cacheArr.length === 0) {
26
+ return;
27
+ }
28
+ timer = setTimeout(() => {
29
+ timer = 0;
30
+ batchSendData();
31
+ }, delay);
32
+ }
33
+
34
+ // 停止计时器
35
+ function stopTimer(): void {
36
+ if (timer !== 0) {
37
+ clearTimeout(timer);
38
+ timer = 0;
39
+ }
40
+ }
41
+
42
+ // 批量发送埋点
43
+ function batchSendData(): void {
44
+ if (cacheArr.length === 0) {
45
+ return;
46
+ }
47
+
48
+ const batch = cacheArr.splice(0, max);
49
+ const request = helper.getTms().createRequest();
50
+ request.post('basic/event/upload', { batch })
51
+ .then((res: any) => {
52
+ if (res.errCode !== 0) {
53
+ requestFail(batch);
54
+ } else {
55
+ startTimer();
56
+ }
57
+ })
58
+ .catch((e: any) => {
59
+ console.error(e);
60
+ requestFail(batch);
61
+ });
62
+ }
63
+
64
+ // 发送失败就再加回队列
65
+ function requestFail(batch: DataItem[][]): void {
66
+ batch.forEach(item => cacheArr.push(item));
67
+ checkQueue(true);
68
+ }
69
+
70
+ /**
71
+ * 立刻发送埋点
72
+ * @param arr
73
+ */
74
+ function send(arr: DataItem[]): void {
75
+ stopTimer();
76
+ cacheArr.unshift(arr); // 如果队列中很多,排前面比较稳妥
77
+ batchSendData();
78
+ }
79
+
80
+ /**
81
+ * 添加埋点到队列中,等待聚合发送
82
+ * @param arr
83
+ */
84
+ function queue(arr: DataItem[]): void {
85
+ cacheArr.push(arr);
86
+ checkQueue(false);
87
+ }
88
+
89
+ // 小程序关闭时马上发送所有埋点
90
+ wx.onAppHide(() => {
91
+ stopTimer();
92
+ batchSendData();
93
+ });
94
+
95
+ export default {
96
+ send,
97
+ queue,
98
+ };
@@ -0,0 +1,44 @@
1
+ /* eslint-disable */
2
+
3
+ type DataItem = string | null | undefined;
4
+
5
+ /**
6
+ * 初始化参数对象
7
+ */
8
+ interface IInitOptions {
9
+ client: string, // sinan、mycar等
10
+ appVersion: string,
11
+ }
12
+
13
+ /**
14
+ * 页面对象
15
+ */
16
+ interface IPage {
17
+ route: string,
18
+ options: object,
19
+ }
20
+
21
+ /**
22
+ * 旧的埋点参数类型
23
+ */
24
+ interface IOldParams {
25
+ [key: number]: any,
26
+ }
27
+
28
+ interface ISystemInfo {
29
+ model: string,
30
+ wxVersion: string,
31
+ platform: '',
32
+ SDKVersion: '',
33
+ host: '',
34
+ }
35
+
36
+ interface ILocation {
37
+ province: string,
38
+ cityName: string,
39
+ }
40
+
41
+ interface IDeviceData {
42
+ networkType: string,
43
+ location: ILocation,
44
+ }
@@ -1,199 +0,0 @@
1
- /**
2
- * 调用云函数上报埋点
3
- */
4
-
5
- import syncApi from './syncfnmanager';
6
-
7
- type DataType = string | null;
8
-
9
- interface IInitOptions {
10
- client: string, // sinan、mycar等
11
- appVersion: string,
12
- cloudEnvId: string,
13
- }
14
-
15
- interface IPage {
16
- route: string,
17
- options: object,
18
- }
19
-
20
- const openidIndex = 7; // openId的下标,由云函数填充
21
-
22
- let initOptions: IInitOptions;
23
-
24
- function init(options: IInitOptions): void {
25
- if (!initOptions) {
26
- initOptions = options;
27
- wx.cloud.init({ env: options.cloudEnvId });
28
- }
29
- }
30
-
31
- function getSystemInfoString(): string {
32
- const system = syncApi.getSystemInfoSync();
33
- // @ts-ignore
34
- // eslint-disable-next-line
35
- const { model = '', version: wxVersion = '', platform = '', SDKVersion = '', host = '' } = system;
36
- const info = { model, wxVersion, platform, SDKVersion, host };
37
- return encodeURIComponent(JSON.stringify(info));
38
- }
39
-
40
- function convert2String(value: any): string {
41
- const type = typeof value;
42
- if (type === 'string') {
43
- return value;
44
- }
45
- if (type === 'object') {
46
- return JSON.stringify(value);
47
- }
48
- return String(value);
49
- }
50
-
51
- function getSeqInfo(): { timestamp: number, seqId: string } {
52
- const timestamp = Date.now();
53
- const random = Math.random().toString();
54
- const randomStr = random.slice(2, 7);
55
- const sourceId = 7; // 6 未知 7 云函数 8 出行 9 我的车
56
- const seqId = `${timestamp}${sourceId}${randomStr}`;
57
- return { timestamp, seqId };
58
- }
59
-
60
- function getPageInfo(): IPage | null {
61
- const pages = getCurrentPages();
62
- // 首页未渲染
63
- if (pages.length === 0) {
64
- const launch = syncApi.getLaunchOptionsSync();
65
- return { route: launch.path, options: launch.query };
66
- }
67
- const page = pages.pop();
68
- // 插件页
69
- if (!page) {
70
- return null;
71
- }
72
- return page;
73
- }
74
-
75
- /**
76
- * 获取埋点的基础字段
77
- * @param page
78
- * @param seqId 请求ID
79
- */
80
- function getBaseData(page: IPage, seqId: string): { arr: DataType[], nextIndex: number } {
81
- const pageQuery = encodeURIComponent(JSON.stringify(page.options));
82
- const pageUrl = page.route;
83
- const { appVersion, client } = initOptions;
84
- const system = getSystemInfoString();
85
- // @ts-ignore
86
- const { scene = -1 } = syncApi.getLaunchOptionsSync();
87
-
88
- const arr = new Array(41);
89
- // 0: log_time
90
- // 1: access_time,用户点击时间,服务端统一处理
91
- // 2: user_ip,前端无需赋值
92
- // 3: qimei,灯塔中的用户ID
93
- // 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
94
- // 5: user_id,用户ID
95
- // 6: qq_no,登录QQ的openid,
96
- // 7: wechat_id,登录微信的openid,由云函数填充
97
- // 8: phone_no,手机号
98
- // 9: platform,客户端请求来源,小程序是2
99
- arr[9] = '2';
100
- // 10: app_version,客户端|小程序版本号
101
- arr[10] = appVersion;
102
- // 11: channel,外部推广渠道或车辆所属渠道
103
- // 12: net_type,wifi、3G、4G等
104
- // 13: product_id,手图APP、车联网等
105
- // 14: busi_type,1: 定位、2: 检索、3: 导航、4: 车主服务、5: 小程序
106
- arr[14] = '5';
107
- // 15: request_id,请求ID
108
- arr[15] = seqId;
109
- // 16: session_id,标识用户一次访问过程的ID
110
- // 17: f17 - province,省份
111
- // 18: f18 - city,城市
112
- // 19: f19 - 系统信息
113
- arr[19] = system;
114
- // 20: f20 - sinan、mycar等
115
- arr[20] = client;
116
- // 21: f21 - 小程序场景值
117
- arr[21] = scene.toString();
118
- // 22: f22 - 标记是云函数上报
119
- arr[22] = 'cloud';
120
- // 23: f23 - 当前页面的url
121
- arr[23] = pageUrl;
122
- // 24: f24 - 当前页面的query
123
- arr[24] = pageQuery;
124
- // 25 ~ 30: 预留字典给后续扩展使用
125
- // 31 ~ 40: 提供给开发自定义
126
- return { arr, nextIndex: 31 };
127
- }
128
-
129
- function formatData(page: IPage, seqId: string, data: any[]): DataType[] {
130
- const { arr, nextIndex } = getBaseData(page, seqId);
131
- let index = nextIndex;
132
- for (const item of data) {
133
- if (index >= arr.length) {
134
- console.error('埋点参数个数超出上限而被截断', data);
135
- break;
136
- }
137
- arr[index] = convert2String(item);
138
- index += 1;
139
- }
140
- return arr;
141
- }
142
-
143
- function report(...data: any[]): void {
144
- console.log('云埋点', data);
145
- const page = getPageInfo();
146
- if (page === null) {
147
- console.log('插件页不上报埋点');
148
- return;
149
- }
150
- // @ts-ignore
151
- const { scene = -1 } = syncApi.getLaunchOptionsSync();
152
- // 小程序爬虫产生的日志
153
- if (scene === 1129 || scene === 1030) {
154
- console.warn('小程序爬虫,不上报');
155
- } else {
156
- const { timestamp, seqId } = getSeqInfo();
157
- const arr = formatData(page, seqId, data);
158
- doCallCloudFunc(arr, timestamp, seqId, openidIndex);
159
- }
160
- }
161
-
162
- function doCallCloudFunc(arr: DataType[], timestamp: number, seqId: string, openidIndex: number): void {
163
- const { cloudEnvId, appVersion } = initOptions;
164
- const onFail = (err: any) => {
165
- console.error('云埋点失败:', arr, err);
166
- };
167
- wx.cloud.callFunction({
168
- name: 'report',
169
- data: {
170
- openidIndex,
171
- params: { seqId, timestamp, appVersion, batch: [arr] },
172
- },
173
- config: { env: cloudEnvId },
174
- success: (res: any) => {
175
- const body = JSON.parse(res.result.body);
176
- if (body.errCode !== 0) {
177
- onFail(res);
178
- }
179
- },
180
- fail: onFail,
181
- });
182
- }
183
-
184
- /**
185
- * 调用云函数将已经组装完整的埋点数据arr发出去
186
- * @param arr
187
- */
188
- function callCloudFunc(arr: string[]): void {
189
- const { timestamp, seqId } = getSeqInfo();
190
- doCallCloudFunc(arr, timestamp, seqId, openidIndex);
191
- }
192
-
193
- export default {
194
- init,
195
- report,
196
- callCloudFunc,
197
- convert2String,
198
- getSystemInfoString,
199
- };
package/src/fastreport.js DELETED
@@ -1,51 +0,0 @@
1
- /**
2
- * @copyright 2020-present, Tencent, Inc. All rights reserved.
3
- * @author Fenggang.Sun <fenggangsun@tencent.com>
4
- * @file 快速上报数据,不处理过多逻辑,保证快速上报
5
- */
6
- import { getEnvInfo, getAuthInfo } from './env';
7
- import CloudReport from './cloudReport';
8
-
9
- const handleParamOfDifferentType = (param) => {
10
- const data = new Array(41);
11
- Object.keys(param).forEach((key) => {
12
- data[key] = CloudReport.convert2String(param[key]);
13
- });
14
- return data;
15
- };
16
-
17
- const defAssign = (value, defaultValue) => (value || defaultValue);
18
-
19
- /**
20
- * @class FastReport
21
- * @classdesc 快速上报模块,不依赖用户标识和位置
22
- */
23
- export default class FastReport {
24
- /**
25
- * @memberof FastReport
26
- * @description 快速上报
27
- * @param {Object} param 埋点数据
28
- */
29
- static report(param = {}) {
30
- if (!param[27]) {
31
- return Promise.reject('invalid report param');
32
- }
33
- const env = getEnvInfo();
34
- const data = handleParamOfDifferentType(param);
35
- data[9] = '2';
36
- data[10] = defAssign(data[10], env.appVersion);
37
- data[26] = defAssign(data[26], data[27]?.[0]);
38
- data[28] = env.client;
39
- if (!data[29]) {
40
- const appShowScene = wx.getStorageSync('appShowScene');
41
- if (appShowScene) {
42
- data[29] = String(appShowScene);
43
- }
44
- }
45
- data[33] = CloudReport.getSystemInfoString();
46
- getAuthInfo().then((user) => {
47
- data[5] = user.uid;
48
- CloudReport.callCloudFunc(data);
49
- });
50
- }
51
- };
package/src/report.js DELETED
@@ -1,433 +0,0 @@
1
- import Request from './request';
2
- import getLocInstance from './location/index';
3
- import { getAuthInfo } from './env';
4
-
5
- const R = new Request();
6
- const ReportInterval = 3000; // 轮训上报间隔
7
- const ReportDataQueue = []; // 上报数据队列
8
- let ReportTaskId = -1; // 轮训上报id
9
-
10
- let appShowOptions = null; // 设置小程序onShow参数
11
- function getAppShowOptions() {
12
- if (appShowOptions === null) {
13
- try {
14
- appShowOptions = wx.getLaunchOptionsSync();
15
- } catch (_) {
16
- appShowOptions = {};
17
- }
18
- }
19
- return appShowOptions;
20
- }
21
-
22
- function getTMS() {
23
- return getApp({ allowDefault: true }).tms;
24
- }
25
-
26
- // 缓存系统信息,避免每次都重新获取系统信息
27
- let systemPromise = null;
28
- /**
29
- * 获取系统信息
30
- * @private
31
- * @returns {object} 系统信息
32
- */
33
- const getSystemPromise = () => {
34
- if (systemPromise === null) {
35
- systemPromise = new Promise((resolve) => {
36
- wx.getSystemInfo({
37
- success: (systemInfo) => {
38
- const { model, version: wxVersion, platform, SDKVersion, host } = systemInfo;
39
- resolve({ model, wxVersion, platform, SDKVersion, host });
40
- },
41
- fail: () => {
42
- resolve({});
43
- },
44
- });
45
- });
46
- }
47
- return systemPromise;
48
- };
49
-
50
- let networkPromise = null;
51
- /**
52
- * 获取网络信息
53
- * @private
54
- * @returns {string} 网络信息
55
- */
56
- const getNetworkType = () => {
57
- if (networkPromise === null) {
58
- networkPromise = new Promise((resolve) => {
59
- wx.getNetworkType({
60
- success: (netInfo) => {
61
- resolve(netInfo.networkType);
62
- },
63
- fail: () => {
64
- resolve('unknown');
65
- },
66
- });
67
- });
68
- }
69
- return networkPromise;
70
- };
71
-
72
- let ProvinceInfoCache = null;
73
- /**
74
- * 获取省市信息
75
- * @private
76
- */
77
- const getProvinceInfo = () => {
78
- if (!ProvinceInfoCache) {
79
- ProvinceInfoCache = new Promise(async (resolve) => {
80
- try {
81
- const loc = await getLocInstance().getLocationDetailSilent();
82
- resolve({
83
- cityName: loc.cityName,
84
- province: loc.province,
85
- });
86
- } catch (_) {
87
- // 获取位置失败时,尝试获取本地存储的位置信息
88
- const loc = getLocInstance().getUserLocation();
89
- if (loc?.cityName) {
90
- resolve(loc);
91
- } else {
92
- resolve({ cityName: '', province: '' });
93
- }
94
- }
95
- });
96
- }
97
- return ProvinceInfoCache;
98
- };
99
-
100
- let ProvinceInfoCacheByIp = null;
101
- /**
102
- * 调接口获取省市信息
103
- * @private
104
- */
105
- const getProvinceInfoByIp = () => {
106
- if (!ProvinceInfoCacheByIp) {
107
- ProvinceInfoCacheByIp = new Promise(async (resolve) => {
108
- try {
109
- const loc = await getLocInstance().getIpLocation();
110
- const { city, province } = loc.ad_info;
111
- resolve({ cityName: city, province });
112
- } catch (_) {
113
- resolve({ cityName: '', province: '' });
114
- }
115
- });
116
- }
117
- return ProvinceInfoCacheByIp;
118
- };
119
-
120
- // 用于缓存用户唯一标识userId
121
- let userId = '';
122
- /**
123
- * 获取用户唯一标识userId
124
- * @private
125
- * @returns {string} userId
126
- */
127
- const getUserId = async () => {
128
- if (userId) {
129
- return userId;
130
- }
131
-
132
- ({ userId } = await getAuthInfo());
133
- return userId;
134
- };
135
-
136
- let launchFrom = '';
137
- /**
138
- * 获取用户启动小程序时的渠道来源值
139
- * @private
140
- * @returns {string} 用户启动小程序时的渠道来源值
141
- */
142
- const getLaunchFrom = () => {
143
- if (launchFrom) {
144
- return launchFrom;
145
- }
146
-
147
- try {
148
- const LaunchOptions = wx.getLaunchOptionsSync() || {};
149
- const { query = {} } = LaunchOptions;
150
- launchFrom = (query.from || '');
151
-
152
- return launchFrom;
153
- } catch (_) {
154
- return '';
155
- }
156
- };
157
-
158
- /**
159
- * 将上报的对象类型的数据转换成数组类型
160
- * @private
161
- * @param {object} reportData 需要上报的数据
162
- * @returns {array} 数组格式的上报数据
163
- */
164
- const getReportDataList = (reportData = {}) => {
165
- const reportList = [];
166
- reportList.length = 40;
167
- Object.keys(reportData).forEach((key) => {
168
- const reportIndex = Number(key);
169
- reportList[reportIndex] = reportData[key];
170
- });
171
-
172
- return reportList;
173
- };
174
-
175
- // 小程序版本号
176
- let verNum = null;
177
- function getMiniProgramVersion() {
178
- if (verNum === null) {
179
- const { miniProgram: { version } = {} } = wx.getAccountInfoSync() || {};
180
- verNum = version;
181
- }
182
- return verNum;
183
- }
184
-
185
- /**
186
- * 格式化上报数据
187
- * @private
188
- * @param {object} reportData 需要上报的数据
189
- * @param {object} deviceData 设备数据(系统信息,网络状况,位置信息)
190
- * @returns {array} 格式化后的上报数据
191
- */
192
- const formatReportData = async (reportData, deviceData) => {
193
- const [system, netType, provinceInfo] = deviceData;
194
- const param = Array.isArray(reportData.param) ? reportData.param : getReportDataList(reportData);
195
- const { client } = await getTMS().getEnvInfo();
196
-
197
- param[5] = await getUserId();
198
- param[10] = getMiniProgramVersion(); // 上报小程序版本号
199
- param[16] = getLaunchFrom(); // 渠道公共参数
200
- param[17] = param[17] || provinceInfo?.province;
201
- param[18] = param[18] || provinceInfo?.cityName;
202
- param[19] = system?.host; // 上报宿主app信息 { appId, env, version }
203
- param[28] = client;
204
-
205
- // 默认上报数据
206
- const defaultReportData = {
207
- 9: '2',
208
- 12: netType,
209
- 13: '2',
210
- 29: getAppShowOptions().scene, // 打开小程序的场景值
211
- 33: JSON.stringify(system), // 数据字符串化
212
- 36: getAppShowOptions(), // 打开小程序的场景值及参数
213
- };
214
- // 对部分空数据使用默认数据填充
215
- const handleDefaultData = (paramItem, defaultData) => {
216
- const arr = [paramItem, paramItem === 0, paramItem === false];
217
- return (arr.some(item => !!item)) ? paramItem : (defaultData || '');
218
- };
219
-
220
- Object.keys(defaultReportData).forEach((index) => {
221
- const paramItem = param[index];
222
- param[index] = handleDefaultData(paramItem, defaultReportData[index]);
223
- });
224
-
225
- // 所有上报数据都转换为字符串
226
- param.forEach((reportItem, index) => {
227
- if (reportItem && typeof reportItem !== 'string') {
228
- param[index] = `${JSON.stringify(reportItem)}`;
229
- } else {
230
- param[index] = handleDefaultData(reportItem, '');
231
- }
232
- });
233
-
234
- return param.map(item => (item !== null ? encodeURIComponent(item) : item));
235
- };
236
-
237
- /**
238
- * 格式化上报到小程序后台的数据(自定义分析使用)
239
- * @private
240
- * @param {array} data 埋点数据
241
- * @returns {object} 格式化的数据
242
- */
243
- const formatAnalyticsData = async (data) => {
244
- const analyticsData = {};
245
- const { 10: version, 29: scene } = data;
246
- analyticsData[10] = version; // 小程序版本号
247
- analyticsData[29] = scene; // 小程序场景值
248
- analyticsData[17] = decodeURIComponent(data[17]); // 用户所在省份
249
- analyticsData[18] = decodeURIComponent(data[18]); // 用户所在城市
250
-
251
- // 将30-40位埋点字段补充到上报数据中
252
- for (let i = 30; i < 41; i += 1) {
253
- analyticsData[i] = decodeURIComponent(data[i] || '');
254
- // 如果第33列是设备信息,则忽略
255
- if (data[33] && data[33].indexOf('model') > -1) {
256
- analyticsData[33] = '';
257
- }
258
- }
259
- const { client } = await getTMS().getEnvInfo();
260
-
261
- return { analyticsData, eventName: `${client}_${data[27]}` };
262
- };
263
-
264
- /**
265
- * 上报数据到小程序后台
266
- * @private
267
- * @param {array} list 需要上报的数据列表
268
- * @returns {void}
269
- */
270
- const reportAnalytics = (list = []) => {
271
- try {
272
- list.forEach((item) => {
273
- const { eventName, analyticsData } = formatAnalyticsData(item);
274
- wx.reportAnalytics(eventName, analyticsData);
275
- });
276
- } catch (_) {}
277
- };
278
-
279
- /**
280
- * @description report 方法 将上报数据缓存到内存中
281
- * @name report
282
- * @param {object} reportData 需要上报的数据
283
- * @param {boolean} reportNow 是否立即上报
284
- * @param {boolean} locPoi 是否通过微信接口获取poi
285
- * @returns {Void} 无返回值
286
- */
287
- const cache = (reportData, reportNow, locPoi = true) => {
288
- // 如果是微信爬虫 | 自动化测试场景,则不进行数据上报
289
- const DO_NOT_NEED_REPORT_SCENES = [1030, 1129];
290
- if (DO_NOT_NEED_REPORT_SCENES.includes(getAppShowOptions().scene)) {
291
- return Promise.resolve();
292
- }
293
-
294
- const task = [getSystemPromise(), getNetworkType()];
295
- if (locPoi) {
296
- task.push(getProvinceInfo());
297
- } else {
298
- task.push(getProvinceInfoByIp());
299
- }
300
- return Promise.all(task)
301
- .then(async (deviceData) => {
302
- // 先对上报数据进行预处理
303
- const result = await formatReportData(reportData, deviceData);
304
- // 将需要上报的数据缓存在队列中
305
- ReportDataQueue.push(result);
306
- })
307
- .then(() => {
308
- if (reportNow) {
309
- return sendData();
310
- }
311
- return { cache: true };
312
- });
313
- };
314
-
315
- /**
316
- * 发送数据到服务端
317
- * @private
318
- * @returns {promise} 发送请求
319
- */
320
- const sendData = async () => {
321
- // 没有数据时,不发送上报请求
322
- if (ReportDataQueue.length === 0) {
323
- return Promise.resolve();
324
- }
325
-
326
- const cacheReportData = [...ReportDataQueue];
327
- return R.post('basic/event/upload', { userId: await getUserId(), batch: cacheReportData })
328
- .then((res = {}) => {
329
- const { errCode } = res || {};
330
- // 已经发送成功,则将本次发送的数据从数据队列中删除
331
- if (errCode === 0) {
332
- ReportDataQueue.splice(0, cacheReportData.length);
333
- reportAnalytics(cacheReportData); // 将数据上报至小程序后台
334
- }
335
- });
336
- };
337
-
338
- /**
339
- * 开启轮询上报定时器
340
- * @private
341
- * @param {boolean} clearTimerAfterSend 是发送完数据后否清除定时器标识
342
- * @returns {void}
343
- */
344
- const startSendTimer = (clearTimerAfterSend) => {
345
- clearInterval(ReportTaskId);
346
- ReportTaskId = setInterval(async () => {
347
- sendData();
348
- if (clearTimerAfterSend) {
349
- clearInterval(ReportTaskId);
350
- }
351
- }, ReportInterval);
352
- };
353
-
354
- /**
355
- * 停止定时器
356
- * @private
357
- * @returns {void}
358
- */
359
- const stopSendTimer = () => {
360
- clearInterval(ReportTaskId);
361
- };
362
-
363
- /**
364
- * 发送上报数据到服务端
365
- * @private
366
- * @param {boolean} reportNow 是否立即上报
367
- * @param {boolean} clearTimerAfterSend 是否发送之后清除定时器
368
- * @returns {void}
369
- */
370
- const report = (reportNow = false, clearTimerAfterSend = false) => {
371
- if (reportNow) {
372
- if (ReportDataQueue.length > 0) {
373
- sendData();
374
- }
375
- if (!clearTimerAfterSend) {
376
- startSendTimer(clearTimerAfterSend);
377
- } else {
378
- stopSendTimer();
379
- }
380
- } else {
381
- startSendTimer(clearTimerAfterSend);
382
- }
383
- };
384
-
385
- /**
386
- * 更新小程序onShow参数
387
- * @private
388
- * @param {object} options 小程序onShow参数
389
- * @returns {void}
390
- */
391
- const setAppShowOptions = (options) => {
392
- appShowOptions = options;
393
- };
394
-
395
- /**
396
- * init之后再监听
397
- */
398
- const startListenApp = () => {
399
- const doAppShow = (options) => {
400
- // 更新onShow参数
401
- setAppShowOptions(options);
402
- // 上报数据
403
- report(true);
404
- };
405
-
406
- // 避免vendor分包加载太慢而错过onAppShow
407
- if (wx.tmsAppShowOptions) {
408
- doAppShow(wx.tmsAppShowOptions);
409
- wx.tmsAppShowOptions = null;
410
- }
411
-
412
- /**
413
- * 监听小程序onShow事件
414
- * @private
415
- */
416
- wx.onAppShow((options) => {
417
- doAppShow(options);
418
- });
419
-
420
- /**
421
- * 监听小程序onHide事件
422
- * @private
423
- */
424
- wx.onAppHide(() => {
425
- // onHide时立即上报,并清除定时器
426
- report(true, true);
427
- });
428
- };
429
-
430
- export {
431
- cache,
432
- startListenApp,
433
- };