mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	refactor: code review
This commit is contained in:
		@@ -55,7 +55,7 @@
 | 
				
			|||||||
    "prettier": "^3.1.0",
 | 
					    "prettier": "^3.1.0",
 | 
				
			||||||
    "sass": "^1.69.0",
 | 
					    "sass": "^1.69.0",
 | 
				
			||||||
    "typescript": "^5.3.2",
 | 
					    "typescript": "^5.3.2",
 | 
				
			||||||
    "vite": "^5.0.10",
 | 
					    "vite": "^5.0.11",
 | 
				
			||||||
    "vue-eslint-parser": "^9.3.2"
 | 
					    "vue-eslint-parser": "^9.3.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "browserslist": [
 | 
					  "browserslist": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,7 +171,7 @@ const basicFormData = {
 | 
				
			|||||||
    srcDbId: -1,
 | 
					    srcDbId: -1,
 | 
				
			||||||
    targetDbId: -1,
 | 
					    targetDbId: -1,
 | 
				
			||||||
    dataSql: 'select * from',
 | 
					    dataSql: 'select * from',
 | 
				
			||||||
    pageSize: 100,
 | 
					    pageSize: 1000,
 | 
				
			||||||
    updField: 'id',
 | 
					    updField: 'id',
 | 
				
			||||||
    updFieldVal: '0',
 | 
					    updFieldVal: '0',
 | 
				
			||||||
    fieldMap: [{ src: 'a', target: 'b' }],
 | 
					    fieldMap: [{ src: 'a', target: 'b' }],
 | 
				
			||||||
@@ -291,7 +291,7 @@ watch(tabActiveName, async (newValue: string) => {
 | 
				
			|||||||
            let updField = srcDbDialect.wrapName(state.form.updField!);
 | 
					            let updField = srcDbDialect.wrapName(state.form.updField!);
 | 
				
			||||||
            state.previewDataSql = `SELECT * FROM (\n ${state.form.dataSql?.trim() || '请输入数据sql'} \n ) t \n where ${updField} > '${
 | 
					            state.previewDataSql = `SELECT * FROM (\n ${state.form.dataSql?.trim() || '请输入数据sql'} \n ) t \n where ${updField} > '${
 | 
				
			||||||
                state.form.updFieldVal || ''
 | 
					                state.form.updFieldVal || ''
 | 
				
			||||||
            }' \n ${srcDbDialect.getPageSql(1, state.form.pageSize || 100)}`;
 | 
					            }'`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 检查字段映射中是否存在重复的目标字段
 | 
					            // 检查字段映射中是否存在重复的目标字段
 | 
				
			||||||
            let fields = new Set();
 | 
					            let fields = new Set();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,11 +13,6 @@
 | 
				
			|||||||
                <el-button v-auth="perms.save" type="primary" icon="plus" @click="edit(false)">添加</el-button>
 | 
					                <el-button v-auth="perms.save" type="primary" icon="plus" @click="edit(false)">添加</el-button>
 | 
				
			||||||
                <el-button v-auth="perms.del" :disabled="selectionData.length < 1" @click="del()" type="danger" icon="delete">删除</el-button>
 | 
					                <el-button v-auth="perms.del" :disabled="selectionData.length < 1" @click="del()" type="danger" icon="delete">删除</el-button>
 | 
				
			||||||
            </template>
 | 
					            </template>
 | 
				
			||||||
            <template #recentState="{ data }">
 | 
					 | 
				
			||||||
                <el-tag v-if="data.recentState == 1" class="ml-2" type="success">成功</el-tag>
 | 
					 | 
				
			||||||
                <el-tag v-else-if="data.recentState == 2" class="ml-2" type="success">失败</el-tag>
 | 
					 | 
				
			||||||
                <el-tag v-else-if="data.recentState == 0" class="ml-2" type="">未执行</el-tag>
 | 
					 | 
				
			||||||
            </template>
 | 
					 | 
				
			||||||
            <template #status="{ data }">
 | 
					            <template #status="{ data }">
 | 
				
			||||||
                <span v-if="actionBtns[perms.status]">
 | 
					                <span v-if="actionBtns[perms.status]">
 | 
				
			||||||
                    <el-switch
 | 
					                    <el-switch
 | 
				
			||||||
@@ -59,6 +54,7 @@ import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			|||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
import { SearchItem } from '@/components/SearchForm';
 | 
					import { SearchItem } from '@/components/SearchForm';
 | 
				
			||||||
 | 
					import { DbDataSyncRecentStateEnum, DbDataSyncRunningStateEnum } from './enums';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DataSyncTaskEdit = defineAsyncComponent(() => import('./SyncTaskEdit.vue'));
 | 
					const DataSyncTaskEdit = defineAsyncComponent(() => import('./SyncTaskEdit.vue'));
 | 
				
			||||||
const DataSyncTaskLog = defineAsyncComponent(() => import('./SyncTaskLog.vue'));
 | 
					const DataSyncTaskLog = defineAsyncComponent(() => import('./SyncTaskLog.vue'));
 | 
				
			||||||
@@ -75,7 +71,8 @@ const searchItems = [SearchItem.input('name', '名称')];
 | 
				
			|||||||
// 任务名、修改人、修改时间、最近一次任务执行状态、状态(停用启用)、操作
 | 
					// 任务名、修改人、修改时间、最近一次任务执行状态、状态(停用启用)、操作
 | 
				
			||||||
const columns = ref([
 | 
					const columns = ref([
 | 
				
			||||||
    TableColumn.new('taskName', '任务名'),
 | 
					    TableColumn.new('taskName', '任务名'),
 | 
				
			||||||
    TableColumn.new('recentState', '最近任务状态').alignCenter().isSlot(),
 | 
					    TableColumn.new('runningState', '运行状态').alignCenter().typeTag(DbDataSyncRunningStateEnum),
 | 
				
			||||||
 | 
					    TableColumn.new('recentState', '最近任务状态').alignCenter().typeTag(DbDataSyncRecentStateEnum),
 | 
				
			||||||
    TableColumn.new('status', '状态').alignCenter().isSlot(),
 | 
					    TableColumn.new('status', '状态').alignCenter().isSlot(),
 | 
				
			||||||
    TableColumn.new('modifier', '修改人').alignCenter(),
 | 
					    TableColumn.new('modifier', '修改人').alignCenter(),
 | 
				
			||||||
    TableColumn.new('updateTime', '修改时间').alignCenter().isTime(),
 | 
					    TableColumn.new('updateTime', '修改时间').alignCenter().isTime(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,9 +23,6 @@ import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			|||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
    visible: {
 | 
					 | 
				
			||||||
        type: Boolean,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    taskId: {
 | 
					    taskId: {
 | 
				
			||||||
        type: Number,
 | 
					        type: Number,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -35,6 +32,8 @@ const props = defineProps({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dialogVisible = defineModel<boolean>('visible', { default: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const columns = ref([
 | 
					const columns = ref([
 | 
				
			||||||
    // 状态:1.成功  -1.失败
 | 
					    // 状态:1.成功  -1.失败
 | 
				
			||||||
    TableColumn.new('status', '状态').alignCenter().isSlot(),
 | 
					    TableColumn.new('status', '状态').alignCenter().isSlot(),
 | 
				
			||||||
@@ -44,14 +43,15 @@ const columns = ref([
 | 
				
			|||||||
    TableColumn.new('resNum', '数据条数'),
 | 
					    TableColumn.new('resNum', '数据条数'),
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(props, async (newValue: any) => {
 | 
					watch(dialogVisible, (newValue: any) => {
 | 
				
			||||||
    state.dialogVisible = newValue.visible;
 | 
					    if (!newValue) {
 | 
				
			||||||
    if (!state.dialogVisible) {
 | 
					 | 
				
			||||||
        state.polling = false;
 | 
					        state.polling = false;
 | 
				
			||||||
        watchPolling(false);
 | 
					        watchPolling(false);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state.query.taskId = props.taskId!;
 | 
					    state.query.taskId = props.taskId!;
 | 
				
			||||||
 | 
					    search();
 | 
				
			||||||
    state.realTime = props.running;
 | 
					    state.realTime = props.running;
 | 
				
			||||||
    watchPolling(props.running);
 | 
					    watchPolling(props.running);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -77,14 +77,6 @@ const watchPolling = (polling: boolean) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(
 | 
					 | 
				
			||||||
    () => props.taskId,
 | 
					 | 
				
			||||||
    async (newValue: any) => {
 | 
					 | 
				
			||||||
        state.query.taskId = newValue!;
 | 
					 | 
				
			||||||
        search();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const logTableRef: Ref<any> = ref(null);
 | 
					const logTableRef: Ref<any> = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const search = () => {
 | 
					const search = () => {
 | 
				
			||||||
@@ -98,13 +90,12 @@ const search = () => {
 | 
				
			|||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
 | 
					const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
 | 
				
			||||||
//定义事件
 | 
					//定义事件
 | 
				
			||||||
const cancel = () => {
 | 
					const cancel = () => {
 | 
				
			||||||
    emit('update:visible', false);
 | 
					    dialogVisible.value = false;
 | 
				
			||||||
    emit('cancel');
 | 
					    emit('cancel');
 | 
				
			||||||
    watchPolling(false);
 | 
					    watchPolling(false);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
    dialogVisible: false,
 | 
					 | 
				
			||||||
    polling: false,
 | 
					    polling: false,
 | 
				
			||||||
    pollingIndex: 0 as any,
 | 
					    pollingIndex: 0 as any,
 | 
				
			||||||
    realTime: props.running,
 | 
					    realTime: props.running,
 | 
				
			||||||
@@ -119,5 +110,5 @@ const state = reactive({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { dialogVisible, query, realTime } = toRefs(state);
 | 
					const { query, realTime } = toRefs(state);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,3 +8,14 @@ export const DbSqlExecTypeEnum = {
 | 
				
			|||||||
    Query: EnumValue.of(4, 'QUERY').setTagColor('#A8DEE0'),
 | 
					    Query: EnumValue.of(4, 'QUERY').setTagColor('#A8DEE0'),
 | 
				
			||||||
    Other: EnumValue.of(-1, 'OTHER').setTagColor('#F9E2AE'),
 | 
					    Other: EnumValue.of(-1, 'OTHER').setTagColor('#F9E2AE'),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DbDataSyncRecentStateEnum = {
 | 
				
			||||||
 | 
					    Success: EnumValue.of(1, '成功').setTagType('success'),
 | 
				
			||||||
 | 
					    Fail: EnumValue.of(-1, '失败').setTagType('danger'),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DbDataSyncRunningStateEnum = {
 | 
				
			||||||
 | 
					    Success: EnumValue.of(1, '运行中').setTagType('success'),
 | 
				
			||||||
 | 
					    Wait: EnumValue.of(2, '待运行').setTagType('primary'),
 | 
				
			||||||
 | 
					    Fail: EnumValue.of(3, '已停止').setTagType('danger'),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -351,7 +351,7 @@ func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []str
 | 
				
			|||||||
		writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表记录: %s \n-- ----------------------------\n", table))
 | 
							writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表记录: %s \n-- ----------------------------\n", table))
 | 
				
			||||||
		writer.WriteString("BEGIN;\n")
 | 
							writer.WriteString("BEGIN;\n")
 | 
				
			||||||
		insertSql := "INSERT INTO %s VALUES (%s);\n"
 | 
							insertSql := "INSERT INTO %s VALUES (%s);\n"
 | 
				
			||||||
		dbMeta.WalkTableRecord(table, func(record map[string]any, columns []*dbm.QueryColumn) {
 | 
							dbMeta.WalkTableRecord(table, func(record map[string]any, columns []*dbm.QueryColumn) error {
 | 
				
			||||||
			var values []string
 | 
								var values []string
 | 
				
			||||||
			writer.TryFlush()
 | 
								writer.TryFlush()
 | 
				
			||||||
			for _, column := range columns {
 | 
								for _, column := range columns {
 | 
				
			||||||
@@ -369,6 +369,7 @@ func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []str
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			writer.WriteString(fmt.Sprintf(insertSql, quotedTable, strings.Join(values, ", ")))
 | 
								writer.WriteString(fmt.Sprintf(insertSql, quotedTable, strings.Join(values, ", ")))
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		writer.WriteString("COMMIT;\n")
 | 
							writer.WriteString("COMMIT;\n")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,17 +2,18 @@ package application
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/internal/db/dbm"
 | 
						"mayfly-go/internal/db/dbm"
 | 
				
			||||||
	"mayfly-go/internal/db/domain/entity"
 | 
						"mayfly-go/internal/db/domain/entity"
 | 
				
			||||||
	"mayfly-go/internal/db/domain/repository"
 | 
						"mayfly-go/internal/db/domain/repository"
 | 
				
			||||||
	"mayfly-go/pkg/base"
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
						"mayfly-go/pkg/errorx"
 | 
				
			||||||
	"mayfly-go/pkg/gormx"
 | 
						"mayfly-go/pkg/gormx"
 | 
				
			||||||
	"mayfly-go/pkg/logx"
 | 
						"mayfly-go/pkg/logx"
 | 
				
			||||||
	"mayfly-go/pkg/model"
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
	"mayfly-go/pkg/scheduler"
 | 
						"mayfly-go/pkg/scheduler"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,7 +39,7 @@ type DataSyncTask interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	RemoveCronJobByKey(taskKey string)
 | 
						RemoveCronJobByKey(taskKey string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RunCronJob(id uint64)
 | 
						RunCronJob(id uint64) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GetTaskLogList(condition *entity.DataSyncLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
 | 
						GetTaskLogList(condition *entity.DataSyncLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -80,7 +81,9 @@ func (app *dataSyncAppImpl) AddCronJob(taskEntity *entity.DataSyncTask) {
 | 
				
			|||||||
	// 根据状态添加新的任务
 | 
						// 根据状态添加新的任务
 | 
				
			||||||
	if taskEntity.Status == entity.DataSyncTaskStatusEnable {
 | 
						if taskEntity.Status == entity.DataSyncTaskStatusEnable {
 | 
				
			||||||
		scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
 | 
							scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
 | 
				
			||||||
			go app.RunCronJob(taskEntity.Id)
 | 
								if err := app.RunCronJob(taskEntity.Id); err != nil {
 | 
				
			||||||
 | 
									logx.Errorf("定时执行数据同步任务失败: %s", err.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -120,51 +123,21 @@ func (app *dataSyncAppImpl) changeRunningState(id uint64, state int8) {
 | 
				
			|||||||
	_ = app.UpdateById(context.Background(), task)
 | 
						_ = app.UpdateById(context.Background(), task)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (app *dataSyncAppImpl) RunCronJob(id uint64) {
 | 
					func (app *dataSyncAppImpl) RunCronJob(id uint64) error {
 | 
				
			||||||
	// 查询最新的任务信息
 | 
						// 查询最新的任务信息
 | 
				
			||||||
	task, err := app.GetById(new(entity.DataSyncTask), id)
 | 
						task, err := app.GetById(new(entity.DataSyncTask), id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logx.Warnf("[%d]任务不存在", id)
 | 
							return errorx.NewBiz("任务不存在")
 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if task.RunningState == entity.DataSyncTaskRunStateRunning {
 | 
						if task.RunningState == entity.DataSyncTaskRunStateRunning {
 | 
				
			||||||
		logx.Warnf("数据同步任务正在执行中:%s => %s", task.TaskName, task.TaskKey)
 | 
							return errorx.NewBiz("该任务正在执行中")
 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 开始运行时,修改状态为运行中
 | 
						// 开始运行时,修改状态为运行中
 | 
				
			||||||
	app.changeRunningState(id, entity.DataSyncTaskRunStateRunning)
 | 
						app.changeRunningState(id, entity.DataSyncTaskRunStateRunning)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logx.Warnf("开始执行数据同步任务:%s => %s", task.TaskName, task.TaskKey)
 | 
						logx.Infof("开始执行数据同步任务:%s => %s", task.TaskName, task.TaskKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 获取源数据库连接
 | 
						go func() {
 | 
				
			||||||
	srcConn, err := GetDbApp().GetDbConn(uint64(task.SrcDbId), task.SrcDbName)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		app.endRunning(task, entity.DataSyncTaskStateFail, "连接源数据库失败", "", 0)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 获取目标数据库连接
 | 
					 | 
				
			||||||
	targetConn, err := GetDbApp().GetDbConn(uint64(task.TargetDbId), task.TargetDbName)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		app.endRunning(task, entity.DataSyncTaskStateFail, "连接目标数据库失败", "", 0)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 当前分页
 | 
					 | 
				
			||||||
	page := 1
 | 
					 | 
				
			||||||
	// 记录每次分页返回数据条数
 | 
					 | 
				
			||||||
	resSize := task.PageSize
 | 
					 | 
				
			||||||
	// 记录本次同步数据总数
 | 
					 | 
				
			||||||
	total := 0
 | 
					 | 
				
			||||||
	srcDialect := srcConn.GetDialect()
 | 
					 | 
				
			||||||
	// 记录更新字段最新值
 | 
					 | 
				
			||||||
	updFieldVal := task.UpdFieldVal
 | 
					 | 
				
			||||||
	targetDialect := targetConn.GetDialect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		if resSize < task.PageSize {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// 通过占位符格式化sql
 | 
							// 通过占位符格式化sql
 | 
				
			||||||
		updSql := ""
 | 
							updSql := ""
 | 
				
			||||||
		orderSql := ""
 | 
							orderSql := ""
 | 
				
			||||||
@@ -172,54 +145,113 @@ func (app *dataSyncAppImpl) RunCronJob(id uint64) {
 | 
				
			|||||||
			updSql = fmt.Sprintf("and %s > '%s'", task.UpdField, task.UpdFieldVal)
 | 
								updSql = fmt.Sprintf("and %s > '%s'", task.UpdField, task.UpdFieldVal)
 | 
				
			||||||
			orderSql = "order by " + task.UpdField + " asc "
 | 
								orderSql = "order by " + task.UpdField + " asc "
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		pageSql := srcDialect.PageSql(page, task.PageSize)
 | 
					 | 
				
			||||||
		// 组装查询sql
 | 
							// 组装查询sql
 | 
				
			||||||
		sql := fmt.Sprintf("select * from (%s) t where 1 = 1 %s %s %s", task.DataSql, updSql, orderSql, pageSql)
 | 
							sql := fmt.Sprintf("select * from (%s) t where 1 = 1 %s %s", task.DataSql, updSql, orderSql)
 | 
				
			||||||
		logx.Infof("同步任务:[%s],执行sql:[%s]", task.TaskName, sql)
 | 
					
 | 
				
			||||||
		// 源数据库执行sql查询结果
 | 
							err = app.doDataSync(sql, task)
 | 
				
			||||||
		columns, res, err := srcConn.Query(sql)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			app.endRunning(task, entity.DataSyncTaskStateFail, fmt.Sprintf("查询源数据库失败:%s", err.Error()), sql, 0)
 | 
								app.endRunning(task, entity.DataSyncTaskStateFail, fmt.Sprintf("执行失败: %s", err.Error()), sql, 0)
 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(res) == 0 {
 | 
						}()
 | 
				
			||||||
			app.endRunning(task, entity.DataSyncTaskStateSuccess, fmt.Sprintf("执行成功,新数据:%d 条", total), sql, 0)
 | 
					
 | 
				
			||||||
			return
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		// 每次分页查询成功后,记录一些数据
 | 
					
 | 
				
			||||||
		resSize = len(res)
 | 
					func (app *dataSyncAppImpl) doDataSync(sql string, task *entity.DataSyncTask) error {
 | 
				
			||||||
		total += resSize
 | 
						// 获取源数据库连接
 | 
				
			||||||
		page++
 | 
						srcConn, err := GetDbApp().GetDbConn(uint64(task.SrcDbId), task.SrcDbName)
 | 
				
			||||||
		index := 0
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("连接源数据库失败: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取目标数据库连接
 | 
				
			||||||
 | 
						targetConn, err := GetDbApp().GetDbConn(uint64(task.TargetDbId), task.TargetDbName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("连接目标数据库失败: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						targetDbTx, err := targetConn.Begin()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("开启目标数据库事务失败: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								targetDbTx.Rollback()
 | 
				
			||||||
 | 
								err = fmt.Errorf("%v", r)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srcDialect := srcConn.GetDialect()
 | 
				
			||||||
 | 
						// 记录更新字段最新值
 | 
				
			||||||
 | 
						targetDialect := targetConn.GetDialect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// task.FieldMap为json数组字符串 [{"src":"id","target":"id"}],转为map
 | 
						// task.FieldMap为json数组字符串 [{"src":"id","target":"id"}],转为map
 | 
				
			||||||
	var fieldMap []map[string]string
 | 
						var fieldMap []map[string]string
 | 
				
			||||||
	err = json.Unmarshal([]byte(task.FieldMap), &fieldMap)
 | 
						err = json.Unmarshal([]byte(task.FieldMap), &fieldMap)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			app.endRunning(task, entity.DataSyncTaskStateFail, "解析字段映射json出错", sql, resSize)
 | 
							return errorx.NewBiz("解析字段映射json出错: %s", err.Error())
 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						var updFieldType dbm.DataType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 记录本次同步数据总数
 | 
				
			||||||
 | 
						total := 0
 | 
				
			||||||
 | 
						batchSize := task.PageSize
 | 
				
			||||||
 | 
						result := make([]map[string]any, 0)
 | 
				
			||||||
 | 
						var queryColumns []*dbm.QueryColumn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = srcConn.WalkQueryRows(context.Background(), sql, func(row map[string]any, columns []*dbm.QueryColumn) error {
 | 
				
			||||||
 | 
							if len(queryColumns) == 0 {
 | 
				
			||||||
 | 
								queryColumns = columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// 遍历columns 取task.UpdField的字段类型
 | 
								// 遍历columns 取task.UpdField的字段类型
 | 
				
			||||||
		updFieldType := dbm.DataTypeString
 | 
								updFieldType = dbm.DataTypeString
 | 
				
			||||||
			for _, column := range columns {
 | 
								for _, column := range columns {
 | 
				
			||||||
				if column.Name == task.UpdField {
 | 
									if column.Name == task.UpdField {
 | 
				
			||||||
					updFieldType = srcDialect.GetDataType(column.Type)
 | 
										updFieldType = srcDialect.GetDataType(column.Type)
 | 
				
			||||||
					break
 | 
										break
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							total++
 | 
				
			||||||
 | 
							result = append(result, row)
 | 
				
			||||||
 | 
							if total%batchSize == 0 {
 | 
				
			||||||
 | 
								if err := app.srcData2TargetDb(result, fieldMap, updFieldType, task, srcDialect, targetDialect, targetDbTx); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								result = result[:0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							targetDbTx.Rollback()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 处理剩余的数据
 | 
				
			||||||
 | 
						if len(result) > 0 {
 | 
				
			||||||
 | 
							if err := app.srcData2TargetDb(result, fieldMap, updFieldType, task, srcDialect, targetDialect, targetDbTx); err != nil {
 | 
				
			||||||
 | 
								targetDbTx.Rollback()
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := targetDbTx.Commit(); err != nil {
 | 
				
			||||||
 | 
							return errorx.NewBiz("数据同步-目标数据库事务提交失败: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						logx.Infof("同步任务:[%s],执行完毕,保存记录成功:[%d]条", task.TaskName, total)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 保存执行成功日志
 | 
				
			||||||
 | 
						app.endRunning(task, entity.DataSyncTaskStateSuccess, fmt.Sprintf("本次任务执行成功,新数据:%d 条", total), "", total)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (app *dataSyncAppImpl) srcData2TargetDb(srcRes []map[string]any, fieldMap []map[string]string, updFieldType dbm.DataType, task *entity.DataSyncTask, srcDialect dbm.DbDialect, targetDialect dbm.DbDialect, targetDbTx *sql.Tx) error {
 | 
				
			||||||
	var data = make([]map[string]any, 0)
 | 
						var data = make([]map[string]any, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 遍历res,组装插入sql
 | 
						// 遍历res,组装插入sql
 | 
				
			||||||
		for _, record := range res {
 | 
						for _, record := range srcRes {
 | 
				
			||||||
			index++
 | 
					 | 
				
			||||||
			// 获取查询结果最后一条数据的UpdField字段值
 | 
					 | 
				
			||||||
			if index == resSize {
 | 
					 | 
				
			||||||
				updFieldVal = fmt.Sprintf("%v", record[task.UpdField])
 | 
					 | 
				
			||||||
				updFieldVal = srcDialect.FormatStrData(updFieldVal, updFieldType)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		var rowData = make(map[string]any)
 | 
							var rowData = make(map[string]any)
 | 
				
			||||||
		// 遍历字段映射, target字段的值为src字段取值
 | 
							// 遍历字段映射, target字段的值为src字段取值
 | 
				
			||||||
		for _, item := range fieldMap {
 | 
							for _, item := range fieldMap {
 | 
				
			||||||
@@ -232,6 +264,10 @@ func (app *dataSyncAppImpl) RunCronJob(id uint64) {
 | 
				
			|||||||
		data = append(data, rowData)
 | 
							data = append(data, rowData)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updFieldVal := fmt.Sprintf("%v", srcRes[len(srcRes)-1][task.UpdField])
 | 
				
			||||||
 | 
						updFieldVal = srcDialect.FormatStrData(updFieldVal, updFieldType)
 | 
				
			||||||
 | 
						task.UpdFieldVal = updFieldVal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 获取目标库字段数组
 | 
						// 获取目标库字段数组
 | 
				
			||||||
	targetWrapColumns := make([]string, 0)
 | 
						targetWrapColumns := make([]string, 0)
 | 
				
			||||||
	// 获取源库字段数组
 | 
						// 获取源库字段数组
 | 
				
			||||||
@@ -253,59 +289,30 @@ func (app *dataSyncAppImpl) RunCronJob(id uint64) {
 | 
				
			|||||||
		values = append(values, rawValue)
 | 
							values = append(values, rawValue)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 生成占位符字符串:如:(?,?)
 | 
					 | 
				
			||||||
		// 重复字符串并用逗号连接
 | 
					 | 
				
			||||||
		repeated := strings.Repeat("?,", len(targetWrapColumns))
 | 
					 | 
				
			||||||
		// 去除最后一个逗号,占位符由括号包裹
 | 
					 | 
				
			||||||
		placeholder := fmt.Sprintf("(%s)", strings.TrimSuffix(repeated, ","))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 目标数据库执行sql批量插入
 | 
						// 目标数据库执行sql批量插入
 | 
				
			||||||
		err = targetDialect.SaveBatch(targetConn, task.TargetTableName, strings.Join(targetWrapColumns, ","), placeholder, values)
 | 
						_, err := targetDialect.BatchInsert(targetDbTx, task.TargetTableName, targetWrapColumns, values)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			// 保存执行成功日志
 | 
							return err
 | 
				
			||||||
			logx.Errorf("保存记录失败:%s", err.Error())
 | 
					 | 
				
			||||||
			app.endRunning(task, entity.DataSyncTaskStateFail, err.Error(), sql, resSize)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 保存运行时日志
 | 
					 | 
				
			||||||
		logx.Infof("同步任务:[%s],保存记录成功:[%d]条", task.TaskName, total)
 | 
					 | 
				
			||||||
		app.saveLog(task.Id, entity.DataSyncTaskStateSuccess, fmt.Sprintf("分页执行成功,新数据:%d 条", total), sql, total)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 运行过程中,判断状态是否为已关闭,是则结束运行,否则继续运行
 | 
						// 运行过程中,判断状态是否为已关闭,是则结束运行,否则继续运行
 | 
				
			||||||
		taskParam, _ := app.GetById(new(entity.DataSyncTask), id)
 | 
						taskParam, _ := app.GetById(new(entity.DataSyncTask), task.Id)
 | 
				
			||||||
	if taskParam.RunningState == entity.DataSyncTaskRunStateStop {
 | 
						if taskParam.RunningState == entity.DataSyncTaskRunStateStop {
 | 
				
			||||||
			app.endRunning(task, entity.DataSyncTaskStateFail, "手动停止任务", sql, resSize)
 | 
							return errorx.NewBiz("该任务已被手动终止")
 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 记录一次数据状态
 | 
						return nil
 | 
				
			||||||
		taskParam = new(entity.DataSyncTask)
 | 
					 | 
				
			||||||
		taskParam.Id = task.Id
 | 
					 | 
				
			||||||
		taskParam.UpdFieldVal = updFieldVal
 | 
					 | 
				
			||||||
		taskParam.RecentState = entity.DataSyncTaskStateSuccess
 | 
					 | 
				
			||||||
		taskParam.RunningState = entity.DataSyncTaskRunStateRunning
 | 
					 | 
				
			||||||
		_ = app.UpdateById(context.Background(), taskParam)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logx.Infof("同步任务:[%s],执行完毕,保存记录成功:[%d]条", task.TaskName, total)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 记录更新字段最新值
 | 
					 | 
				
			||||||
	task.UpdFieldVal = updFieldVal
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 保存执行成功日志
 | 
					 | 
				
			||||||
	app.endRunning(task, entity.DataSyncTaskStateSuccess, fmt.Sprintf("本次任务执行成功,新数据:%d 条", total), "", total)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (app *dataSyncAppImpl) endRunning(taskEntity *entity.DataSyncTask, state int8, msg string, sql string, resNum int) {
 | 
					func (app *dataSyncAppImpl) endRunning(taskEntity *entity.DataSyncTask, state int8, msg string, sql string, resNum int) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	logx.Info(msg)
 | 
						logx.Info(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	task := new(entity.DataSyncTask)
 | 
						task := new(entity.DataSyncTask)
 | 
				
			||||||
	task.Id = taskEntity.Id
 | 
						task.Id = taskEntity.Id
 | 
				
			||||||
	task.RecentState = state
 | 
						task.RecentState = state
 | 
				
			||||||
 | 
						if state == entity.DataSyncTaskStateSuccess {
 | 
				
			||||||
		task.UpdFieldVal = taskEntity.UpdFieldVal
 | 
							task.UpdFieldVal = taskEntity.UpdFieldVal
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	task.RunningState = entity.DataSyncTaskRunStateReady
 | 
						task.RunningState = entity.DataSyncTaskRunStateReady
 | 
				
			||||||
	// 运行失败之后设置任务状态为禁用
 | 
						// 运行失败之后设置任务状态为禁用
 | 
				
			||||||
	//if state == entity.DataSyncTaskStateFail {
 | 
						//if state == entity.DataSyncTaskStateFail {
 | 
				
			||||||
@@ -315,7 +322,6 @@ func (app *dataSyncAppImpl) endRunning(taskEntity *entity.DataSyncTask, state in
 | 
				
			|||||||
	_ = app.UpdateById(context.Background(), task)
 | 
						_ = app.UpdateById(context.Background(), task)
 | 
				
			||||||
	// 保存执行日志
 | 
						// 保存执行日志
 | 
				
			||||||
	app.saveLog(taskEntity.Id, state, msg, sql, resNum)
 | 
						app.saveLog(taskEntity.Id, state, msg, sql, resNum)
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (app *dataSyncAppImpl) saveLog(taskId uint64, state int8, msg string, sql string, resNum int) {
 | 
					func (app *dataSyncAppImpl) saveLog(taskId uint64, state int8, msg string, sql string, resNum int) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,9 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 游标遍历查询结果集处理函数
 | 
				
			||||||
 | 
					type WalkQueryRowsFunc func(row map[string]any, columns []*QueryColumn) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// db实例连接信息
 | 
					// db实例连接信息
 | 
				
			||||||
type DbConn struct {
 | 
					type DbConn struct {
 | 
				
			||||||
	Id   string
 | 
						Id   string
 | 
				
			||||||
@@ -36,13 +39,21 @@ func (d *DbConn) Query(querySql string, args ...any) ([]*QueryColumn, []map[stri
 | 
				
			|||||||
// 依次返回 列信息数组(顺序),结果map,错误
 | 
					// 依次返回 列信息数组(顺序),结果map,错误
 | 
				
			||||||
func (d *DbConn) QueryContext(ctx context.Context, querySql string, args ...any) ([]*QueryColumn, []map[string]any, error) {
 | 
					func (d *DbConn) QueryContext(ctx context.Context, querySql string, args ...any) ([]*QueryColumn, []map[string]any, error) {
 | 
				
			||||||
	result := make([]map[string]any, 0, 16)
 | 
						result := make([]map[string]any, 0, 16)
 | 
				
			||||||
	columns, err := walkTableRecord(ctx, d.db, querySql, func(record map[string]any, columns []*QueryColumn) {
 | 
						var queryColumns []*QueryColumn
 | 
				
			||||||
		result = append(result, record)
 | 
					
 | 
				
			||||||
 | 
						err := d.WalkQueryRows(ctx, querySql, func(row map[string]any, columns []*QueryColumn) error {
 | 
				
			||||||
 | 
							if len(queryColumns) == 0 {
 | 
				
			||||||
 | 
								queryColumns = columns
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							result = append(result, row)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
	}, args...)
 | 
						}, args...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, wrapSqlError(err)
 | 
							return nil, nil, wrapSqlError(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return columns, result, nil
 | 
					
 | 
				
			||||||
 | 
						return queryColumns, result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 将查询结果映射至struct,可具体参考sqlx库
 | 
					// 将查询结果映射至struct,可具体参考sqlx库
 | 
				
			||||||
@@ -61,10 +72,9 @@ func (d *DbConn) Query2Struct(execSql string, dest any) error {
 | 
				
			|||||||
	return scanAll(rows, dest, false)
 | 
						return scanAll(rows, dest, false)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WalkTableRecord 遍历表记录
 | 
					// 游标方式遍历查询结果集, walkFn返回error不为nil, 则跳出遍历
 | 
				
			||||||
func (d *DbConn) WalkTableRecord(ctx context.Context, selectSql string, walk func(record map[string]any, columns []*QueryColumn)) error {
 | 
					func (d *DbConn) WalkQueryRows(ctx context.Context, querySql string, walkFn WalkQueryRowsFunc, args ...any) error {
 | 
				
			||||||
	_, err := walkTableRecord(ctx, d.db, selectSql, walk)
 | 
						return walkQueryRows(ctx, d.db, querySql, walkFn, args...)
 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 执行 update, insert, delete,建表等sql
 | 
					// 执行 update, insert, delete,建表等sql
 | 
				
			||||||
@@ -73,16 +83,44 @@ func (d *DbConn) Exec(sql string, args ...any) (int64, error) {
 | 
				
			|||||||
	return d.ExecContext(context.Background(), sql, args...)
 | 
						return d.ExecContext(context.Background(), sql, args...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 事务执行 update, insert, delete,建表等sql,若tx == nil,则不使用事务
 | 
				
			||||||
 | 
					// 返回影响条数和错误
 | 
				
			||||||
 | 
					func (d *DbConn) TxExec(tx *sql.Tx, execSql string, args ...any) (int64, error) {
 | 
				
			||||||
 | 
						return d.TxExecContext(context.Background(), tx, execSql, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 执行 update, insert, delete,建表等sql
 | 
					// 执行 update, insert, delete,建表等sql
 | 
				
			||||||
// 返回影响条数和错误
 | 
					// 返回影响条数和错误
 | 
				
			||||||
func (d *DbConn) ExecContext(ctx context.Context, sql string, args ...any) (int64, error) {
 | 
					func (d *DbConn) ExecContext(ctx context.Context, execSql string, args ...any) (int64, error) {
 | 
				
			||||||
	res, err := d.db.ExecContext(ctx, sql, args...)
 | 
						res, err := d.db.ExecContext(ctx, execSql, args...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, wrapSqlError(err)
 | 
							return 0, wrapSqlError(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return res.RowsAffected()
 | 
						return res.RowsAffected()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 事务执行 update, insert, delete,建表等sql,若tx == nil,则不适用事务
 | 
				
			||||||
 | 
					// 返回影响条数和错误
 | 
				
			||||||
 | 
					func (d *DbConn) TxExecContext(ctx context.Context, tx *sql.Tx, execSql string, args ...any) (int64, error) {
 | 
				
			||||||
 | 
						var res sql.Result
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if tx != nil {
 | 
				
			||||||
 | 
							res, err = tx.ExecContext(ctx, execSql, args...)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							res, err = d.db.ExecContext(ctx, execSql, args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, wrapSqlError(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res.RowsAffected()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 开启事务
 | 
				
			||||||
 | 
					func (d *DbConn) Begin() (*sql.Tx, error) {
 | 
				
			||||||
 | 
						return d.db.Begin()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取数据库元信息实现接口
 | 
					// 获取数据库元信息实现接口
 | 
				
			||||||
func (d *DbConn) GetDialect() DbDialect {
 | 
					func (d *DbConn) GetDialect() DbDialect {
 | 
				
			||||||
	switch d.Info.Type {
 | 
						switch d.Info.Type {
 | 
				
			||||||
@@ -111,11 +149,12 @@ func (d *DbConn) Close() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func walkTableRecord(ctx context.Context, db *sql.DB, selectSql string, walk func(record map[string]any, columns []*QueryColumn), args ...any) ([]*QueryColumn, error) {
 | 
					// 游标方式遍历查询rows, walkFn error不为nil, 则跳出遍历
 | 
				
			||||||
 | 
					func walkQueryRows(ctx context.Context, db *sql.DB, selectSql string, walkFn WalkQueryRowsFunc, args ...any) error {
 | 
				
			||||||
	rows, err := db.QueryContext(ctx, selectSql, args...)
 | 
						rows, err := db.QueryContext(ctx, selectSql, args...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
 | 
						// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
 | 
				
			||||||
	// 后面的链接过来直接报错或拒绝,实际上也没有起效果
 | 
						// 后面的链接过来直接报错或拒绝,实际上也没有起效果
 | 
				
			||||||
@@ -127,7 +166,7 @@ func walkTableRecord(ctx context.Context, db *sql.DB, selectSql string, walk fun
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	colTypes, err := rows.ColumnTypes()
 | 
						colTypes, err := rows.ColumnTypes()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	lenCols := len(colTypes)
 | 
						lenCols := len(colTypes)
 | 
				
			||||||
	// 列名用于前端表头名称按照数据库与查询字段顺序显示
 | 
						// 列名用于前端表头名称按照数据库与查询字段顺序显示
 | 
				
			||||||
@@ -145,7 +184,7 @@ func walkTableRecord(ctx context.Context, db *sql.DB, selectSql string, walk fun
 | 
				
			|||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
 | 
							// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
 | 
				
			||||||
		if err := rows.Scan(scans...); err != nil {
 | 
							if err := rows.Scan(scans...); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// 每行数据
 | 
							// 每行数据
 | 
				
			||||||
		rowData := make(map[string]any, lenCols)
 | 
							rowData := make(map[string]any, lenCols)
 | 
				
			||||||
@@ -153,10 +192,13 @@ func walkTableRecord(ctx context.Context, db *sql.DB, selectSql string, walk fun
 | 
				
			|||||||
		for i, v := range values {
 | 
							for i, v := range values {
 | 
				
			||||||
			rowData[colTypes[i].Name()] = valueConvert(v, colTypes[i])
 | 
								rowData[colTypes[i].Name()] = valueConvert(v, colTypes[i])
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		walk(rowData, cols)
 | 
							if err = walkFn(rowData, cols); err != nil {
 | 
				
			||||||
 | 
								logx.Error("游标遍历查询结果集出错,退出遍历: %s", err.Error())
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return cols, nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 将查询的值转为对应列类型的实际值,不全部转为字符串
 | 
					// 将查询的值转为对应列类型的实际值,不全部转为字符串
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package dbm
 | 
					package dbm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
	"mayfly-go/pkg/biz"
 | 
						"mayfly-go/pkg/biz"
 | 
				
			||||||
	"mayfly-go/pkg/utils/collx"
 | 
						"mayfly-go/pkg/utils/collx"
 | 
				
			||||||
@@ -71,12 +72,8 @@ type DbDialect interface {
 | 
				
			|||||||
	// 获取建表ddl
 | 
						// 获取建表ddl
 | 
				
			||||||
	GetTableDDL(tableName string) (string, error)
 | 
						GetTableDDL(tableName string) (string, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 获取指定表的数据-分页查询
 | 
					 | 
				
			||||||
	// @return columns: 列字段名;result: 结果集;error: 错误
 | 
					 | 
				
			||||||
	GetTableRecord(tableName string, pageNum, pageSize int) ([]*QueryColumn, []map[string]any, error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// WalkTableRecord 遍历指定表的数据
 | 
						// WalkTableRecord 遍历指定表的数据
 | 
				
			||||||
	WalkTableRecord(tableName string, walk func(record map[string]any, columns []*QueryColumn)) error
 | 
						WalkTableRecord(tableName string, walkFn WalkQueryRowsFunc) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GetSchemas() ([]string, error)
 | 
						GetSchemas() ([]string, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,11 +83,8 @@ type DbDialect interface {
 | 
				
			|||||||
	// 封装名字,如,mysql: `table_name`,  dm: "table_name"
 | 
						// 封装名字,如,mysql: `table_name`,  dm: "table_name"
 | 
				
			||||||
	WrapName(name string) string
 | 
						WrapName(name string) string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 分页sql,如:mysql: limit 1 ,10, dm: limit 10 offset 1
 | 
					 | 
				
			||||||
	PageSql(pageNum int, pageSize int) string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 批量保存数据
 | 
						// 批量保存数据
 | 
				
			||||||
	SaveBatch(conn *DbConn, tableName string, columns string, placeholder string, values [][]any) error
 | 
						BatchInsert(tx *sql.Tx, tableName string, columns []string, values [][]any) (int64, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GetDataType(dbColumnType string) DataType
 | 
						GetDataType(dbColumnType string) DataType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -255,12 +255,8 @@ func (dd *DMDialect) GetTableDDL(tableName string) (string, error) {
 | 
				
			|||||||
	return builder.String(), nil
 | 
						return builder.String(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (dd *DMDialect) GetTableRecord(tableName string, pageNum, pageSize int) ([]*QueryColumn, []map[string]any, error) {
 | 
					func (dd *DMDialect) WalkTableRecord(tableName string, walkFn WalkQueryRowsFunc) error {
 | 
				
			||||||
	return dd.dc.Query(fmt.Sprintf("SELECT * FROM %s OFFSET %d LIMIT %d", tableName, (pageNum-1)*pageSize, pageSize))
 | 
						return dd.dc.WalkQueryRows(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walkFn)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (dd *DMDialect) WalkTableRecord(tableName string, walk func(record map[string]any, columns []*QueryColumn)) error {
 | 
					 | 
				
			||||||
	return dd.dc.WalkTableRecord(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walk)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取DM当前连接的库可访问的schemaNames
 | 
					// 获取DM当前连接的库可访问的schemaNames
 | 
				
			||||||
@@ -286,10 +282,6 @@ func (pd *DMDialect) WrapName(name string) string {
 | 
				
			|||||||
	return "\"" + name + "\""
 | 
						return "\"" + name + "\""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *DMDialect) PageSql(pageNum int, pageSize int) string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("LIMIT %d OFFSET %d", pageSize, (pageNum-1)*pageSize)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (pd *DMDialect) GetDataType(dbColumnType string) DataType {
 | 
					func (pd *DMDialect) GetDataType(dbColumnType string) DataType {
 | 
				
			||||||
	if regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit`).MatchString(dbColumnType) {
 | 
						if regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit`).MatchString(dbColumnType) {
 | 
				
			||||||
		return DataTypeNumber
 | 
							return DataTypeNumber
 | 
				
			||||||
@@ -309,21 +301,29 @@ func (pd *DMDialect) GetDataType(dbColumnType string) DataType {
 | 
				
			|||||||
	return DataTypeString
 | 
						return DataTypeString
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *DMDialect) SaveBatch(conn *DbConn, tableName string, columns string, placeholder string, values [][]any) error {
 | 
					func (pd *DMDialect) BatchInsert(tx *sql.Tx, tableName string, columns []string, values [][]any) (int64, error) {
 | 
				
			||||||
	// 执行批量insert sql
 | 
						// 执行批量insert sql
 | 
				
			||||||
	// insert into "table_name" ("column1", "column2", ...) values (value1, value2, ...)
 | 
						// insert into "table_name" ("column1", "column2", ...) values (value1, value2, ...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sqlTemp := fmt.Sprintf("insert into %s (%s) values %s", pd.WrapName(tableName), columns, placeholder)
 | 
						// 生成占位符字符串:如:(?,?)
 | 
				
			||||||
 | 
						// 重复字符串并用逗号连接
 | 
				
			||||||
 | 
						repeated := strings.Repeat("?,", len(columns))
 | 
				
			||||||
 | 
						// 去除最后一个逗号,占位符由括号包裹
 | 
				
			||||||
 | 
						placeholder := fmt.Sprintf("(%s)", strings.TrimSuffix(repeated, ","))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sqlTemp := fmt.Sprintf("insert into %s (%s) values %s", pd.WrapName(tableName), strings.Join(columns, ","), placeholder)
 | 
				
			||||||
 | 
						effRows := 0
 | 
				
			||||||
	for _, value := range values {
 | 
						for _, value := range values {
 | 
				
			||||||
		// 达梦数据库只能一条条的执行insert
 | 
							// 达梦数据库只能一条条的执行insert
 | 
				
			||||||
		_, err := conn.Exec(sqlTemp, value...)
 | 
							er, err := pd.dc.TxExec(tx, sqlTemp, value...)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			logx.Errorf("执行sql失败:%s", err.Error())
 | 
								logx.Errorf("执行sql失败:%s", err.Error())
 | 
				
			||||||
			return err
 | 
								return int64(effRows), err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							effRows += int(er)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 执行批量insert sql
 | 
						// 执行批量insert sql
 | 
				
			||||||
	return nil
 | 
						return int64(effRows), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *DMDialect) FormatStrData(dbColumnValue string, dataType DataType) string {
 | 
					func (pd *DMDialect) FormatStrData(dbColumnValue string, dataType DataType) string {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -189,12 +189,8 @@ func (md *MysqlDialect) GetTableDDL(tableName string) (string, error) {
 | 
				
			|||||||
	return res[0]["Create Table"].(string) + ";", nil
 | 
						return res[0]["Create Table"].(string) + ";", nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (md *MysqlDialect) GetTableRecord(tableName string, pageNum, pageSize int) ([]*QueryColumn, []map[string]any, error) {
 | 
					func (md *MysqlDialect) WalkTableRecord(tableName string, walkFn WalkQueryRowsFunc) error {
 | 
				
			||||||
	return md.dc.Query(fmt.Sprintf("SELECT * FROM %s LIMIT %d, %d", tableName, (pageNum-1)*pageSize, pageSize))
 | 
						return md.dc.WalkQueryRows(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walkFn)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (md *MysqlDialect) WalkTableRecord(tableName string, walk func(record map[string]any, columns []*QueryColumn)) error {
 | 
					 | 
				
			||||||
	return md.dc.WalkTableRecord(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walk)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (md *MysqlDialect) GetSchemas() ([]string, error) {
 | 
					func (md *MysqlDialect) GetSchemas() ([]string, error) {
 | 
				
			||||||
@@ -210,10 +206,6 @@ func (pd *MysqlDialect) WrapName(name string) string {
 | 
				
			|||||||
	return "`" + name + "`"
 | 
						return "`" + name + "`"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *MysqlDialect) PageSql(pageNum int, pageSize int) string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("limit %d, %d", (pageNum-1)*pageSize, pageSize)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (pd *MysqlDialect) GetDataType(dbColumnType string) DataType {
 | 
					func (pd *MysqlDialect) GetDataType(dbColumnType string) DataType {
 | 
				
			||||||
	if regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit`).MatchString(dbColumnType) {
 | 
						if regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit`).MatchString(dbColumnType) {
 | 
				
			||||||
		return DataTypeNumber
 | 
							return DataTypeNumber
 | 
				
			||||||
@@ -233,24 +225,29 @@ func (pd *MysqlDialect) GetDataType(dbColumnType string) DataType {
 | 
				
			|||||||
	return DataTypeString
 | 
						return DataTypeString
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *MysqlDialect) SaveBatch(conn *DbConn, tableName string, columns string, placeholder string, values [][]any) error {
 | 
					func (pd *MysqlDialect) BatchInsert(tx *sql.Tx, tableName string, columns []string, values [][]any) (int64, error) {
 | 
				
			||||||
 | 
						// 生成占位符字符串:如:(?,?)
 | 
				
			||||||
 | 
						// 重复字符串并用逗号连接
 | 
				
			||||||
 | 
						repeated := strings.Repeat("?,", len(columns))
 | 
				
			||||||
 | 
						// 去除最后一个逗号,占位符由括号包裹
 | 
				
			||||||
 | 
						placeholder := fmt.Sprintf("(%s)", strings.TrimSuffix(repeated, ","))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 执行批量insert sql,mysql支持批量insert语法
 | 
						// 执行批量insert sql,mysql支持批量insert语法
 | 
				
			||||||
	// insert into table_name (column1, column2, ...) values (value1, value2, ...), (value1, value2, ...), ...
 | 
						// insert into table_name (column1, column2, ...) values (value1, value2, ...), (value1, value2, ...), ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 重复占位符字符串n遍
 | 
						// 重复占位符字符串n遍
 | 
				
			||||||
	repeated := strings.Repeat(placeholder+",", len(values))
 | 
						repeated = strings.Repeat(placeholder+",", len(values))
 | 
				
			||||||
	// 去除最后一个逗号
 | 
						// 去除最后一个逗号
 | 
				
			||||||
	placeholder = strings.TrimSuffix(repeated, ",")
 | 
						placeholder = strings.TrimSuffix(repeated, ",")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sqlStr := fmt.Sprintf("insert into %s (%s) values %s", pd.WrapName(tableName), columns, placeholder)
 | 
						sqlStr := fmt.Sprintf("insert into %s (%s) values %s", pd.WrapName(tableName), strings.Join(columns, ","), placeholder)
 | 
				
			||||||
	// 执行批量insert sql
 | 
						// 执行批量insert sql
 | 
				
			||||||
	// 把二维数组转为一维数组
 | 
						// 把二维数组转为一维数组
 | 
				
			||||||
	var args []any
 | 
						var args []any
 | 
				
			||||||
	for _, v := range values {
 | 
						for _, v := range values {
 | 
				
			||||||
		args = append(args, v...)
 | 
							args = append(args, v...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err := conn.Exec(sqlStr, args...)
 | 
						return pd.dc.TxExec(tx, sqlStr, args...)
 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *MysqlDialect) FormatStrData(dbColumnValue string, dataType DataType) string {
 | 
					func (pd *MysqlDialect) FormatStrData(dbColumnValue string, dataType DataType) string {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -257,12 +257,8 @@ func (pd *PgsqlDialect) GetTableDDL(tableName string) (string, error) {
 | 
				
			|||||||
	return res[0]["sql"].(string), nil
 | 
						return res[0]["sql"].(string), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *PgsqlDialect) GetTableRecord(tableName string, pageNum, pageSize int) ([]*QueryColumn, []map[string]any, error) {
 | 
					func (pd *PgsqlDialect) WalkTableRecord(tableName string, walkFn WalkQueryRowsFunc) error {
 | 
				
			||||||
	return pd.dc.Query(fmt.Sprintf("SELECT * FROM %s OFFSET %d LIMIT %d", tableName, (pageNum-1)*pageSize, pageSize))
 | 
						return pd.dc.WalkQueryRows(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walkFn)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (pd *PgsqlDialect) WalkTableRecord(tableName string, walk func(record map[string]any, columns []*QueryColumn)) error {
 | 
					 | 
				
			||||||
	return pd.dc.WalkTableRecord(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walk)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取pgsql当前连接的库可访问的schemaNames
 | 
					// 获取pgsql当前连接的库可访问的schemaNames
 | 
				
			||||||
@@ -288,10 +284,6 @@ func (pd *PgsqlDialect) WrapName(name string) string {
 | 
				
			|||||||
	return name
 | 
						return name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *PgsqlDialect) PageSql(pageNum int, pageSize int) string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("LIMIT %d OFFSET %d", pageSize, (pageNum-1)*pageSize)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (pd *PgsqlDialect) GetDataType(dbColumnType string) DataType {
 | 
					func (pd *PgsqlDialect) GetDataType(dbColumnType string) DataType {
 | 
				
			||||||
	if regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit`).MatchString(dbColumnType) {
 | 
						if regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit`).MatchString(dbColumnType) {
 | 
				
			||||||
		return DataTypeNumber
 | 
							return DataTypeNumber
 | 
				
			||||||
@@ -311,24 +303,33 @@ func (pd *PgsqlDialect) GetDataType(dbColumnType string) DataType {
 | 
				
			|||||||
	return DataTypeString
 | 
						return DataTypeString
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *PgsqlDialect) SaveBatch(conn *DbConn, tableName string, columns string, placeholder string, values [][]any) error {
 | 
					func (pd *PgsqlDialect) BatchInsert(tx *sql.Tx, tableName string, columns []string, values [][]any) (int64, error) {
 | 
				
			||||||
	// 执行批量insert sql,跟mysql一样  pg或高斯支持批量insert语法
 | 
						// 执行批量insert sql,跟mysql一样  pg或高斯支持批量insert语法
 | 
				
			||||||
	// insert into table_name (column1, column2, ...) values (value1, value2, ...), (value1, value2, ...), ...
 | 
						// insert into table_name (column1, column2, ...) values (value1, value2, ...), (value1, value2, ...), ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 生成占位符字符串:如:(?,?)
 | 
				
			||||||
 | 
						// 重复字符串并用逗号连接
 | 
				
			||||||
 | 
						repeated := strings.Repeat("?,", len(columns))
 | 
				
			||||||
 | 
						// 去除最后一个逗号,占位符由括号包裹
 | 
				
			||||||
 | 
						placeholder := fmt.Sprintf("(%s)", strings.TrimSuffix(repeated, ","))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 执行批量insert sql,mysql支持批量insert语法
 | 
				
			||||||
 | 
						// insert into table_name (column1, column2, ...) values (value1, value2, ...), (value1, value2, ...), ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 重复占位符字符串n遍
 | 
						// 重复占位符字符串n遍
 | 
				
			||||||
	repeated := strings.Repeat(placeholder+",", len(values))
 | 
						repeated = strings.Repeat(placeholder+",", len(values))
 | 
				
			||||||
	// 去除最后一个逗号
 | 
						// 去除最后一个逗号
 | 
				
			||||||
	placeholder = strings.TrimSuffix(repeated, ",")
 | 
						placeholder = strings.TrimSuffix(repeated, ",")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sqlStr := fmt.Sprintf("insert into %s (%s) values %s", pd.WrapName(tableName), columns, placeholder)
 | 
						sqlStr := fmt.Sprintf("insert into %s (%s) values %s", pd.WrapName(tableName), strings.Join(columns, ","), placeholder)
 | 
				
			||||||
	// 执行批量insert sql
 | 
						// 执行批量insert sql
 | 
				
			||||||
	// 把二维数组转为一维数组
 | 
						// 把二维数组转为一维数组
 | 
				
			||||||
	var args []any
 | 
						var args []any
 | 
				
			||||||
	for _, v := range values {
 | 
						for _, v := range values {
 | 
				
			||||||
		args = append(args, v...)
 | 
							args = append(args, v...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err := conn.Exec(sqlStr, args...)
 | 
					
 | 
				
			||||||
	return err
 | 
						return pd.dc.TxExec(tx, sqlStr, args...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pd *PgsqlDialect) FormatStrData(dbColumnValue string, dataType DataType) string {
 | 
					func (pd *PgsqlDialect) FormatStrData(dbColumnValue string, dataType DataType) string {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user