Files
sryang 577060bc78 chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
2026-09-10 09:42:37 +08:00

1090 lines
42 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace 報到系統客戶端.Data
{
/// <summary>
/// SQLite 資料庫服務初始化和連線管理
/// </summary>
public class DatabaseService
{
private static DatabaseService _instance;
private static readonly object _lockObject = new object();
private string _databasePath;
private SqliteConnection _connection;
private const string DB_FILENAME = "data.db";
// 靜態資料表定義
private static readonly Dictionary<string, List<TableColumnDefinition>> TableDefinitions =
new Dictionary<string, List<TableColumnDefinition>>
{
{
"Classes", new List<TableColumnDefinition>
{
new TableColumnDefinition { Name = "ID", Type = "INTEGER PRIMARY KEY", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CourseCode", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CourseName", Type = "TEXT", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "CourseDate", Type = "DATETIME", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CourseLocation", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "NotificationTemplate", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "AgreedTerms", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "StartTime", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "EndTime", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CreatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" },
new TableColumnDefinition { Name = "UpdatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" }
}
},
{
"SignUps", new List<TableColumnDefinition>
{
new TableColumnDefinition { Name = "ID", Type = "INTEGER PRIMARY KEY AUTOINCREMENT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CourseID", Type = "INTEGER", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "SeqNo", Type = "INTEGER", IsNotNull = true, DefaultValue = "0" },
new TableColumnDefinition { Name = "Name", Type = "TEXT", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "Mobile", Type = "TEXT", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "QRCode", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "SignUpType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "IDNumber", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "IDType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "MealType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "Email", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "LossJobTimes", Type = "INTEGER", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "Gender", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "AgreementStatus", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CheckInTime", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CheckOutTime", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CreatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" },
new TableColumnDefinition { Name = "UpdatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" }
}
},
{
"BPSN", new List<TableColumnDefinition>
{
new TableColumnDefinition { Name = "BPENO", Type = "TEXT PRIMARY KEY", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "BPNME", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "BPAWD", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "Is系統管理群組", Type = "BOOLEAN", IsNotNull = false, DefaultValue = "0" },
new TableColumnDefinition { Name = "Is客戶管理群組", Type = "BOOLEAN", IsNotNull = false, DefaultValue = "0" },
new TableColumnDefinition { Name = "Is現場工作群組", Type = "BOOLEAN", IsNotNull = false, DefaultValue = "0" },
new TableColumnDefinition { Name = "Is查詢群組", Type = "BOOLEAN", IsNotNull = false, DefaultValue = "0" },
new TableColumnDefinition { Name = "CreatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" },
new TableColumnDefinition { Name = "UpdatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" }
}
},
{
"CheckIn", new List<TableColumnDefinition>
{
new TableColumnDefinition { Name = "ID", Type = "INTEGER PRIMARY KEY AUTOINCREMENT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CourseID", Type = "INTEGER", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "Name", Type = "TEXT", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "Mobile", Type = "TEXT", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "SignUpType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "IDNumber", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "IDType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "MealType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "Email", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "LossJobTimes", Type = "INTEGER", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "Gender", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "AgreementStatus", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CheckInType", Type = "TEXT", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CheckInTime", Type = "DATETIME", IsNotNull = true, DefaultValue = null },
new TableColumnDefinition { Name = "UploadTime", Type = "DATETIME", IsNotNull = false, DefaultValue = null },
new TableColumnDefinition { Name = "CreatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" },
new TableColumnDefinition { Name = "UpdatedAt", Type = "DATETIME", IsNotNull = false, DefaultValue = "CURRENT_TIMESTAMP" }
}
}
};
private static readonly Dictionary<string, string[]> TableIndexes =
new Dictionary<string, string[]>
{
{ "Classes", new[] { "idx_classes_courseid|CourseCode" } },
{ "SignUps", new[] {
"idx_signups_courseid|CourseID",
"idx_signups_seqno|SeqNo",
"idx_signups_qrcode|QRCode",
"idx_signups_name|Name",
"idx_signups_mobile|Mobile",
"idx_signups_idnumber|IDNumber",
"idx_signups_email|Email"
}},
{ "BPSN", new[] { "idx_bpsn_bpeno|BPENO" } },
{ "CheckIn", new[] {
"idx_checkin_courseid|CourseID",
"idx_checkin_name|Name",
"idx_checkin_mobile|Mobile",
"idx_checkin_idnumber|IDNumber",
"idx_checkin_email|Email",
"idx_checkin_checkintime|CheckInTime"
}}
};
/// <summary>
/// 取得單一實例
/// </summary>
public static DatabaseService Instance
{
get
{
if (_instance == null)
{
lock (_lockObject)
{
if (_instance == null)
{
_instance = new DatabaseService();
}
}
}
return _instance;
}
}
public string DatabasePath
{
get { return _databasePath; }
}
private DatabaseService()
{
try
{
// 設定資料庫路徑 AppData\Roaming\報到系統客戶端
string appDataPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"報到系統客戶端"
);
// 如果資料夾不存在則建立
if (!Directory.Exists(appDataPath))
{
Directory.CreateDirectory(appDataPath);
}
// 建立 DB 子資料夾
string dbPath = Path.Combine(appDataPath, "DB");
if (!Directory.Exists(dbPath))
{
Directory.CreateDirectory(dbPath);
}
_databasePath = Path.Combine(dbPath, DB_FILENAME);
// 初始化資料庫
InitializeDatabase();
}
catch (Exception ex)
{
throw new Exception($"初始化資料庫服務失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 取得資料庫連線
/// </summary>
public SqliteConnection GetConnection()
{
try
{
if (_connection == null || _connection.State == System.Data.ConnectionState.Closed)
{
// 使用改進的連線字串,設定更多參數確保穩定性
string connectionString = string.Format(
"Data Source={0};",
_databasePath
);
_connection = new SqliteConnection(connectionString);
_connection.Open();
}
return _connection;
}
catch (DllNotFoundException ex)
{
throw new Exception(
"無法載入 SQLite 原生庫 (e_sqlite3.dll)。\n\n" +
"請嘗試以下方案:\n" +
"1. 確認專案目標平台設定(x86 或 x64)\n" +
"2. 在 NuGet Package Manager 執行:\n" +
" - Update-Package System.Data.SQLite -Reinstall\n" +
"3. 確認應用程式執行的平台與編譯平台相符\n" +
"4. 清理並重新編譯專案\n\n" +
"原始錯誤:" + ex.Message, ex);
}
catch (Exception ex)
{
throw new Exception($"開啟資料庫連線失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 初始化資料庫(建立表格)
/// </summary>
private void InitializeDatabase()
{
try
{
var conn = GetConnection();
// 根據定義動態建立所有表格
foreach (var tableName in TableDefinitions.Keys)
{
CreateTableFromDefinition(conn, tableName);
CreateIndexesForTable(conn, tableName);
}
}
catch (Exception ex)
{
throw new Exception($"初始化資料庫失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 根據定義動態建立表格
/// </summary>
private void CreateTableFromDefinition(SqliteConnection conn, string tableName)
{
try
{
if (!TableDefinitions.ContainsKey(tableName))
{
throw new Exception($"找不到表格 {tableName} 的定義");
}
string sql = BuildCreateTableSql(tableName);
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
throw new Exception($"建立表格 {tableName} 失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 根據定義建立表格索引
/// </summary>
private void CreateIndexesForTable(SqliteConnection conn, string tableName)
{
try
{
if (!TableIndexes.ContainsKey(tableName))
{
return;
}
var indexes = TableIndexes[tableName];
foreach (var indexDef in indexes)
{
string[] parts = indexDef.Split('|');
if (parts.Length == 2)
{
string indexName = parts[0];
string columnName = parts[1];
string sql = $"CREATE INDEX IF NOT EXISTS {indexName} ON {tableName}({columnName})";
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"建立表格 {tableName} 索引失敗:{ex.Message}");
}
}
/// <summary>
/// 動態構建 CREATE TABLE SQL 語句
/// </summary>
private string BuildCreateTableSql(string tableName)
{
if (!TableDefinitions.ContainsKey(tableName))
{
throw new Exception($"找不到表格 {tableName} 的定義");
}
var columns = TableDefinitions[tableName];
var columnSql = new List<string>();
foreach (var column in columns)
{
string colDef = $"{column.Name} {column.Type}";
if (column.IsNotNull)
{
colDef += " NOT NULL";
}
if (!string.IsNullOrEmpty(column.DefaultValue))
{
colDef += $" DEFAULT {column.DefaultValue}";
}
columnSql.Add(colDef);
}
string sql = $"CREATE TABLE IF NOT EXISTS {tableName} (\n " +
string.Join(",\n ", columnSql) +
"\n)";
return sql;
}
/// <summary>
/// 動態構建 ALTER TABLE ADD COLUMN SQL 語句
/// </summary>
private string BuildAddColumnSql(string tableName, TableColumnDefinition column)
{
string colDef = $"{column.Name} {column.Type}";
if (column.IsNotNull)
{
colDef += " NOT NULL";
}
if (!string.IsNullOrEmpty(column.DefaultValue))
{
colDef += $" DEFAULT {column.DefaultValue}";
}
return $"ALTER TABLE {tableName} ADD COLUMN {colDef}";
}
/// <summary>
/// 清除所有表格資料
/// </summary>
public void ClearAllTables()
{
try
{
var conn = GetConnection();
// 清除 SignUps 表
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM SignUps", conn))
{
cmd.ExecuteNonQuery();
}
// 清除 Classes 表
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM Classes", conn))
{
cmd.ExecuteNonQuery();
}
// 清除 BPSN 表
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM BPSN", conn))
{
cmd.ExecuteNonQuery();
}
// 清除 CheckIn 表
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM CheckIn", conn))
{
cmd.ExecuteNonQuery();
}
// 重設自動遞增序號
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM sqlite_sequence", conn))
{
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
throw new Exception($"清除表格失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 檢查資料表是否存在
/// </summary>
public bool TableExists(string tableName)
{
try
{
var conn = GetConnection();
string sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=@tableName";
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@tableName", tableName);
Int64 count = (Int64)cmd.ExecuteScalar();
return count > 0;
}
}
catch (Exception ex)
{
throw new Exception($"檢查表格是否存在失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 重建所有資料表
/// </summary>
public void DropAndCreateAllTables()
{
try
{
var conn = GetConnection();
// 刪除表格順序很重要,需要考慮外鍵關係
// 從靜態定義中取得表格名稱,並按反向順序刪除
var tableNames = new List<string>(TableDefinitions.Keys);
tableNames.Reverse();
foreach (string tableName in tableNames)
{
string sql = $"DROP TABLE IF EXISTS {tableName}";
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
}
// 重新初始化資料庫結構
InitializeDatabase();
}
catch (Exception ex)
{
throw new Exception($"刪除表格失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 刪除指定的資料表
/// </summary>
public void DropTable(string tableName)
{
try
{
if (string.IsNullOrWhiteSpace(tableName))
{
throw new ArgumentException("表格名稱不能為空", nameof(tableName));
}
var conn = GetConnection();
string sql = $"DROP TABLE IF EXISTS {tableName}";
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
throw new Exception($"刪除表格失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 取得表格中現有的欄位名稱列表
/// </summary>
private HashSet<string> GetExistingColumnNames(string tableName)
{
var columnNames = new HashSet<string>();
try
{
var columns = GetTableColumns(tableName);
foreach (var column in columns)
{
columnNames.Add(column.ColumnName);
}
}
catch
{
// 忽略錯誤
}
return columnNames;
}
/// <summary>
/// 獲取資料表的列信息
/// </summary>
public List<TableColumnInfo> GetTableColumns(string tableName)
{
var columns = new List<TableColumnInfo>();
try
{
var conn = GetConnection();
string sql = $"PRAGMA table_info({tableName})";
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
using (SqliteDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
columns.Add(new TableColumnInfo
{
ColumnId = reader.GetInt32(0),
ColumnName = reader.GetString(1),
ColumnType = reader.GetString(2),
IsNotNull = reader.GetInt32(3) != 0,
DefaultValue = reader.IsDBNull(4) ? null : reader.GetString(4),
IsPrimaryKey = reader.GetInt32(5) != 0
});
}
}
}
}
catch (Exception ex)
{
throw new Exception($"獲取表格列信息失敗:{ex.Message}", ex);
}
return columns;
}
/// <summary>
/// 檢查並補足缺少的欄位
/// </summary>
public void AddMissingColumns()
{
try
{
var conn = GetConnection();
foreach (var tableName in TableDefinitions.Keys)
{
if (!TableExists(tableName))
{
System.Diagnostics.Debug.WriteLine($"表格 {tableName} 不存在,跳過欄位檢查");
continue;
}
var expectedColumns = TableDefinitions[tableName];
var existingColumnNames = GetExistingColumnNames(tableName);
foreach (var column in expectedColumns)
{
if (!existingColumnNames.Contains(column.Name))
{
try
{
string sql = BuildAddColumnSql(tableName, column);
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
System.Diagnostics.Debug.WriteLine($"已新增欄位 {tableName}.{column.Name}");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"新增欄位 {tableName}.{column.Name} 失敗:{ex.Message}");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception($"檢查並新增缺少的欄位失敗:{ex.Message}", ex);
}
}
/// <summary>
/// 驗證所有表格的結構是否符合定義
/// </summary>
public bool ValidateAllTablesStructure()
{
try
{
foreach (var tableName in TableDefinitions.Keys)
{
if (!ValidateSingleTableStructure(tableName))
{
return false;
}
}
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 驗證單一表格的結構是否符合定義
/// </summary>
public bool ValidateSingleTableStructure(string tableName)
{
try
{
if (!TableDefinitions.ContainsKey(tableName))
{
throw new Exception($"找不到表格 {tableName} 的定義");
}
if (!TableExists(tableName))
{
return false;
}
var expectedColumns = TableDefinitions[tableName];
var existingColumnNames = GetExistingColumnNames(tableName);
foreach (var expectedColumn in expectedColumns)
{
if (!existingColumnNames.Contains(expectedColumn.Name))
{
System.Diagnostics.Debug.WriteLine($"表格 {tableName} 缺少欄位:{expectedColumn.Name}");
return false;
}
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"驗證表格 {tableName} 結構失敗:{ex.Message}");
return false;
}
}
/// <summary>
/// 獲取所有表格與其定義的結構對比結果
/// </summary>
public Dictionary<string, TableStructureValidationResult> ValidateAllTablesStructureDetailed()
{
var results = new Dictionary<string, TableStructureValidationResult>();
foreach (var tableName in TableDefinitions.Keys)
{
results[tableName] = ValidateSingleTableStructureDetailed(tableName);
}
return results;
}
/// <summary>
/// 獲取單一表格與定義的結構對比結果(詳細版)
/// </summary>
public TableStructureValidationResult ValidateSingleTableStructureDetailed(string tableName)
{
var result = new TableStructureValidationResult { TableName = tableName };
try
{
if (!TableDefinitions.ContainsKey(tableName))
{
result.IsValid = false;
result.Message = $"找不到表格 {tableName} 的定義";
return result;
}
if (!TableExists(tableName))
{
result.IsValid = false;
result.Message = $"表格 {tableName} 不存在";
result.MissingColumns = new List<string>(TableDefinitions[tableName].Count);
foreach (var col in TableDefinitions[tableName])
{
result.MissingColumns.Add(col.Name);
}
return result;
}
var expectedColumns = TableDefinitions[tableName];
var existingColumns = GetTableColumns(tableName);
var existingColumnNames = GetExistingColumnNames(tableName);
result.MissingColumns = new List<string>();
result.ExtraColumns = new List<string>();
// 檢查缺少的欄位
foreach (var expectedColumn in expectedColumns)
{
if (!existingColumnNames.Contains(expectedColumn.Name))
{
result.MissingColumns.Add(expectedColumn.Name);
}
}
// 檢查多餘的欄位
foreach (var existingColumn in existingColumns)
{
if (!expectedColumns.Any(c => c.Name == existingColumn.ColumnName))
{
result.ExtraColumns.Add(existingColumn.ColumnName);
}
}
result.IsValid = result.MissingColumns.Count == 0 && result.ExtraColumns.Count == 0;
if (result.IsValid)
{
result.Message = $"表格 {tableName} 結構驗證成功";
}
else
{
var issues = new List<string>();
if (result.MissingColumns.Count > 0)
{
issues.Add($"缺少 {result.MissingColumns.Count} 個欄位:{string.Join(", ", result.MissingColumns)}");
}
if (result.ExtraColumns.Count > 0)
{
issues.Add($"有 {result.ExtraColumns.Count} 個多餘欄位:{string.Join(", ", result.ExtraColumns)}");
}
result.Message = string.Join("", issues);
}
}
catch (Exception ex)
{
result.IsValid = false;
result.Message = $"驗證表格 {tableName} 結構失敗:{ex.Message}";
}
return result;
}
/// <summary>
/// 執行資料庫健康檢查
/// </summary>
public DatabaseHealthCheckResult PerformHealthCheck()
{
var healthCheckResult = new DatabaseHealthCheckResult();
healthCheckResult.CheckTime = DateTime.Now;
try
{
// 檢查所有表格是否存在
healthCheckResult.AllTablesExist = true;
foreach (var tableName in TableDefinitions.Keys)
{
if (!TableExists(tableName))
{
healthCheckResult.AllTablesExist = false;
if (healthCheckResult.MissingTables == null)
{
healthCheckResult.MissingTables = new List<string>();
}
healthCheckResult.MissingTables.Add(tableName);
}
}
// 驗證所有表格結構
var validationResults = ValidateAllTablesStructureDetailed();
healthCheckResult.TableValidationResults = validationResults;
healthCheckResult.AllTablesValid = validationResults.Values.All(r => r.IsValid);
// 獲取資料庫統計信息
healthCheckResult.TablesStatistics = GetAllTablesStatistics();
// 獲取資料庫檔案大小
healthCheckResult.DatabaseFileSize = GetDatabaseFileSize();
healthCheckResult.DatabaseFileSizeFormatted = GetDatabaseFileSizeFormatted();
healthCheckResult.IsHealthy = healthCheckResult.AllTablesExist && healthCheckResult.AllTablesValid;
}
catch (Exception ex)
{
healthCheckResult.IsHealthy = false;
healthCheckResult.ErrorMessage = ex.Message;
}
return healthCheckResult;
}
/// <summary>
/// 自動修復資料庫結構(根據定義補足缺少的欄位)
/// </summary>
public DatabaseRepairResult AutoRepairDatabase()
{
var repairResult = new DatabaseRepairResult();
repairResult.RepairTime = DateTime.Now;
try
{
var conn = GetConnection();
foreach (var tableName in TableDefinitions.Keys)
{
var tableRepairResult = new TableRepairResult { TableName = tableName };
if (!TableExists(tableName))
{
// 建立缺少的表格
try
{
CreateTableFromDefinition(conn, tableName);
CreateIndexesForTable(conn, tableName);
tableRepairResult.IsRepaired = true;
tableRepairResult.Message = $"表格 {tableName} 已建立";
tableRepairResult.Action = "CreateTable";
}
catch (Exception ex)
{
tableRepairResult.IsRepaired = false;
tableRepairResult.Message = $"建立表格 {tableName} 失敗:{ex.Message}";
tableRepairResult.Action = "CreateTable";
}
}
else
{
// 補足缺少的欄位
var expectedColumns = TableDefinitions[tableName];
var existingColumnNames = GetExistingColumnNames(tableName);
var addedColumns = new List<string>();
foreach (var column in expectedColumns)
{
if (!existingColumnNames.Contains(column.Name))
{
try
{
string sql = BuildAddColumnSql(tableName, column);
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
}
addedColumns.Add(column.Name);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"新增欄位 {tableName}.{column.Name} 失敗:{ex.Message}");
}
}
}
if (addedColumns.Count > 0)
{
tableRepairResult.IsRepaired = true;
tableRepairResult.Message = $"已新增 {addedColumns.Count} 個欄位:{string.Join(", ", addedColumns)}";
tableRepairResult.Action = "AddColumns";
tableRepairResult.AddedColumns = addedColumns;
}
else
{
tableRepairResult.IsRepaired = true;
tableRepairResult.Message = $"表格 {tableName} 無需修復";
tableRepairResult.Action = "NoAction";
}
}
repairResult.TableRepairResults.Add(tableRepairResult);
}
repairResult.IsSuccessful = repairResult.TableRepairResults.All(r => r.IsRepaired);
}
catch (Exception ex)
{
repairResult.IsSuccessful = false;
repairResult.ErrorMessage = ex.Message;
}
return repairResult;
}
/// <summary>
/// 關閉資料庫連線
/// </summary>
public void CloseConnection()
{
try
{
if (_connection != null && _connection.State != System.Data.ConnectionState.Closed)
{
_connection.Close();
_connection.Dispose();
_connection = null;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"關閉連線時發生錯誤:{ex.Message}");
}
}
/// <summary>
/// 獲取所有資料表的統計信息
/// </summary>
public List<TableStatistics> GetAllTablesStatistics()
{
var statistics = new List<TableStatistics>();
foreach (var tableName in TableDefinitions.Keys)
{
try
{
if (TableExists(tableName))
{
var conn = GetConnection();
string sql = $"SELECT COUNT(*) FROM {tableName}";
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
{
int recordCount = (int)cmd.ExecuteScalar();
statistics.Add(new TableStatistics
{
TableName = tableName,
RecordCount = recordCount,
CheckTime = DateTime.Now
});
}
}
}
catch
{
// 跳過無法取得統計信息的表格
}
}
return statistics;
}
/// <summary>
/// 獲取資料庫檔案大小(單位:字節)
/// </summary>
public long GetDatabaseFileSize()
{
try
{
if (File.Exists(_databasePath))
{
var fileInfo = new FileInfo(_databasePath);
return fileInfo.Length;
}
return 0;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"獲取資料庫文件大小失敗:{ex.Message}");
return 0;
}
}
/// <summary>
/// 獲取資料庫檔案大小的格式化字符串(KB、MB等)
/// </summary>
public string GetDatabaseFileSizeFormatted()
{
long sizeInBytes = GetDatabaseFileSize();
string[] sizes = { "B", "KB", "MB", "GB" };
double len = sizeInBytes;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}
return $"{len:0.##} {sizes[order]}";
}
}
/// <summary>
/// 資料表欄位定義
/// </summary>
public class TableColumnDefinition
{
public string Name { get; set; }
public string Type { get; set; }
public bool IsNotNull { get; set; }
public string DefaultValue { get; set; }
}
/// <summary>
/// 資料表列信息
/// </summary>
public class TableColumnInfo
{
public int ColumnId { get; set; }
public string ColumnName { get; set; }
public string ColumnType { get; set; }
public bool IsNotNull { get; set; }
public string DefaultValue { get; set; }
public bool IsPrimaryKey { get; set; }
}
/// <summary>
/// 資料表統計信息
/// </summary>
public class TableStatistics
{
public string TableName { get; set; }
public int RecordCount { get; set; }
public DateTime CheckTime { get; set; }
}
/// <summary>
/// 表格結構驗證結果
/// </summary>
public class TableStructureValidationResult
{
public string TableName { get; set; }
public bool IsValid { get; set; }
public string Message { get; set; }
public List<string> MissingColumns { get; set; }
public List<string> ExtraColumns { get; set; }
}
/// <summary>
/// 資料庫健康檢查結果
/// </summary>
public class DatabaseHealthCheckResult
{
public DateTime CheckTime { get; set; }
public bool IsHealthy { get; set; }
public bool AllTablesExist { get; set; }
public bool AllTablesValid { get; set; }
public List<string> MissingTables { get; set; }
public Dictionary<string, TableStructureValidationResult> TableValidationResults { get; set; }
public List<TableStatistics> TablesStatistics { get; set; }
public long DatabaseFileSize { get; set; }
public string DatabaseFileSizeFormatted { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// 表格修復結果
/// </summary>
public class TableRepairResult
{
public string TableName { get; set; }
public bool IsRepaired { get; set; }
public string Message { get; set; }
public string Action { get; set; } // "CreateTable", "AddColumns", "NoAction"
public List<string> AddedColumns { get; set; }
}
/// <summary>
/// 資料庫修復結果
/// </summary>
public class DatabaseRepairResult
{
public DateTime RepairTime { get; set; }
public bool IsSuccessful { get; set; }
public List<TableRepairResult> TableRepairResults { get; set; }
public string ErrorMessage { get; set; }
public DatabaseRepairResult()
{
TableRepairResults = new List<TableRepairResult>();
}
}
}