mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-07 05:45:46 +08:00
263 lines
13 KiB
C#
263 lines
13 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
}
|