mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 07:20:24 +08:00
feat: 新增数据库导出功能&其他小优化
This commit is contained in:
@@ -30,7 +30,7 @@ import { useStore } from '@/store/index.ts';
|
||||
export default defineComponent({
|
||||
name: 'layoutBreadcrumbSearch',
|
||||
setup() {
|
||||
const layoutMenuAutocompleteRef = ref();
|
||||
const layoutMenuAutocompleteRef: any = ref(null);
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
@@ -68,7 +68,6 @@ export default defineComponent({
|
||||
// 初始化菜单数据
|
||||
const initTageView = () => {
|
||||
if (state.tagsViewList.length > 0) return false;
|
||||
console.log(getRoutes(store.state.routesList.routesList));
|
||||
getRoutes(store.state.routesList.routesList).map((v: any) => {
|
||||
if (!v.meta.isHide) {
|
||||
state.tagsViewList.push({ ...v });
|
||||
|
||||
@@ -72,6 +72,32 @@
|
||||
|
||||
<el-dialog width="75%" :title="`${db} 表信息`" :before-close="closeTableInfo" v-model="tableInfoDialog.visible">
|
||||
<el-row class="mb10">
|
||||
<el-popover v-model:visible="showDumpInfo" :width="470" placement="right">
|
||||
<template #reference>
|
||||
<el-button class="ml5" type="success" size="small" @click="showDumpInfo = !showDumpInfo">导出</el-button>
|
||||
</template>
|
||||
<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 label="导出表: ">
|
||||
<el-table @selection-change="handleDumpTableSelectionChange" max-height="300" size="small" :data="tableInfoDialog.infos">
|
||||
<el-table-column type="selection" width="45" />
|
||||
<el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
|
||||
<div style="text-align: right">
|
||||
<el-button @click="showDumpInfo = false" size="small">取消</el-button>
|
||||
<el-button @click="dump(db)" type="success" size="small">确定</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<el-button type="primary" size="small" @click="tableCreateDialog.visible = true">创建表</el-button>
|
||||
</el-row>
|
||||
<el-table v-loading="tableInfoDialog.loading" border stripe :data="tableInfoDialog.infos" size="small">
|
||||
@@ -226,6 +252,10 @@ import { dbApi } from './api';
|
||||
import enums from './enums';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import SqlExecBox from './component/SqlExecBox.ts';
|
||||
import config from '@/common/config';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
import { isTrue } from '@/common/assert';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DbList',
|
||||
components: {
|
||||
@@ -255,6 +285,13 @@ export default defineComponent({
|
||||
},
|
||||
datas: [],
|
||||
total: 0,
|
||||
showDumpInfo: false,
|
||||
dumpInfo: {
|
||||
id: 0,
|
||||
db: '',
|
||||
type: 3,
|
||||
tables: [],
|
||||
},
|
||||
// sql执行记录弹框
|
||||
sqlExecLogDialog: {
|
||||
title: '',
|
||||
@@ -392,6 +429,29 @@ export default defineComponent({
|
||||
searchSqlExecLog();
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择导出数据库表
|
||||
*/
|
||||
const handleDumpTableSelectionChange = (vals: any) => {
|
||||
state.dumpInfo.tables = vals.map((x: any) => x.tableName);
|
||||
};
|
||||
|
||||
/**
|
||||
* 数据库信息导出
|
||||
*/
|
||||
const dump = (db: string) => {
|
||||
isTrue(state.dumpInfo.tables.length > 0, '请选择要导出的表');
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute(
|
||||
'href',
|
||||
`${config.baseApiUrl}/dbs/${state.dbId}/dump?db=${db}&type=${state.dumpInfo.type}&tables=${state.dumpInfo.tables.join(
|
||||
','
|
||||
)}&token=${getSession('token')}`
|
||||
);
|
||||
a.click();
|
||||
state.showDumpInfo = false;
|
||||
};
|
||||
|
||||
const onShowRollbackSql = async (sqlExecLog: any) => {
|
||||
const columns = await dbApi.columnMetadata.request({ id: sqlExecLog.dbId, db: sqlExecLog.db, tableName: sqlExecLog.table });
|
||||
const primaryKey = columns[0].columnName;
|
||||
@@ -447,6 +507,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const closeTableInfo = () => {
|
||||
state.showDumpInfo = false;
|
||||
state.tableInfoDialog.visible = false;
|
||||
state.tableInfoDialog.infos = [];
|
||||
};
|
||||
@@ -516,6 +577,8 @@ export default defineComponent({
|
||||
valChange,
|
||||
deleteDb,
|
||||
onShowSqlExec,
|
||||
handleDumpTableSelectionChange,
|
||||
dump,
|
||||
onBeforeCloseSqlExecDialog,
|
||||
handleSqlExecPageChange,
|
||||
searchSqlExecLog,
|
||||
|
||||
@@ -927,7 +927,7 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
// 转为字符串比较,可能存在数字等
|
||||
let text = row[property] + '';
|
||||
let text = (row[property] ? row[property] : '') + '';
|
||||
let div = cell.children[0];
|
||||
if (div) {
|
||||
let input = document.createElement('input');
|
||||
|
||||
@@ -5,6 +5,7 @@ export const dbApi = {
|
||||
dbs: Api.create("/dbs", 'get'),
|
||||
saveDb: Api.create("/dbs", 'post'),
|
||||
deleteDb: Api.create("/dbs/{id}", 'delete'),
|
||||
dumpDb: Api.create("/dbs/{id}/dump", 'post'),
|
||||
tableInfos: Api.create("/dbs/{id}/t-infos", 'get'),
|
||||
tableIndex: Api.create("/dbs/{id}/t-index", 'get'),
|
||||
tableDdl: Api.create("/dbs/{id}/t-create-ddl", 'get'),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
||||
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue" :options="cmOptions" />
|
||||
<el-input v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
@@ -14,9 +14,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import { toRefs, ref, nextTick, reactive, defineComponent } from 'vue';
|
||||
import { dbApi } from '../api';
|
||||
import { ElDialog, ElButton, ElInput, ElMessage } from 'element-plus';
|
||||
import { ElDialog, ElButton, ElInput, ElMessage, InputInstance } from 'element-plus';
|
||||
// import base style
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
// 引入主题后还需要在 options 中指定主题才会生效
|
||||
@@ -50,6 +50,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props: any) {
|
||||
const remarkInputRef = ref<InputInstance>();
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
sqlValue: '',
|
||||
@@ -133,10 +134,14 @@ export default defineComponent({
|
||||
state.dbId = props.dbId;
|
||||
state.db = props.db;
|
||||
state.dialogVisible = true;
|
||||
nextTick(() => {
|
||||
remarkInputRef.value?.focus();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
remarkInputRef,
|
||||
open,
|
||||
runSql,
|
||||
cancel,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module mayfly-go
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // jwt
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/mojocn/base64Captcha v1.3.5 // 验证码
|
||||
@@ -27,8 +27,8 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.10.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
@@ -41,6 +41,7 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.18.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
@@ -48,9 +49,10 @@ require (
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
@@ -16,8 +16,10 @@ import (
|
||||
"mayfly-go/pkg/ws"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xwb1989/sqlparser"
|
||||
)
|
||||
|
||||
type Db struct {
|
||||
@@ -27,6 +29,8 @@ type Db struct {
|
||||
ProjectApp application.Project
|
||||
}
|
||||
|
||||
const DEFAULT_COLUMN_SIZE = 500
|
||||
|
||||
// @router /api/dbs [get]
|
||||
func (d *Db) Dbs(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
@@ -91,7 +95,7 @@ func (d *Db) ExecSql(rc *ctx.ReqCtx) {
|
||||
rc.ReqParam = fmt.Sprintf("db: %d:%s | sql: %s", id, db, sql)
|
||||
|
||||
biz.NotEmpty(sql, "sql不能为空")
|
||||
if strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select") || strings.HasPrefix(sql, "show") {
|
||||
if strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select") || strings.HasPrefix(sql, "show") || strings.HasPrefix(sql, "explain") {
|
||||
colNames, res, err := dbInstance.SelectData(sql)
|
||||
biz.ErrIsNilAppendErr(err, "查询失败: %s")
|
||||
colAndRes := make(map[string]interface{})
|
||||
@@ -128,12 +132,8 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
|
||||
fileheader, err := g.FormFile("file")
|
||||
biz.ErrIsNilAppendErr(err, "读取sql文件失败: %s")
|
||||
|
||||
// 读取sql文件并根据;切割sql语句
|
||||
file, _ := fileheader.Open()
|
||||
filename := fileheader.Filename
|
||||
bytes, _ := ioutil.ReadAll(file)
|
||||
sqlContent := string(bytes)
|
||||
sqls := strings.Split(sqlContent, ";")
|
||||
dbId, db := GetIdAndDb(g)
|
||||
|
||||
go func() {
|
||||
@@ -153,12 +153,14 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
|
||||
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, db.ProjectId), "%s")
|
||||
|
||||
for _, sql := range sqls {
|
||||
sql = strings.Trim(sql, " ")
|
||||
if sql == "" || sql == "\n" {
|
||||
continue
|
||||
tokens := sqlparser.NewTokenizer(file)
|
||||
for {
|
||||
stmt, err := sqlparser.ParseNext(tokens)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
_, err := db.Exec(sql)
|
||||
sql := sqlparser.String(stmt)
|
||||
_, err = db.Exec(sql)
|
||||
if err != nil {
|
||||
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]%s执行失败: [%s]", filename, dbInfo, err.Error())))
|
||||
return
|
||||
@@ -168,6 +170,88 @@ func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
|
||||
}()
|
||||
}
|
||||
|
||||
// 数据库dump
|
||||
func (d *Db) DumpSql(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
dbId, db := GetIdAndDb(g)
|
||||
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"
|
||||
|
||||
now := time.Now()
|
||||
filename := fmt.Sprintf("%s.%s.sql", db, now.Format("200601021504"))
|
||||
g.Header("Content-Type", "application/octet-stream")
|
||||
g.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("数据库id: %d -- %s", dbId, db)
|
||||
dbInstance := d.DbApp.GetDbInstance(dbId, db)
|
||||
writer := g.Writer
|
||||
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("\n-- ----------------------------\n")
|
||||
|
||||
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(dbInstance.GetCreateTableDdl(table)[0]["Create Table"].(string) + ";\n")
|
||||
}
|
||||
|
||||
if !needData {
|
||||
continue
|
||||
}
|
||||
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表记录: %s \n-- ----------------------------\n", table))
|
||||
writer.WriteString("BEGIN;\n")
|
||||
|
||||
countSql := fmt.Sprintf("SELECT COUNT(*) count FROM %s", table)
|
||||
_, countRes, _ := dbInstance.SelectData(countSql)
|
||||
// 查询出所有列信息总数,手动分页获取所有数据
|
||||
maCount := int(countRes[0]["count"].(int64))
|
||||
// 计算需要查询的页数
|
||||
pageNum := maCount / DEFAULT_COLUMN_SIZE
|
||||
if maCount%DEFAULT_COLUMN_SIZE > 0 {
|
||||
pageNum++
|
||||
}
|
||||
|
||||
sqlTmp := "SELECT * FROM %s LIMIT %d, %d"
|
||||
for index := 0; index < pageNum; index++ {
|
||||
sql := fmt.Sprintf(sqlTmp, table, index*DEFAULT_COLUMN_SIZE, DEFAULT_COLUMN_SIZE)
|
||||
columns, result, _ := dbInstance.SelectData(sql)
|
||||
|
||||
insertSql := "INSERT INTO `%s` VALUES (%s);\n"
|
||||
for _, res := range result {
|
||||
var values []string
|
||||
for _, column := range columns {
|
||||
value := res[column]
|
||||
if value == nil {
|
||||
values = append(values, "NULL")
|
||||
continue
|
||||
}
|
||||
strValue, ok := value.(string)
|
||||
if ok {
|
||||
values = append(values, fmt.Sprintf("%#v", strValue))
|
||||
} else {
|
||||
values = append(values, utils.ToString(value))
|
||||
}
|
||||
}
|
||||
writer.WriteString(fmt.Sprintf(insertSql, table, strings.Join(values, ", ")))
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteString("COMMIT;\n")
|
||||
}
|
||||
rc.NoRes = true
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/t-metadata [get]
|
||||
func (d *Db) TableMA(rc *ctx.ReqCtx) {
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
|
||||
@@ -223,8 +223,9 @@ func (d *DbInstance) SelectData(execSql string) ([]string, []map[string]interfac
|
||||
execSql = strings.Trim(execSql, " ")
|
||||
isSelect := strings.HasPrefix(execSql, "SELECT") || strings.HasPrefix(execSql, "select")
|
||||
isShow := strings.HasPrefix(execSql, "show")
|
||||
isExplain := strings.HasPrefix(execSql, "explain")
|
||||
|
||||
if !isSelect && !isShow {
|
||||
if !isSelect && !isShow && !isExplain {
|
||||
return nil, nil, errors.New("该sql非查询语句")
|
||||
}
|
||||
// 没加limit,则默认限制50条
|
||||
@@ -272,14 +273,13 @@ func (d *DbInstance) SelectData(execSql string) ([]string, []map[string]interfac
|
||||
colName := colType.Name()
|
||||
// 字段类型名
|
||||
colScanType := colType.ScanType().Name()
|
||||
|
||||
// 如果是密码字段,则脱敏显示
|
||||
if colName == "password" {
|
||||
v = []byte("******")
|
||||
}
|
||||
if isFirst {
|
||||
colNames = append(colNames, colName)
|
||||
}
|
||||
if v == nil {
|
||||
rowData[colName] = nil
|
||||
continue
|
||||
}
|
||||
// 这里把[]byte数据转成string
|
||||
stringV := string(v)
|
||||
if stringV == "" {
|
||||
|
||||
@@ -61,6 +61,12 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
rc.Handle(d.ExecSqlFile)
|
||||
})
|
||||
|
||||
db.GET(":dbId/dump", func(g *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(g).
|
||||
WithLog(ctx.NewLogInfo("Sql文件dump")).
|
||||
Handle(d.DumpSql)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-metadata", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.TableMA)
|
||||
})
|
||||
|
||||
@@ -16,7 +16,6 @@ type HandlerFunc func(*ReqCtx)
|
||||
type ReqCtx struct {
|
||||
GinCtx *gin.Context // gin context
|
||||
|
||||
// NeedToken bool // 是否需要token
|
||||
RequiredPermission *Permission // 需要的权限信息,默认为nil,需要校验token
|
||||
LoginAccount *model.LoginAccount // 登录账号信息,只有校验token后才会有值
|
||||
|
||||
@@ -26,7 +25,7 @@ type ReqCtx struct {
|
||||
Err interface{} // 请求错误
|
||||
|
||||
timed int64 // 执行时间
|
||||
noRes bool // 无需返回结果,即文件下载等
|
||||
NoRes bool // 无需返回结果,即文件下载等
|
||||
}
|
||||
|
||||
func (rc *ReqCtx) Handle(handler HandlerFunc) {
|
||||
@@ -55,13 +54,13 @@ func (rc *ReqCtx) Handle(handler HandlerFunc) {
|
||||
begin := time.Now()
|
||||
handler(rc)
|
||||
rc.timed = time.Now().Sub(begin).Milliseconds()
|
||||
if !rc.noRes {
|
||||
if !rc.NoRes {
|
||||
ginx.SuccessRes(ginCtx, rc.ResData)
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *ReqCtx) Download(reader io.Reader, filename string) {
|
||||
rc.noRes = true
|
||||
rc.NoRes = true
|
||||
ginx.Download(rc.GinCtx, reader, filename)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -107,3 +109,45 @@ func ReverStrTemplate(temp, str string, res map[string]interface{}) {
|
||||
ReverStrTemplate(next, StrTrim(SubString(str, UnicodeIndex(str, value)+StrLen(value), StrLen(str))), res)
|
||||
}
|
||||
}
|
||||
|
||||
func ToString(value interface{}) string {
|
||||
// interface 转 string
|
||||
var key string
|
||||
if value == nil {
|
||||
return key
|
||||
}
|
||||
|
||||
switch it := value.(type) {
|
||||
case float64:
|
||||
return strconv.FormatFloat(it, 'f', -1, 64)
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(it), 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.Itoa(it)
|
||||
case uint:
|
||||
return strconv.Itoa(int(it))
|
||||
case int8:
|
||||
return strconv.Itoa(int(it))
|
||||
case uint8:
|
||||
return strconv.Itoa(int(it))
|
||||
case int16:
|
||||
return strconv.Itoa(int(it))
|
||||
case uint16:
|
||||
return strconv.Itoa(int(it))
|
||||
case int32:
|
||||
return strconv.Itoa(int(it))
|
||||
case uint32:
|
||||
return strconv.Itoa(int(it))
|
||||
case int64:
|
||||
return strconv.FormatInt(it, 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(it, 10)
|
||||
case string:
|
||||
return it
|
||||
case []byte:
|
||||
return string(value.([]byte))
|
||||
default:
|
||||
newValue, _ := json.Marshal(value)
|
||||
return string(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user