Merge pull request 'feat:新增数据库备份(mysql),自动备份,自动清理备份文件的功能。' (#121) from skywolf627/Admin.NET.Pro:main into main
Reviewed-on: http://101.43.53.74:3000/Admin.NET/Admin.NET.Pro/pulls/121
This commit is contained in:
commit
683ebe16a5
@ -29,6 +29,7 @@
|
|||||||
<PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.1" />
|
<PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.1" />
|
||||||
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.1" />
|
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.1" />
|
||||||
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
||||||
|
<PackageReference Include="MySqlBackup.NET.MySqlConnector" Version="2.3.8" />
|
||||||
<PackageReference Include="NewLife.Redis" Version="5.7.2024.801" />
|
<PackageReference Include="NewLife.Redis" Version="5.7.2024.801" />
|
||||||
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
|
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
|
||||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
|
|||||||
49
Admin.NET/Admin.NET.Core/Job/DbBackupJob.cs
Normal file
49
Admin.NET/Admin.NET.Core/Job/DbBackupJob.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||||
|
//
|
||||||
|
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||||
|
//
|
||||||
|
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||||
|
|
||||||
|
using Furion.TimeCrontab;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
|
[JobDetail("job_DbBackupJob", Description = "备份数据库", GroupName = "default", Concurrent = false)]
|
||||||
|
[Cron("0 1 * * *", CronStringFormat.Default, TriggerId = "trigger_DbBackupJob", Description = "备份数据库", RunOnStart = false)]
|
||||||
|
public class DbBackupJob : IJob
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public DbBackupJob(IServiceScopeFactory scopeFactory, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
string msg = $"【{DateTime.Now}】开始备份数据库";
|
||||||
|
var originColor = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
Console.ForegroundColor = originColor;
|
||||||
|
|
||||||
|
using var serviceScope = _scopeFactory.CreateScope();
|
||||||
|
var dbBackupService = serviceScope.ServiceProvider.GetRequiredService<DbBackupService>();
|
||||||
|
|
||||||
|
//清理7天前的备份文件
|
||||||
|
dbBackupService.DeleteExpiredDbFile(7);
|
||||||
|
//开始备份
|
||||||
|
await dbBackupService.Backup();
|
||||||
|
|
||||||
|
msg = $"【{DateTime.Now}】数据库备份完成";
|
||||||
|
originColor = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
Console.ForegroundColor = originColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -175,6 +175,11 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
|
|||||||
new SysMenu{ Id=1310000000445, Pid=1310000000441, Title="获取支付订单详情(微信接口)", Permission="sysWechatPay/payInfoFromWechat", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
new SysMenu{ Id=1310000000445, Pid=1310000000441, Title="获取支付订单详情(微信接口)", Permission="sysWechatPay/payInfoFromWechat", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
new SysMenu{ Id=1310000000446, Pid=1310000000441, Title="退款申请", Permission="sysWechatPay/refundDomestic", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
new SysMenu{ Id=1310000000446, Pid=1310000000441, Title="退款申请", Permission="sysWechatPay/refundDomestic", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
|
|
||||||
|
new SysMenu{ Id=1310000000447, Pid=1310000000301, Title="数据库备份", Path="/platform/dbBackup", Name="dbBackup", Component="/system/dbBackup/index", Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=240 },
|
||||||
|
new SysMenu{ Id=1310000000448, Pid=1310000000447, Title="查询", Permission="dbBackup/page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
|
new SysMenu{ Id=1310000000449, Pid=1310000000447, Title="删除", Permission="dbBackup/delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
|
new SysMenu{ Id=1310000000450, Pid=1310000000447, Title="增加", Permission="dbBackup/add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
|
|
||||||
new SysMenu{ Id=1310000000501, Pid=0, Title="日志管理", Path="/log", Name="log", Component="Layout", Icon="ele-DocumentCopy", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=12000 },
|
new SysMenu{ Id=1310000000501, Pid=0, Title="日志管理", Path="/log", Name="log", Component="Layout", Icon="ele-DocumentCopy", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=12000 },
|
||||||
new SysMenu{ Id=1310000000511, Pid=1310000000501, Title="访问日志", Path="/log/logvis", Name="sysLogVis", Component="/system/log/logvis/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
new SysMenu{ Id=1310000000511, Pid=1310000000501, Title="访问日志", Path="/log/logvis", Name="sysLogVis", Component="/system/log/logvis/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
new SysMenu{ Id=1310000000512, Pid=1310000000511, Title="查询", Permission="sysLogVis/page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
new SysMenu{ Id=1310000000512, Pid=1310000000511, Title="查询", Permission="sysLogVis/page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||||
|
|||||||
123
Admin.NET/Admin.NET.Core/Service/DbBackup/DbBackupService.cs
Normal file
123
Admin.NET/Admin.NET.Core/Service/DbBackup/DbBackupService.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||||
|
//
|
||||||
|
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||||
|
//
|
||||||
|
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Admin.NET.Core.Service;
|
||||||
|
|
||||||
|
public class DbBackupService : IDynamicApiController, ITransient
|
||||||
|
{
|
||||||
|
private readonly SqlSugarRepository<SysUser> _sysUserRep;
|
||||||
|
private readonly string backupDir;
|
||||||
|
|
||||||
|
public DbBackupService(SqlSugarRepository<SysUser> sysUserRep)
|
||||||
|
{
|
||||||
|
_sysUserRep = sysUserRep;
|
||||||
|
backupDir = Path.Combine(App.WebHostEnvironment.WebRootPath, "DbBackup");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 备份数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ApiDescriptionSettings(Name = "Add")]
|
||||||
|
[DisplayName("备份数据库")]
|
||||||
|
public async Task AddBackup()
|
||||||
|
{
|
||||||
|
await Backup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 备份数据库 用于job调用
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[ApiDescriptionSettings(false)]
|
||||||
|
public async Task Backup()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
if (option?.DbType == SqlSugar.DbType.MySql) //备份mysql 其他数据库自行扩展
|
||||||
|
{
|
||||||
|
var path = Path.Combine(backupDir, $"{configId}-{DateTime.Now:yyyyMMddHHmmss}.sql");
|
||||||
|
_sysUserRep.Context.DbMaintenance.BackupDataBase(_sysUserRep.Context.Ado.Connection.Database, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除过期备份文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="day">过期天数</param>
|
||||||
|
[ApiDescriptionSettings(false)]
|
||||||
|
public void DeleteExpiredDbFile(int day = 7)
|
||||||
|
{
|
||||||
|
var list = Directory.GetFiles(backupDir);
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
var info = new FileInfo(item);
|
||||||
|
if (info.CreationTime.AddDays(day) < DateTime.Now)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(item);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取备份列表
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ApiDescriptionSettings(Name = "Page")]
|
||||||
|
[DisplayName("获取备份列表")]
|
||||||
|
public List<BackupDto> GetBackupList()
|
||||||
|
{
|
||||||
|
var list = Directory.GetFiles(backupDir);
|
||||||
|
var result = new List<BackupDto>();
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
var info = new FileInfo(item);
|
||||||
|
result.Add(new BackupDto
|
||||||
|
{
|
||||||
|
FileName = info.Name,
|
||||||
|
Size = info.Length,
|
||||||
|
CreateTime = info.CreationTime
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除备份
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
[HttpPost]
|
||||||
|
[ApiDescriptionSettings(Name = "Delete")]
|
||||||
|
[DisplayName("删除备份")]
|
||||||
|
public void DeleteBackup([FromQuery] string fileName)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(backupDir, fileName);
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Admin.NET/Admin.NET.Core/Service/DbBackup/Dto/BackupDto.cs
Normal file
20
Admin.NET/Admin.NET.Core/Service/DbBackup/Dto/BackupDto.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||||
|
//
|
||||||
|
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||||
|
//
|
||||||
|
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Admin.NET.Core.Service;
|
||||||
|
|
||||||
|
public class BackupDto
|
||||||
|
{
|
||||||
|
public long Size { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
}
|
||||||
@ -17,3 +17,25 @@ export const getAllDictList = () =>
|
|||||||
url: `${Api.AllDictList}`,
|
url: `${Api.AllDictList}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取所有备份文件
|
||||||
|
export const getBackupList = () =>
|
||||||
|
request({
|
||||||
|
url: `/api/dbBackup/page`,
|
||||||
|
method: 'post',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增备份文件
|
||||||
|
export const addBackup = () =>
|
||||||
|
request({
|
||||||
|
url: `/api/dbBackup/add`,
|
||||||
|
method: 'post',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除备份文件
|
||||||
|
export const deleteBackup = (fileName: String) =>
|
||||||
|
request({
|
||||||
|
url: `/api/dbBackup/delete`,
|
||||||
|
method: 'post',
|
||||||
|
params: { fileName },
|
||||||
|
});
|
||||||
|
|||||||
129
Web/src/views/system/dbBackup/index.vue
Normal file
129
Web/src/views/system/dbBackup/index.vue
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card class="full-table" shadow="hover" style="margin-top: 5px">
|
||||||
|
<vxe-grid ref="xGrid" class="xGrid-style" v-bind="options" v-on="gridEvents">
|
||||||
|
<template #toolbar_buttons>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="ele-Plus"
|
||||||
|
@click="handleAdd"
|
||||||
|
v-auth="'dbBackup/add'"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #toolbar_tools> </template>
|
||||||
|
|
||||||
|
<template #row_createTime="{ row }">
|
||||||
|
<el-tooltip :content="row.createTime" placement="top">
|
||||||
|
<span>{{ formatPast(row.createTime) }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<template #row_size="{ row }">
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
</vxe-grid>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { ElMessageBox, ElMessage } from "element-plus";
|
||||||
|
import { VxeGridInstance, VxeGridListeners } from "vxe-table";
|
||||||
|
import { useVxeTable } from "/@/hooks/useVxeTableOptionsHook";
|
||||||
|
import { formatPast } from "/@/utils/formatTime";
|
||||||
|
import { getBackupList, addBackup, deleteBackup } from "/@/api/system/admin";
|
||||||
|
|
||||||
|
const xGrid = ref<VxeGridInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
// 表格参数配置
|
||||||
|
const options = useVxeTable(
|
||||||
|
{
|
||||||
|
id: "dbBackup",
|
||||||
|
name: "备份信息",
|
||||||
|
columns: [
|
||||||
|
// { type: 'checkbox', width: 40, fixed: 'left' },
|
||||||
|
{ type: "seq", title: "序号", width: 60, fixed: "left" },
|
||||||
|
{
|
||||||
|
field: "fileName",
|
||||||
|
title: "文件名称",
|
||||||
|
minWidth: 180,
|
||||||
|
showOverflow: "tooltip",
|
||||||
|
treeNode: true,
|
||||||
|
align: "left",
|
||||||
|
headerAlign: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "size",
|
||||||
|
title: "文件大小",
|
||||||
|
width: 100,
|
||||||
|
showOverflow: "tooltip",
|
||||||
|
slots: { default: "row_size" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "createTime",
|
||||||
|
title: "备份时间",
|
||||||
|
width: 140,
|
||||||
|
showOverflow: "tooltip",
|
||||||
|
slots: { default: "row_createTime" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
fixed: "right",
|
||||||
|
width: 100,
|
||||||
|
showOverflow: true,
|
||||||
|
slots: { default: "row_buttons" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// vxeGrid配置参数(此处可覆写任何参数),参考vxe-table官方文档
|
||||||
|
{
|
||||||
|
stripe: false,
|
||||||
|
// 多选配置
|
||||||
|
checkboxConfig: { range: false },
|
||||||
|
// 代理配置
|
||||||
|
proxyConfig: { autoLoad: true, ajax: { query: () => handleQueryApi() } },
|
||||||
|
// 分页配置
|
||||||
|
pagerConfig: { enabled: false },
|
||||||
|
// 工具栏配置
|
||||||
|
toolbarConfig: { export: false },
|
||||||
|
// 树形配置
|
||||||
|
treeConfig: { expandAll: false },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 查询api
|
||||||
|
const handleQueryApi = async () => {
|
||||||
|
return getBackupList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAdd = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await addBackup();
|
||||||
|
loading.value = false;
|
||||||
|
await xGrid.value?.commitProxy("reload");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
loading.value = true;
|
||||||
|
await deleteBackup(row.fileName);
|
||||||
|
loading.value = false;
|
||||||
|
await xGrid.value?.commitProxy("reload");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Loading…
Reference in New Issue
Block a user