SSPC-Tester/SSPCTester.Logic/Storage/ProjectStore.cs

93 lines
3.2 KiB
C#
Raw Permalink Normal View History

using System.Text.Json;
using SSPCTester.Logic.Models;
namespace SSPCTester.Logic.Storage;
/// <summary>
/// 项目读写:每项目一个独立文件夹,含 project.json / curves/ / report.pdf。
/// </summary>
public sealed class ProjectStore
{
private static readonly JsonSerializerOptions _jsonOpts = new()
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
/// <summary>项目根目录(来自设置页)。</summary>
public string ProjectsRoot { get; set; } = "";
public IEnumerable<string> 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<Project?> 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<Project>(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<List<Project>> LoadAllAsync()
{
var list = new List<Project>();
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));
}
}