多轮审查和修复
This commit is contained in:
134
Assets/Scripts/Support/Analytics/AnalyticsManager.cs
Normal file
134
Assets/Scripts/Support/Analytics/AnalyticsManager.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Newtonsoft.Json;
|
||||
using BaseGames.Core;
|
||||
|
||||
namespace BaseGames.Support.Analytics
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 游戏玩法数据分析收集器(架构 16_SupportingModules §7)。
|
||||
/// 在本地记录结构化事件日志,供后续分析或上传。
|
||||
/// 注:不收集任何个人身份信息(PII)。
|
||||
/// </summary>
|
||||
public class AnalyticsManager : MonoBehaviour, IAnalyticsService
|
||||
{
|
||||
[Header("配置")]
|
||||
[Tooltip("是否启用分析收集")]
|
||||
[SerializeField] private bool _enabled = true;
|
||||
[Tooltip("超过此条数时自动将缓存刷写到磁盘")]
|
||||
[SerializeField] private int _flushThreshold = 50;
|
||||
|
||||
private readonly List<AnalyticsEvent> _buffer = new();
|
||||
private string _logPath;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
#if !UNITY_EDITOR && !DEVELOPMENT_BUILD
|
||||
if (!_enabled) { enabled = false; return; }
|
||||
#endif
|
||||
if (ServiceLocator.GetOrDefault<IAnalyticsService>() != null) { Destroy(gameObject); return; }
|
||||
ServiceLocator.Register<IAnalyticsService>(this);
|
||||
_logPath = Path.Combine(Application.persistentDataPath, "analytics.json");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Flush();
|
||||
ServiceLocator.Unregister<IAnalyticsService>(this);
|
||||
}
|
||||
|
||||
private void OnApplicationQuit() => Flush();
|
||||
|
||||
// ── 实例方法(IAnalyticsService 实现)────────────────────────────────────
|
||||
/// <summary>记录一个自定义分析事件。</summary>
|
||||
public void Track(string eventName, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
if (!_enabled) return;
|
||||
Enqueue(eventName, parameters);
|
||||
}
|
||||
|
||||
// ── 预定义事件 ────────────────────────────────────────────────────────────
|
||||
public void TrackBossKill(string bossId, float duration, int deathCount)
|
||||
=> Track("boss_kill", new Dictionary<string, object>
|
||||
{
|
||||
{ "boss_id", bossId },
|
||||
{ "duration", duration },
|
||||
{ "death_count", deathCount },
|
||||
});
|
||||
|
||||
public void TrackDeath(string cause, string sceneId, Vector2 position)
|
||||
=> Track("player_death", new Dictionary<string, object>
|
||||
{
|
||||
{ "cause", cause },
|
||||
{ "scene", sceneId },
|
||||
{ "pos_x", Mathf.RoundToInt(position.x) },
|
||||
{ "pos_y", Mathf.RoundToInt(position.y) },
|
||||
});
|
||||
|
||||
public void TrackAbilityUnlock(string abilityId)
|
||||
=> Track("ability_unlock", new Dictionary<string, object>
|
||||
{
|
||||
{ "ability", abilityId }
|
||||
});
|
||||
|
||||
public void TrackSessionStart(int slotIndex)
|
||||
=> Track("session_start", new Dictionary<string, object>
|
||||
{
|
||||
{ "slot", slotIndex },
|
||||
});
|
||||
|
||||
public void TrackSessionEnd(float playtimeSeconds)
|
||||
=> Track("session_end", new Dictionary<string, object>
|
||||
{
|
||||
{ "playtime", playtimeSeconds },
|
||||
});
|
||||
|
||||
// ── 内部实现 ──────────────────────────────────────────────────────────────
|
||||
private void Enqueue(string name, Dictionary<string, object> parms)
|
||||
{
|
||||
_buffer.Add(new AnalyticsEvent
|
||||
{
|
||||
EventName = name,
|
||||
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Parameters = parms ?? new Dictionary<string, object>(),
|
||||
});
|
||||
|
||||
if (_buffer.Count >= _flushThreshold)
|
||||
Flush();
|
||||
}
|
||||
|
||||
private void Flush()
|
||||
{
|
||||
if (_buffer.Count == 0) return;
|
||||
try
|
||||
{
|
||||
var existing = new List<AnalyticsEvent>();
|
||||
if (File.Exists(_logPath))
|
||||
{
|
||||
string json = File.ReadAllText(_logPath);
|
||||
var loaded = JsonConvert.DeserializeObject<List<AnalyticsEvent>>(json);
|
||||
if (loaded != null) existing.AddRange(loaded);
|
||||
}
|
||||
existing.AddRange(_buffer);
|
||||
File.WriteAllText(_logPath, JsonConvert.SerializeObject(existing, Formatting.Indented));
|
||||
_buffer.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[Analytics] 写入失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class AnalyticsEvent
|
||||
{
|
||||
public string EventName;
|
||||
public long Timestamp;
|
||||
public Dictionary<string, object> Parameters;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace BaseGames.Support.Analytics
|
||||
Reference in New Issue
Block a user