@chiyou/minigame-framework 1.4.0 → 1.4.2

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.
@@ -3,10 +3,10 @@ import { LogUtils } from "../Utils/LogUtils";
3
3
  import { ServiceLocator } from "../Utils/ServiceLocator";
4
4
  import { FwkErrorCode } from "../Definition/FwkErrorDefinition";
5
5
  import { PlatformID } from "../Definition/SystemDefinition";
6
- import { UmengConfig, EventParams, AnalyticsEventId, AnalyticsParamKey, UmaSDK, StageEventType, StageRunningEventType, StageEndParams, StageRunningParams, CommonParams, UmaSDKStage } from "../Definition/AnalyticsDefinition";
6
+ import { UmengConfig, EventParams, AnalyticsEventId, AnalyticsParamKey, StageResult, ReviveMethod, LaunchCategory, UmaSDK, CommonParams } from "../Definition/AnalyticsDefinition";
7
7
  import { EventMgr } from "./EventMgr";
8
8
  import { FrameworkBase } from "../Definition/FrameworkBase";
9
- import { SystemMgr } from "./SystemMgr";
9
+ import type { SystemMgr } from "./SystemMgr";
10
10
 
11
11
  export class AnalyticsMgr extends BaseMgr {
12
12
  /** 单例实例 */
@@ -20,11 +20,15 @@ export class AnalyticsMgr extends BaseMgr {
20
20
  private _isEnabled: boolean = false;
21
21
  /** 当前平台对应的 SDK 实例(通过 wx.uma 全局访问) */
22
22
  private _sdk: UmaSDK = null;
23
- /** 关卡 SDK 实例 */
24
- private _sdkStage: UmaSDKStage = null;
25
23
  /** 通用属性(trackEvent 自动合并) */
26
24
  private _commonParams: CommonParams = null;
27
25
 
26
+ /** 当前会话已进入过的关卡(用于 is_first 判重) */
27
+ private _sessionEnteredStages: Set<string> = new Set();
28
+
29
+ /** 各关卡的开始时间戳(用于自动计算 duration) */
30
+ private _stageStartTimestamps: Map<string, number> = new Map();
31
+
28
32
  onLoad(): void {
29
33
  super.onLoad();
30
34
 
@@ -74,12 +78,20 @@ export class AnalyticsMgr extends BaseMgr {
74
78
 
75
79
  // 根据当前平台是否开启统计来决定是否启用
76
80
  const platformID: PlatformID = BaseMgr.Instance.getCurrentPlatformID();
77
- const enabled = umengConfig.platformEnableMap.get(platformID);
81
+ let enabled = umengConfig.platformEnableMap.get(platformID);
82
+
83
+ if (enabled) {
84
+ // 额外检查:本地游戏无网络请求能力,不上报统计
85
+ const systemMgr = ServiceLocator.Instance.get<SystemMgr>("SystemMgr");
86
+ if (systemMgr && !systemMgr.supportRequest()) {
87
+ enabled = false;
88
+ LogUtils.Instance.info(AnalyticsMgr.TAG, "本地游戏不支持网络请求,统计禁用");
89
+ }
90
+ }
78
91
 
79
92
  if (enabled) {
80
93
  // 从全局获取 SDK 实例(在 game.js 中初始化后挂载到 wx.uma)
81
94
  this._sdk = this._getGlobalSDK();
82
- this._sdkStage = this._sdk?.stage || null;
83
95
  this._isEnabled = !!this._sdk;
84
96
  } else {
85
97
  this._isEnabled = false;
@@ -139,7 +151,6 @@ export class AnalyticsMgr extends BaseMgr {
139
151
  * 后续所有 trackEvent 调用自动合并这些属性。
140
152
  * 合并规则:通用属性与自定义属性同级平铺,自定义属性可覆盖同名通用属性。
141
153
  *
142
- * 注意:仅适用于 trackEvent,不适用于 stage 系列接口。
143
154
  * 友盟 SDK 的 setSuperProperty 在小游戏环境不可用,需在框架封装层实现。
144
155
  */
145
156
  private _initCommonParams(): void {
@@ -290,175 +301,156 @@ export class AnalyticsMgr extends BaseMgr {
290
301
 
291
302
  // ==================== 游戏特定事件 ====================
292
303
 
293
- /** 广告展示 */
294
- public trackAdShow(adType: string, adId: string, extraParams?: EventParams): void {
295
- const params: Record<string, any> = {
296
- [AnalyticsParamKey.AdType]: adType,
297
- [AnalyticsParamKey.AdId]: adId,
298
- ...this._flattenParams(extraParams),
299
- };
300
- this.trackEvent(AnalyticsEventId.AdShow, params);
304
+ /**
305
+ * 关卡开始上报
306
+ * 自动判重,is_first=1 仅在该用户当前会话内首次进入该关卡时上报。
307
+ * 同时记录开始时间戳,供 stageTrackEnd 自动计算 duration。
308
+ * @param stageId 关卡ID(必传)
309
+ * @param stageName 关卡名称(必传)
310
+ */
311
+ public stageTrackStart(stageId: string, stageName: string): void {
312
+ if (!this._isEnabled) return;
313
+
314
+ const isFirst = this._sessionEnteredStages.has(stageId) ? 0 : 1;
315
+ if (isFirst === 1) {
316
+ this._sessionEnteredStages.add(stageId);
317
+ }
318
+
319
+ // 记录开始时间戳(毫秒)
320
+ this._stageStartTimestamps.set(stageId, Date.now());
321
+
322
+ this.trackEvent(AnalyticsEventId.StageStart, {
323
+ [AnalyticsParamKey.StageId]: stageId,
324
+ [AnalyticsParamKey.StageName]: stageName,
325
+ [AnalyticsParamKey.IsFirst]: isFirst,
326
+ });
301
327
  }
302
328
 
303
- /** 广告点击 */
304
- public trackAdClick(adType: string, adId: string, extraParams?: EventParams): void {
305
- const params: Record<string, any> = {
306
- [AnalyticsParamKey.AdType]: adType,
307
- [AnalyticsParamKey.AdId]: adId,
308
- ...this._flattenParams(extraParams),
309
- };
310
- this.trackEvent(AnalyticsEventId.AdClick, params);
329
+ /**
330
+ * 关卡结束上报
331
+ * 规则:只有“最终结局”才上报。复活成功后的关卡仍在进行中,不报 stage_end。
332
+ * duration 由框架自动计算(当前时间戳 - stageTrackStart 记录的时间戳)。
333
+ * @param stageId 关卡ID
334
+ * @param result 结果:success / fail / abandon
335
+ * @param progress 关卡进度(0-1),成功时=1
336
+ * @param isRevived 是否为复活后通关(0=原始,1=复活后),默认 0
337
+ */
338
+ public stageTrackEnd(
339
+ stageId: string,
340
+ result: StageResult,
341
+ progress: number,
342
+ isRevived: number = 0,
343
+ ): void {
344
+ if (!this._isEnabled) return;
345
+
346
+ // 自动计算 duration(毫秒)
347
+ const startTs = this._stageStartTimestamps.get(stageId);
348
+ const duration = startTs ? Date.now() - startTs : 0;
349
+ // 上报后清除,避免内存泄漏
350
+ this._stageStartTimestamps.delete(stageId);
351
+
352
+ this.trackEvent(AnalyticsEventId.StageEnd, {
353
+ [AnalyticsParamKey.StageId]: stageId,
354
+ [AnalyticsParamKey.Result]: result,
355
+ [AnalyticsParamKey.Duration]: duration,
356
+ [AnalyticsParamKey.Progress]: progress,
357
+ [AnalyticsParamKey.IsRevived]: isRevived,
358
+ });
311
359
  }
312
360
 
313
- // ==================== 关卡行为上报(友盟 stage 接口) ====================
361
+ /**
362
+ * 复活成功上报
363
+ * 触发时机:玩家选择复活(看广告或使用道具)并成功复活后上报一次。
364
+ * 复活前的失败不报 stage_end(还未到最终结局)。
365
+ * @param stageId 关卡ID
366
+ * @param method 复活方式
367
+ */
368
+ public stageTrackRevive(stageId: string, method: ReviveMethod): void {
369
+ if (!this._isEnabled) return;
370
+
371
+ this.trackEvent(AnalyticsEventId.StageRevive, {
372
+ [AnalyticsParamKey.StageId]: stageId,
373
+ [AnalyticsParamKey.Method]: method,
374
+ });
375
+ }
376
+
377
+ // ==================== 留存事件 ====================
314
378
 
315
379
  /**
316
- * 关卡开始
317
- * @param stageId 关卡ID(string类型)
318
- * @param stageName 关卡名称
380
+ * 游戏启动上报(DAU / 留存基数)
381
+ * LifeCycleMgr.onGameColdStart() 自动调用,业务层无需手动上报。
382
+ * @param launchScene 原始启动场景值
383
+ * @param launchCategory 归并后的场景分类
384
+ * @param launchHour 启动小时(0-23)
319
385
  */
320
- public stageOnStart(stageId: string, stageName: string): void {
321
- if (!this._isEnabled || !this._sdkStage) return;
322
- try {
323
- this._sdkStage.onStart({ stageId, stageName });
324
- LogUtils.Instance.info(AnalyticsMgr.TAG, "关卡开始", {
325
- operation: "stageOnStart",
326
- stageId,
327
- stageName,
328
- });
329
- } catch (e) {
330
- LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
331
- operation: "stageOnStart",
332
- reason: String(e),
333
- stageId,
334
- stageName,
335
- });
336
- }
386
+ public trackGameLaunch(launchScene: string, launchCategory: LaunchCategory, launchHour: number): void {
387
+ if (!this._isEnabled) return;
388
+ this.trackEvent(AnalyticsEventId.GameLaunch, {
389
+ [AnalyticsParamKey.LaunchScene]: launchScene,
390
+ [AnalyticsParamKey.LaunchCategory]: launchCategory,
391
+ [AnalyticsParamKey.LaunchHour]: launchHour,
392
+ });
337
393
  }
338
394
 
395
+ // ==================== 广告事件 ====================
396
+
339
397
  /**
340
- * 关卡结束
341
- * @param stageId 关卡ID(string类型)
342
- * @param stageName 关卡名称
343
- * @param event 结束结果("complete" | "fail")
344
- * @param duration 关卡耗时(毫秒,可选)
398
+ * 广告加载成功
399
+ * @param adScene 广告场景(业务自定义,如 revive / daily_bonus)
345
400
  */
346
- public stageOnEnd(stageId: string, stageName: string, event: StageEventType, duration?: number): void {
347
- if (!this._isEnabled || !this._sdkStage) return;
348
- try {
349
- const params: StageEndParams = { stageId, stageName, event };
350
- if (duration !== undefined) {
351
- params._um_sdu = duration;
352
- }
353
- this._sdkStage.onEnd(params);
354
- LogUtils.Instance.info(AnalyticsMgr.TAG, "关卡结束", {
355
- operation: "stageOnEnd",
356
- stageId,
357
- stageName,
358
- event,
359
- duration,
360
- });
361
- } catch (e) {
362
- LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
363
- operation: "stageOnEnd",
364
- reason: String(e),
365
- stageId,
366
- stageName,
367
- event,
368
- });
369
- }
401
+ public trackAdLoadSuccess(adScene: string): void {
402
+ if (!this._isEnabled) return;
403
+ this.trackEvent(AnalyticsEventId.AdLoadSuccess, {
404
+ [AnalyticsParamKey.AdScene]: adScene,
405
+ });
370
406
  }
371
407
 
372
408
  /**
373
- * 关卡中行为 - 使用道具
374
- * @param stageId 关卡ID(string类型)
375
- * @param stageName 关卡名称
376
- * @param itemName 道具名称
377
- * @param itemId 道具ID(可选)
378
- * @param itemCount 道具数量(可选)
379
- * @param itemMoney 道具单价(可选)
409
+ * 广告加载失败
410
+ * @param adScene 广告场景
411
+ * @param errCode 错误码
412
+ * @param errMsg 错误信息
380
413
  */
381
- public stageOnRunningTools(
382
- stageId: string,
383
- stageName: string,
384
- itemName: string,
385
- itemId?: string,
386
- itemCount?: number,
387
- itemMoney?: number
388
- ): void {
389
- if (!this._isEnabled || !this._sdkStage) return;
390
- try {
391
- const params: StageRunningParams = {
392
- stageId,
393
- stageName,
394
- event: StageRunningEventType.Tools,
395
- params: { itemName, itemId, itemCount, itemMoney },
396
- };
397
- this._sdkStage.onRunning(params);
398
- LogUtils.Instance.info(AnalyticsMgr.TAG, "关卡使用道具", {
399
- operation: "stageOnRunningTools",
400
- stageId,
401
- stageName,
402
- itemName,
403
- itemId,
404
- itemCount,
405
- itemMoney,
406
- });
407
- } catch (e) {
408
- LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
409
- operation: "stageOnRunningTools",
410
- reason: String(e),
411
- stageId,
412
- stageName,
413
- itemName,
414
- });
415
- }
414
+ public trackAdLoadError(adScene: string, errCode: number, errMsg: string): void {
415
+ if (!this._isEnabled) return;
416
+ this.trackEvent(AnalyticsEventId.AdLoadError, {
417
+ [AnalyticsParamKey.AdScene]: adScene,
418
+ [AnalyticsParamKey.ErrCode]: errCode,
419
+ [AnalyticsParamKey.ErrMsg]: errMsg,
420
+ });
416
421
  }
417
422
 
418
423
  /**
419
- * 关卡中行为 - 获得奖励
420
- * @param stageId 关卡ID(string类型)
421
- * @param stageName 关卡名称
422
- * @param itemName 奖励名称
423
- * @param itemId 奖励ID(可选)
424
- * @param itemCount 奖励数量(可选)
425
- * @param itemMoney 奖励单价(可选)
424
+ * 广告展示成功
425
+ * @param adScene 广告场景
426
426
  */
427
- public stageOnRunningAward(
428
- stageId: string,
429
- stageName: string,
430
- itemName: string,
431
- itemId?: string,
432
- itemCount?: number,
433
- itemMoney?: number
434
- ): void {
435
- if (!this._isEnabled || !this._sdkStage) return;
436
- try {
437
- const params: StageRunningParams = {
438
- stageId,
439
- stageName,
440
- event: StageRunningEventType.Award,
441
- params: { itemName, itemId, itemCount, itemMoney },
442
- };
443
- this._sdkStage.onRunning(params);
444
- LogUtils.Instance.info(AnalyticsMgr.TAG, "关卡获得奖励", {
445
- operation: "stageOnRunningAward",
446
- stageId,
447
- stageName,
448
- itemName,
449
- itemId,
450
- itemCount,
451
- itemMoney,
452
- });
453
- } catch (e) {
454
- LogUtils.Instance.error(AnalyticsMgr.TAG, FwkErrorCode.Analytics.SDKCallFailed, {
455
- operation: "stageOnRunningAward",
456
- reason: String(e),
457
- stageId,
458
- stageName,
459
- itemName,
460
- });
461
- }
427
+ public trackAdShow(adScene: string): void {
428
+ if (!this._isEnabled) return;
429
+ this.trackEvent(AnalyticsEventId.AdShow, {
430
+ [AnalyticsParamKey.AdScene]: adScene,
431
+ });
432
+ }
433
+
434
+ /**
435
+ * 广告观看完成(isEnded=true)
436
+ * @param adScene 广告场景
437
+ */
438
+ public trackAdShowComplete(adScene: string): void {
439
+ if (!this._isEnabled) return;
440
+ this.trackEvent(AnalyticsEventId.AdShowComplete, {
441
+ [AnalyticsParamKey.AdScene]: adScene,
442
+ });
443
+ }
444
+
445
+ /**
446
+ * 广告跳过(isEnded=false)
447
+ * @param adScene 广告场景
448
+ */
449
+ public trackAdShowSkip(adScene: string): void {
450
+ if (!this._isEnabled) return;
451
+ this.trackEvent(AnalyticsEventId.AdShowSkip, {
452
+ [AnalyticsParamKey.AdScene]: adScene,
453
+ });
462
454
  }
463
455
 
464
456
  // ==================== 状态查询 ====================
@@ -3,6 +3,7 @@ import { LogUtils } from "../Utils/LogUtils";
3
3
  import { ServiceLocator } from "../Utils/ServiceLocator";
4
4
  import { BaseMgr } from "./BaseMgr";
5
5
  import { EventMgr } from "./EventMgr";
6
+ import { AnalyticsMgr } from "./AnalyticsMgr";
6
7
 
7
8
  /** 生命周期管理器 */
8
9
  export class LifeCycleMgr extends BaseMgr {
@@ -121,6 +122,11 @@ export class LifeCycleMgr extends BaseMgr {
121
122
  }
122
123
 
123
124
  this.getPlatformAdapter().onGameColdStart();
125
+
126
+ // 上报 GameLaunch 事件
127
+ const info = this.getPlatformAdapter().getColdStartSceneInfo();
128
+ const launchHour = new Date().getHours();
129
+ AnalyticsMgr.Instance.trackGameLaunch(info.scene, info.category, launchHour);
124
130
  }
125
131
 
126
132
  /**
@@ -147,30 +153,6 @@ export class LifeCycleMgr extends BaseMgr {
147
153
  EventMgr.Instance.emit(FrameworkBase.Message.LifeCycle_onGameHide);
148
154
  }
149
155
 
150
- /**
151
- * 是否从最近使用启动
152
- * @return 是否从最近使用启动
153
- */
154
- public isLaunchFromRecentUse(): boolean {
155
- if (this.getPlatformAdapter() === null) {
156
- return false;
157
- }
158
-
159
- return this.getPlatformAdapter().isLaunchFromRecentUse();
160
- }
161
-
162
- /**
163
- * 是否从桌面快捷方式启动
164
- * @return 是否从桌面快捷方式启动
165
- */
166
- public isLaunchFromDesktopShortcut(): boolean {
167
- if (this.getPlatformAdapter() === null) {
168
- return false;
169
- }
170
-
171
- return this.getPlatformAdapter().isLaunchFromDesktopShortcut();
172
- }
173
-
174
156
  /**
175
157
  * 是否支持跳转至最近使用
176
158
  * @param callback 回调函数
@@ -195,15 +177,12 @@ export class LifeCycleMgr extends BaseMgr {
195
177
  this.getPlatformAdapter().navigateToRecentUse();
196
178
  }
197
179
 
198
- /**
199
- * 是否从广告启动
200
- * @return 是否从广告启动
201
- */
202
- public isLaunchFromAdvertisement(): boolean {
180
+ /** 获取冷启动场景信息 */
181
+ public getColdStartSceneInfo(): { scene: string; category: string } | null {
203
182
  if (this.getPlatformAdapter() === null) {
204
- return false;
183
+ return null;
205
184
  }
206
185
 
207
- return this.getPlatformAdapter().isLaunchFromAdvertisement();
186
+ return this.getPlatformAdapter().getColdStartSceneInfo();
208
187
  }
209
188
  }