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

622 lines
19 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 UnityEngine;
public partial class BIManager : Singlenton<BIManager>, IInitable, IUpdatable
{
/// <summary>
/// 基础属性
/// </summary>
private BIBaseInfo baseInfo;
/// <summary>
/// 关卡属性
/// </summary>
private BILevelInfo levelInfo;
/// <summary>
/// BI接口
/// </summary>
private IBIService _biService;
/// <summary>
/// 一次性事件缓存类
/// </summary>
private StorageEvent _storageEvent;
/// <summary>
/// 是否初始化
/// </summary>
private bool _isInit;
/// <summary>
/// 启用自己服务器打点
/// </summary>
private bool sendPhxh;
/// <summary>
/// 参数字典
/// </summary>
private Dictionary<string, object> _tempEventDictionary = new(12);
private readonly string PhxhBiUrl = "https://bi.kedrgame.com/api/v1/events";
/// <summary>
/// BI专属accessKey请勿与中台key混用
/// </summary>
private readonly string AccessKey = "yd9kWI798IWyyq1t";
/// <summary>
/// BI专属密钥请勿与中台密钥混用
/// </summary>
private readonly string SecretKey = "iQLHTbOqeXtZZpxOZve-PDxTK9DkHPSKRbpXb30eS3g=";
#region 初始化
/// <summary>
/// 游戏启动初始化
/// </summary>
public void Init()
{
baseInfo = new BIBaseInfo();
levelInfo = new BILevelInfo();
_storageEvent = StorageMgr.Instance.GetStorage<StorageEvent>(Framework.Constants.Storage.BI_FTE_EVENT);
_storageEvent ??= new StorageEvent();
InitDictPool();
_networkAvailable = true;
_useQueueMode = _sendInterval > 0;
InitService();
#if BI_PHXH
sendPhxh = true;
#else
sendPhxh = false;
#endif
_isInit = true;
}
/// <summary>
/// 登陆后初始化
/// </summary>
/// <param name="name"></param>
public void AfterLoginInitService(SDKManager.SdkName name)
{
_biService = SDKManager.Instance.GetSdkHelper(name) as IBIService;
if (_biService == null)
{
DebugUtil.Log("After Login Get BI Service error, SDK helper name is {0}", name);
}
}
/// <summary>
/// 初始化登陆后基本属性
/// </summary>
public void InitLoginBaseInfo()
{
baseInfo.SetUserInfo();
baseInfo.PrintAllValues();
if (!_isInit || _biService == null) return;
_biService.SetUser(baseInfo.actor_id);
SetUserProperty();
// 发送积累事件
SendQueuedEvents();
}
/// <summary>
/// 初始化第三方打点平台
/// </summary>
private void InitService()
{
var helperName = SDKManager.SdkName.TalkingData;
#if SDK_FIREBASE
helperName = SDKManager.SdkName.FireBase;
_biService = SDKManager.Instance.GetSdkHelper(helperName) as IBIService;
#endif
#if SDK_TAPTAP
helperName = SDKManager.SdkName.TapTap;
_biService = SDKManager.Instance.GetSdkHelper(helperName) as IBIService;
#endif
#if SDK_TALKINGDATA
// TD在登录后初始化
// helperName = SDKManager.SdkName.TalkingData;
// _biService = SDKManager.Instance.GetSdkHelper(helperName) as IBIService;
#endif
if (_biService == null)
{
DebugUtil.Log("Before Login Get BI Service error, SDK helper name is {0}", helperName);
}
}
/// <summary>
/// 设置用户属性
/// </summary>
private 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);
#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)
{
_biService?.SetUserProperty(name, value);
}
#endregion
#region 释放
public void Release()
{
_tempEventDictionary = null;
_dictPool = null; // 释放对象池
if (_isInit && _networkAvailable && eventQueue.Count > 0 && _biService != null)
{
SendQueuedEvents();
}
}
#endregion
#region 周期/队列 发送
/// <summary>
/// 事件队列
/// </summary>
private readonly Queue<(string, Dictionary<string, object>)> 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();
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;
// 如果网络状态发生变化
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)
{
// 检查队列是否已满
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));
}
/// <summary>
/// 发送队列中的所有事件
/// </summary>
private void SendQueuedEvents()
{
if (_biService == null || !_networkAvailable)
return;
// 只要队列有事件就全部处理
while (eventQueue.Count > 0)
{
//将缓存的事件上报
var (eventName, param) = eventQueue.Dequeue();
DebugUtil.Log("发送缓存事件: {0}, 事件参数: {1}", eventName, JsonConvert.SerializeObject(param));
_biService.ReportEvent(eventName, param);
TrackEventPhxh(eventName, param);
}
}
private bool IsRecorded(string eventName)
{
return _storageEvent.EventList.Contains(eventName);
}
/// <summary>
/// 更新关卡信息
/// </summary>
public void SetLevelInfo(int levelID, int levelType, int levelDifficulty, int levelEvent, int param0 = 0,
int param1 = 0)
{
if (!_isInit)
return;
levelInfo.level_id = levelID;
levelInfo.level_type = levelType;
levelInfo.difficulty = levelDifficulty;
levelInfo.level_event = levelEvent;
levelInfo.param0 = param0;
levelInfo.param1 = param1;
}
/// <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
{
_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 || _biService == null)
{
EnqueueEvent(eventName, userParam);
}
else
{
DebugUtil.Log("发送事件: {0}, 事件参数: {1}", eventName, JsonConvert.SerializeObject(userParam));
_biService.ReportEvent(eventName, userParam);
TrackEventPhxh(eventName, userParam);
}
}
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;
}
TrackEvent(eventName, param1Key, param1, param2Key, param2, param3Key, param3);
}
/// <summary>
/// 上报关卡自定义事件 上报前需要更新关卡信息
/// </summary>
public void TrackLevelEvent(string eventName,
string param1Key = null, object param1 = null,
string param2Key = null, object param2 = null,
string param3Key = null, object param3 = null)
{
if (!_isInit)
return;
try
{
_tempEventDictionary.Clear();
var userParam = _tempEventDictionary;
userParam.Add("level_id", levelInfo.level_id); // 关卡ID
userParam.Add("level_type", levelInfo.level_type); // 关卡类型
userParam.Add("level_difficult", levelInfo.difficulty); // 关卡难度
userParam.Add("level_event", levelInfo.level_event); // 关卡进度
userParam.Add("level_param0", levelInfo.param0); // 关卡参数
userParam.Add("level_param1", levelInfo.param1);
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);
DebugUtil.Log("发送关卡事件: {0}, 事件参数: {1}", eventName, JsonConvert.SerializeObject(userParam));
if (_useQueueMode || _biService == null)
{
EnqueueEvent(eventName, userParam);
}
else
{
_biService.ReportEvent(eventName, userParam);
TrackEventPhxh(eventName, userParam);
}
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
/// <summary>
/// 上报一次性关卡自定义事件 上报前需要更新关卡信息
/// </summary>
public void TrackLevelEventOnce(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;
}
TrackLevelEvent(eventName, param1Key, param1, param2Key, param2, param3Key, param3);
}
#endregion
#region PHXH 服务器打点
private void TrackEventPhxh(string eventName, Dictionary<string, object> param)
{
if (!_isInit || !sendPhxh)
return;
try
{
var url = PhxhBiUrl;//PackDataInst.Inst.localInfo.httpUrl + "bi/events"; //"http://bi.kedrgame.com/api/v1/events"; //192.168.2.135:17777
baseInfo ??= new BIBaseInfo();
var timestampMs = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var sendData = new PhxhBIEvent();
if (param != null)
{
var data = JsonConvert.SerializeObject(param);
sendData.event_value = data;
}
sendData.event_key = eventName;
sendData.app_name = baseInfo.app_name;
sendData.channel = baseInfo.channel;
sendData.model = baseInfo.model;
sendData.uuid = baseInfo.uuid;
sendData.resolution = baseInfo.resolution;
sendData.platform = baseInfo.platform;
sendData.os = baseInfo.os;
sendData.version = baseInfo.version;
sendData.build_code = baseInfo.build_code;
sendData.assets_version = baseInfo.assets_version;
sendData.language = baseInfo.language;
sendData.local_time = timestampMs;
sendData.actor_id = baseInfo.actor_id;
sendData.third_platform = baseInfo.third_platform;
sendData.third_id = baseInfo.third_id;
sendData.access_key = baseInfo.access_key;
Dictionary<string, string> headData = PostHeader.PostHeaderInfo(new Dictionary<string, string>
{
{ "event_key", sendData.event_key },
{ "app_name",sendData.app_name },
{ "event_value", sendData.event_value },
{ "channel", sendData.channel },
{ "model", sendData.model },
{ "uuid", sendData.uuid },
{ "resolution", sendData.resolution },
{ "platform", sendData.platform },
{ "os", sendData.os },
{ "version", sendData.version },
{ "build_code", sendData.build_code },
{ "assets_version", sendData.assets_version },
{ "language", sendData.language },
{ "actor_id", sendData.actor_id },
{ "third_platform", sendData.third_platform },
{ "third_id", sendData.third_id },
{ "local_time", sendData.local_time.ToString() },
{ "access_key", sendData.access_key }
}, SecretKey, AccessKey);
HttpHelper.PostAsync(url, sendData, headData).Forget();
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
private class PhxhBIEvent
{
public string event_key; // 事件名称
public string event_value; // 事件参数
// public string game_id; // 游戏ID 服务器自己打
public string app_name; // 游戏名称
public string channel; // 渠道
public string model; // 设备信息
public string uuid; // 设备ID
public string resolution; // 分辨率
public string platform; // 操作系统 安卓 ios
public string os; // 操作系统
public string version; // 游戏版本
public string build_code; // 构建版本
public string assets_version; // 资源版本
public string language; // 语言
public string actor_id; // 玩家ID
public string third_platform; // 第三方账号源
public string third_id; // 第三方账号ID
public string access_key;//中台accesskey包体打包内自带
public long local_time; // 本地时间
// public string server_time; // 服务器时间 服务器自己打
}
#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
}