mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
* fix.hy3试图修复中 * Resolve dev paths and fix splash UI thread Compute a solutionRoot and expand development search paths (LanMountainDesktop and dev-test) in DeploymentLocator, add logging when scanning/finding hosts, and return distinct full paths. Ensure backward-compatible path checks. Fix cross-thread UI calls: invoke splashWindow.DismissAsync on the UI thread in LauncherFlowCoordinator, and make SplashWindow.DismissAsync ensure it runs on the UI thread before closing (simplified Close call). These changes improve development host discovery and prevent UI-thread access issues during shutdown. * Add configurable data location (portable/system) Introduce support for choosing and resolving the application's data root (system user dir vs. portable app folder). Adds DataLocationConfig model, DataLocationResolver (load/save/resolve/migrate), a UI prompt (DataLocationPromptWindow) and an OOBE step (DataLocationOobeStep) to let users pick and optionally migrate existing data. Wire the chosen data root into the launcher flow and host launch plan (forwarded via --data-root and LMD_DATA_ROOT), and add AppDataPathProvider to let runtime services read the effective data root (initialized in Program.Main). Update various services (logging, settings, DB, plugin/market, startup registry, etc.) to use the new provider/resolver and register the config type in the JSON context. This enables portable installs, safe migration, and runtime overrides via CLI or environment variable. * Add dev/debug startup flow and launch profiles Handle design-time initialization and add a developer debug startup path: App now skips normal startup when in design mode and shows a DevDebugWindow when running in debug (unless a preview or apply-update command). CommandContext.IsDebugMode is extended to include DOTNET_ENVIRONMENT=Development via a new IsDevelopmentEnvironment helper. Program.Main and BuildAvaloniaApp are made public to aid tooling. Added multiple launchSettings profiles for debug and preview commands that set DOTNET_ENVIRONMENT=Development to simplify IDE debugging and UI previewing. * Simplify splash to fade; add themed about banners Simplify splash startup visuals by removing the multi-mode/slide behavior and always using a fade animation. Update App to create SplashWindow without a StartupVisualMode parameter and remove related fields, layout configuration, slide animation, and easing helpers from SplashWindow. Clean up unused using. Replace the single about_banner asset with theme-aware variants (about_banner_dark.png and about_banner_light.png), delete the old about_banner.png, and update AboutSettingsPage to use a DynamicResource ImageBrush (AboutBannerBrush) that selects the appropriate banner per theme. * Use AppJsonContext for startup state serialization Switch serialization to the source-generated System.Text.Json context: add JsonSerializable(typeof(StartupAttemptRecord)) to AppJsonContext and replace the previous JsonSerializerOptions-based Serialize/Deserialize calls with AppJsonContext.Default.StartupAttemptRecord. Also remove the now-unused SerializerOptions field. Additionally, update .gitignore to exclude /test-aot-publish. * Add OOBE redesign, theme & data location support Introduce a redesigned OOBE flow and data-location/theme support across the launcher. Adds a new ThemeService for applying light/dark and accent colors; integrates FluentIcons.Avalonia package for icons. Overhauls OobeWindow (UX animations, typing effect, multi-step theme and data-location pages, Monet options, and final welcome step) and its code-behind to handle step navigation, accent selection, and data-location resolution. Adds DataLocation UI and handlers (DataLocationPromptWindow changes, DataLocation resolver usage) and wires a DevDebug UI for toggling/opening the data-location page. UpdateEngineService now resolves the launcher root via DataLocationResolver. Misc: update various view models, localization entries and remove TrimmerRoots.xml. * Refactor data location paths and add background service Refactor DataLocationResolver to centralize data path resolution (ResolveLauncherDataPath, ResolveDesktopDataPath, ResolveConfigPath, ResolveLauncherLogsPath, ResolveLauncherStatePath) and replace usages of the previous ".launcher" layout with a "Launcher" folder. Update API: LoadConfig/SaveConfig reorganized and ApplyLocationChoice now accepts an optional custom path and migration flag; migration logic updated accordingly. Update dependent services and views (Logger, DeploymentLocator, UpdateEngineService, OobeStateService, StartupAttemptRegistry, LauncherDebugSettingsStore, OobeWindow) to use the new resolver APIs and paths. Add LauncherBackgroundService to load/validate/cache a custom splash background image and wire it into SplashWindow (AXAML/Axaml.cs) with UI placeholders and overlay. Misc: minor cleanup of Oobe/Splash XAML and related code adjustments and logging improvements.
156 lines
5.3 KiB
C#
156 lines
5.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using Microsoft.Data.Sqlite;
|
|
|
|
namespace LanMountainDesktop.Services;
|
|
|
|
public static class AppDatabaseServiceFactory
|
|
{
|
|
private static readonly Lazy<AppDatabaseService> SharedService = new(
|
|
() => new AppDatabaseService(),
|
|
isThreadSafe: true);
|
|
|
|
public static AppDatabaseService CreateDefault()
|
|
{
|
|
return SharedService.Value;
|
|
}
|
|
}
|
|
|
|
public sealed class AppDatabaseService
|
|
{
|
|
private readonly object _schemaSyncRoot = new();
|
|
private readonly string _databasePath;
|
|
private bool _schemaInitialized;
|
|
|
|
public AppDatabaseService()
|
|
{
|
|
var dataDirectory = AppDataPathProvider.GetDataRoot();
|
|
_databasePath = Path.Combine(dataDirectory, "app.db");
|
|
}
|
|
|
|
public AppDatabaseService(string databasePath)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(databasePath))
|
|
{
|
|
throw new ArgumentException("Database path cannot be null or whitespace.", nameof(databasePath));
|
|
}
|
|
|
|
_databasePath = databasePath;
|
|
}
|
|
|
|
public SqliteConnection OpenConnection()
|
|
{
|
|
var directory = Path.GetDirectoryName(_databasePath);
|
|
if (!string.IsNullOrWhiteSpace(directory))
|
|
{
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
var connection = new SqliteConnection($"Data Source={_databasePath};Cache=Shared;Mode=ReadWriteCreate");
|
|
connection.Open();
|
|
|
|
using (var command = connection.CreateCommand())
|
|
{
|
|
command.CommandText = "PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 3000;";
|
|
command.ExecuteNonQuery();
|
|
}
|
|
|
|
EnsureSchema(connection);
|
|
return connection;
|
|
}
|
|
|
|
private void EnsureSchema(SqliteConnection connection)
|
|
{
|
|
if (_schemaInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (_schemaSyncRoot)
|
|
{
|
|
if (_schemaInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
using var command = connection.CreateCommand();
|
|
command.CommandText = """
|
|
PRAGMA journal_mode = WAL;
|
|
PRAGMA synchronous = NORMAL;
|
|
|
|
CREATE TABLE IF NOT EXISTS app_meta (
|
|
key TEXT NOT NULL PRIMARY KEY,
|
|
value TEXT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS study_session_reports (
|
|
session_id TEXT NOT NULL PRIMARY KEY,
|
|
label TEXT NOT NULL,
|
|
started_at_utc_ms INTEGER NOT NULL,
|
|
ended_at_utc_ms INTEGER NOT NULL,
|
|
duration_ms INTEGER NOT NULL,
|
|
avg_score REAL NOT NULL,
|
|
slice_count INTEGER NOT NULL,
|
|
report_json TEXT NOT NULL,
|
|
updated_at_utc_ms INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_study_session_reports_ended_at
|
|
ON study_session_reports(ended_at_utc_ms DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS study_noise_slices (
|
|
timeline_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
start_at_utc_ms INTEGER NOT NULL,
|
|
end_at_utc_ms INTEGER NOT NULL,
|
|
source_type INTEGER NOT NULL,
|
|
session_id TEXT NULL,
|
|
score REAL NOT NULL,
|
|
avg_db REAL NOT NULL,
|
|
p95_db REAL NOT NULL,
|
|
p50_dbfs REAL NOT NULL,
|
|
over_ratio_dbfs REAL NOT NULL,
|
|
segment_count INTEGER NOT NULL,
|
|
slice_json TEXT NOT NULL,
|
|
created_at_utc_ms INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_study_noise_slices_end_at
|
|
ON study_noise_slices(end_at_utc_ms DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_study_noise_slices_source_time
|
|
ON study_noise_slices(source_type, end_at_utc_ms DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS attendance_sessions (
|
|
session_id TEXT NOT NULL PRIMARY KEY,
|
|
label TEXT NOT NULL,
|
|
started_at_utc_ms INTEGER NOT NULL,
|
|
ended_at_utc_ms INTEGER NULL,
|
|
status TEXT NOT NULL,
|
|
score REAL NULL,
|
|
payload_json TEXT NULL,
|
|
created_at_utc_ms INTEGER NOT NULL,
|
|
updated_at_utc_ms INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_attendance_sessions_started_at
|
|
ON attendance_sessions(started_at_utc_ms DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS attendance_events (
|
|
event_id TEXT NOT NULL PRIMARY KEY,
|
|
session_id TEXT NOT NULL,
|
|
event_type TEXT NOT NULL,
|
|
occurred_at_utc_ms INTEGER NOT NULL,
|
|
payload_json TEXT NULL,
|
|
created_at_utc_ms INTEGER NOT NULL,
|
|
FOREIGN KEY(session_id) REFERENCES attendance_sessions(session_id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_attendance_events_session_time
|
|
ON attendance_events(session_id, occurred_at_utc_ms DESC);
|
|
""";
|
|
command.ExecuteNonQuery();
|
|
_schemaInitialized = true;
|
|
}
|
|
}
|
|
}
|