using System.Text.Json; using SSPCTester.Logic.Models; namespace SSPCTester.Logic.Storage; /// /// 项目读写:每项目一个独立文件夹,含 project.json / curves/ / report.pdf。 /// public sealed class ProjectStore { private static readonly JsonSerializerOptions _jsonOpts = new() { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; /// 项目根目录(来自设置页)。 public string ProjectsRoot { get; set; } = ""; public IEnumerable EnumerateProjectFolders() { if (string.IsNullOrWhiteSpace(ProjectsRoot) || !Directory.Exists(ProjectsRoot)) yield break; foreach (var dir in Directory.EnumerateDirectories(ProjectsRoot)) { if (File.Exists(Path.Combine(dir, "project.json"))) yield return dir; } } public async Task LoadAsync(string folder) { var file = Path.Combine(folder, "project.json"); if (!File.Exists(file)) return null; await using var fs = File.OpenRead(file); var project = await JsonSerializer.DeserializeAsync(fs, _jsonOpts); if (project != null) { project.FolderPath = folder; project.TestFunction ??= new TestFunctionNotes(); if (HasLegacyZeroBasedChannels(project)) { project.BasicResults.Clear(); project.CurveResults.Clear(); project.PowerResults.Clear(); } } return project; } private static bool HasLegacyZeroBasedChannels(Project project) => project.BasicResults.Any(x => x.Channel == 0) || project.CurveResults.Any(x => x.Channel == 0) || project.PowerResults.Any(x => x.Channel == 0); public async Task SaveAsync(Project project) { if (string.IsNullOrWhiteSpace(project.FolderPath)) { if (string.IsNullOrWhiteSpace(ProjectsRoot)) throw new InvalidOperationException("尚未配置 ProjectsRoot。"); string safeName = MakeSafeName(project.Name); project.FolderPath = Path.Combine(ProjectsRoot, safeName); } Directory.CreateDirectory(project.FolderPath); Directory.CreateDirectory(Path.Combine(project.FolderPath, "curves")); var file = Path.Combine(project.FolderPath, "project.json"); await using var fs = File.Create(file); await JsonSerializer.SerializeAsync(fs, project, _jsonOpts); } public async Task> LoadAllAsync() { var list = new List(); foreach (var folder in EnumerateProjectFolders()) { try { var p = await LoadAsync(folder); if (p != null) list.Add(p); } catch { /* 跳过损坏的项目 */ } } return list.OrderByDescending(p => p.LastTestedAt == default ? p.CreatedAt : p.LastTestedAt).ToList(); } private static string MakeSafeName(string name) { var invalid = Path.GetInvalidFileNameChars(); return string.Concat(name.Select(c => invalid.Contains(c) ? '_' : c)); } }