2025-09-14 22:56:08 +08:00
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Npgsql ;
namespace Admin.NET.Core.Service ;
/// <summary>
/// 系统数据库管理服务 🧩
/// </summary>
[ApiDescriptionSettings(Order = 250, Description = "数据库管理")]
public class SysDatabaseService : IDynamicApiController , ITransient
{
private readonly UserManager _userManager ;
private readonly ISqlSugarClient _db ;
private readonly IViewEngine _viewEngine ;
private readonly CodeGenOptions _codeGenOptions ;
2025-09-19 02:46:24 +08:00
private readonly CodeGenStrategyFactory _codeGenStrategyFactory ;
2025-09-14 22:56:08 +08:00
public SysDatabaseService ( UserManager userManager ,
ISqlSugarClient db ,
IViewEngine viewEngine ,
2025-09-19 02:46:24 +08:00
IOptions < CodeGenOptions > codeGenOptions ,
CodeGenStrategyFactory codeGenStrategyFactory )
2025-09-14 22:56:08 +08:00
{
_userManager = userManager ;
_db = db ;
_viewEngine = viewEngine ;
_codeGenOptions = codeGenOptions . Value ;
2025-09-19 02:46:24 +08:00
_codeGenStrategyFactory = codeGenStrategyFactory ;
2025-09-14 22:56:08 +08:00
}
/// <summary>
/// 增加列 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "AddColumn"), HttpPost]
[DisplayName("增加列")]
public void AddColumn ( DbColumnInput input )
{
var column = new DbColumnInfo
{
ColumnDescription = input . ColumnDescription ,
DbColumnName = input . DbColumnName ,
IsIdentity = input . IsIdentity = = 1 ,
IsNullable = input . IsNullable = = 1 ,
IsPrimarykey = input . IsPrimarykey = = 1 ,
Length = input . Length ,
DecimalDigits = input . DecimalDigits ,
DataType = input . DataType
} ;
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
db . DbMaintenance . AddColumn ( input . TableName , column ) ;
// 添加默认值
if ( ! string . IsNullOrWhiteSpace ( input . DefaultValue ) )
db . DbMaintenance . AddDefaultValue ( input . TableName , column . DbColumnName , input . DefaultValue ) ;
db . DbMaintenance . AddColumnRemark ( input . DbColumnName , input . TableName , input . ColumnDescription ) ;
if ( column . IsPrimarykey )
db . DbMaintenance . AddPrimaryKey ( input . TableName , input . DbColumnName ) ;
}
/// <summary>
/// 删除列 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "DeleteColumn"), HttpPost]
[DisplayName("删除列")]
public void DeleteColumn ( DeleteDbColumnInput input )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
db . DbMaintenance . DropColumn ( input . TableName , input . DbColumnName ) ;
}
/// <summary>
/// 编辑列 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "UpdateColumn"), HttpPost]
[DisplayName("编辑列")]
public void UpdateColumn ( UpdateDbColumnInput input )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
db . DbMaintenance . RenameColumn ( input . TableName , input . OldColumnName , input . ColumnName ) ;
if ( ! string . IsNullOrWhiteSpace ( input . DefaultValue ) )
db . DbMaintenance . AddDefaultValue ( input . TableName , input . ColumnName , input . DefaultValue ) ;
if ( db . DbMaintenance . IsAnyColumnRemark ( input . ColumnName , input . TableName ) )
db . DbMaintenance . DeleteColumnRemark ( input . ColumnName , input . TableName ) ;
db . DbMaintenance . AddColumnRemark ( input . ColumnName , input . TableName , string . IsNullOrWhiteSpace ( input . Description ) ? input . ColumnName : input . Description ) ;
}
/// <summary>
/// 移动列顺序 🔖
/// </summary>
/// <param name="input"></param>
[DisplayName("移动列顺序")]
public void MoveColumn ( MoveDbColumnInput input )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
var dbMaintenance = db . DbMaintenance ;
var columns = dbMaintenance . GetColumnInfosByTableName ( input . TableName , false ) ;
var targetColumn = columns . FirstOrDefault ( u = > u . DbColumnName . Equals ( input . ColumnName , StringComparison . OrdinalIgnoreCase ) ) ? ? throw new Exception ( $"列 {input.ColumnName} 在表 {input.TableName} 中不存在" ) ;
var dbType = db . CurrentConnectionConfig . DbType ;
switch ( dbType )
{
case SqlSugar . DbType . MySql :
MoveColumnInMySQL ( db , input . TableName , input . ColumnName , input . AfterColumnName ) ;
break ;
default :
throw new NotSupportedException ( $"暂不支持 {dbType} 数据库的列移动操作" ) ;
}
}
/// <summary>
/// 获取列定义
/// </summary>
/// <param name="db"></param>
/// <param name="tableName"></param>
/// <param name="columnName"></param>
/// <param name="noDefault"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private string GetColumnDefinitionInMySQL ( ISqlSugarClient db , string tableName , string columnName , bool noDefault = false )
{
var columnDef = db . Ado . SqlQuery < dynamic > ( $"SHOW FULL COLUMNS FROM `{tableName}` WHERE Field = '{columnName}'" ) . FirstOrDefault ( ) ? ? throw new Exception ( $"Column {columnName} not found" ) ;
var definition = new StringBuilder ( ) ;
definition . Append ( $"`{columnName}` " ) ; // 列名
definition . Append ( $"{columnDef.Type} " ) ; // 数据类型
// 处理约束条件
definition . Append ( columnDef . Null = = "YES" ? "NULL " : "NOT NULL " ) ;
if ( columnDef . Default ! = null & & ! noDefault )
definition . Append ( $"DEFAULT '{columnDef.Default}' " ) ;
if ( ! string . IsNullOrEmpty ( columnDef . Extra ) )
definition . Append ( $"{columnDef.Extra} " ) ;
if ( ! string . IsNullOrEmpty ( columnDef . Comment ) )
definition . Append ( $"COMMENT '{columnDef.Comment.Replace(" ' ", " ' ' ")}'" ) ;
return definition . ToString ( ) ;
}
/// <summary>
/// MySQL 列移动实现
/// </summary>
/// <param name="db"></param>
/// <param name="tableName"></param>
/// <param name="columnName"></param>
/// <param name="afterColumnName"></param>
private void MoveColumnInMySQL ( ISqlSugarClient db , string tableName , string columnName , string afterColumnName )
{
var definition = GetColumnDefinitionInMySQL ( db , tableName , columnName ) ;
var sql = new StringBuilder ( ) ;
sql . Append ( $"ALTER TABLE `{tableName}` MODIFY COLUMN {definition}" ) ;
if ( string . IsNullOrEmpty ( afterColumnName ) )
sql . Append ( " FIRST" ) ;
else
sql . Append ( $" AFTER `{afterColumnName}`" ) ;
db . Ado . ExecuteCommand ( sql . ToString ( ) ) ;
}
/// <summary>
/// 增加表 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "AddTable"), HttpPost]
[DisplayName("增加表")]
public void AddTable ( DbTableInput input )
{
if ( input . DbColumnInfoList = = null | | input . DbColumnInfoList . Count = = 0 )
throw Oops . Oh ( ErrorCodeEnum . db1000 ) ;
if ( input . DbColumnInfoList . GroupBy ( u = > u . DbColumnName ) . Any ( u = > u . Count ( ) > 1 ) )
throw Oops . Oh ( ErrorCodeEnum . db1002 ) ;
var config = App . GetOptions < DbConnectionOptions > ( ) . ConnectionConfigs . FirstOrDefault ( u = > u . ConfigId . ToString ( ) = = input . ConfigId ) ;
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
var typeBuilder = db . DynamicBuilder ( ) . CreateClass ( input . TableName , new SugarTable ( ) { TableName = input . TableName , TableDescription = input . Description } ) ;
input . DbColumnInfoList . ForEach ( u = >
{
var dbColumnName = config ! . DbSettings . EnableUnderLine ? UtilMethods . ToUnderLine ( u . DbColumnName . Trim ( ) ) : u . DbColumnName . Trim ( ) ;
// 虚拟类都默认string类型, 具体以列数据类型为准
typeBuilder . CreateProperty ( dbColumnName , typeof ( string ) , new SugarColumn ( )
{
IsPrimaryKey = u . IsPrimarykey = = 1 ,
IsIdentity = u . IsIdentity = = 1 ,
ColumnDataType = u . DataType ,
Length = u . Length ,
IsNullable = u . IsNullable = = 1 ,
DecimalDigits = u . DecimalDigits ,
ColumnDescription = u . ColumnDescription ,
DefaultValue = u . DefaultValue ,
} ) ;
} ) ;
db . CodeFirst . InitTables ( typeBuilder . BuilderType ( ) ) ;
}
/// <summary>
/// 删除表 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "DeleteTable"), HttpPost]
[DisplayName("删除表")]
public void DeleteTable ( DeleteDbTableInput input )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
db . DbMaintenance . DropTable ( input . TableName ) ;
}
/// <summary>
/// 编辑表 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "UpdateTable"), HttpPost]
[DisplayName("编辑表")]
public void UpdateTable ( UpdateDbTableInput input )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
db . DbMaintenance . RenameTable ( input . OldTableName , input . TableName ) ;
try
{
if ( db . DbMaintenance . IsAnyTableRemark ( input . TableName ) )
db . DbMaintenance . DeleteTableRemark ( input . TableName ) ;
if ( ! string . IsNullOrWhiteSpace ( input . Description ) )
db . DbMaintenance . AddTableRemark ( input . TableName , input . Description ) ;
}
catch ( NotSupportedException ex )
{
throw Oops . Oh ( ex . ToString ( ) ) ;
}
}
2025-09-22 00:36:27 +08:00
/// <summary>
/// 获取库列表 🔖
/// </summary>
/// <returns></returns>
[DisplayName("获取库列表")]
public List < DbOutput > GetList ( )
{
var dbOutputs = new List < DbOutput > ( ) ;
var configIds = App . GetOptions < DbConnectionOptions > ( ) . ConnectionConfigs . Select ( u = > u . ConfigId . ToString ( ) ) . ToList ( ) ;
foreach ( var config in configIds )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( config ) ;
dbOutputs . Add ( new DbOutput
{
ConfigId = config ,
DbName = db . Ado . Connection . Database
} ) ;
}
return dbOutputs ;
}
/// <summary>
/// 获取表列表 🔖
/// </summary>
/// <param name="configId">ConfigId</param>
/// <returns></returns>
[DisplayName("获取表列表")]
public List < DbTableInfo > GetTableList ( string configId = SqlSugarConst . MainConfigId )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( configId ) ;
return db . DbMaintenance . GetTableInfoList ( false ) ;
}
/// <summary>
/// 获取字段列表 🔖
/// </summary>
/// <param name="tableName">表名</param>
/// <param name="configId">ConfigId</param>
/// <returns></returns>
[DisplayName("获取字段列表")]
public List < DbColumnOutput > GetColumnList ( string tableName , string configId = SqlSugarConst . MainConfigId )
{
if ( string . IsNullOrWhiteSpace ( tableName ) ) return [ ] ;
var db = _db . AsTenant ( ) . GetConnectionScope ( configId ) ;
return db . DbMaintenance . GetColumnInfosByTableName ( tableName , false ) . Adapt < List < DbColumnOutput > > ( ) ;
}
/// <summary>
/// 获取数据库数据类型列表 🔖
/// </summary>
/// <param name="configId"></param>
/// <returns></returns>
[DisplayName("获取数据库数据类型列表")]
public List < string > GetDbTypeList ( string configId = SqlSugarConst . MainConfigId )
{
var db = _db . AsTenant ( ) . GetConnectionScope ( configId ) ;
return db . DbMaintenance . GetDbTypes ( ) . OrderBy ( u = > u ) . ToList ( ) ;
}
/// <summary>
/// 获取可视化库表结构 🔖
/// </summary>
/// <returns></returns>
[DisplayName("获取可视化库表结构")]
public VisualDbTable GetVisualDbTable ( )
{
var visualTableList = new List < VisualTable > ( ) ;
var visualColumnList = new List < VisualColumn > ( ) ;
var columnRelationList = new List < ColumnRelation > ( ) ;
var dbOptions = App . GetOptions < DbConnectionOptions > ( ) . ConnectionConfigs . First ( u = > u . ConfigId . ToString ( ) = = SqlSugarConst . MainConfigId ) ;
// 遍历所有实体获取所有库表结构
var random = new Random ( ) ;
var entityTypes = App . EffectiveTypes . Where ( u = > ! u . IsInterface & & ! u . IsAbstract & & u . IsClass & & u . IsDefined ( typeof ( SugarTable ) , false ) ) . ToList ( ) ;
foreach ( var entityType in entityTypes )
{
var entityInfo = _db . EntityMaintenance . GetEntityInfoNoCache ( entityType ) ;
var visualTable = new VisualTable
{
TableName = entityInfo . DbTableName ,
TableComents = entityInfo . TableDescription + entityInfo . DbTableName ,
X = random . Next ( 5000 ) ,
Y = random . Next ( 5000 )
} ;
visualTableList . Add ( visualTable ) ;
foreach ( EntityColumnInfo columnInfo in entityInfo . Columns )
{
var visualColumn = new VisualColumn
{
TableName = columnInfo . DbTableName ,
ColumnName = dbOptions . DbSettings . EnableUnderLine ? UtilMethods . ToUnderLine ( columnInfo . DbColumnName ) : columnInfo . DbColumnName ,
DataType = columnInfo . PropertyInfo . PropertyType . Name ,
DataLength = columnInfo . Length . ToString ( ) ,
ColumnDescription = columnInfo . ColumnDescription ,
} ;
visualColumnList . Add ( visualColumn ) ;
// 根据导航配置获取表之间关联关系
if ( columnInfo . Navigat ! = null )
{
var name1 = columnInfo . Navigat . GetName ( ) ;
var name2 = columnInfo . Navigat . GetName2 ( ) ;
var targetColumnName = string . IsNullOrEmpty ( name2 ) ? "Id" : name2 ;
var relation = new ColumnRelation
{
SourceTableName = columnInfo . DbTableName ,
SourceColumnName = dbOptions . DbSettings . EnableUnderLine ? UtilMethods . ToUnderLine ( name1 ) : name1 ,
Type = columnInfo . Navigat . GetNavigateType ( ) = = NavigateType . OneToOne ? "ONE_TO_ONE" : "ONE_TO_MANY" ,
TargetTableName = dbOptions . DbSettings . EnableUnderLine ? UtilMethods . ToUnderLine ( columnInfo . DbColumnName ) : columnInfo . DbColumnName ,
TargetColumnName = dbOptions . DbSettings . EnableUnderLine ? UtilMethods . ToUnderLine ( targetColumnName ) : targetColumnName
} ;
columnRelationList . Add ( relation ) ;
}
}
}
return new VisualDbTable { VisualTableList = visualTableList , VisualColumnList = visualColumnList , ColumnRelationList = columnRelationList } ;
}
2025-09-14 22:56:08 +08:00
/// <summary>
/// 创建实体 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "CreateEntity"), HttpPost]
[DisplayName("创建实体")]
2025-09-19 02:46:24 +08:00
public async Task CreateEntity ( CreateEntityInput input )
2025-09-14 22:56:08 +08:00
{
2025-09-19 02:46:24 +08:00
var template = await GenerateEntity ( input ) ;
await File . WriteAllTextAsync ( template . OutPath , template . Context , Encoding . UTF8 ) ;
2025-09-14 22:56:08 +08:00
}
/// <summary>
/// 创建实体文件内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
2025-09-19 02:46:24 +08:00
[ApiDescriptionSettings(Name = "GenerateEntity"), HttpPost]
2025-09-14 22:56:08 +08:00
[DisplayName("创建实体文件内容")]
2025-09-19 02:46:24 +08:00
public async Task < TemplateContextOutput > GenerateEntity ( CreateEntityInput input )
2025-09-14 22:56:08 +08:00
{
2025-09-19 02:46:24 +08:00
var strategy = _codeGenStrategyFactory . GetStrategy < CreateEntityInput > ( CodeGenSceneEnum . TableEntity ) ;
return ( await strategy . GenerateCode ( input ) ) ? . FirstOrDefault ( ) ;
2025-09-14 22:56:08 +08:00
}
/// <summary>
/// 创建种子数据 🔖
/// </summary>
/// <param name="input"></param>
[ApiDescriptionSettings(Name = "CreateSeedData"), HttpPost]
[DisplayName("创建种子数据")]
public async Task CreateSeedData ( CreateSeedDataInput input )
{
2025-09-19 02:46:24 +08:00
var strategy = _codeGenStrategyFactory . GetStrategy < CreateSeedDataInput > ( CodeGenSceneEnum . TableSeedData ) ;
var result = ( await strategy . GenerateCode ( input ) ) ? . FirstOrDefault ( ) ;
await File . WriteAllTextAsync ( result ! . OutPath , result ! . Context , Encoding . UTF8 ) ;
2025-09-14 22:56:08 +08:00
}
/// <summary>
/// 获取种子数据列表 🔖
/// </summary>
/// <param name="configId"></param>
/// <returns></returns>
[DisplayName("获取种子数据列表")]
public List < DataInitItemOutput > GetSeedDataList ( [ FromQuery ] string configId )
{
var seedDataTypes = SeedDataHelper . GetInitSeedDataTypeList ( ) ;
var outputList = new List < DataInitItemOutput > ( ) ;
foreach ( var seedDataType in seedDataTypes )
{
var instance = Activator . CreateInstance ( seedDataType ) ;
var hasDataMethod = seedDataType . GetMethod ( "HasData" ) ;
var seedData = ( ( IEnumerable ) hasDataMethod ? . Invoke ( instance , null ) ) ? . Cast < object > ( ) . ToArray ( ) ? ? [ ] ;
var entityType = seedDataType . GetInterfaces ( ) . First ( ) . GetGenericArguments ( ) . First ( ) ;
var seedDataAtt = seedDataType . GetCustomAttribute < SeedDataAttribute > ( ) ;
outputList . Add ( new DataInitItemOutput ( )
{
Name = seedDataType . Name ,
AssemblyName = seedDataType . Assembly . ManifestModule . Name ,
Order = seedDataAtt ! = null ? seedDataAtt . Order : 0 ,
Count = seedData . Length ,
Description = entityType . GetCustomAttribute < SugarTable > ( ) . TableDescription + "种子数据"
} ) ;
}
return outputList ;
}
/// <summary>
/// 初始化表结构 🔖
/// </summary>
/// <param name="input"></param>
[DisplayName("初始化表结构")]
public void InitTable ( InitTableInput input )
{
if ( ! _userManager . SuperAdmin )
throw Oops . Oh ( "只有超管才可以操作!" ) ;
var dbProvider = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
SqlSugarSetup . InitTable ( dbProvider , false , input . EntityNames ) ;
}
/// <summary>
/// 初始化种子数据 🔖
/// </summary>
/// <param name="input"></param>
[DisplayName("初始化种子数据")]
public void InitSeedData ( InitSeedDataInput input )
{
if ( ! _userManager . SuperAdmin )
throw Oops . Oh ( "只有超管才可以操作!" ) ;
var dbProvider = _db . AsTenant ( ) . GetConnectionScope ( input . ConfigId ) ;
SqlSugarSetup . InitSeedData ( dbProvider , false , input . SeedNames ) ;
}
/// <summary>
/// 获取库表信息
/// </summary>
/// <returns></returns>
private async Task < IEnumerable < EntityInfo > > GetEntityInfos ( )
{
var entityInfos = new List < EntityInfo > ( ) ;
var type = typeof ( SugarTable ) ;
var types = new List < Type > ( ) ;
if ( _codeGenOptions . EntityAssemblyNames ! = null )
{
foreach ( var asm in _codeGenOptions . EntityAssemblyNames . Select ( Assembly . Load ) )
{
types . AddRange ( asm . GetExportedTypes ( ) . ToList ( ) ) ;
}
}
2025-09-14 23:19:08 +08:00
Type [ ] cosType = types . Where ( u = > IsMyAttribute ( Attribute . GetCustomAttributes ( u , false ) ) ) . ToArray ( ) ;
2025-09-14 22:56:08 +08:00
foreach ( var c in cosType )
{
var sugarAttribute = c . GetCustomAttributes ( type , true ) ? . FirstOrDefault ( ) ;
var des = c . GetCustomAttributes ( typeof ( DescriptionAttribute ) , true ) ;
var description = "" ;
if ( des . Length > 0 )
{
description = ( ( DescriptionAttribute ) des [ 0 ] ) . Description ;
}
entityInfos . Add ( new EntityInfo ( )
{
EntityName = c . Name ,
DbTableName = sugarAttribute = = null ? c . Name : ( ( SugarTable ) sugarAttribute ) . TableName ,
TableDescription = description ,
Type = c
} ) ;
}
return await Task . FromResult ( entityInfos ) ;
bool IsMyAttribute ( Attribute [ ] o )
{
return o . Any ( a = > a . GetType ( ) = = type ) ;
}
}
/// <summary>
/// 获取实体模板文件路径
/// </summary>
/// <returns></returns>
private static string GetEntityTemplatePath ( )
{
var templatePath = Path . Combine ( App . WebHostEnvironment . WebRootPath , "template" ) ;
return Path . Combine ( templatePath , "Entity.cs.vm" ) ;
}
/// <summary>
/// 获取种子数据模板文件路径
/// </summary>
/// <returns></returns>
private static string GetSeedDataTemplatePath ( )
{
var templatePath = Path . Combine ( App . WebHostEnvironment . WebRootPath , "template" ) ;
return Path . Combine ( templatePath , "SeedData.cs.vm" ) ;
}
/// <summary>
/// 设置生成实体文件路径
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string GetEntityTargetPath ( CreateEntityInput input )
{
var backendPath = Path . Combine ( new DirectoryInfo ( App . WebHostEnvironment . ContentRootPath ) . Parent . FullName , input . Position , "Entity" ) ;
if ( ! Directory . Exists ( backendPath ) )
Directory . CreateDirectory ( backendPath ) ;
return Path . Combine ( backendPath , input . EntityName + ".cs" ) ;
}
/// <summary>
/// 设置生成种子数据文件路径
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string GetSeedDataTargetPath ( CreateSeedDataInput input )
{
var backendPath = Path . Combine ( new DirectoryInfo ( App . WebHostEnvironment . ContentRootPath ) . Parent . FullName , input . Position , "SeedData" ) ;
if ( ! Directory . Exists ( backendPath ) )
Directory . CreateDirectory ( backendPath ) ;
return Path . Combine ( backendPath , input . SeedDataName + ".cs" ) ;
}
/// <summary>
/// 备份数据库( PostgreSQL) 🔖
/// </summary>
/// <returns></returns>
[HttpPost, NonUnify]
[DisplayName("备份数据库( PostgreSQL) ")]
public async Task < IActionResult > BackupDatabase ( )
{
if ( _db . CurrentConnectionConfig . DbType ! = SqlSugar . DbType . PostgreSQL )
throw Oops . Oh ( "只支持 PostgreSQL 数据库 😁" ) ;
var npgsqlConn = new NpgsqlConnectionStringBuilder ( _db . CurrentConnectionConfig . ConnectionString ) ;
if ( npgsqlConn = = null | | string . IsNullOrWhiteSpace ( npgsqlConn . Host ) | | string . IsNullOrWhiteSpace ( npgsqlConn . Username ) | | string . IsNullOrWhiteSpace ( npgsqlConn . Password ) | |
string . IsNullOrWhiteSpace ( npgsqlConn . Database ) )
throw Oops . Oh ( "PostgreSQL 数据库配置错误" ) ;
// 确保备份目录存在
var backupDirectory = Path . Combine ( Directory . GetCurrentDirectory ( ) , "backups" ) ;
Directory . CreateDirectory ( backupDirectory ) ;
// 构建备份文件名
string backupFileName = $"backup_{DateTime.Now:yyyyMMddHHmmss}.sql" ;
string backupFilePath = Path . Combine ( backupDirectory , backupFileName ) ;
// 启动pg_dump进程进行备份
// 设置密码: export PGPASSWORD='xxxxxx'
var bash = $"-U {npgsqlConn.Username} -h {npgsqlConn.Host} -p {npgsqlConn.Port} -E UTF8 -F c -b -v -f {backupFilePath} {npgsqlConn.Database}" ;
var startInfo = new ProcessStartInfo
{
FileName = "pg_dump" ,
Arguments = bash ,
UseShellExecute = false ,
RedirectStandardOutput = true ,
RedirectStandardError = true ,
CreateNoWindow = true ,
EnvironmentVariables =
{
["PGPASSWORD"] = npgsqlConn . Password
}
} ;
//_logger.LogInformation("备份数据库: pg_dump " + bash);
//try
//{
using ( var backupProcess = Process . Start ( startInfo ) )
{
await backupProcess . WaitForExitAsync ( ) ;
//var output = await backupProcess.StandardOutput.ReadToEndAsync();
//var error = await backupProcess.StandardError.ReadToEndAsync();
// 检查备份是否成功
if ( backupProcess . ExitCode ! = 0 )
{
throw Oops . Oh ( $"备份失败: ExitCode({backupProcess.ExitCode})" ) ;
}
}
// _logger.LogInformation($"备份成功:{backupFilePath}");
//}
//catch (Exception ex)
//{
// _logger.LogError(ex, $"备份失败:");
// throw;
//}
// 若备份成功则提供下载链接
return new FileStreamResult ( new FileStream ( backupFilePath , FileMode . Open ) , "application/octet-stream" )
{
FileDownloadName = backupFileName
} ;
}
2025-09-22 00:36:27 +08:00
/// <summary>
/// 生成SQL语句 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("生成SQL语句")]
public async Task < string > GenerateSQL ( GenerateSQLInput input )
{
2025-09-28 11:59:46 +08:00
return await Task . FromResult ( BuildSQL . Build ( input ) ) ;
2025-09-22 00:36:27 +08:00
}
/// <summary>
/// 执行SQL语句 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("执行SQL语句")]
public async Task < DataTable > ExecuteSQL ( GenerateSQLInput input )
{
var sql = await GenerateSQL ( input ) ;
2025-09-28 11:59:46 +08:00
if ( Regex . IsMatch ( sql , @"\b(delete|update)\b" , RegexOptions . IgnoreCase ) )
throw Oops . Oh ( "非法SQL语句, 已停止执行" ) ;
var dt = await _db . Ado . GetDataTableAsync ( sql ) ;
2025-09-22 00:36:27 +08:00
return dt ;
}
2024-06-15 13:02:35 +08:00
}