using System; using System.Collections.Generic; using asap.core; using UnityEngine; namespace Activity { /// /// 入账/扣账成功后由 发布, /// 供各活动自行判断是否刷新入口合并红点等(避免订阅者与具体玩法耦合)。 /// public readonly struct ActivityEventTokenBalanceChanged { public int EventId { get; } public ActivityEventTokenBalanceChanged(int eventId) { EventId = eventId; } } } namespace Activity.Services { /// /// 活动代币服务实现 /// 负责管理所有活动的代币数据,提供统一的存储接口 /// public class EventTokenService : IEventTokenService { #region 常量与字段 private const string TransitionDataKey = "TransitionData"; private Dictionary _tokenData = new Dictionary(); private readonly object _lock = new object(); private bool _isInitialized; private readonly game.PlayerDataStorage.IPlayerSaveStore store; public EventTokenService(game.PlayerDataStorage.IPlayerSaveStore store) { this.store = store ?? throw new ArgumentNullException(nameof(store)); } #endregion #region 初始化 public void Initialize() { lock (_lock) { if (!store.IsCurrent) throw new InvalidOperationException("Activity token session is no longer current."); if (_isInitialized) return; var read = store.Read(TransitionDataKey); var value = read.ValueOrThrow(); Dictionary loaded; if (read.Found == false) loaded = new Dictionary(); else { if (string.IsNullOrWhiteSpace(value)) throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be empty."); loaded = Newtonsoft.Json.JsonConvert.DeserializeObject>(value); if (loaded == null) throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be null."); } _tokenData = loaded; _isInitialized = true; } } // Publish the candidate balance only after the current store accepts its exact JSON. // Acceptance remains distinct from cloud confirmation, which belongs to the enclosing action. private void SaveTokens(Dictionary candidate) { store.Write(TransitionDataKey, Newtonsoft.Json.JsonConvert.SerializeObject(candidate)).RequireAccepted(); _tokenData = candidate; } #endregion #region 基础操作 public void AddToken(int eventId, int count) { if (eventId <= 0) { Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}"); return; } if (count == 0) return; lock (_lock) { try { EnsureInitialized(); var currentCount = GetTokenCount(eventId); var candidate = new Dictionary(_tokenData); candidate[eventId] = currentCount + count; SaveTokens(candidate); #if UNITY_EDITOR Debug.Log($"[EventTokenService] Added {count} tokens to event {eventId}, new total: {_tokenData[eventId]}"); #endif } catch (Exception e) { Debug.LogError($"[EventTokenService] AddToken failed for event {eventId}: {e.Message}"); throw; } } PublishActivityEventTokenBalanceChanged(eventId); } public int RemoveToken(int eventId, int count) { if (eventId <= 0) { Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}"); return 0; } int actualRemoved = 0; lock (_lock) { try { EnsureInitialized(); var currentCount = GetTokenCount(eventId); actualRemoved = Mathf.Min(count, currentCount); var newCount = currentCount - actualRemoved; var candidate = new Dictionary(_tokenData); if (newCount <= 0) { candidate.Remove(eventId); } else { candidate[eventId] = newCount; } SaveTokens(candidate); #if UNITY_EDITOR Debug.Log($"[EventTokenService] Removed {actualRemoved} tokens from event {eventId}, remaining: {newCount}"); #endif } catch (Exception e) { Debug.LogError($"[EventTokenService] RemoveToken failed for event {eventId}: {e.Message}"); throw; } } PublishActivityEventTokenBalanceChanged(eventId); return actualRemoved; } public int GetTokenCount(int eventId) { if (eventId <= 0) return 0; lock (_lock) { try { EnsureInitialized(); return _tokenData.GetValueOrDefault(eventId, 0); } catch (Exception e) { Debug.LogError($"[EventTokenService] GetTokenCount failed for event {eventId}: {e.Message}"); throw; } } } public void SetToken(int eventId, int count) { if (eventId <= 0) { Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}"); return; } lock (_lock) { try { EnsureInitialized(); var candidate = new Dictionary(_tokenData); candidate[eventId] = count; SaveTokens(candidate); #if UNITY_EDITOR Debug.Log($"[EventTokenService] Set event {eventId} token count to {count}"); #endif } catch (Exception e) { Debug.LogError($"[EventTokenService] SetToken failed for event {eventId}: {e.Message}"); throw; } } } public void ClearToken(int eventId) { if (eventId <= 0) return; lock (_lock) { try { EnsureInitialized(); var candidate = new Dictionary(_tokenData); if (candidate.Remove(eventId)) { SaveTokens(candidate); } } catch (Exception e) { Debug.LogError($"[EventTokenService] ClearToken failed for event {eventId}: {e.Message}"); throw; } } } #endregion #region 批量操作 public Dictionary GetAllTokens() { lock (_lock) { EnsureInitialized(); return new Dictionary(_tokenData); } } public void SetTokens(Dictionary tokens) { if (tokens == null) return; lock (_lock) { try { EnsureInitialized(); var candidate = new Dictionary(); foreach (var kvp in tokens) { if (kvp.Key > 0) { candidate[kvp.Key] = kvp.Value; } } SaveTokens(candidate); #if UNITY_EDITOR Debug.Log($"[EventTokenService] Set {tokens.Count} token entries"); #endif } catch (Exception e) { Debug.LogError($"[EventTokenService] SetTokens failed: {e.Message}"); throw; } } } public void ClearTokens(IEnumerable eventIds) { if (eventIds == null) return; lock (_lock) { try { EnsureInitialized(); var candidate = new Dictionary(_tokenData); bool changed = false; foreach (var eventId in eventIds) { if (candidate.Remove(eventId)) { changed = true; } } if (changed) { SaveTokens(candidate); } } catch (Exception e) { Debug.LogError($"[EventTokenService] ClearTokens failed: {e.Message}"); throw; } } } #endregion #region 私有辅助方法 private void EnsureInitialized() { Initialize(); } private static void PublishActivityEventTokenBalanceChanged(int eventId) { try { GContext.Publish(new Activity.ActivityEventTokenBalanceChanged(eventId)); } catch { // GContext 未就绪等 } } #endregion } }