Files
squad-rain-ops-mini/Helpers/DbHelper.cs

263 lines
13 KiB
C#
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using SqlSugar;
using System;
using System.IO;
using RainOpsMini.Models;
using System.Reflection;
using System.Linq;
using RainOpsMini.Helpers.SquadGameLog;
namespace RainOpsMini.Helpers
{
public class DbHelper
{
public static string DbPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RainOpsData.db");
private static string ConnectionString => $"Data Source={DbPath}";
public static SqlSugarScope GetInstance()
{
var db = new SqlSugarScope(new ConnectionConfig()
{
ConnectionString = ConnectionString,
DbType = DbType.Sqlite,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute,
ConfigureExternalServices = new ConfigureExternalServices
{
// Handle nullable types for SQLite
EntityService = (c, p) =>
{
if (p.IsPrimarykey == false && new NullabilityInfoContext().Create(c).WriteState is NullabilityState.Nullable)
{
p.IsNullable = true;
}
}
}
});
// Print SQL for debugging
db.Aop.OnLogExecuting = (sql, pars) =>
{
// Console.WriteLine($"[SQL] {sql}"); // Reduce noise
};
return db;
}
public static void InitDatabase()
{
try
{
Console.WriteLine($"[DB] Initializing database at: {DbPath}");
// Ensure directory exists
var dir = Path.GetDirectoryName(DbPath);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
using (var db = GetInstance())
{
// Create database file if not exists
if (!File.Exists(DbPath))
{
Console.WriteLine("[DB] Creating new database file...");
db.DbMaintenance.CreateDatabase();
}
// Create Tables - ensure ChatLog and ServerInfoLog tables exist
try
{
Console.WriteLine($"[DB] Starting CodeFirst InitTables for {typeof(Log_Disconnect).Name} etc...");
db.CodeFirst.InitTables(
typeof(ChatLog),
typeof(ServerInfoLog),
typeof(Log_Disconnect),
typeof(Log_MatchLost),
typeof(Log_BattleEnd),
typeof(Log_CreateSquad),
typeof(Log_NewPlayerConnect),
typeof(Log_Wound),
typeof(Log_Die),
typeof(Log_Revived),
typeof(Log_Attack),
typeof(Log_Tick),
typeof(Log_SetNextLayer),
typeof(Log_ChangeLayer),
typeof(Log_RemovedPlayer),
typeof(RainOpsMini.Models.PluginConfigEntity),
typeof(RainOpsMini.Models.BanRecord),
typeof(RainOpsMini.Models.UserLoginLog),
typeof(RainOpsMini.Models.UserIntegral),
typeof(RainOpsMini.Models.CdkInfo),
typeof(PlayerProfile),
typeof(PlayerSession)
);
Console.WriteLine("[DB] CodeFirst InitTables executed successfully.");
// Ensure Log_Tick has PlayerCount column (Migration for existing databases)
try
{
var tickTableInfo = db.DbMaintenance.GetColumnInfosByTableName("Log_Tick");
if (tickTableInfo != null && tickTableInfo.Count > 0 && !tickTableInfo.Any(c => c.DbColumnName.Equals("PlayerCount", StringComparison.OrdinalIgnoreCase)))
{
Console.WriteLine("[DB] Adding missing column PlayerCount to Log_Tick table...");
db.DbMaintenance.AddColumn("Log_Tick", new DbColumnInfo
{
DbColumnName = "PlayerCount",
DataType = "INTEGER",
IsNullable = true
});
}
if (tickTableInfo != null && tickTableInfo.Count > 0 && !tickTableInfo.Any(c => c.DbColumnName.Equals("MatchTime", StringComparison.OrdinalIgnoreCase)))
{
Console.WriteLine("[DB] Adding missing column MatchTime to Log_Tick table...");
db.DbMaintenance.AddColumn("Log_Tick", new DbColumnInfo
{
DbColumnName = "MatchTime",
DataType = "INTEGER",
IsNullable = true
});
}
if (tickTableInfo != null && tickTableInfo.Count > 0 && !tickTableInfo.Any(c => c.DbColumnName.Equals("MapName", StringComparison.OrdinalIgnoreCase)))
{
Console.WriteLine("[DB] Adding missing column MapName to Log_Tick table...");
db.DbMaintenance.AddColumn("Log_Tick", new DbColumnInfo
{
DbColumnName = "MapName",
DataType = "Text",
IsNullable = true
});
}
}
catch (Exception ex)
{
Console.WriteLine($"[DB] Failed to migrate Log_Tick: {ex.Message}");
}
// Migration for Log_Die, Log_Wound, Log_Revived (New columns)
try
{
string[] tables2 = new[] { "Log_Die", "Log_Wound", "Log_Revived" };
foreach (var table in tables2)
{
var cols = db.DbMaintenance.GetColumnInfosByTableName(table);
var newCols = new[] {
new { Name = "MapName", Type = "Text" },
new { Name = "MatchTime", Type = "Text" },
new { Name = "IsTK", Type = "Boolean" }, // Sqlite stores bool as int usually, SqlSugar handles it
new { Name = "AttackerName", Type = "Text" },
new { Name = "AttackerSteamID", Type = "Text" },
new { Name = "VictimName", Type = "Text" },
new { Name = "VictimSteamID", Type = "Text" }
};
foreach (var newCol in newCols)
{
if (cols != null && !cols.Any(c => c.DbColumnName.Equals(newCol.Name, StringComparison.OrdinalIgnoreCase)))
{
// Specific handling for Log_Revived which might not need Attacker/Victim names if we use Reviver/Revived
// But consistent schema is fine.
// Actually Log_Revived uses Reviver/Revived. I added Attacker/Victim to Log_Revived in entities?
// Wait, I added MapName, MatchTime, IsTK to Revived. I did NOT add Attacker/Victim to Revived in the Entity update step?
// Let me double check my Entity update.
// Logic check:
// Log_Die/Wound: Added MapName, MatchTime, IsTK, AttackerName, AttackerSteamID, VictimName, VictimSteamID
// Log_Revived: Added MapName, MatchTime, IsTK. (Already has Reviver/Revived).
if (table == "Log_Revived" && (newCol.Name.Contains("Attacker") || newCol.Name.Contains("Victim"))) continue;
Console.WriteLine($"[DB] Adding missing column {newCol.Name} to {table} table...");
db.DbMaintenance.AddColumn(table, new DbColumnInfo
{
DbColumnName = newCol.Name,
DataType = newCol.Type,
IsNullable = true
});
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[DB] Failed to migrate Log_Die/Wound/Revived: {ex.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"[DB] CodeFirst CRITICAL FAILURE: {ex.Message}");
Console.WriteLine($"[DB] StackTrace: {ex.StackTrace}");
// 尝试写入文件以便调试
File.WriteAllText("db_init_error.log", $"{DateTime.Now}: {ex.Message}\n{ex.StackTrace}");
throw; // 抛出异常,阻止程序继续运行
}
// Verify table existence
var tables = db.DbMaintenance.GetTableInfoList();
bool exists = tables.Any(t => t.Name.Equals("ChatLogs", StringComparison.OrdinalIgnoreCase));
if (!exists)
{
Console.WriteLine("[DB] Warning: ChatLogs table not found after CodeFirst. Attempting manual creation...");
// Manual fallback
string createTableSql = @"
CREATE TABLE IF NOT EXISTS ChatLogs (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Type TEXT,
EosId TEXT,
SteamId TEXT,
Name TEXT,
Message TEXT,
Timestamp datetime,
TeamId INTEGER,
SquadId INTEGER
);";
db.Ado.ExecuteCommand(createTableSql);
Console.WriteLine("[DB] Manual creation SQL executed.");
// Verify again
exists = db.DbMaintenance.GetTableInfoList().Any(t => t.Name.Equals("ChatLogs", StringComparison.OrdinalIgnoreCase));
}
if (exists)
{
Console.WriteLine("[DB] Verification: ChatLogs table exists.");
}
else
{
Console.WriteLine("[DB] Error: ChatLogs table was NOT created!");
}
// Verify ServerInfoLogs existence
bool serverInfoExists = db.DbMaintenance.GetTableInfoList().Any(t => t.Name.Equals("ServerInfoLogs", StringComparison.OrdinalIgnoreCase));
if (!serverInfoExists)
{
Console.WriteLine("[DB] Warning: ServerInfoLogs table not found after CodeFirst. Attempting manual creation...");
string createTableSql = @"
CREATE TABLE IF NOT EXISTS ServerInfoLogs (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Timestamp datetime,
PlayerCount INTEGER,
MaxPlayers INTEGER,
MapName TEXT,
ServerName TEXT
);";
db.Ado.ExecuteCommand(createTableSql);
Console.WriteLine("[DB] Manual creation for ServerInfoLogs executed.");
}
else
{
Console.WriteLine("[DB] Verification: ServerInfoLogs table exists.");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[Error] Database initialization failed: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
}
}
}