feat: flow design & page query refactor

This commit is contained in:
meilin.huang
2025-05-20 21:04:47 +08:00
parent 44d379a016
commit f676ec9e7b
269 changed files with 5072 additions and 5075 deletions

View File

@@ -77,7 +77,7 @@ func (d *Db) ReqConfs() *req.Confs {
// @router /api/dbs [get]
func (d *Db) Dbs(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage[*entity.DbQuery](rc, new(entity.DbQuery))
queryCond := req.BindQuery[*entity.DbQuery](rc, new(entity.DbQuery))
// 不存在可访问标签id即没有可操作数据
tags := d.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
@@ -85,14 +85,15 @@ func (d *Db) Dbs(rc *req.Ctx) {
CodePathLikes: collx.AsArray(queryCond.TagPath),
})
if len(tags) == 0 {
rc.ResData = model.EmptyPageResult[any]()
rc.ResData = model.NewEmptyPageResult[any]()
return
}
queryCond.Codes = tags.GetCodes()
var dbvos []*vo.DbListVO
res, err := d.dbApp.GetPageList(queryCond, page, &dbvos)
res, err := d.dbApp.GetPageList(queryCond)
biz.ErrIsNil(err)
resVo := model.PageResultConv[*entity.DbListPO, *vo.DbListVO](res)
dbvos := resVo.List
instances, _ := d.instanceApp.GetByIds(collx.ArrayMap(dbvos, func(i *vo.DbListVO) uint64 {
return i.InstanceId
@@ -110,7 +111,7 @@ func (d *Db) Dbs(rc *req.Ctx) {
}
}
rc.ResData = res
rc.ResData = resVo
}
func (d *Db) Save(rc *req.Ctx) {

View File

@@ -1,220 +0,0 @@
package api
import (
"context"
"mayfly-go/internal/db/api/form"
"mayfly-go/internal/db/api/vo"
"mayfly-go/internal/db/application"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/req"
"mayfly-go/pkg/utils/timex"
"strconv"
"strings"
"time"
)
type DbBackup struct {
backupApp *application.DbBackupApp `inject:"DbBackupApp"`
dbApp application.Db `inject:"DbApp"`
restoreApp *application.DbRestoreApp `inject:"DbRestoreApp"`
}
// todo: 鉴权,避免未经授权进行数据库备份和恢复
// GetPageList 获取数据库备份任务
// @router /api/dbs/:dbId/backups [GET]
func (d *DbBackup) GetPageList(rc *req.Ctx) {
dbId := uint64(rc.PathParamInt("dbId"))
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
db, err := d.dbApp.GetById(dbId)
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
queryCond, page := req.BindQueryAndPage[*entity.DbBackupQuery](rc, new(entity.DbBackupQuery))
queryCond.DbInstanceId = db.InstanceId
queryCond.InDbNames = strings.Fields(db.Database)
res, err := d.backupApp.GetPageList(queryCond, page, new([]vo.DbBackup))
biz.ErrIsNilAppendErr(err, "获取数据库备份任务失败: %v")
rc.ResData = res
}
// Create 保存数据库备份任务
// @router /api/dbs/:dbId/backups [POST]
func (d *DbBackup) Create(rc *req.Ctx) {
backupForm := req.BindJsonAndValid(rc, &form.DbBackupForm{})
rc.ReqParam = backupForm
dbNames := strings.Fields(backupForm.DbNames)
biz.IsTrue(len(dbNames) > 0, "解析数据库备份任务失败:数据库名称未定义")
dbId := uint64(rc.PathParamInt("dbId"))
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
db, err := d.dbApp.GetById(dbId)
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
jobs := make([]*entity.DbBackup, 0, len(dbNames))
for _, dbName := range dbNames {
job := &entity.DbBackup{
DbInstanceId: db.InstanceId,
DbName: dbName,
Enabled: true,
Repeated: backupForm.Repeated,
StartTime: backupForm.StartTime,
Interval: backupForm.Interval,
Name: backupForm.Name,
}
jobs = append(jobs, job)
}
biz.ErrIsNilAppendErr(d.backupApp.Create(rc.MetaCtx, jobs), "添加数据库备份任务失败: %v")
}
// Update 保存数据库备份任务
// @router /api/dbs/:dbId/backups/:backupId [PUT]
func (d *DbBackup) Update(rc *req.Ctx) {
backupForm := &form.DbBackupForm{}
req.BindJsonAndValid(rc, backupForm)
rc.ReqParam = backupForm
job := &entity.DbBackup{}
job.Id = backupForm.Id
job.Name = backupForm.Name
job.StartTime = backupForm.StartTime
job.Interval = backupForm.Interval
job.MaxSaveDays = backupForm.MaxSaveDays
biz.ErrIsNilAppendErr(d.backupApp.Update(rc.MetaCtx, job), "保存数据库备份任务失败: %v")
}
func (d *DbBackup) walk(rc *req.Ctx, paramName string, fn func(ctx context.Context, id uint64) error) error {
idsStr := rc.PathParam(paramName)
biz.NotEmpty(idsStr, paramName+" 为空")
rc.ReqParam = idsStr
ids := strings.Fields(idsStr)
for _, v := range ids {
value, err := strconv.Atoi(v)
if err != nil {
return err
}
backupId := uint64(value)
err = fn(rc.MetaCtx, backupId)
if err != nil {
return err
}
}
return nil
}
// Delete 删除数据库备份任务
// @router /api/dbs/:dbId/backups/:backupId [DELETE]
func (d *DbBackup) Delete(rc *req.Ctx) {
err := d.walk(rc, "backupId", d.backupApp.Delete)
biz.ErrIsNilAppendErr(err, "删除数据库备份任务失败: %v")
}
// Enable 启用数据库备份任务
// @router /api/dbs/:dbId/backups/:backupId/enable [PUT]
func (d *DbBackup) Enable(rc *req.Ctx) {
err := d.walk(rc, "backupId", d.backupApp.Enable)
biz.ErrIsNilAppendErr(err, "启用数据库备份任务失败: %v")
}
// Disable 禁用数据库备份任务
// @router /api/dbs/:dbId/backups/:backupId/disable [PUT]
func (d *DbBackup) Disable(rc *req.Ctx) {
err := d.walk(rc, "backupId", d.backupApp.Disable)
biz.ErrIsNilAppendErr(err, "禁用数据库备份任务失败: %v")
}
// Start 禁用数据库备份任务
// @router /api/dbs/:dbId/backups/:backupId/start [PUT]
func (d *DbBackup) Start(rc *req.Ctx) {
err := d.walk(rc, "backupId", d.backupApp.StartNow)
biz.ErrIsNilAppendErr(err, "运行数据库备份任务失败: %v")
}
// GetDbNamesWithoutBackup 获取未配置定时备份的数据库名称
// @router /api/dbs/:dbId/db-names-without-backup [GET]
func (d *DbBackup) GetDbNamesWithoutBackup(rc *req.Ctx) {
dbId := uint64(rc.PathParamInt("dbId"))
db, err := d.dbApp.GetById(dbId, "instance_id", "database")
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
dbNames := strings.Fields(db.Database)
dbNamesWithoutBackup, err := d.backupApp.GetDbNamesWithoutBackup(db.InstanceId, dbNames)
biz.ErrIsNilAppendErr(err, "获取未配置定时备份的数据库名称失败: %v")
rc.ResData = dbNamesWithoutBackup
}
// GetHistoryPageList 获取数据库备份历史
// @router /api/dbs/:dbId/backups/:backupId/histories [GET]
func (d *DbBackup) GetHistoryPageList(rc *req.Ctx) {
dbId := uint64(rc.PathParamInt("dbId"))
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
db, err := d.dbApp.GetById(dbId, "instance_id", "database")
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
backupHistoryCond, page := req.BindQueryAndPage[*entity.DbBackupHistoryQuery](rc, new(entity.DbBackupHistoryQuery))
backupHistoryCond.DbInstanceId = db.InstanceId
backupHistoryCond.InDbNames = strings.Fields(db.Database)
backupHistories := make([]*vo.DbBackupHistory, 0, page.PageSize)
res, err := d.backupApp.GetHistoryPageList(backupHistoryCond, page, &backupHistories)
biz.ErrIsNilAppendErr(err, "获取数据库备份历史失败: %v")
historyIds := make([]uint64, 0, len(backupHistories))
for _, history := range backupHistories {
historyIds = append(historyIds, history.Id)
}
restores := make([]*entity.DbRestore, 0, page.PageSize)
if err := d.restoreApp.GetRestoresEnabled(&restores, historyIds...); err != nil {
biz.ErrIsNilAppendErr(err, "获取数据库备份恢复记录失败")
}
for _, history := range backupHistories {
for _, restore := range restores {
if restore.DbBackupHistoryId == history.Id {
history.LastStatus = restore.LastStatus
history.LastResult = restore.LastResult
history.LastTime = restore.LastTime
break
}
}
}
rc.ResData = res
}
// RestoreHistories 从数据库备份历史中恢复数据库
// @router /api/dbs/:dbId/backup-histories/:backupHistoryId/restore [POST]
func (d *DbBackup) RestoreHistories(rc *req.Ctx) {
pm := rc.PathParam("backupHistoryId")
biz.NotEmpty(pm, "backupHistoryId 为空")
idsStr := strings.Fields(pm)
ids := make([]uint64, 0, len(idsStr))
for _, s := range idsStr {
id, err := strconv.ParseUint(s, 10, 64)
biz.ErrIsNilAppendErr(err, "从数据库备份历史恢复数据库失败: %v")
ids = append(ids, id)
}
histories := make([]*entity.DbBackupHistory, 0, len(ids))
err := d.backupApp.GetHistories(ids, &histories)
biz.ErrIsNilAppendErr(err, "添加数据库恢复任务失败: %v")
restores := make([]*entity.DbRestore, 0, len(histories))
now := time.Now()
for _, history := range histories {
job := &entity.DbRestore{
DbInstanceId: history.DbInstanceId,
DbName: history.DbName,
Enabled: true,
Repeated: false,
StartTime: now,
Interval: 0,
PointInTime: timex.NewNullTime(time.Time{}),
DbBackupId: history.DbBackupId,
DbBackupHistoryId: history.Id,
DbBackupHistoryName: history.Name,
}
restores = append(restores, job)
}
biz.ErrIsNilAppendErr(d.restoreApp.Create(rc.MetaCtx, restores), "添加数据库恢复任务失败: %v")
}
// DeleteHistories 删除数据库备份历史
// @router /api/dbs/:dbId/backup-histories/:backupHistoryId [DELETE]
func (d *DbBackup) DeleteHistories(rc *req.Ctx) {
err := d.walk(rc, "backupHistoryId", d.backupApp.DeleteHistory)
biz.ErrIsNilAppendErr(err, "删除数据库备份历史失败: %v")
}

View File

@@ -8,6 +8,7 @@ import (
"mayfly-go/internal/db/imsg"
"mayfly-go/internal/pkg/utils"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/model"
"mayfly-go/pkg/req"
"mayfly-go/pkg/utils/stringx"
"strings"
@@ -49,17 +50,17 @@ func (d *DataSyncTask) ReqConfs() *req.Confs {
}
func (d *DataSyncTask) Tasks(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage[*entity.DataSyncTaskQuery](rc, new(entity.DataSyncTaskQuery))
res, err := d.dataSyncTaskApp.GetPageList(queryCond, page, new([]vo.DataSyncTaskListVO))
queryCond := req.BindQuery[*entity.DataSyncTaskQuery](rc, new(entity.DataSyncTaskQuery))
res, err := d.dataSyncTaskApp.GetPageList(queryCond)
biz.ErrIsNil(err)
rc.ResData = res
rc.ResData = model.PageResultConv[*entity.DataSyncTask, *vo.DataSyncLogListVO](res)
}
func (d *DataSyncTask) Logs(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage[*entity.DataSyncLogQuery](rc, new(entity.DataSyncLogQuery))
res, err := d.dataSyncTaskApp.GetTaskLogList(queryCond, page, new([]vo.DataSyncLogListVO))
queryCond := req.BindQuery(rc, new(entity.DataSyncLogQuery))
res, err := d.dataSyncTaskApp.GetTaskLogList(queryCond)
biz.ErrIsNil(err)
rc.ResData = res
rc.ResData = model.PageResultConv[*entity.DataSyncLog, *vo.DataSyncLogListVO](res)
}
func (d *DataSyncTask) SaveTask(rc *req.Ctx) {

View File

@@ -55,7 +55,7 @@ func (d *Instance) ReqConfs() *req.Confs {
// Instances 获取数据库实例信息
// @router /api/instances [get]
func (d *Instance) Instances(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage[*entity.InstanceQuery](rc, new(entity.InstanceQuery))
queryCond := req.BindQuery(rc, new(entity.InstanceQuery))
tags := d.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
TypePaths: collx.AsArray(tagentity.NewTypePaths(tagentity.TagTypeDbInstance, tagentity.TagTypeAuthCert)),
@@ -63,7 +63,7 @@ func (d *Instance) Instances(rc *req.Ctx) {
})
// 不存在可操作的数据库,即没有可操作数据
if len(tags) == 0 {
rc.ResData = model.EmptyPageResult[any]()
rc.ResData = model.NewEmptyPageResult[any]()
return
}
@@ -71,9 +71,10 @@ func (d *Instance) Instances(rc *req.Ctx) {
dbInstCodes := tagentity.GetCodesByCodePaths(tagentity.TagTypeDbInstance, tagCodePaths...)
queryCond.Codes = dbInstCodes
var instvos []*vo.InstanceListVO
res, err := d.instanceApp.GetPageList(queryCond, page, &instvos)
res, err := d.instanceApp.GetPageList(queryCond)
biz.ErrIsNil(err)
resVo := model.PageResultConv[*entity.DbInstance, *vo.InstanceListVO](res)
instvos := resVo.List
// 填充授权凭证信息
d.resourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodesByCodePaths(tagentity.TagTypeAuthCert, tagCodePaths...), collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert {
@@ -85,7 +86,7 @@ func (d *Instance) Instances(rc *req.Ctx) {
return insvo
})...)
rc.ResData = res
rc.ResData = resVo
}
func (d *Instance) TestConn(rc *req.Ctx) {

View File

@@ -1,143 +0,0 @@
package api
import (
"context"
"mayfly-go/internal/db/api/form"
"mayfly-go/internal/db/api/vo"
"mayfly-go/internal/db/application"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/req"
"strconv"
"strings"
)
type DbRestore struct {
restoreApp *application.DbRestoreApp `inject:"DbRestoreApp"`
dbApp application.Db `inject:"DbApp"`
}
// GetPageList 获取数据库恢复任务
// @router /api/dbs/:dbId/restores [GET]
func (d *DbRestore) GetPageList(rc *req.Ctx) {
dbId := uint64(rc.PathParamInt("dbId"))
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
db, err := d.dbApp.GetById(dbId, "db_instance_id", "database")
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
var restores []vo.DbRestore
queryCond, page := req.BindQueryAndPage[*entity.DbRestoreQuery](rc, new(entity.DbRestoreQuery))
queryCond.DbInstanceId = db.InstanceId
queryCond.InDbNames = strings.Fields(db.Database)
res, err := d.restoreApp.GetPageList(queryCond, page, &restores)
biz.ErrIsNilAppendErr(err, "获取数据库恢复任务失败: %v")
rc.ResData = res
}
// Create 保存数据库恢复任务
// @router /api/dbs/:dbId/restores [POST]
func (d *DbRestore) Create(rc *req.Ctx) {
restoreForm := &form.DbRestoreForm{}
req.BindJsonAndValid(rc, restoreForm)
rc.ReqParam = restoreForm
dbId := uint64(rc.PathParamInt("dbId"))
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
db, err := d.dbApp.GetById(dbId, "instanceId")
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
job := &entity.DbRestore{
DbInstanceId: db.InstanceId,
DbName: restoreForm.DbName,
Enabled: true,
Repeated: restoreForm.Repeated,
StartTime: restoreForm.StartTime,
Interval: restoreForm.Interval,
PointInTime: restoreForm.PointInTime,
DbBackupId: restoreForm.DbBackupId,
DbBackupHistoryId: restoreForm.DbBackupHistoryId,
DbBackupHistoryName: restoreForm.DbBackupHistoryName,
}
biz.ErrIsNilAppendErr(d.restoreApp.Create(rc.MetaCtx, job), "添加数据库恢复任务失败: %v")
}
func (d *DbRestore) createWithBackupHistory(backupHistoryIds string) {
}
// Update 保存数据库恢复任务
// @router /api/dbs/:dbId/restores/:restoreId [PUT]
func (d *DbRestore) Update(rc *req.Ctx) {
restoreForm := &form.DbRestoreForm{}
req.BindJsonAndValid(rc, restoreForm)
rc.ReqParam = restoreForm
job := &entity.DbRestore{}
job.Id = restoreForm.Id
job.StartTime = restoreForm.StartTime
job.Interval = restoreForm.Interval
biz.ErrIsNilAppendErr(d.restoreApp.Update(rc.MetaCtx, job), "保存数据库恢复任务失败: %v")
}
func (d *DbRestore) walk(rc *req.Ctx, fn func(ctx context.Context, restoreId uint64) error) error {
idsStr := rc.PathParam("restoreId")
biz.NotEmpty(idsStr, "restoreId 为空")
rc.ReqParam = idsStr
ids := strings.Fields(idsStr)
for _, v := range ids {
value, err := strconv.Atoi(v)
if err != nil {
return err
}
restoreId := uint64(value)
err = fn(rc.MetaCtx, restoreId)
if err != nil {
return err
}
}
return nil
}
// Delete 删除数据库恢复任务
// @router /api/dbs/:dbId/restores/:restoreId [DELETE]
func (d *DbRestore) Delete(rc *req.Ctx) {
err := d.walk(rc, d.restoreApp.Delete)
biz.ErrIsNilAppendErr(err, "删除数据库恢复任务失败: %v")
}
// Enable 启用数据库恢复任务
// @router /api/dbs/:dbId/restores/:restoreId/enable [PUT]
func (d *DbRestore) Enable(rc *req.Ctx) {
err := d.walk(rc, d.restoreApp.Enable)
biz.ErrIsNilAppendErr(err, "启用数据库恢复任务失败: %v")
}
// Disable 禁用数据库恢复任务
// @router /api/dbs/:dbId/restores/:restoreId/disable [PUT]
func (d *DbRestore) Disable(rc *req.Ctx) {
err := d.walk(rc, d.restoreApp.Disable)
biz.ErrIsNilAppendErr(err, "禁用数据库恢复任务失败: %v")
}
// GetDbNamesWithoutRestore 获取未配置定时恢复的数据库名称
// @router /api/dbs/:dbId/db-names-without-backup [GET]
func (d *DbRestore) GetDbNamesWithoutRestore(rc *req.Ctx) {
dbId := uint64(rc.PathParamInt("dbId"))
db, err := d.dbApp.GetById(dbId, "instance_id", "database")
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
dbNames := strings.Fields(db.Database)
dbNamesWithoutRestore, err := d.restoreApp.GetDbNamesWithoutRestore(db.InstanceId, dbNames)
biz.ErrIsNilAppendErr(err, "获取未配置定时备份的数据库名称失败: %v")
rc.ResData = dbNamesWithoutRestore
}
// GetHistoryPageList 获取数据库备份历史
// @router /api/dbs/:dbId/restores/:restoreId/histories [GET]
func (d *DbRestore) GetHistoryPageList(rc *req.Ctx) {
queryCond := &entity.DbRestoreHistoryQuery{
DbRestoreId: uint64(rc.PathParamInt("restoreId")),
}
res, err := d.restoreApp.GetHistoryPageList(queryCond, rc.GetPageParam(), new([]vo.DbRestoreHistory))
biz.ErrIsNilAppendErr(err, "获取数据库备份历史失败: %v")
rc.ResData = res
}

View File

@@ -26,14 +26,13 @@ func (d *DbSqlExec) ReqConfs() *req.Confs {
}
func (d *DbSqlExec) DbSqlExecs(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage(rc, new(entity.DbSqlExecQuery))
queryCond := req.BindQuery(rc, new(entity.DbSqlExecQuery))
if statusStr := rc.Query("status"); statusStr != "" {
queryCond.Status = collx.ArrayMap[string, int8](strings.Split(statusStr, ","), func(val string) int8 {
return cast.ToInt8(val)
})
}
res, err := d.dbSqlExecApp.GetPageList(queryCond, page, new([]entity.DbSqlExec))
res, err := d.dbSqlExecApp.GetPageList(queryCond)
biz.ErrIsNil(err)
rc.ResData = res
}

View File

@@ -11,6 +11,7 @@ import (
fileapp "mayfly-go/internal/file/application"
tagapp "mayfly-go/internal/tag/application"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/model"
"mayfly-go/pkg/req"
"mayfly-go/pkg/utils/collx"
"strings"
@@ -60,21 +61,20 @@ func (d *DbTransferTask) ReqConfs() *req.Confs {
}
func (d *DbTransferTask) Tasks(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage[*entity.DbTransferTaskQuery](rc, new(entity.DbTransferTaskQuery))
res, err := d.dbTransferTask.GetPageList(queryCond, page, new([]vo.DbTransferTaskListVO))
biz.ErrIsNil(err)
queryCond := req.BindQuery(rc, new(entity.DbTransferTaskQuery))
if res.List != nil {
list := res.List.(*[]vo.DbTransferTaskListVO)
for _, item := range *list {
item.RunningState = entity.DbTransferTaskRunStateSuccess
if d.dbTransferTask.IsRunning(item.Id) {
item.RunningState = entity.DbTransferTaskRunStateRunning
}
res, err := d.dbTransferTask.GetPageList(queryCond)
biz.ErrIsNil(err)
resVo := model.PageResultConv[*entity.DbTransferTask, *vo.DbTransferTaskListVO](res)
for _, item := range resVo.List {
item.RunningState = entity.DbTransferTaskRunStateSuccess
if d.dbTransferTask.IsRunning(item.Id) {
item.RunningState = entity.DbTransferTaskRunStateRunning
}
}
rc.ResData = res
rc.ResData = resVo
}
func (d *DbTransferTask) SaveTask(rc *req.Ctx) {
@@ -122,8 +122,9 @@ func (d *DbTransferTask) Stop(rc *req.Ctx) {
}
func (d *DbTransferTask) Files(rc *req.Ctx) {
queryCond, page := req.BindQueryAndPage[*entity.DbTransferFileQuery](rc, new(entity.DbTransferFileQuery))
res, err := d.dbTransferFile.GetPageList(queryCond, page, new([]vo.DbTransferFileListVO))
queryCond := req.BindQuery(rc, new(entity.DbTransferFileQuery))
res, err := d.dbTransferFile.GetPageList(queryCond)
biz.ErrIsNil(err)
rc.ResData = res
}

View File

@@ -1,52 +0,0 @@
package vo
import (
"encoding/json"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/utils/timex"
"time"
)
// DbBackup 数据库备份任务
type DbBackup struct {
Id uint64 `json:"id"`
DbName string `json:"dbName"` // 数据库名
CreateTime time.Time `json:"createTime"` // 创建时间
StartTime time.Time `json:"startTime"` // 开始时间
Interval time.Duration `json:"-"` // 间隔时间
IntervalDay uint64 `json:"intervalDay" gorm:"-"` // 间隔天数
MaxSaveDays int `json:"maxSaveDays"` // 数据库备份历史保留天数,过期将自动删除
Enabled bool `json:"enabled"` // 是否启用
EnabledDesc string `json:"enabledDesc"` // 启用状态描述
LastTime timex.NullTime `json:"lastTime"` // 最近一次执行时间
LastStatus entity.DbJobStatus `json:"lastStatus"` // 最近一次执行状态
LastResult string `json:"lastResult"` // 最近一次执行结果
DbInstanceId uint64 `json:"dbInstanceId"` // 数据库实例ID
Name string `json:"name"` // 备份任务名称
}
func (backup *DbBackup) MarshalJSON() ([]byte, error) {
type dbBackup DbBackup
backup.IntervalDay = uint64(backup.Interval / time.Hour / 24)
if len(backup.EnabledDesc) == 0 {
if backup.Enabled {
backup.EnabledDesc = "已启用"
} else {
backup.EnabledDesc = "已禁用"
}
}
return json.Marshal((*dbBackup)(backup))
}
// DbBackupHistory 数据库备份历史
type DbBackupHistory struct {
Id uint64 `json:"id"`
DbBackupId uint64 `json:"dbBackupId"`
CreateTime time.Time `json:"createTime"`
DbName string `json:"dbName"` // 数据库名称
Name string `json:"name"` // 备份历史名称
BinlogFileName string `json:"binlogFileName"`
LastTime timex.NullTime `json:"lastTime" gorm:"-"` // 最近一次恢复时间
LastStatus entity.DbJobStatus `json:"lastStatus" gorm:"-"` // 最近一次恢复状态
LastResult string `json:"lastResult" gorm:"-"` // 最近一次恢复结果
}

View File

@@ -1,45 +0,0 @@
package vo
import (
"encoding/json"
"mayfly-go/pkg/utils/timex"
"time"
)
// DbRestore 数据库备份任务
type DbRestore struct {
Id uint64 `json:"id"`
DbName string `json:"dbName"` // 数据库名
StartTime time.Time `json:"startTime"` // 开始时间
Interval time.Duration `json:"-"` // 间隔时间
IntervalDay uint64 `json:"intervalDay" gorm:"-"` // 间隔天数
Enabled bool `json:"enabled"` // 是否启用
EnabledDesc string `json:"enabledDesc"` // 启用状态描述
LastTime timex.NullTime `json:"lastTime"` // 最近一次执行时间
LastStatus string `json:"lastStatus"` // 最近一次执行状态
LastResult string `json:"lastResult"` // 最近一次执行结果
PointInTime timex.NullTime `json:"pointInTime"` // 指定数据库恢复的时间点
DbBackupId uint64 `json:"dbBackupId"` // 数据库备份任务ID
DbBackupHistoryId uint64 `json:"dbBackupHistoryId"` // 数据库备份历史ID
DbBackupHistoryName string `json:"dbBackupHistoryName"` // 数据库备份历史名称
DbInstanceId uint64 `json:"dbInstanceId"` // 数据库实例ID
}
func (restore *DbRestore) MarshalJSON() ([]byte, error) {
type dbBackup DbRestore
restore.IntervalDay = uint64(restore.Interval / time.Hour / 24)
if len(restore.EnabledDesc) == 0 {
if restore.Enabled {
restore.EnabledDesc = "已启用"
} else {
restore.EnabledDesc = "已禁用"
}
}
return json.Marshal((*dbBackup)(restore))
}
// DbRestoreHistory 数据库备份历史
type DbRestoreHistory struct {
Id uint64 `json:"id"`
DbRestoreId uint64 `json:"dbRestoreId"`
}

View File

@@ -13,11 +13,6 @@ func InitIoc() {
ioc.Register(new(dataSyncAppImpl), ioc.WithComponentName("DbDataSyncTaskApp"))
ioc.Register(new(dbTransferAppImpl), ioc.WithComponentName("DbTransferTaskApp"))
ioc.Register(new(dbTransferFileAppImpl), ioc.WithComponentName("DbTransferFileApp"))
ioc.Register(newDbScheduler(), ioc.WithComponentName("DbScheduler"))
ioc.Register(new(DbBackupApp), ioc.WithComponentName("DbBackupApp"))
ioc.Register(new(DbRestoreApp), ioc.WithComponentName("DbRestoreApp"))
ioc.Register(newDbBinlogApp(), ioc.WithComponentName("DbBinlogApp"))
}
func Init() {
@@ -43,18 +38,6 @@ func GetDbSqlExecApp() DbSqlExec {
return ioc.Get[DbSqlExec]("DbSqlExecApp")
}
func GetDbBackupApp() *DbBackupApp {
return ioc.Get[*DbBackupApp]("DbBackupApp")
}
func GetDbRestoreApp() *DbRestoreApp {
return ioc.Get[*DbRestoreApp]("DbRestoreApp")
}
func GetDbBinlogApp() *DbBinlogApp {
return ioc.Get[*DbBinlogApp]("DbBinlogApp")
}
func GetDataSyncTaskApp() DataSyncTask {
return ioc.Get[DataSyncTask]("DbDataSyncTaskApp")
}

View File

@@ -29,7 +29,7 @@ type Db interface {
base.App[*entity.Db]
// 分页获取
GetPageList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DbQuery, orderBy ...string) (*model.PageResult[*entity.DbListPO], error)
SaveDb(ctx context.Context, entity *entity.Db) error
@@ -62,8 +62,8 @@ type dbAppImpl struct {
var _ (Db) = (*dbAppImpl)(nil)
// 分页获取数据库信息列表
func (d *dbAppImpl) GetPageList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return d.GetRepo().GetDbList(condition, pageParam, toEntity, orderBy...)
func (d *dbAppImpl) GetPageList(condition *entity.DbQuery, orderBy ...string) (*model.PageResult[*entity.DbListPO], error) {
return d.GetRepo().GetDbList(condition, orderBy...)
}
func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {

View File

@@ -1,283 +0,0 @@
package application
import (
"context"
"encoding/binary"
"errors"
"fmt"
"math"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils/timex"
"sync"
"time"
"gorm.io/gorm"
"github.com/google/uuid"
)
const maxBackupHistoryDays = 30
var (
errRestoringBackupHistory = errors.New("正在从备份历史中恢复数据库")
)
type DbBackupApp struct {
scheduler *dbScheduler `inject:"DbScheduler"`
backupRepo repository.DbBackup `inject:"DbBackupRepo"`
backupHistoryRepo repository.DbBackupHistory `inject:"DbBackupHistoryRepo"`
restoreRepo repository.DbRestore `inject:"DbRestoreRepo"`
dbApp Db `inject:"DbApp"`
mutex sync.Mutex
closed chan struct{}
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
}
func (app *DbBackupApp) Init() error {
var jobs []*entity.DbBackup
if err := app.backupRepo.ListToDo(&jobs); err != nil {
return err
}
if err := app.scheduler.AddJob(context.Background(), jobs); err != nil {
return err
}
app.ctx, app.cancel = context.WithCancel(context.Background())
app.wg.Add(1)
go func() {
defer app.wg.Done()
for app.ctx.Err() == nil {
if err := app.prune(app.ctx); err != nil {
logx.Errorf("清理数据库备份历史失败: %s", err.Error())
timex.SleepWithContext(app.ctx, time.Minute*15)
continue
}
timex.SleepWithContext(app.ctx, time.Hour*24)
}
}()
return nil
}
func (app *DbBackupApp) prune(ctx context.Context) error {
jobs, err := app.backupRepo.SelectByCond(map[string]any{})
if err != nil {
return err
}
for _, job := range jobs {
if ctx.Err() != nil {
return nil
}
historyCond := map[string]any{
"db_backup_id": job.Id,
}
histories, _ := app.backupHistoryRepo.SelectByCond(historyCond)
expiringTime := time.Now().Add(-math.MaxInt64)
if job.MaxSaveDays > 0 {
expiringTime = time.Now().Add(-time.Hour * 24 * time.Duration(job.MaxSaveDays+1))
}
for _, history := range histories {
if ctx.Err() != nil {
return nil
}
if history.CreateTime.After(expiringTime) {
break
}
err := app.DeleteHistory(ctx, history.Id)
if errors.Is(err, errRestoringBackupHistory) {
break
}
if err != nil {
return err
}
}
}
return nil
}
func (app *DbBackupApp) Close() {
app.scheduler.Close()
if app.cancel != nil {
app.cancel()
app.cancel = nil
}
app.wg.Wait()
}
func (app *DbBackupApp) Create(ctx context.Context, jobs []*entity.DbBackup) error {
app.mutex.Lock()
defer app.mutex.Unlock()
if err := app.backupRepo.AddJob(ctx, jobs); err != nil {
return err
}
return app.scheduler.AddJob(ctx, jobs)
}
func (app *DbBackupApp) Update(ctx context.Context, job *entity.DbBackup) error {
app.mutex.Lock()
defer app.mutex.Unlock()
if err := app.backupRepo.UpdateById(ctx, job); err != nil {
return err
}
_ = app.scheduler.UpdateJob(ctx, job)
return nil
}
func (app *DbBackupApp) Delete(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
if err := app.scheduler.RemoveJob(ctx, entity.DbJobTypeBackup, jobId); err != nil {
return err
}
history := &entity.DbBackupHistory{
DbBackupId: jobId,
}
err := app.backupHistoryRepo.GetByCond(history)
switch {
default:
return err
case err == nil:
return fmt.Errorf("请先删除关联的数据库备份历史【%s】", history.Name)
case errors.Is(err, gorm.ErrRecordNotFound):
}
if err := app.backupRepo.DeleteById(ctx, jobId); err != nil {
return err
}
return nil
}
func (app *DbBackupApp) Enable(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
repo := app.backupRepo
job, err := repo.GetById(jobId)
if err != nil {
return err
}
if job.IsEnabled() {
return nil
}
if job.IsExpired() {
return errors.New("任务已过期")
}
_ = app.scheduler.EnableJob(ctx, job)
if err := repo.UpdateEnabled(ctx, jobId, true); err != nil {
logx.Errorf("数据库备份任务已启用( jobId: %d ),任务状态保存失败: %v", jobId, err)
return err
}
return nil
}
func (app *DbBackupApp) Disable(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
repo := app.backupRepo
job, err := repo.GetById(jobId)
if err != nil {
return err
}
if !job.IsEnabled() {
return nil
}
_ = app.scheduler.DisableJob(ctx, entity.DbJobTypeBackup, jobId)
if err := repo.UpdateEnabled(ctx, jobId, false); err != nil {
logx.Errorf("数据库恢复任务已禁用( jobId: %d ),任务状态保存失败: %v", jobId, err)
return err
}
return nil
}
func (app *DbBackupApp) StartNow(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
job, err := app.backupRepo.GetById(jobId)
if err != nil {
return err
}
if !job.IsEnabled() {
return errors.New("任务未启用")
}
_ = app.scheduler.StartJobNow(ctx, job)
return nil
}
// GetPageList 分页获取数据库备份任务
func (app *DbBackupApp) GetPageList(condition *entity.DbBackupQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.backupRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
}
// GetDbNamesWithoutBackup 获取未配置定时备份的数据库名称
func (app *DbBackupApp) GetDbNamesWithoutBackup(instanceId uint64, dbNames []string) ([]string, error) {
return app.backupRepo.GetDbNamesWithoutBackup(instanceId, dbNames)
}
// GetHistoryPageList 分页获取数据库备份历史
func (app *DbBackupApp) GetHistoryPageList(condition *entity.DbBackupHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.backupHistoryRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
}
func (app *DbBackupApp) GetHistories(backupHistoryIds []uint64, toEntity any) error {
return app.backupHistoryRepo.GetHistories(backupHistoryIds, toEntity)
}
func NewIncUUID() (uuid.UUID, error) {
var uid uuid.UUID
now, seq, err := uuid.GetTime()
if err != nil {
return uid, err
}
timeHi := uint32((now >> 28) & 0xffffffff)
timeMid := uint16((now >> 12) & 0xffff)
timeLow := uint16(now & 0x0fff)
timeLow |= 0x1000 // Version 1
binary.BigEndian.PutUint32(uid[0:], timeHi)
binary.BigEndian.PutUint16(uid[4:], timeMid)
binary.BigEndian.PutUint16(uid[6:], timeLow)
binary.BigEndian.PutUint16(uid[8:], seq)
copy(uid[10:], uuid.NodeID())
return uid, nil
}
func (app *DbBackupApp) DeleteHistory(ctx context.Context, historyId uint64) (retErr error) {
app.mutex.Lock()
defer app.mutex.Unlock()
if _, err := app.backupHistoryRepo.UpdateDeleting(false, historyId); err != nil {
return err
}
ok, err := app.backupHistoryRepo.UpdateDeleting(true, historyId)
if err != nil {
return err
}
if !ok {
return errRestoringBackupHistory
}
job, err := app.backupHistoryRepo.GetById(historyId)
if err != nil {
return err
}
conn, err := app.dbApp.GetDbConnByInstanceId(job.DbInstanceId)
if err != nil {
return err
}
dbProgram, err := conn.GetDialect().GetDbProgram()
if err != nil {
return err
}
if err := dbProgram.RemoveBackupHistory(ctx, job.DbBackupId, job.Uuid); err != nil {
return err
}
return app.backupHistoryRepo.DeleteById(ctx, historyId)
}

View File

@@ -1,170 +0,0 @@
package application
import (
"context"
"math"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/timex"
"sync"
"time"
)
type DbBinlogApp struct {
scheduler *dbScheduler `inject:"DbScheduler"`
binlogRepo repository.DbBinlog `inject:"DbBinlogRepo"`
binlogHistoryRepo repository.DbBinlogHistory `inject:"DbBinlogHistoryRepo"`
backupRepo repository.DbBackup `inject:"DbBackupRepo"`
backupHistoryRepo repository.DbBackupHistory `inject:"DbBackupHistoryRepo"`
instanceRepo repository.Instance `inject:"DbInstanceRepo"`
dbApp Db `inject:"DbApp"`
context context.Context
cancel context.CancelFunc
waitGroup sync.WaitGroup
}
func newDbBinlogApp() *DbBinlogApp {
ctx, cancel := context.WithCancel(context.Background())
svc := &DbBinlogApp{
context: ctx,
cancel: cancel,
}
return svc
}
func (app *DbBinlogApp) Init() error {
app.context, app.cancel = context.WithCancel(context.Background())
app.waitGroup.Add(1)
go app.run()
return nil
}
func (app *DbBinlogApp) run() {
defer app.waitGroup.Done()
for app.context.Err() == nil {
if err := app.fetchBinlog(app.context); err != nil {
timex.SleepWithContext(app.context, time.Minute)
continue
}
if err := app.pruneBinlog(app.context); err != nil {
timex.SleepWithContext(app.context, time.Minute)
continue
}
timex.SleepWithContext(app.context, entity.BinlogDownloadInterval)
}
}
func (app *DbBinlogApp) fetchBinlog(ctx context.Context) error {
jobs, err := app.loadJobs(ctx)
if err != nil {
logx.Errorf("DbBinlogApp: 加载 BINLOG 同步任务失败: %s", err.Error())
timex.SleepWithContext(app.context, time.Minute)
return err
}
if ctx.Err() != nil {
return ctx.Err()
}
if err := app.scheduler.AddJob(app.context, jobs); err != nil {
logx.Error("DbBinlogApp: 添加 BINLOG 同步任务失败: ", err.Error())
return err
}
return nil
}
func (app *DbBinlogApp) pruneBinlog(ctx context.Context) error {
jobs, err := app.binlogRepo.SelectByCond(map[string]any{})
if err != nil {
logx.Error("DbBinlogApp: 获取 BINLOG 同步任务失败: ", err.Error())
return err
}
for _, instance := range jobs {
if ctx.Err() != nil {
return ctx.Err()
}
var histories []*entity.DbBinlogHistory
backupHistory, backupHistoryExists, err := app.backupHistoryRepo.GetEarliestHistoryForBinlog(instance.Id)
if err != nil {
logx.Errorf("DbBinlogApp: 获取数据库备份历史失败: %s", err.Error())
return err
}
var binlogSeq int64 = math.MaxInt64
if backupHistoryExists {
binlogSeq = backupHistory.BinlogSequence
}
if err := app.binlogHistoryRepo.GetHistoriesBeforeSequence(ctx, instance.Id, binlogSeq, &histories); err != nil {
logx.Errorf("DbBinlogApp: 获取数据库 BINLOG 历史失败: %s", err.Error())
return err
}
conn, err := app.dbApp.GetDbConnByInstanceId(instance.Id)
if err != nil {
logx.Errorf("DbBinlogApp: 创建数据库连接失败: %s", err.Error())
return err
}
dbProgram, err := conn.GetDialect().GetDbProgram()
if err != nil {
logx.Errorf("DbBinlogApp: 获取数据库备份与恢复程序失败: %s", err.Error())
return err
}
for i, history := range histories {
// todo: 在避免并发访问的前提下删除本地最新的 BINLOG 文件
if !backupHistoryExists && i == len(histories)-1 {
// 暂不删除本地最新的 BINLOG 文件
break
}
if ctx.Err() != nil {
return ctx.Err()
}
if err := dbProgram.PruneBinlog(history); err != nil {
logx.Errorf("清理 BINLOG 文件失败: %v", err)
continue
}
if err := app.binlogHistoryRepo.DeleteById(ctx, history.Id); err != nil {
logx.Errorf("删除 BINLOG 历史失败: %v", err)
continue
}
}
}
return nil
}
func (app *DbBinlogApp) loadJobs(ctx context.Context) ([]*entity.DbBinlog, error) {
var instanceIds []uint64
if err := app.backupRepo.ListDbInstances(true, true, &instanceIds); err != nil {
return nil, err
}
jobs := make([]*entity.DbBinlog, 0, len(instanceIds))
for _, id := range instanceIds {
if ctx.Err() != nil {
break
}
binlog := entity.NewDbBinlog(id)
if err := app.AddJobIfNotExists(app.context, binlog); err != nil {
return nil, err
}
jobs = append(jobs, binlog)
}
return jobs, nil
}
func (app *DbBinlogApp) Close() {
cancel := app.cancel
if cancel == nil {
return
}
app.cancel = nil
cancel()
app.waitGroup.Wait()
}
func (app *DbBinlogApp) AddJobIfNotExists(ctx context.Context, job *entity.DbBinlog) error {
if err := app.binlogRepo.AddJobIfNotExists(ctx, job); err != nil {
return err
}
if job.Id == 0 {
return nil
}
return nil
}

View File

@@ -28,7 +28,7 @@ type DataSyncTask interface {
base.App[*entity.DataSyncTask]
// GetPageList 分页获取数据库实例
GetPageList(condition *entity.DataSyncTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DataSyncTaskQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncTask], error)
Save(ctx context.Context, instanceEntity *entity.DataSyncTask) error
@@ -44,7 +44,7 @@ type DataSyncTask interface {
StopTask(ctx context.Context, id uint64) error
GetTaskLogList(condition *entity.DataSyncLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetTaskLogList(condition *entity.DataSyncLogQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncLog], error)
}
var _ (DataSyncTask) = (*dataSyncAppImpl)(nil)
@@ -65,8 +65,8 @@ func (app *dataSyncAppImpl) InjectDbDataSyncTaskRepo(repo repository.DataSyncTas
app.Repo = repo
}
func (app *dataSyncAppImpl) GetPageList(condition *entity.DataSyncTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.GetRepo().GetTaskList(condition, pageParam, toEntity, orderBy...)
func (app *dataSyncAppImpl) GetPageList(condition *entity.DataSyncTaskQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncTask], error) {
return app.GetRepo().GetTaskList(condition, orderBy...)
}
func (app *dataSyncAppImpl) Save(ctx context.Context, taskEntity *entity.DataSyncTask) error {
@@ -406,39 +406,36 @@ func (app *dataSyncAppImpl) InitCronJob() {
_ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning})
// 把所有正常任务添加到定时任务中
pageParam := &model.PageParam{
PageSize: 100,
PageNum: 1,
}
cond := new(entity.DataSyncTaskQuery)
cond.PageNum = 1
cond.PageSize = 100
cond.Status = entity.DataSyncTaskStatusEnable
jobs := new([]entity.DataSyncTask)
pr, err := app.GetPageList(cond, pageParam, jobs)
tasks, err := app.GetPageList(cond)
if err != nil {
logx.ErrorTrace("the data synchronization task failed to initialize", err)
return
}
total := pr.Total
total := tasks.Total
add := 0
for {
for _, job := range *jobs {
app.AddCronJob(contextx.NewTraceId(), &job)
for _, job := range tasks.List {
app.AddCronJob(contextx.NewTraceId(), job)
add++
}
if add >= int(total) {
return
}
pageParam.PageNum++
_, _ = app.GetPageList(cond, pageParam, jobs)
cond.PageNum = cond.PageNum + 1
tasks, _ = app.GetPageList(cond)
}
}
func (app *dataSyncAppImpl) GetTaskLogList(condition *entity.DataSyncLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.dbDataSyncLogRepo.GetTaskLogList(condition, pageParam, toEntity, orderBy...)
func (app *dataSyncAppImpl) GetTaskLogList(condition *entity.DataSyncLogQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncLog], error) {
return app.dbDataSyncLogRepo.GetTaskLogList(condition, orderBy...)
}
// MarkRunning 标记任务执行中

View File

@@ -25,7 +25,7 @@ type Instance interface {
base.App[*entity.DbInstance]
// GetPageList 分页获取数据库实例
GetPageList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.InstanceQuery, orderBy ...string) (*model.PageResult[*entity.DbInstance], error)
TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error
@@ -55,8 +55,8 @@ type instanceAppImpl struct {
var _ (Instance) = (*instanceAppImpl)(nil)
// GetPageList 分页获取数据库实例
func (app *instanceAppImpl) GetPageList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.GetRepo().GetInstanceList(condition, pageParam, toEntity, orderBy...)
func (app *instanceAppImpl) GetPageList(condition *entity.InstanceQuery, orderBy ...string) (*model.PageResult[*entity.DbInstance], error) {
return app.GetRepo().GetInstanceList(condition, orderBy...)
}
func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error {

View File

@@ -1,137 +0,0 @@
package application
import (
"context"
"errors"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/model"
"sync"
)
type DbRestoreApp struct {
scheduler *dbScheduler `inject:"DbScheduler"`
restoreRepo repository.DbRestore `inject:"DbRestoreRepo"`
restoreHistoryRepo repository.DbRestoreHistory `inject:"DbRestoreHistoryRepo"`
mutex sync.Mutex
}
func (app *DbRestoreApp) Init() error {
var jobs []*entity.DbRestore
if err := app.restoreRepo.ListToDo(&jobs); err != nil {
return err
}
if err := app.scheduler.AddJob(context.Background(), jobs); err != nil {
return err
}
return nil
}
func (app *DbRestoreApp) Close() {
app.scheduler.Close()
}
func (app *DbRestoreApp) Create(ctx context.Context, jobs any) error {
app.mutex.Lock()
defer app.mutex.Unlock()
if err := app.restoreRepo.AddJob(ctx, jobs); err != nil {
return err
}
_ = app.scheduler.AddJob(ctx, jobs)
return nil
}
func (app *DbRestoreApp) Update(ctx context.Context, job *entity.DbRestore) error {
app.mutex.Lock()
defer app.mutex.Unlock()
if err := app.restoreRepo.UpdateById(ctx, job); err != nil {
return err
}
_ = app.scheduler.UpdateJob(ctx, job)
return nil
}
func (app *DbRestoreApp) Delete(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
if err := app.scheduler.RemoveJob(ctx, entity.DbJobTypeRestore, jobId); err != nil {
return err
}
history := &entity.DbRestoreHistory{
DbRestoreId: jobId,
}
if err := app.restoreHistoryRepo.DeleteByCond(ctx, history); err != nil {
return err
}
if err := app.restoreRepo.DeleteById(ctx, jobId); err != nil {
return err
}
return nil
}
func (app *DbRestoreApp) Enable(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
repo := app.restoreRepo
job, err := repo.GetById(jobId)
if err != nil {
return err
}
if job.IsEnabled() {
return nil
}
if job.IsExpired() {
return errors.New("任务已过期")
}
_ = app.scheduler.EnableJob(ctx, job)
if err := repo.UpdateEnabled(ctx, jobId, true); err != nil {
logx.Errorf("数据库恢复任务已启用( jobId: %d ),任务状态保存失败: %v", jobId, err)
return err
}
return nil
}
func (app *DbRestoreApp) Disable(ctx context.Context, jobId uint64) error {
app.mutex.Lock()
defer app.mutex.Unlock()
repo := app.restoreRepo
job, err := repo.GetById(jobId)
if err != nil {
return err
}
if !job.IsEnabled() {
return nil
}
_ = app.scheduler.DisableJob(ctx, entity.DbJobTypeRestore, jobId)
if err := repo.UpdateEnabled(ctx, jobId, false); err != nil {
logx.Errorf("数据库恢复任务已禁用( jobId: %d ),任务状态保存失败: %v", jobId, err)
return err
}
return nil
}
// GetPageList 分页获取数据库恢复任务
func (app *DbRestoreApp) GetPageList(condition *entity.DbRestoreQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.restoreRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
}
// GetRestoresEnabled 获取数据库恢复任务
func (app *DbRestoreApp) GetRestoresEnabled(toEntity any, backupHistoryId ...uint64) error {
return app.restoreRepo.GetEnabledRestores(toEntity, backupHistoryId...)
}
// GetDbNamesWithoutRestore 获取未配置定时恢复的数据库名称
func (app *DbRestoreApp) GetDbNamesWithoutRestore(instanceId uint64, dbNames []string) ([]string, error) {
return app.restoreRepo.GetDbNamesWithoutRestore(instanceId, dbNames)
}
// GetHistoryPageList 分页获取数据库备份历史
func (app *DbRestoreApp) GetHistoryPageList(condition *entity.DbRestoreHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.restoreHistoryRepo.GetDbRestoreHistories(condition, pageParam, toEntity, orderBy...)
}

View File

@@ -1,381 +0,0 @@
package application
import (
"context"
"errors"
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/runner"
"reflect"
"strconv"
"sync"
"time"
"golang.org/x/sync/singleflight"
"gorm.io/gorm"
)
const (
maxRunning = 8
)
type dbScheduler struct {
mutex sync.Mutex
runner *runner.Runner[entity.DbJob]
dbApp Db `inject:"DbApp"`
backupRepo repository.DbBackup `inject:"DbBackupRepo"`
backupHistoryRepo repository.DbBackupHistory `inject:"DbBackupHistoryRepo"`
restoreRepo repository.DbRestore `inject:"DbRestoreRepo"`
restoreHistoryRepo repository.DbRestoreHistory `inject:"DbRestoreHistoryRepo"`
binlogRepo repository.DbBinlog `inject:"DbBinlogRepo"`
binlogHistoryRepo repository.DbBinlogHistory `inject:"DbBinlogHistoryRepo"`
sfGroup singleflight.Group
}
func newDbScheduler() *dbScheduler {
scheduler := &dbScheduler{}
scheduler.runner = runner.NewRunner[entity.DbJob](maxRunning, scheduler.runJob,
runner.WithScheduleJob[entity.DbJob](scheduler.scheduleJob),
runner.WithRunnableJob[entity.DbJob](scheduler.runnableJob),
runner.WithUpdateJob[entity.DbJob](scheduler.updateJob),
)
return scheduler
}
func (s *dbScheduler) scheduleJob(job entity.DbJob) (time.Time, error) {
return job.Schedule()
}
func (s *dbScheduler) UpdateJob(ctx context.Context, job entity.DbJob) error {
s.mutex.Lock()
defer s.mutex.Unlock()
_ = s.runner.Update(ctx, job)
return nil
}
func (s *dbScheduler) Close() {
s.runner.Close()
}
func (s *dbScheduler) AddJob(ctx context.Context, jobs any) error {
s.mutex.Lock()
defer s.mutex.Unlock()
reflectValue := reflect.ValueOf(jobs)
switch reflectValue.Kind() {
case reflect.Array, reflect.Slice:
reflectLen := reflectValue.Len()
for i := 0; i < reflectLen; i++ {
job := reflectValue.Index(i).Interface().(entity.DbJob)
_ = s.runner.Add(ctx, job)
}
default:
job := jobs.(entity.DbJob)
_ = s.runner.Add(ctx, job)
}
return nil
}
func (s *dbScheduler) RemoveJob(ctx context.Context, jobType entity.DbJobType, jobId uint64) error {
s.mutex.Lock()
defer s.mutex.Unlock()
if err := s.runner.Remove(ctx, entity.FormatJobKey(jobType, jobId)); err != nil {
return err
}
return nil
}
func (s *dbScheduler) EnableJob(ctx context.Context, job entity.DbJob) error {
s.mutex.Lock()
defer s.mutex.Unlock()
_ = s.runner.Add(ctx, job)
return nil
}
func (s *dbScheduler) DisableJob(ctx context.Context, jobType entity.DbJobType, jobId uint64) error {
s.mutex.Lock()
defer s.mutex.Unlock()
_ = s.runner.Remove(ctx, entity.FormatJobKey(jobType, jobId))
return nil
}
func (s *dbScheduler) StartJobNow(ctx context.Context, job entity.DbJob) error {
s.mutex.Lock()
defer s.mutex.Unlock()
_ = s.runner.StartNow(ctx, job)
return nil
}
func (s *dbScheduler) backup(ctx context.Context, dbProgram dbi.DbProgram, backup *entity.DbBackup) error {
id, err := NewIncUUID()
if err != nil {
return err
}
history := &entity.DbBackupHistory{
Uuid: id.String(),
DbBackupId: backup.Id,
DbInstanceId: backup.DbInstanceId,
DbName: backup.DbName,
}
binlogInfo, err := dbProgram.Backup(ctx, history)
if err != nil {
return err
}
now := time.Now()
name := backup.DbName
if len(backup.Name) > 0 {
name = fmt.Sprintf("%s-%s", backup.DbName, backup.Name)
}
history.Name = fmt.Sprintf("%s[%s]", name, now.Format(time.DateTime))
history.CreateTime = now
history.BinlogFileName = binlogInfo.FileName
history.BinlogSequence = binlogInfo.Sequence
history.BinlogPosition = binlogInfo.Position
if err := s.backupHistoryRepo.Insert(ctx, history); err != nil {
return err
}
return nil
}
func (s *dbScheduler) singleFlightFetchBinlog(ctx context.Context, dbProgram dbi.DbProgram, instanceId uint64, targetTime time.Time) error {
key := strconv.FormatUint(instanceId, 10)
for ctx.Err() == nil {
c := s.sfGroup.DoChan(key, func() (interface{}, error) {
if err := s.fetchBinlog(ctx, dbProgram, instanceId, true, targetTime); err != nil {
return targetTime, err
}
return targetTime, nil
})
select {
case res := <-c:
if targetTime.Compare(res.Val.(time.Time)) <= 0 {
return res.Err
}
case <-ctx.Done():
}
}
return ctx.Err()
}
func (s *dbScheduler) restore(ctx context.Context, dbProgram dbi.DbProgram, restore *entity.DbRestore) error {
if restore.PointInTime.Valid {
if err := s.fetchBinlog(ctx, dbProgram, restore.DbInstanceId, true, restore.PointInTime.Time); err != nil {
return err
}
if err := s.restorePointInTime(ctx, dbProgram, restore); err != nil {
return err
}
} else {
backupHistory, err := s.backupHistoryRepo.GetById(restore.DbBackupHistoryId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
err = errors.New("备份历史已删除")
}
return err
}
if err := s.restoreBackupHistory(ctx, dbProgram, backupHistory); err != nil {
return err
}
}
history := &entity.DbRestoreHistory{
CreateTime: time.Now(),
DbRestoreId: restore.Id,
}
if err := s.restoreHistoryRepo.Insert(ctx, history); err != nil {
return err
}
return nil
}
func (s *dbScheduler) updateJob(ctx context.Context, job entity.DbJob) error {
switch t := job.(type) {
case *entity.DbBackup:
return s.backupRepo.UpdateById(ctx, t)
case *entity.DbRestore:
return s.restoreRepo.UpdateById(ctx, t)
case *entity.DbBinlog:
return s.binlogRepo.UpdateById(ctx, t)
default:
return fmt.Errorf("无效的数据库任务类型: %T", t)
}
}
func (s *dbScheduler) runJob(ctx context.Context, job entity.DbJob) error {
conn, err := s.dbApp.GetDbConnByInstanceId(job.GetInstanceId())
if err != nil {
return err
}
dbProgram, err := conn.GetDialect().GetDbProgram()
if err != nil {
return err
}
switch t := job.(type) {
case *entity.DbBackup:
return s.backup(ctx, dbProgram, t)
case *entity.DbRestore:
return s.restore(ctx, dbProgram, t)
case *entity.DbBinlog:
return s.fetchBinlog(ctx, dbProgram, t.DbInstanceId, false, time.Now())
default:
return fmt.Errorf("无效的数据库任务类型: %T", t)
}
}
func (s *dbScheduler) runnableJob(job entity.DbJob, nextRunning runner.NextJobFunc[entity.DbJob]) (bool, error) {
if job.IsExpired() {
return false, runner.ErrJobExpired
}
const maxCountByInstanceId = 4
const maxCountByDbName = 1
var countByInstanceId, countByDbName int
for item, ok := nextRunning(); ok; item, ok = nextRunning() {
if job.GetInstanceId() == item.GetInstanceId() {
countByInstanceId++
if countByInstanceId >= maxCountByInstanceId {
return false, nil
}
if job.GetDbName() == item.GetDbName() {
countByDbName++
if countByDbName >= maxCountByDbName {
return false, nil
}
}
if (job.GetJobType() == entity.DbJobTypeBinlog && item.GetJobType() == entity.DbJobTypeRestore) ||
(job.GetJobType() == entity.DbJobTypeRestore && item.GetJobType() == entity.DbJobTypeBinlog) {
return false, nil
}
}
}
return true, nil
}
func (s *dbScheduler) restorePointInTime(ctx context.Context, dbProgram dbi.DbProgram, job *entity.DbRestore) error {
binlogHistory, err := s.binlogHistoryRepo.GetHistoryByTime(job.DbInstanceId, job.PointInTime.Time)
if err != nil {
return err
}
position, err := dbProgram.GetBinlogEventPositionAtOrAfterTime(ctx, binlogHistory.FileName, job.PointInTime.Time)
if err != nil {
return err
}
target := &entity.BinlogInfo{
FileName: binlogHistory.FileName,
Sequence: binlogHistory.Sequence,
Position: position,
}
backupHistory, err := s.backupHistoryRepo.GetLatestHistoryForBinlog(job.DbInstanceId, job.DbName, target)
if err != nil {
return err
}
start := &entity.BinlogInfo{
FileName: backupHistory.BinlogFileName,
Sequence: backupHistory.BinlogSequence,
Position: backupHistory.BinlogPosition,
}
binlogHistories, err := s.binlogHistoryRepo.GetHistories(job.DbInstanceId, start, target)
if err != nil {
return err
}
restoreInfo := &dbi.RestoreInfo{
BackupHistory: backupHistory,
BinlogHistories: binlogHistories,
StartPosition: backupHistory.BinlogPosition,
TargetPosition: target.Position,
TargetTime: job.PointInTime.Time,
}
if err := dbProgram.ReplayBinlog(ctx, job.DbName, job.DbName, restoreInfo); err != nil {
return err
}
if err := s.restoreBackupHistory(ctx, dbProgram, backupHistory); err != nil {
return err
}
// 由于 ReplayBinlog 未记录 BINLOG 事件,系统自动备份,避免数据丢失
backup := &entity.DbBackup{
DbInstanceId: backupHistory.DbInstanceId,
DbName: backupHistory.DbName,
Enabled: true,
Repeated: false,
StartTime: time.Now(),
Interval: 0,
Name: "系统备份",
}
backup.Id = backupHistory.DbBackupId
if err := s.backup(ctx, dbProgram, backup); err != nil {
return err
}
return nil
}
func (s *dbScheduler) restoreBackupHistory(ctx context.Context, program dbi.DbProgram, backupHistory *entity.DbBackupHistory) (retErr error) {
if _, err := s.backupHistoryRepo.UpdateRestoring(false, backupHistory.Id); err != nil {
return err
}
ok, err := s.backupHistoryRepo.UpdateRestoring(true, backupHistory.Id)
if err != nil {
return err
}
defer func() {
_, err = s.backupHistoryRepo.UpdateRestoring(false, backupHistory.Id)
if err == nil {
return
}
if retErr == nil {
retErr = err
return
}
retErr = fmt.Errorf("%w, %w", retErr, err)
}()
if !ok {
return errors.New("关联的数据库备份历史已删除")
}
return program.RestoreBackupHistory(ctx, backupHistory.DbName, backupHistory.DbBackupId, backupHistory.Uuid)
}
func (s *dbScheduler) fetchBinlog(ctx context.Context, dbProgram dbi.DbProgram, instanceId uint64, downloadLatestBinlogFile bool, targetTime time.Time) error {
if enabled, err := dbProgram.CheckBinlogEnabled(ctx); err != nil {
return err
} else if !enabled {
return errors.New("数据库未启用 BINLOG")
}
if enabled, err := dbProgram.CheckBinlogRowFormat(ctx); err != nil {
return err
} else if !enabled {
return errors.New("数据库未启用 BINLOG 行模式")
}
earliestBackupSequence := int64(-1)
binlogHistory, ok, err := s.binlogHistoryRepo.GetLatestHistory(instanceId)
if err != nil {
return err
}
if downloadLatestBinlogFile && targetTime.Before(binlogHistory.LastEventTime) {
return nil
}
if !ok {
backupHistory, ok, err := s.backupHistoryRepo.GetEarliestHistoryForBinlog(instanceId)
if err != nil {
return err
}
if !ok {
return nil
}
earliestBackupSequence = backupHistory.BinlogSequence
}
// todo: 将循环从 dbProgram.FetchBinlogs 中提取出来,实现 BINLOG 同步成功后逐一保存 binlogHistory
binlogFiles, err := dbProgram.FetchBinlogs(ctx, downloadLatestBinlogFile, earliestBackupSequence, binlogHistory)
if err != nil {
return err
}
return s.binlogHistoryRepo.InsertWithBinlogFiles(ctx, instanceId, binlogFiles)
}

View File

@@ -61,7 +61,7 @@ type DbSqlExec interface {
DeleteBy(ctx context.Context, condition *entity.DbSqlExec) error
// 分页获取
GetPageList(condition *entity.DbSqlExecQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DbSqlExecQuery, orderBy ...string) (*model.PageResult[*entity.DbSqlExec], error)
}
var _ (DbSqlExec) = (*dbSqlExecAppImpl)(nil)
@@ -313,8 +313,8 @@ func (d *dbSqlExecAppImpl) DeleteBy(ctx context.Context, condition *entity.DbSql
return d.dbSqlExecRepo.DeleteByCond(ctx, condition)
}
func (d *dbSqlExecAppImpl) GetPageList(condition *entity.DbSqlExecQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return d.dbSqlExecRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
func (d *dbSqlExecAppImpl) GetPageList(condition *entity.DbSqlExecQuery, orderBy ...string) (*model.PageResult[*entity.DbSqlExec], error) {
return d.dbSqlExecRepo.GetPageList(condition, orderBy...)
}
// 保存sql执行记录如果是查询类则根据系统配置判断是否保存

View File

@@ -34,7 +34,7 @@ type DbTransferTask interface {
base.App[*entity.DbTransferTask]
// GetPageList 分页获取数据库实例
GetPageList(condition *entity.DbTransferTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DbTransferTaskQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferTask], error)
Save(ctx context.Context, instanceEntity *entity.DbTransferTask) error
@@ -69,8 +69,8 @@ type dbTransferAppImpl struct {
fileApp fileapp.File `inject:"T"`
}
func (app *dbTransferAppImpl) GetPageList(condition *entity.DbTransferTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.GetRepo().GetTaskList(condition, pageParam, toEntity, orderBy...)
func (app *dbTransferAppImpl) GetPageList(condition *entity.DbTransferTaskQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferTask], error) {
return app.GetRepo().GetTaskList(condition, orderBy...)
}
func (app *dbTransferAppImpl) Save(ctx context.Context, taskEntity *entity.DbTransferTask) error {
@@ -144,16 +144,15 @@ func (app *dbTransferAppImpl) InitCronJob() {
_ = app.transferFileApp.UpdateByCond(context.TODO(), &entity.DbTransferFile{Status: entity.DbTransferFileStatusFail}, &entity.DbTransferFile{Status: entity.DbTransferFileStatusRunning})
// 把所有需要定时执行的任务添加到定时任务中
pageParam := &model.PageParam{
PageSize: 100,
PageNum: 1,
}
cond := new(entity.DbTransferTaskQuery)
cond.PageNum = 1
cond.PageSize = 100
cond.Status = entity.DbTransferTaskStatusEnable
cond.CronAble = entity.DbTransferTaskCronAbleEnable
jobs := new([]entity.DbTransferTask)
jobs := []entity.DbTransferTask{}
pr, _ := app.GetPageList(cond, pageParam, jobs)
pr, _ := app.GetPageList(cond)
if nil == pr || pr.Total == 0 {
return
}
@@ -161,15 +160,15 @@ func (app *dbTransferAppImpl) InitCronJob() {
add := 0
for {
for _, job := range *jobs {
for _, job := range jobs {
app.AddCronJob(contextx.NewTraceId(), &job)
add++
}
if add >= int(total) {
return
}
pageParam.PageNum++
_, _ = app.GetPageList(cond, pageParam, jobs)
cond.PageNum++
_, _ = app.GetPageList(cond)
}
}

View File

@@ -13,7 +13,7 @@ type DbTransferFile interface {
base.App[*entity.DbTransferFile]
// GetPageList 分页获取数据库实例
GetPageList(condition *entity.DbTransferFileQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DbTransferFileQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferFile], error)
Save(ctx context.Context, instanceEntity *entity.DbTransferFile) error
@@ -28,8 +28,8 @@ type dbTransferFileAppImpl struct {
fileApp fileapp.File `inject:"T"`
}
func (app *dbTransferFileAppImpl) GetPageList(condition *entity.DbTransferFileQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return app.GetRepo().GetPageList(condition, pageParam, toEntity, orderBy...)
func (app *dbTransferFileAppImpl) GetPageList(condition *entity.DbTransferFileQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferFile], error) {
return app.GetRepo().GetPageList(condition, orderBy...)
}
func (app *dbTransferFileAppImpl) Save(ctx context.Context, taskEntity *entity.DbTransferFile) error {

View File

@@ -2,8 +2,6 @@ package dbi
import (
"context"
"mayfly-go/internal/db/domain/entity"
"path/filepath"
"time"
)
@@ -11,9 +9,9 @@ type DbProgram interface {
CheckBinlogEnabled(ctx context.Context) (bool, error)
CheckBinlogRowFormat(ctx context.Context) (bool, error)
Backup(ctx context.Context, backupHistory *entity.DbBackupHistory) (*entity.BinlogInfo, error)
// Backup(ctx context.Context, backupHistory *entity.DbBackupHistory) (*entity.BinlogInfo, error)
FetchBinlogs(ctx context.Context, downloadLatestBinlogFile bool, earliestBackupSequence int64, latestBinlogHistory *entity.DbBinlogHistory) ([]*entity.BinlogFile, error)
// FetchBinlogs(ctx context.Context, downloadLatestBinlogFile bool, earliestBackupSequence int64, latestBinlogHistory *entity.DbBinlogHistory) ([]*entity.BinlogFile, error)
ReplayBinlog(ctx context.Context, originalDatabase, targetDatabase string, restoreInfo *RestoreInfo) error
@@ -23,21 +21,22 @@ type DbProgram interface {
GetBinlogEventPositionAtOrAfterTime(ctx context.Context, binlogName string, targetTime time.Time) (position int64, parseErr error)
PruneBinlog(history *entity.DbBinlogHistory) error
// PruneBinlog(history *entity.DbBinlogHistory) error
}
type RestoreInfo struct {
BackupHistory *entity.DbBackupHistory
BinlogHistories []*entity.DbBinlogHistory
StartPosition int64
TargetPosition int64
TargetTime time.Time
// BackupHistory *entity.DbBackupHistory
// BinlogHistories []*entity.DbBinlogHistory
StartPosition int64
TargetPosition int64
TargetTime time.Time
}
func (ri *RestoreInfo) GetBinlogPaths(binlogDir string) []string {
files := make([]string, 0, len(ri.BinlogHistories))
for _, history := range ri.BinlogHistories {
files = append(files, filepath.Join(binlogDir, history.FileName))
}
return files
// files := make([]string, 0, len(ri.BinlogHistories))
// for _, history := range ri.BinlogHistories {
// files = append(files, filepath.Join(binlogDir, history.FileName))
// }
// return files
return nil
}

View File

@@ -24,7 +24,8 @@ type MysqlDialect struct {
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
func (md *MysqlDialect) GetDbProgram() (dbi.DbProgram, error) {
return NewDbProgramMysql(md.dc), nil
return nil, nil
// return NewDbProgramMysql(md.dc), nil
}
func (md *MysqlDialect) CopyTable(copy *dbi.DbCopyTable) error {

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,18 @@
package mysql
import (
"mayfly-go/internal/db/domain/entity"
"strings"
"testing"
// func Test_readBinlogInfoFromBackup(t *testing.T) {
// text := `
// --
// -- Position to start replication or point-in-time recovery from
// --
"github.com/stretchr/testify/require"
)
func Test_readBinlogInfoFromBackup(t *testing.T) {
text := `
--
-- Position to start replication or point-in-time recovery from
--
-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000003', MASTER_LOG_POS=379;
`
got, err := readBinlogInfoFromBackup(strings.NewReader(text))
require.NoError(t, err)
require.Equal(t, &entity.BinlogInfo{
FileName: "binlog.000003",
Sequence: 3,
Position: 379,
}, got)
}
// -- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000003', MASTER_LOG_POS=379;
// `
// got, err := readBinlogInfoFromBackup(strings.NewReader(text))
// require.NoError(t, err)
// require.Equal(t, &entity.BinlogInfo{
// FileName: "binlog.000003",
// Sequence: 3,
// Position: 379,
// }, got)
// }

View File

@@ -1,91 +0,0 @@
package entity
import (
"mayfly-go/pkg/runner"
"time"
)
var _ DbJob = (*DbBackup)(nil)
// DbBackup 数据库备份任务
type DbBackup struct {
DbJobBaseImpl
DbInstanceId uint64 // 数据库实例ID
DbName string // 数据库名称
Name string // 数据库备份名称
Enabled bool // 是否启用
EnabledDesc string // 启用状态描述
StartTime time.Time // 开始时间
Interval time.Duration // 间隔时间
MaxSaveDays int // 数据库备份历史保留天数,过期将自动删除
Repeated bool // 是否重复执行
}
func (b *DbBackup) GetInstanceId() uint64 {
return b.DbInstanceId
}
func (b *DbBackup) GetDbName() string {
return b.DbName
}
func (b *DbBackup) GetJobType() DbJobType {
return DbJobTypeBackup
}
func (b *DbBackup) Schedule() (time.Time, error) {
if b.IsFinished() {
return time.Time{}, runner.ErrJobFinished
}
if !b.Enabled {
return time.Time{}, runner.ErrJobDisabled
}
switch b.LastStatus {
case DbJobSuccess:
lastTime := b.LastTime.Time
if lastTime.Before(b.StartTime) {
lastTime = b.StartTime.Add(-b.Interval)
}
return lastTime.Add(b.Interval - lastTime.Sub(b.StartTime)%b.Interval), nil
case DbJobRunning, DbJobFailed:
return time.Now().Add(time.Minute), nil
default:
return b.StartTime, nil
}
}
func (b *DbBackup) IsFinished() bool {
return !b.Repeated && b.LastStatus == DbJobSuccess
}
func (b *DbBackup) IsEnabled() bool {
return b.Enabled
}
func (b *DbBackup) IsExpired() bool {
return false
}
func (b *DbBackup) SetEnabled(enabled bool, desc string) {
b.Enabled = enabled
b.EnabledDesc = desc
}
func (b *DbBackup) Update(job runner.Job) {
backup := job.(*DbBackup)
b.StartTime = backup.StartTime
b.Interval = backup.Interval
}
func (b *DbBackup) GetInterval() time.Duration {
return b.Interval
}
func (b *DbBackup) GetKey() DbJobKey {
return b.getKey(b.GetJobType())
}
func (b *DbBackup) SetStatus(status runner.JobStatus, err error) {
b.setLastStatus(b.GetJobType(), status, err)
}

View File

@@ -1,25 +0,0 @@
package entity
import (
"mayfly-go/pkg/model"
"time"
)
// DbBackupHistory 数据库备份历史
type DbBackupHistory struct {
model.DeletedModel
Uuid string `json:"uuid"`
Name string `json:"name"` // 备份历史名称
CreateTime time.Time `json:"createTime"` // 创建时间: 2023-11-08 02:00:00
DbBackupId uint64 `json:"dbBackupId"`
DbInstanceId uint64 `json:"dbInstanceId"`
DbName string `json:"dbName"`
BinlogFileName string `json:"binlogFileName"`
BinlogSequence int64 `json:"binlogSequence"`
BinlogPosition int64 `json:"binlogPosition"`
}
func (d *DbBackupHistory) TableName() string {
return "t_db_backup_history"
}

View File

@@ -1,87 +0,0 @@
package entity
import (
"mayfly-go/pkg/runner"
"time"
)
const (
BinlogDownloadInterval = time.Minute * 15
)
// BinlogFile is the metadata of the MySQL binlog file.
type BinlogFile struct {
Name string
RemoteSize int64
LocalSize int64
// Sequence is parsed from Name and is for the sorting purpose.
Sequence int64
FirstEventTime time.Time
LastEventTime time.Time
Downloaded bool
}
var _ DbJob = (*DbBinlog)(nil)
// DbBinlog 数据库备份任务
type DbBinlog struct {
DbJobBaseImpl
DbInstanceId uint64 // 数据库实例ID
}
func NewDbBinlog(instanceId uint64) *DbBinlog {
job := &DbBinlog{}
job.Id = instanceId
job.DbInstanceId = instanceId
return job
}
func (b *DbBinlog) GetInstanceId() uint64 {
return b.DbInstanceId
}
func (b *DbBinlog) GetDbName() string {
// binlog 是全库级别的
return ""
}
func (b *DbBinlog) Schedule() (time.Time, error) {
switch b.LastStatus {
case DbJobSuccess:
return time.Time{}, runner.ErrJobFinished
case DbJobFailed:
return time.Now().Add(BinlogDownloadInterval), nil
default:
return time.Now(), nil
}
}
func (b *DbBinlog) Update(_ runner.Job) {}
func (b *DbBinlog) IsEnabled() bool {
return true
}
func (b *DbBinlog) IsExpired() bool {
return false
}
func (b *DbBinlog) SetEnabled(_ bool, _ string) {}
func (b *DbBinlog) GetInterval() time.Duration {
return 0
}
func (b *DbBinlog) GetJobType() DbJobType {
return DbJobTypeBinlog
}
func (b *DbBinlog) GetKey() DbJobKey {
return b.getKey(b.GetJobType())
}
func (b *DbBinlog) SetStatus(status DbJobStatus, err error) {
b.setLastStatus(b.GetJobType(), status, err)
}

View File

@@ -1,29 +0,0 @@
package entity
import (
"mayfly-go/pkg/model"
"time"
)
// DbBinlogHistory 数据库 binlog 历史
type DbBinlogHistory struct {
model.DeletedModel
CreateTime time.Time `json:"createTime"` // 创建时间: 2023-11-08 02:00:00
FileName string
FileSize int64
Sequence int64
FirstEventTime time.Time
LastEventTime time.Time
DbInstanceId uint64 `json:"dbInstanceId"`
}
func (d *DbBinlogHistory) TableName() string {
return "t_db_binlog_history"
}
type BinlogInfo struct {
FileName string `json:"fileName"`
Sequence int64 `json:"sequence"`
Position int64 `json:"position"`
}

View File

@@ -1,126 +0,0 @@
package entity
import (
"fmt"
"mayfly-go/pkg/model"
"mayfly-go/pkg/runner"
"mayfly-go/pkg/utils/stringx"
"mayfly-go/pkg/utils/timex"
"time"
)
const LastResultSize = 256
type DbJobKey = runner.JobKey
type DbJobStatus = runner.JobStatus
const (
DbJobRunning = runner.JobRunning
DbJobSuccess = runner.JobSuccess
DbJobFailed = runner.JobFailed
)
type DbJobType string
func (typ DbJobType) String() string {
return string(typ)
}
const (
DbJobUnknown DbJobType = "db-unknown"
DbJobTypeBackup DbJobType = "db-backup"
DbJobTypeRestore DbJobType = "db-restore"
DbJobTypeBinlog DbJobType = "db-binlog"
)
const (
DbJobNameUnknown = "未知任务"
DbJobNameBackup = "数据库备份"
DbJobNameRestore = "数据库恢复"
DbJobNameBinlog = "BINLOG同步"
)
var _ runner.Job = (DbJob)(nil)
type DbJobBase interface {
model.ModelI
}
type DbJob interface {
runner.Job
DbJobBase
GetInstanceId() uint64
GetKey() string
GetJobType() DbJobType
GetDbName() string
Schedule() (time.Time, error)
IsEnabled() bool
IsExpired() bool
SetEnabled(enabled bool, desc string)
Update(job runner.Job)
GetInterval() time.Duration
}
var _ DbJobBase = (*DbJobBaseImpl)(nil)
type DbJobBaseImpl struct {
model.Model
LastStatus DbJobStatus // 最近一次执行状态
LastResult string // 最近一次执行结果
LastTime timex.NullTime // 最近一次执行时间
jobKey runner.JobKey
}
func (d *DbJobBaseImpl) getJobType() DbJobType {
job, ok := any(d).(DbJob)
if !ok {
return DbJobUnknown
}
return job.GetJobType()
}
func (d *DbJobBaseImpl) setLastStatus(jobType DbJobType, status DbJobStatus, err error) {
var statusName, jobName string
switch status {
case DbJobRunning:
statusName = "运行中"
case DbJobSuccess:
statusName = "成功"
case DbJobFailed:
statusName = "失败"
default:
return
}
switch jobType {
case DbJobTypeBackup:
jobName = DbJobNameBackup
case DbJobTypeRestore:
jobName = DbJobNameRestore
case DbJobTypeBinlog:
jobName = DbJobNameBinlog
default:
jobName = jobType.String()
}
d.LastStatus = status
var result = jobName + statusName
if err != nil {
result = fmt.Sprintf("%s: %v", result, err)
}
d.LastResult = stringx.Truncate(result, LastResultSize, LastResultSize, "")
d.LastTime = timex.NewNullTime(time.Now())
}
func FormatJobKey(typ DbJobType, jobId uint64) DbJobKey {
return fmt.Sprintf("%v-%d", typ, jobId)
}
func (d *DbJobBaseImpl) getKey(jobType DbJobType) DbJobKey {
if len(d.jobKey) == 0 {
d.jobKey = FormatJobKey(jobType, d.Id)
}
return d.jobKey
}

View File

@@ -1,88 +0,0 @@
package entity
import (
"mayfly-go/pkg/runner"
"mayfly-go/pkg/utils/timex"
"time"
)
var _ DbJob = (*DbRestore)(nil)
// DbRestore 数据库恢复任务
type DbRestore struct {
DbJobBaseImpl
DbInstanceId uint64 // 数据库实例ID
DbName string // 数据库名称
Enabled bool // 是否启用
EnabledDesc string // 启用状态描述
StartTime time.Time // 开始时间
Interval time.Duration // 间隔时间
Repeated bool // 是否重复执行
PointInTime timex.NullTime `json:"pointInTime"` // 指定数据库恢复的时间点
DbBackupId uint64 `json:"dbBackupId"` // 用于恢复的数据库恢复任务ID
DbBackupHistoryId uint64 `json:"dbBackupHistoryId"` // 用于恢复的数据库恢复历史ID
DbBackupHistoryName string `json:"dbBackupHistoryName"` // 数据库恢复历史名称
}
func (r *DbRestore) GetInstanceId() uint64 {
return r.DbInstanceId
}
func (r *DbRestore) GetDbName() string {
return r.DbName
}
func (r *DbRestore) Schedule() (time.Time, error) {
if !r.Enabled {
return time.Time{}, runner.ErrJobDisabled
}
switch r.LastStatus {
case DbJobSuccess, DbJobFailed:
return time.Time{}, runner.ErrJobFinished
default:
if time.Now().Sub(r.StartTime) > time.Hour {
return time.Time{}, runner.ErrJobExpired
}
return r.StartTime, nil
}
}
func (r *DbRestore) IsEnabled() bool {
return r.Enabled
}
func (r *DbRestore) SetEnabled(enabled bool, desc string) {
r.Enabled = enabled
r.EnabledDesc = desc
}
func (r *DbRestore) IsExpired() bool {
return !r.Repeated && time.Now().After(r.StartTime.Add(time.Hour))
}
func (r *DbRestore) IsFinished() bool {
return !r.Repeated && r.LastStatus == DbJobSuccess
}
func (r *DbRestore) Update(job runner.Job) {
restore := job.(*DbRestore)
r.StartTime = restore.StartTime
r.Interval = restore.Interval
}
func (r *DbRestore) GetInterval() time.Duration {
return r.Interval
}
func (r *DbRestore) GetJobType() DbJobType {
return DbJobTypeRestore
}
func (r *DbRestore) GetKey() DbJobKey {
return r.getKey(r.GetJobType())
}
func (r *DbRestore) SetStatus(status DbJobStatus, err error) {
r.setLastStatus(r.GetJobType(), status, err)
}

View File

@@ -1,18 +0,0 @@
package entity
import (
"mayfly-go/pkg/model"
"time"
)
// DbRestoreHistory 数据库恢复历史
type DbRestoreHistory struct {
model.DeletedModel
CreateTime time.Time `orm:"column(create_time)" json:"createTime"` // 创建时间: 2023-11-08 02:00:00
DbRestoreId uint64 `orm:"column(db_restore_id)" json:"dbRestoreId"`
}
func (d *DbRestoreHistory) TableName() string {
return "t_db_restore_history"
}

View File

@@ -0,0 +1,20 @@
package entity
import "time"
type DbListPO struct {
Id *int64 `json:"id"`
Code string `json:"code"`
Name *string `json:"name"`
GetDatabaseMode DbGetDatabaseMode `json:"getDatabaseMode"` // 获取数据库方式
Database *string `json:"database"`
Remark *string `json:"remark"`
InstanceId uint64 `json:"instanceId"`
AuthCertName string `json:"authCertName"`
CreateTime *time.Time `json:"createTime"`
Creator *string `json:"creator"`
CreatorId *int64 `json:"creatorId"`
UpdateTime *time.Time `json:"updateTime"`
Modifier *string `json:"modifier"`
ModifierId *int64 `json:"modifierId"`
}

View File

@@ -1,7 +1,11 @@
package entity
import "mayfly-go/pkg/model"
// InstanceQuery 数据库实例查询
type InstanceQuery struct {
model.PageParam
Id uint64 `json:"id" form:"id"`
Name string `json:"name" form:"name"`
Code string `json:"code" form:"code"`
@@ -12,19 +16,27 @@ type InstanceQuery struct {
}
type DataSyncTaskQuery struct {
model.PageParam
Name string `json:"name" form:"name"`
Status int8 `json:"status" form:"status"`
}
type DataSyncLogQuery struct {
model.PageParam
TaskId uint64 `json:"task_id" form:"taskId"`
}
type DbTransferTaskQuery struct {
model.PageParam
Name string `json:"name" form:"name"`
Status int8 `json:"status" form:"status"`
CronAble int8 `json:"cronAble" form:"cronAble"`
}
type DbTransferFileQuery struct {
model.PageParam
TaskId uint64 `json:"task_id" form:"taskId"`
Name string `json:"name" form:"name"`
}
@@ -35,6 +47,8 @@ type DbTransferLogQuery struct {
// 数据库查询实体,不与数据库表字段一一对应
type DbQuery struct {
model.PageParam
Id uint64 `form:"id"`
TagPath string `form:"tagPath"`
Code string `json:"code" form:"code"`
@@ -43,6 +57,8 @@ type DbQuery struct {
}
type DbSqlExecQuery struct {
model.PageParam
Id uint64 `json:"id" form:"id"`
DbId uint64 `json:"dbId" form:"dbId"`
Db string `json:"db" form:"db"`
@@ -76,6 +92,8 @@ type DbBackupHistoryQuery struct {
// DbRestoreQuery 数据库备份任务查询
type DbRestoreQuery struct {
*model.PageParam
Id uint64 `json:"id" form:"id"`
DbName string `json:"dbName" form:"dbName"`
InDbNames []string `json:"-" form:"-"`

View File

@@ -10,5 +10,5 @@ type Db interface {
base.Repo[*entity.Db]
// 分页获取数据信息列表
GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetDbList(condition *entity.DbQuery, orderBy ...string) (*model.PageResult[*entity.DbListPO], error)
}

View File

@@ -1,17 +0,0 @@
package repository
import (
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/model"
)
type DbBackup interface {
DbJob[*entity.DbBackup]
ListToDo(jobs any) error
ListDbInstances(enabled bool, repeated bool, instanceIds *[]uint64) error
GetDbNamesWithoutBackup(instanceId uint64, dbNames []string) ([]string, error)
// GetPageList 分页获取数据库任务列表
GetPageList(condition *entity.DbBackupQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
}

View File

@@ -1,24 +0,0 @@
package repository
import (
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/base"
"mayfly-go/pkg/model"
)
type DbBackupHistory interface {
base.Repo[*entity.DbBackupHistory]
// GetPageList 分页获取数据备份历史
GetPageList(condition *entity.DbBackupHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetLatestHistoryForBinlog(instanceId uint64, dbName string, bi *entity.BinlogInfo) (*entity.DbBackupHistory, error)
GetEarliestHistoryForBinlog(instanceId uint64) (*entity.DbBackupHistory, bool, error)
GetHistories(backupHistoryIds []uint64, toEntity any) error
UpdateDeleting(deleting bool, backupHistoryId ...uint64) (bool, error)
UpdateRestoring(restoring bool, backupHistoryId ...uint64) (bool, error)
ZeroBinlogInfo(backupHistoryId uint64) error
}

View File

@@ -1,12 +0,0 @@
package repository
import (
"context"
"mayfly-go/internal/db/domain/entity"
)
type DbBinlog interface {
DbJob[*entity.DbBinlog]
AddJobIfNotExists(ctx context.Context, job *entity.DbBinlog) error
}

View File

@@ -1,24 +0,0 @@
package repository
import (
"context"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/base"
"time"
)
type DbBinlogHistory interface {
base.Repo[*entity.DbBinlogHistory]
GetHistories(instanceId uint64, start, target *entity.BinlogInfo) ([]*entity.DbBinlogHistory, error)
GetHistoryByTime(instanceId uint64, targetTime time.Time) (*entity.DbBinlogHistory, error)
GetLatestHistory(instanceId uint64) (*entity.DbBinlogHistory, bool, error)
InsertWithBinlogFiles(ctx context.Context, instanceId uint64, binlogFiles []*entity.BinlogFile) error
Upsert(ctx context.Context, history *entity.DbBinlogHistory) error
GetHistoriesBeforeSequence(ctx context.Context, instanceId uint64, binlogSeq int64, histories *[]*entity.DbBinlogHistory) error
}

View File

@@ -10,12 +10,12 @@ type DataSyncTask interface {
base.Repo[*entity.DataSyncTask]
// 分页获取数据库实例信息列表
GetTaskList(condition *entity.DataSyncTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetTaskList(condition *entity.DataSyncTaskQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncTask], error)
}
type DataSyncLog interface {
base.Repo[*entity.DataSyncLog]
// 分页获取数据库实例信息列表
GetTaskLogList(condition *entity.DataSyncLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetTaskLogList(condition *entity.DataSyncLogQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncLog], error)
}

View File

@@ -1,22 +0,0 @@
package repository
import (
"context"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/base"
)
type DbJobBase[T entity.DbJob] interface {
base.Repo[T]
// UpdateLastStatus 更新任务执行状态
UpdateLastStatus(ctx context.Context, job entity.DbJob) error
}
type DbJob[T entity.DbJob] interface {
DbJobBase[T]
// AddJob 添加数据库任务
AddJob(ctx context.Context, jobs any) error
UpdateEnabled(ctx context.Context, jobId uint64, enabled bool) error
}

View File

@@ -1,18 +0,0 @@
package repository
import (
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/model"
)
type DbRestore interface {
DbJob[*entity.DbRestore]
ListToDo(jobs any) error
GetDbNamesWithoutRestore(instanceId uint64, dbNames []string) ([]string, error)
// GetPageList 分页获取数据库任务列表
GetPageList(condition *entity.DbRestoreQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetEnabledRestores(toEntity any, backupHistoryId ...uint64) error
}

View File

@@ -1,14 +0,0 @@
package repository
import (
"mayfly-go/internal/db/domain/entity"
"mayfly-go/pkg/base"
"mayfly-go/pkg/model"
)
type DbRestoreHistory interface {
base.Repo[*entity.DbRestoreHistory]
// GetDbRestoreHistories 分页获取数据备份历史
GetDbRestoreHistories(condition *entity.DbRestoreHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
}

View File

@@ -10,5 +10,5 @@ type DbSqlExec interface {
base.Repo[*entity.DbSqlExec]
// 分页获取
GetPageList(condition *entity.DbSqlExecQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DbSqlExecQuery, orderBy ...string) (*model.PageResult[*entity.DbSqlExec], error)
}

View File

@@ -10,5 +10,5 @@ type DbTransferTask interface {
base.Repo[*entity.DbTransferTask]
// 分页获取数据库实例信息列表
GetTaskList(condition *entity.DbTransferTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetTaskList(condition *entity.DbTransferTaskQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferTask], error)
}

View File

@@ -10,5 +10,5 @@ type DbTransferFile interface {
base.Repo[*entity.DbTransferFile]
// 分页获取数据库实例信息列表
GetPageList(condition *entity.DbTransferFileQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetPageList(condition *entity.DbTransferFileQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferFile], error)
}

View File

@@ -10,5 +10,5 @@ type Instance interface {
base.Repo[*entity.DbInstance]
// 分页获取数据库实例信息列表
GetInstanceList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
GetInstanceList(condition *entity.InstanceQuery, orderBy ...string) (*model.PageResult[*entity.DbInstance], error)
}

View File

@@ -1,11 +0,0 @@
package repository
type Repositories struct {
Instance Instance
Backup DbBackup
BackupHistory DbBackupHistory
Restore DbRestore
RestoreHistory DbRestoreHistory
Binlog DbBinlog
BinlogHistory DbBinlogHistory
}

View File

@@ -4,6 +4,7 @@ import (
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/base"
"mayfly-go/pkg/gormx"
"mayfly-go/pkg/model"
)
@@ -16,7 +17,8 @@ func newDbRepo() repository.Db {
}
// 分页获取数据库信息列表
func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, orderBy ...string) (*model.PageResult[*entity.DbListPO], error) {
pd := model.NewCond().Eq("instance_id", condition.InstanceId).In("code", condition.Codes).Eq("id", condition.Id)
return d.PageByCondToAny(pd, pageParam, toEntity)
list := []*entity.DbListPO{}
return gormx.PageByCond(d.GetModel(), pd, condition.PageParam, list)
}

View File

@@ -1,94 +0,0 @@
package persistence
import (
"context"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/global"
"mayfly-go/pkg/gormx"
"mayfly-go/pkg/model"
"slices"
)
var _ repository.DbBackup = (*dbBackupRepoImpl)(nil)
type dbBackupRepoImpl struct {
dbJobBaseImpl[*entity.DbBackup]
}
func NewDbBackupRepo() repository.DbBackup {
dr := &dbBackupRepoImpl{}
return dr
}
func (d *dbBackupRepoImpl) GetDbNamesWithoutBackup(instanceId uint64, dbNames []string) ([]string, error) {
var dbNamesWithBackup []string
err := global.Db.Model(d.NewModel()).
Where("db_instance_id = ?", instanceId).
Where("repeated = ?", true).
Scopes(gormx.UndeleteScope).
Pluck("db_name", &dbNamesWithBackup).
Error
if err != nil {
return nil, err
}
result := make([]string, 0, len(dbNames))
for _, name := range dbNames {
if !slices.Contains(dbNamesWithBackup, name) {
result = append(result, name)
}
}
return result, nil
}
func (d *dbBackupRepoImpl) ListDbInstances(enabled bool, repeated bool, instanceIds *[]uint64) error {
return global.Db.Model(d.NewModel()).
Where("enabled = ?", enabled).
Where("repeated = ?", repeated).
Scopes(gormx.UndeleteScope).
Distinct().
Pluck("db_instance_id", &instanceIds).
Error
}
func (d *dbBackupRepoImpl) ListToDo(jobs any) error {
db := global.Db.Model(d.NewModel())
err := db.Where("enabled = ?", true).
Where(db.Where("repeated = ?", true).Or("last_status <> ?", entity.DbJobSuccess)).
Scopes(gormx.UndeleteScope).
Find(jobs).Error
if err != nil {
return err
}
return nil
}
// GetPageList 分页获取数据库备份任务列表
func (d *dbBackupRepoImpl) GetPageList(condition *entity.DbBackupQuery, pageParam *model.PageParam, toEntity any, _ ...string) (*model.PageResult[any], error) {
qd := model.NewCond().
Eq("id", condition.Id).
Eq0("db_instance_id", condition.DbInstanceId).
Eq0("repeated", condition.Repeated).
In0("db_name", condition.InDbNames).
Like("db_name", condition.DbName)
return d.PageByCondToAny(qd, pageParam, toEntity)
}
// AddJob 添加数据库任务
func (d *dbBackupRepoImpl) AddJob(ctx context.Context, jobs any) error {
return addJob[*entity.DbBackup](ctx, d.dbJobBaseImpl, jobs)
}
func (d *dbBackupRepoImpl) UpdateEnabled(ctx context.Context, jobId uint64, enabled bool) error {
cond := map[string]any{
"id": jobId,
}
desc := "已禁用"
if enabled {
desc = "已启用"
}
return d.UpdateByCond(ctx, map[string]any{
"enabled": enabled,
"enabled_desc": desc,
}, cond)
}

View File

@@ -1,123 +0,0 @@
package persistence
import (
"errors"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/base"
"mayfly-go/pkg/global"
"mayfly-go/pkg/gormx"
"mayfly-go/pkg/model"
"gorm.io/gorm"
)
var _ repository.DbBackupHistory = (*dbBackupHistoryRepoImpl)(nil)
type dbBackupHistoryRepoImpl struct {
base.RepoImpl[*entity.DbBackupHistory]
}
func NewDbBackupHistoryRepo() repository.DbBackupHistory {
return &dbBackupHistoryRepoImpl{}
}
func (repo *dbBackupHistoryRepoImpl) GetPageList(condition *entity.DbBackupHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
qd := model.NewCond().
Eq("id", condition.Id).
Eq0("db_instance_id", condition.DbInstanceId).
In0("db_name", condition.InDbNames).
Eq("db_backup_id", condition.DbBackupId).
Eq("db_name", condition.DbName)
return repo.PageByCondToAny(qd, pageParam, toEntity)
}
func (repo *dbBackupHistoryRepoImpl) GetHistories(backupHistoryIds []uint64, toEntity any) error {
return global.Db.Model(repo.NewModel()).
Where("id in ?", backupHistoryIds).
Where("deleting = false").
Scopes(gormx.UndeleteScope).
Find(toEntity).
Error
}
func (repo *dbBackupHistoryRepoImpl) GetLatestHistoryForBinlog(instanceId uint64, dbName string, bi *entity.BinlogInfo) (*entity.DbBackupHistory, error) {
history := &entity.DbBackupHistory{}
db := global.Db
err := db.Model(repo.NewModel()).
Where("db_instance_id = ?", instanceId).
Where("db_name = ?", dbName).
Where(db.Where("binlog_sequence < ?", bi.Sequence).
Or(db.Where("binlog_sequence = ?", bi.Sequence).
Where("binlog_position <= ?", bi.Position))).
Where("binlog_sequence > 0").
Where("deleting = false").
Scopes(gormx.UndeleteScope).
Order("binlog_sequence desc, binlog_position desc").
First(history).Error
if err != nil {
return nil, err
}
return history, err
}
func (repo *dbBackupHistoryRepoImpl) GetEarliestHistoryForBinlog(instanceId uint64) (*entity.DbBackupHistory, bool, error) {
history := &entity.DbBackupHistory{}
db := global.Db.Model(repo.NewModel())
err := db.Where("db_instance_id = ?", instanceId).
Where("binlog_sequence > 0").
Where("deleting = false").
Scopes(gormx.UndeleteScope).
Order("binlog_sequence").
First(history).Error
switch {
case err == nil:
return history, true, nil
case errors.Is(err, gorm.ErrRecordNotFound):
return history, false, nil
default:
return nil, false, err
}
}
func (repo *dbBackupHistoryRepoImpl) UpdateDeleting(deleting bool, backupHistoryId ...uint64) (bool, error) {
db := global.Db.Model(repo.NewModel()).
Where("id in ?", backupHistoryId).
Where("restoring = false").
Scopes(gormx.UndeleteScope).
Update("deleting", deleting)
if db.Error != nil {
return false, db.Error
}
if db.RowsAffected != int64(len(backupHistoryId)) {
return false, nil
}
return true, nil
}
func (repo *dbBackupHistoryRepoImpl) UpdateRestoring(restoring bool, backupHistoryId ...uint64) (bool, error) {
db := global.Db.Model(repo.NewModel()).
Where("id in ?", backupHistoryId).
Where("deleting = false").
Scopes(gormx.UndeleteScope).
Update("restoring", restoring)
if db.Error != nil {
return false, db.Error
}
if db.RowsAffected != int64(len(backupHistoryId)) {
return false, nil
}
return true, nil
}
func (repo *dbBackupHistoryRepoImpl) ZeroBinlogInfo(backupHistoryId uint64) error {
return global.Db.Model(repo.NewModel()).
Where("id = ?", backupHistoryId).
Where("restoring = false").
Scopes(gormx.UndeleteScope).
Updates(&map[string]any{
"binlog_file_name": "",
"binlog_sequence": 0,
"binlog_position": 0,
}).Error
}

View File

@@ -1,38 +0,0 @@
package persistence
import (
"context"
"fmt"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/global"
"gorm.io/gorm/clause"
)
var _ repository.DbBinlog = (*dbBinlogRepoImpl)(nil)
type dbBinlogRepoImpl struct {
dbJobBaseImpl[*entity.DbBinlog]
}
func NewDbBinlogRepo() repository.DbBinlog {
return &dbBinlogRepoImpl{}
}
func (d *dbBinlogRepoImpl) AddJobIfNotExists(_ context.Context, job *entity.DbBinlog) error {
// todo: 如果存在已删除记录,如何处理?
if err := global.Db.Clauses(clause.OnConflict{DoNothing: true}).Create(job).Error; err != nil {
return fmt.Errorf("启动 binlog 下载失败: %w", err)
}
return nil
}
// AddJob 添加数据库任务
func (d *dbBinlogRepoImpl) AddJob(ctx context.Context, jobs any) error {
panic("not implement, use AddJobIfNotExists")
}
func (d *dbBinlogRepoImpl) UpdateEnabled(_ context.Context, jobId uint64, enabled bool) error {
panic("not implement")
}

View File

@@ -1,131 +0,0 @@
package persistence
import (
"context"
"errors"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/base"
"mayfly-go/pkg/global"
"mayfly-go/pkg/gormx"
"mayfly-go/pkg/model"
"time"
"gorm.io/gorm"
)
var _ repository.DbBinlogHistory = (*dbBinlogHistoryRepoImpl)(nil)
type dbBinlogHistoryRepoImpl struct {
base.RepoImpl[*entity.DbBinlogHistory]
}
func NewDbBinlogHistoryRepo() repository.DbBinlogHistory {
return &dbBinlogHistoryRepoImpl{}
}
func (repo *dbBinlogHistoryRepoImpl) GetHistoryByTime(instanceId uint64, targetTime time.Time) (*entity.DbBinlogHistory, error) {
qc := model.NewCond().
Eq("db_instance_id", instanceId).
Le("first_event_time", targetTime).
OrderByDesc("first_event_time")
history := &entity.DbBinlogHistory{}
if err := repo.GetByCond(qc.Dest(history)); err != nil {
return nil, err
}
return history, nil
}
func (repo *dbBinlogHistoryRepoImpl) GetHistories(instanceId uint64, start, target *entity.BinlogInfo) ([]*entity.DbBinlogHistory, error) {
qc := model.NewCond().
Eq("db_instance_id", instanceId).
Ge("sequence", start.Sequence).
Le("sequence", target.Sequence).
OrderByAsc("sequence")
histories, err := repo.SelectByCond(qc)
if err != nil {
return nil, err
}
if len(histories) == 0 {
return nil, errors.New("未找到满足条件的 binlog 文件")
}
return histories, nil
}
func (repo *dbBinlogHistoryRepoImpl) GetLatestHistory(instanceId uint64) (*entity.DbBinlogHistory, bool, error) {
history := &entity.DbBinlogHistory{}
qc := model.NewCond().
Eq("db_instance_id", instanceId).
OrderByDesc("sequence").
Dest(history)
err := repo.GetByCond(qc)
switch {
case err == nil:
return history, true, nil
case errors.Is(err, gorm.ErrRecordNotFound):
return history, false, nil
default:
return nil, false, err
}
}
func (repo *dbBinlogHistoryRepoImpl) Upsert(_ context.Context, history *entity.DbBinlogHistory) error {
return gormx.Tx(func(db *gorm.DB) error {
old := &entity.DbBinlogHistory{}
err := db.Where("db_instance_id = ?", history.DbInstanceId).
Where("sequence = ?", history.Sequence).
Scopes(gormx.UndeleteScope).
First(old).Error
switch {
case err == nil:
return db.Model(old).Select("create_time", "file_size", "first_event_time", "last_event_time").Updates(history).Error
case errors.Is(err, gorm.ErrRecordNotFound):
return db.Create(history).Error
default:
return err
}
})
}
func (repo *dbBinlogHistoryRepoImpl) InsertWithBinlogFiles(ctx context.Context, instanceId uint64, binlogFiles []*entity.BinlogFile) error {
if len(binlogFiles) == 0 {
return nil
}
histories := make([]*entity.DbBinlogHistory, 0, len(binlogFiles))
for _, fileOnServer := range binlogFiles {
if !fileOnServer.Downloaded {
break
}
history := &entity.DbBinlogHistory{
CreateTime: time.Now(),
FileName: fileOnServer.Name,
FileSize: fileOnServer.RemoteSize,
Sequence: fileOnServer.Sequence,
FirstEventTime: fileOnServer.FirstEventTime,
LastEventTime: fileOnServer.LastEventTime,
DbInstanceId: instanceId,
}
histories = append(histories, history)
}
if len(histories) > 1 {
if err := repo.BatchInsert(ctx, histories[:len(histories)-1]); err != nil {
return err
}
}
if len(histories) > 0 {
if err := repo.Upsert(ctx, histories[len(histories)-1]); err != nil {
return err
}
}
return nil
}
func (repo *dbBinlogHistoryRepoImpl) GetHistoriesBeforeSequence(ctx context.Context, instanceId uint64, binlogSeq int64, histories *[]*entity.DbBinlogHistory) error {
return global.Db.Model(repo.NewModel()).
Where("db_instance_id = ?", instanceId).
Where("sequence < ?", binlogSeq).
Scopes(gormx.UndeleteScope).
Order("id").
Find(histories).
Error
}

View File

@@ -16,24 +16,24 @@ func newDataSyncTaskRepo() repository.DataSyncTask {
}
// 分页获取数据库信息列表
func (d *dataSyncTaskRepoImpl) GetTaskList(condition *entity.DataSyncTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
func (d *dataSyncTaskRepoImpl) GetTaskList(condition *entity.DataSyncTaskQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncTask], error) {
qd := model.NewCond().
Like("task_name", condition.Name).
Eq("status", condition.Status)
return d.PageByCondToAny(qd, pageParam, toEntity)
return d.PageByCond(qd, condition.PageParam)
}
type dataSyncLogRepoImpl struct {
base.RepoImpl[*entity.DataSyncLog]
}
// 分页获取数据库信息列表
func (d *dataSyncLogRepoImpl) GetTaskLogList(condition *entity.DataSyncLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
qd := model.NewCond().
Eq("task_id", condition.TaskId)
return d.PageByCondToAny(qd, pageParam, toEntity)
}
func newDataSyncLogRepo() repository.DataSyncLog {
return &dataSyncLogRepoImpl{}
}
// 分页获取数据库信息列表
func (d *dataSyncLogRepoImpl) GetTaskLogList(condition *entity.DataSyncLogQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncLog], error) {
qd := model.NewCond().
Eq("task_id", condition.TaskId)
return d.PageByCond(qd, condition.PageParam)
}

View File

@@ -1,78 +0,0 @@
package persistence
import (
"context"
"errors"
"fmt"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/base"
"mayfly-go/pkg/gormx"
"reflect"
"gorm.io/gorm"
)
var _ repository.DbJobBase[entity.DbJob] = (*dbJobBaseImpl[entity.DbJob])(nil)
type dbJobBaseImpl[T entity.DbJob] struct {
base.RepoImpl[T]
}
func (d *dbJobBaseImpl[T]) UpdateLastStatus(ctx context.Context, job entity.DbJob) error {
return d.UpdateById(ctx, job.(T), "last_status", "last_result", "last_time")
}
func addJob[T entity.DbJob](ctx context.Context, repo dbJobBaseImpl[T], jobs any) error {
// refactor jobs from any to []T
return gormx.Tx(func(db *gorm.DB) error {
var instanceId uint64
var dbNames []string
reflectValue := reflect.ValueOf(jobs)
var plural bool
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
plural = true
reflectLen := reflectValue.Len()
dbNames = make([]string, 0, reflectLen)
for i := 0; i < reflectLen; i++ {
job := reflectValue.Index(i).Interface().(entity.DbJob)
if instanceId == 0 {
instanceId = job.GetInstanceId()
}
if job.GetInstanceId() != instanceId {
return errors.New("不支持同时为多个数据库实例添加数据库任务")
}
if job.GetInterval() == 0 {
// 单次执行的数据库任务可重复创建
continue
}
dbNames = append(dbNames, job.GetDbName())
}
default:
job := jobs.(entity.DbJob)
instanceId = job.GetInstanceId()
if job.GetInterval() > 0 {
dbNames = append(dbNames, job.GetDbName())
}
}
var res []string
err := db.Model(repo.NewModel()).Select("db_name").
Where("db_instance_id = ?", instanceId).
Where("db_name in ?", dbNames).
Where("repeated = true").
Scopes(gormx.UndeleteScope).
Find(&res).Error
if err != nil {
return err
}
if len(res) > 0 {
return errors.New(fmt.Sprintf("数据库任务已存在: %v", res))
}
if plural {
return repo.BatchInsertWithDb(ctx, db, jobs.([]T))
}
return repo.InsertWithDb(ctx, db, jobs.(T))
})
}

View File

@@ -1,95 +0,0 @@
package persistence
import (
"context"
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/global"
"mayfly-go/pkg/gormx"
"mayfly-go/pkg/model"
"slices"
)
var _ repository.DbRestore = (*dbRestoreRepoImpl)(nil)
type dbRestoreRepoImpl struct {
dbJobBaseImpl[*entity.DbRestore]
}
func NewDbRestoreRepo() repository.DbRestore {
return &dbRestoreRepoImpl{}
}
func (d *dbRestoreRepoImpl) GetDbNamesWithoutRestore(instanceId uint64, dbNames []string) ([]string, error) {
var dbNamesWithRestore []string
err := global.Db.Model(d.NewModel()).
Where("db_instance_id = ?", instanceId).
Where("repeated = ?", true).
Scopes(gormx.UndeleteScope).
Pluck("db_name", &dbNamesWithRestore).
Error
if err != nil {
return nil, err
}
result := make([]string, 0, len(dbNames))
for _, name := range dbNames {
if !slices.Contains(dbNamesWithRestore, name) {
result = append(result, name)
}
}
return result, nil
}
func (d *dbRestoreRepoImpl) ListToDo(jobs any) error {
db := global.Db.Model(d.NewModel())
err := db.Where("enabled = ?", true).
Where(db.Where("repeated = ?", true).Or("last_status <> ?", entity.DbJobSuccess)).
Scopes(gormx.UndeleteScope).
Find(jobs).Error
if err != nil {
return err
}
return nil
}
// GetPageList 分页获取数据库备份任务列表
func (d *dbRestoreRepoImpl) GetPageList(condition *entity.DbRestoreQuery, pageParam *model.PageParam, toEntity any, _ ...string) (*model.PageResult[any], error) {
qd := model.NewCond().
Eq("id", condition.Id).
Eq0("db_instance_id", condition.DbInstanceId).
Eq0("repeated", condition.Repeated).
In0("db_name", condition.InDbNames).
Like("db_name", condition.DbName)
return d.PageByCondToAny(qd, pageParam, toEntity)
}
func (d *dbRestoreRepoImpl) GetEnabledRestores(toEntity any, backupHistoryId ...uint64) error {
return global.Db.Model(d.NewModel()).
Select("id", "db_backup_history_id", "last_status", "last_result", "last_time").
Where("db_backup_history_id in ?", backupHistoryId).
Where("enabled = true").
Scopes(gormx.UndeleteScope).
Order("id DESC").
Find(toEntity).
Error
}
// AddJob 添加数据库任务
func (d *dbRestoreRepoImpl) AddJob(ctx context.Context, jobs any) error {
return addJob[*entity.DbRestore](ctx, d.dbJobBaseImpl, jobs)
}
func (d *dbRestoreRepoImpl) UpdateEnabled(ctx context.Context, jobId uint64, enabled bool) error {
cond := map[string]any{
"id": jobId,
}
desc := "已禁用"
if enabled {
desc = "已启用"
}
return d.UpdateByCond(ctx, map[string]any{
"enabled": enabled,
"enabled_desc": desc,
}, cond)
}

View File

@@ -1,26 +0,0 @@
package persistence
import (
"mayfly-go/internal/db/domain/entity"
"mayfly-go/internal/db/domain/repository"
"mayfly-go/pkg/base"
"mayfly-go/pkg/model"
)
var _ repository.DbRestoreHistory = (*dbRestoreHistoryRepoImpl)(nil)
type dbRestoreHistoryRepoImpl struct {
base.RepoImpl[*entity.DbRestoreHistory]
}
func NewDbRestoreHistoryRepo() repository.DbRestoreHistory {
return &dbRestoreHistoryRepoImpl{}
}
func (d *dbRestoreHistoryRepoImpl) GetDbRestoreHistories(condition *entity.DbRestoreHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
qd := model.NewCond().
Eq("id", condition.Id).
Eq("db_backup_id", condition.DbRestoreId)
return d.PageByCondToAny(qd, pageParam, toEntity)
}

View File

@@ -16,7 +16,7 @@ func newDbSqlExecRepo() repository.DbSqlExec {
}
// 分页获取
func (d *dbSqlExecRepoImpl) GetPageList(condition *entity.DbSqlExecQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
func (d *dbSqlExecRepoImpl) GetPageList(condition *entity.DbSqlExecQuery, orderBy ...string) (*model.PageResult[*entity.DbSqlExec], error) {
qd := model.NewCond().
Eq("db_id", condition.DbId).
Eq("`table`", condition.Table).
@@ -25,5 +25,5 @@ func (d *dbSqlExecRepoImpl) GetPageList(condition *entity.DbSqlExecQuery, pagePa
Eq("flow_biz_key", condition.FlowBizKey).
In("status", condition.Status).
RLike("db", condition.Db).OrderBy(orderBy...)
return d.PageByCondToAny(qd, pageParam, toEntity)
return d.PageByCond(qd, condition.PageParam)
}

View File

@@ -16,11 +16,11 @@ func newDbTransferTaskRepo() repository.DbTransferTask {
}
// 分页获取数据库信息列表
func (d *dbTransferTaskRepoImpl) GetTaskList(condition *entity.DbTransferTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
func (d *dbTransferTaskRepoImpl) GetTaskList(condition *entity.DbTransferTaskQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferTask], error) {
qd := model.NewCond().
Like("task_name", condition.Name).
Eq("status", condition.Status).
Eq("cron_able", condition.CronAble)
//Eq("status", condition.Status)
return d.PageByCondToAny(qd, pageParam, toEntity)
return d.PageByCond(qd, condition.PageParam)
}

View File

@@ -16,10 +16,10 @@ func newDbTransferFileRepo() repository.DbTransferFile {
}
// 分页获取数据库信息列表
func (d *dbTransferFileRepoImpl) GetPageList(condition *entity.DbTransferFileQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
func (d *dbTransferFileRepoImpl) GetPageList(condition *entity.DbTransferFileQuery, orderBy ...string) (*model.PageResult[*entity.DbTransferFile], error) {
qd := model.NewCond().
Eq("task_id", condition.TaskId).
OrderByDesc("create_time")
//Eq("status", condition.Status)
return d.PageByCondToAny(qd, pageParam, toEntity)
return d.PageByCond(qd, condition.PageParam)
}

View File

@@ -16,7 +16,7 @@ func NewInstanceRepo() repository.Instance {
}
// 分页获取数据库信息列表
func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, orderBy ...string) (*model.PageResult[*entity.DbInstance], error) {
qd := model.NewCond().
Eq("id", condition.Id).
Eq("host", condition.Host).
@@ -30,5 +30,5 @@ func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, page
qd.And("host like ? or name like ? or code like ?", keyword, keyword, keyword)
}
return d.PageByCondToAny(qd, pageParam, toEntity)
return d.PageByCond(qd, condition.PageParam)
}

View File

@@ -13,11 +13,4 @@ func InitIoc() {
ioc.Register(newDataSyncLogRepo(), ioc.WithComponentName("DbDataSyncLogRepo"))
ioc.Register(newDbTransferTaskRepo(), ioc.WithComponentName("DbTransferTaskRepo"))
ioc.Register(newDbTransferFileRepo(), ioc.WithComponentName("DbTransferFileRepo"))
ioc.Register(NewDbBackupRepo(), ioc.WithComponentName("DbBackupRepo"))
ioc.Register(NewDbBackupHistoryRepo(), ioc.WithComponentName("DbBackupHistoryRepo"))
ioc.Register(NewDbRestoreRepo(), ioc.WithComponentName("DbRestoreRepo"))
ioc.Register(NewDbRestoreHistoryRepo(), ioc.WithComponentName("DbRestoreHistoryRepo"))
ioc.Register(NewDbBinlogRepo(), ioc.WithComponentName("DbBinlogRepo"))
ioc.Register(NewDbBinlogHistoryRepo(), ioc.WithComponentName("DbBinlogHistoryRepo"))
}

View File

@@ -1,23 +1,10 @@
package init
import "mayfly-go/internal/db/application"
// 终止进程时的处理函数
func Terminate() {
closeDbTasks()
}
func closeDbTasks() {
restoreApp := application.GetDbRestoreApp()
if restoreApp != nil {
restoreApp.Close()
}
binlogApp := application.GetDbBinlogApp()
if binlogApp != nil {
binlogApp.Close()
}
backupApp := application.GetDbBackupApp()
if backupApp != nil {
backupApp.Close()
}
}