using Serilog; using SqlSugar; using YZWater.Core.Models; namespace YZWater.Core.Services; /// /// 数据库服务 - 使用 SqlSugar + SQLite /// public static class DatabaseService { private static SqlSugarClient? _db; private static readonly string DbPath = "yzwater.db"; /// /// 数据库实例 /// public static ISqlSugarClient Db => _db ?? throw new InvalidOperationException("数据库未初始化,请先调用 Initialize()"); /// /// 初始化数据库(需先调用 LogService.Initialize()) /// public static void Initialize() { InitDatabase(); } private static void InitDatabase() { _db = new SqlSugarClient(new ConnectionConfig { ConnectionString = $"Data Source={DbPath}", DbType = DbType.Sqlite, IsAutoCloseConnection = true, InitKeyType = InitKeyType.Attribute }); _db.Aop.OnLogExecuting = (sql, pars) => { Log.Debug("SQL: {Sql}", sql); }; _db.DbMaintenance.CreateDatabase(); // 启用 SQLite WAL 模式(提升并发读写性能) try { _db.Ado.ExecuteCommand("PRAGMA journal_mode=WAL"); _db.Ado.ExecuteCommand("PRAGMA synchronous=NORMAL"); // WAL 模式下推荐 _db.Ado.ExecuteCommand("PRAGMA cache_size=-64000"); // 64MB 缓存 _db.Ado.ExecuteCommand("PRAGMA temp_store=MEMORY"); // 临时表使用内存 Log.Information("SQLite WAL 模式已启用"); } catch (Exception ex) { Log.Warning(ex, "启用 WAL 模式失败(继续使用默认模式)"); } // 逐个初始化表,单个失败不删除数据也不影响其他表 var tables = new[] { typeof(Person), typeof(AlarmRecord), typeof(FlowRecord), typeof(EquipmentStatus), typeof(AuditLog), typeof(User), typeof(AnalogRecord) }; foreach (var tableType in tables) { try { _db.CodeFirst.InitTables(tableType); } catch (Exception ex) { // 仅记录错误,继续初始化其他表,不删除数据库 Log.Error(ex, "初始化表 {Table} 失败(保留已有数据,跳过此表)", tableType.Name); } } try { AuthService.Initialize(); } catch (Exception ex) { Log.Error(ex, "AuthService 初始化失败(默认管理员可能未创建)"); } // 创建索引以提升查询性能(忽略已存在的索引) try { CreateIndexes(); } catch (Exception ex) { Log.Warning(ex, "创建索引失败(不影响功能)"); } Log.Information("数据库初始化完成"); } /// /// 创建常用查询索引 /// private static void CreateIndexes() { var indexSqls = new[] { "CREATE INDEX IF NOT EXISTS idx_flow_record_time ON flow_records(RecordTime)", "CREATE INDEX IF NOT EXISTS idx_alarm_time ON alarm_records(AlarmTime)", "CREATE INDEX IF NOT EXISTS idx_equip_device_id ON equipment_status(DeviceId)", "CREATE INDEX IF NOT EXISTS idx_equip_update_time ON equipment_status(LastUpdateTime)", "CREATE INDEX IF NOT EXISTS idx_analog_record_time ON analog_records(RecordTime)", "CREATE INDEX IF NOT EXISTS idx_analog_tag_name ON analog_records(TagName)" }; foreach (var sql in indexSqls) { try { _db!.Ado.ExecuteCommand(sql); } catch (Exception ex) { Log.Debug(ex, "创建索引失败: {Sql}", sql); } } } public static void Dispose() { _db?.Dispose(); } }