gio-miniprogram-sdk 4.0.1 → 4.2.0

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.
Files changed (76) hide show
  1. package/dist/alipay.js +1 -1
  2. package/dist/baidu.js +1 -1
  3. package/dist/bytedance.js +1 -1
  4. package/dist/jingdong.js +1 -1
  5. package/dist/kuaishou.js +1 -1
  6. package/dist/plugins/gioABTest.js +1 -1
  7. package/dist/plugins/gioCompress.js +1 -1
  8. package/dist/plugins/gioEventAutoTracking.js +1 -1
  9. package/dist/plugins/gioImpressionTracking.js +1 -1
  10. package/dist/plugins/gioMultipleInstances.js +1 -0
  11. package/dist/qq.js +1 -1
  12. package/dist/quickapp.js +1 -1
  13. package/dist/taobao.js +1 -1
  14. package/dist/taro.js +1 -1
  15. package/dist/uniapp.js +1 -1
  16. package/dist/wechat.js +1 -1
  17. package/package.json +39 -42
  18. package/rollup.config.js +11 -6
  19. package/rollup.plugins.config.js +18 -10
  20. package/src/{pluginsOut/frameworks → adapters}/gioTaobaoAdapter.ts +49 -43
  21. package/src/{pluginsOut/frameworks → adapters}/gioTaroAdapter.ts +6 -4
  22. package/src/{pluginsOut/frameworks → adapters}/gioUniAppAdapter.ts +37 -6
  23. package/src/constants/config.ts +48 -18
  24. package/src/constants/emitMsg.ts +23 -0
  25. package/src/constants/regex.ts +0 -1
  26. package/src/core/dataStore/eventContextBuilder.ts +54 -25
  27. package/src/core/dataStore/eventHooks/appEffects.ts +50 -31
  28. package/src/core/dataStore/eventHooks/index.ts +13 -6
  29. package/src/core/dataStore/eventHooks/minipPage.ts +41 -13
  30. package/src/core/dataStore/eventHooks/pageEffects.ts +54 -31
  31. package/src/core/dataStore/index.ts +287 -205
  32. package/src/core/growingIO.ts +370 -180
  33. package/src/core/pluginsInstance/base.ts +157 -0
  34. package/src/core/pluginsInstance/taro.ts +15 -0
  35. package/src/core/pluginsInstance/tb.ts +15 -0
  36. package/src/core/pluginsInstance/uniapp.ts +15 -0
  37. package/src/core/uploader.ts +131 -82
  38. package/src/core/userStore.ts +160 -121
  39. package/src/index.ts +37 -28
  40. package/src/minipInstance/base.ts +2 -1
  41. package/src/minipInstance/quickapp.ts +5 -19
  42. package/src/platformConfig/base.ts +0 -5
  43. package/src/{pluginsOut/ordinary → plugins}/gioABTest.ts +88 -44
  44. package/src/{pluginsOut/ordinary → plugins}/gioCompress.ts +2 -0
  45. package/src/{pluginsOut/ordinary → plugins}/gioEventAutoTracking.ts +45 -21
  46. package/src/{pluginsOut/ordinary → plugins}/gioImpressionTracking.ts +67 -23
  47. package/src/plugins/gioMultipleInstances.ts +187 -0
  48. package/src/types/base.ts +10 -3
  49. package/src/types/dataStore.ts +35 -16
  50. package/src/types/eventHooks.ts +8 -6
  51. package/src/types/growingIO.ts +74 -39
  52. package/src/types/platforms.ts +0 -4
  53. package/src/types/plugins.ts +1 -1
  54. package/src/types/uploader.ts +16 -10
  55. package/src/types/userStore.ts +21 -17
  56. package/src/utils/glodash.ts +10 -3
  57. package/src/utils/tools.ts +31 -6
  58. package/src/utils/verifiers.ts +25 -50
  59. package/typings/global.d.ts +5 -0
  60. package/.babelrc +0 -4
  61. package/bin/build/full.sh +0 -32
  62. package/bin/build/jd.sh +0 -14
  63. package/bin/build/ks.sh +0 -14
  64. package/bin/build/my.sh +0 -14
  65. package/bin/build/plugins.sh +0 -5
  66. package/bin/build/qq.sh +0 -14
  67. package/bin/build/quickapp.sh +0 -14
  68. package/bin/build/swan.sh +0 -14
  69. package/bin/build/taro.sh +0 -18
  70. package/bin/build/tb.sh +0 -18
  71. package/bin/build/tt.sh +0 -14
  72. package/bin/build/uniapp.sh +0 -18
  73. package/bin/build/wx.sh +0 -14
  74. package/src/core/plugins.ts +0 -152
  75. package/src/pluginsOut/ordinary/gioCustomTracking.ts +0 -71
  76. package/bin/{build/all.sh → build.sh} +3 -3
