Phase 1: data models and local storage service

- Add Models: Category, ToolItem (IsValid runtime-only, not stored), AppConfig

- Add Services: IDataService + JsonDataService (JSON load/save)

- Path validation on Load(): File.Exists check, logs warning for missing paths

- Startup: auto-calls dataService.Load() to load config
This commit is contained in:
2026-05-09 20:34:36 +08:00
parent 875e331116
commit 752f09a7e4
6 changed files with 222 additions and 0 deletions

View File

@@ -20,6 +20,10 @@ public partial class App : Application
ConfigureServices(services);
Services = services.BuildServiceProvider();
// 启动时加载配置文件(含路径验证与容错)
var dataService = Services.GetRequiredService<IDataService>();
dataService.Load();
var mainWindow = Services.GetRequiredService<MainWindow>();
mainWindow.Show();
}
@@ -32,6 +36,9 @@ public partial class App : Application
// 日志服务
services.AddSingleton<ILogService, LogService>();
// 数据持久化服务
services.AddSingleton<IDataService, JsonDataService>();
// 主窗口
services.AddSingleton<MainWindow>();
}

View File

@@ -0,0 +1,27 @@
namespace PersonalToolBox.Models;
/// <summary>
/// 应用全局配置,对应 config.json 的顶层结构
/// </summary>
public class AppConfig
{
/// <summary>
/// 主题模式 (Dark / Light)
/// </summary>
public string Theme { get; set; } = "Dark";
/// <summary>
/// 是否开机自启动
/// </summary>
public bool AutoStart { get; set; } = false;
/// <summary>
/// 工具分类列表
/// </summary>
public List<Category> Categories { get; set; } = new();
/// <summary>
/// 工具项列表
/// </summary>
public List<ToolItem> Tools { get; set; } = new();
}

View File

@@ -0,0 +1,17 @@
namespace PersonalToolBox.Models;
/// <summary>
/// 工具标签页分类
/// </summary>
public class Category
{
/// <summary>
/// 分类唯一标识
/// </summary>
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// 分类显示名称(如:开发工具、常用脚本)
/// </summary>
public string Name { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,50 @@
using System.Text.Json.Serialization;
namespace PersonalToolBox.Models;
/// <summary>
/// 工具项模型,表示用户添加的一个可执行工具
/// </summary>
public class ToolItem
{
/// <summary>
/// 工具唯一标识 (UUID)
/// </summary>
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// 工具显示名称
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 内置图标库的字符编码
/// </summary>
public string IconCode { get; set; } = string.Empty;
/// <summary>
/// 可执行文件路径或 URL
/// </summary>
public string ExecutablePath { get; set; } = string.Empty;
/// <summary>
/// 运行参数(选填)
/// </summary>
public string Arguments { get; set; } = string.Empty;
/// <summary>
/// 所属分类 ID
/// </summary>
public string CategoryId { get; set; } = string.Empty;
/// <summary>
/// 全局快捷键(如 Ctrl+Alt+T选填
/// </summary>
public string HotKey { get; set; } = string.Empty;
/// <summary>
/// 路径是否有效(仅运行时计算,不存入 JSON
/// </summary>
[JsonIgnore]
public bool IsValid { get; set; } = true;
}

View File

@@ -0,0 +1,24 @@
using PersonalToolBox.Models;
namespace PersonalToolBox.Services;
/// <summary>
/// 数据持久化服务接口,负责配置的加载和保存
/// </summary>
public interface IDataService
{
/// <summary>
/// 当前加载的配置数据
/// </summary>
AppConfig Config { get; }
/// <summary>
/// 从 config.json 加载配置,验证工具路径有效性
/// </summary>
void Load();
/// <summary>
/// 保存当前配置到 config.json
/// </summary>
void Save();
}

View File

@@ -0,0 +1,97 @@
using System.IO;
using System.Text.Json;
using PersonalToolBox.Models;
namespace PersonalToolBox.Services;
/// <summary>
/// 基于 JSON 文件的数据持久化服务
/// 配置文件路径: 可执行程序目录/config.json
/// </summary>
public class JsonDataService : IDataService
{
private readonly ILogService _logService;
private readonly string _filePath;
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNameCaseInsensitive = true
};
public AppConfig Config { get; private set; } = new();
public JsonDataService(ILogService logService)
{
_logService = logService;
_filePath = Path.Combine(AppContext.BaseDirectory, "config.json");
}
public void Load()
{
if (!File.Exists(_filePath))
{
_logService.Info("配置文件不存在,使用默认配置");
Config = new AppConfig();
Save();
return;
}
try
{
var json = File.ReadAllText(_filePath);
var config = JsonSerializer.Deserialize<AppConfig>(json, JsonOptions);
if (config == null)
{
_logService.Warning("配置文件解析结果为空,使用默认配置");
Config = new AppConfig();
return;
}
Config = config;
// 核心容错逻辑:验证所有工具路径是否有效
foreach (var tool in Config.Tools)
{
if (string.IsNullOrWhiteSpace(tool.ExecutablePath))
continue;
// URL 格式的路径(如 https://...)不需要检查本地文件是否存在
if (Uri.TryCreate(tool.ExecutablePath, UriKind.Absolute, out var uri) &&
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
{
tool.IsValid = true;
continue;
}
if (!File.Exists(tool.ExecutablePath))
{
tool.IsValid = false;
_logService.Warning($"工具 \"{tool.Name}\" 路径失效,找不到文件: {tool.ExecutablePath}");
}
}
_logService.Info($"配置加载完成: {Config.Categories.Count} 个分类, {Config.Tools.Count} 个工具");
}
catch (JsonException ex)
{
_logService.Error($"配置文件 JSON 解析失败: {ex.Message}");
Config = new AppConfig();
}
}
public void Save()
{
try
{
var json = JsonSerializer.Serialize(Config, JsonOptions);
File.WriteAllText(_filePath, json);
_logService.Info("配置已保存");
}
catch (Exception ex)
{
_logService.Error($"配置保存失败: {ex.Message}");
}
}
}