feat: 数据库备份功能优化
This commit is contained in:
parent
8c108fe80b
commit
91091df964
@ -758,7 +758,7 @@ public enum ErrorCodeEnum
|
||||
db1001,
|
||||
|
||||
/// <summary>
|
||||
/// 数据表不存在
|
||||
/// 不允许添加相同字段名
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("不允许添加相同字段名")]
|
||||
db1002,
|
||||
@ -768,6 +768,12 @@ public enum ErrorCodeEnum
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("实体文件不存在或匹配不到。如果是刚刚生成的实体,请重启服务后再试")]
|
||||
db1003,
|
||||
|
||||
/// <summary>
|
||||
/// 数据库类型:{0} 不支持备份
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("数据库类型:{0} 不支持备份")]
|
||||
db1004,
|
||||
|
||||
/// <summary>
|
||||
/// 父节点不存在
|
||||
|
||||
14
Admin.NET/Admin.NET.Core/Service/DataBase/Dto/DbOutput.cs
Normal file
14
Admin.NET/Admin.NET.Core/Service/DataBase/Dto/DbOutput.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core.Service;
|
||||
|
||||
public class DbOutput
|
||||
{
|
||||
public string ConfigId { get; set; }
|
||||
|
||||
public string DbName { get; set; }
|
||||
}
|
||||
@ -33,9 +33,21 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取库列表")]
|
||||
public List<string> GetList()
|
||||
public List<DbOutput> GetList()
|
||||
{
|
||||
return App.GetOptions<DbConnectionOptions>().ConnectionConfigs.Select(u => u.ConfigId.ToString()).ToList();
|
||||
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>
|
||||
@ -305,7 +317,9 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
{
|
||||
var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId);
|
||||
input.Position = string.IsNullOrWhiteSpace(input.Position) ? "Admin.NET.Application" : input.Position;
|
||||
input.EntityName = string.IsNullOrWhiteSpace(input.EntityName) ? (config.DbSettings.EnableUnderLine ? CodeGenUtil.CamelColumnName(input.TableName, null) : input.TableName) : input.EntityName;
|
||||
input.EntityName = string.IsNullOrWhiteSpace(input.EntityName)
|
||||
? (config.DbSettings.EnableUnderLine ? CodeGenUtil.CamelColumnName(input.TableName, null) : input.TableName)
|
||||
: input.EntityName;
|
||||
string[] dbColumnNames = Array.Empty<string>();
|
||||
// Entity.cs.vm中是允许创建没有基类的实体的,所以这里也要做出相同的判断
|
||||
if (!string.IsNullOrWhiteSpace(input.BaseClassName))
|
||||
@ -325,7 +339,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
if (_codeGenOptions.BaseEntityNames.Contains(input.BaseClassName, StringComparer.OrdinalIgnoreCase))
|
||||
dbColumnInfos = dbColumnInfos.Where(u => !dbColumnNames.Contains(u.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var dbTableInfo = db.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == input.TableName || u.Name == input.TableName.ToLower()) ?? throw Oops.Oh(ErrorCodeEnum.db1001);
|
||||
var dbTableInfo = db.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == input.TableName || u.Name == input.TableName.ToLower()) ??
|
||||
throw Oops.Oh(ErrorCodeEnum.db1001);
|
||||
var templatePath = GetEntityTemplatePath();
|
||||
var tContent = File.ReadAllText(templatePath);
|
||||
var tResult = _viewEngine.RunCompileFromCached(tContent, new
|
||||
@ -365,6 +380,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
entityType = item.Type;
|
||||
break;
|
||||
}
|
||||
|
||||
if (entityType == null) throw Oops.Oh(ErrorCodeEnum.db1003);
|
||||
|
||||
input.EntityName = entityType.Name;
|
||||
@ -385,7 +401,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
if (input.FilterExistingData && records.Count != 0)
|
||||
{
|
||||
// 获取实体类型-所有种数据数据类型
|
||||
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName))
|
||||
var entityTypes = App.EffectiveTypes.Where(u =>
|
||||
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName))
|
||||
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
|
||||
.ToList();
|
||||
if (entityTypes.Count == 1) // 只有一个实体匹配才能过滤
|
||||
@ -424,6 +441,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
recordsToRemove.Add(record);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var itemToRemove in recordsToRemove)
|
||||
{
|
||||
records.Remove(itemToRemove);
|
||||
@ -436,7 +454,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
// 检查有没有 System.Text.Json.Serialization.JsonIgnore 或 Newtonsoft.Json.JsonIgnore 的属性
|
||||
// 如果 JsonIgnore 和 SugarColumn 都存在,那么后成序更化时就生成这了这些字段,就需要在这里另外补充(以处理用户表SysUser中的Password为例)
|
||||
var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
|
||||
p.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
|
||||
p.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null)
|
||||
.ToList();
|
||||
var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
|
||||
if (jsonIgnoreProperties.Count > 0)
|
||||
{
|
||||
@ -456,8 +475,10 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
else if (v.GetType() == typeof(DateTime))
|
||||
strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
|
||||
}
|
||||
|
||||
record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
|
||||
}
|
||||
|
||||
recordIndex++;
|
||||
jsonIgnoreInfo.Add(record);
|
||||
}
|
||||
@ -497,6 +518,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
{
|
||||
value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")";
|
||||
}
|
||||
|
||||
return $"{prop.Name}={value}";
|
||||
}))).ToList();
|
||||
|
||||
@ -556,6 +578,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
{
|
||||
description = ((DescriptionAttribute)des[0]).Description;
|
||||
}
|
||||
|
||||
entityInfos.Add(new EntityInfo()
|
||||
{
|
||||
EntityName = c.Name,
|
||||
@ -564,6 +587,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
Type = c
|
||||
});
|
||||
}
|
||||
|
||||
return await Task.FromResult(entityInfos);
|
||||
|
||||
bool IsMyAttribute(Attribute[] o)
|
||||
@ -630,7 +654,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
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))
|
||||
if (npgsqlConn == null || string.IsNullOrWhiteSpace(npgsqlConn.Host) || string.IsNullOrWhiteSpace(npgsqlConn.Username) || string.IsNullOrWhiteSpace(npgsqlConn.Password) ||
|
||||
string.IsNullOrWhiteSpace(npgsqlConn.Database))
|
||||
throw Oops.Oh("PostgreSQL 数据库配置错误");
|
||||
|
||||
// 确保备份目录存在
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
using DbType = SqlSugar.DbType;
|
||||
|
||||
namespace Admin.NET.Core.Service;
|
||||
|
||||
/// <summary>
|
||||
@ -12,13 +14,15 @@ namespace Admin.NET.Core.Service;
|
||||
[ApiDescriptionSettings(Order = 255, Description = "数据库备份")]
|
||||
public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly SqlSugarRepository<SysUser> _sysUserRep;
|
||||
private readonly string backupDir;
|
||||
private readonly ISqlSugarClient _db;
|
||||
private readonly string _backupDir;
|
||||
private readonly SysDatabaseService _databaseService;
|
||||
|
||||
public SysDbBackupService(SqlSugarRepository<SysUser> sysUserRep)
|
||||
public SysDbBackupService(ISqlSugarClient db, SysDatabaseService databaseService)
|
||||
{
|
||||
_sysUserRep = sysUserRep;
|
||||
backupDir = Path.Combine(App.WebHostEnvironment.WebRootPath, "DbBackup");
|
||||
_db = db;
|
||||
_databaseService = databaseService;
|
||||
_backupDir = Path.Combine(App.WebHostEnvironment.WebRootPath, "DbBackup");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -30,9 +34,9 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(backupDir))
|
||||
Directory.CreateDirectory(backupDir);
|
||||
var fileList = Directory.GetFiles(backupDir);
|
||||
if (!Directory.Exists(_backupDir))
|
||||
Directory.CreateDirectory(_backupDir);
|
||||
var fileList = Directory.GetFiles(_backupDir);
|
||||
|
||||
var dbBackupList = new List<DbBackupOutput>();
|
||||
foreach (var item in fileList)
|
||||
@ -45,6 +49,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
CreateTime = info.CreationTime
|
||||
});
|
||||
}
|
||||
|
||||
return dbBackupList;
|
||||
}
|
||||
catch
|
||||
@ -59,9 +64,9 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
/// <returns></returns>
|
||||
[ApiDescriptionSettings(Name = "Add"), HttpPost]
|
||||
[DisplayName("备份数据库")]
|
||||
public async Task AddBackup()
|
||||
public async Task AddBackup([FromQuery] string configId)
|
||||
{
|
||||
await Backup();
|
||||
await Backup(configId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -72,7 +77,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
[DisplayName("删除备份")]
|
||||
public void DeleteBackup([FromQuery] string fileName)
|
||||
{
|
||||
var path = Path.Combine(backupDir, fileName);
|
||||
var path = Path.Combine(_backupDir, fileName);
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
@ -81,24 +86,50 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public async Task Backup()
|
||||
public async Task Backup(string configId)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var options = App.GetOptions<DbConnectionOptions>();
|
||||
foreach (var option in options.ConnectionConfigs)
|
||||
{
|
||||
var configId = option.ConfigId == null || string.IsNullOrWhiteSpace(option.ConfigId.ToString())
|
||||
? SqlSugarConst.MainConfigId : option.ConfigId.ToString();
|
||||
var options = App.GetOptions<DbConnectionOptions>();
|
||||
var option = options.ConnectionConfigs.FirstOrDefault(u => u.ConfigId + "" == configId) ?? throw Oops.Bah(ErrorCodeEnum.D1002);
|
||||
var db = _db.AsTenant().GetConnectionScope(configId);
|
||||
|
||||
// 备份mysql 其他数据库自行扩展
|
||||
if (option?.DbType == SqlSugar.DbType.MySql)
|
||||
// 扩展名
|
||||
var ext = "bak";
|
||||
switch (option.DbType)
|
||||
{
|
||||
case DbType.MySql:
|
||||
ext = "sql";
|
||||
break;
|
||||
case DbType.SqlServer:
|
||||
ext = "bak";
|
||||
break;
|
||||
case DbType.Sqlite:
|
||||
ext = "db";
|
||||
break;
|
||||
case DbType.PostgreSQL:
|
||||
ext = "sql";
|
||||
break;
|
||||
}
|
||||
|
||||
var path = Path.Combine(_backupDir, $"{db.Ado.Connection.Database}_{DateTime.Now:yyyyMMddHHmmss}.{ext}");
|
||||
|
||||
// 备份数据库
|
||||
switch (option.DbType)
|
||||
{
|
||||
case DbType.MySql or DbType.Sqlite or DbType.SqlServer:
|
||||
await Task.Run(() => { db.DbMaintenance.BackupDataBase(db.Ado.Connection.Database, path); });
|
||||
break;
|
||||
case DbType.PostgreSQL:
|
||||
{
|
||||
var path = Path.Combine(backupDir, $"{configId}-{DateTime.Now:yyyyMMddHHmmss}.sql");
|
||||
_sysUserRep.Context.DbMaintenance.BackupDataBase(_sysUserRep.Context.Ado.Connection.Database, path);
|
||||
var fileStreamResult = (FileStreamResult)(await _databaseService.BackupDatabase());
|
||||
// 将 fileStreamResult 保存为文件
|
||||
await using var fileStream = new FileStream(path, FileMode.Create);
|
||||
await fileStreamResult.FileStream.CopyToAsync(fileStream);
|
||||
await fileStream.FlushAsync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
default:
|
||||
throw Oops.Bah(ErrorCodeEnum.db1004, option.DbType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -108,7 +139,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
[NonAction]
|
||||
public void DeleteExpiredDbFile(int day = 7)
|
||||
{
|
||||
var list = Directory.GetFiles(backupDir);
|
||||
var list = Directory.GetFiles(_backupDir);
|
||||
foreach (var item in list)
|
||||
{
|
||||
var info = new FileInfo(item);
|
||||
@ -120,7 +151,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import { Configuration } from '../configuration';
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
|
||||
import { AdminNETResultListDbColumnOutput } from '../models';
|
||||
import { AdminNETResultListDbOutput } from '../models';
|
||||
import { AdminNETResultListDbTableInfo } from '../models';
|
||||
import { AdminNETResultListString } from '../models';
|
||||
import { AdminNETResultString } from '../models';
|
||||
@ -989,7 +990,7 @@ export const SysDatabaseApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListString>>> {
|
||||
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListDbOutput>>> {
|
||||
const localVarAxiosArgs = await SysDatabaseApiAxiosParamCreator(configuration).apiSysDatabaseListGet(options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
@ -1179,7 +1180,7 @@ export const SysDatabaseApiFactory = function (configuration?: Configuration, ba
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListString>> {
|
||||
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListDbOutput>> {
|
||||
return SysDatabaseApiFp(configuration).apiSysDatabaseListGet(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
@ -1362,7 +1363,7 @@ export class SysDatabaseApi extends BaseAPI {
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysDatabaseApi
|
||||
*/
|
||||
public async apiSysDatabaseListGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListString>> {
|
||||
public async apiSysDatabaseListGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListDbOutput>> {
|
||||
return SysDatabaseApiFp(this.configuration).apiSysDatabaseListGet(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
|
||||
@ -27,10 +27,11 @@ export const SysDbBackupApiAxiosParamCreator = function (configuration?: Configu
|
||||
/**
|
||||
*
|
||||
* @summary 备份数据库 🔖
|
||||
* @param {string} [configId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysDbBackupAddPost: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
apiSysDbBackupAddPost: async (configId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysDbBackup/add`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
@ -51,6 +52,10 @@ export const SysDbBackupApiAxiosParamCreator = function (configuration?: Configu
|
||||
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
|
||||
if (configId !== undefined) {
|
||||
localVarQueryParameter['configId'] = configId;
|
||||
}
|
||||
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
query.set(key, localVarQueryParameter[key]);
|
||||
@ -170,11 +175,12 @@ export const SysDbBackupApiFp = function(configuration?: Configuration) {
|
||||
/**
|
||||
*
|
||||
* @summary 备份数据库 🔖
|
||||
* @param {string} [configId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysDbBackupAddPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
|
||||
const localVarAxiosArgs = await SysDbBackupApiAxiosParamCreator(configuration).apiSysDbBackupAddPost(options);
|
||||
async apiSysDbBackupAddPost(configId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
|
||||
const localVarAxiosArgs = await SysDbBackupApiAxiosParamCreator(configuration).apiSysDbBackupAddPost(configId, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
@ -219,11 +225,12 @@ export const SysDbBackupApiFactory = function (configuration?: Configuration, ba
|
||||
/**
|
||||
*
|
||||
* @summary 备份数据库 🔖
|
||||
* @param {string} [configId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysDbBackupAddPost(options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysDbBackupApiFp(configuration).apiSysDbBackupAddPost(options).then((request) => request(axios, basePath));
|
||||
async apiSysDbBackupAddPost(configId?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysDbBackupApiFp(configuration).apiSysDbBackupAddPost(configId, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
@ -257,12 +264,13 @@ export class SysDbBackupApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @summary 备份数据库 🔖
|
||||
* @param {string} [configId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysDbBackupApi
|
||||
*/
|
||||
public async apiSysDbBackupAddPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysDbBackupApiFp(this.configuration).apiSysDbBackupAddPost(options).then((request) => request(this.axios, this.basePath));
|
||||
public async apiSysDbBackupAddPost(configId?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysDbBackupApiFp(this.configuration).apiSysDbBackupAddPost(configId, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Admin.NET 通用权限开发平台
|
||||
* 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
|
||||
*
|
||||
* OpenAPI spec version: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { DbOutput } from './db-output';
|
||||
/**
|
||||
* 全局返回结果
|
||||
*
|
||||
* @export
|
||||
* @interface AdminNETResultListDbOutput
|
||||
*/
|
||||
export interface AdminNETResultListDbOutput {
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof AdminNETResultListDbOutput
|
||||
*/
|
||||
code?: number;
|
||||
|
||||
/**
|
||||
* 类型success、warning、error
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AdminNETResultListDbOutput
|
||||
*/
|
||||
type?: string | null;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AdminNETResultListDbOutput
|
||||
*/
|
||||
message?: string | null;
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*
|
||||
* @type {Array<DbOutput>}
|
||||
* @memberof AdminNETResultListDbOutput
|
||||
*/
|
||||
result?: Array<DbOutput> | null;
|
||||
|
||||
/**
|
||||
* 附加数据
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof AdminNETResultListDbOutput
|
||||
*/
|
||||
extras?: any | null;
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof AdminNETResultListDbOutput
|
||||
*/
|
||||
time?: Date;
|
||||
}
|
||||
34
Web/src/api-services/models/db-output.ts
Normal file
34
Web/src/api-services/models/db-output.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Admin.NET 通用权限开发平台
|
||||
* 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
|
||||
*
|
||||
* OpenAPI spec version: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @interface DbOutput
|
||||
*/
|
||||
export interface DbOutput {
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @memberof DbOutput
|
||||
*/
|
||||
configId?: string | null;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @memberof DbOutput
|
||||
*/
|
||||
dbName?: string | null;
|
||||
}
|
||||
@ -25,6 +25,7 @@ export enum GenericParameterAttributes {
|
||||
NUMBER_4 = 4,
|
||||
NUMBER_8 = 8,
|
||||
NUMBER_16 = 16,
|
||||
NUMBER_28 = 28
|
||||
NUMBER_28 = 28,
|
||||
NUMBER_32 = 32
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ export * from './admin-netresult-list-const-output';
|
||||
export * from './admin-netresult-list-database-output';
|
||||
export * from './admin-netresult-list-db-backup-output';
|
||||
export * from './admin-netresult-list-db-column-output';
|
||||
export * from './admin-netresult-list-db-output';
|
||||
export * from './admin-netresult-list-db-table-info';
|
||||
export * from './admin-netresult-list-enum-entity';
|
||||
export * from './admin-netresult-list-enum-type-output';
|
||||
@ -181,6 +182,7 @@ export * from './db-backup-output';
|
||||
export * from './db-column-input';
|
||||
export * from './db-column-output';
|
||||
export * from './db-object-type';
|
||||
export * from './db-output';
|
||||
export * from './db-table-info';
|
||||
export * from './db-table-input';
|
||||
export * from './db-type';
|
||||
|
||||
@ -61,12 +61,6 @@ export interface TypeInfo {
|
||||
*/
|
||||
metadataToken?: number;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @memberof TypeInfo
|
||||
*/
|
||||
isInterface?: boolean;
|
||||
|
||||
/**
|
||||
* @type {MemberTypes}
|
||||
* @memberof TypeInfo
|
||||
@ -103,6 +97,12 @@ export interface TypeInfo {
|
||||
*/
|
||||
module?: Module;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @memberof TypeInfo
|
||||
*/
|
||||
isInterface?: boolean;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @memberof TypeInfo
|
||||
|
||||
@ -55,12 +55,6 @@ export interface Type {
|
||||
*/
|
||||
metadataToken?: number;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @memberof Type
|
||||
*/
|
||||
isInterface?: boolean;
|
||||
|
||||
/**
|
||||
* @type {MemberTypes}
|
||||
* @memberof Type
|
||||
@ -97,6 +91,12 @@ export interface Type {
|
||||
*/
|
||||
module?: Module;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @memberof Type
|
||||
*/
|
||||
isInterface?: boolean;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @memberof Type
|
||||
|
||||
@ -3,10 +3,21 @@
|
||||
<el-card class="full-table" shadow="hover" style="margin-top: 5px">
|
||||
<vxe-grid ref="xGrid" class="xGrid-style" v-bind="options">
|
||||
<template #toolbar_buttons>
|
||||
<el-button type="primary" icon="ele-Plus" @click="handleAdd" v-auth="'dbBackup/add'" :loading="loading"> 新增 </el-button>
|
||||
<el-form :inlineMessage="true" label-width="auto" style="flex: 1 1 0%">
|
||||
<el-row :gutter="10">
|
||||
<el-col class="mb5" :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="库名" prop="configId">
|
||||
<el-select v-model="state.configId" placeholder="库名" filterable>
|
||||
<el-option v-for="item in state.dbData" :key="item.configId" :label="`${item.dbName}(${item.configId})`" :value="item.configId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col class="mb5" :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-button type="primary" icon="ele-Plus" @click="handleAdd" v-auth="'dbBackup/add'" :loading="state.loading"> 新增 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
<template #toolbar_tools> </template>
|
||||
|
||||
<template #row_createTime="{ row }">
|
||||
<el-tooltip :content="row.createTime" placement="top">
|
||||
<span>{{ formatPast(row.createTime) }}</span>
|
||||
@ -16,9 +27,7 @@
|
||||
<span>{{ (row.size / 1024 / 1024).toFixed(2) }} MB</span>
|
||||
</template>
|
||||
<template #row_buttons="{ row }">
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button icon="ele-Delete" text type="danger" v-auth="'dbBackup/delete'" @click="handleDelete(row)"> </el-button>
|
||||
</el-tooltip>
|
||||
<el-button icon="ele-Delete" text type="danger" v-auth="'dbBackup/delete'" @click="handleDelete(row)"> 删除 </el-button>
|
||||
</template>
|
||||
</vxe-grid>
|
||||
</el-card>
|
||||
@ -26,16 +35,22 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { VxeGridInstance } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { formatPast } from '/@/utils/formatTime';
|
||||
|
||||
import { getAPI } from '/@/utils/axios-utils';
|
||||
import { SysDbBackupApi } from '/@/api-services/api';
|
||||
import { SysDatabaseApi, SysDbBackupApi } from '/@/api-services/api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
const xGrid = ref<VxeGridInstance>();
|
||||
const loading = ref(false);
|
||||
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
dbData: [] as any,
|
||||
configId: '',
|
||||
});
|
||||
|
||||
// 表格参数配置
|
||||
const options = useVxeTable(
|
||||
{
|
||||
@ -56,29 +71,61 @@ const options = useVxeTable(
|
||||
// 分页配置
|
||||
pagerConfig: { enabled: false },
|
||||
// 工具栏配置
|
||||
toolbarConfig: { enabled: false },
|
||||
toolbarConfig: { enabled: true },
|
||||
}
|
||||
);
|
||||
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
options.loading = true;
|
||||
let res = await getAPI(SysDatabaseApi).apiSysDatabaseListGet();
|
||||
state.dbData = res.data.result;
|
||||
|
||||
options.loading = false;
|
||||
});
|
||||
|
||||
// 查询api
|
||||
const handleQueryApi = async () => {
|
||||
return await getAPI(SysDbBackupApi).apiSysDbBackupListGet();
|
||||
};
|
||||
|
||||
// 增加备份
|
||||
// 新增备份
|
||||
const handleAdd = async () => {
|
||||
loading.value = true;
|
||||
await getAPI(SysDbBackupApi).apiSysDbBackupAddPost();
|
||||
loading.value = false;
|
||||
await xGrid.value?.commitProxy('reload');
|
||||
try {
|
||||
if (!state.configId) {
|
||||
ElMessage.warning('请选择库名');
|
||||
return;
|
||||
}
|
||||
|
||||
await ElMessageBox.confirm('确定要新增备份吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
|
||||
state.loading = true;
|
||||
await getAPI(SysDbBackupApi).apiSysDbBackupAddPost(state.configId);
|
||||
await xGrid.value?.commitProxy('reload');
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 删除备份
|
||||
const handleDelete = async (row: any) => {
|
||||
loading.value = true;
|
||||
await getAPI(SysDbBackupApi).apiSysDbBackupDeletePost(row.fileName);
|
||||
loading.value = false;
|
||||
await xGrid.value?.commitProxy('reload');
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要删除备份:【${row.fileName}】吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
|
||||
state.loading = true;
|
||||
await getAPI(SysDbBackupApi).apiSysDbBackupDeletePost(row.fileName);
|
||||
await xGrid.value?.commitProxy('reload');
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<el-col class="mb5" :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="库名" prop="configId">
|
||||
<el-select v-model="state.configId" placeholder="库名" filterable @change="handleQueryTable">
|
||||
<el-option v-for="item in state.dbData" :key="item" :label="item" :value="item" />
|
||||
<el-option v-for="item in state.dbData" :key="item.configId" :label="`${item.dbName}(${item.configId})`" :value="item.configId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user