@@ -1,4 +1,5 @@
1
1
  import {
2
+ forEach,
2
3
  isArray,
3
4
  isEmpty,
4
5
  isFunction,
@@ -12,12 +13,14 @@ import {
12
13
  callError,
13
14
  consoleText,
14
15
  eventNameValidate,
15
- getAppInst,
16
+ getDynamicAttributes,
17
+ getGlobal,
16
18
  getPlainPlatform,
17
19
  guid,
18
- limitObject
20
+ limitObject,
21
+ niceCallback
19
22
  } from '@@/utils/tools';
20
- import { ALLOWED_MODIFY_OPTIONS } from '@@/constants/config';
23
+ import { ALLOW_SET_OPTIONS, DEFAULT_SETTINGS } from '@@/constants/config';
21
24
  import { DataStoreType, OriginOptions } from '@@/types/dataStore';
22
25
  import { FRAMEWORKS, PLATFORMTYPES } from '@@/types/platforms';
23
26
  import { GrowingIOType } from '@@/types/growingIO';
@@ -32,10 +35,11 @@ import DataStore from './dataStore';
32
35
  import mitt from 'mitt';
33
36
  import platformConfig from '__GIO_PLATFORM_CONFIG__';
34
37
  import PlatformInstance from '__GIO_PLATFORM_INSTANCE__';
35
- import Plugins from '@@/core/plugins';
38
+ import Plugins from '__GIO_PLUGIN_INSTANCE__';
36
39
  import qs from 'querystringify';
37
40
  import Uploader from './uploader';
38
41
  import UserStore from '@@/core/userStore';
42
+ import EMIT_MSG from '@@/constants/emitMsg';
39
43
 
40
44
  // SDK全局参数 版本号、小程序平台、框架名称、运行环境 均由打包工具替换写入
41
45
  const sdkVersion: any = '__SDK_VERSION__';
@@ -58,7 +62,9 @@ class GrowingIO implements GrowingIOType {
58
62
  public dataStore: DataStoreType;
59
63
  public plugins: PluginsType;
60
64
  public uploader: UploaderType;
61
-
65
+ // 当前主SDK的trackingId,用于区分事件归属。
66
+ public trackingId: string;
67
+ public subInstance: any;
62
68
  constructor() {
63
69
  this.utils = { ...glodash, ...tools, qs };
64
70
  this.emitter = mitt();
@@ -94,84 +100,186 @@ class GrowingIO implements GrowingIOType {
94
100
  this.dataStore = new DataStore(this);
95
101
  // 用户实例
96
102
  this.userStore = new UserStore(this);
103
+ // 初始化请求逻辑
104
+ this.uploader = new Uploader(this);
97
105
  // 插件管理实例
98
106
  this.plugins = new Plugins(this);
99
107
  // 加载内置插件
100
108
  this.plugins.innerPluginInit();
101
- // 数据管理实例和上传实例均在各自环境中继承实现
102
109
  }
103
110
 
111
+ // SDK初始化方法
112
+ init = (options: any) => {
113
+ // 初始化实例之前
114
+ this.emitter.emit(EMIT_MSG.ON_SDK_INITIALIZE_BEFORE, { options });
115
+ try {
116
+ consoleText('Gio小程序SDK 初始化中...', 'info');
117
+ if (
118
+ options.trackingId &&
119
+ this.dataStore.initializedTrackingIds.includes(options.trackingId)
120
+ ) {
121
+ throw new Error(`已存在实例 ${options.trackingId},请勿重复初始化!`);
122
+ }
123
+ if (this.trackingId && !this.plugins.gioMultipleInstances) {
124
+ throw new Error('您正在尝试初始化另一个实例,请集成多实例插件后重试!');
125
+ }
126
+ // 初始化实例配置
127
+ const vdsConfig = this.dataStore.initTrackerOptions(options);
128
+ // 广播基础配置初始化完成
129
+ this.emitter?.emit(EMIT_MSG.OPTION_INITIALIZED, vdsConfig);
130
+ // 标记SDK已初始化完成
131
+ this.gioSDKInitialized = true;
132
+ // 初始化实例hooker
133
+ this.dataStore.initTrackerHooker(vdsConfig);
134
+ // 手动初始化用户信息
135
+ if (this.gioPlatform !== 'quickapp') {
136
+ this.userStore.initUserInfo(options.trackingId);
137
+ }
138
+ // 广播SDK初始化完成
139
+ this.emitter.emit(EMIT_MSG.SDK_INITIALIZED, vdsConfig);
140
+ // 初始化完成后把实例注入到小程序全局
141
+ if (!this.trackingId || this.trackingId === options.trackingId) {
142
+ // ?直接挂载对象,存在问题:Page.data 触发深拷贝 存在循环引用会导致死循环
143
+ getGlobal().__gio__ = () => this;
144
+ }
145
+ consoleText('Gio小程序SDK 初始化完成!', 'success');
146
+ // 在插件中手动触发visit
147
+ if (this.inPlugin && this.minipInstance.platform !== 'tbp') {
148
+ this.dataStore.sendVisit(vdsConfig.trackingId);
149
+ }
150
+ if (this.vdsConfig.forceLogin) {
151
+ consoleText(
152
+ 'forceLogin已开启,请调用 identify 方法设置 openId 以继续上报!',
153
+ 'info'
154
+ );
155
+ }
156
+ return true;
157
+ } catch (error) {
158
+ consoleText(error, 'error');
159
+ return false;
160
+ }
161
+ };
162
+
163
+ // 需要校验实例的方法执行
164
+ handlerDistribute = (trackingId: string, handler: string, args: any) => {
165
+ const { initializedTrackingIds, getTrackerVds } = this.dataStore;
166
+ const trakerVds = getTrackerVds(trackingId);
167
+ if (trakerVds && initializedTrackingIds.includes(trackingId)) {
168
+ return this[handler](trackingId, ...args);
169
+ } else {
170
+ consoleText(`不存在实例:${trackingId},请检查!`, 'warn');
171
+ }
172
+ };
173
+
104
174
  // 手动注册插件
105
175
  registerPlugins = (plugins: any) => {
106
176
  this.plugins.pluginItems = [...this.plugins.pluginItems, ...plugins];
107
177
  this.plugins.installAll(plugins);
108
178
  };
109
179
 
110
- // SDK初始化方法
111
- init = (args: any) => {
112
- consoleText('Gio小程序SDK 初始化中...', 'info');
113
- // 初始化全局配置
114
- this.dataStore.initOptions(args);
115
- // 广播基础配置初始化完成
116
- this.emitter?.emit('OPTION_INITIALIZED', this);
117
- // 标记SDK已初始化完成
118
- this.gioSDKInitialized = true;
119
- // 初始化完成后把实例注入到小程序全局
120
- // ?注意不要轻易修改实例挂载的地方(这里指app),会影响分包插件和运营SDK的实例取值
121
- if (!this.vdsConfig.subpackage) {
122
- const app = getAppInst();
123
- // 直接挂载对象,存在问题:Page.data 触发深拷贝 存在循环引用会导致死循环
124
- app.__gio__ = () => {
125
- return this;
126
- };
127
- }
128
- // 初始化请求逻辑
129
- this.uploader = new Uploader(this);
130
- // 没有开启IDMapping的时候要把userKey清掉,防止之前有数被带到上报数据里
131
- if (!this.vdsConfig.idMapping) {
132
- this.userStore.userKey = '';
133
- }
134
- // 广播SDK初始化完成
135
- this.emitter.emit('SDK_INITIALIZED', this);
136
- consoleText('Gio小程序SDK 初始化完成!', 'success');
137
- if (this.vdsConfig.forceLogin) {
138
- consoleText(
139
- 'forceLogin已开启,请调用 identify 方法设置 openId 以继续上报!',
140
- 'info'
141
- );
142
- }
143
- // 在插件中手动触发visit
144
- if (this.inPlugin && this.minipInstance.platform !== 'tbp') {
145
- this.dataStore.sendVisit();
146
- }
147
- return true;
148
- };
180
+ // 获取所有已注册插件
181
+ getPlugins = () => this.plugins.pluginItems;
182
+
183
+ // 获取设备ID(匿名用户ID)
184
+ getDeviceId = () => this.userStore.getUid();
149
185
 
150
186
  // 运行中获取配置
151
- getOption = (k?: string) => this.dataStore.getOption(k);
187
+ getOption = (trackingId: string, k?: string) =>
188
+ this.dataStore.getOption(trackingId, k);
152
189
 
153
190
  // 运行中修改配置
154
- setOption = (k: string, v: any) => {
155
- if (keys(ALLOWED_MODIFY_OPTIONS).includes(k)) {
156
- const r = this.dataStore.setOption(k, v);
157
- if (r && ALLOWED_MODIFY_OPTIONS[k]) {
158
- consoleText(
159
- `已${v ? '开启' : '关闭'}${ALLOWED_MODIFY_OPTIONS[k]}`,
160
- 'info'
161
- );
191
+ setOption = (trackingId: string, k: string, v: any) => {
192
+ if (keys(ALLOW_SET_OPTIONS).includes(k)) {
193
+ if (typeof v === DEFAULT_SETTINGS[k]?.type) {
194
+ this.dataStore.setOption(trackingId, k, v);
195
+ consoleText(`已修改${ALLOW_SET_OPTIONS[k]}: ${v}`, 'info');
196
+ return true;
197
+ } else {
198
+ consoleText(`参数格式不正确:${v},请检查后重试!`, 'error');
199
+ return false;
162
200
  }
163
- return r;
164
201
  } else {
165
- consoleText(`不存在可修改的配置项:${k},请检查后重试!`, 'warn');
202
+ consoleText(`不存在可修改的配置项:${k},请检查后重试!`, 'error');
166
203
  return false;
167
204
  }
168
205
  };
169
206
 
170
- // 获取设备ID(匿名用户ID)
171
- getDeviceId = () => this.userStore.uid;
207
+ // 设置页面属性
208
+ setPageAttributes = (trackingId: string, properties: any) => {
209
+ if (isObject(properties) && !isEmpty(properties)) {
210
+ const { currentPage } = this.dataStore.eventHooks;
211
+ if (isEmpty(currentPage.pageProps[trackingId])) {
212
+ currentPage.pageProps[trackingId] = {};
213
+ }
214
+ if (isEmpty(currentPage.pageProps[trackingId][currentPage.path])) {
215
+ currentPage.pageProps[trackingId][currentPage.path] = {};
216
+ }
217
+ // 结果合并,动态属性不作处理
218
+ if (!isEmpty(properties)) {
219
+ forEach(properties, (v, k) => {
220
+ if (
221
+ !isFunction(
222
+ currentPage.pageProps[trackingId][currentPage.path][k]
223
+ ) ||
224
+ isFunction(v)
225
+ ) {
226
+ currentPage.pageProps[trackingId][currentPage.path][k] = v;
227
+ }
228
+ });
229
+ }
230
+ return true;
231
+ } else {
232
+ callError('setPageAttributes');
233
+ return false;
234
+ }
235
+ };
236
+
237
+ // 清空已设置的页面属性
238
+ clearPageAttributes = (
239
+ trackingId: string,
240
+ properties: string[] | undefined
241
+ ) => {
242
+ try {
243
+ if (
244
+ !isEmpty(this.dataStore.eventHooks.currentPage.pageProps[trackingId])
245
+ ) {
246
+ const { currentPage } = this.dataStore.eventHooks;
247
+ if (isArray(properties) && !isEmpty(properties)) {
248
+ properties.forEach((propName: string) => {
249
+ unset(
250
+ currentPage.pageProps[trackingId][currentPage.path],
251
+ propName
252
+ );
253
+ });
254
+ } else {
255
+ currentPage.pageProps[trackingId][currentPage.path] = {};
256
+ }
257
+ }
258
+ } catch (error) {
259
+ callError('clearPageAttributes');
260
+ return false;
261
+ }
262
+ };
263
+
264
+ // 手动发page
265
+ sendPage = (trackingId: string, props?: any) => {
266
+ const { trackPage } = this.dataStore.getTrackerVds(trackingId);
267
+ if (!trackPage) {
268
+ this.dataStore.sendPage(trackingId, props);
269
+ } else {
270
+ consoleText(
271
+ '仅在关闭trackPage时允许调用sendPage,请确认后修改初始化配置项!',
272
+ 'error'
273
+ );
274
+ }
275
+ };
172
276
 
173
277
  // 设置设备ID,一般为openId
174
- identify = (assignmentId: string | number) => {
278
+ identify = (trackingId: string, assignmentId: string | number) => {
279
+ if (trackingId !== this.trackingId) {
280
+ callError('identify', !1, '子实例不允许调用identify');
281
+ return;
282
+ }
175
283
  if (this.vdsConfig.forceLogin) {
176
284
  if (!verifyId(assignmentId)) {
177
285
  callError('identify');
@@ -180,33 +288,66 @@ class GrowingIO implements GrowingIOType {
180
288
  // 截取长度
181
289
  const asId = toString(assignmentId).slice(0, 1000);
182
290
  // 在之后的请求中使用assignmentId作为uid(deviceId)使用
183
- this.userStore.uid = asId;
291
+ this.userStore.setUid(asId);
292
+ // 修改forceLogin配置项
293
+ this.dataStore.setOption(this.trackingId, 'forceLogin', false);
184
294
  // 为已积压的请求使用assignmentId全部赋值deviceId
185
- this.uploader.hoardingQueue.forEach(
186
- (o, i) => (this.uploader.hoardingQueue[i].deviceId = asId)
187
- );
188
- this.dataStore.setOption('forceLogin', false);
189
- // 发送积压队列中的请求
190
- this.uploader.initiateRequest(true);
295
+ this.dataStore.trackersExecute((tid: string) => {
296
+ const hq = this.uploader.getHoardingQueue(tid);
297
+ hq.forEach((o, i) => (hq[i].deviceId = asId));
298
+ // 发送积压队列中的请求
299
+ this.uploader.initiateRequest(tid, true);
300
+ });
191
301
  } else {
192
302
  callError('identify', !1, 'forceLogin未开启');
193
303
  }
194
304
  };
195
305
 
306
+ // 发送用户属性
307
+ setUserAttributes = (trackingId: string, userAttributes: any) => {
308
+ if (isObject(userAttributes) && !isEmpty(userAttributes)) {
309
+ const { eventContextBuilder, eventInterceptor, lastVisitEvent } =
310
+ this.dataStore;
311
+ const event = {
312
+ eventType: 'LOGIN_USER_ATTRIBUTES',
313
+ ...eventContextBuilder(trackingId)
314
+ };
315
+ event.attributes = limitObject({
316
+ ...(event.attributes ?? {}),
317
+ ...getDynamicAttributes(userAttributes)
318
+ });
319
+ // 该方法很可能被放在在app的onShow中的wx.login异步回调中,
320
+ // 此时页面的path和query可能还没取到,就拿visit事件的path和query补上
321
+ if (!event.path) {
322
+ event.path = lastVisitEvent.path;
323
+ event.query = lastVisitEvent.query;
324
+ event.title = lastVisitEvent.title;
325
+ }
326
+ eventInterceptor(event);
327
+ } else {
328
+ callError('setUserAttributes');
329
+ }
330
+ };
331
+
196
332
  // 设置登录用户Id
197
- setUserId = (userId: string | number, userKey?: string) => {
333
+ setUserId = (
334
+ trackingId: string,
335
+ userId: string | number,
336
+ userKey?: string
337
+ ) => {
198
338
  if (verifyId(userId)) {
199
339
  // 切换userId要重设session补发visit
200
- const prevId = this.userStore.gioId;
340
+ const prevId = this.userStore.getGioId(trackingId);
201
341
  userId = toString(userId).slice(0, 1000);
202
342
  userKey = toString(userKey).slice(0, 1000);
203
- this.userStore.userId = userId;
204
- if (this.vdsConfig.idMapping) {
205
- this.userStore.userKey = isNil(userKey) ? '' : userKey;
343
+ this.userStore.setUserId(trackingId, userId);
344
+ const { idMapping } = this.dataStore.getTrackerVds(trackingId);
345
+ if (idMapping) {
346
+ this.userStore.setUserKey(trackingId, isNil(userKey) ? '' : userKey);
206
347
  }
207
348
  // 切换userId时重置session并补发visit
208
349
  if (prevId && prevId !== userId) {
209
- this.userStore.sessionId = '';
350
+ this.userStore.setSessionId(trackingId);
210
351
  const { path, query, settedTitle } =
211
352
  this.dataStore.eventHooks.currentPage;
212
353
  // 重新获取页面title,防止切换配置前修改了title没取到
@@ -214,7 +355,7 @@ class GrowingIO implements GrowingIOType {
214
355
  settedTitle[path] ||
215
356
  this.minipInstance.getPageTitle(this.minipInstance.getCurrentPage());
216
357
  // visit取当前页面值
217
- this.dataStore.sendVisit({ path, query });
358
+ this.dataStore.sendVisit(trackingId, { path, query });
218
359
  }
219
360
  } else {
220
361
  callError('setUserId');
@@ -222,65 +363,127 @@ class GrowingIO implements GrowingIOType {
222
363
  };
223
364
 
224
365
  // 清除登录用户Id
225
- clearUserId = () => {
226
- this.userStore.userId = undefined;
227
- this.userStore.userKey = undefined;
366
+ clearUserId = (trackingId: string) => {
367
+ this.userStore.setUserId(trackingId, undefined);
368
+ this.userStore.setUserKey(trackingId, undefined);
228
369
  };
229
370
 
230
- // 发送用户属性
231
- setUserAttributes = (userAttributes: any) => {
232
- if (!isEmpty(userAttributes) && isObject(userAttributes)) {
233
- this.plugins?.gioCustomTracking?.buildUserAttributesEvent(userAttributes);
371
+ // 设置全局通用属性(即每个事件都会带上的属性值)
372
+ setGeneralProps = (trackingId: string, properties: any) => {
373
+ if (isObject(properties) && !isEmpty(properties)) {
374
+ if (isEmpty(this.dataStore.generalProps[trackingId])) {
375
+ this.dataStore.generalProps[trackingId] = {};
376
+ }
377
+ forEach(properties, (v, k) => {
378
+ if (
379
+ !isFunction(this.dataStore.generalProps[trackingId][k]) ||
380
+ isFunction(v)
381
+ ) {
382
+ this.dataStore.generalProps[trackingId][k] = v;
383
+ }
384
+ });
385
+ return true;
234
386
  } else {
235
- callError('setUserAttributes');
387
+ callError('setGeneralProps');
388
+ return false;
389
+ }
390
+ };
391
+
392
+ // 清空已设置的全局通用属性
393
+ clearGeneralProps = (
394
+ trackingId: string,
395
+ properties: string[] | undefined
396
+ ) => {
397
+ try {
398
+ if (!isEmpty(this.dataStore.generalProps[trackingId])) {
399
+ if (isArray(properties) && !isEmpty(properties)) {
400
+ properties.forEach((propName: string) => {
401
+ unset(this.dataStore.generalProps[trackingId], propName);
402
+ });
403
+ } else {
404
+ this.dataStore.generalProps[trackingId] = {};
405
+ }
406
+ }
407
+ } catch (error) {
408
+ callError('clearGeneralProps');
409
+ return false;
236
410
  }
237
411
  };
238
412
 
239
413
  // 自定义埋点事件
240
- track = (name: string, properties: { [key: string]: string | string[] }) => {
241
- const customEvent =
242
- this.plugins?.gioCustomTracking?.buildCustomEvent || function () {};
243
- customEvent(name, {
244
- ...this.dataStore.generalProps,
245
- ...(isObject(properties) && !isEmpty(properties) ? properties : {})
414
+ track = (
415
+ trackingId: string,
416
+ eventName: string,
417
+ properties: { [key: string]: string | string[] }
418
+ ) => {
419
+ eventNameValidate(eventName, () => {
420
+ const { eventContextBuilder, eventInterceptor } = this.dataStore;
421
+ const event = {
422
+ eventType: 'CUSTOM',
423
+ eventName,
424
+ ...eventContextBuilder(trackingId)
425
+ };
426
+ event.attributes = limitObject({
427
+ ...(event.attributes ?? {}),
428
+ ...getDynamicAttributes({
429
+ ...(isObject(properties) && !isEmpty(properties) ? properties : {})
430
+ })
431
+ });
432
+ // 埋点事件要保留'&&sendTo'字段用于多实例复制发送
433
+ if (
434
+ this.plugins.gioMultipleInstances &&
435
+ properties &&
436
+ properties['&&sendTo']
437
+ ) {
438
+ event['&&sendTo'] = properties['&&sendTo'];
439
+ }
440
+ eventInterceptor(event);
246
441
  });
247
442
  };
248
443
 
249
444
  // 初始化事件计时器
250
- trackTimerStart = (eventName: string) => {
251
- if (this.vdsConfig.dataCollect) {
445
+ trackTimerStart = (trackingId: string, eventName: string) => {
446
+ const { dataCollect } = this.dataStore.getTrackerVds(trackingId);
447
+ if (dataCollect) {
252
448
  return eventNameValidate(eventName, () => {
253
449
  const timerId = guid();
254
- this.dataStore.trackTimers[timerId] = {
450
+ if (!this.dataStore.trackTimers[trackingId]) {
451
+ this.dataStore.trackTimers[trackingId] = {};
452
+ }
453
+ this.dataStore.trackTimers[trackingId][timerId] = {
255
454
  eventName,
256
455
  leng: 0,
257
456
  start: +Date.now()
258
457
  };
259
458
  return timerId;
260
459
  });
460
+ } else {
461
+ consoleText('指定实例未开启数据采集,请检查!', 'error');
261
462
  }
262
463
  return false;
263
464
  };
264
465
 
265
466
  // 暂停事件计时器
266
- trackTimerPause = (timerId: string) => {
267
- if (timerId && this.dataStore.trackTimers[timerId]) {
268
- const tracker = this.dataStore.trackTimers[timerId];
269
- if (tracker.start) {
270
- tracker.leng = tracker.leng + (+Date.now() - tracker.start);
467
+ trackTimerPause = (trackingId: string, timerId: string) => {
468
+ const timers = this.dataStore.trackTimers[trackingId];
469
+ if (timerId && timers && timers[timerId]) {
470
+ const timer = timers[timerId];
471
+ if (timer.start) {
472
+ timer.leng = timer.leng + (+Date.now() - timer.start);
271
473
  }
272
- tracker.start = 0;
474
+ timer.start = 0;
273
475
  return true;
274
476
  }
275
477
  return false;
276
478
  };
277
479
 
278
480
  // 恢复事件计时器
279
- trackTimerResume = (timerId: string) => {
280
- if (timerId && this.dataStore.trackTimers[timerId]) {
281
- const tracker = this.dataStore.trackTimers[timerId];
282
- if (tracker.start === 0) {
283
- tracker.start = +Date.now();
481
+ trackTimerResume = (trackingId: string, timerId: string) => {
482
+ const timers = this.dataStore.trackTimers[trackingId];
483
+ if (timerId && timers && timers[timerId]) {
484
+ const timer = timers[timerId];
485
+ if (timer.start === 0) {
486
+ timer.start = +Date.now();
284
487
  }
285
488
  return true;
286
489
  }
@@ -288,114 +491,101 @@ class GrowingIO implements GrowingIOType {
288
491
  };
289
492
 
290
493
  // 停止事件计时器并上报事件
291
- trackTimerEnd = (timerId: string, attributes: any) => {
292
- if (this.vdsConfig.dataCollect) {
293
- const maxEnd = 60 * 60 * 24 * 1000;
294
- if (timerId && this.dataStore.trackTimers[timerId]) {
295
- const tracker = this.dataStore.trackTimers[timerId];
296
- if (tracker.start !== 0) {
297
- const shortCut = +Date.now() - tracker.start;
298
- tracker.leng = shortCut > 0 ? tracker.leng + shortCut : 0;
494
+ trackTimerEnd = (trackingId: string, timerId: string, properties: any) => {
495
+ const { dataCollect } = this.dataStore.getTrackerVds(trackingId);
496
+ const timers = this.dataStore.trackTimers[trackingId];
497
+ if (timerId && timers && timers[timerId]) {
498
+ if (dataCollect) {
499
+ const maxEnd = 60 * 60 * 24 * 1000;
500
+ const timer = timers[timerId];
501
+ if (timer.start !== 0) {
502
+ const shortCut = +Date.now() - timer.start;
503
+ timer.leng = shortCut > 0 ? timer.leng + shortCut : 0;
299
504
  }
300
- // 要直接构建custom事件,不要去调用埋点插件的方法,万一插件没有加载就发不出去了
301
505
  const { eventContextBuilder, eventInterceptor } = this.dataStore;
302
506
  const event = {
303
507
  eventType: 'CUSTOM',
304
- eventName: tracker.eventName,
305
- attributes: limitObject({
306
- ...attributes,
307
- event_duration: tracker.leng > maxEnd ? 0 : tracker.leng / 1000
308
- }),
309
- ...eventContextBuilder()
508
+ eventName: timer.eventName,
509
+ ...eventContextBuilder(trackingId)
310
510
  };
511
+ event.attributes = limitObject({
512
+ ...(event.attributes ?? {}),
513
+ ...getDynamicAttributes({
514
+ ...(isObject(properties) && !isEmpty(properties) ? properties : {}),
515
+ event_duration: timer.leng > maxEnd ? 0 : timer.leng / 1000
516
+ })
517
+ });
518
+ // 埋点事件要保留'&&sendTo'字段用于多实例复制发送
519
+ if (
520
+ this.plugins.gioMultipleInstances &&
521
+ properties &&
522
+ properties['&&sendTo']
523
+ ) {
524
+ event['&&sendTo'] = properties['&&sendTo'];
525
+ }
311
526
  eventInterceptor(event);
312
- this.removeTimer(timerId);
313
527
  return true;
314
528
  } else {
315
- consoleText('未查找到对应的计时器,请检查!', 'error');
529
+ consoleText('指定实例未开启数据采集,计时器已移除,请检查!', 'error');
316
530
  return false;
317
531
  }
532
+ this.removeTimer(trackingId, timerId);
533
+ } else {
534
+ consoleText('未查询到指定计时器,请检查!', 'error');
535
+ return false;
318
536
  }
319
- return false;
320
537
  };
321
538
 
322
539
  // 移除事件计时器
323
- removeTimer = (timerId: string) => {
324
- if (timerId && this.dataStore.trackTimers[timerId]) {
325
- delete this.dataStore.trackTimers[timerId];
540
+ removeTimer = (trackingId: string, timerId: string) => {
541
+ const timers = this.dataStore.trackTimers[trackingId];
542
+ if (timerId && timers && timers[timerId]) {
543
+ delete timers[timerId];
326
544
  return true;
545
+ } else {
546
+ consoleText('未查询到指定计时器,请检查!', 'error');
547
+ return false;
327
548
  }
328
- return false;
329
549
  };
330
550
 
331
551
  // 清除所有事件计时器
332
- clearTrackTimer = () => {
333
- this.dataStore.trackTimers = {};
334
- };
335
-
336
- // 设置埋点事件的通用属性(即每个埋点事件都会带上的属性值)
337
- setGeneralProps = (properties: any) => {
338
- if (isObject(properties) && !isEmpty(properties)) {
339
- this.dataStore.generalProps = {
340
- ...this.dataStore.generalProps,
341
- ...properties
342
- };
343
- keys(this.dataStore.generalProps).forEach((k: string) => {
344
- if ([undefined, null].includes(this.dataStore.generalProps[k])) {
345
- this.dataStore.generalProps[k] = '';
346
- }
347
- });
348
- } else {
349
- callError('setGeneralProps');
350
- }
552
+ clearTrackTimer = (trackingId: string) => {
553
+ this.dataStore.trackTimers[trackingId] = {};
351
554
  };
352
555
 
353
- // 清空已设置的埋点事件的通用属性
354
- clearGeneralProps = (properties: string[] | undefined) => {
355
- if (isArray(properties) && !isEmpty(properties)) {
356
- properties.forEach((prop: string) => {
357
- unset(this.dataStore.generalProps, prop);
358
- });
359
- } else {
360
- this.dataStore.generalProps = {};
556
+ // 手动获取位置信息补发visit上报
557
+ setLocation = (trackingId: string, latitude: number, longitude: number) => {
558
+ if (isEmpty(this.dataStore.locationData[trackingId])) {
559
+ this.dataStore.locationData[trackingId] = {};
361
560
  }
362
- };
363
-
364
- // 设置页面属性
365
- setPageAttributes = (properties: any) => {
366
- const { currentPage } = this.dataStore.eventHooks;
367
- // 这个currentPath一般认为是在onLoad中获取的当前小程序页面的path,此时SDK还没有parse,保存的还是上一个页面的信息
368
- const currentPath = this.minipInstance.getCurrentPath();
369
- if (['onLoad', 'attached'].includes(currentPage.currentLifecycle)) {
370
- currentPage.pageProps[currentPath] = limitObject(properties);
561
+ const ld = this.dataStore.locationData[trackingId];
562
+ const verifyLT = (o: number) => o >= -180 && o <= 180;
563
+ if (
564
+ verifyLT(latitude) &&
565
+ verifyLT(longitude) &&
566
+ (ld.latitude !== latitude || ld.longitude !== longitude)
567
+ ) {
568
+ this.dataStore.locationData[trackingId] = { latitude, longitude };
371
569
  }
372
570
  };
373
571
 
374
572
  // 手动更新曝光监听(该方法会在曝光插件加载时被重写)
375
573
  updateImpression = () => {
376
574
  consoleText(
377
- '当前未集成半自动浏览插件,请集成插件后再调用 updateImpression!',
378
- 'warn'
575
+ 'updateImpression 错误! 请集成半自动埋点浏览插件后重试!',
576
+ 'error'
379
577
  );
380
578
  };
381
579
 
382
- // 手动获取位置信息补发visit上报
383
- setLocation = (latitude: number, longitude: number) => {
384
- const nld = { latitude, longitude };
385
- const ld = this.dataStore.locationData;
386
- const verifyLT = (o: number) => o >= -180 && o <= 180;
387
- if (
388
- verifyLT(latitude) &&
389
- verifyLT(longitude) &&
390
- (ld.latitude !== latitude || ld.longitude !== longitude)
391
- ) {
392
- this.dataStore.locationData = nld;
393
- }
580
+ // 获取A/B实验数据
581
+ getABTest = (trackingId: string, layerId: string | number, callback: any) => {
582
+ consoleText('获取ABTest数据错误! 请集成ABTest插件后重试!', 'error');
583
+ niceCallback(callback, {});
394
584
  };
395
585
 
396
586
  // 获取打通信息
397
- getGioInfo = () => {
398
- const { uid, userId, userKey, sessionId } = this.userStore;
587
+ getGioInfo = (trackingId: string) => {
588
+ const { getUid, getUserId, getUserKey, getSessionId } = this.userStore;
399
589
  const {
400
590
  projectId,
401
591
  appId,
@@ -404,19 +594,19 @@ class GrowingIO implements GrowingIOType {
404
594
  idMapping,
405
595
  extraParams,
406
596
  ignoreFields
407
- } = this.vdsConfig;
597
+ } = this.dataStore.getTrackerVds(trackingId);
408
598
  const infos: any = {
409
599
  gioappid: appId,
410
- giocs1: userId || '',
600
+ giocs1: getUserId(trackingId) || '',
411
601
  gioplatform: this.platformConfig.platform,
412
602
  gioprojectid: projectId,
413
- gios: sessionId,
414
- giou: uid
603
+ gios: getSessionId(trackingId),
604
+ giou: getUid()
415
605
  };
416
606
  infos.giodatacollect = dataCollect;
417
607
  infos.giodatasourceid = dataSourceId;
418
608
  if (idMapping) {
419
- infos.giouserkey = userKey;
609
+ infos.giouserkey = getUserKey(trackingId);
420
610
  }
421
611
  if (!isEmpty(extraParams)) {
422
612
  extraParams.forEach((o) => {