mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: 新增数据库表信息查看及其他优化
This commit is contained in:
@@ -48,6 +48,22 @@ func (d *Db) DeleteDb(rc *ctx.ReqCtx) {
|
||||
d.DbApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
}
|
||||
|
||||
func (d *Db) TableInfos(rc *ctx.ReqCtx) {
|
||||
rc.ResData = d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).GetTableInfos()
|
||||
}
|
||||
|
||||
func (d *Db) TableIndex(rc *ctx.ReqCtx) {
|
||||
tn := rc.GinCtx.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
rc.ResData = d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).GetTableIndex(tn)
|
||||
}
|
||||
|
||||
func (d *Db) GetCreateTableDdl(rc *ctx.ReqCtx) {
|
||||
tn := rc.GinCtx.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
rc.ResData = d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).GetCreateTableDdl(tn)
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/exec-sql [get]
|
||||
func (d *Db) ExecSql(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
@@ -84,7 +100,6 @@ func (d *Db) ExecSql(rc *ctx.ReqCtx) {
|
||||
|
||||
rc.ResData = colAndRes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/t-metadata [get]
|
||||
|
||||
@@ -49,6 +49,10 @@ func (p *Project) SaveProject(rc *ctx.ReqCtx) {
|
||||
p.ProjectApp.SaveProject(project)
|
||||
}
|
||||
|
||||
func (p *Project) DelProject(rc *ctx.ReqCtx) {
|
||||
p.ProjectApp.DelProject(uint64(ginx.QueryInt(rc.GinCtx, "id", 0)))
|
||||
}
|
||||
|
||||
// 获取项目下的环境信息
|
||||
func (p *Project) GetProjectEnvs(rc *ctx.ReqCtx) {
|
||||
projectEnvs := &[]entity.ProjectEnv{}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/cache"
|
||||
"mayfly-go/base/global"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/server/devops/domain/entity"
|
||||
"mayfly-go/server/devops/domain/repository"
|
||||
@@ -90,17 +92,24 @@ func (d *dbAppImpl) Delete(id uint64) {
|
||||
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
|
||||
}
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
func (da *dbAppImpl) GetDbInstance(id uint64) *DbInstance {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
// Id不为0,则为需要缓存
|
||||
needCache := id != 0
|
||||
if needCache {
|
||||
load, ok := dbCache.Load(id)
|
||||
load, ok := dbCache.Get(id)
|
||||
if ok {
|
||||
return load.(*DbInstance)
|
||||
}
|
||||
}
|
||||
|
||||
d := da.GetById(id)
|
||||
biz.NotNil(d, "数据库信息不存在")
|
||||
global.Log.Infof("连接db: %s:%d/%s", d.Host, d.Port, d.Database)
|
||||
|
||||
DB, err := sql.Open(d.Type, getDsn(d))
|
||||
biz.ErrIsNil(err, fmt.Sprintf("Open %s failed, err:%v\n", d.Type, err))
|
||||
perr := DB.Ping()
|
||||
@@ -117,17 +126,23 @@ func (da *dbAppImpl) GetDbInstance(id uint64) *DbInstance {
|
||||
|
||||
dbi := &DbInstance{Id: id, Type: d.Type, db: DB}
|
||||
if needCache {
|
||||
dbCache.LoadOrStore(d.Id, dbi)
|
||||
dbCache.Put(id, dbi)
|
||||
}
|
||||
return dbi
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
var dbCache sync.Map
|
||||
// 客户端连接缓存,30分钟内没有访问则会被关闭
|
||||
var dbCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info(fmt.Sprintf("删除db连接缓存 id: %d", key))
|
||||
value.(*DbInstance).Close()
|
||||
})
|
||||
|
||||
func GetDbInstanceByCache(id uint64) *DbInstance {
|
||||
if load, ok := dbCache.Load(id); ok {
|
||||
if load, ok := dbCache.Get(fmt.Sprint(id)); ok {
|
||||
return load.(*DbInstance)
|
||||
}
|
||||
return nil
|
||||
@@ -153,13 +168,17 @@ type DbInstance struct {
|
||||
// 依次返回 列名数组,结果map,错误
|
||||
func (d *DbInstance) SelectData(sql string) ([]string, []map[string]string, error) {
|
||||
sql = strings.Trim(sql, " ")
|
||||
if !strings.HasPrefix(sql, "SELECT") && !strings.HasPrefix(sql, "select") {
|
||||
isSelect := strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select")
|
||||
isShow := strings.HasPrefix(sql, "show")
|
||||
|
||||
if !isSelect && !isShow {
|
||||
return nil, nil, errors.New("该sql非查询语句")
|
||||
}
|
||||
// 没加limit,则默认限制50条
|
||||
if !strings.Contains(sql, "limit") && !strings.Contains(sql, "LIMIT") {
|
||||
if isSelect && !strings.Contains(sql, "limit") && !strings.Contains(sql, "LIMIT") {
|
||||
sql = sql + " LIMIT 50"
|
||||
}
|
||||
|
||||
rows, err := d.db.Query(sql)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -224,10 +243,9 @@ func (d *DbInstance) Exec(sql string) (int64, error) {
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
// 关闭连接,并从缓存中移除
|
||||
// 关闭连接
|
||||
func (d *DbInstance) Close() {
|
||||
d.db.Close()
|
||||
dbCache.Delete(d.Id)
|
||||
}
|
||||
|
||||
// 获取dataSourceName
|
||||
@@ -241,6 +259,7 @@ func getDsn(d *entity.Db) string {
|
||||
func CloseDb(id uint64) {
|
||||
if di := GetDbInstanceByCache(id); di != nil {
|
||||
di.Close()
|
||||
dbCache.Delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,10 +271,22 @@ const (
|
||||
create_time createTime 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,
|
||||
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE table_schema = (SELECT database()) AND table_name = '%s'`
|
||||
|
||||
// mysql 列信息元数据
|
||||
MYSQL_COLOUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType,
|
||||
column_comment columnComment, column_key columnKey, extra from information_schema.columns
|
||||
WHERE table_name in (%s) AND table_schema = (SELECT database()) ORDER BY ordinal_position limit 15000`
|
||||
WHERE table_name in (%s) AND table_schema = (SELECT database()) ORDER BY ordinal_position limit 18000`
|
||||
)
|
||||
|
||||
func (d *DbInstance) GetTableMetedatas() []map[string]string {
|
||||
@@ -283,3 +314,30 @@ func (d *DbInstance) GetColumnMetadatas(tableNames ...string) []map[string]strin
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetTableInfos() []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = MYSQL_TABLE_INFO
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetTableIndex(tableName string) []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = fmt.Sprintf(MYSQL_INDEX_INFO, tableName)
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetCreateTableDdl(tableName string) []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = fmt.Sprintf("show create table %s ", tableName)
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ type Project interface {
|
||||
|
||||
SaveProject(project *entity.Project)
|
||||
|
||||
DelProject(id uint64)
|
||||
|
||||
// 根据项目id获取所有该项目下的环境信息列表
|
||||
ListEnvByProjectId(projectId uint64, listPtr interface{})
|
||||
|
||||
@@ -64,6 +66,12 @@ func (p *projectAppImpl) SaveProject(project *entity.Project) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) DelProject(id uint64) {
|
||||
p.projectRepo.Delete(id)
|
||||
p.projectEnvRepo.DeleteEnvs(id)
|
||||
p.projectMemberRepo.DeleteMems(id)
|
||||
}
|
||||
|
||||
// 根据项目id获取所有该项目下的环境信息列表
|
||||
func (p *projectAppImpl) ListEnvByProjectId(projectId uint64, listPtr interface{}) {
|
||||
p.projectEnvRepo.ListEnv(&entity.ProjectEnv{ProjectId: projectId}, listPtr)
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/cache"
|
||||
"mayfly-go/base/global"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/server/devops/domain/entity"
|
||||
"mayfly-go/server/devops/domain/repository"
|
||||
"mayfly-go/server/devops/infrastructure/persistence"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
@@ -85,7 +88,7 @@ func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
|
||||
// Id不为0,则为需要缓存
|
||||
needCache := id != 0
|
||||
if needCache {
|
||||
load, ok := redisCache.Load(id)
|
||||
load, ok := redisCache.Get(id)
|
||||
if ok {
|
||||
return load.(*RedisInstance)
|
||||
}
|
||||
@@ -93,6 +96,8 @@ func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
|
||||
// 缓存不存在,则回调获取redis信息
|
||||
re := r.GetById(id)
|
||||
biz.NotNil(re, "redis信息不存在")
|
||||
global.Log.Infof("连接redis: %s", re.Host)
|
||||
|
||||
rcli := redis.NewClient(&redis.Options{
|
||||
Addr: re.Host,
|
||||
Password: re.Password, // no password set
|
||||
@@ -104,14 +109,20 @@ func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
|
||||
|
||||
ri := &RedisInstance{Id: id, Cli: rcli}
|
||||
if needCache {
|
||||
redisCache.LoadOrStore(re.Id, ri)
|
||||
redisCache.Put(re.Id, ri)
|
||||
}
|
||||
return ri
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
var redisCache sync.Map
|
||||
// redis客户端连接缓存,30分钟内没有访问则会被关闭
|
||||
var redisCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info(fmt.Sprintf("删除redis连接缓存 id: %d", key))
|
||||
value.(*RedisInstance).Cli.Close()
|
||||
})
|
||||
|
||||
// redis实例
|
||||
type RedisInstance struct {
|
||||
@@ -121,7 +132,7 @@ type RedisInstance struct {
|
||||
|
||||
// 关闭redis连接
|
||||
func CloseRedis(id uint64) {
|
||||
if load, ok := redisCache.Load(id); ok {
|
||||
if load, ok := redisCache.Get(id); ok {
|
||||
load.(*RedisInstance).Cli.Close()
|
||||
redisCache.Delete(id)
|
||||
}
|
||||
|
||||
@@ -13,4 +13,6 @@ type Project interface {
|
||||
Save(p *entity.Project)
|
||||
|
||||
Update(project *entity.Project)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
|
||||
@@ -7,4 +7,6 @@ type ProjectEnv interface {
|
||||
ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(entity *entity.ProjectEnv)
|
||||
|
||||
DeleteEnvs(projectId uint64)
|
||||
}
|
||||
|
||||
@@ -16,4 +16,6 @@ type ProjectMemeber interface {
|
||||
|
||||
// 根据成员id和项目id删除关联关系
|
||||
DeleteByPidMid(projectId, accountId uint64)
|
||||
|
||||
DeleteMems(projectId uint64)
|
||||
}
|
||||
|
||||
@@ -28,14 +28,14 @@ type Cli struct {
|
||||
// 机器客户端连接缓存,30分钟内没有访问则会被关闭
|
||||
var cliCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key string, value interface{}) {
|
||||
global.Log.Info(fmt.Sprintf("删除机器连接缓存 id: %s", key))
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info(fmt.Sprintf("删除机器连接缓存 id: %d", key))
|
||||
value.(*Cli).Close()
|
||||
})
|
||||
|
||||
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
|
||||
func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, error) {
|
||||
cli, err := cliCache.ComputeIfAbsent(fmt.Sprint(machineId), func(key string) (interface{}, error) {
|
||||
cli, err := cliCache.ComputeIfAbsent(machineId, func(key interface{}) (interface{}, error) {
|
||||
c, err := newClient(getMachine(machineId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -210,7 +210,7 @@ func (c *Cli) RunTerminal(shell string, stdout, stderr io.Writer) error {
|
||||
|
||||
// 关闭指定机器的连接
|
||||
func Close(id uint64) {
|
||||
if cli, ok := cliCache.Get(fmt.Sprint(id)); ok {
|
||||
if cli, ok := cliCache.Get(id); ok {
|
||||
cli.(*Cli).Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,3 +18,7 @@ func (p *projectEnvRepo) ListEnv(condition *entity.ProjectEnv, toEntity interfac
|
||||
func (p *projectEnvRepo) Save(entity *entity.ProjectEnv) {
|
||||
biz.ErrIsNilAppendErr(model.Insert(entity), "保存环境失败:%s")
|
||||
}
|
||||
|
||||
func (p *projectEnvRepo) DeleteEnvs(projectId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectEnv{ProjectId: projectId})
|
||||
}
|
||||
|
||||
@@ -26,3 +26,7 @@ func (p *projectMemeberRepo) GetPageList(condition *entity.ProjectMember, pagePa
|
||||
func (p *projectMemeberRepo) DeleteByPidMid(projectId, accountId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId})
|
||||
}
|
||||
|
||||
func (p *projectMemeberRepo) DeleteMems(projectId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId})
|
||||
}
|
||||
|
||||
@@ -26,3 +26,7 @@ func (p *projectRepo) Save(project *entity.Project) {
|
||||
func (p *projectRepo) Update(project *entity.Project) {
|
||||
biz.ErrIsNil(model.UpdateById(project), "更新项目信息")
|
||||
}
|
||||
|
||||
func (p *projectRepo) Delete(id uint64) {
|
||||
model.DeleteById(new(entity.Project), id)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,18 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
Handle(d.DeleteDb)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-infos", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.TableInfos)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-index", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.TableIndex)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-create-ddl", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithNeedToken(false).Handle(d.GetCreateTableDdl)
|
||||
})
|
||||
|
||||
// db.GET(":dbId/exec-sql", controllers.SelectData)
|
||||
db.GET(":dbId/exec-sql", func(g *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql语句"))
|
||||
|
||||
@@ -34,6 +34,15 @@ func InitProjectRouter(router *gin.RouterGroup) {
|
||||
Handle(m.SaveProject)
|
||||
})
|
||||
|
||||
delProjectLog := ctx.NewLogInfo("删除项目信息")
|
||||
delPP := ctx.NewPermission("project:del")
|
||||
// 删除项目
|
||||
project.DELETE("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delProjectLog).
|
||||
WithRequiredPermission(delPP).
|
||||
Handle(m.DelProject)
|
||||
})
|
||||
|
||||
// 获取项目下的环境信息列表
|
||||
project.GET("/:projectId/envs", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectEnvs)
|
||||
|
||||
Reference in New Issue
Block a user