using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using VolPro.Core.Extensions; using VolPro.Core.Services; using VolPro.Core.Utilities; namespace VolPro.Core.DbManager.DbCopy { /// /// MySQL 数据库复制工具类 /// public class DbCopyMysql { public static WebResponseContent CopyDatabase(ISqlSugarClient db,ISqlSugarClient toDbContext, string sourceDatabase, string targetDatabase, bool includeData = true) { var result = new CopyResult(); var logs = result.Logs; try { // 验证 if (string.IsNullOrWhiteSpace(sourceDatabase)) throw new ArgumentException("源数据库名不能为空"); if (string.IsNullOrWhiteSpace(targetDatabase)) throw new ArgumentException("目标数据库名不能为空"); if (sourceDatabase.Equals(targetDatabase, StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("源和目标数据库不能相同"); logs.Add($"[{DateTime.Now:HH:mm:ss}] 开始复制: {sourceDatabase} -> {targetDatabase}"); // 检查源数据库 if (!DbExists(db, sourceDatabase)) throw new Exception($"源数据库 '{sourceDatabase}' 不存在"); // 检查目标数据库 if (DbExists(db, targetDatabase)) throw new Exception($"目标数据库 '{targetDatabase}' 已存在"); // 创建目标数据库 logs.Add($"[{DateTime.Now:HH:mm:ss}] 创建目标数据库..."); CreateDb(db, sourceDatabase, targetDatabase); // 复制表 logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制表..."); CopyTables(db,toDbContext, sourceDatabase, targetDatabase, includeData, result, logs); // 复制视图 logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制视图..."); result.ViewsCount = CopyViews(db,toDbContext, sourceDatabase, targetDatabase, logs); // 复制存储过程 logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制存储过程..."); result.ProceduresCount = CopyRoutines(db, toDbContext, sourceDatabase, targetDatabase, "PROCEDURE", logs); // 复制函数 logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制函数..."); result.FunctionsCount = CopyRoutines(db,toDbContext, sourceDatabase, targetDatabase, "FUNCTION", logs); // 复制触发器 logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制触发器..."); result.TriggersCount = CopyTriggers(db, sourceDatabase, targetDatabase, logs); result.Success = true; result.Message = "复制成功"; logs.Add($"[{DateTime.Now:HH:mm:ss}] ✓ 完成! 表:{result.TablesCount} 视图:{result.ViewsCount} " + $"存储过程:{result.ProceduresCount} 函数:{result.FunctionsCount} 触发器:{result.TriggersCount} " + $"行数:{result.TotalRowsCopied} 耗时:{result.ElapsedSeconds:F2}秒"); } catch (Exception ex) { result.Success = false; result.Message = ex.Message; logs.Add($"[{DateTime.Now:HH:mm:ss}] ✗ 错误: {ex.Message}"); } WebResponseContent webResponse = new WebResponseContent(); webResponse.Status = result.Success; webResponse.Message = result.Message; Logger.Info($"mysql创建数据库:{logs.Serialize()}"); return webResponse; } /// /// 获取数据库列表 /// public static List GetDatabases(ISqlSugarClient db) { var all = db.Ado.SqlQuery("SHOW DATABASES"); var system = new[] { "information_schema", "mysql", "performance_schema", "sys" }; return all.Where(x => !system.Contains(x.ToLower())).ToList(); } private static bool DbExists(ISqlSugarClient db, string name) { return db.Ado.GetInt( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @name", new { name }) > 0; } private static void CreateDb(ISqlSugarClient db, string source, string target) { var info = db.Ado.SqlQuerySingle(@" SELECT DEFAULT_CHARACTER_SET_NAME AS Charset, DEFAULT_COLLATION_NAME AS Collation FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @name", new { name = source }); var charset = info?.Charset ?? "utf8mb4"; var collation = info?.Collation ?? "utf8mb4_general_ci"; db.Ado.ExecuteCommand($"CREATE DATABASE `{target}` CHARACTER SET {charset} COLLATE {collation}"); } private static void CopyTables(ISqlSugarClient db, ISqlSugarClient toDbContext, string source, string target, bool includeData, CopyResult result, List logs) { //db.Ado.ExecuteCommand($"USE `{source}`"); var tables = db.Ado.SqlQuery($"USE `{source}`;SHOW TABLES;"); db.Ado.ExecuteCommand($"USE `{target}`"); db.Ado.ExecuteCommand("SET FOREIGN_KEY_CHECKS = 0"); foreach (var table in tables) { try { // 获取建表语句 //db.Ado.ExecuteCommand($"USE `{source}`"); var row = db.Ado.SqlQuerySingle($"USE `{source}`;SHOW CREATE TABLE `{table}`"); var sql = GetValue(row, "Create Table"); if (string.IsNullOrEmpty(sql)) continue; //创建表 toDbContext.Ado.ExecuteCommand($"USE `{target}`"); toDbContext.Ado.ExecuteCommand(sql); result.TablesCount++; logs.Add($" ✓ {table}"); // 复制数据 if (includeData) { var rows = db.Ado.ExecuteCommand( $"INSERT INTO `{target}`.`{table}` SELECT * FROM `{source}`.`{table}`"); result.TotalRowsCopied += rows; if (rows > 0) logs.Add($" {rows} 行"); } } catch (Exception ex) { logs.Add($" ✗ {table}: {ex.Message}"); } } db.Ado.ExecuteCommand("SET FOREIGN_KEY_CHECKS = 1"); } private static int CopyViews(ISqlSugarClient db, ISqlSugarClient toDbContext, string source, string target, List logs) { var views = db.Ado.SqlQuery( "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = @db", new { db = source }); var count = 0; foreach (var view in views) { try { //db.Ado.ExecuteCommand($"USE `{source}`"); var row = db.Ado.SqlQuerySingle($"USE `{source}`;SHOW CREATE VIEW `{view}`;"); //var row = db.Ado.SqlQuerySingle($"SHOW CREATE VIEW `{view}`;"); var sql = GetValue(row, "Create View"); if (string.IsNullOrEmpty(sql)) continue; sql = sql.Replace($"`{source}`.", $"`{target}`."); sql = Regex.Replace(sql, @"DEFINER=`[^`]+`@`[^`]+`\s*", ""); toDbContext.Ado.ExecuteCommand($"USE `{target}`"); toDbContext.Ado.ExecuteCommand(sql); count++; logs.Add($" ✓ {view}"); } catch (Exception ex) { logs.Add($" ✗ {view}: {ex.Message}"); } } return count; } private static int CopyRoutines(ISqlSugarClient db, ISqlSugarClient toDbContext, string source, string target, string type, List logs) { var routines = db.Ado.SqlQuery( "SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = @db AND ROUTINE_TYPE = @type", new { db = source, type }); var count = 0; var key = type == "PROCEDURE" ? "Create Procedure" : "Create Function"; foreach (var routine in routines) { try { //db.Ado.ExecuteCommand($"USE `{source}`"); var row = db.Ado.SqlQuerySingle($"USE `{source}`;SHOW CREATE {type} `{routine}`;"); // var row = db.Ado.SqlQuerySingle($"SHOW CREATE {type} `{routine}`;"); var sql = GetValue(row, key); if (string.IsNullOrEmpty(sql)) continue; sql = Regex.Replace(sql, @"DEFINER=`[^`]+`@`[^`]+`\s*", ""); toDbContext.Ado.ExecuteCommand($"USE `{target}`"); toDbContext.Ado.ExecuteCommand(sql); count++; logs.Add($" ✓ {routine}"); } catch (Exception ex) { logs.Add($" ✗ {routine}: {ex.Message}"); } } return count; } private static int CopyTriggers(ISqlSugarClient db, string source, string target, List logs) { var triggers = db.Ado.SqlQuery( "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA = @db", new { db = source }); var count = 0; foreach (var trigger in triggers) { try { //db.Ado.ExecuteCommand($"USE `{source}`"); var row = db.Ado.SqlQuerySingle($"USE `{source}`;SHOW CREATE TRIGGER `{trigger}`;"); //var row = db.Ado.SqlQuerySingle($"SHOW CREATE TRIGGER `{trigger}`;"); var sql = GetValue(row, "SQL Original Statement"); if (string.IsNullOrEmpty(sql)) continue; sql = Regex.Replace(sql, @"DEFINER=`[^`]+`@`[^`]+`\s*", ""); db.Ado.ExecuteCommand($"USE `{target}`"); db.Ado.ExecuteCommand(sql); count++; logs.Add($" ✓ {trigger}"); } catch (Exception ex) { logs.Add($" ✗ {trigger}: {ex.Message}"); } } return count; } private static string GetValue(dynamic row, string key) { if (row == null) return ""; var dict = row as IDictionary; return dict?.ContainsKey(key) == true ? dict[key]?.ToString() ?? "" : ""; } } public class CopyResult { public bool Success { get; set; } public string Message { get; set; } = ""; public int TablesCount { get; set; } public int ViewsCount { get; set; } public int ProceduresCount { get; set; } public int FunctionsCount { get; set; } public int TriggersCount { get; set; } public long TotalRowsCopied { get; set; } public double ElapsedSeconds { get; set; } public List Logs { get; set; } = new(); } }