using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading.Tasks; using AxCopilot.Models; namespace AxCopilot.Services; internal static class UsageStatisticsService { private static readonly string _statsDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "stats"); private static DailyUsageStats _today = new DailyUsageStats(); private static bool _initialized; private static readonly object _lock = new object(); private const int RetentionDays = 30; public static void RecordLauncherOpen() { EnsureInitialized(); lock (_lock) { _today.LauncherOpens++; } SaveTodayAsync(); } public static void RecordCommandUsage(string commandKey) { if (!string.IsNullOrWhiteSpace(commandKey)) { EnsureInitialized(); lock (_lock) { _today.CommandUsage.TryGetValue(commandKey, out var value); _today.CommandUsage[commandKey] = value + 1; } SaveTodayAsync(); } } public static void AddActiveSeconds(int seconds) { if (seconds > 0) { EnsureInitialized(); lock (_lock) { _today.ActiveSeconds += seconds; } SaveTodayAsync(); } } public static int GetTodayActiveSeconds() { EnsureInitialized(); lock (_lock) { return _today.ActiveSeconds; } } public static void RecordChat(string tab) { if (string.IsNullOrWhiteSpace(tab)) { tab = "Chat"; } EnsureInitialized(); lock (_lock) { _today.ChatCounts.TryGetValue(tab, out var value); _today.ChatCounts[tab] = value + 1; } SaveTodayAsync(); } public static void RecordTokens(int promptTokens, int completionTokens) { if (promptTokens > 0 || completionTokens > 0) { EnsureInitialized(); lock (_lock) { _today.PromptTokens += promptTokens; _today.CompletionTokens += completionTokens; _today.TotalTokens += promptTokens + completionTokens; } SaveTodayAsync(); } } public static List GetStats(int days = 30) { EnsureInitialized(); List list = new List(); DateTime today = DateTime.Today; for (int num = days - 1; num >= 0; num--) { string text = today.AddDays(-num).ToString("yyyy-MM-dd"); if (text == _today.Date) { DailyUsageStats item; lock (_lock) { item = CloneToday(); } list.Add(item); } else { string filePath = GetFilePath(text); if (File.Exists(filePath)) { try { string json = File.ReadAllText(filePath); DailyUsageStats dailyUsageStats = JsonSerializer.Deserialize(json); if (dailyUsageStats != null) { list.Add(dailyUsageStats); } else { list.Add(new DailyUsageStats { Date = text }); } } catch { list.Add(new DailyUsageStats { Date = text }); } } else { list.Add(new DailyUsageStats { Date = text }); } } } return list; } private static void EnsureInitialized() { if (_initialized) { return; } lock (_lock) { if (_initialized) { return; } try { Directory.CreateDirectory(_statsDir); string text = DateTime.Today.ToString("yyyy-MM-dd"); string filePath = GetFilePath(text); if (File.Exists(filePath)) { string json = File.ReadAllText(filePath); DailyUsageStats dailyUsageStats = JsonSerializer.Deserialize(json); _today = dailyUsageStats ?? new DailyUsageStats { Date = text }; } else { _today = new DailyUsageStats { Date = text }; } PurgeOldFiles(); } catch (Exception ex) { LogService.Warn("통계 서비스 초기화 실패: " + ex.Message); _today = new DailyUsageStats { Date = DateTime.Today.ToString("yyyy-MM-dd") }; } _initialized = true; } } private static void PurgeOldFiles() { try { DateTime dateTime = DateTime.Today.AddDays(-30.0); string[] files = Directory.GetFiles(_statsDir, "????-??-??.json"); foreach (string path in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); if (DateTime.TryParse(fileNameWithoutExtension, out var result) && result < dateTime) { File.Delete(path); } } } catch (Exception ex) { LogService.Warn("통계 파일 정리 실패: " + ex.Message); } } private static async Task SaveTodayAsync() { try { DailyUsageStats snapshot; lock (_lock) { snapshot = CloneToday(); } string path = GetFilePath(snapshot.Date); string json = JsonSerializer.Serialize(snapshot, new JsonSerializerOptions { WriteIndented = false }); await File.WriteAllTextAsync(path, json); } catch (Exception ex) { Exception ex2 = ex; LogService.Warn("통계 저장 실패: " + ex2.Message); } } private static DailyUsageStats CloneToday() { return new DailyUsageStats { Date = _today.Date, LauncherOpens = _today.LauncherOpens, ActiveSeconds = _today.ActiveSeconds, CommandUsage = new Dictionary(_today.CommandUsage), ChatCounts = new Dictionary(_today.ChatCounts), TotalTokens = _today.TotalTokens, PromptTokens = _today.PromptTokens, CompletionTokens = _today.CompletionTokens }; } private static string GetFilePath(string dateStr) { return Path.Combine(_statsDir, dateStr + ".json"); } }