feat: 重构 TodoList 架构,新增动态 API 与 MAUI 内嵌 Web 服务

This commit is contained in:
ShaoHua
2026-04-04 22:11:18 +08:00
parent 81c649cb23
commit aa5407daeb
91 changed files with 3425 additions and 1978 deletions
+78 -5
View File
@@ -1,4 +1,10 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Data.Sqlite;
using TodoList.Application;
using TodoList.Application.Data;
using TodoList.Maui.Models;
using TodoList.Maui.Services;
using TodoList.Maui.Services.Platforms;
@@ -17,21 +23,88 @@ public static class MauiProgram
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddSingleton<IHotKeySettingsService, HotKeySettingsService>();
builder.Services.AddSingleton<IGlobalHotKeyService>(sp => new NullGlobalHotKeyService());
var appSettings = LoadAppSettings();
// Set default connection string if not provided
if (string.IsNullOrEmpty(appSettings.WebServer.ConnectionString))
{
var dbPath = Path.Combine(FileSystem.AppDataDirectory, "todolist.db");
appSettings.WebServer.ConnectionString = $"Data Source={dbPath}";
}
builder.Services.AddSingleton(appSettings);
var connectionString = appSettings.WebServer.ConnectionString;
builder.Services.AddApplicationServices(connectionString);
builder.Services.AddSingleton<IHotKeySettingsService>(sp =>
new HotKeySettingsService(sp.GetRequiredService<AppSettings>()));
builder.Services.AddSingleton<IGlobalHotKeyService>(sp => GlobalHotKeyServiceFactory.Create());
builder.Services.AddSingleton<ISystemTrayService>(sp =>
{
#if WINDOWS
return new NullSystemTrayService();
return new WindowsSystemTrayService();
#else
return new NullSystemTrayService();
#endif
});
builder.Services.AddSingleton<IEmbeddedWebServerService, EmbeddedWebServerService>();
#if DEBUG
builder.Logging.AddDebug();
builder.Logging.AddDebug();
#endif
return builder.Build();
var app = builder.Build();
// Ensure database directory exists and apply migrations
using (var scope = app.Services.CreateScope())
{
try
{
var dbContext = scope.ServiceProvider.GetRequiredService<TodoDbContext>();
// Ensure database directory exists for the actual connection string
var sqliteBuilder = new SqliteConnectionStringBuilder(connectionString);
var actualDbPath = sqliteBuilder.DataSource;
if (!string.IsNullOrEmpty(actualDbPath))
{
// If it's a relative path, we might need to resolve it,
// but for SQLite, it's usually better to have absolute paths.
// For MAUI, FileSystem.AppDataDirectory returns an absolute path.
var dbDir = Path.GetDirectoryName(actualDbPath);
if (!string.IsNullOrEmpty(dbDir) && !Directory.Exists(dbDir))
{
Directory.CreateDirectory(dbDir);
}
}
// Ensure database is up to date
dbContext.Database.Migrate();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Database initialization failed: {ex.Message}");
// Fallback to EnsureCreated if Migrate fails (though Migrate is preferred)
using var context = scope.ServiceProvider.GetRequiredService<TodoDbContext>();
context.Database.EnsureCreated();
}
}
var webServer = app.Services.GetRequiredService<IEmbeddedWebServerService>();
_ = webServer.StartAsync();
return app;
}
private static AppSettings LoadAppSettings()
{
var settingsPath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
if (!File.Exists(settingsPath))
{
return new AppSettings();
}
var json = File.ReadAllText(settingsPath);
return JsonSerializer.Deserialize<AppSettings>(json) ?? new AppSettings();
}
}