From 3c4e0b51a7fcba23eb6d315b8d796cff87b3bc87 Mon Sep 17 00:00:00 2001 From: pipizhu Date: Fri, 23 Jan 2026 11:38:49 +0800 Subject: [PATCH] fix: windows save history --- .claude/settings.local.json | 5 +- .../KeyStats/Services/StatsManager.cs | 65 +++++++++++++------ 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 57667eb..e88a8fe 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,10 @@ "Bash(dotnet build:*)", "Bash(taskkill:*)", "Bash(powershell:*)", - "Bash(terminal-notifier:*)" + "Bash(terminal-notifier:*)", + "Bash(ls:*)", + "Bash(gh pr list:*)", + "Bash(git show:*)" ] } } diff --git a/KeyStats.Windows/KeyStats/Services/StatsManager.cs b/KeyStats.Windows/KeyStats/Services/StatsManager.cs index 74848a6..62ea261 100644 --- a/KeyStats.Windows/KeyStats/Services/StatsManager.cs +++ b/KeyStats.Windows/KeyStats/Services/StatsManager.cs @@ -334,13 +334,18 @@ private void SaveStats() { var json = JsonSerializer.Serialize(CurrentStats, new JsonSerializerOptions { WriteIndented = true }); var tempPath = _statsFilePath + ".tmp"; + var backupPath = _statsFilePath + ".bak"; File.WriteAllText(tempPath, json); - // .NET Framework 4.8 兼容:File.Move 没有 3 参数重载,需要先删除 + if (File.Exists(_statsFilePath)) { - File.Delete(_statsFilePath); + // 原子替换:临时文件 → 目标文件,原文件 → 备份文件 + File.Replace(tempPath, _statsFilePath, backupPath); + } + else + { + File.Move(tempPath, _statsFilePath); } - File.Move(tempPath, _statsFilePath); } catch (Exception ex) { @@ -348,6 +353,7 @@ private void SaveStats() } RecordCurrentStatsToHistory(); + SaveHistory(); } } @@ -382,7 +388,6 @@ private void RecordCurrentStatsToHistory() KeyPressCounts = new Dictionary(CurrentStats.KeyPressCounts) }; History[key] = statsCopy; - // SaveHistory is called by the timer callback, no need to call it here } private void SaveHistory() @@ -391,13 +396,17 @@ private void SaveHistory() { var json = JsonSerializer.Serialize(History, new JsonSerializerOptions { WriteIndented = true }); var tempPath = _historyFilePath + ".tmp"; + var backupPath = _historyFilePath + ".bak"; File.WriteAllText(tempPath, json); - // .NET Framework 4.8 兼容:File.Move 没有 3 参数重载,需要先删除 - if (File.Exists(_historyFilePath)) - { - File.Delete(_historyFilePath); - } + + if (File.Exists(_historyFilePath)) + { + File.Replace(tempPath, _historyFilePath, backupPath); + } + else + { File.Move(tempPath, _historyFilePath); + } } catch (Exception ex) { @@ -449,13 +458,17 @@ public void SaveSettings() { var json = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true }); var tempPath = _settingsFilePath + ".tmp"; + var backupPath = _settingsFilePath + ".bak"; File.WriteAllText(tempPath, json); - // .NET Framework 4.8 兼容:File.Move 没有 3 参数重载,需要先删除 - if (File.Exists(_settingsFilePath)) - { - File.Delete(_settingsFilePath); - } + + if (File.Exists(_settingsFilePath)) + { + File.Replace(tempPath, _settingsFilePath, backupPath); + } + else + { File.Move(tempPath, _settingsFilePath); + } } catch (Exception ex) { @@ -512,7 +525,11 @@ private void PerformMidnightReset() ResetStats(now); } // Also prune old history entries during midnight reset - PruneOldHistory(History); + lock (_lock) + { + PruneOldHistory(History); + SaveHistory(); + } ScheduleNextMidnightReset(); } @@ -525,6 +542,11 @@ private void ResetStats(DateTime date) { lock (_lock) { + // 先保存旧数据到 History,避免丢失最后一次保存后的增量 + RecordCurrentStatsToHistory(); + SaveHistory(); + + // 然后创建新的统计对象 CurrentStats = new DailyStats(date); } @@ -642,12 +664,15 @@ public enum HistoryMetric { KeyPresses, Clicks, MouseDistance, ScrollDistance } public List<(DateTime Date, double Value)> GetHistorySeries(HistoryRange range, HistoryMetric metric) { var dates = GetDatesInRange(range); - return dates.Select(date => + lock (_lock) { - var key = date.ToString("yyyy-MM-dd"); - var stats = History.TryGetValue(key, out var s) ? s : new DailyStats(date); - return (date, GetMetricValue(metric, stats)); - }).ToList(); + return dates.Select(date => + { + var key = date.ToString("yyyy-MM-dd"); + var stats = History.TryGetValue(key, out var s) ? s : new DailyStats(date); + return (date, GetMetricValue(metric, stats)); + }).ToList(); + } } public string FormatHistoryValue(HistoryMetric metric, double value)