NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/BI/BIManager.cs

570 lines
16 KiB
C#
Raw Normal View History

2025-03-18 18:40:56 +08:00
using Cysharp.Threading.Tasks;
2025-03-18 19:13:40 +08:00
using Newtonsoft.Json;
2024-05-14 15:29:31 +08:00
using PhxhSDK;
2024-07-25 17:06:02 +08:00
using System;
2025-06-11 16:12:05 +08:00
using System.Collections.Generic;
using System.Linq;
2025-06-11 16:12:05 +08:00
using UnityEngine;
2025-10-23 20:12:06 +08:00
using Assets.PhxhSDK.AOT.BI;
using Framework.BI;
2024-05-14 15:29:31 +08:00
2025-04-22 15:39:26 +08:00
public partial class BIManager : Singlenton<BIManager>, IInitable, IUpdatable
2024-05-14 15:29:31 +08:00
{
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 基础属性
2025-04-22 15:39:26 +08:00
/// </summary>
2025-05-13 15:27:38 +08:00
private BIBaseInfo baseInfo;
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 一次性事件缓存类
2025-04-22 15:39:26 +08:00
/// </summary>
2025-05-13 15:27:38 +08:00
private StorageEvent _storageEvent;
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 是否初始化
2025-04-22 15:39:26 +08:00
/// </summary>
2025-05-13 15:27:38 +08:00
private bool _isInit;
2025-07-16 17:40:42 +08:00
public PublicDictionaryPool DictionaryPool { get; } = new PublicDictionaryPool();
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 参数字典
2025-04-22 15:39:26 +08:00
/// </summary>
2025-05-13 15:27:38 +08:00
private Dictionary<string, object> _tempEventDictionary = new(12);
2025-04-22 15:39:26 +08:00
/// <summary>
/// 服务器时间提供者
/// </summary>
private IServerTimeProvider _serverTimeProvider;
2025-05-13 15:27:38 +08:00
#region 初始化
2025-04-22 15:39:26 +08:00
/// <summary>
/// 注册服务器时间提供者
/// </summary>
/// <param name="provider"></param>
public void RegisterServerTimeProvider(IServerTimeProvider provider)
{
_serverTimeProvider = provider;
}
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 游戏启动初始化
2025-04-22 15:39:26 +08:00
/// </summary>
2024-05-14 15:29:31 +08:00
public void Init()
{
2025-05-13 15:27:38 +08:00
baseInfo = new BIBaseInfo();
_storageEvent = StorageMgr.Instance.GetStorage<StorageEvent>(Framework.Constants.Storage.BI_FTE_EVENT);
_storageEvent ??= new StorageEvent();
InitDictPool();
_networkAvailable = true;
_useQueueMode = _sendInterval > 0;
InitService();
2024-05-14 15:29:31 +08:00
2025-05-13 15:27:38 +08:00
_isInit = true;
2025-10-23 20:12:06 +08:00
// 初始化统一的信息提供者
PhxhBIInfoProvider.Initialize();
2025-05-13 15:27:38 +08:00
}
2025-04-22 15:39:26 +08:00
2025-05-13 15:27:38 +08:00
/// <summary>
/// 初始化登陆后基本属性
/// </summary>
public void InitLoginBaseInfo()
{
try
{
baseInfo.SetUserInfo();
baseInfo.PrintAllValues();
if (!_isInit)
{
DebugUtil.Log("BIManager InitLoginBaseInfo 未初始化");
return;
}
// 登录后更新统一信息提供者
PhxhBIInfoProvider.UpdateAfterLogin(baseInfo.actor_id, baseInfo.third_platform, baseInfo.third_id);
MultiPlatformBIService.SetUser(baseInfo.actor_id);
SetUserProperty();
DebugUtil.Log("BIManager InitLoginBaseInfo 冲刷AOT阶段缓存事件");
// BIManager接管后冲刷AOT阶段缓存的事件
BIToolinAOT.Instance.FlushWaitQueueIfAny();
// 发送积累事件
SendQueuedEvents();
}
catch (Exception e)
{
DebugUtil.LogError($"BIManager.InitLoginBaseInfo error :{e}");
}
2025-04-22 15:39:26 +08:00
}
2025-05-13 15:27:38 +08:00
/// <summary>
/// 初始化第三方打点平台
/// </summary>
private void InitService()
2024-05-14 15:29:31 +08:00
{
2025-10-23 20:12:06 +08:00
MultiPlatformBIService.InitServices();
2024-05-14 15:29:31 +08:00
}
2024-07-22 17:08:02 +08:00
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 设置用户属性
2025-04-22 15:39:26 +08:00
/// </summary>
2025-10-21 18:27:00 +08:00
public void SetUserProperty()
2024-07-22 17:08:02 +08:00
{
2025-06-11 16:12:05 +08:00
SetUserProperty("game_id", baseInfo.app_name);
2025-05-13 15:27:38 +08:00
SetUserProperty("channel", baseInfo.channel);
SetUserProperty("model", baseInfo.model);
SetUserProperty("uuid", baseInfo.uuid);
SetUserProperty("resolution", baseInfo.resolution);
SetUserProperty("platform", baseInfo.platform);
2025-05-14 15:11:09 +08:00
SetUserProperty("os", baseInfo.os);
2025-05-13 15:27:38 +08:00
SetUserProperty("version", baseInfo.version);
SetUserProperty("build_code", baseInfo.build_code);
SetUserProperty("assets_version", baseInfo.assets_version);
SetUserProperty("language", baseInfo.language);
// SetUserProperty("actor_id", baseInfo.actor_id); 通用接口
SetUserProperty("third_platform", baseInfo.third_platform);
SetUserProperty("third_id", baseInfo.third_id);
SetUserProperty("device_memory", baseInfo.storage_info);
SetUserProperty("device_cpu", baseInfo.cpu);
2025-04-22 15:39:26 +08:00
2025-05-13 15:27:38 +08:00
#if SDK_TALKINGDATA
// _biService?.SetUserProperty(TalkingDataProfileParamName.Param1, baseInfo.model);
// _biService?.SetUserProperty(TalkingDataProfileParamName.Param2, baseInfo.storage_info + "MB");
SetUserProperty("storage_info", baseInfo.storage_info + "MB");
#endif
2024-07-22 17:08:02 +08:00
}
2024-05-14 15:29:31 +08:00
2025-10-21 19:57:23 +08:00
public void SetUserProperty(string name, string value)
2025-02-11 14:40:04 +08:00
{
2025-10-23 20:12:06 +08:00
MultiPlatformBIService.SetUserProperty(name, value);
2025-02-11 14:40:04 +08:00
}
2025-04-22 15:39:26 +08:00
#endregion
#region 释放
public void Release()
2024-05-14 15:29:31 +08:00
{
2025-04-22 15:39:26 +08:00
_tempEventDictionary = null;
2025-05-13 15:27:38 +08:00
_dictPool = null; // 释放对象池
2025-08-07 17:01:48 +08:00
if (_isInit && _networkAvailable && eventQueue.Count > 0)
{
2025-04-22 15:39:26 +08:00
SendQueuedEvents();
}
}
2024-05-14 15:29:31 +08:00
2025-04-22 15:39:26 +08:00
#endregion
2024-05-14 15:29:31 +08:00
2025-05-13 15:27:38 +08:00
#region 周期/队列 发送
/// <summary>
2025-08-07 16:01:22 +08:00
/// 事件队列 - 增加时间戳信息
2025-05-13 15:27:38 +08:00
/// </summary>
private readonly Queue<(string eventName, Dictionary<string, object> param, long localTime, ulong serverTime)>
eventQueue = new();
2025-05-13 15:27:38 +08:00
/// <summary>
/// 是否使用队列模式(无网络或设置了发送周期)
/// </summary>
private bool _useQueueMode;
/// <summary>
/// 发送周期 默认随打随发
/// </summary>
private float _sendInterval = 0f;
/// <summary>
/// 网络状态 通过心跳包检测
/// </summary>
private bool _networkAvailable;
/// <summary>
/// 计时器
/// </summary>
private float _timer = 0f;
2024-05-14 15:29:31 +08:00
2025-04-22 15:39:26 +08:00
public void Update(float deltaTime)
{
if (!_isInit)
return;
2025-05-13 15:27:38 +08:00
// 检查网络状态
2025-08-26 12:25:50 +08:00
//CheckNetworkStatus(); //TODO 网络状态不由心跳包检查
2025-05-13 15:27:38 +08:00
if (eventQueue.Count <= 0 || !_networkAvailable) return;
// 周期发送
if (_sendInterval > 0)
2025-04-22 15:39:26 +08:00
{
2025-05-13 15:27:38 +08:00
_timer += Time.deltaTime;
// 达到发送周期
if (_timer >= _sendInterval)
2024-09-18 14:51:32 +08:00
{
2025-05-13 15:27:38 +08:00
_timer = 0;
2025-04-22 15:39:26 +08:00
SendQueuedEvents();
2024-09-18 14:51:32 +08:00
}
2024-05-14 15:29:31 +08:00
}
2025-05-13 15:27:38 +08:00
else
2024-05-14 15:29:31 +08:00
{
2025-04-22 15:39:26 +08:00
SendQueuedEvents();
2024-05-14 15:29:31 +08:00
}
}
2025-04-22 15:39:26 +08:00
/// <summary>
2025-05-13 15:27:38 +08:00
/// 检查网络状态
2025-04-22 15:39:26 +08:00
/// </summary>
2025-05-13 15:27:38 +08:00
private void CheckNetworkStatus()
{
2025-05-13 15:27:38 +08:00
// 获取当前网络状态
var currentNetworkStatus = //NetworkManager.Instance.NetworkAvailable;//由于未登录时没有心跳包改为使用Unity的网络状态检测
Application.internetReachability != NetworkReachability.NotReachable;
2025-05-13 15:27:38 +08:00
// 如果网络状态发生变化
if (_networkAvailable == currentNetworkStatus) return;
if (currentNetworkStatus)
{
// 网络恢复
_networkAvailable = true;
_useQueueMode = _sendInterval > 0; // 如果有设置发送周期,仍然使用队列模式
// 如果没有设置发送周期,立即发送所有队列中的事件
if (!_useQueueMode && eventQueue.Count > 0)
{
SendQueuedEvents();
}
}
else
{
_networkAvailable = false;
_useQueueMode = true;
}
2025-04-22 15:39:26 +08:00
}
2025-04-22 15:39:26 +08:00
/// <summary>
/// 设置事件发送周期
/// </summary>
/// <param name="interval">发送周期单位秒设为0则立即发送</param>
public void SetSendInterval(float interval)
{
_sendInterval = Mathf.Max(0, interval);
_useQueueMode = !_networkAvailable || _sendInterval > 0;
}
2025-04-22 15:39:26 +08:00
#endregion
2025-04-22 15:39:26 +08:00
#region 发送事件
2025-05-13 15:27:38 +08:00
/// <summary>
2025-08-07 16:01:22 +08:00
/// 添加事件到队列 - 增加时间戳参数
2025-05-13 15:27:38 +08:00
/// </summary>
2025-08-07 16:01:22 +08:00
private void EnqueueEvent(string eventName, Dictionary<string, object> param, long localTime, ulong serverTime)
2025-05-13 15:27:38 +08:00
{
// 检查队列是否已满
if (eventQueue.Count >= MAX_QUEUE_SIZE)
{
DebugUtil.LogWarning("BI事件队列已满");
return;
}
// 从对象池获取字典并复制数据
var poolDict = GetDictFromPool();
if (param != null)
{
foreach (var item in param)
{
poolDict[item.Key] = item.Value;
}
}
2025-05-14 15:11:09 +08:00
2025-08-07 16:01:22 +08:00
eventQueue.Enqueue((eventName, poolDict, localTime, serverTime));
2025-05-13 15:27:38 +08:00
}
2025-04-22 15:39:26 +08:00
/// <summary>
/// 发送队列中的所有事件
/// </summary>
private void SendQueuedEvents()
{
2025-10-23 20:12:06 +08:00
if (!_networkAvailable)
2025-04-22 15:39:26 +08:00
return;
2025-05-13 15:27:38 +08:00
// 只要队列有事件就全部处理
2025-04-22 15:39:26 +08:00
while (eventQueue.Count > 0)
{
2025-04-22 15:39:26 +08:00
//将缓存的事件上报
2025-08-07 16:01:22 +08:00
var (eventName, param, localTime, serverTime) = eventQueue.Dequeue();
2025-10-23 20:12:06 +08:00
MultiPlatformBIService.TrackEvent(eventName, param);
2025-08-07 16:01:22 +08:00
TrackEventPhxh(eventName, param, localTime, serverTime);
}
}
2024-05-14 15:29:31 +08:00
private bool IsRecorded(string eventName)
{
2024-10-18 13:47:19 +08:00
return _storageEvent.EventList.Contains(eventName);
2024-05-14 15:29:31 +08:00
}
2025-05-13 15:27:38 +08:00
/// <summary>
/// 上报自定义事件
/// </summary>
public void TrackEvent(string eventName,
2025-05-13 17:47:02 +08:00
string param1Key = null, object param1 = null,
string param2Key = null, object param2 = null,
string param3Key = null, object param3 = null,
string param4Key = null, object param4 = null)
2025-05-13 15:27:38 +08:00
{
if (!_isInit)
return;
try
{
2025-08-07 16:01:22 +08:00
var localTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var serverTime = _serverTimeProvider?.GetServerTime() ?? 0;
2025-05-13 15:27:38 +08:00
_tempEventDictionary.Clear();
var userParam = _tempEventDictionary;
2025-05-13 17:47:02 +08:00
if (!string.IsNullOrEmpty(param1Key) && param1 != null)
2025-05-13 15:27:38 +08:00
userParam.Add(param1Key, param1);
2025-05-13 17:47:02 +08:00
if (!string.IsNullOrEmpty(param2Key) && param2 != null)
2025-05-13 15:27:38 +08:00
userParam.Add(param2Key, param2);
2025-05-13 17:47:02 +08:00
if (!string.IsNullOrEmpty(param3Key) && param3 != null)
2025-05-13 15:27:38 +08:00
userParam.Add(param3Key, param3);
2025-05-13 17:47:02 +08:00
if (!string.IsNullOrEmpty(param4Key) && param4 != null)
userParam.Add(param4Key, param4);
2025-05-13 15:27:38 +08:00
2025-10-23 20:12:06 +08:00
if (_useQueueMode)
2025-05-13 15:27:38 +08:00
{
2025-08-26 12:25:50 +08:00
DebugUtil.Log("缓存发送事件: {0}, 事件参数: {1}", eventName, JsonConvert.SerializeObject(userParam));
2025-08-07 16:01:22 +08:00
EnqueueEvent(eventName, userParam, localTime, serverTime);
2025-05-13 15:27:38 +08:00
}
else
{
2025-10-23 20:12:06 +08:00
MultiPlatformBIService.TrackEvent(eventName, userParam);
2025-08-07 16:01:22 +08:00
TrackEventPhxh(eventName, userParam, localTime, serverTime);
2025-05-13 15:27:38 +08:00
}
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
2025-07-16 17:40:42 +08:00
/// <summary>
/// 上报自定义事件
/// </summary>
public void TrackEventWithRecycleDic(string eventName, RecycleDictionary recycleDictionary)
2025-07-16 17:40:42 +08:00
{
if (!_isInit)
return;
try
{
2025-08-07 16:01:22 +08:00
var localTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var serverTime = _serverTimeProvider?.GetServerTime() ?? 0;
2025-07-16 17:40:42 +08:00
_tempEventDictionary.Clear();
var userParam = _tempEventDictionary;
foreach (var item in recycleDictionary.dict)
{
if (item.Value != null)
{
userParam.Add(item.Key, item.Value);
}
}
2025-07-16 17:40:42 +08:00
DictionaryPool.ReturnDictToPool(recycleDictionary);
2025-10-23 20:12:06 +08:00
if (_useQueueMode)
2025-07-16 17:40:42 +08:00
{
2025-08-07 16:01:22 +08:00
EnqueueEvent(eventName, userParam, localTime, serverTime);
2025-07-16 17:40:42 +08:00
}
else
{
2025-10-23 20:12:06 +08:00
MultiPlatformBIService.TrackEvent(eventName, userParam);
2025-08-07 16:01:22 +08:00
TrackEventPhxh(eventName, userParam, localTime, serverTime);
2025-07-16 17:40:42 +08:00
}
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
2025-05-13 15:27:38 +08:00
/// <summary>
/// 上报一次性自定义事件
/// </summary>
public void TrackEventOnce(string eventName,
2025-05-13 17:47:02 +08:00
string param1Key = null, object param1 = null,
string param2Key = null, object param2 = null,
string param3Key = null, object param3 = null)
2025-05-13 15:27:38 +08:00
{
if (IsRecorded(eventName))
{
DebugUtil.Log("已经上报过 {0} 关卡事件,不会再次上报", eventName);
return;
}
2025-08-14 13:59:50 +08:00
_storageEvent.EventList.Add(eventName);
StorageMgr.Instance.SyncForce = true;
TrackEvent(eventName, param1Key, param1, param2Key, param2, param3Key, param3);
2025-05-13 15:27:38 +08:00
}
2025-05-13 15:27:38 +08:00
#endregion
2025-04-22 15:39:26 +08:00
#region PHXH 服务器打点
2025-03-18 18:40:56 +08:00
2025-08-07 16:01:22 +08:00
private void TrackEventPhxh(string eventName, Dictionary<string, object> param, long localTime, ulong serverTime)
2025-03-18 18:40:56 +08:00
{
2026-02-05 19:26:40 +08:00
if (!_isInit)
2025-03-18 18:40:56 +08:00
return;
2025-10-23 20:12:06 +08:00
// 直接调用通用服务,它会从 PhxhBIInfoProvider 获取所有信息
PhxhBIService.TrackEvent(eventName, param, localTime, serverTime);
2025-05-13 15:27:38 +08:00
}
#endregion
2025-07-16 17:40:42 +08:00
#region 网络队列对象池
2025-05-13 15:27:38 +08:00
/// <summary>
/// 字典对象池大小
/// </summary>
private const int DICT_POOL_SIZE = 32;
/// <summary>
/// 对象池
/// </summary>
private List<Dictionary<string, object>> _dictPool;
/// <summary>
/// 对象池索引
/// </summary>
private int _dictPoolIndex = 0;
/// <summary>
/// 事件队列最大容量
/// </summary>
private const int MAX_QUEUE_SIZE = 1000;
/// <summary>
/// 初始化字典对象池
/// </summary>
private void InitDictPool()
{
_dictPool = new List<Dictionary<string, object>>(DICT_POOL_SIZE);
for (var i = 0; i < DICT_POOL_SIZE; i++)
{
_dictPool.Add(new Dictionary<string, object>(16));
}
}
/// <summary>
/// 从对象池获取一个字典
/// </summary>
private Dictionary<string, object> GetDictFromPool()
{
if (_dictPool == null || _dictPool.Count == 0)
{
InitDictPool();
}
var dict = _dictPool[_dictPoolIndex];
dict.Clear();
_dictPoolIndex = (_dictPoolIndex + 1) % DICT_POOL_SIZE;
return dict;
2025-03-18 18:40:56 +08:00
}
2025-07-16 17:40:42 +08:00
#endregion
2025-04-22 15:39:26 +08:00
2025-07-16 17:40:42 +08:00
#region 外部打点字典对象池
2025-07-16 17:40:42 +08:00
/// <summary>
/// 对象池字典请使用BIManager内部的PublicDictionaryPool获取对象
/// </summary>
public class RecycleDictionary
{
public Dictionary<string, object> dict;
public RecycleDictionary()
{
dict = new Dictionary<string, object>();
}
public void Add(string key, object value)
{
if (dict.ContainsKey(key))
{
dict[key] = value;
}
else
{
dict.Add(key, value);
}
}
public void Clear()
{
dict.Clear();
}
}
/// <summary>
/// 外部可访问的字典对象池类,用于打点事件传入参数
/// </summary>
public class PublicDictionaryPool
{
private const int DICT_POOL_SIZE = 16; // 池大小可根据实际需求调整
private List<RecycleDictionary> _dictPool;
private int _dictPoolIndex = 0;
public PublicDictionaryPool()
{
InitDictPool();
}
private void InitDictPool()
{
_dictPool = new List<RecycleDictionary>(DICT_POOL_SIZE);
for (int i = 0; i < DICT_POOL_SIZE; i++)
{
_dictPool.Add(new RecycleDictionary());
}
2025-07-16 17:40:42 +08:00
_dictPoolIndex = 0;
}
public RecycleDictionary GetDictFromPool()
{
if (_dictPoolIndex >= DICT_POOL_SIZE)
{
// 池已用完,临时分配一个新的字典
// TODO 也可以扩容池
return new RecycleDictionary();
}
2025-07-16 17:40:42 +08:00
var dict = _dictPool[_dictPoolIndex];
_dictPoolIndex++;
dict.Clear();
return dict;
}
public void ReturnDictToPool(RecycleDictionary dict)
{
if (_dictPoolIndex == 0)
return;
_dictPoolIndex--;
_dictPool[_dictPoolIndex] = dict;
}
}
2025-04-22 15:39:26 +08:00
#endregion
2024-05-14 15:29:31 +08:00
}