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

570 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using PhxhSDK;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Assets.PhxhSDK.AOT.BI;
using Framework.BI;
public partial class BIManager : Singlenton<BIManager>, IInitable, IUpdatable
{
/// <summary>
/// 基础属性
/// </summary>
private BIBaseInfo baseInfo;
/// <summary>
/// 一次性事件缓存类
/// </summary>
private StorageEvent _storageEvent;
/// <summary>
/// 是否初始化
/// </summary>
private bool _isInit;
public PublicDictionaryPool DictionaryPool { get; } = new PublicDictionaryPool();
/// <summary>
/// 参数字典
/// </summary>
private Dictionary<string, object> _tempEventDictionary = new(12);
/// <summary>
/// 服务器时间提供者
/// </summary>
private IServerTimeProvider _serverTimeProvider;
#region 初始化
/// <summary>
/// 注册服务器时间提供者
/// </summary>
/// <param name="provider"></param>
public void RegisterServerTimeProvider(IServerTimeProvider provider)
{
_serverTimeProvider = provider;
}
/// <summary>
/// 游戏启动初始化
/// </summary>
public void Init()
{
baseInfo = new BIBaseInfo();
_storageEvent = StorageMgr.Instance.GetStorage<StorageEvent>(Framework.Constants.Storage.BI_FTE_EVENT);
_storageEvent ??= new StorageEvent();
InitDictPool();
_networkAvailable = true;
_useQueueMode = _sendInterval > 0;
InitService();
_isInit = true;
// 初始化统一的信息提供者
PhxhBIInfoProvider.Initialize();
}
/// <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}");
}
}
/// <summary>
/// 初始化第三方打点平台
/// </summary>
private void InitService()
{
MultiPlatformBIService.InitServices();
}
/// <summary>
/// 设置用户属性
/// </summary>
public void SetUserProperty()
{
SetUserProperty("game_id", baseInfo.app_name);
SetUserProperty("channel", baseInfo.channel);
SetUserProperty("model", baseInfo.model);
SetUserProperty("uuid", baseInfo.uuid);
SetUserProperty("resolution", baseInfo.resolution);
SetUserProperty("platform", baseInfo.platform);
SetUserProperty("os", baseInfo.os);
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);
#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
}
public void SetUserProperty(string name, string value)
{
MultiPlatformBIService.SetUserProperty(name, value);
}
#endregion
#region 释放
public void Release()
{
_tempEventDictionary = null;
_dictPool = null; // 释放对象池
if (_isInit && _networkAvailable && eventQueue.Count > 0)
{
SendQueuedEvents();
}
}
#endregion
#region 周期/队列 发送
/// <summary>
/// 事件队列 - 增加时间戳信息
/// </summary>
private readonly Queue<(string eventName, Dictionary<string, object> param, long localTime, ulong serverTime)>
eventQueue = new();
/// <summary>
/// 是否使用队列模式(无网络或设置了发送周期)
/// </summary>
private bool _useQueueMode;
/// <summary>
/// 发送周期 默认随打随发
/// </summary>
private float _sendInterval = 0f;
/// <summary>
/// 网络状态 通过心跳包检测
/// </summary>
private bool _networkAvailable;
/// <summary>
/// 计时器
/// </summary>
private float _timer = 0f;
public void Update(float deltaTime)
{
if (!_isInit)
return;
// 检查网络状态
//CheckNetworkStatus(); //TODO 网络状态不由心跳包检查
if (eventQueue.Count <= 0 || !_networkAvailable) return;
// 周期发送
if (_sendInterval > 0)
{
_timer += Time.deltaTime;
// 达到发送周期
if (_timer >= _sendInterval)
{
_timer = 0;
SendQueuedEvents();
}
}
else
{
SendQueuedEvents();
}
}
/// <summary>
/// 检查网络状态
/// </summary>
private void CheckNetworkStatus()
{
// 获取当前网络状态
var currentNetworkStatus = //NetworkManager.Instance.NetworkAvailable;//由于未登录时没有心跳包改为使用Unity的网络状态检测
Application.internetReachability != NetworkReachability.NotReachable;
// 如果网络状态发生变化
if (_networkAvailable == currentNetworkStatus) return;
if (currentNetworkStatus)
{
// 网络恢复
_networkAvailable = true;
_useQueueMode = _sendInterval > 0; // 如果有设置发送周期,仍然使用队列模式
// 如果没有设置发送周期,立即发送所有队列中的事件
if (!_useQueueMode && eventQueue.Count > 0)
{
SendQueuedEvents();
}
}
else
{
_networkAvailable = false;
_useQueueMode = true;
}
}
/// <summary>
/// 设置事件发送周期
/// </summary>
/// <param name="interval">发送周期单位秒设为0则立即发送</param>
public void SetSendInterval(float interval)
{
_sendInterval = Mathf.Max(0, interval);
_useQueueMode = !_networkAvailable || _sendInterval > 0;
}
#endregion
#region 发送事件
/// <summary>
/// 添加事件到队列 - 增加时间戳参数
/// </summary>
private void EnqueueEvent(string eventName, Dictionary<string, object> param, long localTime, ulong serverTime)
{
// 检查队列是否已满
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;
}
}
eventQueue.Enqueue((eventName, poolDict, localTime, serverTime));
}
/// <summary>
/// 发送队列中的所有事件
/// </summary>
private void SendQueuedEvents()
{
if (!_networkAvailable)
return;
// 只要队列有事件就全部处理
while (eventQueue.Count > 0)
{
//将缓存的事件上报
var (eventName, param, localTime, serverTime) = eventQueue.Dequeue();
MultiPlatformBIService.TrackEvent(eventName, param);
TrackEventPhxh(eventName, param, localTime, serverTime);
}
}
private bool IsRecorded(string eventName)
{
return _storageEvent.EventList.Contains(eventName);
}
/// <summary>
/// 上报自定义事件
/// </summary>
public void TrackEvent(string eventName,
string param1Key = null, object param1 = null,
string param2Key = null, object param2 = null,
string param3Key = null, object param3 = null,
string param4Key = null, object param4 = null)
{
if (!_isInit)
return;
try
{
var localTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var serverTime = _serverTimeProvider?.GetServerTime() ?? 0;
_tempEventDictionary.Clear();
var userParam = _tempEventDictionary;
if (!string.IsNullOrEmpty(param1Key) && param1 != null)
userParam.Add(param1Key, param1);
if (!string.IsNullOrEmpty(param2Key) && param2 != null)
userParam.Add(param2Key, param2);
if (!string.IsNullOrEmpty(param3Key) && param3 != null)
userParam.Add(param3Key, param3);
if (!string.IsNullOrEmpty(param4Key) && param4 != null)
userParam.Add(param4Key, param4);
if (_useQueueMode)
{
DebugUtil.Log("缓存发送事件: {0}, 事件参数: {1}", eventName, JsonConvert.SerializeObject(userParam));
EnqueueEvent(eventName, userParam, localTime, serverTime);
}
else
{
MultiPlatformBIService.TrackEvent(eventName, userParam);
TrackEventPhxh(eventName, userParam, localTime, serverTime);
}
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
/// <summary>
/// 上报自定义事件
/// </summary>
public void TrackEventWithRecycleDic(string eventName, RecycleDictionary recycleDictionary)
{
if (!_isInit)
return;
try
{
var localTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var serverTime = _serverTimeProvider?.GetServerTime() ?? 0;
_tempEventDictionary.Clear();
var userParam = _tempEventDictionary;
foreach (var item in recycleDictionary.dict)
{
if (item.Value != null)
{
userParam.Add(item.Key, item.Value);
}
}
DictionaryPool.ReturnDictToPool(recycleDictionary);
if (_useQueueMode)
{
EnqueueEvent(eventName, userParam, localTime, serverTime);
}
else
{
MultiPlatformBIService.TrackEvent(eventName, userParam);
TrackEventPhxh(eventName, userParam, localTime, serverTime);
}
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
/// <summary>
/// 上报一次性自定义事件
/// </summary>
public void TrackEventOnce(string eventName,
string param1Key = null, object param1 = null,
string param2Key = null, object param2 = null,
string param3Key = null, object param3 = null)
{
if (IsRecorded(eventName))
{
DebugUtil.Log("已经上报过 {0} 关卡事件,不会再次上报", eventName);
return;
}
_storageEvent.EventList.Add(eventName);
StorageMgr.Instance.SyncForce = true;
TrackEvent(eventName, param1Key, param1, param2Key, param2, param3Key, param3);
}
#endregion
#region PHXH 服务器打点
private void TrackEventPhxh(string eventName, Dictionary<string, object> param, long localTime, ulong serverTime)
{
if (!_isInit)
return;
// 直接调用通用服务,它会从 PhxhBIInfoProvider 获取所有信息
PhxhBIService.TrackEvent(eventName, param, localTime, serverTime);
}
#endregion
#region 网络队列对象池
/// <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;
}
#endregion
#region 外部打点字典对象池
/// <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());
}
_dictPoolIndex = 0;
}
public RecycleDictionary GetDictFromPool()
{
if (_dictPoolIndex >= DICT_POOL_SIZE)
{
// 池已用完,临时分配一个新的字典
// TODO 也可以扩容池
return new RecycleDictionary();
}
var dict = _dictPool[_dictPoolIndex];
_dictPoolIndex++;
dict.Clear();
return dict;
}
public void ReturnDictToPool(RecycleDictionary dict)
{
if (_dictPoolIndex == 0)
return;
_dictPoolIndex--;
_dictPool[_dictPoolIndex] = dict;
}
}
#endregion
}