2024-01-05 05:31:32 +00:00
|
|
|
|
package application
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2024-12-08 13:04:23 +08:00
|
|
|
|
"cmp"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
"context"
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"fmt"
|
2024-01-12 13:15:30 +08:00
|
|
|
|
"mayfly-go/internal/db/dbm/dbi"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
"mayfly-go/internal/db/domain/entity"
|
|
|
|
|
|
"mayfly-go/internal/db/domain/repository"
|
2025-06-22 10:52:06 +08:00
|
|
|
|
"mayfly-go/internal/db/imsg"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
"mayfly-go/pkg/base"
|
2025-01-10 12:05:00 +08:00
|
|
|
|
"mayfly-go/pkg/cache"
|
2024-03-29 21:40:26 +08:00
|
|
|
|
"mayfly-go/pkg/contextx"
|
2024-01-06 22:36:50 +08:00
|
|
|
|
"mayfly-go/pkg/errorx"
|
2025-06-22 10:52:06 +08:00
|
|
|
|
"mayfly-go/pkg/i18n"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
"mayfly-go/pkg/logx"
|
|
|
|
|
|
"mayfly-go/pkg/model"
|
|
|
|
|
|
"mayfly-go/pkg/scheduler"
|
2024-12-08 13:04:23 +08:00
|
|
|
|
"mayfly-go/pkg/utils/collx"
|
2024-01-24 08:29:16 +00:00
|
|
|
|
"regexp"
|
|
|
|
|
|
"strings"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
"time"
|
2024-01-30 21:56:49 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2025-06-27 12:17:45 +08:00
|
|
|
|
"github.com/spf13/cast"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type DataSyncTask interface {
|
|
|
|
|
|
base.App[*entity.DataSyncTask]
|
|
|
|
|
|
|
|
|
|
|
|
// GetPageList 分页获取数据库实例
|
2025-05-20 21:04:47 +08:00
|
|
|
|
GetPageList(condition *entity.DataSyncTaskQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncTask], error)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
|
|
|
|
|
Save(ctx context.Context, instanceEntity *entity.DataSyncTask) error
|
|
|
|
|
|
|
|
|
|
|
|
Delete(ctx context.Context, id uint64) error
|
|
|
|
|
|
|
|
|
|
|
|
InitCronJob()
|
|
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
Run(ctx context.Context, id uint64) error
|
2024-01-05 22:16:38 +08:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
StopTask(ctx context.Context, id uint64) error
|
|
|
|
|
|
|
2025-05-20 21:04:47 +08:00
|
|
|
|
GetTaskLogList(condition *entity.DataSyncLogQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncLog], error)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-16 12:09:55 +08:00
|
|
|
|
var _ (DataSyncTask) = (*dataSyncAppImpl)(nil)
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
type dataSyncAppImpl struct {
|
|
|
|
|
|
base.AppImpl[*entity.DataSyncTask, repository.DataSyncTask]
|
2024-01-05 22:16:38 +08:00
|
|
|
|
|
2024-12-16 23:29:18 +08:00
|
|
|
|
dbDataSyncLogRepo repository.DataSyncLog `inject:"T"`
|
2024-01-29 04:20:23 +00:00
|
|
|
|
|
2024-12-16 23:29:18 +08:00
|
|
|
|
dbApp Db `inject:"T"`
|
2024-01-21 22:52:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-24 08:29:16 +00:00
|
|
|
|
var (
|
2024-12-16 23:29:18 +08:00
|
|
|
|
whereReg = regexp.MustCompile(`(?i)where`)
|
2024-01-24 08:29:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-05-20 21:04:47 +08:00
|
|
|
|
func (app *dataSyncAppImpl) GetPageList(condition *entity.DataSyncTaskQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncTask], error) {
|
|
|
|
|
|
return app.GetRepo().GetTaskList(condition, orderBy...)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (app *dataSyncAppImpl) Save(ctx context.Context, taskEntity *entity.DataSyncTask) error {
|
2024-01-07 21:46:25 +08:00
|
|
|
|
var err error
|
2024-01-05 05:31:32 +00:00
|
|
|
|
if taskEntity.Id == 0 {
|
2024-01-30 13:09:26 +00:00
|
|
|
|
// 新建时生成key
|
|
|
|
|
|
taskEntity.TaskKey = uuid.New().String()
|
2024-01-07 21:46:25 +08:00
|
|
|
|
err = app.Insert(ctx, taskEntity)
|
|
|
|
|
|
} else {
|
2025-05-29 20:24:48 +08:00
|
|
|
|
taskEntity.TaskKey = ""
|
2024-01-07 21:46:25 +08:00
|
|
|
|
err = app.UpdateById(ctx, taskEntity)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
2024-01-07 21:46:25 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
app.addCronJob(ctx, taskEntity)
|
2024-01-07 21:46:25 +08:00
|
|
|
|
return nil
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (app *dataSyncAppImpl) Delete(ctx context.Context, id uint64) error {
|
2025-06-22 10:52:06 +08:00
|
|
|
|
task, err := app.GetById(id)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
return errorx.NewBiz("sync task not found")
|
2024-01-07 21:46:25 +08:00
|
|
|
|
}
|
2025-06-22 10:52:06 +08:00
|
|
|
|
scheduler.RemoveByKey(task.TaskKey)
|
|
|
|
|
|
app.MarkStop(id)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
return app.DeleteById(ctx, id)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
func (app *dataSyncAppImpl) Run(ctx context.Context, id uint64) error {
|
|
|
|
|
|
if app.IsRunning(id) {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
logx.Warnf("[%d] the db sync task is running...", id)
|
2025-06-22 10:52:06 +08:00
|
|
|
|
return nil
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-05 14:53:30 +08:00
|
|
|
|
task, err := app.GetById(id)
|
2024-01-05 22:16:38 +08:00
|
|
|
|
if err != nil {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return errorx.NewBiz("task not found")
|
2024-01-05 22:16:38 +08:00
|
|
|
|
}
|
2025-05-29 20:24:48 +08:00
|
|
|
|
|
|
|
|
|
|
logx.InfofContext(ctx, "start the data sync task: %s => %s", task.TaskName, task.TaskKey)
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
if task.RunningState == entity.DataSyncTaskRunStateRunning {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
return errorx.NewBiz("the task is in progress")
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
2025-01-10 12:05:00 +08:00
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
updateStateTask := &entity.DataSyncTask{
|
|
|
|
|
|
RunningState: entity.DataSyncTaskRunStateRunning,
|
|
|
|
|
|
}
|
|
|
|
|
|
updateStateTask.Id = id
|
|
|
|
|
|
if err := app.UpdateById(ctx, updateStateTask); err != nil {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("failed to update task running state: %s", err.Error())
|
2025-06-22 10:52:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
// 标记该任务运行中
|
|
|
|
|
|
app.MarkRunning(id)
|
2024-01-06 22:36:50 +08:00
|
|
|
|
go func() {
|
2025-06-22 10:52:06 +08:00
|
|
|
|
now := time.Now()
|
|
|
|
|
|
syncLog := &entity.DataSyncLog{
|
|
|
|
|
|
TaskId: task.Id,
|
|
|
|
|
|
CreateTime: &now,
|
|
|
|
|
|
Status: entity.DataSyncTaskStateFail, // 默认失败
|
|
|
|
|
|
}
|
|
|
|
|
|
defer app.endRunning(task, syncLog)
|
|
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
// 通过占位符格式化sql
|
|
|
|
|
|
updSql := ""
|
|
|
|
|
|
orderSql := ""
|
|
|
|
|
|
if task.UpdFieldVal != "0" && task.UpdFieldVal != "" && task.UpdField != "" {
|
2024-03-01 04:03:03 +00:00
|
|
|
|
if err != nil {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
logx.ErrorfContext(ctx, "data source connection unavailable: %s", err.Error())
|
2024-03-01 04:03:03 +00:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-09-17 21:23:12 +08:00
|
|
|
|
srcConn, err := app.dbApp.GetDbConn(context.Background(), uint64(task.SrcDbId), task.SrcDbName)
|
2024-12-26 04:11:28 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logx.ErrorfContext(ctx, "failed to connect to the source database: %s", err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
updFieldDataType := dbi.DefaultDbDataType
|
|
|
|
|
|
srcConn.WalkQueryRows(context.Background(), task.DataSql, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
|
|
|
|
|
for _, column := range columns {
|
|
|
|
|
|
if strings.EqualFold(column.Name, cmp.Or(task.UpdFieldSrc, task.UpdField)) {
|
|
|
|
|
|
updFieldDataType = column.DbDataType
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-11 12:42:20 +08:00
|
|
|
|
return dbi.NewStopWalkQueryError("get column data type... ignore~")
|
2024-12-26 04:11:28 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
updSql = fmt.Sprintf("and %s > %s", task.UpdField, updFieldDataType.DataType.SQLValue(task.UpdFieldVal))
|
2024-01-06 22:36:50 +08:00
|
|
|
|
orderSql = "order by " + task.UpdField + " asc "
|
|
|
|
|
|
}
|
2024-03-01 04:03:03 +00:00
|
|
|
|
// 正则判断DataSql是否以where .*结尾,如果是则不添加where 1 = 1
|
|
|
|
|
|
var where = "where 1=1"
|
|
|
|
|
|
if whereReg.MatchString(task.DataSql) {
|
|
|
|
|
|
where = ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
// 组装查询sql
|
2024-12-26 04:11:28 +00:00
|
|
|
|
sqlStr := fmt.Sprintf("%s %s %s %s", task.DataSql, where, updSql, orderSql)
|
2025-06-22 10:52:06 +08:00
|
|
|
|
syncLog.DataSqlFull = sqlStr
|
2024-01-06 22:36:50 +08:00
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
err := app.doDataSync(ctx, sqlStr, task, syncLog)
|
2024-01-06 22:36:50 +08:00
|
|
|
|
if err != nil {
|
2025-06-22 10:52:06 +08:00
|
|
|
|
syncLog.ErrText = i18n.T(imsg.DataSyncFailMsg, "msg", err.Error())
|
|
|
|
|
|
logx.ErrorContext(ctx, syncLog.ErrText)
|
|
|
|
|
|
syncLog.Status = entity.DataSyncTaskStateFail
|
2025-01-10 12:05:00 +08:00
|
|
|
|
} else {
|
2025-06-22 10:52:06 +08:00
|
|
|
|
syncLog.Status = entity.DataSyncTaskStateSuccess
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
func (app *dataSyncAppImpl) doDataSync(ctx context.Context, sql string, task *entity.DataSyncTask, syncLog *entity.DataSyncLog) error {
|
2024-01-05 05:31:32 +00:00
|
|
|
|
// 获取源数据库连接
|
2025-05-22 23:29:50 +08:00
|
|
|
|
srcConn, err := app.dbApp.GetDbConn(ctx, uint64(task.SrcDbId), task.SrcDbName)
|
2025-05-21 04:42:30 +00:00
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
if err != nil {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("failed to connect to the source database: %s", err.Error())
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取目标数据库连接
|
2025-05-22 23:29:50 +08:00
|
|
|
|
targetConn, err := app.dbApp.GetDbConn(ctx, uint64(task.TargetDbId), task.TargetDbName)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
if err != nil {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("failed to connect to the target database: %s", err.Error())
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
// task.FieldMap为json数组字符串 [{"src":"id","target":"id"}],转为map
|
|
|
|
|
|
var fieldMap []map[string]string
|
|
|
|
|
|
err = json.Unmarshal([]byte(task.FieldMap), &fieldMap)
|
|
|
|
|
|
if err != nil {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("there was an error parsing the field map json: %s", err.Error())
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
// 记录本次同步数据总数
|
|
|
|
|
|
total := 0
|
|
|
|
|
|
batchSize := task.PageSize
|
|
|
|
|
|
result := make([]map[string]any, 0)
|
|
|
|
|
|
|
2024-03-01 04:03:03 +00:00
|
|
|
|
// 如果有数据库别名,则从UpdField中去掉数据库别名, 如:a.id => id,用于获取字段具体名称
|
|
|
|
|
|
updFieldName := task.UpdField
|
|
|
|
|
|
if task.UpdField != "" && strings.Contains(task.UpdField, ".") {
|
|
|
|
|
|
updFieldName = strings.Split(task.UpdField, ".")[1]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
targetTableColumns, err := targetConn.GetMetadata().GetColumns(task.TargetTableName)
|
|
|
|
|
|
if err != nil {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("failed to get target table columns: %s", err.Error())
|
2024-12-08 13:04:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
targetColumnName2Column := collx.ArrayToMap(targetTableColumns, func(column dbi.Column) string {
|
|
|
|
|
|
return column.ColumnName
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 目标库对应的insert columns
|
|
|
|
|
|
targetInsertColumns := collx.ArrayMap[map[string]string, dbi.Column](fieldMap, func(val map[string]string) dbi.Column {
|
|
|
|
|
|
return targetColumnName2Column[val["target"]]
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2024-04-17 21:28:28 +08:00
|
|
|
|
_, err = srcConn.WalkQueryRows(context.Background(), sql, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
2024-01-06 22:36:50 +08:00
|
|
|
|
total++
|
|
|
|
|
|
result = append(result, row)
|
|
|
|
|
|
if total%batchSize == 0 {
|
2025-01-10 12:05:00 +08:00
|
|
|
|
if err := app.srcData2TargetDb(result, fieldMap, updFieldName, task, targetConn, targetInsertColumns); err != nil {
|
2024-01-06 22:36:50 +08:00
|
|
|
|
return err
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
2024-01-07 21:46:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录当前已同步的数据量
|
2025-06-22 10:52:06 +08:00
|
|
|
|
syncLog.ErrText = i18n.T(imsg.DataSyncingMsg, "count", total)
|
2024-03-29 21:40:26 +08:00
|
|
|
|
logx.InfoContext(ctx, syncLog.ErrText)
|
2024-01-07 21:46:25 +08:00
|
|
|
|
syncLog.ResNum = total
|
|
|
|
|
|
app.saveLog(syncLog)
|
|
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
result = result[:0]
|
2025-01-10 12:05:00 +08:00
|
|
|
|
|
|
|
|
|
|
// 运行过程中,判断状态是否为已关闭,是则结束运行,否则继续运行
|
|
|
|
|
|
if !app.IsRunning(task.Id) {
|
|
|
|
|
|
return errorx.NewBiz("the task has been terminated manually")
|
|
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
})
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
if err != nil {
|
2025-06-22 10:52:06 +08:00
|
|
|
|
return err
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-06 22:36:50 +08:00
|
|
|
|
// 处理剩余的数据
|
|
|
|
|
|
if len(result) > 0 {
|
2025-01-10 12:05:00 +08:00
|
|
|
|
if err := app.srcData2TargetDb(result, fieldMap, updFieldName, task, targetConn, targetInsertColumns); err != nil {
|
2025-06-22 10:52:06 +08:00
|
|
|
|
return err
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
logx.InfofContext(ctx, "synchronous task: [%s], finished execution, save records successfully: [%d]", task.TaskName, total)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
// 执行成功日志
|
2025-06-22 10:52:06 +08:00
|
|
|
|
syncLog.ErrText = i18n.T(imsg.DataSyncSuccessMsg, "count", total)
|
2024-01-07 21:46:25 +08:00
|
|
|
|
syncLog.ResNum = total
|
|
|
|
|
|
|
2025-06-22 10:52:06 +08:00
|
|
|
|
return nil
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
func (app *dataSyncAppImpl) srcData2TargetDb(srcRes []map[string]any, fieldMap []map[string]string, updFieldName string, task *entity.DataSyncTask, targetDbConn *dbi.DbConn, targetInsertColumns []dbi.Column) (err error) {
|
2024-01-24 08:29:16 +00:00
|
|
|
|
// 遍历res,组装数据
|
2024-12-08 13:04:23 +08:00
|
|
|
|
var targetData = make([]map[string]any, 0)
|
|
|
|
|
|
for _, srcData := range srcRes {
|
|
|
|
|
|
var data = make(map[string]any)
|
2024-01-06 22:36:50 +08:00
|
|
|
|
// 遍历字段映射, target字段的值为src字段取值
|
|
|
|
|
|
for _, item := range fieldMap {
|
|
|
|
|
|
// target字段的值为src字段取值
|
2024-12-08 13:04:23 +08:00
|
|
|
|
data[item["target"]] = srcData[item["src"]]
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
2024-12-08 13:04:23 +08:00
|
|
|
|
targetData = append(targetData, data)
|
|
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-01-17 03:53:15 +00:00
|
|
|
|
targetValues := make([][]any, 0)
|
2024-12-08 13:04:23 +08:00
|
|
|
|
for _, item := range targetData {
|
|
|
|
|
|
var values = make([]any, 0)
|
|
|
|
|
|
for _, column := range targetInsertColumns {
|
|
|
|
|
|
values = append(values, item[column.ColumnName])
|
|
|
|
|
|
}
|
2025-01-17 03:53:15 +00:00
|
|
|
|
targetValues = append(targetValues, values)
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2024-10-20 03:52:23 +00:00
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
// 执行插入
|
2025-01-10 12:05:00 +08:00
|
|
|
|
targetDialect := targetDbConn.GetDialect()
|
|
|
|
|
|
|
|
|
|
|
|
// 生成目标数据库批量插入sql,并执行
|
2025-01-17 03:53:15 +00:00
|
|
|
|
sqls := targetDialect.GetSQLGenerator().GenInsert(task.TargetTableName, targetInsertColumns, targetValues, cmp.Or(task.DuplicateStrategy, dbi.DuplicateStrategyNone))
|
2025-01-10 12:05:00 +08:00
|
|
|
|
|
|
|
|
|
|
// 开启本批次执行事务
|
|
|
|
|
|
targetDbTx, err := targetDbConn.Begin()
|
|
|
|
|
|
if err != nil {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("failed to start the target database transaction: %s", err.Error())
|
2025-01-10 12:05:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
targetDbTx.Rollback()
|
|
|
|
|
|
err = fmt.Errorf("%v", r)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
for _, sql := range sqls {
|
|
|
|
|
|
_, err := targetDbTx.Exec(sql)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
targetDbTx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是mssql,暂不手动提交事务,否则报错 mssql: The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
|
|
|
|
|
|
if err := targetDbTx.Commit(); err != nil {
|
|
|
|
|
|
if targetDbConn.Info.Type != dbi.ToDbType("mssql") {
|
2025-10-18 11:21:33 +08:00
|
|
|
|
return errorx.NewBizf("data synchronization - The target database transaction failed to commit: %s", err.Error())
|
2025-01-10 12:05:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-08 13:04:23 +08:00
|
|
|
|
|
2024-10-20 03:52:23 +00:00
|
|
|
|
setUpdateFieldVal := func(field string) {
|
|
|
|
|
|
// 解决字段大小写问题
|
|
|
|
|
|
updFieldVal := srcRes[len(srcRes)-1][strings.ToUpper(field)]
|
|
|
|
|
|
if updFieldVal == "" || updFieldVal == nil {
|
|
|
|
|
|
updFieldVal = srcRes[len(srcRes)-1][strings.ToLower(field)]
|
|
|
|
|
|
}
|
2024-12-08 13:04:23 +08:00
|
|
|
|
|
2025-01-07 21:02:27 +08:00
|
|
|
|
task.UpdFieldVal = cast.ToString(updFieldVal)
|
2024-01-24 08:29:16 +00:00
|
|
|
|
}
|
2024-10-20 03:52:23 +00:00
|
|
|
|
// 如果指定了更新字段,则以更新字段取值
|
2024-12-26 04:11:28 +00:00
|
|
|
|
setUpdateFieldVal(cmp.Or(task.UpdFieldSrc, updFieldName))
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
func (app *dataSyncAppImpl) StopTask(ctx context.Context, taskId uint64) error {
|
|
|
|
|
|
task := new(entity.DataSyncTask)
|
|
|
|
|
|
task.Id = taskId
|
|
|
|
|
|
task.RunningState = entity.DataSyncTaskRunStateStop
|
|
|
|
|
|
if err := app.UpdateById(ctx, task); err != nil {
|
|
|
|
|
|
return err
|
2024-01-06 22:36:50 +08:00
|
|
|
|
}
|
2025-01-10 12:05:00 +08:00
|
|
|
|
app.MarkStop(taskId)
|
2024-01-06 22:36:50 +08:00
|
|
|
|
return nil
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-07 21:46:25 +08:00
|
|
|
|
func (app *dataSyncAppImpl) endRunning(taskEntity *entity.DataSyncTask, log *entity.DataSyncLog) {
|
|
|
|
|
|
logx.Info(log.ErrText)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-07 21:46:25 +08:00
|
|
|
|
state := log.Status
|
2024-01-05 05:31:32 +00:00
|
|
|
|
task := new(entity.DataSyncTask)
|
|
|
|
|
|
task.Id = taskEntity.Id
|
|
|
|
|
|
task.RecentState = state
|
2025-01-10 12:05:00 +08:00
|
|
|
|
task.UpdFieldVal = taskEntity.UpdFieldVal
|
2024-01-05 05:31:32 +00:00
|
|
|
|
task.RunningState = entity.DataSyncTaskRunStateReady
|
|
|
|
|
|
_ = app.UpdateById(context.Background(), task)
|
|
|
|
|
|
// 保存执行日志
|
2024-01-07 21:46:25 +08:00
|
|
|
|
app.saveLog(log)
|
2025-01-10 12:05:00 +08:00
|
|
|
|
app.MarkStop(task.Id)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
2024-01-05 22:16:38 +08:00
|
|
|
|
|
2024-01-07 21:46:25 +08:00
|
|
|
|
func (app *dataSyncAppImpl) saveLog(log *entity.DataSyncLog) {
|
2024-01-23 19:30:28 +08:00
|
|
|
|
app.dbDataSyncLogRepo.Save(context.Background(), log)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (app *dataSyncAppImpl) InitCronJob() {
|
2025-10-07 15:41:19 +08:00
|
|
|
|
ctx := contextx.NewTraceId()
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
defer func() {
|
|
|
|
|
|
if err := recover(); err != nil {
|
2025-10-07 15:41:19 +08:00
|
|
|
|
logx.ErrorTraceContext(ctx, "the data synchronization task failed to initialize", err)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
2024-04-29 12:29:56 +08:00
|
|
|
|
// 修改执行中状态为待执行
|
2024-05-16 17:26:32 +08:00
|
|
|
|
_ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning})
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2025-06-01 20:39:54 +08:00
|
|
|
|
if err := app.CursorByCond(&entity.DataSyncTaskQuery{Status: entity.DataSyncTaskStatusEnable}, func(dst *entity.DataSyncTask) error {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
app.MarkStop(dst.Id)
|
2025-10-07 15:41:19 +08:00
|
|
|
|
app.addCronJob(ctx, dst)
|
2025-06-01 20:39:54 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}); err != nil {
|
2025-10-07 15:41:19 +08:00
|
|
|
|
logx.ErrorTraceContext(ctx, "the db data sync task failed to initialize: %v", err)
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-01-05 22:16:38 +08:00
|
|
|
|
|
2025-05-20 21:04:47 +08:00
|
|
|
|
func (app *dataSyncAppImpl) GetTaskLogList(condition *entity.DataSyncLogQuery, orderBy ...string) (*model.PageResult[*entity.DataSyncLog], error) {
|
|
|
|
|
|
return app.dbDataSyncLogRepo.GetTaskLogList(condition, orderBy...)
|
2024-01-05 22:16:38 +08:00
|
|
|
|
}
|
2025-01-10 12:05:00 +08:00
|
|
|
|
|
|
|
|
|
|
// MarkRunning 标记任务执行中
|
|
|
|
|
|
func (app *dataSyncAppImpl) MarkRunning(taskId uint64) {
|
|
|
|
|
|
cache.Set(fmt.Sprintf("mayfly:db:syncdata:%d", taskId), 1, -1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MarkStop 标记任务结束
|
|
|
|
|
|
func (app *dataSyncAppImpl) MarkStop(taskId uint64) {
|
|
|
|
|
|
cache.Del(fmt.Sprintf("mayfly:db:syncdata:%d", taskId))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// IsRunning 判断任务是否执行中
|
|
|
|
|
|
func (app *dataSyncAppImpl) IsRunning(taskId uint64) bool {
|
|
|
|
|
|
return cache.GetStr(fmt.Sprintf("mayfly:db:syncdata:%d", taskId)) != ""
|
|
|
|
|
|
}
|
2025-06-22 10:52:06 +08:00
|
|
|
|
|
|
|
|
|
|
func (app *dataSyncAppImpl) addCronJob(ctx context.Context, taskEntity *entity.DataSyncTask) {
|
|
|
|
|
|
key := taskEntity.TaskKey
|
|
|
|
|
|
// 先移除旧的任务
|
|
|
|
|
|
scheduler.RemoveByKey(key)
|
|
|
|
|
|
|
|
|
|
|
|
// 根据状态添加新的任务
|
|
|
|
|
|
if taskEntity.Status == entity.DataSyncTaskStatusEnable {
|
|
|
|
|
|
taskId := taskEntity.Id
|
2025-10-07 15:41:19 +08:00
|
|
|
|
logx.InfofContext(ctx, "start add the data sync task job: %s, cron[%s]", taskEntity.TaskName, taskEntity.TaskCron)
|
2025-06-22 10:52:06 +08:00
|
|
|
|
if err := scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
|
|
|
|
|
|
if err := app.Run(context.Background(), taskId); err != nil {
|
2025-10-07 15:41:19 +08:00
|
|
|
|
logx.ErrorfContext(ctx, "the data sync task failed to execute at a scheduled time: %s", err.Error())
|
2025-06-22 10:52:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}); err != nil {
|
2025-10-07 15:41:19 +08:00
|
|
|
|
logx.ErrorTraceContext(ctx, "add db data sync job failed", err)
|
2025-06-22 10:52:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|