refactor: 组件升级、代码优化

This commit is contained in:
meilin.huang
2023-05-24 12:32:17 +08:00
parent 4fa52412c1
commit 9900b236ef
48 changed files with 777 additions and 345 deletions

View File

@@ -4,22 +4,22 @@ go 1.20
require (
github.com/gin-gonic/gin v1.9.0
github.com/go-sql-driver/mysql v1.7.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/go-sql-driver/mysql v1.7.1
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/gorilla/websocket v1.5.0
github.com/lib/pq v1.10.7
github.com/mojocn/base64Captcha v1.3.5 //
github.com/pkg/sftp v1.13.5
github.com/redis/go-redis/v9 v9.0.2
github.com/redis/go-redis/v9 v9.0.4
github.com/robfig/cron/v3 v3.0.1 //
github.com/sirupsen/logrus v1.9.0
github.com/sirupsen/logrus v1.9.2
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
go.mongodb.org/mongo-driver v1.11.4 // mongo
golang.org/x/crypto v0.8.0 // ssh
golang.org/x/crypto v0.9.0 // ssh
gopkg.in/yaml.v3 v3.0.1
// gorm
gorm.io/driver/mysql v1.5.0
gorm.io/gorm v1.25.0
gorm.io/gorm v1.25.1
)
require (
@@ -55,9 +55,9 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

View File

@@ -36,7 +36,7 @@ func (i *Index) Count(rc *req.Ctx) {
dbNum = i.DbApp.Count(&dbentity.DbQuery{TagIds: tagIds})
redisNum = i.RedisApp.Count(&redisentity.RedisQuery{TagIds: tagIds})
}
rc.ResData = map[string]interface{}{
rc.ResData = map[string]any{
"mongoNum": mongoNum,
"machineNum": machienNum,
"dbNum": dbNum,

View File

@@ -266,7 +266,7 @@ func (d *Db) DumpSql(rc *req.Ctx) {
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)[0]["Create Table"].(string) + ";\n")
writer.WriteString(dbmeta.GetCreateTableDdl(table) + ";\n")
}
if !needData {
@@ -339,7 +339,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
tables := dm.GetTables()
tableNames := make([]string, 0)
for _, v := range tables {
tableNames = append(tableNames, v["tableName"].(string))
tableNames = append(tableNames, v.TableName)
}
// key = 表名value = 列名数组
res := make(map[string][]string)
@@ -353,15 +353,15 @@ func (d *Db) HintTables(rc *req.Ctx) {
// 获取所有表下的所有列信息
columnMds := dm.GetColumns(tableNames...)
for _, v := range columnMds {
tName := v["tableName"].(string)
tName := v.TableName
if res[tName] == nil {
res[tName] = make([]string, 0)
}
columnName := fmt.Sprintf("%s [%s]", v["columnName"], v["columnType"])
comment := v["columnComment"]
columnName := fmt.Sprintf("%s [%s]", v.ColumnName, v.ColumnType)
comment := v.ColumnComment
// 如果字段备注不为空,则加上备注信息
if comment != nil && comment != "" {
if comment != "" {
columnName = fmt.Sprintf("%s[%s]", columnName, comment)
}

View File

@@ -249,22 +249,15 @@ type DbInstance struct {
// 执行查询语句
// 依次返回 列名数组结果map错误
func (d *DbInstance) SelectData(execSql string) ([]string, []map[string]interface{}, error) {
func (d *DbInstance) SelectData(execSql string) ([]string, []map[string]any, error) {
return SelectDataByDb(d.db, execSql)
}
// 将查询结果映射至struct可具体参考sqlx库
func (d *DbInstance) SelectData2Struct(execSql string, dest interface{}) error {
func (d *DbInstance) SelectData2Struct(execSql string, dest any) error {
return Select2StructByDb(d.db, execSql, dest)
}
// 执行内部查询语句,不返回列名以及不限制行数
// 依次返回 结果map错误
func (d *DbInstance) innerSelect(execSql string) ([]map[string]interface{}, error) {
_, res, err := SelectDataByDb(d.db, execSql)
return res, err
}
// 执行 update, insert, delete建表等sql
// 返回影响条数和错误
func (d *DbInstance) Exec(sql string) (int64, error) {

View File

@@ -1,32 +1,102 @@
package application
import (
"embed"
"mayfly-go/pkg/biz"
"strings"
)
// 表信息
type Table struct {
TableName string `json:"tableName"` // 表名
TableComment string `json:"tableComment"` // 表备注
CreateTime string `json:"createTime"` // 创建时间
TableRows int `json:"tableRows"`
DataLength int64 `json:"dataLength"`
IndexLength int64 `json:"indexLength"`
}
// 表的列信息
type Column struct {
TableName string `json:"tableName"` // 表名
ColumnName string `json:"columnName"` // 列名
ColumnType string `json:"columnType"` // 列类型
ColumnComment string `json:"columnComment"` // 列备注
ColumnKey string `json:"columnKey"`
ColumnDefault string `json:"columnDefault"`
Nullable string `json:"nullable"` // 是否可为null
Extra string `json:"extra"` // 其他信息
}
// 表索引信息
type Index struct {
IndexName string `json:"indexName"` // 索引名
ColumnName string `json:"columnName"` // 列名
IndexType string `json:"indexType"` // 索引类型
IndexComment string `json:"indexComment"` // 备注
SeqInIndex int `json:"seqInIndex"`
NonUnique int `json:"nonUnique"`
}
// -----------------------------------元数据接口定义------------------------------------------
// 数据库元信息接口(表、列、获取表数据等元信息)
// 所有数据查出来直接用map接收注意不同数据库实现该接口返回的map中的key需要统一.
// 即: 使用别名统一即可。如table_name AS tableName
type DbMetadata interface {
// 获取表基础元信息
// 表名: tableName, 备注: tableComment
GetTables() []map[string]interface{}
GetTables() []Table
// 获取指定表名的所有列元信息
// 表名: tableName, 列名: columnName, 列类型: columnType, 备注: columnComment, 是否可为null: nullable, 其他信息: extra
GetColumns(tableNames ...string) []map[string]interface{}
GetColumns(tableNames ...string) []Column
// 获取表主键字段名,没有主键标识则默认第一个字段
GetPrimaryKey(tablename string) string
// 获取表信息比GetTables获取更详细的表信息
GetTableInfos() []map[string]interface{}
GetTableInfos() []Table
// 获取表索引信息
GetTableIndex(tableName string) []map[string]interface{}
GetTableIndex(tableName string) []Index
// 获取建表ddl
GetCreateTableDdl(tableName string) []map[string]interface{}
GetCreateTableDdl(tableName string) string
// 获取指定表的数据-分页查询
// @return columns: 列字段名result: 结果集error: 错误
GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]interface{}, error)
}
// ------------------------- 元数据sql操作 -------------------------
//
//go:embed metasql/*
var metasql embed.FS
// sql缓存 key: sql备注的key 如MYSQL_TABLE_MA value: sql内容
var sqlCache = make(map[string]string, 20)
// 获取本地文件的sql内容并进行解析获取对应key的sql内容
func GetLocalSql(file, key string) string {
sql := sqlCache[key]
if sql != "" {
return sql
}
bytes, err := metasql.ReadFile(file)
biz.ErrIsNilAppendErr(err, "获取sql meta文件内容失败: %s")
allSql := string(bytes)
sqls := strings.Split(allSql, "\n\n")
var resSql string
for _, sql := range sqls {
// 获取sql第一行的sql备注信息如--MYSQL_TABLE_MA 表信息元数据
info := strings.SplitN(sql, "\n", 2)
// 原始sql即去除第一行的key与备注信息
rowSql := info[1]
// 获取sql keyMYSQL_TABLE_MA
sqlKey := strings.Split(strings.Split(info[0], " ")[0], "--")[1]
if key == sqlKey {
resSql = rowSql
}
sqlCache[sqlKey] = rowSql
}
return resSql
}

View File

@@ -0,0 +1,69 @@
--MYSQL_TABLE_MA
SELECT
table_name tableName,
table_comment tableComment
from
information_schema.tables
WHERE
table_schema = (
SELECT
database ()
)
--MYSQL_TABLE_INFO
SELECT
table_name tableName,
table_comment tableComment,
table_rows tableRows,
data_length dataLength,
index_length indexLength,
create_time createTime
FROM
information_schema.tables
WHERE
table_schema = (
SELECT
database ()
)
--MYSQL_INDEX_INFO
SELECT
index_name indexName,
column_name columnName,
index_type indexType,
non_unique nonUnique,
SEQ_IN_INDEX seqInIndex,
INDEX_COMMENT indexComment
FROM
information_schema.STATISTICS
WHERE
table_schema = (
SELECT
database ()
)
AND table_name = '%s'
ORDER BY
index_name asc,
SEQ_IN_INDEX asc
--MYSQL_COLUMN_MA
SELECT
table_name tableName,
column_name columnName,
column_type columnType,
column_default columnDefault,
column_comment columnComment,
column_key columnKey,
extra extra,
is_nullable nullable
from
information_schema.columns
WHERE
table_schema = (
SELECT
database ()
)
AND table_name in (%s)
ORDER BY
tableName,
ordinal_position

View File

@@ -0,0 +1,169 @@
--PGSQL_TABLE_MA
SELECT
obj_description (c.oid) AS "tableComment",
c.relname AS "tableName"
FROM
pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE
n.nspname = (
select
current_schema ()
)
AND c.reltype > 0
--PGSQL_TABLE_INFO
SELECT
obj_description (c.oid) AS "tableComment",
c.relname AS "tableName",
pg_table_size ('"' || n.nspname || '"."' || c.relname || '"') as "dataLength",
pg_indexes_size ('"' || n.nspname || '"."' || c.relname || '"') as "indexLength",
c.reltuples as "tableRows"
FROM
pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE
n.nspname = (
select
current_schema ()
)
AND c.reltype > 0
--PGSQL_INDEX_INFO
SELECT
indexname AS "indexName",
indexdef AS "indexComment"
FROM
pg_indexes
WHERE
schemaname = (
select
current_schema ()
)
AND tablename = '%s'
--PGSQL_COLUMN_MA
SELECT
C.relname AS "tableName",
A.attname AS "columnName",
tc.is_nullable AS "nullable",
concat_ws ( '', t.typname, SUBSTRING ( format_type ( a.atttypid, a.atttypmod ) FROM '\(.*\)' ) ) AS "columnType",
(CASE WHEN ( SELECT COUNT(*) FROM pg_constraint WHERE conrelid = a.attrelid AND conkey[1]= attnum AND contype = 'p' ) > 0 THEN 'PRI' ELSE '' END ) AS "columnKey",
d.description AS "columnComment"
FROM
pg_attribute a LEFT JOIN pg_description d ON d.objoid = a.attrelid
AND d.objsubid = A.attnum
LEFT JOIN pg_class c ON A.attrelid = c.oid
LEFT JOIN pg_namespace pn ON c.relnamespace = pn.oid
LEFT JOIN pg_type t ON a.atttypid = t.oid
JOIN information_schema.columns tc ON tc.column_name = a.attname AND tc.table_name = C.relname AND tc.table_schema = pn.nspname
WHERE
A.attnum >= 0
AND pn.nspname = (select current_schema())
AND C.relname in (%s)
ORDER BY
C.relname DESC,
A.attnum ASC
--PGSQL_TABLE_DDL_FUNC ddl函数
CREATE OR REPLACE FUNCTION showcreatetable(namespace character varying, tablename character varying)
RETURNS character varying AS
$BODY$
declare
tableScript character varying default '';
begin
-- columns
tableScript:=tableScript || ' CREATE TABLE '|| tablename|| ' ( '|| chr(13)||chr(10) || array_to_string(
array(
select ' ' || concat_ws(' ',fieldName, fieldType, fieldLen, indexType, isNullStr, fieldComment ) as column_line
from (
select a.attname as fieldName,format_type(a.atttypid,a.atttypmod) as fieldType,(case when atttypmod-4>0 then
atttypmod-4 else 0 end) as fieldLen,
(case when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and
contype='p')>0 then 'PRI'
when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and contype='u')>0
then 'UNI'
when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and contype='f')>0
then 'FRI'
else '' end) as indexType,
(case when a.attnotnull=true then 'not null' else 'null' end) as isNullStr,
' comment ' || col_description(a.attrelid,a.attnum) as fieldComment
from pg_attribute a where attstattarget=-1 and attrelid = (select c.oid from pg_class c,pg_namespace n where
c.relnamespace=n.oid and n.nspname =namespace and relname =tablename)
) as string_columns
),','||chr(13)||chr(10)) || ',';
-- 约束
tableScript:= tableScript || chr(13)||chr(10) || array_to_string(
array(
select concat(' CONSTRAINT ',conname ,c ,u,p,f) from (
select conname,
case when contype='c' then ' CHECK('|| ( select findattname(namespace,tablename,'c') ) ||')' end as c ,
case when contype='u' then ' UNIQUE('|| ( select findattname(namespace,tablename,'u') ) ||')' end as u ,
case when contype='p' then ' PRIMARY KEY ('|| ( select findattname(namespace,tablename,'p') ) ||')' end as p ,
case when contype='f' then ' FOREIGN KEY('|| ( select findattname(namespace,tablename,'u') ) ||') REFERENCES '||
(select p.relname from pg_class p where p.oid=c.confrelid ) || '('|| ( select
findattname(namespace,tablename,'u') ) ||')' end as f
from pg_constraint c
where contype in('u','c','f','p') and conrelid=(
select oid from pg_class where relname=tablename and relnamespace =(
select oid from pg_namespace where nspname = namespace
)
)
) as t
) ,',' || chr(13)||chr(10) ) || chr(13)||chr(10) ||' ); ';
-- indexs
-- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table pg_language
--
/** **/
--- 获取非约束索引 column
-- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table pg_language
tableScript:= tableScript || chr(13)||chr(10) || chr(13)||chr(10) || array_to_string(
array(
select 'CREATE INDEX ' || indexrelname || ' ON ' || tablename || ' USING btree '|| '(' || attname || ');' from (
SELECT
i.relname AS indexrelname , x.indkey,
( select array_to_string (
array(
select a.attname from pg_attribute a where attrelid=c.oid and a.attnum in ( select unnest(x.indkey) )
)
,',' ) )as attname
FROM pg_class c
JOIN pg_index x ON c.oid = x.indrelid
JOIN pg_class i ON i.oid = x.indexrelid
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname=tablename and i.relname not in
( select constraint_name from information_schema.key_column_usage where table_name=tablename )
)as t
) ,','|| chr(13)||chr(10));
-- COMMENT COMMENT ON COLUMN sys_activity.id IS '主键';
tableScript:= tableScript || chr(13)||chr(10) || chr(13)||chr(10) || array_to_string(
array(
SELECT 'COMMENT ON COLUMN' || tablename || '.' || a.attname ||' IS '|| ''''|| d.description ||''''
FROM pg_class c
JOIN pg_description d ON c.oid=d.objoid
JOIN pg_attribute a ON c.oid = a.attrelid
WHERE c.relname=tablename
AND a.attnum = d.objsubid),','|| chr(13)||chr(10)) ;
return tableScript;
end
$BODY$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION findattname(namespace character varying, tablename character varying, ctype character
varying)
RETURNS character varying as $BODY$
declare
tt oid ;
aname character varying default '';
begin
tt := oid from pg_class where relname= tablename and relnamespace =(select oid from pg_namespace where
nspname=namespace) ;
aname:= array_to_string(
array(
select a.attname from pg_attribute a
where a.attrelid=tt and a.attnum in (
select unnest(conkey) from pg_constraint c where contype=ctype
and conrelid=tt and array_to_string(conkey,',') is not null
)
),',');
return aname;
end
$BODY$ LANGUAGE plpgsql

View File

@@ -7,6 +7,7 @@ import (
"mayfly-go/internal/db/domain/entity"
machineapp "mayfly-go/internal/machine/application"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/utils"
"net"
"github.com/go-sql-driver/mysql"
@@ -30,25 +31,11 @@ func getMysqlDB(d *entity.Db, db string) (*sql.DB, error) {
// ---------------------------------- mysql元数据 -----------------------------------
const (
// mysql 表信息元数据
MYSQL_TABLE_MA = `SELECT table_name tableName, table_comment tableComment from information_schema.tables WHERE table_schema = (SELECT database())`
// mysql 表信息
MYSQL_TABLE_INFO = `SELECT table_name tableName, table_comment tableComment, table_rows tableRows,
data_length dataLength, index_length indexLength, create_time createTime
FROM information_schema.tables
WHERE table_schema = (SELECT database())`
// mysql 索引信息
MYSQL_INDEX_INFO = `SELECT index_name indexName, column_name columnName, index_type indexType, non_unique nonUnique,
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
FROM information_schema.STATISTICS
WHERE table_schema = (SELECT database()) AND table_name = '%s' ORDER BY index_name asc , SEQ_IN_INDEX asc`
// mysql 列信息元数据
MYSQL_COLUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType, column_default columnDefault,
column_comment columnComment, column_key columnKey, extra, is_nullable nullable from information_schema.columns
WHERE table_schema = (SELECT database()) AND table_name in (%s) ORDER BY tableName, ordinal_position`
MYSQL_META_FILE = "metasql/mysql_meta.sql"
MYSQL_TABLE_MA_KEY = "MYSQL_TABLE_MA"
MYSQL_TABLE_INFO_KEY = "MYSQL_TABLE_INFO"
MYSQL_INDEX_INFO_KEY = "MYSQL_INDEX_INFO"
MYSQL_COLUMN_MA_KEY = "MYSQL_COLUMN_MA"
)
type MysqlMetadata struct {
@@ -56,14 +43,22 @@ type MysqlMetadata struct {
}
// 获取表基础元信息, 如表名等
func (mm *MysqlMetadata) GetTables() []map[string]interface{} {
res, err := mm.di.innerSelect(MYSQL_TABLE_MA)
func (mm *MysqlMetadata) GetTables() []Table {
_, res, err := mm.di.SelectData(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_MA_KEY))
biz.ErrIsNilAppendErr(err, "获取表基本信息失败: %s")
return res
tables := make([]Table, 0)
for _, re := range res {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: utils.Any2String(re["tableComment"]),
})
}
return tables
}
// 获取列元信息, 如列名等
func (mm *MysqlMetadata) GetColumns(tableNames ...string) []map[string]interface{} {
func (mm *MysqlMetadata) GetColumns(tableNames ...string) []Column {
tableName := ""
for i := 0; i < len(tableNames); i++ {
if i != 0 {
@@ -71,9 +66,22 @@ func (mm *MysqlMetadata) GetColumns(tableNames ...string) []map[string]interface
}
tableName = tableName + "'" + tableNames[i] + "'"
}
result, err := mm.di.innerSelect(fmt.Sprintf(MYSQL_COLUMN_MA, tableName))
_, res, err := mm.di.SelectData(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_COLUMN_MA_KEY), tableName))
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
return result
columns := make([]Column, 0)
for _, re := range res {
columns = append(columns, Column{
TableName: re["tableName"].(string),
ColumnName: re["columnName"].(string),
ColumnType: utils.Any2String(re["columnType"]),
ColumnComment: utils.Any2String(re["columnComment"]),
Nullable: utils.Any2String(re["nullable"]),
ColumnKey: utils.Any2String(re["columnKey"]),
ColumnDefault: utils.Any2String(re["columnDefault"]),
})
}
return columns
}
// 获取表主键字段名,不存在主键标识则默认第一个字段
@@ -81,36 +89,59 @@ func (mm *MysqlMetadata) GetPrimaryKey(tablename string) string {
columns := mm.GetColumns(tablename)
biz.IsTrue(len(columns) > 0, "[%s] 表不存在", tablename)
for _, v := range columns {
if v["columnKey"].(string) == "PRI" {
return v["columnName"].(string)
if v.ColumnKey == "PRI" {
return v.ColumnName
}
}
return columns[0]["columnName"].(string)
return columns[0].ColumnName
}
// 获取表信息比GetTableMetedatas获取更详细的表信息
func (mm *MysqlMetadata) GetTableInfos() []map[string]interface{} {
res, err := mm.di.innerSelect(MYSQL_TABLE_INFO)
func (mm *MysqlMetadata) GetTableInfos() []Table {
_, res, err := mm.di.SelectData(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
return res
tables := make([]Table, 0)
for _, re := range res {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: utils.Any2String(re["tableComment"]),
CreateTime: utils.Any2String(re["createTime"]),
TableRows: utils.Any2Int(re["tableRows"]),
DataLength: utils.Any2Int64(re["dataLength"]),
IndexLength: utils.Any2Int64(re["indexLength"]),
})
}
return tables
}
// 获取表索引信息
func (mm *MysqlMetadata) GetTableIndex(tableName string) []map[string]interface{} {
res, err := mm.di.innerSelect(fmt.Sprintf(MYSQL_INDEX_INFO, tableName))
func (mm *MysqlMetadata) GetTableIndex(tableName string) []Index {
_, res, err := mm.di.SelectData(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_INDEX_INFO_KEY), tableName))
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
indexs := make([]Index, 0)
for _, re := range res {
indexs = append(indexs, Index{
IndexName: re["indexName"].(string),
ColumnName: utils.Any2String(re["columnName"]),
IndexType: utils.Any2String(re["indexType"]),
IndexComment: utils.Any2String(re["indexComment"]),
NonUnique: utils.Any2Int(re["nonUnique"]),
SeqInIndex: utils.Any2Int(re["seqInIndex"]),
})
}
// 把查询结果以索引名分组,索引字段以逗号连接
result := make([]map[string]interface{}, 0)
result := make([]Index, 0)
key := ""
for _, v := range res {
for _, v := range indexs {
// 当前的索引名
in := v["indexName"].(string)
in := v.IndexName
if key == in {
// 索引字段已根据名称和顺序排序,故取最后一个即可
i := len(result) - 1
// 同索引字段以逗号连接
result[i]["columnName"] = result[i]["columnName"].(string) + "," + v["columnName"].(string)
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
} else {
key = in
result = append(result, v)
@@ -120,9 +151,9 @@ func (mm *MysqlMetadata) GetTableIndex(tableName string) []map[string]interface{
}
// 获取建表ddl
func (mm *MysqlMetadata) GetCreateTableDdl(tableName string) []map[string]interface{} {
res, _ := mm.di.innerSelect(fmt.Sprintf("show create table %s ", tableName))
return res
func (mm *MysqlMetadata) GetCreateTableDdl(tableName string) string {
_, res, _ := mm.di.SelectData(fmt.Sprintf("show create table %s ", tableName))
return res[0]["Create Table"].(string)
}
func (mm *MysqlMetadata) GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]interface{}, error) {

View File

@@ -57,38 +57,12 @@ func (pd *PqSqlDialer) DialTimeout(network, address string, timeout time.Duratio
// ---------------------------------- pgsql元数据 -----------------------------------
const (
// postgres 表信息元数据
PGSQL_TABLE_MA = `SELECT obj_description(c.oid) AS "tableComment", c.relname AS "tableName" FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = (select current_schema()) AND c.reltype > 0`
PGSQL_TABLE_INFO = `SELECT obj_description(c.oid) AS "tableComment", c.relname AS "tableName" FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = (select current_schema()) AND c.reltype > 0`
PGSQL_INDEX_INFO = `SELECT indexname AS "indexName", indexdef AS "indexComment"
FROM pg_indexes WHERE schemaname = (select current_schema()) AND tablename = '%s'`
PGSQL_COLUMN_MA = `SELECT
C.relname AS "tableName",
A.attname AS "columnName",
tc.is_nullable AS "nullable",
concat_ws ( '', t.typname, SUBSTRING ( format_type ( a.atttypid, a.atttypmod ) FROM '\(.*\)' ) ) AS "columnType",
(CASE WHEN ( SELECT COUNT(*) FROM pg_constraint WHERE conrelid = a.attrelid AND conkey[1]= attnum AND contype = 'p' ) > 0 THEN 'PRI' ELSE '' END ) AS "columnKey",
d.description AS "columnComment"
FROM
pg_attribute a LEFT JOIN pg_description d ON d.objoid = a.attrelid
AND d.objsubid = A.attnum
LEFT JOIN pg_class c ON A.attrelid = c.oid
LEFT JOIN pg_namespace pn ON c.relnamespace = pn.oid
LEFT JOIN pg_type t ON a.atttypid = t.oid
JOIN information_schema.columns tc ON tc.column_name = a.attname AND tc.table_name = C.relname AND tc.table_schema = pn.nspname
WHERE
A.attnum >= 0
AND pn.nspname = (select current_schema())
AND C.relname in (%s)
ORDER BY
C.relname DESC,
A.attnum ASC
`
PGSQL_META_FILE = "metasql/pgsql_meta.sql"
PGSQL_TABLE_MA_KEY = "PGSQL_TABLE_MA"
PGSQL_TABLE_INFO_KEY = "PGSQL_TABLE_INFO"
PGSQL_INDEX_INFO_KEY = "PGSQL_INDEX_INFO"
PGSQL_COLUMN_MA_KEY = "PGSQL_COLUMN_MA"
PGSQL_TABLE_DDL_KEY = "PGSQL_TABLE_DDL_FUNC"
)
type PgsqlMetadata struct {
@@ -96,14 +70,22 @@ type PgsqlMetadata struct {
}
// 获取表基础元信息, 如表名等
func (pm *PgsqlMetadata) GetTables() []map[string]interface{} {
res, err := pm.di.innerSelect(PGSQL_TABLE_MA)
func (pm *PgsqlMetadata) GetTables() []Table {
_, res, err := pm.di.SelectData(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_MA_KEY))
biz.ErrIsNilAppendErr(err, "获取表基本信息失败: %s")
return res
tables := make([]Table, 0)
for _, re := range res {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: utils.Any2String(re["tableComment"]),
})
}
return tables
}
// 获取列元信息, 如列名等
func (pm *PgsqlMetadata) GetColumns(tableNames ...string) []map[string]interface{} {
func (pm *PgsqlMetadata) GetColumns(tableNames ...string) []Column {
tableName := ""
for i := 0; i < len(tableNames); i++ {
if i != 0 {
@@ -111,41 +93,86 @@ func (pm *PgsqlMetadata) GetColumns(tableNames ...string) []map[string]interface
}
tableName = tableName + "'" + tableNames[i] + "'"
}
result, err := pm.di.innerSelect(fmt.Sprintf(PGSQL_COLUMN_MA, tableName))
_, res, err := pm.di.SelectData(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_COLUMN_MA_KEY), tableName))
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
return result
columns := make([]Column, 0)
for _, re := range res {
columns = append(columns, Column{
TableName: re["tableName"].(string),
ColumnName: re["columnName"].(string),
ColumnType: utils.Any2String(re["columnType"]),
ColumnComment: utils.Any2String(re["columnComment"]),
Nullable: utils.Any2String(re["nullable"]),
ColumnKey: utils.Any2String(re["columnKey"]),
ColumnDefault: utils.Any2String(re["columnDefault"]),
})
}
return columns
}
func (pm *PgsqlMetadata) GetPrimaryKey(tablename string) string {
columns := pm.GetColumns(tablename)
biz.IsTrue(len(columns) > 0, "[%s] 表不存在", tablename)
for _, v := range columns {
if v["columnKey"].(string) == "PRI" {
return v["columnName"].(string)
if v.ColumnKey == "PRI" {
return v.ColumnName
}
}
return columns[0]["columnName"].(string)
return columns[0].ColumnName
}
// 获取表信息比GetTables获取更详细的表信息
func (pm *PgsqlMetadata) GetTableInfos() []map[string]interface{} {
res, err := pm.di.innerSelect(PGSQL_TABLE_INFO)
func (pm *PgsqlMetadata) GetTableInfos() []Table {
_, res, err := pm.di.SelectData(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
return res
tables := make([]Table, 0)
for _, re := range res {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: utils.Any2String(re["tableComment"]),
CreateTime: utils.Any2String(re["createTime"]),
TableRows: utils.Any2Int(re["tableRows"]),
DataLength: utils.Any2Int64(re["dataLength"]),
IndexLength: utils.Any2Int64(re["indexLength"]),
})
}
return tables
}
// 获取表索引信息
func (pm *PgsqlMetadata) GetTableIndex(tableName string) []map[string]interface{} {
res, err := pm.di.innerSelect(fmt.Sprintf(PGSQL_INDEX_INFO, tableName))
func (pm *PgsqlMetadata) GetTableIndex(tableName string) []Index {
_, res, err := pm.di.SelectData(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_INDEX_INFO_KEY), tableName))
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
return res
indexs := make([]Index, 0)
for _, re := range res {
indexs = append(indexs, Index{
IndexName: re["indexName"].(string),
ColumnName: utils.Any2String(re["columnName"]),
IndexType: utils.Any2String(re["indexType"]),
IndexComment: utils.Any2String(re["indexComment"]),
NonUnique: utils.Any2Int(re["nonUnique"]),
SeqInIndex: utils.Any2Int(re["seqInIndex"]),
})
}
return indexs
}
// 获取建表ddl
func (pm *PgsqlMetadata) GetCreateTableDdl(tableName string) []map[string]interface{} {
biz.IsTrue(tableName == "", "暂不支持获取pgsql建表DDL")
return nil
func (pm *PgsqlMetadata) GetCreateTableDdl(tableName string) string {
_, err := pm.di.Exec(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_DDL_KEY))
biz.ErrIsNilAppendErr(err, "创建ddl函数失败: %s")
_, schemaRes, _ := pm.di.SelectData("select current_schema() as schema")
schemaName := schemaRes[0]["schema"].(string)
ddlSql := fmt.Sprintf("select showcreatetable('%s','%s') as sql", schemaName, tableName)
_, res, err := pm.di.SelectData(ddlSql)
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
return res[0]["sql"].(string)
}
func (pm *PgsqlMetadata) GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]interface{}, error) {

View File

@@ -22,7 +22,7 @@ DROP TABLE IF EXISTS `t_db`;
CREATE TABLE `t_db` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库实例名称',
`host` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`host` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`port` int(8) NOT NULL,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,

View File

@@ -10,7 +10,7 @@ import (
"mayfly-go/pkg/utils"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
func InitTokenConfig() {

54
server/pkg/utils/any.go Normal file
View File

@@ -0,0 +1,54 @@
package utils
import (
"strconv"
)
// any类型转换为string, 如果any为nil则返回空字符串
func Any2String(val any) string {
if value, ok := val.(string); !ok {
return ""
} else {
return value
}
}
// any类型转换为int可将字符串或int64转换, 如果any为nil则返回0
func Any2Int(val any) int {
switch value := val.(type) {
case int:
return value
case string:
if intV, err := strconv.Atoi(value); err == nil {
return intV
}
case int64:
return int(value)
case uint64:
return int(value)
case int32:
return int(value)
case uint32:
return int(value)
case int16:
return int(value)
case uint16:
return int(value)
case int8:
return int(value)
case uint8:
return int(value)
default:
return 0
}
return 0
}
// any类型转换为int64, 如果any为nil则返回0
func Any2Int64(val any) int64 {
if value, ok := val.(int64); !ok {
return int64(Any2Int(val))
} else {
return value
}
}

View File

@@ -3,7 +3,7 @@ package assert
import "fmt"
// 断言条件为真不满足的panic
func IsTrue(condition bool, panicMsg string, params ...interface{}) {
func IsTrue(condition bool, panicMsg string, params ...any) {
if !condition {
if len(params) != 0 {
panic(fmt.Sprintf(panicMsg, params...))
@@ -12,10 +12,10 @@ func IsTrue(condition bool, panicMsg string, params ...interface{}) {
}
}
func State(condition bool, panicMsg string, params ...interface{}) {
func State(condition bool, panicMsg string, params ...any) {
IsTrue(condition, panicMsg, params...)
}
func NotEmpty(str string, panicMsg string, params ...interface{}) {
func NotEmpty(str string, panicMsg string, params ...any) {
IsTrue(str != "", panicMsg, params...)
}