feat:新增数据库备份(mysql),自动备份,自动清理备份文件的功能。
This commit is contained in:
parent
dc70be51f8
commit
cb566b1842
@ -29,6 +29,7 @@
|
||||
<PackageReference Include="Magicodes.IE.Pdf" 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="MySqlBackup.NET.MySqlConnector" Version="2.3.8" />
|
||||
<PackageReference Include="NewLife.Redis" Version="5.7.2024.801" />
|
||||
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.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=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=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 },
|
||||
|
||||
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}`,
|
||||
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