Files

281 lines
9.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using DataEyeAnalytics;
using Newtonsoft.Json;
using UnityEngine;
namespace BallKingdomCrush
{
public class BIManager : MonoBehaviour
{
private static BIManager _instance;
private static readonly object _initLock = new object();
// 状态标志
private bool _initialized = false; // 仅在初始化成功后为 true
private bool _initializing = false; // 防止并发多次尝试初始化
private string ntpServer = "time.windows.com";
private string defaultChannel = "gp";
private int maxInitRetries = 6;
private float retryIntervalSeconds = 0.5f;
public static BIManager Instance
{
get
{
if (_instance == null)
{
var go = new GameObject("BIManager");
DontDestroyOnLoad(go);
_instance = go.AddComponent<BIManager>();
}
return _instance;
}
}
private void Awake()
{
// 单例保护:如果场景里已有实例,销毁重复的
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(gameObject);
}
else if (_instance != this)
{
Destroy(gameObject);
return;
}
}
private void Start()
{
// Start 调用 InitBI 是安全的:InitBI 本身是幂等且线程安全的
InitBI();
}
/// <summary>
/// 对外统一初始化入口(幂等、线程安全)
/// </summary>
public void InitBI()
{
// 快速检查
if (_initialized) return;
lock (_initLock)
{
if (_initialized || _initializing)
{
// 已经初始化或正在初始化,直接返回
return;
}
_initializing = true;
}
// 使用协程执行初始化(必要时可以做重试)
StartCoroutine(DoInitCoroutine());
}
private IEnumerator DoInitCoroutine()
{
try
{
int attempt = 0;
Exception lastException = null;
while (attempt < maxInitRetries)
{
attempt++;
try
{
// 这里不主动调用 DataEyeAnalyticsAPI.Init(...) 假设你使用的是场景里的 DataEyeAnalytics 预制体,
// SDK 会在预制体 Awake/Start 里初始化。如果你的流程是手动 Init,请在这里调用 Init(appId)。
// 1) 校准时间(默认实例)
DataEyeAnalyticsAPI.CalibrateTimeWithNtp(ntpServer);
// 2) 设置超参数
var superProperties = new Dictionary<string, object>
{
{ "channel", defaultChannel }
};
DataEyeAnalyticsAPI.SetSuperProperties(superProperties);
// 3) 开启自动采集
DataEyeAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL);
// 如果上面没有抛异常,认为初始化成功
_initialized = true;
Debug.Log($"[BIManager] InitBI succeeded on attempt {attempt}");
yield break;
}
catch (ArgumentNullException ane)
{
// 典型:DataEye 内部 instance 还没创建(appId 为 null -> retry
lastException = ane;
Debug.LogWarning(
$"[BIManager] DataEye not ready (attempt {attempt}), will retry. msg={ane.Message}");
}
catch (Exception ex)
{
// 其他异常,记录并决定是否重试
lastException = ex;
Debug.LogWarning($"[BIManager] InitBI attempt {attempt} threw: {ex.Message}");
}
// 等待后重试
yield return new WaitForSeconds(retryIntervalSeconds);
}
// 重试用尽仍失败 —— 打日志(不再抛异常以免影响游戏流程)
Debug.LogError("[BIManager] InitBI failed after retries. Last exception: " +
(lastException?.ToString() ?? "null"));
}
finally
{
lock (_initLock)
{
_initializing = false;
}
}
}
// ================= 通用埋点 =================
public void TrackEvent(string eventName, Dictionary<string, object> properties = null)
{
// if (properties == null)
// properties = new Dictionary<string, object>();
//
// DataEyeAnalyticsAPI.Track(eventName, properties);
// DataEyeAnalyticsAPI.Flush();
//
// Debug.Log($"[BI] 事件上报: {eventName}, 属性={JsonConvert.SerializeObject(properties)}");
}
// ================= 预制事件封装 =================
/// <summary>
/// 上报广告事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="adType">NativeRewardedVideoBannerInterstitialSplash</param>
/// <param name="placementId">聚合广告位id</param>
/// <param name="networkFirmId">广告网络ID</param>
public void TrackAdEvent(string eventName, string adType, string placementId = null,
string networkFirmId = null, double revenue = 0, string scene = null, string currency = "USD")
{
var props = new Dictionary<string, object>
{
{ "ad_type", adType }
};
if (!string.IsNullOrEmpty(placementId))
props["PlacementId"] = placementId;
if (!string.IsNullOrEmpty(networkFirmId))
props["NetworkFirmId"] = networkFirmId;
if (revenue > 0)
{
props["Revenue"] = revenue;
props["Ecpm"] = revenue * 1000;
}
if (!string.IsNullOrEmpty(scene))
props["scene"] = scene;
if (!string.IsNullOrEmpty(currency))
props["Currency"] = currency;
TrackEvent(eventName, props);
}
/// <summary>
/// 上报内购事件
/// </summary>
/// <param name="revenue">收入</param>
/// <param name="currency">币种</param>
/// <param name="payType">支付方式 0=商店支付,1=三方支付</param>
/// <param name="itemId">sku</param>
/// <param name="status">order:下单 paid:付费成功 paid_err:扣款失败</param>
/// <param name="itemName">商品名称</param>
/// <param name="msg">如果遇到异常或相关情况,将err_msg进行上报</param>
public void TrackPurchase(double revenue, string currency, string payType, string itemId,
string status, string itemName = null, string msg = null)
{
var props = new Dictionary<string, object>
{
{ "Revenue", revenue },
{ "Currency", currency },
{ "type", payType },
{ "item_id", itemId },
{ "purchase_status", status }
};
if (!string.IsNullOrEmpty(itemName))
props["item_name"] = itemName;
if (!string.IsNullOrEmpty(msg))
props["msg"] = msg;
TrackEvent(BIEvent.PURCHASE, props);
}
/// <summary>
/// 上报页面浏览
/// </summary>
public void TrackPageView(string pageName)
{
var props = new Dictionary<string, object>
{
{ "page_name", pageName } // 例如 "loading"、"game"
};
TrackEvent(BIEvent.PAGE_VIEW, props);
}
/// <summary>
/// AB 分组埋点
/// </summary>
public void TrackABConfig(int responseTime)
{
var props = new Dictionary<string, object>
{
{ "response_time", responseTime }
};
TrackEvent(BIEvent.AB_CONFIG, props);
}
}
}
public static class BIEvent
{
// 自动采集(SDK 开启自动采集即可,不需要手动埋点)
public const string APP_INSTALL = "app_install";
public const string APP_START = "app_start";
public const string APP_END = "app_end";
public const string APP_VIEW = "app_view";
public const string APP_CRASH = "app_crash";
// 内部预制事件
public const string AD_REQUEST = "ad_request"; // 广告请求
public const string AD_INVENTORY = "ad_inventory"; // 广告填充的时候
public const string AD_IMP = "ad_imp"; // 广告展示
public const string AD_CLICK = "ad_click"; // 广告点击
public const string PURCHASE = "purchase"; // 内购
// 页面相关
public const string PAGE_VIEW = "page_view_customize";
// 其他业务事件(AB 测试等)
public const string AB_CONFIG = "config"; // AB
}