From fc1b9ef35d1a081c6ebe2bef988bcc79e5a1c95c Mon Sep 17 00:00:00 2001 From: zongyangleo Date: Tue, 30 Jan 2024 13:09:26 +0000 Subject: [PATCH] =?UTF-8?q?!97=20=E4=B8=80=E4=BA=9B=E4=BC=98=E5=8C=96=20*?= =?UTF-8?q?=20refactor:=20=E9=87=8D=E6=9E=84=E8=A1=A8=E6=A0=BC=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E7=BB=84=E4=BB=B6=EF=BC=8C=E9=80=82=E9=85=8D=E5=A4=A7?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=87=8F=E5=88=86=E9=A1=B5=20*=20fix?= =?UTF-8?q?=EF=BC=9A=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20*=20feat:=20gaussdb=E5=8D=95=E7=8B=AC=E6=8F=90=E5=87=BA?= =?UTF-8?q?=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mayfly_go_web/src/assets/iconfont/iconfont.js | 2 +- .../src/assets/iconfont/iconfont.json | 7 + .../src/views/ops/db/InstanceEdit.vue | 11 +- mayfly_go_web/src/views/ops/db/SqlExec.vue | 2 +- .../ops/db/component/table/DbTableDataOp.vue | 138 ++++++++++++++---- .../src/views/ops/db/dialect/gauss_dialect.ts | 17 +++ .../src/views/ops/db/dialect/index.ts | 5 +- server/internal/db/api/db_data_sync.go | 10 +- server/internal/db/application/db.go | 2 +- .../internal/db/application/db_data_sync.go | 24 ++- server/internal/db/dbm/dbi/db_type.go | 15 +- server/internal/db/dbm/postgres/meta.go | 11 +- 12 files changed, 183 insertions(+), 61 deletions(-) create mode 100644 mayfly_go_web/src/views/ops/db/dialect/gauss_dialect.ts diff --git a/mayfly_go_web/src/assets/iconfont/iconfont.js b/mayfly_go_web/src/assets/iconfont/iconfont.js index ba16f984..a5702aac 100644 --- a/mayfly_go_web/src/assets/iconfont/iconfont.js +++ b/mayfly_go_web/src/assets/iconfont/iconfont.js @@ -1,5 +1,5 @@ (window._iconfont_svg_string_3953964 = - ''), + ''), (function (c) { var t = (t = document.getElementsByTagName('script'))[t.length - 1], a = t.getAttribute('data-injectcss'), diff --git a/mayfly_go_web/src/assets/iconfont/iconfont.json b/mayfly_go_web/src/assets/iconfont/iconfont.json index bf0a0409..2df2c56f 100644 --- a/mayfly_go_web/src/assets/iconfont/iconfont.json +++ b/mayfly_go_web/src/assets/iconfont/iconfont.json @@ -81,6 +81,13 @@ "font_class": "MSSQLNATIVE", "unicode": "e600", "unicode_decimal": 58880 + }, + { + "icon_id": "7699332", + "name": "gaussdb", + "font_class": "gauss", + "unicode": "e683", + "unicode_decimal": 59011 } ] } diff --git a/mayfly_go_web/src/views/ops/db/InstanceEdit.vue b/mayfly_go_web/src/views/ops/db/InstanceEdit.vue index 549703b6..710c5b7d 100644 --- a/mayfly_go_web/src/views/ops/db/InstanceEdit.vue +++ b/mayfly_go_web/src/views/ops/db/InstanceEdit.vue @@ -9,13 +9,18 @@ - - + + {{ dbTypeAndDialect[1].getInfo().name }} diff --git a/mayfly_go_web/src/views/ops/db/SqlExec.vue b/mayfly_go_web/src/views/ops/db/SqlExec.vue index fd5ef572..1aab107c 100644 --- a/mayfly_go_web/src/views/ops/db/SqlExec.vue +++ b/mayfly_go_web/src/views/ops/db/SqlExec.vue @@ -443,7 +443,7 @@ onBeforeUnmount(() => { * 设置editor高度和数据表高度 */ const setHeight = () => { - state.dataTabsTableHeight = window.innerHeight - 270 + 'px'; + state.dataTabsTableHeight = window.innerHeight - 253 + 'px'; state.tablesOpHeight = window.innerHeight - 225 + 'px'; }; diff --git a/mayfly_go_web/src/views/ops/db/component/table/DbTableDataOp.vue b/mayfly_go_web/src/views/ops/db/component/table/DbTableDataOp.vue index 64a45fe2..3ff64f89 100644 --- a/mayfly_go_web/src/views/ops/db/component/table/DbTableDataOp.vue +++ b/mayfly_go_web/src/views/ops/db/component/table/DbTableDataOp.vue @@ -158,21 +158,62 @@ @data-delete="onRefresh" > - - + + + {{ sql }} + + + + + +
+ +
+ + +
+ + + +
+ + + + + + + 总 {{ state.total }} 条 +
+
-
- {{ state.sql }} -
@@ -242,6 +283,7 @@ import { DbDialect, getDbDialect } from '@/views/ops/db/dialect'; import SvgIcon from '@/components/svgIcon/index.vue'; import ColumnFormItem from './ColumnFormItem.vue'; import { useEventListener, useStorage } from '@vueuse/core'; +import { copyToClipboard } from '@/common/utils/string'; const props = defineProps({ dbId: { @@ -290,7 +332,10 @@ const state = reactive({ defaultPageSize * 40, defaultPageSize * 80, ], - count: 0, + setPageNum: 0, + total: 0, + showTotal: 0, + counting: false, selectionDatas: [] as any, condPopVisible: false, columnNameSearch: '', @@ -314,7 +359,7 @@ const state = reactive({ dbDialect: {} as DbDialect, }); -const { datas, condition, loading, columns, pageNum, pageSize, pageSizes, count, hasUpdatedFileds, conditionDialog, addDataDialog, dbDialect } = toRefs(state); +const { datas, condition, loading, columns, pageNum, pageSize, pageSizes, sql, hasUpdatedFileds, conditionDialog, addDataDialog, dbDialect } = toRefs(state); watch( () => props.tableHeight, @@ -347,18 +392,19 @@ const onRefresh = async () => { await selectData(); }; -/** - * 数据tab修改页数 - */ -const pageChange = async () => { - await selectData(); -}; +watch( + () => state.pageNum, + async () => { + await selectData(); + } +); /** * 单表数据信息查询数据 */ const selectData = async () => { state.loading = true; + state.setPageNum = state.pageNum; const dbInst = getNowDbInst(); const db = props.dbName; const table = props.tableName; @@ -371,16 +417,10 @@ const selectData = async () => { state.columns = columns; } - const countRes = await dbInst.runSql(db, dbInst.getDefaultCountSql(table, state.condition)); - state.count = parseInt(countRes.res[0].count || countRes.res[0].COUNT || 0); let sql = dbInst.getDefaultSelectSql(db, table, state.condition, state.orderBy, state.pageNum, state.pageSize); state.sql = sql; - if (state.count > 0) { - const colAndData: any = await dbInst.runSql(db, sql); - state.datas = colAndData.res; - } else { - state.datas = []; - } + const colAndData: any = await dbInst.runSql(db, sql); + state.datas = colAndData.res; } finally { state.loading = false; } @@ -392,6 +432,38 @@ const handleSizeChange = async (size: any) => { await selectData(); }; +const handleEndPage = async () => { + await handleCount(); + state.pageNum = Math.ceil(state.total / state.pageSize); + await selectData(); +}; + +const handleSetPageNum = async () => { + state.pageNum = state.setPageNum; + await selectData(); +}; +const handleCount = async () => { + state.counting = true; + + try { + const db = props.dbName; + const table = props.tableName; + const dbInst = getNowDbInst(); + const countRes = await dbInst.runSql(db, dbInst.getDefaultCountSql(table, state.condition)); + state.total = parseInt(countRes.res[0].count || countRes.res[0].COUNT || 0); + state.showTotal = true; + } catch (e) { + /* empty */ + } + + state.counting = false; +}; + +const handleCopySql = async (sql: string) => { + await copyToClipboard(sql); + ElMessage.success('复制成功'); +}; + // 完整的条件,每次选中后会重置条件框内容,故需要这个变量在获取建议时将文本框内容保存 let completeCond = ''; // 是否存在列建议 @@ -586,4 +658,8 @@ const addRow = async () => { }; - + diff --git a/mayfly_go_web/src/views/ops/db/dialect/gauss_dialect.ts b/mayfly_go_web/src/views/ops/db/dialect/gauss_dialect.ts new file mode 100644 index 00000000..9e1e88eb --- /dev/null +++ b/mayfly_go_web/src/views/ops/db/dialect/gauss_dialect.ts @@ -0,0 +1,17 @@ +import { PostgresqlDialect } from '@/views/ops/db/dialect/postgres_dialect'; +import { DialectInfo } from '@/views/ops/db/dialect/index'; + +let gsDialectInfo: DialectInfo; +export class GaussDialect extends PostgresqlDialect { + getInfo(): DialectInfo { + if (gsDialectInfo) { + return gsDialectInfo; + } + + gsDialectInfo = {} as DialectInfo; + Object.assign(gsDialectInfo, super.getInfo()); + gsDialectInfo.icon = 'iconfont icon-gauss'; + gsDialectInfo.name = 'GaussDB'; + return gsDialectInfo; + } +} diff --git a/mayfly_go_web/src/views/ops/db/dialect/index.ts b/mayfly_go_web/src/views/ops/db/dialect/index.ts index e8416c46..91c1caab 100644 --- a/mayfly_go_web/src/views/ops/db/dialect/index.ts +++ b/mayfly_go_web/src/views/ops/db/dialect/index.ts @@ -5,6 +5,7 @@ import { OracleDialect } from '@/views/ops/db/dialect/oracle_dialect'; import { MariadbDialect } from '@/views/ops/db/dialect/mariadb_dialect'; import { SqliteDialect } from '@/views/ops/db/dialect/sqlite_dialect'; import { MssqlDialect } from '@/views/ops/db/dialect/mssql_dialect'; +import { GaussDialect } from '@/views/ops/db/dialect/gauss_dialect'; export interface sqlColumnType { udtName: string; @@ -116,6 +117,7 @@ export const DbType = { mysql: 'mysql', mariadb: 'mariadb', postgresql: 'postgres', + gauss: 'gauss', dm: 'dm', // 达梦 oracle: 'oracle', sqlite: 'sqlite', @@ -126,7 +128,7 @@ export const DbType = { export const noSchemaTypes = [DbType.mysql, DbType.mariadb, DbType.sqlite]; // 有schema层的数据库 -export const schemaDbTypes = [DbType.postgresql, DbType.dm, DbType.oracle, DbType.mssql]; +export const schemaDbTypes = [DbType.postgresql, DbType.gauss, DbType.dm, DbType.oracle, DbType.mssql]; export const editDbTypes = [...noSchemaTypes, ...schemaDbTypes]; @@ -225,6 +227,7 @@ export const getDbDialect = (dbType: string): DbDialect => { registerDbDialect(DbType.mysql, mysqlDialect); registerDbDialect(DbType.mariadb, new MariadbDialect()); registerDbDialect(DbType.postgresql, new PostgresqlDialect()); + registerDbDialect(DbType.gauss, new GaussDialect()); registerDbDialect(DbType.dm, new DMDialect()); registerDbDialect(DbType.oracle, new OracleDialect()); registerDbDialect(DbType.sqlite, new SqliteDialect()); diff --git a/server/internal/db/api/db_data_sync.go b/server/internal/db/api/db_data_sync.go index b327a034..d8b0784a 100644 --- a/server/internal/db/api/db_data_sync.go +++ b/server/internal/db/api/db_data_sync.go @@ -14,7 +14,6 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/google/uuid" ) type DataSyncTask struct { @@ -46,13 +45,6 @@ func (d *DataSyncTask) SaveTask(rc *req.Ctx) { task.DataSql = sql form.DataSql = sql - key := task.TaskKey - // 判断key为空就生成随机key - if key == "" { - key = uuid.New().String() - task.TaskKey = key - } - rc.ReqParam = form biz.ErrIsNil(d.DataSyncTaskApp.Save(rc.MetaCtx, task)) } @@ -88,7 +80,7 @@ func (d *DataSyncTask) ChangeStatus(rc *req.Ctx) { func (d *DataSyncTask) Run(rc *req.Ctx) { taskId := getTaskId(rc.GinCtx) rc.ReqParam = taskId - d.DataSyncTaskApp.RunCronJob(taskId) + _ = d.DataSyncTaskApp.RunCronJob(taskId) } func (d *DataSyncTask) Stop(rc *req.Ctx) { diff --git a/server/internal/db/application/db.go b/server/internal/db/application/db.go index 8ecf216d..ff75fad5 100644 --- a/server/internal/db/application/db.go +++ b/server/internal/db/application/db.go @@ -154,7 +154,7 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) { checkDb := dbName // 兼容pgsql/dm db/schema模式 - if dbi.DbTypePostgres.Equal(instance.Type) || dbi.DbTypeDM.Equal(instance.Type) || dbi.DbTypeOracle.Equal(instance.Type) || dbi.DbTypeMssql.Equal(instance.Type) { + if dbi.DbTypePostgres.Equal(instance.Type) || dbi.DbTypeGauss.Equal(instance.Type) || dbi.DbTypeDM.Equal(instance.Type) || dbi.DbTypeOracle.Equal(instance.Type) || dbi.DbTypeMssql.Equal(instance.Type) { ss := strings.Split(dbName, "/") if len(ss) > 1 { checkDb = ss[0] diff --git a/server/internal/db/application/db_data_sync.go b/server/internal/db/application/db_data_sync.go index 60f78c74..803b91cd 100644 --- a/server/internal/db/application/db_data_sync.go +++ b/server/internal/db/application/db_data_sync.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "fmt" + "github.com/google/uuid" "mayfly-go/internal/db/dbm/dbi" "mayfly-go/internal/db/domain/entity" "mayfly-go/internal/db/domain/repository" @@ -53,8 +54,8 @@ var ( dateTimeReg = regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`) ) -func (d *dataSyncAppImpl) InjectDbDataSyncTaskRepo(repo repository.DataSyncTask) { - d.Repo = repo +func (app *dataSyncAppImpl) InjectDbDataSyncTaskRepo(repo repository.DataSyncTask) { + app.Repo = repo } func (app *dataSyncAppImpl) GetPageList(condition *entity.DataSyncTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { @@ -64,15 +65,22 @@ func (app *dataSyncAppImpl) GetPageList(condition *entity.DataSyncTaskQuery, pag func (app *dataSyncAppImpl) Save(ctx context.Context, taskEntity *entity.DataSyncTask) error { var err error if taskEntity.Id == 0 { + // 新建时生成key + taskEntity.TaskKey = uuid.New().String() err = app.Insert(ctx, taskEntity) } else { err = app.UpdateById(ctx, taskEntity) } + if err != nil { return err } - app.AddCronJob(taskEntity) + task, err := app.GetById(new(entity.DataSyncTask), taskEntity.Id) + if err != nil { + return err + } + app.AddCronJob(task) return nil } @@ -92,9 +100,13 @@ func (app *dataSyncAppImpl) AddCronJob(taskEntity *entity.DataSyncTask) { // 根据状态添加新的任务 if taskEntity.Status == entity.DataSyncTaskStatusEnable { scheduler.AddFunByKey(key, taskEntity.TaskCron, func() { - if err := app.RunCronJob(taskEntity.Id); err != nil { - logx.Errorf("定时执行数据同步任务失败: %s", err.Error()) - } + go func() { + taskId := taskEntity.Id + logx.Infof("开始执行同步任务: %d", taskId) + if err := app.RunCronJob(taskId); err != nil { + logx.Errorf("定时执行数据同步任务失败: %s", err.Error()) + } + }() }) } } diff --git a/server/internal/db/dbm/dbi/db_type.go b/server/internal/db/dbm/dbi/db_type.go index 21b015f6..1a020fe6 100644 --- a/server/internal/db/dbm/dbi/db_type.go +++ b/server/internal/db/dbm/dbi/db_type.go @@ -14,6 +14,7 @@ const ( DbTypeMysql DbType = "mysql" DbTypeMariadb DbType = "mariadb" DbTypePostgres DbType = "postgres" + DbTypeGauss DbType = "gauss" DbTypeDM DbType = "dm" DbTypeOracle DbType = "oracle" DbTypeSqlite DbType = "sqlite" @@ -43,7 +44,7 @@ func (dbType DbType) QuoteIdentifier(name string) string { switch dbType { case DbTypeMysql, DbTypeMariadb: return quoteIdentifier(name, "`") - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: return quoteIdentifier(name, `"`) case DbTypeMssql: return fmt.Sprintf("[%s]", name) @@ -56,7 +57,7 @@ func (dbType DbType) RemoveQuote(name string) string { switch dbType { case DbTypeMysql, DbTypeMariadb: return removeQuote(name, "`") - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: return removeQuote(name, `"`) default: return removeQuote(name, `"`) @@ -69,7 +70,7 @@ func (dbType DbType) QuoteLiteral(literal string) string { literal = strings.ReplaceAll(literal, `\`, `\\`) literal = strings.ReplaceAll(literal, `'`, `''`) return "'" + literal + "'" - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: return pq.QuoteLiteral(literal) default: return pq.QuoteLiteral(literal) @@ -80,7 +81,7 @@ func (dbType DbType) MetaDbName() string { switch dbType { case DbTypeMysql, DbTypeMariadb: return "" - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: return "postgres" case DbTypeDM: return "" @@ -93,7 +94,7 @@ func (dbType DbType) Dialect() sqlparser.Dialect { switch dbType { case DbTypeMysql, DbTypeMariadb: return sqlparser.MysqlDialect{} - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: return sqlparser.PostgresDialect{} default: return sqlparser.PostgresDialect{} @@ -121,7 +122,7 @@ func (dbType DbType) StmtSetForeignKeyChecks(check bool) string { } else { return "SET FOREIGN_KEY_CHECKS = 0;\n" } - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: // not currently supported postgres return "" default: @@ -133,7 +134,7 @@ func (dbType DbType) StmtUseDatabase(dbName string) string { switch dbType { case DbTypeMysql, DbTypeMariadb: return fmt.Sprintf("USE %s;\n", dbType.QuoteIdentifier(dbName)) - case DbTypePostgres: + case DbTypePostgres, DbTypeGauss: // not currently supported postgres return "" default: diff --git a/server/internal/db/dbm/postgres/meta.go b/server/internal/db/dbm/postgres/meta.go index 56b1f004..b37878a3 100644 --- a/server/internal/db/dbm/postgres/meta.go +++ b/server/internal/db/dbm/postgres/meta.go @@ -16,13 +16,18 @@ import ( func init() { dbi.Register(dbi.DbTypePostgres, new(PostgresMeta)) + + gauss := new(PostgresMeta) + gauss.Param = "dbtype=gauss" + dbi.Register(dbi.DbTypeGauss, gauss) } type PostgresMeta struct { + Param string } func (md *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) { - driverName := string(d.Type) + driverName := "postgres" // SSH Conect if d.SshTunnelMachineId > 0 { // 如果使用了隧道,则使用`postgres:ssh:隧道机器id`注册名 @@ -67,6 +72,10 @@ func (md *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) { dsn = fmt.Sprintf("%s %s", dsn, strings.Join(strings.Split(d.Params, "&"), " ")) } + if md.Param != "" && !strings.Contains(dsn, "dbtype") { + dsn = fmt.Sprintf("%s %s", dsn, md.Param) + } + return sql.Open(driverName, dsn) }