feature: 批量导出数据库

This commit is contained in:
wanli
2023-08-31 20:05:38 +08:00
parent 618d782af3
commit b9c6ac8d6d
2 changed files with 105 additions and 15 deletions

View File

@@ -80,8 +80,8 @@
<template #more="{ data }">
<el-button @click="showInfo(data)" link>详情</el-button>
<el-button class="ml5" type="primary" @click="onShowSqlExec(data)" link>SQL执行记录</el-button>
<el-button class="ml5" type="primary" @click="onDumpDbs(data)" link>导出</el-button>
</template>
<template #action="{ data }">
@@ -180,6 +180,26 @@
</el-table>
</el-dialog>
<el-dialog width="601" :title="`${db} 数据库导出`" v-model="exportDialog.visible">
<el-form-item label="导出内容: ">
<el-radio-group v-model="dumpInfo.type">
<el-radio :label="1" size="small">结构</el-radio>
<el-radio :label="2" size="small">数据</el-radio>
<el-radio :label="3" size="small">结构+数据</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-transfer :titles="['全部数据库', '导出数据库']" max-height="300" size="small" v-model="exportDialog.value" :data="exportDialog.data">
</el-transfer>
</el-form-item>
<div style="text-align: right">
<el-button @click="exportDialog.visible = false" size="small">取消</el-button>
<el-button @click="dumpDbs()" type="success" size="small">确定</el-button>
</div>
</el-dialog>
<el-dialog
width="90%"
:title="`${sqlExecLogDialog.title} - SQL执行记录`"
@@ -313,7 +333,7 @@ const columns = ref([
TableColumn.new('name', '名称'),
TableColumn.new('database', '数据库').isSlot().setMinWidth(70),
TableColumn.new('remark', '备注'),
TableColumn.new('more', '更多').isSlot().setMinWidth(165).fixedRight(),
TableColumn.new('more', '更多').isSlot().setMinWidth(200).fixedRight(),
]);
// 该用户拥有的的操作列按钮权限
@@ -402,6 +422,13 @@ const state = reactive({
tableNameSearch: '',
tableCommentSearch: '',
},
exportDialog: {
visible: false,
dbId: 0,
type: 3,
data: [],
value: [],
},
columnDialog: {
visible: false,
columns: [],
@@ -456,6 +483,7 @@ const {
rollbackSqlDialog,
chooseTableName,
tableInfoDialog,
exportDialog,
columnDialog,
indexDialog,
ddlDialog,
@@ -613,6 +641,37 @@ const dump = (db: string) => {
state.showDumpInfo = false;
};
const onDumpDbs = async (row: any) => {
const dbs = row.database.split(' ');
const data = []
for (let name of dbs) {
data.push({
key: name,
label: name,
})
}
state.exportDialog.value = []
state.exportDialog.data = data
state.exportDialog.dbId = row.id;
state.exportDialog.visible = true;
};
/**
* 数据库信息导出
*/
const dumpDbs = () => {
isTrue(state.exportDialog.value.length > 0, '请添加要导出的数据库');
const a = document.createElement('a');
a.setAttribute(
'href',
`${config.baseApiUrl}/dbs/${state.exportDialog.dbId}/dump?db=${state.exportDialog.value.join(',')}&type=${state.exportDialog.type}&token=${getSession(
'token'
)}`
);
a.click();
state.exportDialog.visible = false;
};
const onShowRollbackSql = async (sqlExecLog: any) => {
const columns = await dbApi.columnMetadata.request({ id: sqlExecLog.dbId, db: sqlExecLog.db, tableName: sqlExecLog.table });
const primaryKey = getPrimaryKey(columns);

View File

@@ -219,38 +219,71 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
// 数据库dump
func (d *Db) DumpSql(rc *req.Ctx) {
g := rc.GinCtx
db := getDbName(g)
dbId := getDbId(g)
dbNamesStr := g.Query("db")
dumpType := g.Query("type")
tablesStr := g.Query("tables")
biz.NotEmpty(tablesStr, "请选择要导出的表")
tables := strings.Split(tablesStr, ",")
// 是否需要导出表结构
needStruct := dumpType == "1" || dumpType == "3"
// 是否需要导出数据
needData := dumpType == "2" || dumpType == "3"
dbInstance := d.getDbConnection(rc.GinCtx)
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.Info.TagPath), "%s")
db := d.DbApp.GetById(dbId)
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, db.TagPath), "%s")
now := time.Now()
filename := fmt.Sprintf("%s.%s.sql", db, now.Format("200601021504"))
filename := fmt.Sprintf("%s.%s.sql", db.Name, now.Format("20060102150405"))
g.Header("Content-Type", "application/octet-stream")
g.Header("Content-Disposition", "attachment; filename="+filename)
var dbNames, tables []string
if len(dbNamesStr) > 0 {
dbNames = strings.Split(dbNamesStr, ",")
}
if len(dbNames) == 1 && len(tablesStr) > 0 {
tables = strings.Split(tablesStr, ",")
}
writer := g.Writer
for _, dbName := range dbNames {
d.dumpDb(writer, db, dbName, tables, needStruct, needData)
}
rc.ReqParam = fmt.Sprintf("DB[id=%d, tag=%s, name=%s, databases=%s, tables=%s, dumpType=%s]", db.Id, db.TagPath, db.Name, dbNamesStr, tablesStr, dumpType)
}
func (d *Db) dumpDb(writer gin.ResponseWriter, db *entity.Db, dbName string, tables []string, needStruct bool, needData bool) {
writer.WriteString("-- ----------------------------")
writer.WriteString("\n-- 导出平台: mayfly-go")
writer.WriteString(fmt.Sprintf("\n-- 导出时间: %s ", now.Format("2006-01-02 15:04:05")))
writer.WriteString(fmt.Sprintf("\n-- 导出数据库: %s ", db))
writer.WriteString(fmt.Sprintf("\n-- 导出时间: %s ", time.Now().Format("2006-01-02 15:04:05")))
writer.WriteString(fmt.Sprintf("\n-- 导出数据库: %s ", dbName))
writer.WriteString("\n-- ----------------------------\n")
dbmeta := d.getDbConnection(rc.GinCtx).GetMeta()
instance := d.InstanceApp.GetById(db.InstanceId)
dbInst := d.DbApp.GetDbConnection(db, instance, dbName)
switch dbInst.Info.Type {
case entity.DbTypeMysql:
writer.WriteString(fmt.Sprintf("use `%s`;\n", dbName))
case entity.DbTypePostgres:
writer.WriteString(fmt.Sprintf("\\connect `%s`;\n", dbName))
default:
biz.IsTrue(false, "数据库类型必须为 MySQL 或 PostgreSQL")
}
dbMeta := dbInst.GetMeta()
if len(tables) == 0 {
ti := dbMeta.GetTableInfos()
tables = make([]string, len(ti))
for i, table := range ti {
tables[i] = table.TableName
}
}
for _, table := range tables {
if needStruct {
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", table))
writer.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS `%s`;\n", table))
writer.WriteString(dbmeta.GetCreateTableDdl(table) + ";\n")
writer.WriteString(dbMeta.GetCreateTableDdl(table) + ";\n")
}
if !needData {
@@ -262,7 +295,7 @@ func (d *Db) DumpSql(rc *req.Ctx) {
pageNum := 1
for {
columns, result, _ := dbmeta.GetTableRecord(table, pageNum, DEFAULT_ROW_SIZE)
columns, result, _ := dbMeta.GetTableRecord(table, pageNum, DEFAULT_ROW_SIZE)
resultLen := len(result)
if resultLen == 0 {
break
@@ -293,8 +326,6 @@ func (d *Db) DumpSql(rc *req.Ctx) {
writer.WriteString("COMMIT;\n")
}
rc.ReqParam = fmt.Sprintf("%s, tables: %s, dumpType: %s", dbInstance.Info.GetLogDesc(), tablesStr, dumpType)
}
// @router /api/db/:dbId/t-metadata [get]