mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 00:10:25 +08:00 
			
		
		
		
	feat: 完善数据库信息保存以及项目、redis相关操作
This commit is contained in:
		
							
								
								
									
										3
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							@@ -4,6 +4,7 @@
 | 
				
			|||||||
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
 | 
					    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
 | 
				
			||||||
    "version": "0.2.0",
 | 
					    "version": "0.2.0",
 | 
				
			||||||
    "configurations": [
 | 
					    "configurations": [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "mayfly-go",
 | 
					            "name": "mayfly-go",
 | 
				
			||||||
            "type": "go",
 | 
					            "type": "go",
 | 
				
			||||||
@@ -12,6 +13,6 @@
 | 
				
			|||||||
            "program": "${fileDirname}/main.go",
 | 
					            "program": "${fileDirname}/main.go",
 | 
				
			||||||
            "env": {},
 | 
					            "env": {},
 | 
				
			||||||
            "args": []
 | 
					            "args": []
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -6,18 +6,11 @@ type BizError struct {
 | 
				
			|||||||
	err  string
 | 
						err  string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					var (
 | 
				
			||||||
	SuccessCode = 200
 | 
						Success       *BizError = NewBizErrCode(200, "success")
 | 
				
			||||||
	SuccessMsg  = "success"
 | 
						BizErr        *BizError = NewBizErrCode(400, "biz error")
 | 
				
			||||||
 | 
						ServerError   *BizError = NewBizErrCode(500, "server error")
 | 
				
			||||||
	BizErrorCode = 400
 | 
						PermissionErr *BizError = NewBizErrCode(501, "token error")
 | 
				
			||||||
	BizErrorMsg  = "error"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ServerErrorCode = 500
 | 
					 | 
				
			||||||
	ServerErrorMsg  = "server error"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TokenErrorCode = 501
 | 
					 | 
				
			||||||
	TokenErrorMsg  = "token error"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 错误消息
 | 
					// 错误消息
 | 
				
			||||||
@@ -32,7 +25,7 @@ func (e *BizError) Code() int16 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 创建业务逻辑错误结构体,默认为业务逻辑错误
 | 
					// 创建业务逻辑错误结构体,默认为业务逻辑错误
 | 
				
			||||||
func NewBizErr(msg string) *BizError {
 | 
					func NewBizErr(msg string) *BizError {
 | 
				
			||||||
	return &BizError{code: BizErrorCode, err: msg}
 | 
						return &BizError{code: BizErr.code, err: msg}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 创建业务逻辑错误结构体,可设置指定错误code
 | 
					// 创建业务逻辑错误结构体,可设置指定错误code
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								base/captcha/captcha.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								base/captcha/captcha.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					package captcha
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mojocn/base64Captcha"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var store = base64Captcha.DefaultMemStore
 | 
				
			||||||
 | 
					var driver base64Captcha.Driver = base64Captcha.DefaultDriverDigit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 生成验证码
 | 
				
			||||||
 | 
					func Generate() (string, string) {
 | 
				
			||||||
 | 
						c := base64Captcha.NewCaptcha(driver, store)
 | 
				
			||||||
 | 
						// 获取
 | 
				
			||||||
 | 
						id, b64s, err := c.Generate()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "获取验证码错误: %s")
 | 
				
			||||||
 | 
						return id, b64s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 验证验证码
 | 
				
			||||||
 | 
					func Verify(id string, val string) bool {
 | 
				
			||||||
 | 
						if id == "" || val == "" {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 同时清理掉这个图片
 | 
				
			||||||
 | 
						return store.Verify(id, val, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,9 +4,13 @@ import (
 | 
				
			|||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/base/utils"
 | 
						"mayfly-go/base/utils"
 | 
				
			||||||
 | 
						"mayfly-go/base/utils/assert"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 配置文件映射对象
 | 
				
			||||||
 | 
					var Conf *Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录")
 | 
						configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录")
 | 
				
			||||||
	flag.Parse()
 | 
						flag.Parse()
 | 
				
			||||||
@@ -18,6 +22,8 @@ func init() {
 | 
				
			|||||||
	if err := utils.LoadYml(startConfigParam.ConfigFilePath, yc); err != nil {
 | 
						if err := utils.LoadYml(startConfigParam.ConfigFilePath, yc); err != nil {
 | 
				
			||||||
		panic(fmt.Sprintf("读取配置文件[%s]失败: %s", startConfigParam.ConfigFilePath, err.Error()))
 | 
							panic(fmt.Sprintf("读取配置文件[%s]失败: %s", startConfigParam.ConfigFilePath, err.Error()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// 校验配置文件内容信息
 | 
				
			||||||
 | 
						yc.Valid()
 | 
				
			||||||
	Conf = yc
 | 
						Conf = yc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,12 +39,17 @@ var startConfigParam *CmdConfigParam
 | 
				
			|||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	App    *App    `yaml:"app"`
 | 
						App    *App    `yaml:"app"`
 | 
				
			||||||
	Server *Server `yaml:"server"`
 | 
						Server *Server `yaml:"server"`
 | 
				
			||||||
 | 
						Jwt    *Jwt    `yaml:"jwt"`
 | 
				
			||||||
	Redis  *Redis  `yaml:"redis"`
 | 
						Redis  *Redis  `yaml:"redis"`
 | 
				
			||||||
	Mysql  *Mysql  `yaml:"mysql"`
 | 
						Mysql  *Mysql  `yaml:"mysql"`
 | 
				
			||||||
 | 
						Log    *Log    `yaml:"log"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 配置文件映射对象
 | 
					// 配置文件内容校验
 | 
				
			||||||
var Conf *Config
 | 
					func (c *Config) Valid() {
 | 
				
			||||||
 | 
						assert.IsTrue(c.Jwt != nil, "配置文件的[jwt]信息不能为空")
 | 
				
			||||||
 | 
						c.Jwt.Valid()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取执行可执行文件时,指定的启动参数
 | 
					// 获取执行可执行文件时,指定的启动参数
 | 
				
			||||||
func getStartConfig() *CmdConfigParam {
 | 
					func getStartConfig() *CmdConfigParam {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								base/config/jwt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								base/config/jwt.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/base/utils/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Jwt struct {
 | 
				
			||||||
 | 
						Key        string `yaml:"key"`
 | 
				
			||||||
 | 
						ExpireTime uint64 `yaml:"expire-time"` // 过期时间,单位分钟
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (j *Jwt) Valid() {
 | 
				
			||||||
 | 
						assert.IsTrue(j.Key != "", "config.yml之 [jwt.key] 不能为空")
 | 
				
			||||||
 | 
						assert.IsTrue(j.ExpireTime != 0, "config.yml之 [jwt.expire-time] 不能为空")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								base/config/log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								base/config/log.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Log struct {
 | 
				
			||||||
 | 
						Level string   `yaml:"level"`
 | 
				
			||||||
 | 
						File  *LogFile `yaml:"file"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LogFile struct {
 | 
				
			||||||
 | 
						Name string `yaml:"name"`
 | 
				
			||||||
 | 
						Path string `yaml:"path"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取完整路径文件名
 | 
				
			||||||
 | 
					func (l *LogFile) GetFilename() string {
 | 
				
			||||||
 | 
						var filepath, filename string
 | 
				
			||||||
 | 
						if fp := l.Path; fp == "" {
 | 
				
			||||||
 | 
							filepath = "./"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							filepath = fp
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if fn := l.Name; fn == "" {
 | 
				
			||||||
 | 
							filename = "default.log"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							filename = fn
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return path.Join(filepath, filename)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,6 +6,7 @@ type Server struct {
 | 
				
			|||||||
	Port       int            `yaml:"port"`
 | 
						Port       int            `yaml:"port"`
 | 
				
			||||||
	Model      string         `yaml:"model"`
 | 
						Model      string         `yaml:"model"`
 | 
				
			||||||
	Cors       bool           `yaml:"cors"`
 | 
						Cors       bool           `yaml:"cors"`
 | 
				
			||||||
 | 
						Tls        *Tls           `yaml:"tls"`
 | 
				
			||||||
	Static     *[]*Static     `yaml:"static"`
 | 
						Static     *[]*Static     `yaml:"static"`
 | 
				
			||||||
	StaticFile *[]*StaticFile `yaml:"static-file"`
 | 
						StaticFile *[]*StaticFile `yaml:"static-file"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -23,3 +24,9 @@ type StaticFile struct {
 | 
				
			|||||||
	RelativePath string `yaml:"relative-path"`
 | 
						RelativePath string `yaml:"relative-path"`
 | 
				
			||||||
	Filepath     string `yaml:"filepath"`
 | 
						Filepath     string `yaml:"filepath"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Tls struct {
 | 
				
			||||||
 | 
						Enable   bool   `yaml:"enable"`    // 是否启用tls
 | 
				
			||||||
 | 
						KeyFile  string `yaml:"key-file"`  // 私钥文件路径
 | 
				
			||||||
 | 
						CertFile string `yaml:"cert-file"` // 证书文件路径
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,14 +9,9 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"runtime/debug"
 | 
						"runtime/debug"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log "github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	log.SetFormatter(new(logger.LogFormatter))
 | 
					 | 
				
			||||||
	log.SetReportCaller(true)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type LogInfo struct {
 | 
					type LogInfo struct {
 | 
				
			||||||
	LogResp     bool   // 是否记录返回结果
 | 
						LogResp     bool   // 是否记录返回结果
 | 
				
			||||||
	Description string // 请求描述
 | 
						Description string // 请求描述
 | 
				
			||||||
@@ -37,7 +32,7 @@ func LogHandler(rc *ReqCtx) error {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lfs := log.Fields{}
 | 
						lfs := logrus.Fields{}
 | 
				
			||||||
	if la := rc.LoginAccount; la != nil {
 | 
						if la := rc.LoginAccount; la != nil {
 | 
				
			||||||
		lfs["uid"] = la.Id
 | 
							lfs["uid"] = la.Id
 | 
				
			||||||
		lfs["uname"] = la.Username
 | 
							lfs["uname"] = la.Username
 | 
				
			||||||
@@ -47,10 +42,10 @@ func LogHandler(rc *ReqCtx) error {
 | 
				
			|||||||
	lfs[req.Method] = req.URL.Path
 | 
						lfs[req.Method] = req.URL.Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := rc.Err; err != nil {
 | 
						if err := rc.Err; err != nil {
 | 
				
			||||||
		log.WithFields(lfs).Error(getErrMsg(rc, err))
 | 
							logger.Log.WithFields(lfs).Error(getErrMsg(rc, err))
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.WithFields(lfs).Info(getLogMsg(rc))
 | 
						logger.Log.WithFields(lfs).Info(getLogMsg(rc))
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/base/biz"
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
	"mayfly-go/base/cache"
 | 
						"mayfly-go/base/cache"
 | 
				
			||||||
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,7 +38,7 @@ type DefaultPermissionCodeRegistry struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (r *DefaultPermissionCodeRegistry) SaveCodes(userId uint64, codes []string) {
 | 
					func (r *DefaultPermissionCodeRegistry) SaveCodes(userId uint64, codes []string) {
 | 
				
			||||||
	if r.cache == nil {
 | 
						if r.cache == nil {
 | 
				
			||||||
		r.cache = cache.NewTimedCache(30*time.Minute, 5*time.Second).WithUpdateAccessTime(true)
 | 
							r.cache = cache.NewTimedCache(time.Minute*time.Duration(config.Conf.Jwt.ExpireTime), 5*time.Second)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r.cache.Put(fmt.Sprintf("%v", userId), codes)
 | 
						r.cache.Put(fmt.Sprintf("%v", userId), codes)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -79,7 +80,7 @@ func SetPermissionCodeRegistery(pcr PermissionCodeRegistry) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	permissionCodeRegistry PermissionCodeRegistry = &DefaultPermissionCodeRegistry{}
 | 
						permissionCodeRegistry PermissionCodeRegistry = &DefaultPermissionCodeRegistry{}
 | 
				
			||||||
	permissionError                               = biz.NewBizErrCode(biz.TokenErrorCode, biz.TokenErrorMsg)
 | 
						// permissionError                               = biz.NewBizErrCode(biz.TokenErrorCode, biz.TokenErrorMsg)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func PermissionHandler(rc *ReqCtx) error {
 | 
					func PermissionHandler(rc *ReqCtx) error {
 | 
				
			||||||
@@ -94,16 +95,16 @@ func PermissionHandler(rc *ReqCtx) error {
 | 
				
			|||||||
		tokenStr = rc.GinCtx.Query("token")
 | 
							tokenStr = rc.GinCtx.Query("token")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if tokenStr == "" {
 | 
						if tokenStr == "" {
 | 
				
			||||||
		return permissionError
 | 
							return biz.PermissionErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	loginAccount, err := ParseToken(tokenStr)
 | 
						loginAccount, err := ParseToken(tokenStr)
 | 
				
			||||||
	if err != nil || loginAccount == nil {
 | 
						if err != nil || loginAccount == nil {
 | 
				
			||||||
		return permissionError
 | 
							return biz.PermissionErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 权限不为nil,并且permission code不为空,则校验是否有权限code
 | 
						// 权限不为nil,并且permission code不为空,则校验是否有权限code
 | 
				
			||||||
	if permission != nil && permission.Code != "" {
 | 
						if permission != nil && permission.Code != "" {
 | 
				
			||||||
		if !permissionCodeRegistry.HasCode(loginAccount.Id, permission.Code) {
 | 
							if !permissionCodeRegistry.HasCode(loginAccount.Id, permission.Code) {
 | 
				
			||||||
			return permissionError
 | 
								return biz.PermissionErr
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ package ctx
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"mayfly-go/base/ginx"
 | 
						"mayfly-go/base/ginx"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
 | 
						"mayfly-go/base/utils/assert"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
@@ -37,9 +38,7 @@ func (rc *ReqCtx) Handle(handler HandlerFunc) {
 | 
				
			|||||||
		// 应用所有请求后置处理器
 | 
							// 应用所有请求后置处理器
 | 
				
			||||||
		ApplyHandlerInterceptor(afterHandlers, rc)
 | 
							ApplyHandlerInterceptor(afterHandlers, rc)
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	if ginCtx == nil {
 | 
						assert.IsTrue(ginCtx != nil, "ginContext == nil")
 | 
				
			||||||
		panic("ginContext == nil")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 默认为不记录请求参数,可在handler回调函数中覆盖赋值
 | 
						// 默认为不记录请求参数,可在handler回调函数中覆盖赋值
 | 
				
			||||||
	rc.ReqParam = 0
 | 
						rc.ReqParam = 0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,15 +4,16 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"mayfly-go/base/biz"
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/dgrijalva/jwt-go"
 | 
						"github.com/dgrijalva/jwt-go"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					var (
 | 
				
			||||||
	JwtKey  = "mykey"
 | 
						JwtKey  = config.Conf.Jwt.Key
 | 
				
			||||||
	ExpTime = time.Hour * 24 * 7
 | 
						ExpTime = config.Conf.Jwt.ExpireTime
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 创建用户token
 | 
					// 创建用户token
 | 
				
			||||||
@@ -22,7 +23,7 @@ func CreateToken(userId uint64, username string) string {
 | 
				
			|||||||
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
						token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
				
			||||||
		"id":       userId,
 | 
							"id":       userId,
 | 
				
			||||||
		"username": username,
 | 
							"username": username,
 | 
				
			||||||
		"exp":      time.Now().Add(ExpTime).Unix(),
 | 
							"exp":      time.Now().Add(time.Minute * time.Duration(ExpTime)).Unix(),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 使用自定义字符串加密 and get the complete encoded token as a string
 | 
						// 使用自定义字符串加密 and get the complete encoded token as a string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
package ginx
 | 
					package ginx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"mayfly-go/base/biz"
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
	"mayfly-go/base/global"
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
@@ -13,14 +12,14 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 绑定并校验请求结构体参数
 | 
					// 绑定并校验请求结构体参数
 | 
				
			||||||
func BindJsonAndValid(g *gin.Context, data interface{}) {
 | 
					func BindJsonAndValid(g *gin.Context, data interface{}) {
 | 
				
			||||||
	if err := g.BindJSON(data); err != nil {
 | 
						if err := g.ShouldBindJSON(data); err != nil {
 | 
				
			||||||
		panic(biz.NewBizErr(err.Error()))
 | 
							panic(biz.NewBizErr(err.Error()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 绑定查询字符串到
 | 
					// 绑定查询字符串到
 | 
				
			||||||
func BindQuery(g *gin.Context, data interface{}) {
 | 
					func BindQuery(g *gin.Context, data interface{}) {
 | 
				
			||||||
	if err := g.BindQuery(data); err != nil {
 | 
						if err := g.ShouldBindQuery(data); err != nil {
 | 
				
			||||||
		panic(biz.NewBizErr(err.Error()))
 | 
							panic(biz.NewBizErr(err.Error()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -44,7 +43,6 @@ func QueryInt(g *gin.Context, qm string, defaultInt int) int {
 | 
				
			|||||||
// 获取路径参数
 | 
					// 获取路径参数
 | 
				
			||||||
func PathParamInt(g *gin.Context, pm string) int {
 | 
					func PathParamInt(g *gin.Context, pm string) int {
 | 
				
			||||||
	value, _ := strconv.Atoi(g.Param(pm))
 | 
						value, _ := strconv.Atoi(g.Param(pm))
 | 
				
			||||||
	biz.IsTrue(value != 0, fmt.Sprintf("%s不存在", pm))
 | 
					 | 
				
			||||||
	return value
 | 
						return value
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,7 +62,7 @@ func SuccessRes(g *gin.Context, data interface{}) {
 | 
				
			|||||||
func ErrorRes(g *gin.Context, err interface{}) {
 | 
					func ErrorRes(g *gin.Context, err interface{}) {
 | 
				
			||||||
	switch t := err.(type) {
 | 
						switch t := err.(type) {
 | 
				
			||||||
	case *biz.BizError:
 | 
						case *biz.BizError:
 | 
				
			||||||
		g.JSON(http.StatusOK, model.Error(t.Code(), t.Error()))
 | 
							g.JSON(http.StatusOK, model.Error(t))
 | 
				
			||||||
		break
 | 
							break
 | 
				
			||||||
	case error:
 | 
						case error:
 | 
				
			||||||
		g.JSON(http.StatusOK, model.ServerError())
 | 
							g.JSON(http.StatusOK, model.ServerError())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,11 @@
 | 
				
			|||||||
package global
 | 
					package global
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"mayfly-go/base/config"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
	"mayfly-go/base/logger"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 日志
 | 
					var (
 | 
				
			||||||
var Log = logger.Log
 | 
						Log *logrus.Logger // 日志
 | 
				
			||||||
 | 
						Db  *gorm.DB       // gorm
 | 
				
			||||||
// config.yml配置文件映射对象
 | 
					)
 | 
				
			||||||
var Config = config.Conf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// gorm
 | 
					 | 
				
			||||||
var Db *gorm.DB
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,14 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"mime/multipart"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var client = &http.Client{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 默认超时
 | 
					// 默认超时
 | 
				
			||||||
const DefTimeout = 60
 | 
					const DefTimeout = 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,6 +26,13 @@ type RequestWrapper struct {
 | 
				
			|||||||
	header  map[string]string
 | 
						header  map[string]string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MultipartFile struct {
 | 
				
			||||||
 | 
						FieldName string // 字段名
 | 
				
			||||||
 | 
						FileName  string // 文件名
 | 
				
			||||||
 | 
						FilePath  string // 文件路径,文件路径不为空,则优先读取文件路径的内容
 | 
				
			||||||
 | 
						Bytes     []byte // 文件内容
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 创建一个请求
 | 
					// 创建一个请求
 | 
				
			||||||
func NewRequest(url string) *RequestWrapper {
 | 
					func NewRequest(url string) *RequestWrapper {
 | 
				
			||||||
	return &RequestWrapper{url: url}
 | 
						return &RequestWrapper{url: url}
 | 
				
			||||||
@@ -31,12 +42,21 @@ func (r *RequestWrapper) Url(url string) *RequestWrapper {
 | 
				
			|||||||
	r.url = url
 | 
						r.url = url
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequestWrapper) Header(name, value string) *RequestWrapper {
 | 
				
			||||||
 | 
						if r.header == nil {
 | 
				
			||||||
 | 
							r.header = make(map[string]string)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r.header[name] = value
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RequestWrapper) Timeout(timeout int) *RequestWrapper {
 | 
					func (r *RequestWrapper) Timeout(timeout int) *RequestWrapper {
 | 
				
			||||||
	r.timeout = timeout
 | 
						r.timeout = timeout
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RequestWrapper) GetByParam(paramMap map[string]string) ResponseWrapper {
 | 
					func (r *RequestWrapper) GetByParam(paramMap map[string]string) *ResponseWrapper {
 | 
				
			||||||
	var params string
 | 
						var params string
 | 
				
			||||||
	for k, v := range paramMap {
 | 
						for k, v := range paramMap {
 | 
				
			||||||
		if params != "" {
 | 
							if params != "" {
 | 
				
			||||||
@@ -50,13 +70,13 @@ func (r *RequestWrapper) GetByParam(paramMap map[string]string) ResponseWrapper
 | 
				
			|||||||
	return r.Get()
 | 
						return r.Get()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RequestWrapper) Get() ResponseWrapper {
 | 
					func (r *RequestWrapper) Get() *ResponseWrapper {
 | 
				
			||||||
	r.method = "GET"
 | 
						r.method = "GET"
 | 
				
			||||||
	r.body = nil
 | 
						r.body = nil
 | 
				
			||||||
	return request(r)
 | 
						return request(r)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RequestWrapper) PostJson(body string) ResponseWrapper {
 | 
					func (r *RequestWrapper) PostJson(body string) *ResponseWrapper {
 | 
				
			||||||
	buf := bytes.NewBufferString(body)
 | 
						buf := bytes.NewBufferString(body)
 | 
				
			||||||
	r.method = "POST"
 | 
						r.method = "POST"
 | 
				
			||||||
	r.body = buf
 | 
						r.body = buf
 | 
				
			||||||
@@ -67,7 +87,7 @@ func (r *RequestWrapper) PostJson(body string) ResponseWrapper {
 | 
				
			|||||||
	return request(r)
 | 
						return request(r)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RequestWrapper) PostObj(body interface{}) ResponseWrapper {
 | 
					func (r *RequestWrapper) PostObj(body interface{}) *ResponseWrapper {
 | 
				
			||||||
	marshal, err := json.Marshal(body)
 | 
						marshal, err := json.Marshal(body)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return createRequestError(errors.New("解析json obj错误"))
 | 
							return createRequestError(errors.New("解析json obj错误"))
 | 
				
			||||||
@@ -75,7 +95,7 @@ func (r *RequestWrapper) PostObj(body interface{}) ResponseWrapper {
 | 
				
			|||||||
	return r.PostJson(string(marshal))
 | 
						return r.PostJson(string(marshal))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *RequestWrapper) PostParams(params string) ResponseWrapper {
 | 
					func (r *RequestWrapper) PostParams(params string) *ResponseWrapper {
 | 
				
			||||||
	buf := bytes.NewBufferString(params)
 | 
						buf := bytes.NewBufferString(params)
 | 
				
			||||||
	r.method = "POST"
 | 
						r.method = "POST"
 | 
				
			||||||
	r.body = buf
 | 
						r.body = buf
 | 
				
			||||||
@@ -86,9 +106,54 @@ func (r *RequestWrapper) PostParams(params string) ResponseWrapper {
 | 
				
			|||||||
	return request(r)
 | 
						return request(r)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RequestWrapper) PostMulipart(files []MultipartFile, reqParams map[string]string) *ResponseWrapper {
 | 
				
			||||||
 | 
						buf := &bytes.Buffer{}
 | 
				
			||||||
 | 
						// 文件写入 buf
 | 
				
			||||||
 | 
						writer := multipart.NewWriter(buf)
 | 
				
			||||||
 | 
						for _, uploadFile := range files {
 | 
				
			||||||
 | 
							var reader io.Reader
 | 
				
			||||||
 | 
							// 如果文件路径不为空,则读取该路径文件,否则使用bytes
 | 
				
			||||||
 | 
							if uploadFile.FilePath != "" {
 | 
				
			||||||
 | 
								file, err := os.Open(uploadFile.FilePath)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return createRequestError(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer file.Close()
 | 
				
			||||||
 | 
								reader = file
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								reader = bytes.NewBuffer(uploadFile.Bytes)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							part, err := writer.CreateFormFile(uploadFile.FieldName, uploadFile.FileName)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return createRequestError(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err = io.Copy(part, reader)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 如果有其他参数,则写入body
 | 
				
			||||||
 | 
						if reqParams != nil {
 | 
				
			||||||
 | 
							for k, v := range reqParams {
 | 
				
			||||||
 | 
								if err := writer.WriteField(k, v); err != nil {
 | 
				
			||||||
 | 
									return createRequestError(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := writer.Close(); err != nil {
 | 
				
			||||||
 | 
							return createRequestError(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.method = "POST"
 | 
				
			||||||
 | 
						r.body = buf
 | 
				
			||||||
 | 
						if r.header == nil {
 | 
				
			||||||
 | 
							r.header = make(map[string]string)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r.header["Content-type"] = writer.FormDataContentType()
 | 
				
			||||||
 | 
						return request(r)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ResponseWrapper struct {
 | 
					type ResponseWrapper struct {
 | 
				
			||||||
	StatusCode int
 | 
						StatusCode int
 | 
				
			||||||
	Body       string
 | 
						Body       []byte
 | 
				
			||||||
	Header     http.Header
 | 
						Header     http.Header
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,28 +161,33 @@ func (r *ResponseWrapper) IsSuccess() bool {
 | 
				
			|||||||
	return r.StatusCode == 200
 | 
						return r.StatusCode == 200
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *ResponseWrapper) ToObj(obj interface{}) {
 | 
					func (r *ResponseWrapper) BodyToObj(objPtr interface{}) error {
 | 
				
			||||||
	if !r.IsSuccess() {
 | 
						_ = json.Unmarshal(r.Body, &objPtr)
 | 
				
			||||||
		return
 | 
						return r.getError()
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_ = json.Unmarshal([]byte(r.Body), &obj)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *ResponseWrapper) ToMap() map[string]interface{} {
 | 
					func (r *ResponseWrapper) BodyToString() (string, error) {
 | 
				
			||||||
	if !r.IsSuccess() {
 | 
						return string(r.Body), r.getError()
 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *ResponseWrapper) BodyToMap() (map[string]interface{}, error) {
 | 
				
			||||||
	var res map[string]interface{}
 | 
						var res map[string]interface{}
 | 
				
			||||||
	err := json.Unmarshal([]byte(r.Body), &res)
 | 
						err := json.Unmarshal(r.Body, &res)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return res
 | 
						return res, r.getError()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func request(rw *RequestWrapper) ResponseWrapper {
 | 
					func (r *ResponseWrapper) getError() error {
 | 
				
			||||||
	wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
 | 
						if !r.IsSuccess() {
 | 
				
			||||||
	client := &http.Client{}
 | 
							return errors.New(string(r.Body))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func request(rw *RequestWrapper) *ResponseWrapper {
 | 
				
			||||||
 | 
						wrapper := &ResponseWrapper{StatusCode: 0, Header: make(http.Header)}
 | 
				
			||||||
	timeout := rw.timeout
 | 
						timeout := rw.timeout
 | 
				
			||||||
	if timeout > 0 {
 | 
						if timeout > 0 {
 | 
				
			||||||
		client.Timeout = time.Duration(timeout) * time.Second
 | 
							client.Timeout = time.Duration(timeout) * time.Second
 | 
				
			||||||
@@ -132,17 +202,17 @@ func request(rw *RequestWrapper) ResponseWrapper {
 | 
				
			|||||||
	setRequestHeader(req, rw.header)
 | 
						setRequestHeader(req, rw.header)
 | 
				
			||||||
	resp, err := client.Do(req)
 | 
						resp, err := client.Do(req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
 | 
							wrapper.Body = []byte(fmt.Sprintf("执行HTTP请求错误-%s", err.Error()))
 | 
				
			||||||
		return wrapper
 | 
							return wrapper
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer resp.Body.Close()
 | 
						defer resp.Body.Close()
 | 
				
			||||||
	body, err := ioutil.ReadAll(resp.Body)
 | 
						body, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
 | 
							wrapper.Body = []byte(fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error()))
 | 
				
			||||||
		return wrapper
 | 
							return wrapper
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wrapper.StatusCode = resp.StatusCode
 | 
						wrapper.StatusCode = resp.StatusCode
 | 
				
			||||||
	wrapper.Body = string(body)
 | 
						wrapper.Body = body
 | 
				
			||||||
	wrapper.Header = resp.Header
 | 
						wrapper.Header = resp.Header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return wrapper
 | 
						return wrapper
 | 
				
			||||||
@@ -155,7 +225,6 @@ func setRequestHeader(req *http.Request, header map[string]string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createRequestError(err error) ResponseWrapper {
 | 
					func createRequestError(err error) *ResponseWrapper {
 | 
				
			||||||
	errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
 | 
						return &ResponseWrapper{0, []byte(fmt.Sprintf("创建HTTP请求错误-%s", err.Error())), make(http.Header)}
 | 
				
			||||||
	return ResponseWrapper{0, errorMessage, make(http.Header)}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,9 @@ package logger
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,12 +14,38 @@ import (
 | 
				
			|||||||
var Log = logrus.New()
 | 
					var Log = logrus.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	// customFormatter := new(logrus.TextFormatter)
 | 
					 | 
				
			||||||
	// customFormatter.TimestampFormat = "2006-01-02 15:04:05.000"
 | 
					 | 
				
			||||||
	// customFormatter.FullTimestamp = true
 | 
					 | 
				
			||||||
	Log.SetFormatter(new(LogFormatter))
 | 
						Log.SetFormatter(new(LogFormatter))
 | 
				
			||||||
	Log.SetReportCaller(true)
 | 
						Log.SetReportCaller(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logConf := config.Conf.Log
 | 
				
			||||||
 | 
						// 如果不存在日志配置信息,则默认debug级别
 | 
				
			||||||
 | 
						if logConf == nil {
 | 
				
			||||||
		Log.SetLevel(logrus.DebugLevel)
 | 
							Log.SetLevel(logrus.DebugLevel)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 根据配置文件设置日志级别
 | 
				
			||||||
 | 
						if level := logConf.Level; level != "" {
 | 
				
			||||||
 | 
							l, err := logrus.ParseLevel(level)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf("日志级别不存在: %s", level))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Log.SetLevel(l)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							Log.SetLevel(logrus.DebugLevel)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if logFile := logConf.File; logFile != nil {
 | 
				
			||||||
 | 
							//写入文件
 | 
				
			||||||
 | 
							file, err := os.OpenFile(logFile.GetFilename(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModeAppend|0666)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf("创建日志文件失败: %s", err.Error()))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Log.Out = file
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						global.Log = Log
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type LogFormatter struct{}
 | 
					type LogFormatter struct{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package model
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
	"mayfly-go/base/global"
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,22 +72,13 @@ func GetById(model interface{}, id uint64, cols ...string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 根据id列表查询
 | 
					// 根据id列表查询
 | 
				
			||||||
func GetByIdIn(model interface{}, list interface{}, ids []uint64, orderBy ...string) {
 | 
					func GetByIdIn(model interface{}, list interface{}, ids []uint64, orderBy ...string) {
 | 
				
			||||||
	var idsStr string
 | 
					 | 
				
			||||||
	for i, v := range ids {
 | 
					 | 
				
			||||||
		idStr := strconv.Itoa(int(v))
 | 
					 | 
				
			||||||
		if i == 0 {
 | 
					 | 
				
			||||||
			idsStr += idStr
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			idsStr += ("," + idStr)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var orderByStr string
 | 
						var orderByStr string
 | 
				
			||||||
	if orderBy == nil {
 | 
						if orderBy == nil {
 | 
				
			||||||
		orderByStr = "id desc"
 | 
							orderByStr = "id desc"
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		orderByStr = strings.Join(orderBy, ",")
 | 
							orderByStr = strings.Join(orderBy, ",")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	global.Db.Model(model).Where("id in (?)", idsStr).Order(orderByStr).Find(list)
 | 
						global.Db.Model(model).Where("id in (?)", ids).Order(orderByStr).Find(list)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据id列表查询
 | 
					// 根据id列表查询
 | 
				
			||||||
@@ -151,11 +143,11 @@ func GetByConditionTo(conditionModel interface{}, toModel interface{}) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取分页结果
 | 
					// 获取分页结果
 | 
				
			||||||
func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interface{}, orderBy ...string) PageResult {
 | 
					func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interface{}, orderBy ...string) *PageResult {
 | 
				
			||||||
	var count int64
 | 
						var count int64
 | 
				
			||||||
	global.Db.Model(conditionModel).Where(conditionModel).Count(&count)
 | 
						global.Db.Model(conditionModel).Where(conditionModel).Count(&count)
 | 
				
			||||||
	if count == 0 {
 | 
						if count == 0 {
 | 
				
			||||||
		return PageResult{Total: 0, List: []string{}}
 | 
							return &PageResult{Total: 0, List: []string{}}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	page := pageParam.PageNum
 | 
						page := pageParam.PageNum
 | 
				
			||||||
	pageSize := pageParam.PageSize
 | 
						pageSize := pageParam.PageSize
 | 
				
			||||||
@@ -165,12 +157,13 @@ func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interfac
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		orderByStr = strings.Join(orderBy, ",")
 | 
							orderByStr = strings.Join(orderBy, ",")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	global.Db.Model(conditionModel).Where(conditionModel).Order(orderByStr).Limit(pageSize).Offset((page - 1) * pageSize).Find(toModels)
 | 
						err := global.Db.Model(conditionModel).Where(conditionModel).Order(orderByStr).Limit(pageSize).Offset((page - 1) * pageSize).Find(toModels).Error
 | 
				
			||||||
	return PageResult{Total: count, List: toModels}
 | 
						biz.ErrIsNil(err, "查询失败")
 | 
				
			||||||
 | 
						return &PageResult{Total: count, List: toModels}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据sql获取分页对象
 | 
					// 根据sql获取分页对象
 | 
				
			||||||
func GetPageBySql(sql string, param *PageParam, toModel interface{}, args ...interface{}) PageResult {
 | 
					func GetPageBySql(sql string, param *PageParam, toModel interface{}, args ...interface{}) *PageResult {
 | 
				
			||||||
	db := global.Db
 | 
						db := global.Db
 | 
				
			||||||
	selectIndex := strings.Index(sql, "SELECT ") + 7
 | 
						selectIndex := strings.Index(sql, "SELECT ") + 7
 | 
				
			||||||
	fromIndex := strings.Index(sql, " FROM")
 | 
						fromIndex := strings.Index(sql, " FROM")
 | 
				
			||||||
@@ -180,12 +173,13 @@ func GetPageBySql(sql string, param *PageParam, toModel interface{}, args ...int
 | 
				
			|||||||
	var count int
 | 
						var count int
 | 
				
			||||||
	db.Raw(countSql, args...).Scan(&count)
 | 
						db.Raw(countSql, args...).Scan(&count)
 | 
				
			||||||
	if count == 0 {
 | 
						if count == 0 {
 | 
				
			||||||
		return PageResult{Total: 0, List: []string{}}
 | 
							return &PageResult{Total: 0, List: []string{}}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 分页查询
 | 
						// 分页查询
 | 
				
			||||||
	limitSql := sql + " LIMIT " + strconv.Itoa(param.PageNum-1) + ", " + strconv.Itoa(param.PageSize)
 | 
						limitSql := sql + " LIMIT " + strconv.Itoa(param.PageNum-1) + ", " + strconv.Itoa(param.PageSize)
 | 
				
			||||||
	db.Raw(limitSql).Scan(toModel)
 | 
						err := db.Raw(limitSql).Scan(toModel).Error
 | 
				
			||||||
	return PageResult{Total: int64(count), List: toModel}
 | 
						biz.ErrIsNil(err, "查询失败")
 | 
				
			||||||
 | 
						return &PageResult{Total: int64(count), List: toModel}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetListBySql(sql string, params ...interface{}) []map[string]interface{} {
 | 
					func GetListBySql(sql string, params ...interface{}) []map[string]interface{} {
 | 
				
			||||||
@@ -194,6 +188,6 @@ func GetListBySql(sql string, params ...interface{}) []map[string]interface{} {
 | 
				
			|||||||
	return maps
 | 
						return maps
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetListBySql2Model(sql string, toEntity interface{}, params ...interface{}) {
 | 
					func GetListBySql2Model(sql string, toEntity interface{}, params ...interface{}) error {
 | 
				
			||||||
	global.Db.Raw(sql, params).Find(toEntity)
 | 
						return global.Db.Raw(sql, params).Find(toEntity).Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,20 +3,12 @@ package model
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	SuccessCode = 200
 | 
						SuccessCode = 200
 | 
				
			||||||
	SuccessMsg  = "success"
 | 
						SuccessMsg  = "success"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	BizErrorCode = 400
 | 
					 | 
				
			||||||
	BizErrorMsg  = "error"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ServerErrorCode = 500
 | 
					 | 
				
			||||||
	ServerErrorMsg  = "server error"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TokenErrorCode = 501
 | 
					 | 
				
			||||||
	TokenErrorMsg  = "token error"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 统一返回结果结构体
 | 
					// 统一返回结果结构体
 | 
				
			||||||
@@ -52,15 +44,19 @@ func SuccessNoData() *Result {
 | 
				
			|||||||
	return &Result{Code: SuccessCode, Msg: SuccessMsg}
 | 
						return &Result{Code: SuccessCode, Msg: SuccessMsg}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 返回服务器错误Result
 | 
					func Error(bizerr *biz.BizError) *Result {
 | 
				
			||||||
func ServerError() *Result {
 | 
						return &Result{Code: bizerr.Code(), Msg: bizerr.Error()}
 | 
				
			||||||
	return &Result{Code: ServerErrorCode, Msg: ServerErrorMsg}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Error(code int16, msg string) *Result {
 | 
					// 返回服务器错误Result
 | 
				
			||||||
	return &Result{Code: code, Msg: msg}
 | 
					func ServerError() *Result {
 | 
				
			||||||
 | 
						return Error(biz.ServerError)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TokenError() *Result {
 | 
					func TokenError() *Result {
 | 
				
			||||||
	return &Result{Code: TokenErrorCode, Msg: TokenErrorMsg}
 | 
						return Error(biz.PermissionErr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ErrorBy(code int16, msg string) *Result {
 | 
				
			||||||
 | 
						return &Result{Code: code, Msg: msg}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package starter
 | 
					package starter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
	"mayfly-go/base/global"
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gorm.io/driver/mysql"
 | 
						"gorm.io/driver/mysql"
 | 
				
			||||||
@@ -10,7 +11,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GormMysql() *gorm.DB {
 | 
					func GormMysql() *gorm.DB {
 | 
				
			||||||
	m := global.Config.Mysql
 | 
						m := config.Conf.Mysql
 | 
				
			||||||
	if m == nil || m.Dbname == "" {
 | 
						if m == nil || m.Dbname == "" {
 | 
				
			||||||
		global.Log.Panic("未找到数据库配置信息")
 | 
							global.Log.Panic("未找到数据库配置信息")
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package starter
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
	"mayfly-go/base/global"
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-redis/redis"
 | 
						"github.com/go-redis/redis"
 | 
				
			||||||
@@ -9,7 +10,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func ConnRedis() *redis.Client {
 | 
					func ConnRedis() *redis.Client {
 | 
				
			||||||
	// 设置redis客户端
 | 
						// 设置redis客户端
 | 
				
			||||||
	redisConf := global.Config.Redis
 | 
						redisConf := config.Conf.Redis
 | 
				
			||||||
	if redisConf == nil {
 | 
						if redisConf == nil {
 | 
				
			||||||
		global.Log.Panic("未找到redis配置信息")
 | 
							global.Log.Panic("未找到redis配置信息")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,24 @@
 | 
				
			|||||||
package starter
 | 
					package starter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
	"mayfly-go/base/global"
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RunWebServer(web *gin.Engine) {
 | 
					func RunWebServer(web *gin.Engine) {
 | 
				
			||||||
	port := global.Config.Server.GetPort()
 | 
						server := config.Conf.Server
 | 
				
			||||||
	if app := global.Config.App; app != nil {
 | 
						port := server.GetPort()
 | 
				
			||||||
 | 
						if app := config.Conf.App; app != nil {
 | 
				
			||||||
		global.Log.Infof("%s- Listening and serving HTTP on %s", app.GetAppInfo(), port)
 | 
							global.Log.Infof("%s- Listening and serving HTTP on %s", app.GetAppInfo(), port)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		global.Log.Infof("Listening and serving HTTP on %s", port)
 | 
							global.Log.Infof("Listening and serving HTTP on %s", port)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if server.Tls != nil && server.Tls.Enable {
 | 
				
			||||||
 | 
							web.RunTLS(port, server.Tls.CertFile, server.Tls.KeyFile)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		web.Run(port)
 | 
							web.Run(port)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								base/utils/assert/assert.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								base/utils/assert/assert.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					package assert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 断言条件为真,不满足的panic
 | 
				
			||||||
 | 
					func IsTrue(condition bool, panicMsg string, params ...interface{}) {
 | 
				
			||||||
 | 
						if !condition {
 | 
				
			||||||
 | 
							if len(params) != 0 {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf(panicMsg, params...))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							panic(panicMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func State(condition bool, panicMsg string, params ...interface{}) {
 | 
				
			||||||
 | 
						IsTrue(condition, panicMsg, params...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NotEmpty(str string, panicMsg string, params ...interface{}) {
 | 
				
			||||||
 | 
						IsTrue(str != "", panicMsg, params...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								base/utils/jsonschemal_util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								base/utils/jsonschemal_util.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/xeipuuv/gojsonschema"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ValidJsonString(schemal, json string) error {
 | 
				
			||||||
 | 
						scheme, jsonLoader := gojsonschema.NewStringLoader(schemal), gojsonschema.NewStringLoader(json)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := gojsonschema.Validate(scheme, jsonLoader)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if result.Valid() {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						errs := make([]string, 0)
 | 
				
			||||||
 | 
						for _, desc := range result.Errors() {
 | 
				
			||||||
 | 
							errs = append(errs, desc.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return errors.New(strings.Join(errs, "|"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										58
									
								
								base/utils/jsonschemal_util_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								base/utils/jsonschemal_util_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/xeipuuv/gojsonschema"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestJsonSchemal(t *testing.T) {
 | 
				
			||||||
 | 
						schema := `{
 | 
				
			||||||
 | 
							"$schema": "http://json-schema.org/draft-04/schema#",
 | 
				
			||||||
 | 
							"title": "Product",
 | 
				
			||||||
 | 
							"description": "A product from Acme's catalog",
 | 
				
			||||||
 | 
							"type": "object",
 | 
				
			||||||
 | 
							"properties": {
 | 
				
			||||||
 | 
								"id": {
 | 
				
			||||||
 | 
									"description": "The unique identifier for a product",
 | 
				
			||||||
 | 
									"type": "integer"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"name": {
 | 
				
			||||||
 | 
									"description": "Name of the product",
 | 
				
			||||||
 | 
									"type": "string"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"price": {
 | 
				
			||||||
 | 
									"type": "number",
 | 
				
			||||||
 | 
									"minimum": 0,
 | 
				
			||||||
 | 
									"exclusiveMinimum": true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"required": ["id", "name", "price"]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						json := `{"id": 1, "name": "test", "price": -21}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := ValidJsonString(schema, json)
 | 
				
			||||||
 | 
						fmt.Print(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestJs(t *testing.T) {
 | 
				
			||||||
 | 
						schemaLoader := gojsonschema.NewStringLoader(`{"type": "object","properties":{"a":{"type":"object"}},"required":["a"]}`) // json格式
 | 
				
			||||||
 | 
						documentLoader := gojsonschema.NewStringLoader(`{"a":"b"}`)                                                              // 待校验的json数据
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := gojsonschema.Validate(schemaLoader, documentLoader)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if result.Valid() {
 | 
				
			||||||
 | 
							fmt.Printf("The document is valid\n")
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fmt.Printf("The document is not valid. see errors :\n")
 | 
				
			||||||
 | 
							for _, desc := range result.Errors() {
 | 
				
			||||||
 | 
								fmt.Printf("- %s\n", desc)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -20,3 +20,8 @@ func LoadYml(path string, out interface{}) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func LoadYmlByString(yamlStr string, out interface{}) error {
 | 
				
			||||||
 | 
						// yaml解析
 | 
				
			||||||
 | 
						return yaml.Unmarshal([]byte(yamlStr), out)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,20 +5,23 @@ go 1.16
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	// jwt
 | 
						// jwt
 | 
				
			||||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
						github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
				
			||||||
	github.com/gin-gonic/gin v1.6.2
 | 
						github.com/gin-gonic/gin v1.7.2
 | 
				
			||||||
	github.com/go-redis/redis v6.14.2+incompatible
 | 
						github.com/go-redis/redis v6.15.9+incompatible
 | 
				
			||||||
	github.com/go-sql-driver/mysql v1.5.0
 | 
					 | 
				
			||||||
	github.com/gorilla/websocket v1.4.2
 | 
						github.com/gorilla/websocket v1.4.2
 | 
				
			||||||
	github.com/onsi/ginkgo v1.16.1 // indirect
 | 
						// 验证码
 | 
				
			||||||
	github.com/onsi/gomega v1.11.0 // indirect
 | 
						github.com/mojocn/base64Captcha v1.3.4
 | 
				
			||||||
	github.com/pkg/sftp v1.12.0
 | 
						github.com/onsi/ginkgo v1.16.4 // indirect
 | 
				
			||||||
 | 
						github.com/onsi/gomega v1.13.0 // indirect
 | 
				
			||||||
 | 
						github.com/pkg/sftp v1.13.1
 | 
				
			||||||
	// 定时任务
 | 
						// 定时任务
 | 
				
			||||||
	github.com/robfig/cron/v3 v3.0.1
 | 
						github.com/robfig/cron/v3 v3.0.1
 | 
				
			||||||
	github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0
 | 
						github.com/sirupsen/logrus v1.8.1
 | 
				
			||||||
	github.com/sirupsen/logrus v1.6.0
 | 
					    // jsonschemal校验
 | 
				
			||||||
 | 
						github.com/xeipuuv/gojsonschema v1.2.0
 | 
				
			||||||
	// ssh
 | 
						// ssh
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
 | 
						golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
 | 
						gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
 | 
				
			||||||
 | 
						// gorm
 | 
				
			||||||
	gorm.io/driver/mysql v1.0.5
 | 
						gorm.io/driver/mysql v1.0.5
 | 
				
			||||||
	gorm.io/gorm v1.21.6
 | 
						gorm.io/gorm v1.21.11
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										88
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								go.sum
									
									
									
									
									
								
							@@ -8,21 +8,23 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
 | 
				
			|||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 | 
					github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
					github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
					github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
				
			||||||
github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM=
 | 
					github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
 | 
				
			||||||
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 | 
					github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
 | 
				
			||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 | 
					github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 | 
				
			||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 | 
					github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 | 
				
			||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
 | 
					github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
 | 
				
			||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 | 
					github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 | 
				
			||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
 | 
					github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
 | 
				
			||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
 | 
					github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
 | 
				
			||||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
 | 
					github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
 | 
				
			||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 | 
					github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
 | 
				
			||||||
github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
 | 
					github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
 | 
				
			||||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
					github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 | 
					github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
					github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
				
			||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 | 
					github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 | 
				
			||||||
 | 
					github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
 | 
				
			||||||
 | 
					github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 | 
				
			||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
				
			||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
					github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
				
			||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 | 
					github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 | 
				
			||||||
@@ -31,12 +33,14 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
 | 
				
			|||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 | 
					github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 | 
				
			||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 | 
					github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 | 
				
			||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
					github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
				
			||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 | 
					github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
				
			||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
					github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 | 
				
			||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
					github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
					github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
					github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
				
			||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 | 
					github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 | 
				
			||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
					github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
				
			||||||
@@ -48,8 +52,6 @@ github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
 | 
				
			|||||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
					github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
				
			||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
 | 
					github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
 | 
				
			||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
					github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
				
			||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
 | 
					 | 
				
			||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
					 | 
				
			||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
 | 
					github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
 | 
				
			||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 | 
					github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 | 
				
			||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 | 
					github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 | 
				
			||||||
@@ -60,73 +62,91 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
 | 
				
			|||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
					github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
				
			||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
 | 
					github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
 | 
				
			||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
					github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
				
			||||||
 | 
					github.com/mojocn/base64Captcha v1.3.4 h1:9+MZzjNSfBHniYOIpoP4xyDDPCXy14JIjsEFf89PlNw=
 | 
				
			||||||
 | 
					github.com/mojocn/base64Captcha v1.3.4/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY=
 | 
				
			||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 | 
					github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 | 
				
			||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 | 
					github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 | 
				
			||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 | 
					github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 | 
				
			||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
					github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
				
			||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 | 
					github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 | 
				
			||||||
github.com/onsi/ginkgo v1.16.1 h1:foqVmeWDD6yYpK+Yz3fHyNIxFYNxswxqNFjSKe+vI54=
 | 
					github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
 | 
				
			||||||
github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
 | 
					github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
 | 
				
			||||||
 | 
					github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 | 
				
			||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
					github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
				
			||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
					github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
				
			||||||
github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
 | 
					github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
 | 
				
			||||||
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
 | 
					github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
 | 
				
			||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
					github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pkg/sftp v1.12.0 h1:/f3b24xrDhkhddlaobPe2JgBqfdt+gC/NYl0QY9IOuI=
 | 
					github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs=
 | 
				
			||||||
github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
 | 
					github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 | 
					github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 | 
				
			||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 | 
					github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 | 
				
			||||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0 h1:QIF48X1cihydXibm+4wfAc0r/qyPyuFiPFRNphdMpEE=
 | 
					github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 | 
				
			||||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
 | 
					github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
				
			||||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
 | 
					 | 
				
			||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 | 
					 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
					github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 | 
					github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 | 
					github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 | 
				
			||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 | 
					github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 | 
				
			||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
 | 
					github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
 | 
				
			||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 | 
					github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 | 
				
			||||||
 | 
					github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
 | 
				
			||||||
 | 
					github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 | 
				
			||||||
 | 
					github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
 | 
				
			||||||
 | 
					github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 | 
				
			||||||
 | 
					github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
 | 
				
			||||||
 | 
					github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 | 
				
			||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
 | 
					golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
 | 
					golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ=
 | 
				
			||||||
 | 
					golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
				
			||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
					golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
 | 
					golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 | 
				
			||||||
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
					golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
				
			||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 | 
					 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
 | 
					golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 | 
				
			||||||
 | 
					golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
				
			||||||
@@ -140,8 +160,10 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
 | 
				
			|||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
					google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
				
			||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 | 
					google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 | 
				
			||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 | 
					google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 | 
				
			||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
					google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
					gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
				
			||||||
@@ -158,5 +180,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 | 
				
			|||||||
gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
 | 
					gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
 | 
				
			||||||
gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
 | 
					gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
 | 
				
			||||||
gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
 | 
					gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
 | 
				
			||||||
gorm.io/gorm v1.21.6 h1:xEFbH7WShsnAM+HeRNv7lOeyqmDAK+dDnf1AMf/cVPQ=
 | 
					gorm.io/gorm v1.21.11 h1:CxkXW6Cc+VIBlL8yJEHq+Co4RYXdSLiMKNvgoZPjLK4=
 | 
				
			||||||
gorm.io/gorm v1.21.6/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
 | 
					gorm.io/gorm v1.21.11/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,4 +2,4 @@
 | 
				
			|||||||
ENV = 'production'
 | 
					ENV = 'production'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 线上环境接口地址
 | 
					# 线上环境接口地址
 | 
				
			||||||
VITE_API_URL = 'http://localhost:8888/api'
 | 
					VITE_API_URL = 'http://api.mayflygo.1yue.net/api'
 | 
				
			||||||
@@ -18,6 +18,7 @@
 | 
				
			|||||||
	</head>
 | 
						</head>
 | 
				
			||||||
	<body>
 | 
						<body>
 | 
				
			||||||
		<div id="app"></div>
 | 
							<div id="app"></div>
 | 
				
			||||||
 | 
					        <script type="text/javascript" src="./config.js"></script>
 | 
				
			||||||
		<script type="module" src="/src/main.ts"></script>
 | 
							<script type="module" src="/src/main.ts"></script>
 | 
				
			||||||
		<!-- <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script> -->
 | 
							<!-- <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script> -->
 | 
				
			||||||
	</body>
 | 
						</body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,9 @@
 | 
				
			|||||||
    "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
 | 
					    "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "core-js": "^3.6.5",
 | 
					 | 
				
			||||||
    "axios": "^0.21.1",
 | 
					    "axios": "^0.21.1",
 | 
				
			||||||
    "codemirror": "^5.61.0",
 | 
					    "codemirror": "^5.61.0",
 | 
				
			||||||
 | 
					    "core-js": "^3.6.5",
 | 
				
			||||||
    "countup.js": "^2.0.7",
 | 
					    "countup.js": "^2.0.7",
 | 
				
			||||||
    "cropperjs": "^1.5.11",
 | 
					    "cropperjs": "^1.5.11",
 | 
				
			||||||
    "echarts": "^5.1.1",
 | 
					    "echarts": "^5.1.1",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								mayfly_go_web/public/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								mayfly_go_web/public/config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					window.globalConfig = {
 | 
				
			||||||
 | 
					    "BaseApiUrl": "http://localhost:8888/api"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -48,7 +48,7 @@ class Api {
 | 
				
			|||||||
     * 操作该权限,即请求对应的url
 | 
					     * 操作该权限,即请求对应的url
 | 
				
			||||||
     * @param {Object} param 请求该权限的参数
 | 
					     * @param {Object} param 请求该权限的参数
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    request(param: any): Promise<any> {
 | 
					    request(param: any = null): Promise<any> {
 | 
				
			||||||
        return request.send(this, param);
 | 
					        return request.send(this, param);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
class AssertError extends Error {
 | 
					class AssertError extends Error {
 | 
				
			||||||
    constructor(message: string) {
 | 
					    constructor(message: string) {
 | 
				
			||||||
        super(message); // (1)
 | 
					        super(message);
 | 
				
			||||||
        // 错误类名
 | 
					        // 错误类名
 | 
				
			||||||
        this.name = "AssertError";
 | 
					        this.name = "AssertError";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
const config = {
 | 
					const config = {
 | 
				
			||||||
    baseApiUrl: import.meta.env.VITE_API_URL
 | 
					    baseApiUrl:  (window as any).globalConfig.BaseApiUrl
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default config
 | 
					export default config
 | 
				
			||||||
@@ -2,7 +2,7 @@ import request from './request'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    login: (param: any) => request.request('POST', '/sys/accounts/login', param, null),
 | 
					    login: (param: any) => request.request('POST', '/sys/accounts/login', param, null),
 | 
				
			||||||
    captcha: () => request.request('GET', '/open/captcha', null, null),
 | 
					    captcha: () => request.request('GET', '/sys/captcha', null, null),
 | 
				
			||||||
    logout: (param: any) => request.request('POST', '/sys/accounts/logout/{token}', param, null),
 | 
					    logout: (param: any) => request.request('POST', '/sys/accounts/logout/{token}', param, null),
 | 
				
			||||||
    getMenuRoute: (param: any) => request.request('Get', '/sys/resources/account', param, null)
 | 
					    getMenuRoute: (param: any) => request.request('Get', '/sys/resources/account', param, null)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -53,6 +53,14 @@ export default defineComponent({
 | 
				
			|||||||
            type: String,
 | 
					            type: String,
 | 
				
			||||||
            default: null,
 | 
					            default: null,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        height: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					            default: "500px",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        width: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					            default: "auto",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        canChangeMode: {
 | 
					        canChangeMode: {
 | 
				
			||||||
            type: Boolean,
 | 
					            type: Boolean,
 | 
				
			||||||
            default: false,
 | 
					            default: false,
 | 
				
			||||||
@@ -165,15 +173,14 @@ export default defineComponent({
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        watch(
 | 
					        // watch(
 | 
				
			||||||
            () => props.options,
 | 
					        //     () => props.options,
 | 
				
			||||||
            (newValue, oldValue) => {
 | 
					        //     (newValue, oldValue) => {
 | 
				
			||||||
                console.log('options change', newValue);
 | 
					        //         for (const key in newValue) {
 | 
				
			||||||
                for (const key in newValue) {
 | 
					        //             coder.setOption(key, newValue[key]);
 | 
				
			||||||
                    coder.setOption(key, newValue[key]);
 | 
					        //         }
 | 
				
			||||||
                }
 | 
					        //     }
 | 
				
			||||||
            }
 | 
					        // );
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const init = () => {
 | 
					        const init = () => {
 | 
				
			||||||
            if (props.options) {
 | 
					            if (props.options) {
 | 
				
			||||||
@@ -195,6 +202,9 @@ export default defineComponent({
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            coder.setSize(props.width, props.height);
 | 
				
			||||||
 | 
					            // editor.setSize('width','height');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 修改编辑器的语法配置
 | 
					            // 修改编辑器的语法配置
 | 
				
			||||||
            setMode(language.value);
 | 
					            setMode(language.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -285,6 +295,7 @@ export default defineComponent({
 | 
				
			|||||||
                coder.setValue(newVal);
 | 
					                coder.setValue(newVal);
 | 
				
			||||||
                state.content = newVal;
 | 
					                state.content = newVal;
 | 
				
			||||||
                coder.scrollTo(scrollInfo.left, scrollInfo.top);
 | 
					                coder.scrollTo(scrollInfo.left, scrollInfo.top);
 | 
				
			||||||
 | 
					                refresh()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -292,6 +303,7 @@ export default defineComponent({
 | 
				
			|||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
            textarea,
 | 
					            textarea,
 | 
				
			||||||
            changeMode,
 | 
					            changeMode,
 | 
				
			||||||
 | 
					            refresh,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,7 @@ export default defineComponent({
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const submit = () => {
 | 
							const submit = () => {
 | 
				
			||||||
			dynamicForm.validate((valid: boolean) => {
 | 
								dynamicForm.value.validate((valid: boolean) => {
 | 
				
			||||||
				if (valid) {
 | 
									if (valid) {
 | 
				
			||||||
					// 提交的表单数据
 | 
										// 提交的表单数据
 | 
				
			||||||
					const subform = { ...state.form };
 | 
										const subform = { ...state.form };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,5 +8,9 @@ export const imports = {
 | 
				
			|||||||
    "ResourceList": () => import('@/views/system/resource'),
 | 
					    "ResourceList": () => import('@/views/system/resource'),
 | 
				
			||||||
    "RoleList": () => import('@/views/system/role'),
 | 
					    "RoleList": () => import('@/views/system/role'),
 | 
				
			||||||
    "AccountList": () => import('@/views/system/account'),
 | 
					    "AccountList": () => import('@/views/system/account'),
 | 
				
			||||||
    "SelectData": () => import('@/views/ops/db'),
 | 
					    "ProjectList": () => import('@/views/ops/project/ProjectList.vue'),
 | 
				
			||||||
 | 
					    "DbList": () => import('@/views/ops/db/DbList.vue'),
 | 
				
			||||||
 | 
					    "SqlExec": () => import('@/views/ops/db'),
 | 
				
			||||||
 | 
					    "RedisList": () => import('@/views/ops/redis'),
 | 
				
			||||||
 | 
					    "DataOperation": () => import('@/views/ops/redis/DataOperation.vue'),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -14,8 +14,8 @@ body,
 | 
				
			|||||||
	padding: 0;
 | 
						padding: 0;
 | 
				
			||||||
	width: 100%;
 | 
						width: 100%;
 | 
				
			||||||
	height: 100%;
 | 
						height: 100%;
 | 
				
			||||||
	font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
 | 
						font-family: Microsoft YaHei, Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, SimSun, sans-serif;
 | 
				
			||||||
	font-weight: 500;
 | 
						font-weight: 450;
 | 
				
			||||||
	-webkit-font-smoothing: antialiased;
 | 
						-webkit-font-smoothing: antialiased;
 | 
				
			||||||
	-webkit-tap-highlight-color: transparent;
 | 
						-webkit-tap-highlight-color: transparent;
 | 
				
			||||||
	background-color: #f8f8f8;
 | 
						background-color: #f8f8f8;
 | 
				
			||||||
@@ -274,7 +274,7 @@ body,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.toolbar {
 | 
					.toolbar {
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
    padding: 8px;
 | 
					    padding: 6px;
 | 
				
			||||||
    background-color: #ffffff;
 | 
					    background-color: #ffffff;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
    line-height: 32px;
 | 
					    line-height: 32px;
 | 
				
			||||||
@@ -284,3 +284,9 @@ body,
 | 
				
			|||||||
.fl {
 | 
					.fl {
 | 
				
			||||||
    float: left;
 | 
					    float: left;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.search-form {
 | 
				
			||||||
 | 
					    .el-form-item {
 | 
				
			||||||
 | 
					        margin-bottom: 3px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,10 +4,10 @@
 | 
				
			|||||||
            <div class="left">
 | 
					            <div class="left">
 | 
				
			||||||
                <div class="left-item">
 | 
					                <div class="left-item">
 | 
				
			||||||
                    <div class="left-item-animation left-item-num">401</div>
 | 
					                    <div class="left-item-animation left-item-num">401</div>
 | 
				
			||||||
                    <div class="left-item-animation left-item-title">您未被授权,没有操作权限</div>
 | 
					                    <div class="left-item-animation left-item-title">您未被授权或登录超时,没有操作权限</div>
 | 
				
			||||||
                    <div class="left-item-animation left-item-msg"></div>
 | 
					                    <div class="left-item-animation left-item-msg"></div>
 | 
				
			||||||
                    <div class="left-item-animation left-item-btn">
 | 
					                    <div class="left-item-animation left-item-btn">
 | 
				
			||||||
                        <el-button type="primary" round @click="onSetAuth">重新授权</el-button>
 | 
					                        <el-button type="primary" round @click="onSetAuth">重新登录</el-button>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,8 +7,8 @@
 | 
				
			|||||||
                        <img :src="getUserInfos.photo" />
 | 
					                        <img :src="getUserInfos.photo" />
 | 
				
			||||||
                        <div class="home-card-first-right ml15">
 | 
					                        <div class="home-card-first-right ml15">
 | 
				
			||||||
                            <div class="flex-margin">
 | 
					                            <div class="flex-margin">
 | 
				
			||||||
                                <div class="home-card-first-right-title">{{ currentTime }},admin!</div>
 | 
					                                <div class="home-card-first-right-title">{{ `${currentTime}, ${getUserInfos.username}` }}</div>
 | 
				
			||||||
                                <div class="home-card-first-right-msg mt5">超级管理</div>
 | 
					                                <!-- <div class="home-card-first-right-msg mt5">超级管理</div> -->
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <el-form class="login-content-form">
 | 
					    <el-form ref="loginFormRef" :model="loginForm" :rules="rules" class="login-content-form">
 | 
				
			||||||
        <el-form-item>
 | 
					        <el-form-item prop="username">
 | 
				
			||||||
            <el-input type="text" placeholder="请输入用户名" prefix-icon="el-icon-user" v-model="loginForm.username" clearable autocomplete="off">
 | 
					            <el-input type="text" placeholder="请输入用户名" prefix-icon="el-icon-user" v-model="loginForm.username" clearable autocomplete="off">
 | 
				
			||||||
            </el-input>
 | 
					            </el-input>
 | 
				
			||||||
        </el-form-item>
 | 
					        </el-form-item>
 | 
				
			||||||
        <el-form-item>
 | 
					        <el-form-item prop="password">
 | 
				
			||||||
            <el-input
 | 
					            <el-input
 | 
				
			||||||
                type="password"
 | 
					                type="password"
 | 
				
			||||||
                placeholder="请输入密码"
 | 
					                placeholder="请输入密码"
 | 
				
			||||||
@@ -15,29 +15,36 @@
 | 
				
			|||||||
            >
 | 
					            >
 | 
				
			||||||
            </el-input>
 | 
					            </el-input>
 | 
				
			||||||
        </el-form-item>
 | 
					        </el-form-item>
 | 
				
			||||||
        <el-form-item>
 | 
					        <el-form-item prop="captcha">
 | 
				
			||||||
            <el-row :gutter="15">
 | 
					            <el-row :gutter="15">
 | 
				
			||||||
                <el-col :span="16">
 | 
					                <el-col :span="16">
 | 
				
			||||||
                    <el-input
 | 
					                    <el-input
 | 
				
			||||||
                        type="text"
 | 
					                        type="text"
 | 
				
			||||||
                        maxlength="4"
 | 
					                        maxlength="6"
 | 
				
			||||||
                        placeholder="请输入验证码"
 | 
					                        placeholder="请输入验证码"
 | 
				
			||||||
                        prefix-icon="el-icon-position"
 | 
					                        prefix-icon="el-icon-position"
 | 
				
			||||||
                        v-model="loginForm.code"
 | 
					                        v-model="loginForm.captcha"
 | 
				
			||||||
                        clearable
 | 
					                        clearable
 | 
				
			||||||
                        autocomplete="off"
 | 
					                        autocomplete="off"
 | 
				
			||||||
                        @keyup.enter="onSignIn"
 | 
					                        @keyup.enter="login"
 | 
				
			||||||
                    ></el-input>
 | 
					                    ></el-input>
 | 
				
			||||||
                </el-col>
 | 
					                </el-col>
 | 
				
			||||||
                <el-col :span="8">
 | 
					                <el-col :span="8">
 | 
				
			||||||
                    <div class="login-content-code">
 | 
					                    <div class="login-content-code">
 | 
				
			||||||
                        <span class="login-content-code-img">1234</span>
 | 
					                        <img
 | 
				
			||||||
 | 
					                            class="login-content-code-img"
 | 
				
			||||||
 | 
					                            @click="getCaptcha"
 | 
				
			||||||
 | 
					                            width="130px"
 | 
				
			||||||
 | 
					                            height="40px"
 | 
				
			||||||
 | 
					                            :src="captchaImage"
 | 
				
			||||||
 | 
					                            style="cursor: pointer"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </el-col>
 | 
					                </el-col>
 | 
				
			||||||
            </el-row>
 | 
					            </el-row>
 | 
				
			||||||
        </el-form-item>
 | 
					        </el-form-item>
 | 
				
			||||||
        <el-form-item>
 | 
					        <el-form-item>
 | 
				
			||||||
            <el-button type="primary" class="login-content-submit" round @click="onSignIn" :loading="loading.signIn">
 | 
					            <el-button type="primary" class="login-content-submit" round @click="login" :loading="loading.signIn">
 | 
				
			||||||
                <span>登 录</span>
 | 
					                <span>登 录</span>
 | 
				
			||||||
            </el-button>
 | 
					            </el-button>
 | 
				
			||||||
        </el-form-item>
 | 
					        </el-form-item>
 | 
				
			||||||
@@ -45,7 +52,7 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { toRefs, reactive, defineComponent, computed } from 'vue';
 | 
					import { onMounted, ref, toRefs, reactive, defineComponent, computed } from 'vue';
 | 
				
			||||||
import { useRoute, useRouter } from 'vue-router';
 | 
					import { useRoute, useRouter } from 'vue-router';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
import { initAllFun, initBackEndControlRoutesFun } from '@/router/index.ts';
 | 
					import { initAllFun, initBackEndControlRoutesFun } from '@/router/index.ts';
 | 
				
			||||||
@@ -60,22 +67,51 @@ export default defineComponent({
 | 
				
			|||||||
        const store = useStore();
 | 
					        const store = useStore();
 | 
				
			||||||
        const route = useRoute();
 | 
					        const route = useRoute();
 | 
				
			||||||
        const router = useRouter();
 | 
					        const router = useRouter();
 | 
				
			||||||
 | 
					        const loginFormRef: any = ref(null);
 | 
				
			||||||
        const state = reactive({
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            captchaImage: '',
 | 
				
			||||||
            loginForm: {
 | 
					            loginForm: {
 | 
				
			||||||
                username: 'test',
 | 
					                username: 'test',
 | 
				
			||||||
                password: '123456',
 | 
					                password: '123456',
 | 
				
			||||||
                code: '1234',
 | 
					                captcha: '',
 | 
				
			||||||
 | 
					                cid: '',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            rules: {
 | 
				
			||||||
 | 
					                username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
 | 
				
			||||||
 | 
					                password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
 | 
				
			||||||
 | 
					                captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            loading: {
 | 
					            loading: {
 | 
				
			||||||
                signIn: false,
 | 
					                signIn: false,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onMounted(() => {
 | 
				
			||||||
 | 
					            getCaptcha();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const getCaptcha = async () => {
 | 
				
			||||||
 | 
					            let res: any = await openApi.captcha();
 | 
				
			||||||
 | 
					            state.captchaImage = res.base64Captcha;
 | 
				
			||||||
 | 
					            state.loginForm.cid = res.cid;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 时间获取
 | 
					        // 时间获取
 | 
				
			||||||
        const currentTime = computed(() => {
 | 
					        const currentTime = computed(() => {
 | 
				
			||||||
            return formatAxis(new Date());
 | 
					            return formatAxis(new Date());
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 校验登录表单并登录
 | 
				
			||||||
 | 
					        const login = () => {
 | 
				
			||||||
 | 
					            loginFormRef.value.validate((valid: boolean) => {
 | 
				
			||||||
 | 
					                if (valid) {
 | 
				
			||||||
 | 
					                    onSignIn();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 登录
 | 
					        // 登录
 | 
				
			||||||
        const onSignIn = async () => {
 | 
					        const onSignIn = async () => {
 | 
				
			||||||
            state.loading.signIn = true;
 | 
					            state.loading.signIn = true;
 | 
				
			||||||
@@ -87,6 +123,8 @@ export default defineComponent({
 | 
				
			|||||||
                setSession('menus', loginRes.menus);
 | 
					                setSession('menus', loginRes.menus);
 | 
				
			||||||
            } catch (e) {
 | 
					            } catch (e) {
 | 
				
			||||||
                state.loading.signIn = false;
 | 
					                state.loading.signIn = false;
 | 
				
			||||||
 | 
					                state.loginForm.captcha = '';
 | 
				
			||||||
 | 
					                getCaptcha();
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // 用户信息模拟数据
 | 
					            // 用户信息模拟数据
 | 
				
			||||||
@@ -132,9 +170,12 @@ export default defineComponent({
 | 
				
			|||||||
                ElMessage.success(`${currentTimeInfo},欢迎回来!`);
 | 
					                ElMessage.success(`${currentTimeInfo},欢迎回来!`);
 | 
				
			||||||
            }, 300);
 | 
					            }, 300);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
 | 
					            getCaptcha,
 | 
				
			||||||
            currentTime,
 | 
					            currentTime,
 | 
				
			||||||
            onSignIn,
 | 
					            loginFormRef,
 | 
				
			||||||
 | 
					            login,
 | 
				
			||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										84
									
								
								mayfly_go_web/src/views/ops/component/ProjectEnvSelect.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								mayfly_go_web/src/views/ops/component/ProjectEnvSelect.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <el-form class="search-form" label-position="right" :inline="true" label-width="60px" size="small">
 | 
				
			||||||
 | 
					            <el-form-item prop="project" label="项目" label-width="40px">
 | 
				
			||||||
 | 
					                <el-select v-model="projectId" placeholder="请选择项目" @change="changeProject" filterable>
 | 
				
			||||||
 | 
					                    <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"></el-option>
 | 
				
			||||||
 | 
					                </el-select>
 | 
				
			||||||
 | 
					            </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-form-item prop="env" label="环境" label-width="40px">
 | 
				
			||||||
 | 
					                <el-select style="width: 100px" v-model="envId" placeholder="环境" @change="changeEnv" filterable>
 | 
				
			||||||
 | 
					                    <el-option v-for="item in envs" :key="item.id" :label="item.name" :value="item.id">
 | 
				
			||||||
 | 
					                        <span style="float: left">{{ item.name }}</span>
 | 
				
			||||||
 | 
					                        <span style="float: right; color: #8492a6; font-size: 13px">{{ item.remark }}</span>
 | 
				
			||||||
 | 
					                    </el-option>
 | 
				
			||||||
 | 
					                </el-select>
 | 
				
			||||||
 | 
					            </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <slot></slot>
 | 
				
			||||||
 | 
					        </el-form>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { ref, toRefs, reactive, watch, defineComponent, onMounted } from 'vue';
 | 
				
			||||||
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					import { notEmpty } from '@/common/assert';
 | 
				
			||||||
 | 
					import { projectApi } from '../project/api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'ProjectEnvSelect',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        visible: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        data: {
 | 
				
			||||||
 | 
					            type: Object,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        title: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        machineId: {
 | 
				
			||||||
 | 
					            type: Number,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        isCommon: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup(props: any, { emit }) {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            projects: [] as any,
 | 
				
			||||||
 | 
					            envs: [] as any,
 | 
				
			||||||
 | 
					            projectId: null,
 | 
				
			||||||
 | 
					            envId: null,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(props, (newValue, oldValue) => {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onMounted(async () => {
 | 
				
			||||||
 | 
					            state.projects = await projectApi.accountProjects.request(null);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeProject = async (projectId: any) => {
 | 
				
			||||||
 | 
					            emit('update:projectId', projectId);
 | 
				
			||||||
 | 
					            emit('changeProjectEnv', state.projectId, null);
 | 
				
			||||||
 | 
					            state.envId = null;
 | 
				
			||||||
 | 
					            state.envs = await projectApi.projectEnvs.request({ projectId });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeEnv = (envId: any) => {
 | 
				
			||||||
 | 
					            emit('update:envId', envId);
 | 
				
			||||||
 | 
					            emit('changeProjectEnv', state.projectId, envId);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            changeProject,
 | 
				
			||||||
 | 
					            changeEnv,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										240
									
								
								mayfly_go_web/src/views/ops/db/DbEdit.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								mayfly_go_web/src/views/ops/db/DbEdit.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,240 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="35%">
 | 
				
			||||||
 | 
					            <el-form :model="form" ref="dbForm" :rules="rules" label-width="85px" size="small">
 | 
				
			||||||
 | 
					                <el-form-item prop="projectId" label="项目:" required>
 | 
				
			||||||
 | 
					                    <el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
 | 
				
			||||||
 | 
					                        <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item prop="envId" label="环境:" required>
 | 
				
			||||||
 | 
					                    <el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
 | 
				
			||||||
 | 
					                        <el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="name" label="别名:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="type" label="类型:" required>
 | 
				
			||||||
 | 
					                    <el-select style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
 | 
				
			||||||
 | 
					                        <el-option key="item.id" label="mysql" value="mysql"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="host" label="host:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.host" placeholder="请输入主机ip" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="port" label="port:" required>
 | 
				
			||||||
 | 
					                    <el-input type="number" v-model.trim="form.port" placeholder="请输入端口"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="username" label="用户名:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="password" label="密码:" required>
 | 
				
			||||||
 | 
					                    <el-input
 | 
				
			||||||
 | 
					                        type="password"
 | 
				
			||||||
 | 
					                        show-password
 | 
				
			||||||
 | 
					                        v-model.trim="form.password"
 | 
				
			||||||
 | 
					                        placeholder="请输入密码"
 | 
				
			||||||
 | 
					                        autocomplete="new-password"
 | 
				
			||||||
 | 
					                    ></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="database" label="数据库名:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.database" placeholder="请输入数据库名"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					            </el-form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <div class="dialog-footer">
 | 
				
			||||||
 | 
					                    <el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini">确 定</el-button>
 | 
				
			||||||
 | 
					                    <el-button @click="cancel()" size="mini">取 消</el-button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
 | 
				
			||||||
 | 
					import { dbApi } from './api';
 | 
				
			||||||
 | 
					import { projectApi } from '../project/api.ts';
 | 
				
			||||||
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'DbEdit',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        visible: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        projects: {
 | 
				
			||||||
 | 
					            type: Array,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        db: {
 | 
				
			||||||
 | 
					            type: [Boolean, Object],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        title: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup(props: any, { emit }) {
 | 
				
			||||||
 | 
					        const dbForm: any = ref(null);
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            visible: false,
 | 
				
			||||||
 | 
					            projects: [],
 | 
				
			||||||
 | 
					            envs: [],
 | 
				
			||||||
 | 
					            form: {
 | 
				
			||||||
 | 
					                id: null,
 | 
				
			||||||
 | 
					                name: null,
 | 
				
			||||||
 | 
					                port: 3306,
 | 
				
			||||||
 | 
					                username: null,
 | 
				
			||||||
 | 
					                password: null,
 | 
				
			||||||
 | 
					                project: null,
 | 
				
			||||||
 | 
					                projectId: null,
 | 
				
			||||||
 | 
					                envId: null,
 | 
				
			||||||
 | 
					                env: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            btnLoading: false,
 | 
				
			||||||
 | 
					            rules: {
 | 
				
			||||||
 | 
					                projectId: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择项目',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                envId: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择环境',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                name: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入别名',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                type: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择数据库类型',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                host: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入主机ip',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                port: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入端口',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                username: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入用户名',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                password: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入密码',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                database: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入数据库名',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(props, async (newValue, oldValue) => {
 | 
				
			||||||
 | 
					            state.visible = newValue.visible;
 | 
				
			||||||
 | 
					            state.projects = newValue.projects;
 | 
				
			||||||
 | 
					            if (newValue.db) {
 | 
				
			||||||
 | 
					                getEnvs(newValue.db.projectId);
 | 
				
			||||||
 | 
					                state.form = { ...newValue.db };
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                state.envs = [];
 | 
				
			||||||
 | 
					                state.form = { port: 3306 } as any;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const getEnvs = async (projectId: any) => {
 | 
				
			||||||
 | 
					            state.envs = await projectApi.projectEnvs.request({ projectId });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeProject = (projectId: number) => {
 | 
				
			||||||
 | 
					            for (let p of state.projects as any) {
 | 
				
			||||||
 | 
					                if (p.id == projectId) {
 | 
				
			||||||
 | 
					                    state.form.project = p.name;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.envs = [];
 | 
				
			||||||
 | 
					            getEnvs(projectId);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeEnv = (envId: number) => {
 | 
				
			||||||
 | 
					            for (let p of state.envs as any) {
 | 
				
			||||||
 | 
					                if (p.id == envId) {
 | 
				
			||||||
 | 
					                    state.form.env = p.name;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const btnOk = async () => {
 | 
				
			||||||
 | 
					            dbForm.value.validate((valid: boolean) => {
 | 
				
			||||||
 | 
					                if (valid) {
 | 
				
			||||||
 | 
					                    dbApi.saveDb.request(state.form).then((res: any) => {
 | 
				
			||||||
 | 
					                        ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					                        emit('val-change', state.form);
 | 
				
			||||||
 | 
					                        state.btnLoading = true;
 | 
				
			||||||
 | 
					                        setTimeout(() => {
 | 
				
			||||||
 | 
					                            state.btnLoading = false;
 | 
				
			||||||
 | 
					                        }, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        cancel();
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ElMessage.error('请正确填写信息');
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cancel = () => {
 | 
				
			||||||
 | 
					            emit('update:visible', false);
 | 
				
			||||||
 | 
					            emit('cancel');
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                dbForm.value.resetFields();
 | 
				
			||||||
 | 
					                //  重置对象属性为null
 | 
				
			||||||
 | 
					                state.form = {} as any;
 | 
				
			||||||
 | 
					            }, 200);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            dbForm,
 | 
				
			||||||
 | 
					            changeProject,
 | 
				
			||||||
 | 
					            changeEnv,
 | 
				
			||||||
 | 
					            btnOk,
 | 
				
			||||||
 | 
					            cancel,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										196
									
								
								mayfly_go_web/src/views/ops/db/DbList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								mayfly_go_web/src/views/ops/db/DbList.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div class="db-list">
 | 
				
			||||||
 | 
					        <div class="toolbar">
 | 
				
			||||||
 | 
					            <el-row>
 | 
				
			||||||
 | 
					                <el-col>
 | 
				
			||||||
 | 
					                    <el-form class="search-form" label-position="right" :inline="true" label-width="60px" size="small">
 | 
				
			||||||
 | 
					                        <el-form-item prop="project" label="项目">
 | 
				
			||||||
 | 
					                            <el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable>
 | 
				
			||||||
 | 
					                                <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id">
 | 
				
			||||||
 | 
					                                </el-option>
 | 
				
			||||||
 | 
					                            </el-select>
 | 
				
			||||||
 | 
					                        </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <el-form-item label="数据库">
 | 
				
			||||||
 | 
					                            <el-input v-model="query.database" auto-complete="off" clearable></el-input>
 | 
				
			||||||
 | 
					                        </el-form-item>
 | 
				
			||||||
 | 
					                        <el-button type="primary" icon="el-icon-search" size="mini" @click="search()">查询</el-button>
 | 
				
			||||||
 | 
					                    </el-form>
 | 
				
			||||||
 | 
					                </el-col>
 | 
				
			||||||
 | 
					            </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-row class="mt5">
 | 
				
			||||||
 | 
					                <el-col>
 | 
				
			||||||
 | 
					                    <el-button v-auth="permissions.saveDb" type="primary" icon="el-icon-plus" size="mini" @click="editDb(true)">添加</el-button>
 | 
				
			||||||
 | 
					                    <el-button
 | 
				
			||||||
 | 
					                        v-auth="permissions.saveDb"
 | 
				
			||||||
 | 
					                        :disabled="chooseId == null"
 | 
				
			||||||
 | 
					                        @click="editDb(false)"
 | 
				
			||||||
 | 
					                        type="primary"
 | 
				
			||||||
 | 
					                        icon="el-icon-edit"
 | 
				
			||||||
 | 
					                        size="mini"
 | 
				
			||||||
 | 
					                        >编辑</el-button
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                    <el-button
 | 
				
			||||||
 | 
					                       v-auth="permissions.delDb"
 | 
				
			||||||
 | 
					                        :disabled="chooseId == null"
 | 
				
			||||||
 | 
					                        @click="deleteDb(chooseId)"
 | 
				
			||||||
 | 
					                        type="danger"
 | 
				
			||||||
 | 
					                        icon="el-icon-delete"
 | 
				
			||||||
 | 
					                        size="mini"
 | 
				
			||||||
 | 
					                        >删除</el-button
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                </el-col>
 | 
				
			||||||
 | 
					            </el-row>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <el-table :data="datas" border ref="table" @current-change="choose" show-overflow-tooltip>
 | 
				
			||||||
 | 
					            <el-table-column label="选择" width="50px">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-radio v-model="chooseId" :label="scope.row.id">
 | 
				
			||||||
 | 
					                        <i></i>
 | 
				
			||||||
 | 
					                    </el-radio>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="project" label="项目" min-width="100"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="env" label="环境" min-width="100"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="name" label="名称" min-width="200"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column min-width="160" label="host:port">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    {{ `${scope.row.host}:${scope.row.port}` }}
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="type" label="类型" min-width="80"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="database" label="数据库" min-width="120"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="username" label="用户名" min-width="100"></el-table-column>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column min-width="160" prop="createTime" label="创建时间">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					        </el-table>
 | 
				
			||||||
 | 
					        <el-pagination
 | 
				
			||||||
 | 
					            @current-change="handlePageChange"
 | 
				
			||||||
 | 
					            style="text-align: center"
 | 
				
			||||||
 | 
					            background
 | 
				
			||||||
 | 
					            layout="prev, pager, next, total, jumper"
 | 
				
			||||||
 | 
					            :total="total"
 | 
				
			||||||
 | 
					            v-model:current-page="query.pageNum"
 | 
				
			||||||
 | 
					            :page-size="query.pageSize"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <db-edit @val-change="valChange" :projects="projects" :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" v-model:db="dbEditDialog.data"></db-edit>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang='ts'>
 | 
				
			||||||
 | 
					import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
				
			||||||
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
 | 
					import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
 | 
				
			||||||
 | 
					import DbEdit from './DbEdit.vue';
 | 
				
			||||||
 | 
					import { dbApi } from './api';
 | 
				
			||||||
 | 
					import { projectApi } from '../project/api.ts';
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'DbList',
 | 
				
			||||||
 | 
					    components: {
 | 
				
			||||||
 | 
					        ProjectEnvSelect,
 | 
				
			||||||
 | 
					        DbEdit,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup() {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					             permissions: {
 | 
				
			||||||
 | 
					                saveDb: 'db:save',
 | 
				
			||||||
 | 
					                delDb: 'db:del',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            projects: [],
 | 
				
			||||||
 | 
					            chooseId: null,
 | 
				
			||||||
 | 
					            /**
 | 
				
			||||||
 | 
					             * 选中的数据
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            chooseData: null,
 | 
				
			||||||
 | 
					            /**
 | 
				
			||||||
 | 
					             * 查询条件
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            query: {
 | 
				
			||||||
 | 
					                pageNum: 1,
 | 
				
			||||||
 | 
					                pageSize: 10,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            datas: [],
 | 
				
			||||||
 | 
					            total: 0,
 | 
				
			||||||
 | 
					            dbEditDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                data: null,
 | 
				
			||||||
 | 
					                title: '新增数据库',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onMounted(async () => {
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					            state.projects = (await projectApi.projects.request({ pageNum: 1, pageSize: 100 })).list;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const choose = (item: any) => {
 | 
				
			||||||
 | 
					            if (!item) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.chooseId = item.id;
 | 
				
			||||||
 | 
					            state.chooseData = item;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const search = async () => {
 | 
				
			||||||
 | 
					            let res: any = await dbApi.dbs.request(state.query);
 | 
				
			||||||
 | 
					            state.datas = res.list;
 | 
				
			||||||
 | 
					            state.total = res.total;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const handlePageChange = (curPage: number) => {
 | 
				
			||||||
 | 
					            state.query.pageNum = curPage;
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const editDb = (isAdd = false) => {
 | 
				
			||||||
 | 
					            if (isAdd) {
 | 
				
			||||||
 | 
					                state.dbEditDialog.data = null;
 | 
				
			||||||
 | 
					                state.dbEditDialog.title = '新增数据库';
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                state.dbEditDialog.data = state.chooseData;
 | 
				
			||||||
 | 
					                state.dbEditDialog.title = '修改数据库';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.dbEditDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const valChange = () => {
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const deleteDb = async (id: number) => {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                await ElMessageBox.confirm(`确定删除该库?`, '提示', {
 | 
				
			||||||
 | 
					                    confirmButtonText: '确定',
 | 
				
			||||||
 | 
					                    cancelButtonText: '取消',
 | 
				
			||||||
 | 
					                    type: 'warning',
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                await dbApi.deleteDb.request({ id });
 | 
				
			||||||
 | 
					                ElMessage.success('删除成功');
 | 
				
			||||||
 | 
					                state.chooseData = null;
 | 
				
			||||||
 | 
					                state.chooseId = null;
 | 
				
			||||||
 | 
					                search();
 | 
				
			||||||
 | 
					            } catch (err) {}
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            // enums,
 | 
				
			||||||
 | 
					            search,
 | 
				
			||||||
 | 
					            choose,
 | 
				
			||||||
 | 
					            handlePageChange,
 | 
				
			||||||
 | 
					            editDb,
 | 
				
			||||||
 | 
					            valChange,
 | 
				
			||||||
 | 
					            deleteDb,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -1,15 +1,28 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <div class="toolbar">
 | 
					        <div class="toolbar">
 | 
				
			||||||
            <div class="fl">
 | 
					            <el-row type="flex" justify="space-between">
 | 
				
			||||||
                <el-select size="small" v-model="dbId" placeholder="请选择数据库" @change="changeDb" @clear="clearDb" clearable filterable>
 | 
					                <el-col :span="24">
 | 
				
			||||||
                    <el-option v-for="item in dbs" :key="item.id" :label="`${item.name} [${dbTypeName(item.type)}]`" :value="item.id"> </el-option>
 | 
					                    <project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearDb">
 | 
				
			||||||
 | 
					                        <template #default>
 | 
				
			||||||
 | 
					                            <el-form-item label="数据库">
 | 
				
			||||||
 | 
					                                <el-select v-model="dbId" placeholder="请选择数据库" @change="changeDb" @clear="clearDb" clearable filterable>
 | 
				
			||||||
 | 
					                                    <el-option v-for="item in dbs" :key="item.id" :label="item.database" :value="item.id">
 | 
				
			||||||
 | 
					                                        <span style="float: left">{{ item.database }}</span>
 | 
				
			||||||
 | 
					                                        <span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{
 | 
				
			||||||
 | 
					                                            `${item.name}  [${item.type}]`
 | 
				
			||||||
 | 
					                                        }}</span>
 | 
				
			||||||
 | 
					                                    </el-option>
 | 
				
			||||||
                                </el-select>
 | 
					                                </el-select>
 | 
				
			||||||
            </div>
 | 
					                            </el-form-item>
 | 
				
			||||||
 | 
					                        </template>
 | 
				
			||||||
 | 
					                    </project-env-select>
 | 
				
			||||||
 | 
					                </el-col>
 | 
				
			||||||
 | 
					            </el-row>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-container style="height: 50%; border: 1px solid #eee; margin-top: 1px">
 | 
					        <el-container style="border: 1px solid #eee; margin-top: 1px">
 | 
				
			||||||
            <el-aside width="70%" style="background-color: rgb(238, 241, 246)">
 | 
					            <el-aside id="sqlcontent" width="65%" style="background-color: rgb(238, 241, 246)">
 | 
				
			||||||
                <div class="toolbar">
 | 
					                <div class="toolbar">
 | 
				
			||||||
                    <div class="fl">
 | 
					                    <div class="fl">
 | 
				
			||||||
                        <el-button @click="runSql" type="success" icon="el-icon-video-play" size="mini" plain>执行</el-button>
 | 
					                        <el-button @click="runSql" type="success" icon="el-icon-video-play" size="mini" plain>执行</el-button>
 | 
				
			||||||
@@ -19,12 +32,12 @@
 | 
				
			|||||||
                        <el-button @click="saveSql" type="primary" icon="el-icon-document-add" size="mini" plain>保存</el-button>
 | 
					                        <el-button @click="saveSql" type="primary" icon="el-icon-document-add" size="mini" plain>保存</el-button>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <codemirror class="codesql" ref="cmEditor" language="sql" v-model="sql" :options="cmOptions" />
 | 
					                <codemirror @beforeChange="onBeforeChange" class="codesql" ref="cmEditor" language="sql" v-model="sql" :options="cmOptions" />
 | 
				
			||||||
            </el-aside>
 | 
					            </el-aside>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <el-container style="margin-left: 2px">
 | 
					            <el-container style="margin-left: 2px">
 | 
				
			||||||
                <el-header style="text-align: left; height: 45px; font-size: 12px; padding: 0px">
 | 
					                <el-header style="text-align: left; height: 45px; font-size: 12px; padding: 0px">
 | 
				
			||||||
                    <el-select v-model="tableName" placeholder="请选择表" @change="changeTable" clearable filterable style="width: 99%">
 | 
					                    <el-select v-model="tableName" placeholder="请选择表" @change="changeTable" filterable style="width: 99%">
 | 
				
			||||||
                        <el-option
 | 
					                        <el-option
 | 
				
			||||||
                            v-for="item in tableMetadata"
 | 
					                            v-for="item in tableMetadata"
 | 
				
			||||||
                            :key="item.tableName"
 | 
					                            :key="item.tableName"
 | 
				
			||||||
@@ -35,20 +48,22 @@
 | 
				
			|||||||
                    </el-select>
 | 
					                    </el-select>
 | 
				
			||||||
                </el-header>
 | 
					                </el-header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-main style="padding: 0px; height: 100%; overflow: hidden">
 | 
					                <el-main style="padding: 0px; overflow: hidden">
 | 
				
			||||||
                    <el-table :data="columnMetadata" height="100%" size="mini">
 | 
					                    <el-table :data="columnMetadata" height="100%" size="mini">
 | 
				
			||||||
                        <el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column>
 | 
					                        <el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
                        <el-table-column prop="columnType" label="类型" show-overflow-tooltip> </el-table-column>
 | 
					 | 
				
			||||||
                        <el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
 | 
					                        <el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					                        <el-table-column width="120" prop="columnType" label="类型" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
                    </el-table>
 | 
					                    </el-table>
 | 
				
			||||||
                </el-main>
 | 
					                </el-main>
 | 
				
			||||||
            </el-container>
 | 
					            </el-container>
 | 
				
			||||||
        </el-container>
 | 
					        </el-container>
 | 
				
			||||||
        <el-table style="margin-top: 1px" :data="selectRes.data" size="mini" max-height="300" stripe border>
 | 
					
 | 
				
			||||||
 | 
					        <el-table style="margin-top: 1px" :data="execRes.data" size="mini" max-height="300" :empty-text="execRes.emptyResText" stripe border>
 | 
				
			||||||
            <el-table-column
 | 
					            <el-table-column
 | 
				
			||||||
                min-width="92"
 | 
					                min-width="100"
 | 
				
			||||||
 | 
					                :width="flexColumnWidth(item, execRes.data)"
 | 
				
			||||||
                align="center"
 | 
					                align="center"
 | 
				
			||||||
                v-for="item in selectRes.tableColumn"
 | 
					                v-for="item in execRes.tableColumn"
 | 
				
			||||||
                :key="item"
 | 
					                :key="item"
 | 
				
			||||||
                :prop="item"
 | 
					                :prop="item"
 | 
				
			||||||
                :label="item"
 | 
					                :label="item"
 | 
				
			||||||
@@ -56,20 +71,11 @@
 | 
				
			|||||||
            >
 | 
					            >
 | 
				
			||||||
            </el-table-column>
 | 
					            </el-table-column>
 | 
				
			||||||
        </el-table>
 | 
					        </el-table>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- <el-pagination
 | 
					 | 
				
			||||||
      style="text-align: center"
 | 
					 | 
				
			||||||
      background
 | 
					 | 
				
			||||||
      layout="prev, pager, next, total, jumper"
 | 
					 | 
				
			||||||
      :total="data.total"
 | 
					 | 
				
			||||||
      :current-page.sync="params.pageNum"
 | 
					 | 
				
			||||||
      :page-size="params.pageSize"
 | 
					 | 
				
			||||||
    	/> -->
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { toRefs, reactive, computed, onMounted, defineComponent, ref } from 'vue';
 | 
					import { toRefs, reactive, computed, defineComponent, ref } from 'vue';
 | 
				
			||||||
import { dbApi } from './api';
 | 
					import { dbApi } from './api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'codemirror/theme/ambiance.css';
 | 
					import 'codemirror/theme/ambiance.css';
 | 
				
			||||||
@@ -79,48 +85,56 @@ import 'codemirror/lib/codemirror.css';
 | 
				
			|||||||
// 引入主题后还需要在 options 中指定主题才会生效
 | 
					// 引入主题后还需要在 options 中指定主题才会生效
 | 
				
			||||||
import 'codemirror/theme/base16-light.css';
 | 
					import 'codemirror/theme/base16-light.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// require('codemirror/addon/edit/matchbrackets')
 | 
					 | 
				
			||||||
import 'codemirror/addon/selection/active-line';
 | 
					import 'codemirror/addon/selection/active-line';
 | 
				
			||||||
import { codemirror } from '@/components/codemirror';
 | 
					import { codemirror } from '@/components/codemirror';
 | 
				
			||||||
// import 'codemirror/mode/sql/sql.js';
 | 
					// import 'codemirror/mode/sql/sql.js';
 | 
				
			||||||
// import 'codemirror/addon/hint/show-hint.js';
 | 
					import 'codemirror/addon/hint/show-hint.js';
 | 
				
			||||||
// import 'codemirror/addon/hint/sql-hint.js';
 | 
					import 'codemirror/addon/hint/sql-hint.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sqlFormatter from 'sql-formatter';
 | 
					import sqlFormatter from 'sql-formatter';
 | 
				
			||||||
import { notEmpty } from '@/common/assert';
 | 
					import { notNull, notEmpty } from '@/common/assert';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'SelectData',
 | 
					    name: 'SqlExec',
 | 
				
			||||||
    components: {
 | 
					    components: {
 | 
				
			||||||
        codemirror,
 | 
					        codemirror,
 | 
				
			||||||
 | 
					        ProjectEnvSelect,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    setup() {
 | 
				
			||||||
        const cmEditor: any = ref(null);
 | 
					        const cmEditor: any = ref(null);
 | 
				
			||||||
        const state = reactive({
 | 
					        const state = reactive({
 | 
				
			||||||
            dbs: [],
 | 
					            dbs: [],
 | 
				
			||||||
            tables: [],
 | 
					            tables: [],
 | 
				
			||||||
            dbId: '',
 | 
					            dbId: null,
 | 
				
			||||||
            tableName: '',
 | 
					            tableName: '',
 | 
				
			||||||
            tableMetadata: [],
 | 
					            tableMetadata: [],
 | 
				
			||||||
            columnMetadata: [],
 | 
					            columnMetadata: [],
 | 
				
			||||||
            sql: '',
 | 
					            sql: '',
 | 
				
			||||||
            selectRes: {
 | 
					            sqlTabs: {
 | 
				
			||||||
 | 
					                tabs: [] as any,
 | 
				
			||||||
 | 
					                active: '',
 | 
				
			||||||
 | 
					                index: 1,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            execRes: {
 | 
				
			||||||
                tableColumn: [],
 | 
					                tableColumn: [],
 | 
				
			||||||
                data: [],
 | 
					                data: [],
 | 
				
			||||||
 | 
					                emptyResText: '没有数据',
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            params: {
 | 
					            params: {
 | 
				
			||||||
                pageNum: 1,
 | 
					                pageNum: 1,
 | 
				
			||||||
                pageSize: 10,
 | 
					                pageSize: 10,
 | 
				
			||||||
 | 
					                envId: null,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            cmOptions: {
 | 
					            cmOptions: {
 | 
				
			||||||
                tabSize: 4,
 | 
					                tabSize: 4,
 | 
				
			||||||
                mode: 'text/x-sql',
 | 
					                mode: 'text/x-sql',
 | 
				
			||||||
                // theme: 'cobalt',
 | 
					 | 
				
			||||||
                lineNumbers: true,
 | 
					                lineNumbers: true,
 | 
				
			||||||
                line: true,
 | 
					                line: true,
 | 
				
			||||||
                indentWithTabs: true,
 | 
					                indentWithTabs: true,
 | 
				
			||||||
                smartIndent: true,
 | 
					                smartIndent: true,
 | 
				
			||||||
                // matchBrackets: true,
 | 
					                matchBrackets: true,
 | 
				
			||||||
                theme: 'base16-light',
 | 
					                theme: 'base16-light',
 | 
				
			||||||
                autofocus: true,
 | 
					                autofocus: true,
 | 
				
			||||||
                extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键
 | 
					                extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键
 | 
				
			||||||
@@ -137,13 +151,18 @@ export default defineComponent({
 | 
				
			|||||||
            return cmEditor.value.coder;
 | 
					            return cmEditor.value.coder;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const dbTypeName = (type: any) => {
 | 
					        /**
 | 
				
			||||||
            return 'mysql';
 | 
					         * 项目及环境更改后的回调事件
 | 
				
			||||||
        };
 | 
					         */
 | 
				
			||||||
 | 
					        const changeProjectEnv = (projectId: any, envId: any) => {
 | 
				
			||||||
        onMounted(() => {
 | 
					            state.dbs = [];
 | 
				
			||||||
 | 
					            state.dbId = null;
 | 
				
			||||||
 | 
					            clearDb();
 | 
				
			||||||
 | 
					            if (envId != null) {
 | 
				
			||||||
 | 
					                state.params.envId = envId;
 | 
				
			||||||
                search();
 | 
					                search();
 | 
				
			||||||
        });
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * 输入字符给提示
 | 
					         * 输入字符给提示
 | 
				
			||||||
@@ -154,29 +173,92 @@ export default defineComponent({
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const onBeforeChange = (instance: any, changeObj: any) => {
 | 
				
			||||||
 | 
					            var text = changeObj.text[0];
 | 
				
			||||||
 | 
					            // 将sql提示去除
 | 
				
			||||||
 | 
					            changeObj.text[0] = text.split('  ')[0];
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * 执行sql
 | 
					         * 执行sql
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        const runSql = async () => {
 | 
					        const runSql = async () => {
 | 
				
			||||||
            notEmpty(state.dbId, '请先选择数据库');
 | 
					            notNull(state.dbId, '请先选择数据库');
 | 
				
			||||||
            // 没有选中的文本,则为全部文本
 | 
					            // 没有选中的文本,则为全部文本
 | 
				
			||||||
            let selectSql = getSql();
 | 
					            let sql = getSql();
 | 
				
			||||||
            notEmpty(selectSql, '内容不能为空');
 | 
					            notNull(sql, '内容不能为空');
 | 
				
			||||||
            const res = await dbApi.selectData.request({
 | 
					
 | 
				
			||||||
 | 
					            state.execRes.tableColumn = [];
 | 
				
			||||||
 | 
					            state.execRes.data = [];
 | 
				
			||||||
 | 
					            state.execRes.emptyResText = '查询中...';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const res = await dbApi.sqlExec.request({
 | 
				
			||||||
                id: state.dbId,
 | 
					                id: state.dbId,
 | 
				
			||||||
                selectSql: selectSql,
 | 
					                sql: sql,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            let tableColumn: any;
 | 
					            state.execRes.emptyResText = '没有数据';
 | 
				
			||||||
            let data;
 | 
					            state.execRes.tableColumn = res.colNames;
 | 
				
			||||||
            if (res.length > 0) {
 | 
					            state.execRes.data = res.res;
 | 
				
			||||||
                tableColumn = Object.keys(res[0]);
 | 
					        };
 | 
				
			||||||
                data = res;
 | 
					
 | 
				
			||||||
            } else {
 | 
					        const flexColumnWidth = (str: any, tableData: any, flag = 'equal') => {
 | 
				
			||||||
                tableColumn = [];
 | 
					            // str为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
 | 
				
			||||||
                data = [];
 | 
					            // flag为可选值,可不传该参数,传参时可选'max'或'equal',默认为'max'
 | 
				
			||||||
 | 
					            // flag为'max'则设置列宽适配该列中最长的内容,flag为'equal'则设置列宽适配该列中第一行内容的长度。
 | 
				
			||||||
 | 
					            str = str + '';
 | 
				
			||||||
 | 
					            let columnContent = '';
 | 
				
			||||||
 | 
					            if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            state.selectRes.tableColumn = tableColumn;
 | 
					            if (!str || !str.length || str.length === 0 || str === undefined) {
 | 
				
			||||||
            state.selectRes.data = data;
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (flag === 'equal') {
 | 
				
			||||||
 | 
					                // 获取该列中第一个不为空的数据(内容)
 | 
				
			||||||
 | 
					                for (let i = 0; i < tableData.length; i++) {
 | 
				
			||||||
 | 
					                    if (tableData[i][str].length > 0) {
 | 
				
			||||||
 | 
					                        columnContent = tableData[i][str];
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // 获取该列中最长的数据(内容)
 | 
				
			||||||
 | 
					                let index = 0;
 | 
				
			||||||
 | 
					                for (let i = 0; i < tableData.length; i++) {
 | 
				
			||||||
 | 
					                    if (tableData[i][str] === null) {
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    const now_temp = tableData[i][str] + '';
 | 
				
			||||||
 | 
					                    const max_temp = tableData[index][str] + '';
 | 
				
			||||||
 | 
					                    if (now_temp.length > max_temp.length) {
 | 
				
			||||||
 | 
					                        index = i;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                columnContent = tableData[index][str];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // 以下分配的单位长度可根据实际需求进行调整
 | 
				
			||||||
 | 
					            let flexWidth = 0;
 | 
				
			||||||
 | 
					            for (const char of columnContent) {
 | 
				
			||||||
 | 
					                if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
 | 
				
			||||||
 | 
					                    // 如果是英文字符,为字符分配8个单位宽度
 | 
				
			||||||
 | 
					                    flexWidth += 8;
 | 
				
			||||||
 | 
					                } else if (char >= '\u4e00' && char <= '\u9fa5') {
 | 
				
			||||||
 | 
					                    // 如果是中文字符,为字符分配15个单位宽度
 | 
				
			||||||
 | 
					                    flexWidth += 15;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // 其他种类字符,为字符分配8个单位宽度
 | 
				
			||||||
 | 
					                    flexWidth += 8;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (flexWidth < 80) {
 | 
				
			||||||
 | 
					                // 设置最小宽度
 | 
				
			||||||
 | 
					                flexWidth = 80;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (flexWidth > 350) {
 | 
				
			||||||
 | 
					                // 设置最大宽度
 | 
				
			||||||
 | 
					                flexWidth = 350;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return flexWidth + 'px';
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
@@ -193,7 +275,7 @@ export default defineComponent({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const saveSql = async () => {
 | 
					        const saveSql = async () => {
 | 
				
			||||||
            notEmpty(state.sql, 'sql内容不能为空');
 | 
					            notEmpty(state.sql, 'sql内容不能为空');
 | 
				
			||||||
            notEmpty(state.dbId, '请先选择数据库');
 | 
					            notNull(state.dbId, '请先选择数据库');
 | 
				
			||||||
            await dbApi.saveSql.request({ id: state.dbId, sql: state.sql, type: 1 });
 | 
					            await dbApi.saveSql.request({ id: state.dbId, sql: state.sql, type: 1 });
 | 
				
			||||||
            ElMessage.success('保存成功');
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@@ -235,9 +317,10 @@ export default defineComponent({
 | 
				
			|||||||
            state.tableName = '';
 | 
					            state.tableName = '';
 | 
				
			||||||
            state.tableMetadata = [];
 | 
					            state.tableMetadata = [];
 | 
				
			||||||
            state.columnMetadata = [];
 | 
					            state.columnMetadata = [];
 | 
				
			||||||
            state.selectRes.data = [];
 | 
					            state.execRes.data = [];
 | 
				
			||||||
            state.selectRes.tableColumn = [];
 | 
					            state.execRes.tableColumn = [];
 | 
				
			||||||
            state.sql = '';
 | 
					            state.sql = '';
 | 
				
			||||||
 | 
					            state.cmOptions.hintOptions.tables = [];
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 选择表事件
 | 
					        // 选择表事件
 | 
				
			||||||
@@ -268,7 +351,7 @@ export default defineComponent({
 | 
				
			|||||||
                codemirror.value.replaceSelection(sqlFormatter.format(selectSql));
 | 
					                codemirror.value.replaceSelection(sqlFormatter.format(selectSql));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                /* 将sql内容进行格式后放入编辑器中*/
 | 
					                /* 将sql内容进行格式后放入编辑器中*/
 | 
				
			||||||
                codemirror.value.setValue(sqlFormatter.format(state.sql));
 | 
					                state.sql = sqlFormatter.format(sqlFormatter.format(state.sql));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -280,14 +363,16 @@ export default defineComponent({
 | 
				
			|||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
            cmEditor,
 | 
					            cmEditor,
 | 
				
			||||||
            dbTypeName,
 | 
					            changeProjectEnv,
 | 
				
			||||||
            inputRead,
 | 
					            inputRead,
 | 
				
			||||||
            changeTable,
 | 
					            changeTable,
 | 
				
			||||||
            runSql,
 | 
					            runSql,
 | 
				
			||||||
 | 
					            flexColumnWidth,
 | 
				
			||||||
            saveSql,
 | 
					            saveSql,
 | 
				
			||||||
            changeDb,
 | 
					            changeDb,
 | 
				
			||||||
            clearDb,
 | 
					            clearDb,
 | 
				
			||||||
            formatSql,
 | 
					            formatSql,
 | 
				
			||||||
 | 
					            onBeforeChange,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -298,4 +383,9 @@ export default defineComponent({
 | 
				
			|||||||
    font-size: 10pt;
 | 
					    font-size: 10pt;
 | 
				
			||||||
    font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
 | 
					    font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#sqlcontent {
 | 
				
			||||||
 | 
					    .CodeMirror {
 | 
				
			||||||
 | 
					        height: 300px !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
@@ -3,11 +3,13 @@ import Api from '@/common/Api';
 | 
				
			|||||||
export const dbApi = {
 | 
					export const dbApi = {
 | 
				
			||||||
    // 获取权限列表
 | 
					    // 获取权限列表
 | 
				
			||||||
    dbs: Api.create("/dbs", 'get'),
 | 
					    dbs: Api.create("/dbs", 'get'),
 | 
				
			||||||
 | 
					    saveDb: Api.create("/dbs", 'post'),
 | 
				
			||||||
 | 
					    deleteDb: Api.create("/dbs/{id}", 'delete'),
 | 
				
			||||||
    tableMetadata: Api.create("/dbs/{id}/t-metadata", 'get'),
 | 
					    tableMetadata: Api.create("/dbs/{id}/t-metadata", 'get'),
 | 
				
			||||||
    columnMetadata: Api.create("/dbs/{id}/c-metadata", 'get'),
 | 
					    columnMetadata: Api.create("/dbs/{id}/c-metadata", 'get'),
 | 
				
			||||||
    // 获取表即列提示
 | 
					    // 获取表即列提示
 | 
				
			||||||
    hintTables: Api.create("/dbs/{id}/hint-tables", 'get'),
 | 
					    hintTables: Api.create("/dbs/{id}/hint-tables", 'get'),
 | 
				
			||||||
    selectData: Api.create("/dbs/{id}/select", 'get'),
 | 
					    sqlExec: Api.create("/dbs/{id}/exec-sql", 'get'),
 | 
				
			||||||
    // 保存sql
 | 
					    // 保存sql
 | 
				
			||||||
    saveSql: Api.create("/dbs/{id}/sql", 'post'),
 | 
					    saveSql: Api.create("/dbs/{id}/sql", 'post'),
 | 
				
			||||||
    // 获取保存的sql
 | 
					    // 获取保存的sql
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
export { default } from './SelectData.vue';
 | 
					export { default } from './SqlExec.vue';
 | 
				
			||||||
							
								
								
									
										182
									
								
								mayfly_go_web/src/views/ops/machine/MachineEdit.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								mayfly_go_web/src/views/ops/machine/MachineEdit.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="35%">
 | 
				
			||||||
 | 
					            <el-form :model="form" ref="machineForm" :rules="rules" label-width="85px" size="small">
 | 
				
			||||||
 | 
					                <!-- <el-form-item prop="projectId" label="项目:" required>
 | 
				
			||||||
 | 
					                    <el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
 | 
				
			||||||
 | 
					                        <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item prop="envId" label="环境:" required>
 | 
				
			||||||
 | 
					                    <el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
 | 
				
			||||||
 | 
					                        <el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item> -->
 | 
				
			||||||
 | 
					                <el-form-item prop="name" label="名称:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.name" placeholder="请输入机器别名" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="ip" label="ip:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.ip" placeholder="请输入主机ip" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="port" label="port:" required>
 | 
				
			||||||
 | 
					                    <el-input type="number" v-model.trim="form.port" placeholder="请输入端口"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="username" label="用户名:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="password" label="密码:" required>
 | 
				
			||||||
 | 
					                    <el-input
 | 
				
			||||||
 | 
					                        type="password"
 | 
				
			||||||
 | 
					                        show-password
 | 
				
			||||||
 | 
					                        v-model.trim="form.password"
 | 
				
			||||||
 | 
					                        placeholder="请输入密码"
 | 
				
			||||||
 | 
					                        autocomplete="new-password"
 | 
				
			||||||
 | 
					                    ></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					            </el-form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <div class="dialog-footer">
 | 
				
			||||||
 | 
					                    <el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini">确 定</el-button>
 | 
				
			||||||
 | 
					                    <el-button @click="cancel()" size="mini">取 消</el-button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { toRefs, reactive, watch, onMounted, defineComponent, ref } from 'vue';
 | 
				
			||||||
 | 
					import { machineApi } from './api';
 | 
				
			||||||
 | 
					import { projectApi } from '../project/api.ts';
 | 
				
			||||||
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'MachineEdit',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        visible: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        machine: {
 | 
				
			||||||
 | 
					            type: [Boolean, Object],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        title: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup(props: any, { emit }) {
 | 
				
			||||||
 | 
					        const machineForm: any = ref(null);
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            visible: false,
 | 
				
			||||||
 | 
					            form: {
 | 
				
			||||||
 | 
					                id: null,
 | 
				
			||||||
 | 
					                name: null,
 | 
				
			||||||
 | 
					                port: 22,
 | 
				
			||||||
 | 
					                username: null,
 | 
				
			||||||
 | 
					                password: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            btnLoading: false,
 | 
				
			||||||
 | 
					            rules: {
 | 
				
			||||||
 | 
					                projectId: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择项目',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                envId: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择环境',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                name: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入别名',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                ip: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入主机ip',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                port: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入端口',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                username: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入用户名',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                password: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入密码',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(props, async (newValue, oldValue) => {
 | 
				
			||||||
 | 
					            state.visible = newValue.visible;
 | 
				
			||||||
 | 
					            if (newValue.machine) {
 | 
				
			||||||
 | 
					                state.form = { ...newValue.machine };
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                state.form = { port: 22 } as any;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const btnOk = async () => {
 | 
				
			||||||
 | 
					            machineForm.value.validate((valid: boolean) => {
 | 
				
			||||||
 | 
					                if (valid) {
 | 
				
			||||||
 | 
					                    machineApi.saveMachine.request(state.form).then((res: any) => {
 | 
				
			||||||
 | 
					                        ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					                        emit('val-change', state.form);
 | 
				
			||||||
 | 
					                        state.btnLoading = true;
 | 
				
			||||||
 | 
					                        setTimeout(() => {
 | 
				
			||||||
 | 
					                            state.btnLoading = false;
 | 
				
			||||||
 | 
					                        }, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        cancel();
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ElMessage.error('请正确填写信息');
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cancel = () => {
 | 
				
			||||||
 | 
					            emit('update:visible', false);
 | 
				
			||||||
 | 
					            emit('cancel');
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                machineForm.value.resetFields();
 | 
				
			||||||
 | 
					                //  重置对象属性为null
 | 
				
			||||||
 | 
					                state.form = {} as any;
 | 
				
			||||||
 | 
					            }, 200);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            machineForm,
 | 
				
			||||||
 | 
					            btnOk,
 | 
				
			||||||
 | 
					            cancel,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -42,16 +42,19 @@
 | 
				
			|||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
            </el-table-column>
 | 
					            </el-table-column>
 | 
				
			||||||
            <el-table-column prop="name" label="名称" width></el-table-column>
 | 
					            <el-table-column prop="name" label="名称" width></el-table-column>
 | 
				
			||||||
            <el-table-column prop="ip" label="IP" width></el-table-column>
 | 
					            <el-table-column prop="ip" label="ip:port" min-width="160">
 | 
				
			||||||
            <el-table-column prop="port" label="端口" :min-width="40"></el-table-column>
 | 
					                <template #default="scope">
 | 
				
			||||||
            <el-table-column prop="username" label="用户名" :min-width="40"></el-table-column>
 | 
					                    {{ `${scope.row.ip}:${scope.row.port}` }}
 | 
				
			||||||
            <el-table-column prop="createTime" label="创建时间" :min-width="100">
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="username" label="用户名" :min-width="45"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="createTime" label="创建时间" min-width="160">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
					                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
            </el-table-column>
 | 
					            </el-table-column>
 | 
				
			||||||
            <el-table-column prop="creator" label="创建者" :min-width="50"></el-table-column>
 | 
					            <el-table-column prop="creator" label="创建者" min-width="50"></el-table-column>
 | 
				
			||||||
            <el-table-column prop="updateTime" label="更新时间" :min-width="100">
 | 
					            <el-table-column prop="updateTime" label="更新时间" min-width="160">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
                    {{ $filters.dateFormat(scope.row.updateTime) }}
 | 
					                    {{ $filters.dateFormat(scope.row.updateTime) }}
 | 
				
			||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
@@ -59,7 +62,7 @@
 | 
				
			|||||||
            <el-table-column prop="modifier" label="修改者" :min-width="50"></el-table-column>
 | 
					            <el-table-column prop="modifier" label="修改者" :min-width="50"></el-table-column>
 | 
				
			||||||
            <el-table-column label="操作" min-width="200px">
 | 
					            <el-table-column label="操作" min-width="200px">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
                    <el-button type="primary" @click="monitor(scope.row.id)" icom="el-icon-tickets" size="mini" plain>监控</el-button>
 | 
					                    <!-- <el-button type="primary" @click="monitor(scope.row.id)" icom="el-icon-tickets" size="mini" plain>监控</el-button> -->
 | 
				
			||||||
                    <el-button type="success" @click="serviceManager(scope.row)" size="mini" plain>脚本管理</el-button>
 | 
					                    <el-button type="success" @click="serviceManager(scope.row)" size="mini" plain>脚本管理</el-button>
 | 
				
			||||||
                    <el-button v-auth="'machine:terminal'" type="success" @click="showTerminal(scope.row)" size="mini" plain>终端</el-button>
 | 
					                    <el-button v-auth="'machine:terminal'" type="success" @click="showTerminal(scope.row)" size="mini" plain>终端</el-button>
 | 
				
			||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
@@ -75,6 +78,12 @@
 | 
				
			|||||||
            :page-size="params.pageSize"
 | 
					            :page-size="params.pageSize"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <machine-edit
 | 
				
			||||||
 | 
					            :title="machineEditDialog.title"
 | 
				
			||||||
 | 
					            v-model:visible="machineEditDialog.visible"
 | 
				
			||||||
 | 
					            v-model:machine="machineEditDialog.data"
 | 
				
			||||||
 | 
					        ></machine-edit>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- <el-dialog @close="closeMonitor" title="监控信息" v-model="monitorDialog.visible" width="60%">
 | 
					        <!-- <el-dialog @close="closeMonitor" title="监控信息" v-model="monitorDialog.visible" width="60%">
 | 
				
			||||||
			<monitor ref="monitorDialogRef" :machineId="monitorDialog.machineId" />
 | 
								<monitor ref="monitorDialogRef" :machineId="monitorDialog.machineId" />
 | 
				
			||||||
		</el-dialog> -->
 | 
							</el-dialog> -->
 | 
				
			||||||
@@ -82,40 +91,32 @@
 | 
				
			|||||||
        <service-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
 | 
					        <service-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <file-manage :title="fileDialog.title" v-model:visible="fileDialog.visible" v-model:machineId="fileDialog.machineId" />
 | 
					        <file-manage :title="fileDialog.title" v-model:visible="fileDialog.visible" v-model:machineId="fileDialog.machineId" />
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <dynamic-form-dialog
 | 
					 | 
				
			||||||
            v-model:visible="formDialog.visible"
 | 
					 | 
				
			||||||
            :title="formDialog.title"
 | 
					 | 
				
			||||||
            :formInfo="formDialog.formInfo"
 | 
					 | 
				
			||||||
            v-model:formData="formDialog.formData"
 | 
					 | 
				
			||||||
            @submitSuccess="submitSuccess"
 | 
					 | 
				
			||||||
        ></dynamic-form-dialog>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
					import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { DynamicFormDialog } from '@/components/dynamic-form';
 | 
					import { DynamicFormDialog } from '@/components/dynamic-form';
 | 
				
			||||||
// import Monitor from './Monitor.vue';
 | 
					// import Monitor from './Monitor.vue';
 | 
				
			||||||
import { machineApi } from './api';
 | 
					import { machineApi } from './api';
 | 
				
			||||||
import SshTerminal from './SshTerminal.vue';
 | 
					import SshTerminal from './SshTerminal.vue';
 | 
				
			||||||
import ServiceManage from './ServiceManage.vue';
 | 
					import ServiceManage from './ServiceManage.vue';
 | 
				
			||||||
import FileManage from './FileManage.vue';
 | 
					import FileManage from './FileManage.vue';
 | 
				
			||||||
 | 
					import MachineEdit from './MachineEdit.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'MachineList',
 | 
					    name: 'MachineList',
 | 
				
			||||||
    components: {
 | 
					    components: {
 | 
				
			||||||
        // Monitor,
 | 
					 | 
				
			||||||
        SshTerminal,
 | 
					        SshTerminal,
 | 
				
			||||||
        ServiceManage,
 | 
					        ServiceManage,
 | 
				
			||||||
        FileManage,
 | 
					        FileManage,
 | 
				
			||||||
        DynamicFormDialog,
 | 
					        DynamicFormDialog,
 | 
				
			||||||
 | 
					        MachineEdit,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    setup() {
 | 
				
			||||||
        const router = useRouter();
 | 
					        const router = useRouter();
 | 
				
			||||||
        // const monitorDialogRef = ref();
 | 
					 | 
				
			||||||
        const state = reactive({
 | 
					        const state = reactive({
 | 
				
			||||||
            params: {
 | 
					            params: {
 | 
				
			||||||
                pageNum: 1,
 | 
					                pageNum: 1,
 | 
				
			||||||
@@ -149,86 +150,10 @@ export default defineComponent({
 | 
				
			|||||||
                visible: false,
 | 
					                visible: false,
 | 
				
			||||||
                machineId: 0,
 | 
					                machineId: 0,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            formDialog: {
 | 
					            machineEditDialog: {
 | 
				
			||||||
                visible: false,
 | 
					                visible: false,
 | 
				
			||||||
                title: '',
 | 
					                data: null,
 | 
				
			||||||
                formInfo: {
 | 
					                title: '新增机器',
 | 
				
			||||||
                    createApi: machineApi.save,
 | 
					 | 
				
			||||||
                    updateApi: machineApi.save,
 | 
					 | 
				
			||||||
                    formRows: [
 | 
					 | 
				
			||||||
                        [
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                type: 'input',
 | 
					 | 
				
			||||||
                                label: '名称:',
 | 
					 | 
				
			||||||
                                name: 'name',
 | 
					 | 
				
			||||||
                                placeholder: '请输入名称',
 | 
					 | 
				
			||||||
                                rules: [
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        required: true,
 | 
					 | 
				
			||||||
                                        message: '请输入名称',
 | 
					 | 
				
			||||||
                                        trigger: ['blur', 'change'],
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                ],
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ],
 | 
					 | 
				
			||||||
                        [
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                type: 'input',
 | 
					 | 
				
			||||||
                                label: 'ip:',
 | 
					 | 
				
			||||||
                                name: 'ip',
 | 
					 | 
				
			||||||
                                placeholder: '请输入ip',
 | 
					 | 
				
			||||||
                                rules: [
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        required: true,
 | 
					 | 
				
			||||||
                                        message: '请输入ip',
 | 
					 | 
				
			||||||
                                        trigger: ['blur', 'change'],
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                ],
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ],
 | 
					 | 
				
			||||||
                        [
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                type: 'input',
 | 
					 | 
				
			||||||
                                label: '端口号:',
 | 
					 | 
				
			||||||
                                name: 'port',
 | 
					 | 
				
			||||||
                                placeholder: '请输入端口号',
 | 
					 | 
				
			||||||
                                inputType: 'number',
 | 
					 | 
				
			||||||
                                rules: [
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        required: true,
 | 
					 | 
				
			||||||
                                        message: '请输入ip',
 | 
					 | 
				
			||||||
                                        trigger: ['blur', 'change'],
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                ],
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ],
 | 
					 | 
				
			||||||
                        [
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                type: 'input',
 | 
					 | 
				
			||||||
                                label: '用户名:',
 | 
					 | 
				
			||||||
                                name: 'username',
 | 
					 | 
				
			||||||
                                placeholder: '请输入用户名',
 | 
					 | 
				
			||||||
                                rules: [
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        required: true,
 | 
					 | 
				
			||||||
                                        message: '请输入用户名',
 | 
					 | 
				
			||||||
                                        trigger: ['blur', 'change'],
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                ],
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ],
 | 
					 | 
				
			||||||
                        [
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                type: 'input',
 | 
					 | 
				
			||||||
                                label: '密码:',
 | 
					 | 
				
			||||||
                                name: 'password',
 | 
					 | 
				
			||||||
                                placeholder: '请输入密码',
 | 
					 | 
				
			||||||
                                inputType: 'password',
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ],
 | 
					 | 
				
			||||||
                    ],
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                formData: { port: 22 },
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -261,7 +186,6 @@ export default defineComponent({
 | 
				
			|||||||
        // };
 | 
					        // };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const showTerminal = (row: any) => {
 | 
					        const showTerminal = (row: any) => {
 | 
				
			||||||
            // router.push(`/machine/${row.id}/terminal?id=${row.id}&name=${row.name}&time=${new Date().getTime()}`);
 | 
					 | 
				
			||||||
            const { href } = router.resolve({
 | 
					            const { href } = router.resolve({
 | 
				
			||||||
                path: `/machine/terminal`,
 | 
					                path: `/machine/terminal`,
 | 
				
			||||||
                query: {
 | 
					                query: {
 | 
				
			||||||
@@ -275,21 +199,30 @@ export default defineComponent({
 | 
				
			|||||||
        const openFormDialog = (redis: any) => {
 | 
					        const openFormDialog = (redis: any) => {
 | 
				
			||||||
            let dialogTitle;
 | 
					            let dialogTitle;
 | 
				
			||||||
            if (redis) {
 | 
					            if (redis) {
 | 
				
			||||||
                state.formDialog.formData = state.currentData as any;
 | 
					                state.machineEditDialog.data = state.currentData as any;
 | 
				
			||||||
                dialogTitle = '编辑机器';
 | 
					                dialogTitle = '编辑机器';
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                state.formDialog.formData = { port: 22 };
 | 
					                state.machineEditDialog.data = { port: 22 } as any;
 | 
				
			||||||
                dialogTitle = '添加机器';
 | 
					                dialogTitle = '添加机器';
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            state.formDialog.title = dialogTitle;
 | 
					            state.machineEditDialog.title = dialogTitle;
 | 
				
			||||||
            state.formDialog.visible = true;
 | 
					            state.machineEditDialog.visible = true;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const deleteMachine = async (id: number) => {
 | 
					        const deleteMachine = async (id: number) => {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                await ElMessageBox.confirm(`确定删除该机器信息? 该操作将同时删除脚本及文件配置信息`, '提示', {
 | 
				
			||||||
 | 
					                    confirmButtonText: '确定',
 | 
				
			||||||
 | 
					                    cancelButtonText: '取消',
 | 
				
			||||||
 | 
					                    type: 'warning',
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
                await machineApi.del.request({ id });
 | 
					                await machineApi.del.request({ id });
 | 
				
			||||||
                ElMessage.success('操作成功');
 | 
					                ElMessage.success('操作成功');
 | 
				
			||||||
 | 
					                state.currentId = null;
 | 
				
			||||||
 | 
					                state.currentData = null;
 | 
				
			||||||
                search();
 | 
					                search();
 | 
				
			||||||
 | 
					            } catch (err) {}
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const serviceManager = (row: any) => {
 | 
					        const serviceManager = (row: any) => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,10 @@
 | 
				
			|||||||
                    </el-select>
 | 
					                    </el-select>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item prop="params" label="参数">
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.params" placeholder="参数数组json,若无可不填"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item prop="script" label="内容" id="content">
 | 
					                <el-form-item prop="script" label="内容" id="content">
 | 
				
			||||||
                    <codemirror ref="cmEditor" v-model="form.script" language="shell" />
 | 
					                    <codemirror ref="cmEditor" v-model="form.script" language="shell" />
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
@@ -31,7 +35,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <template #footer>
 | 
					            <template #footer>
 | 
				
			||||||
                <div class="dialog-footer">
 | 
					                <div class="dialog-footer">
 | 
				
			||||||
                    <el-button  v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="btnOk" size="mini" :disabled="submitDisabled">保 存</el-button>
 | 
					                    <el-button
 | 
				
			||||||
 | 
					                        v-auth="'machine:script:save'"
 | 
				
			||||||
 | 
					                        type="primary"
 | 
				
			||||||
 | 
					                        :loading="btnLoading"
 | 
				
			||||||
 | 
					                        @click="btnOk"
 | 
				
			||||||
 | 
					                        size="mini"
 | 
				
			||||||
 | 
					                        :disabled="submitDisabled"
 | 
				
			||||||
 | 
					                        >保 存</el-button
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
                    <el-button @click="cancel()" :disabled="submitDisabled" size="mini">关 闭</el-button>
 | 
					                    <el-button @click="cancel()" :disabled="submitDisabled" size="mini">关 闭</el-button>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </template>
 | 
					            </template>
 | 
				
			||||||
@@ -83,6 +95,7 @@ export default defineComponent({
 | 
				
			|||||||
                machineId: 0,
 | 
					                machineId: 0,
 | 
				
			||||||
                description: '',
 | 
					                description: '',
 | 
				
			||||||
                script: '',
 | 
					                script: '',
 | 
				
			||||||
 | 
					                params: null,
 | 
				
			||||||
                type: null,
 | 
					                type: null,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            btnLoading: false,
 | 
					            btnLoading: false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@
 | 
				
			|||||||
                        </el-radio>
 | 
					                        </el-radio>
 | 
				
			||||||
                    </template>
 | 
					                    </template>
 | 
				
			||||||
                </el-table-column>
 | 
					                </el-table-column>
 | 
				
			||||||
                <el-table-column prop="name" label="名称" :min-width="50"> </el-table-column>
 | 
					                <el-table-column prop="name" label="名称" :min-width="70"> </el-table-column>
 | 
				
			||||||
                <el-table-column prop="description" label="描述" :min-width="100" show-overflow-tooltip></el-table-column>
 | 
					                <el-table-column prop="description" label="描述" :min-width="100" show-overflow-tooltip></el-table-column>
 | 
				
			||||||
                <el-table-column prop="name" label="类型" :min-width="50">
 | 
					                <el-table-column prop="name" label="类型" :min-width="50">
 | 
				
			||||||
                    <template #default="scope">
 | 
					                    <template #default="scope">
 | 
				
			||||||
@@ -64,6 +64,19 @@
 | 
				
			|||||||
            </el-table>
 | 
					            </el-table>
 | 
				
			||||||
        </el-dialog>
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog title="脚本参数" v-model="scriptParamsDialog.visible" width="400px">
 | 
				
			||||||
 | 
					            <el-form ref="paramsForm" :model="scriptParamsDialog.params" label-width="70px" size="mini">
 | 
				
			||||||
 | 
					                <el-form-item v-for="item in scriptParamsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required>
 | 
				
			||||||
 | 
					                    <el-input v-model="scriptParamsDialog.params[item.model]" :placeholder="item.placeholder" autocomplete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					            </el-form>
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <span class="dialog-footer">
 | 
				
			||||||
 | 
					                    <el-button type="primary" @click="hasParamsRun(currentData)" size="mini">确 定</el-button>
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-dialog title="执行结果" v-model="resultDialog.visible" width="40%">
 | 
					        <el-dialog title="执行结果" v-model="resultDialog.visible" width="40%">
 | 
				
			||||||
            <div style="white-space: pre-line; padding: 10px; color: #000000">
 | 
					            <div style="white-space: pre-line; padding: 10px; color: #000000">
 | 
				
			||||||
                {{ resultDialog.result }}
 | 
					                {{ resultDialog.result }}
 | 
				
			||||||
@@ -94,7 +107,7 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { toRefs, reactive, watch, defineComponent } from 'vue';
 | 
					import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import SshTerminal from './SshTerminal.vue';
 | 
					import SshTerminal from './SshTerminal.vue';
 | 
				
			||||||
import { machineApi } from './api';
 | 
					import { machineApi } from './api';
 | 
				
			||||||
@@ -113,6 +126,7 @@ export default defineComponent({
 | 
				
			|||||||
        title: { type: String },
 | 
					        title: { type: String },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup(props: any, context) {
 | 
					    setup(props: any, context) {
 | 
				
			||||||
 | 
					        const paramsForm: any = ref(null);
 | 
				
			||||||
        const state = reactive({
 | 
					        const state = reactive({
 | 
				
			||||||
            visible: false,
 | 
					            visible: false,
 | 
				
			||||||
            type: 0,
 | 
					            type: 0,
 | 
				
			||||||
@@ -125,6 +139,11 @@ export default defineComponent({
 | 
				
			|||||||
                machineId: 9999999,
 | 
					                machineId: 9999999,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            scriptTable: [],
 | 
					            scriptTable: [],
 | 
				
			||||||
 | 
					            scriptParamsDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                params: {},
 | 
				
			||||||
 | 
					                paramsFormItem: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            resultDialog: {
 | 
					            resultDialog: {
 | 
				
			||||||
                visible: false,
 | 
					                visible: false,
 | 
				
			||||||
                result: '',
 | 
					                result: '',
 | 
				
			||||||
@@ -152,13 +171,43 @@ export default defineComponent({
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const runScript = async (script: any) => {
 | 
					        const runScript = async (script: any) => {
 | 
				
			||||||
 | 
					            // 如果存在参数,则弹窗输入参数后执行
 | 
				
			||||||
 | 
					            if (script.params) {
 | 
				
			||||||
 | 
					                state.scriptParamsDialog.paramsFormItem = JSON.parse(script.params);
 | 
				
			||||||
 | 
					                state.scriptParamsDialog.visible = true;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            run(script);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 有参数的脚本执行函数
 | 
				
			||||||
 | 
					        const hasParamsRun = async (script: any) => {
 | 
				
			||||||
 | 
					            // 如果脚本参数弹窗显示,则校验参数表单数据通过后执行
 | 
				
			||||||
 | 
					            if (state.scriptParamsDialog.visible) {
 | 
				
			||||||
 | 
					                paramsForm.value.validate((valid: any) => {
 | 
				
			||||||
 | 
					                    if (valid) {
 | 
				
			||||||
 | 
					                        run(script);
 | 
				
			||||||
 | 
					                        state.scriptParamsDialog.params = {};
 | 
				
			||||||
 | 
					                        state.scriptParamsDialog.visible = false;
 | 
				
			||||||
 | 
					                        paramsForm.value.resetFields();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const run = async (script: any) => {
 | 
				
			||||||
            const noResult = script.type == enums.scriptTypeEnum['NO_RESULT'].value;
 | 
					            const noResult = script.type == enums.scriptTypeEnum['NO_RESULT'].value;
 | 
				
			||||||
            // 如果脚本类型为有结果类型,则显示结果信息
 | 
					            // 如果脚本类型为有结果类型,则显示结果信息
 | 
				
			||||||
            if (script.type == enums.scriptTypeEnum['RESULT'].value || noResult) {
 | 
					            if (script.type == enums.scriptTypeEnum['RESULT'].value || noResult) {
 | 
				
			||||||
                const res = await machineApi.runScript.request({
 | 
					                const res = await machineApi.runScript.request({
 | 
				
			||||||
                    machineId: props.machineId,
 | 
					                    machineId: props.machineId,
 | 
				
			||||||
                    scriptId: script.id,
 | 
					                    scriptId: script.id,
 | 
				
			||||||
 | 
					                    params: state.scriptParamsDialog.params,
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (noResult) {
 | 
					                if (noResult) {
 | 
				
			||||||
                    ElMessage.success('执行完成');
 | 
					                    ElMessage.success('执行完成');
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
@@ -241,9 +290,11 @@ export default defineComponent({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            paramsForm,
 | 
				
			||||||
            enums,
 | 
					            enums,
 | 
				
			||||||
            getScripts,
 | 
					            getScripts,
 | 
				
			||||||
            runScript,
 | 
					            runScript,
 | 
				
			||||||
 | 
					            hasParamsRun,
 | 
				
			||||||
            closeTermnial,
 | 
					            closeTermnial,
 | 
				
			||||||
            choose,
 | 
					            choose,
 | 
				
			||||||
            editScript,
 | 
					            editScript,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,9 +6,9 @@ export const machineApi = {
 | 
				
			|||||||
    info: Api.create("/machines/{id}/sysinfo", 'get'),
 | 
					    info: Api.create("/machines/{id}/sysinfo", 'get'),
 | 
				
			||||||
    top: Api.create("/machines/{id}/top", 'get'),
 | 
					    top: Api.create("/machines/{id}/top", 'get'),
 | 
				
			||||||
    // 保存按钮
 | 
					    // 保存按钮
 | 
				
			||||||
    save: Api.create("/machines", 'post'),
 | 
					    saveMachine: Api.create("/machines", 'post'),
 | 
				
			||||||
    // 删除机器
 | 
					    // 删除机器
 | 
				
			||||||
    del: Api.create("/devops/machines/{id}", 'delete'),
 | 
					    del: Api.create("/machines/delete/{id}", 'delete'),
 | 
				
			||||||
    scripts: Api.create("/machines/{machineId}/scripts", 'get'),
 | 
					    scripts: Api.create("/machines/{machineId}/scripts", 'get'),
 | 
				
			||||||
    runScript: Api.create("/machines/{machineId}/scripts/{scriptId}/run", 'get'),
 | 
					    runScript: Api.create("/machines/{machineId}/scripts/{scriptId}/run", 'get'),
 | 
				
			||||||
    saveScript: Api.create("/machines/{machineId}/scripts", 'post'),
 | 
					    saveScript: Api.create("/machines/{machineId}/scripts", 'post'),
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										413
									
								
								mayfly_go_web/src/views/ops/project/ProjectList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										413
									
								
								mayfly_go_web/src/views/ops/project/ProjectList.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,413 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div class="project-list">
 | 
				
			||||||
 | 
					        <div class="toolbar">
 | 
				
			||||||
 | 
					            <el-button @click="showAddProjectDialog" v-auth="permissions.saveProject" type="primary" icon="el-icon-plus" size="mini">添加</el-button>
 | 
				
			||||||
 | 
					            <el-button
 | 
				
			||||||
 | 
					                @click="showAddProjectDialog(chooseData)"
 | 
				
			||||||
 | 
					                v-auth="permissions.saveProject"
 | 
				
			||||||
 | 
					                :disabled="chooseId == null"
 | 
				
			||||||
 | 
					                type="primary"
 | 
				
			||||||
 | 
					                icon="el-icon-edit"
 | 
				
			||||||
 | 
					                size="mini"
 | 
				
			||||||
 | 
					                >编辑</el-button
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					            <el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="el-icon-setting" size="mini"
 | 
				
			||||||
 | 
					                >成员管理</el-button
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="el-icon-setting" size="mini">环境管理</el-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-button v-auth="'role:del'" :disabled="chooseId == null" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div style="float: right">
 | 
				
			||||||
 | 
					                <el-input
 | 
				
			||||||
 | 
					                    class="mr2"
 | 
				
			||||||
 | 
					                    placeholder="请输入项目名!"
 | 
				
			||||||
 | 
					                    size="small"
 | 
				
			||||||
 | 
					                    style="width: 140px"
 | 
				
			||||||
 | 
					                    v-model="query.name"
 | 
				
			||||||
 | 
					                    @clear="search"
 | 
				
			||||||
 | 
					                    clearable
 | 
				
			||||||
 | 
					                ></el-input>
 | 
				
			||||||
 | 
					                <el-button @click="search" type="success" icon="el-icon-search" size="mini"></el-button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <el-table :data="projects" @current-change="choose" border ref="table" style="width: 100%">
 | 
				
			||||||
 | 
					            <el-table-column label="选择" width="50px">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-radio v-model="chooseId" :label="scope.row.id">
 | 
				
			||||||
 | 
					                        <i></i>
 | 
				
			||||||
 | 
					                    </el-radio>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="name" label="项目名"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="remark" label="描述" min-width="180px" show-overflow-tooltip></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="createTime" label="创建时间">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="creator" label="创建者"> </el-table-column>
 | 
				
			||||||
 | 
					            <!-- <el-table-column label="查看更多" min-width="80px">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-link @click.prevent="showMembers(scope.row)" type="success">成员</el-link>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <el-link class="ml5" @click.prevent="showEnv(scope.row)" type="info">环境</el-link>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column> -->
 | 
				
			||||||
 | 
					        </el-table>
 | 
				
			||||||
 | 
					        <el-pagination
 | 
				
			||||||
 | 
					            @current-change="handlePageChange"
 | 
				
			||||||
 | 
					            style="text-align: center"
 | 
				
			||||||
 | 
					            background
 | 
				
			||||||
 | 
					            layout="prev, pager, next, total, jumper"
 | 
				
			||||||
 | 
					            :total="total"
 | 
				
			||||||
 | 
					            v-model:current-page="query.pageNum"
 | 
				
			||||||
 | 
					            :page-size="query.pageSize"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible">
 | 
				
			||||||
 | 
					            <el-form :model="addProjectDialog.form" size="small" label-width="70px">
 | 
				
			||||||
 | 
					                <el-form-item label="项目名:" required>
 | 
				
			||||||
 | 
					                    <el-input :disabled="addProjectDialog.form.id" v-model="addProjectDialog.form.name" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item label="描述:">
 | 
				
			||||||
 | 
					                    <el-input v-model="addProjectDialog.form.remark" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					            </el-form>
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <div class="dialog-footer">
 | 
				
			||||||
 | 
					                    <el-button @click="addProject" type="primary" size="small">确 定</el-button>
 | 
				
			||||||
 | 
					                    <el-button @click="cancelAddProject()" size="small">取 消</el-button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="500px" :title="showEnvDialog.title" v-model="showEnvDialog.visible">
 | 
				
			||||||
 | 
					            <div class="toolbar">
 | 
				
			||||||
 | 
					                <el-button @click="showAddEnvDialog" v-auth="permissions.saveMember" type="primary" icon="el-icon-plus" size="mini">添加</el-button>
 | 
				
			||||||
 | 
					                <!-- <el-button v-auth="'role:update'" :disabled="chooseId == null" type="danger" icon="el-icon-delete" size="mini">删除</el-button> -->
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <el-table border :data="showEnvDialog.envs" size="small">
 | 
				
			||||||
 | 
					                <el-table-column property="name" label="环境名" width="125"></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="remark" label="描述" width="125"></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="createTime" label="创建时间">
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					            </el-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-dialog width="400px" title="添加环境" :before-close="cancelAddEnv" v-model="showEnvDialog.addVisible">
 | 
				
			||||||
 | 
					                <el-form :model="showEnvDialog.envForm" size="small" label-width="70px">
 | 
				
			||||||
 | 
					                    <el-form-item label="环境名:" required>
 | 
				
			||||||
 | 
					                        <el-input v-model="showEnvDialog.envForm.name" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                    </el-form-item>
 | 
				
			||||||
 | 
					                    <el-form-item label="描述:">
 | 
				
			||||||
 | 
					                        <el-input v-model="showEnvDialog.envForm.remark" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                    </el-form-item>
 | 
				
			||||||
 | 
					                </el-form>
 | 
				
			||||||
 | 
					                <template #footer>
 | 
				
			||||||
 | 
					                    <div class="dialog-footer">
 | 
				
			||||||
 | 
					                        <el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading" size="small">确 定</el-button>
 | 
				
			||||||
 | 
					                        <el-button @click="cancelAddEnv()" size="small">取 消</el-button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-dialog>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="500px" :title="showMemDialog.title" v-model="showMemDialog.visible">
 | 
				
			||||||
 | 
					            <div class="toolbar">
 | 
				
			||||||
 | 
					                <el-button v-auth="permissions.saveMember" @click="showAddMemberDialog()" type="primary" icon="el-icon-plus" size="mini"
 | 
				
			||||||
 | 
					                    >添加</el-button
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                <el-button
 | 
				
			||||||
 | 
					                    v-auth="permissions.delMember"
 | 
				
			||||||
 | 
					                    @click="deleteMember"
 | 
				
			||||||
 | 
					                    :disabled="showMemDialog.chooseId == null"
 | 
				
			||||||
 | 
					                    type="danger"
 | 
				
			||||||
 | 
					                    icon="el-icon-delete"
 | 
				
			||||||
 | 
					                    size="mini"
 | 
				
			||||||
 | 
					                    >移除</el-button
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <el-table @current-change="chooseMember" border :data="showMemDialog.members.list" size="small">
 | 
				
			||||||
 | 
					                <el-table-column label="选择" width="50px">
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        <el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
 | 
				
			||||||
 | 
					                            <i></i>
 | 
				
			||||||
 | 
					                        </el-radio>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="username" label="账号" width="125"></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="createTime" label="加入时间">
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="creator" label="分配者" width="125"></el-table-column>
 | 
				
			||||||
 | 
					            </el-table>
 | 
				
			||||||
 | 
					            <el-pagination
 | 
				
			||||||
 | 
					                @current-change="setMemebers"
 | 
				
			||||||
 | 
					                style="text-align: center"
 | 
				
			||||||
 | 
					                background
 | 
				
			||||||
 | 
					                layout="prev, pager, next, total, jumper"
 | 
				
			||||||
 | 
					                :total="showMemDialog.members.total"
 | 
				
			||||||
 | 
					                v-model:current-page="showMemDialog.query.pageNum"
 | 
				
			||||||
 | 
					                :page-size="showMemDialog.query.pageSize"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
 | 
				
			||||||
 | 
					                <el-form :model="showMemDialog.memForm" size="small" label-width="70px">
 | 
				
			||||||
 | 
					                    <el-form-item label="账号:">
 | 
				
			||||||
 | 
					                        <el-select style="width: 100%" remote :remote-method="getAccount" v-model="showMemDialog.memForm.accountId" filterable placeholder="请选择">
 | 
				
			||||||
 | 
					                            <el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                        </el-select>
 | 
				
			||||||
 | 
					                    </el-form-item>
 | 
				
			||||||
 | 
					                    <!-- <el-form-item label="描述:">
 | 
				
			||||||
 | 
					                        <el-input v-model="showEnvDialog.envForm.remark" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                    </el-form-item> -->
 | 
				
			||||||
 | 
					                </el-form>
 | 
				
			||||||
 | 
					                <template #footer>
 | 
				
			||||||
 | 
					                    <div class="dialog-footer">
 | 
				
			||||||
 | 
					                        <el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading" size="small"
 | 
				
			||||||
 | 
					                            >确 定</el-button
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                        <el-button @click="cancelAddMember()" size="small">取 消</el-button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-dialog>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
				
			||||||
 | 
					import { projectApi } from './api';
 | 
				
			||||||
 | 
					import { accountApi } from '../../system/api';
 | 
				
			||||||
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
 | 
					import { notEmpty, notNull } from '@/common/assert';
 | 
				
			||||||
 | 
					import { auth } from '../../../common/utils/authFunction';
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'ProjectList',
 | 
				
			||||||
 | 
					    components: {},
 | 
				
			||||||
 | 
					    setup() {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            permissions: {
 | 
				
			||||||
 | 
					                saveProject: 'project:save',
 | 
				
			||||||
 | 
					                saveMember: 'project:member:add',
 | 
				
			||||||
 | 
					                delMember: 'project:member:del',
 | 
				
			||||||
 | 
					                saveEnv: 'project:env:add',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            query: {
 | 
				
			||||||
 | 
					                pageNum: 1,
 | 
				
			||||||
 | 
					                pageSize: 10,
 | 
				
			||||||
 | 
					                name: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            total: 0,
 | 
				
			||||||
 | 
					            projects: [],
 | 
				
			||||||
 | 
					            btnLoading: false,
 | 
				
			||||||
 | 
					            chooseId: null as any,
 | 
				
			||||||
 | 
					            chooseData: null as any,
 | 
				
			||||||
 | 
					            addProjectDialog: {
 | 
				
			||||||
 | 
					                title: '新增项目',
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                form: { name: '', remark: '' },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            showEnvDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                envs: [],
 | 
				
			||||||
 | 
					                title: '',
 | 
				
			||||||
 | 
					                addVisible: false,
 | 
				
			||||||
 | 
					                envForm: {
 | 
				
			||||||
 | 
					                    name: '',
 | 
				
			||||||
 | 
					                    remark: '',
 | 
				
			||||||
 | 
					                    projectId: 0,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            showMemDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                chooseId: null,
 | 
				
			||||||
 | 
					                chooseData: null,
 | 
				
			||||||
 | 
					                query: {
 | 
				
			||||||
 | 
					                    pageSize: 8,
 | 
				
			||||||
 | 
					                    pageNum: 1,
 | 
				
			||||||
 | 
					                    projectId: null,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                members: {
 | 
				
			||||||
 | 
					                    list: [],
 | 
				
			||||||
 | 
					                    total: null,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                title: '',
 | 
				
			||||||
 | 
					                addVisible: false,
 | 
				
			||||||
 | 
					                memForm: {},
 | 
				
			||||||
 | 
					                accounts: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onMounted(() => {
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const search = async () => {
 | 
				
			||||||
 | 
					            let res = await projectApi.projects.request(state.query);
 | 
				
			||||||
 | 
					            state.projects = res.list;
 | 
				
			||||||
 | 
					            state.total = res.total;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const handlePageChange = (curPage: number) => {
 | 
				
			||||||
 | 
					            state.query.pageNum = curPage;
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showAddProjectDialog = (data: any) => {
 | 
				
			||||||
 | 
					            if (data) {
 | 
				
			||||||
 | 
					                state.addProjectDialog.form = data;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                state.addProjectDialog.form = {} as any;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.addProjectDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cancelAddProject = () => {
 | 
				
			||||||
 | 
					            state.addProjectDialog.visible = false;
 | 
				
			||||||
 | 
					            state.addProjectDialog.form = {} as any;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const addProject = async () => {
 | 
				
			||||||
 | 
					            const form = state.addProjectDialog.form as any;
 | 
				
			||||||
 | 
					            notEmpty(form.name, '项目名不能为空');
 | 
				
			||||||
 | 
					            notEmpty(form.remark, '项目描述不能为空');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await projectApi.saveProject.request(form);
 | 
				
			||||||
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					            cancelAddProject();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const choose = (item: any) => {
 | 
				
			||||||
 | 
					            if (!item) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.chooseId = item.id;
 | 
				
			||||||
 | 
					            state.chooseData = item;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showMembers = async (project: any) => {
 | 
				
			||||||
 | 
					            state.showMemDialog.query.projectId = project.id;
 | 
				
			||||||
 | 
					            await setMemebers();
 | 
				
			||||||
 | 
					            state.showMemDialog.title = `${project.name}的成员信息`;
 | 
				
			||||||
 | 
					            state.showMemDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 选中成员
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        const chooseMember = (item: any) => {
 | 
				
			||||||
 | 
					            if (!item) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.showMemDialog.chooseData = item;
 | 
				
			||||||
 | 
					            state.showMemDialog.chooseId = item.id;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const deleteMember = async () => {
 | 
				
			||||||
 | 
					            notNull(state.showMemDialog.chooseData, '请选选择成员');
 | 
				
			||||||
 | 
					            await projectApi.deleteProjectMem.request(state.showMemDialog.chooseData);
 | 
				
			||||||
 | 
					            ElMessage.success('移除成功');
 | 
				
			||||||
 | 
					            // 重新赋值成员列表
 | 
				
			||||||
 | 
					            setMemebers();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 设置成员列表信息
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        const setMemebers = async () => {
 | 
				
			||||||
 | 
					            const res = await projectApi.projectMems.request(state.showMemDialog.query);
 | 
				
			||||||
 | 
					            state.showMemDialog.members.list = res.list;
 | 
				
			||||||
 | 
					            state.showMemDialog.members.total = res.total;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showEnv = async (project: any) => {
 | 
				
			||||||
 | 
					            state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: project.id });
 | 
				
			||||||
 | 
					            state.showEnvDialog.title = `${project.name}的环境信息`;
 | 
				
			||||||
 | 
					            state.showEnvDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showAddMemberDialog = () => {
 | 
				
			||||||
 | 
					            state.showMemDialog.addVisible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const addMember = async () => {
 | 
				
			||||||
 | 
					            const memForm = state.showMemDialog.memForm as any;
 | 
				
			||||||
 | 
					            memForm.projectId = state.chooseData.id;
 | 
				
			||||||
 | 
					            notEmpty(memForm.accountId, '请先选择账号');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await projectApi.saveProjectMem.request(memForm);
 | 
				
			||||||
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					            setMemebers();
 | 
				
			||||||
 | 
					            cancelAddMember();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cancelAddMember = () => {
 | 
				
			||||||
 | 
					            state.showMemDialog.memForm = {};
 | 
				
			||||||
 | 
					            state.showMemDialog.addVisible = false;
 | 
				
			||||||
 | 
					            state.showMemDialog.chooseData = null;
 | 
				
			||||||
 | 
					            state.showMemDialog.chooseId = null;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const getAccount = (username: any) => {
 | 
				
			||||||
 | 
					            accountApi.list.request({ username }).then((res) => {
 | 
				
			||||||
 | 
					                state.showMemDialog.accounts = res.list;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showAddEnvDialog = () => {
 | 
				
			||||||
 | 
					            state.showEnvDialog.addVisible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const addEnv = async () => {
 | 
				
			||||||
 | 
					            const envForm = state.showEnvDialog.envForm;
 | 
				
			||||||
 | 
					            envForm.projectId = state.chooseData.id;
 | 
				
			||||||
 | 
					            await projectApi.saveProjectEnv.request(envForm);
 | 
				
			||||||
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					            state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: envForm.projectId });
 | 
				
			||||||
 | 
					            cancelAddEnv();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cancelAddEnv = () => {
 | 
				
			||||||
 | 
					            state.showEnvDialog.envForm = {} as any;
 | 
				
			||||||
 | 
					            state.showEnvDialog.addVisible = false;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const roleEditChange = (data: any) => {
 | 
				
			||||||
 | 
					            ElMessage.success('修改成功!');
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            search,
 | 
				
			||||||
 | 
					            handlePageChange,
 | 
				
			||||||
 | 
					            choose,
 | 
				
			||||||
 | 
					            showAddProjectDialog,
 | 
				
			||||||
 | 
					            addProject,
 | 
				
			||||||
 | 
					            cancelAddProject,
 | 
				
			||||||
 | 
					            showMembers,
 | 
				
			||||||
 | 
					            setMemebers,
 | 
				
			||||||
 | 
					            showEnv,
 | 
				
			||||||
 | 
					            showAddMemberDialog,
 | 
				
			||||||
 | 
					            addMember,
 | 
				
			||||||
 | 
					            chooseMember,
 | 
				
			||||||
 | 
					            deleteMember,
 | 
				
			||||||
 | 
					            cancelAddMember,
 | 
				
			||||||
 | 
					            showAddEnvDialog,
 | 
				
			||||||
 | 
					            addEnv,
 | 
				
			||||||
 | 
					            cancelAddEnv,
 | 
				
			||||||
 | 
					            getAccount,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										15
									
								
								mayfly_go_web/src/views/ops/project/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								mayfly_go_web/src/views/ops/project/api.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					import Api from '@/common/Api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const projectApi = {
 | 
				
			||||||
 | 
					    // 获取账号可访问的项目列表
 | 
				
			||||||
 | 
					    accountProjects: Api.create("/accounts/projects", 'get'),
 | 
				
			||||||
 | 
					    projects: Api.create("/projects", 'get'),
 | 
				
			||||||
 | 
					    saveProject: Api.create("/projects", 'post'),
 | 
				
			||||||
 | 
					    // 获取项目下的环境信息
 | 
				
			||||||
 | 
					    projectEnvs:  Api.create("/projects/{projectId}/envs", 'get'),
 | 
				
			||||||
 | 
					    saveProjectEnv:  Api.create("/projects/{projectId}/envs", 'post'),
 | 
				
			||||||
 | 
					    // 获取项目下的成员信息
 | 
				
			||||||
 | 
					    projectMems:  Api.create("/projects/{projectId}/members", 'get'),
 | 
				
			||||||
 | 
					    saveProjectMem:  Api.create("/projects/{projectId}/members", 'post'),
 | 
				
			||||||
 | 
					    deleteProjectMem:  Api.create("/projects/{projectId}/members/{accountId}", 'delete'),
 | 
				
			||||||
 | 
					}   
 | 
				
			||||||
							
								
								
									
										281
									
								
								mayfly_go_web/src/views/ops/redis/DataOperation.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								mayfly_go_web/src/views/ops/redis/DataOperation.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,281 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <div class="toolbar">
 | 
				
			||||||
 | 
					            <div style="float: left">
 | 
				
			||||||
 | 
					                <el-row type="flex" justify="space-between">
 | 
				
			||||||
 | 
					                    <el-col :span="24">
 | 
				
			||||||
 | 
					                        <project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearRedis">
 | 
				
			||||||
 | 
					                            <template #default>
 | 
				
			||||||
 | 
					                                <el-form-item label="redis" label-width="40px">
 | 
				
			||||||
 | 
					                                    <el-select v-model="scanParam.id" placeholder="请选择redis" @change="changeRedis" @clear="clearRedis" clearable>
 | 
				
			||||||
 | 
					                                        <el-option v-for="item in redisList" :key="item.id" :label="item.host" :value="item.id">
 | 
				
			||||||
 | 
					                                            <span style="float: left">{{ item.host }}</span>
 | 
				
			||||||
 | 
					                                            <span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{
 | 
				
			||||||
 | 
					                                                `库: [${item.db}]`
 | 
				
			||||||
 | 
					                                            }}</span>
 | 
				
			||||||
 | 
					                                        </el-option>
 | 
				
			||||||
 | 
					                                    </el-select>
 | 
				
			||||||
 | 
					                                </el-form-item>
 | 
				
			||||||
 | 
					                                <el-form-item label="key" label-width="40px">
 | 
				
			||||||
 | 
					                                    <el-input
 | 
				
			||||||
 | 
					                                        placeholder="支持*模糊key"
 | 
				
			||||||
 | 
					                                        style="width: 180px"
 | 
				
			||||||
 | 
					                                        v-model="scanParam.match"
 | 
				
			||||||
 | 
					                                        size="mini"
 | 
				
			||||||
 | 
					                                        @clear="clear()"
 | 
				
			||||||
 | 
					                                        clearable
 | 
				
			||||||
 | 
					                                    ></el-input>
 | 
				
			||||||
 | 
					                                </el-form-item>
 | 
				
			||||||
 | 
					                                <el-form-item label-width="40px">
 | 
				
			||||||
 | 
					                                    <el-input placeholder="count" style="width: 62px" v-model="scanParam.count" size="mini"></el-input>
 | 
				
			||||||
 | 
					                                </el-form-item>
 | 
				
			||||||
 | 
					                                <el-button @click="searchKey()" type="success" icon="el-icon-search" size="mini" plain></el-button>
 | 
				
			||||||
 | 
					                                <el-button @click="scan()" icon="el-icon-bottom" size="mini" plain>scan</el-button>
 | 
				
			||||||
 | 
					                                <el-button type="primary" icon="el-icon-plus" size="mini" @click="save(false)" plain></el-button>
 | 
				
			||||||
 | 
					                            </template>
 | 
				
			||||||
 | 
					                        </project-env-select>
 | 
				
			||||||
 | 
					                    </el-col>
 | 
				
			||||||
 | 
					                </el-row>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div style="float: right">
 | 
				
			||||||
 | 
					                <!-- <el-button @click="scan()" icon="el-icon-refresh" size="small" plain>刷新</el-button> -->
 | 
				
			||||||
 | 
					                <span>keys: {{ dbsize }}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <el-table v-loading="loading" :data="keys" border stripe :highlight-current-row="true" style="cursor: pointer">
 | 
				
			||||||
 | 
					            <el-table-column show-overflow-tooltip prop="key" label="key"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="type" label="type" width="80"> </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="ttl" label="ttl(过期时间)" width="120">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    {{ ttlConveter(scope.row.ttl) }}
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column label="操作">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-button @click="getValue(scope.row)" type="success" icon="el-icon-search" size="mini" plain>查看</el-button>
 | 
				
			||||||
 | 
					                    <el-button @click="del(scope.row.key)" type="danger" size="mini" icon="el-icon-delete" plain>删除</el-button>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					        </el-table>
 | 
				
			||||||
 | 
					        <div style="text-align: center; margin-top: 10px"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <value-dialog v-model:visible="valueDialog.visible" :keyValue="valueDialog.value" />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import ValueDialog from './ValueDialog.vue';
 | 
				
			||||||
 | 
					import { redisApi } from './api';
 | 
				
			||||||
 | 
					import { toRefs, reactive, defineComponent } from 'vue';
 | 
				
			||||||
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
 | 
					import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
 | 
				
			||||||
 | 
					import { isTrue, notNull } from '@/common/assert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'DataOperation',
 | 
				
			||||||
 | 
					    components: {
 | 
				
			||||||
 | 
					        ValueDialog,
 | 
				
			||||||
 | 
					        ProjectEnvSelect,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup() {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            loading: false,
 | 
				
			||||||
 | 
					            cluster: 0,
 | 
				
			||||||
 | 
					            redisList: [],
 | 
				
			||||||
 | 
					            query: {
 | 
				
			||||||
 | 
					                envId: 0,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // redis: {
 | 
				
			||||||
 | 
					            //     id: 0,
 | 
				
			||||||
 | 
					            //     info: '',
 | 
				
			||||||
 | 
					            //     conf: '',
 | 
				
			||||||
 | 
					            // },
 | 
				
			||||||
 | 
					            scanParam: {
 | 
				
			||||||
 | 
					                id: null,
 | 
				
			||||||
 | 
					                cluster: 0,
 | 
				
			||||||
 | 
					                match: null,
 | 
				
			||||||
 | 
					                count: 10,
 | 
				
			||||||
 | 
					                cursor: 0,
 | 
				
			||||||
 | 
					                prevCursor: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            valueDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                value: {},
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            keys: [],
 | 
				
			||||||
 | 
					            dbsize: 0,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const searchRedis = async () => {
 | 
				
			||||||
 | 
					            notNull(state.query.envId, '请先选择项目环境');
 | 
				
			||||||
 | 
					            const res = await redisApi.redisList.request(state.query);
 | 
				
			||||||
 | 
					            state.redisList = res.list;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeProjectEnv = (projectId: any, envId: any) => {
 | 
				
			||||||
 | 
					            clearRedis();
 | 
				
			||||||
 | 
					            if (envId != null) {
 | 
				
			||||||
 | 
					                state.query.envId = envId;
 | 
				
			||||||
 | 
					                searchRedis();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeRedis = (redisId: any) => {
 | 
				
			||||||
 | 
					            resetScanParam();
 | 
				
			||||||
 | 
					            state.keys = [];
 | 
				
			||||||
 | 
					            state.dbsize = 0;
 | 
				
			||||||
 | 
					            searchKey();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const scan = () => {
 | 
				
			||||||
 | 
					            isTrue(state.scanParam.id != null, '请先选择redis');
 | 
				
			||||||
 | 
					            isTrue(state.scanParam.count < 2001, 'count不能超过2000');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            state.loading = true;
 | 
				
			||||||
 | 
					            state.scanParam.cluster = state.cluster == 0 ? 0 : 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            redisApi.scan.request(state.scanParam).then((res) => {
 | 
				
			||||||
 | 
					                state.keys = res.keys;
 | 
				
			||||||
 | 
					                state.dbsize = res.dbSize;
 | 
				
			||||||
 | 
					                state.scanParam.cursor = res.cursor;
 | 
				
			||||||
 | 
					                state.loading = false;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const searchKey = () => {
 | 
				
			||||||
 | 
					            state.scanParam.cursor = 0;
 | 
				
			||||||
 | 
					            scan();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const clearRedis = () => {
 | 
				
			||||||
 | 
					            state.redisList = [];
 | 
				
			||||||
 | 
					            state.scanParam.id = null;
 | 
				
			||||||
 | 
					            resetScanParam();
 | 
				
			||||||
 | 
					            state.keys = [];
 | 
				
			||||||
 | 
					            state.dbsize = 0;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const clear = () => {
 | 
				
			||||||
 | 
					            resetScanParam();
 | 
				
			||||||
 | 
					            if (state.scanParam.id) {
 | 
				
			||||||
 | 
					                scan();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const resetScanParam = () => {
 | 
				
			||||||
 | 
					            state.scanParam.match = null;
 | 
				
			||||||
 | 
					            state.scanParam.cursor = 0;
 | 
				
			||||||
 | 
					            state.scanParam.count = 10;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const getValue = async (row: any) => {
 | 
				
			||||||
 | 
					            let api: any;
 | 
				
			||||||
 | 
					            switch (row.type) {
 | 
				
			||||||
 | 
					                case 'string':
 | 
				
			||||||
 | 
					                    api = redisApi.getStringValue;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'hash':
 | 
				
			||||||
 | 
					                    api = redisApi.getHashValue;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'set':
 | 
				
			||||||
 | 
					                    api = redisApi.getSetValue;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    api = redisApi.getStringValue;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const id = state.cluster == 0 ? state.scanParam.id : state.cluster;
 | 
				
			||||||
 | 
					            const res = await api.request({
 | 
				
			||||||
 | 
					                cluster: state.cluster,
 | 
				
			||||||
 | 
					                key: row.key,
 | 
				
			||||||
 | 
					                id,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let timed = row.ttl == 18446744073709552000 ? 0 : row.ttl;
 | 
				
			||||||
 | 
					            state.valueDialog.value = { id: state.scanParam.id, key: row.key, value: res, timed: timed, type: row.type };
 | 
				
			||||||
 | 
					            state.valueDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // closeValueDialog() {
 | 
				
			||||||
 | 
					        //   this.valueDialog.visible = false
 | 
				
			||||||
 | 
					        //   this.valueDialog.value = {}
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const update = (key: string) => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const del = (key: string) => {
 | 
				
			||||||
 | 
					            ElMessageBox.confirm(`此操作将删除对应的key , 是否继续?`, '提示', {
 | 
				
			||||||
 | 
					                confirmButtonText: '确定',
 | 
				
			||||||
 | 
					                cancelButtonText: '取消',
 | 
				
			||||||
 | 
					                type: 'warning',
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					                .then(() => {
 | 
				
			||||||
 | 
					                    let id = state.cluster == 0 ? state.scanParam.id : state.cluster;
 | 
				
			||||||
 | 
					                    redisApi.delKey
 | 
				
			||||||
 | 
					                        .request({
 | 
				
			||||||
 | 
					                            cluster: state.cluster,
 | 
				
			||||||
 | 
					                            key,
 | 
				
			||||||
 | 
					                            id,
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        .then((res) => {
 | 
				
			||||||
 | 
					                            ElMessage.success('删除成功!');
 | 
				
			||||||
 | 
					                            scan();
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .catch((err) => {});
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const ttlConveter = (ttl: any) => {
 | 
				
			||||||
 | 
					            if (ttl == 18446744073709552000) {
 | 
				
			||||||
 | 
					                return '永久';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!ttl) {
 | 
				
			||||||
 | 
					                ttl = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let second = parseInt(ttl); // 秒
 | 
				
			||||||
 | 
					            let min = 0; // 分
 | 
				
			||||||
 | 
					            let hour = 0; // 小时
 | 
				
			||||||
 | 
					            let day = 0;
 | 
				
			||||||
 | 
					            if (second > 60) {
 | 
				
			||||||
 | 
					                min = parseInt(second / 60 + '');
 | 
				
			||||||
 | 
					                second = second % 60;
 | 
				
			||||||
 | 
					                if (min > 60) {
 | 
				
			||||||
 | 
					                    hour = parseInt(min / 60 + '');
 | 
				
			||||||
 | 
					                    min = min % 60;
 | 
				
			||||||
 | 
					                    if (hour > 24) {
 | 
				
			||||||
 | 
					                        day = parseInt(hour / 24 + '');
 | 
				
			||||||
 | 
					                        hour = hour % 24;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let result = '' + second + 's';
 | 
				
			||||||
 | 
					            if (min > 0) {
 | 
				
			||||||
 | 
					                result = '' + min + 'm:' + result;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (hour > 0) {
 | 
				
			||||||
 | 
					                result = '' + hour + 'h:' + result;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (day > 0) {
 | 
				
			||||||
 | 
					                result = '' + day + 'd:' + result;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            changeProjectEnv,
 | 
				
			||||||
 | 
					            changeRedis,
 | 
				
			||||||
 | 
					            clearRedis,
 | 
				
			||||||
 | 
					            searchKey,
 | 
				
			||||||
 | 
					            scan,
 | 
				
			||||||
 | 
					            clear,
 | 
				
			||||||
 | 
					            getValue,
 | 
				
			||||||
 | 
					            del,
 | 
				
			||||||
 | 
					            ttlConveter,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										200
									
								
								mayfly_go_web/src/views/ops/redis/Info.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								mayfly_go_web/src/views/ops/redis/Info.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <el-dialog :title="title" v-model="visible" :show-close="true" width="35%" @close="close()">
 | 
				
			||||||
 | 
					            <el-collapse>
 | 
				
			||||||
 | 
					                <el-collapse-item title="Server(Redis服务器的一般信息)" name="server">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">redis_version(版本):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.redis_version }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">tcp_port(端口):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.tcp_port }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">redis_mode(模式):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.redis_mode }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">os(宿主操作系统):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.os }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">uptime_in_days(运行天数):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.uptime_in_days }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">executable(可执行文件路径):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.executable }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">config_file(配置文件路径):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Server.config_file }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-collapse-item title="Clients(客户端连接)" name="client">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">connected_clients(已连接客户端数):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Clients.connected_clients }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">blocked_clients(正在等待阻塞命令客户端数):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Clients.blocked_clients }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					                <el-collapse-item title="Keyspace(key信息)" name="keyspace">
 | 
				
			||||||
 | 
					                    <div class="row" v-for="(value, key) in info.Keyspace" :key="key">
 | 
				
			||||||
 | 
					                        <span class="title">{{ key }}: </span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ value }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-collapse-item title="Stats(统计)" name="state">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">total_commands_processed(总处理命令数):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Stats.total_commands_processed }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">instantaneous_ops_per_sec(当前qps):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Stats.instantaneous_ops_per_sec }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">total_net_input_bytes(网络入口流量字节数):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Stats.total_net_input_bytes }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">total_net_output_bytes(网络出口流量字节数):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Stats.total_net_output_bytes }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">expired_keys(过期key的总数量):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Stats.expired_keys }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">instantaneous_ops_per_sec(当前qps):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Stats.instantaneous_ops_per_sec }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-collapse-item title="Persistence(持久化)" name="persistence">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">aof_enabled(是否启用aof):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Persistence.aof_enabled }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">loading(是否正在载入持久化文件):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Persistence.loading }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-collapse-item title="Cluster(集群)" name="cluster">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">cluster_enabled(是否启用集群模式):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Cluster.cluster_enabled }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-collapse-item title="Memory(内存消耗相关信息)" name="memory">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_memory(分配内存总量):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Memory.used_memory_human }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">maxmemory(最大内存配置):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Memory.maxmemory }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_memory_rss(已分配的内存总量,操作系统角度):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Memory.used_memory_rss_human }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">mem_fragmentation_ratio(used_memory_rss和used_memory 之间的比率):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Memory.mem_fragmentation_ratio }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_memory_peak(内存消耗峰值):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Memory.used_memory_peak_human }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">total_system_memory(主机总内存):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.Memory.total_system_memory_human }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-collapse-item title="CPU" name="cpu">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_cpu_sys(由Redis服务器消耗的系统CPU):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.CPU.used_cpu_sys }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_cpu_user(由Redis服务器消耗的用户CPU):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.CPU.used_cpu_user }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_cpu_sys_children(由后台进程消耗的系统CPU):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.CPU.used_cpu_sys_children }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <span class="title">used_cpu_user_children(由后台进程消耗的用户CPU):</span>
 | 
				
			||||||
 | 
					                        <span class="value">{{ info.CPU.used_cpu_user_children }}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </el-collapse-item>
 | 
				
			||||||
 | 
					            </el-collapse>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { defineComponent, reactive, watch, toRefs } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'Info',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        visible: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        title: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        info: {
 | 
				
			||||||
 | 
					            type: [Boolean, Object],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup(props: any, { emit }) {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            visible: false,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(
 | 
				
			||||||
 | 
					            () => props.visible,
 | 
				
			||||||
 | 
					            (val) => {
 | 
				
			||||||
 | 
					                state.visible = val;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const close = () => {
 | 
				
			||||||
 | 
					            emit('update:visible', false);
 | 
				
			||||||
 | 
					            emit('close');
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            close,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					.row .title {
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    color: #8492a6;
 | 
				
			||||||
 | 
					    margin-right: 6px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.row .value {
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    color: black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										190
									
								
								mayfly_go_web/src/views/ops/redis/RedisEdit.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								mayfly_go_web/src/views/ops/redis/RedisEdit.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="35%">
 | 
				
			||||||
 | 
					            <el-form :model="form" ref="redisForm" :rules="rules" label-width="85px" size="small">
 | 
				
			||||||
 | 
					                <el-form-item prop="projectId" label="项目:" required>
 | 
				
			||||||
 | 
					                    <el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
 | 
				
			||||||
 | 
					                        <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item prop="envId" label="环境:" required>
 | 
				
			||||||
 | 
					                    <el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
 | 
				
			||||||
 | 
					                        <el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                    </el-select>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="host" label="host:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.host" placeholder="请输入host:port" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="password" label="密码:">
 | 
				
			||||||
 | 
					                    <el-input
 | 
				
			||||||
 | 
					                        type="password"
 | 
				
			||||||
 | 
					                        show-password
 | 
				
			||||||
 | 
					                        v-model.trim="form.password"
 | 
				
			||||||
 | 
					                        placeholder="请输入密码"
 | 
				
			||||||
 | 
					                        autocomplete="new-password"
 | 
				
			||||||
 | 
					                    ></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item prop="db" label="库号:" required>
 | 
				
			||||||
 | 
					                    <el-input v-model.trim="form.db" placeholder="请输入库号"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					            </el-form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <div class="dialog-footer">
 | 
				
			||||||
 | 
					                    <el-button type="primary" :loading="btnLoading" @click="btnOk" size="mini">确 定</el-button>
 | 
				
			||||||
 | 
					                    <el-button @click="cancel()" size="mini">取 消</el-button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
 | 
				
			||||||
 | 
					import { redisApi } from './api';
 | 
				
			||||||
 | 
					import { projectApi } from '../project/api.ts';
 | 
				
			||||||
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'RedisEdit',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        visible: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        projects: {
 | 
				
			||||||
 | 
					            type: Array,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        redis: {
 | 
				
			||||||
 | 
					            type: [Boolean, Object],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        title: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup(props: any, { emit }) {
 | 
				
			||||||
 | 
					        const redisForm: any = ref(null);
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            visible: false,
 | 
				
			||||||
 | 
					            projects: [],
 | 
				
			||||||
 | 
					            envs: [],
 | 
				
			||||||
 | 
					            form: {
 | 
				
			||||||
 | 
					                id: null,
 | 
				
			||||||
 | 
					                name: null,
 | 
				
			||||||
 | 
					                host: null,
 | 
				
			||||||
 | 
					                password: null,
 | 
				
			||||||
 | 
					                project: null,
 | 
				
			||||||
 | 
					                projectId: null,
 | 
				
			||||||
 | 
					                envId: null,
 | 
				
			||||||
 | 
					                env: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            btnLoading: false,
 | 
				
			||||||
 | 
					            rules: {
 | 
				
			||||||
 | 
					                projectId: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择项目',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                envId: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请选择环境',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                host: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入主机ip:port',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                db: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        required: true,
 | 
				
			||||||
 | 
					                        message: '请输入库号',
 | 
				
			||||||
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(props, async (newValue, oldValue) => {
 | 
				
			||||||
 | 
					            state.visible = newValue.visible;
 | 
				
			||||||
 | 
					            state.projects = newValue.projects;
 | 
				
			||||||
 | 
					            if (newValue.redis) {
 | 
				
			||||||
 | 
					                getEnvs(newValue.redis.projectId);
 | 
				
			||||||
 | 
					                state.form = { ...newValue.redis };
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                state.envs = [];
 | 
				
			||||||
 | 
					                state.form = { db: 0 } as any;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const getEnvs = async (projectId: any) => {
 | 
				
			||||||
 | 
					            state.envs = await projectApi.projectEnvs.request({ projectId });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeProject = (projectId: number) => {
 | 
				
			||||||
 | 
					            for (let p of state.projects as any) {
 | 
				
			||||||
 | 
					                if (p.id == projectId) {
 | 
				
			||||||
 | 
					                    state.form.project = p.name;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.envs = [];
 | 
				
			||||||
 | 
					            getEnvs(projectId);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const changeEnv = (envId: number) => {
 | 
				
			||||||
 | 
					            for (let p of state.envs as any) {
 | 
				
			||||||
 | 
					                if (p.id == envId) {
 | 
				
			||||||
 | 
					                    state.form.env = p.name;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const btnOk = async () => {
 | 
				
			||||||
 | 
					            redisForm.value.validate((valid: boolean) => {
 | 
				
			||||||
 | 
					                if (valid) {
 | 
				
			||||||
 | 
					                    redisApi.saveRedis.request(state.form).then((res: any) => {
 | 
				
			||||||
 | 
					                        ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					                        emit('val-change', state.form);
 | 
				
			||||||
 | 
					                        state.btnLoading = true;
 | 
				
			||||||
 | 
					                        setTimeout(() => {
 | 
				
			||||||
 | 
					                            state.btnLoading = false;
 | 
				
			||||||
 | 
					                        }, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        cancel();
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ElMessage.error('请正确填写信息');
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cancel = () => {
 | 
				
			||||||
 | 
					            emit('update:visible', false);
 | 
				
			||||||
 | 
					            emit('cancel');
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                redisForm.value.resetFields();
 | 
				
			||||||
 | 
					                //  重置对象属性为null
 | 
				
			||||||
 | 
					                state.form = {} as any;
 | 
				
			||||||
 | 
					            }, 200);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            redisForm,
 | 
				
			||||||
 | 
					            changeProject,
 | 
				
			||||||
 | 
					            changeEnv,
 | 
				
			||||||
 | 
					            btnOk,
 | 
				
			||||||
 | 
					            cancel,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										369
									
								
								mayfly_go_web/src/views/ops/redis/RedisList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								mayfly_go_web/src/views/ops/redis/RedisList.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,369 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        <div class="toolbar">
 | 
				
			||||||
 | 
					            <el-button type="primary" icon="el-icon-plus" size="mini" @click="editRedis(true)" plain>添加</el-button>
 | 
				
			||||||
 | 
					            <el-button type="primary" icon="el-icon-edit" :disabled="currentId == null" size="mini" @click="editRedis(false)" plain>编辑</el-button>
 | 
				
			||||||
 | 
					            <el-button type="danger" icon="el-icon-delete" :disabled="currentId == null" size="mini" @click="deleteRedis" plain>删除</el-button>
 | 
				
			||||||
 | 
					            <div style="float: right">
 | 
				
			||||||
 | 
					                <!-- <el-input placeholder="host" size="mini" style="width: 140px" v-model="query.host" @clear="search" plain clearable></el-input>
 | 
				
			||||||
 | 
					                <el-select v-model="params.clusterId" size="mini" clearable placeholder="集群选择">
 | 
				
			||||||
 | 
					                    <el-option v-for="item in clusters" :key="item.id" :value="item.id" :label="item.name"></el-option>
 | 
				
			||||||
 | 
					                </el-select> -->
 | 
				
			||||||
 | 
					                <el-select v-model="query.projectId" placeholder="请选择项目" filterable clearable size="small">
 | 
				
			||||||
 | 
					                    <el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
 | 
				
			||||||
 | 
					                </el-select>
 | 
				
			||||||
 | 
					                <el-button class="ml5" @click="search" type="success" icon="el-icon-search" size="mini"></el-button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <el-table :data="redisTable" stripe style="width: 100%" @current-change="choose">
 | 
				
			||||||
 | 
					            <el-table-column label="选择" width="50px">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-radio v-model="currentId" :label="scope.row.id">
 | 
				
			||||||
 | 
					                        <i></i>
 | 
				
			||||||
 | 
					                    </el-radio>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="project" label="项目" width></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="env" label="环境" width></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="host" label="host:port" width></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="createTime" label="创建时间">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="creator" label="创建人"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column label="操作" width>
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-button type="primary" @click="info(scope.row)" icon="el-icon-tickets" size="mini" plain>info</el-button>
 | 
				
			||||||
 | 
					                    <!-- <el-button type="success" @click="manage(scope.row)" :ref="scope.row" size="mini" plain>数据管理</el-button> -->
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					        </el-table>
 | 
				
			||||||
 | 
					        <el-pagination
 | 
				
			||||||
 | 
					            @current-change="handlePageChange"
 | 
				
			||||||
 | 
					            style="text-align: center"
 | 
				
			||||||
 | 
					            background
 | 
				
			||||||
 | 
					            layout="prev, pager, next, total, jumper"
 | 
				
			||||||
 | 
					            :total="total"
 | 
				
			||||||
 | 
					            v-model:current-page="query.pageNum"
 | 
				
			||||||
 | 
					            :page-size="query.pageSize"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <info v-model:visible="infoDialog.visible" :title="infoDialog.title" :info="infoDialog.info"></info>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <redis-edit
 | 
				
			||||||
 | 
					            @val-change="valChange"
 | 
				
			||||||
 | 
					            :projects="projects"
 | 
				
			||||||
 | 
					            :title="redisEditDialog.title"
 | 
				
			||||||
 | 
					            v-model:visible="redisEditDialog.visible"
 | 
				
			||||||
 | 
					            v-model:redis="redisEditDialog.data"
 | 
				
			||||||
 | 
					        ></redis-edit>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Info from './Info.vue';
 | 
				
			||||||
 | 
					import { redisApi } from './api';
 | 
				
			||||||
 | 
					import { toRefs, reactive, defineComponent, onMounted } from 'vue';
 | 
				
			||||||
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
 | 
					import { projectApi } from '../project/api.ts';
 | 
				
			||||||
 | 
					import RedisEdit from './RedisEdit.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'RedisList',
 | 
				
			||||||
 | 
					    components: {
 | 
				
			||||||
 | 
					        Info,
 | 
				
			||||||
 | 
					        RedisEdit,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup() {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            projects: [],
 | 
				
			||||||
 | 
					            redisTable: [],
 | 
				
			||||||
 | 
					            total: 0,
 | 
				
			||||||
 | 
					            currentId: null,
 | 
				
			||||||
 | 
					            currentData: null,
 | 
				
			||||||
 | 
					            query: {
 | 
				
			||||||
 | 
					                pageNum: 1,
 | 
				
			||||||
 | 
					                pageSize: 10,
 | 
				
			||||||
 | 
					                prjectId: null,
 | 
				
			||||||
 | 
					                clusterId: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            redisInfo: {
 | 
				
			||||||
 | 
					                url: '',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            clusters: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    id: 0,
 | 
				
			||||||
 | 
					                    name: '单机',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            infoDialog: {
 | 
				
			||||||
 | 
					                title: '',
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                info: {
 | 
				
			||||||
 | 
					                    Server: {},
 | 
				
			||||||
 | 
					                    Keyspace: {},
 | 
				
			||||||
 | 
					                    Clients: {},
 | 
				
			||||||
 | 
					                    CPU: {},
 | 
				
			||||||
 | 
					                    Memory: {},
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            redisEditDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                data: null,
 | 
				
			||||||
 | 
					                title: '新增redis',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onMounted(async () => {
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					            state.projects = (await projectApi.projects.request({ pageNum: 1, pageSize: 100 })).list;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const handlePageChange = (curPage: number) => {
 | 
				
			||||||
 | 
					            state.query.pageNum = curPage;
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const choose = (item: any) => {
 | 
				
			||||||
 | 
					            if (!item) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.currentId = item.id;
 | 
				
			||||||
 | 
					            state.currentData = item;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // connect() {
 | 
				
			||||||
 | 
					        //   Req.post('/open/redis/connect', this.form, res => {
 | 
				
			||||||
 | 
					        //     this.redisInfo = res
 | 
				
			||||||
 | 
					        //   })
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const deleteRedis = async () => {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                await ElMessageBox.confirm(`确定删除该redis?`, '提示', {
 | 
				
			||||||
 | 
					                    confirmButtonText: '确定',
 | 
				
			||||||
 | 
					                    cancelButtonText: '取消',
 | 
				
			||||||
 | 
					                    type: 'warning',
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                await redisApi.delRedis.request({ id: state.currentId });
 | 
				
			||||||
 | 
					                ElMessage.success('删除成功');
 | 
				
			||||||
 | 
					                state.currentData = null;
 | 
				
			||||||
 | 
					                state.currentId = null;
 | 
				
			||||||
 | 
					                search();
 | 
				
			||||||
 | 
					            } catch (err) {}
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const info = (redis: any) => {
 | 
				
			||||||
 | 
					            redisApi.redisInfo.request({ id: redis.id }).then((res: any) => {
 | 
				
			||||||
 | 
					                state.infoDialog.info = res;
 | 
				
			||||||
 | 
					                state.infoDialog.title = `'${redis.host}' info`;
 | 
				
			||||||
 | 
					                state.infoDialog.visible = true;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const search = async () => {
 | 
				
			||||||
 | 
					            const res = await redisApi.redisList.request(state.query);
 | 
				
			||||||
 | 
					            state.redisTable = res.list;
 | 
				
			||||||
 | 
					            state.total = res.total;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const editRedis = (isAdd = false) => {
 | 
				
			||||||
 | 
					            if (isAdd) {
 | 
				
			||||||
 | 
					                state.redisEditDialog.data = null;
 | 
				
			||||||
 | 
					                state.redisEditDialog.title = '新增redis';
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                state.redisEditDialog.data = state.currentData;
 | 
				
			||||||
 | 
					                state.redisEditDialog.title = '修改redis';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.redisEditDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const valChange = () => {
 | 
				
			||||||
 | 
					            search();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            search,
 | 
				
			||||||
 | 
					            handlePageChange,
 | 
				
			||||||
 | 
					            choose,
 | 
				
			||||||
 | 
					            info,
 | 
				
			||||||
 | 
					            deleteRedis,
 | 
				
			||||||
 | 
					            editRedis,
 | 
				
			||||||
 | 
					            valChange,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					// @Component({
 | 
				
			||||||
 | 
					//   name: 'RedisList',
 | 
				
			||||||
 | 
					//   components: {
 | 
				
			||||||
 | 
					//     Info,
 | 
				
			||||||
 | 
					//     DynamicFormDialog
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					// })
 | 
				
			||||||
 | 
					// export default class RedisList extends Vue {
 | 
				
			||||||
 | 
					//   validatePort = (rule: any, value: any, callback: any) => {
 | 
				
			||||||
 | 
					//     if (value > 65535 || value < 1) {
 | 
				
			||||||
 | 
					//       callback(new Error('端口号错误'))
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					//     callback()
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   redisTable = []
 | 
				
			||||||
 | 
					//   permission = redisPermission
 | 
				
			||||||
 | 
					//   keyPermission = redisKeyPermission
 | 
				
			||||||
 | 
					//   currentId = null
 | 
				
			||||||
 | 
					//   currentData: any = null
 | 
				
			||||||
 | 
					//   params = {
 | 
				
			||||||
 | 
					//     host: null,
 | 
				
			||||||
 | 
					//     clusterId: null
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					//   redisInfo = {
 | 
				
			||||||
 | 
					//     url: ''
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					//   clusters = [
 | 
				
			||||||
 | 
					//     {
 | 
				
			||||||
 | 
					//       id: 0,
 | 
				
			||||||
 | 
					//       name: '单机'
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					//   ]
 | 
				
			||||||
 | 
					//   infoDialog = {
 | 
				
			||||||
 | 
					//     title: '',
 | 
				
			||||||
 | 
					//     visible: false,
 | 
				
			||||||
 | 
					//     info: {
 | 
				
			||||||
 | 
					//       Server: {},
 | 
				
			||||||
 | 
					//       Keyspace: {},
 | 
				
			||||||
 | 
					//       Clients: {},
 | 
				
			||||||
 | 
					//       CPU: {},
 | 
				
			||||||
 | 
					//       Memory: {}
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					//   formDialog = {
 | 
				
			||||||
 | 
					//     visible: false,
 | 
				
			||||||
 | 
					//     title: '',
 | 
				
			||||||
 | 
					//     formInfo: {
 | 
				
			||||||
 | 
					//       createApi: redisApi.save,
 | 
				
			||||||
 | 
					//       updateApi: redisApi.update,
 | 
				
			||||||
 | 
					//       formRows: [
 | 
				
			||||||
 | 
					//         [
 | 
				
			||||||
 | 
					//           {
 | 
				
			||||||
 | 
					//             type: 'input',
 | 
				
			||||||
 | 
					//             label: '主机:',
 | 
				
			||||||
 | 
					//             name: 'host',
 | 
				
			||||||
 | 
					//             placeholder: '请输入节点ip',
 | 
				
			||||||
 | 
					//             rules: [
 | 
				
			||||||
 | 
					//               {
 | 
				
			||||||
 | 
					//                 required: true,
 | 
				
			||||||
 | 
					//                 message: '请输入节点ip',
 | 
				
			||||||
 | 
					//                 trigger: ['blur', 'change']
 | 
				
			||||||
 | 
					//               }
 | 
				
			||||||
 | 
					//             ]
 | 
				
			||||||
 | 
					//           }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         [
 | 
				
			||||||
 | 
					//           {
 | 
				
			||||||
 | 
					//             type: 'input',
 | 
				
			||||||
 | 
					//             label: '端口号:',
 | 
				
			||||||
 | 
					//             name: 'port',
 | 
				
			||||||
 | 
					//             placeholder: '请输入节点端口号',
 | 
				
			||||||
 | 
					//             inputType: 'number',
 | 
				
			||||||
 | 
					//             rules: [
 | 
				
			||||||
 | 
					//               {
 | 
				
			||||||
 | 
					//                 required: true,
 | 
				
			||||||
 | 
					//                 message: '请输入节点端口号',
 | 
				
			||||||
 | 
					//                 trigger: ['blur', 'change']
 | 
				
			||||||
 | 
					//               }
 | 
				
			||||||
 | 
					//             ]
 | 
				
			||||||
 | 
					//           }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         [
 | 
				
			||||||
 | 
					//           {
 | 
				
			||||||
 | 
					//             type: 'input',
 | 
				
			||||||
 | 
					//             label: '密码:',
 | 
				
			||||||
 | 
					//             name: 'pwd',
 | 
				
			||||||
 | 
					//             placeholder: '请输入节点密码',
 | 
				
			||||||
 | 
					//             inputType: 'password'
 | 
				
			||||||
 | 
					//           }
 | 
				
			||||||
 | 
					//         ],
 | 
				
			||||||
 | 
					//         [
 | 
				
			||||||
 | 
					//           {
 | 
				
			||||||
 | 
					//             type: 'input',
 | 
				
			||||||
 | 
					//             label: '描述:',
 | 
				
			||||||
 | 
					//             name: 'description',
 | 
				
			||||||
 | 
					//             placeholder: '请输入节点描述',
 | 
				
			||||||
 | 
					//             inputType: 'textarea'
 | 
				
			||||||
 | 
					//           }
 | 
				
			||||||
 | 
					//         ]
 | 
				
			||||||
 | 
					//       ]
 | 
				
			||||||
 | 
					//     },
 | 
				
			||||||
 | 
					//     formData: { port: 6379 }
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   mounted() {
 | 
				
			||||||
 | 
					//     this.search()
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   choose(item: any) {
 | 
				
			||||||
 | 
					//     if (!item) {
 | 
				
			||||||
 | 
					//       return
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					//     this.currentId = item.id
 | 
				
			||||||
 | 
					//     this.currentData = item
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   // connect() {
 | 
				
			||||||
 | 
					//   //   Req.post('/open/redis/connect', this.form, res => {
 | 
				
			||||||
 | 
					//   //     this.redisInfo = res
 | 
				
			||||||
 | 
					//   //   })
 | 
				
			||||||
 | 
					//   // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   async deleteNode() {
 | 
				
			||||||
 | 
					//     await redisApi.del.request({ id: this.currentId })
 | 
				
			||||||
 | 
					//     this.$message.success('删除成功')
 | 
				
			||||||
 | 
					//     this.search()
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   manage(row: any) {
 | 
				
			||||||
 | 
					//     this.$router.push(`/redis_operation/${row.clusterId}/${row.id}`)
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   info(redis: any) {
 | 
				
			||||||
 | 
					//     redisApi.info.request({ id: redis.id }).then(res => {
 | 
				
			||||||
 | 
					//       this.infoDialog.info = res
 | 
				
			||||||
 | 
					//       this.infoDialog.title = `'${redis.host}' info`
 | 
				
			||||||
 | 
					//       this.infoDialog.visible = true
 | 
				
			||||||
 | 
					//     })
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   search() {
 | 
				
			||||||
 | 
					//     redisApi.list.request(this.params).then(res => {
 | 
				
			||||||
 | 
					//       this.redisTable = res
 | 
				
			||||||
 | 
					//     })
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   openFormDialog(redis: any) {
 | 
				
			||||||
 | 
					//     let dialogTitle
 | 
				
			||||||
 | 
					//     if (redis) {
 | 
				
			||||||
 | 
					//       this.formDialog.formData = this.currentData
 | 
				
			||||||
 | 
					//       dialogTitle = '编辑redis节点'
 | 
				
			||||||
 | 
					//     } else {
 | 
				
			||||||
 | 
					//       this.formDialog.formData = { port: 6379 }
 | 
				
			||||||
 | 
					//       dialogTitle = '添加redis节点'
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//     this.formDialog.title = dialogTitle
 | 
				
			||||||
 | 
					//     this.formDialog.visible = true
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//   submitSuccess() {
 | 
				
			||||||
 | 
					//     this.currentId = null
 | 
				
			||||||
 | 
					//     this.currentData = null
 | 
				
			||||||
 | 
					//     this.search()
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										76
									
								
								mayfly_go_web/src/views/ops/redis/ValueDialog.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								mayfly_go_web/src/views/ops/redis/ValueDialog.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <el-dialog :title="keyValue.key" v-model="visible" :before-close="cancel" :show-close="false" width="750px">
 | 
				
			||||||
 | 
					        <el-form>
 | 
				
			||||||
 | 
					            <el-form-item>
 | 
				
			||||||
 | 
					                <el-input v-model="keyValue.value" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }" autocomplete="off"></el-input>
 | 
				
			||||||
 | 
					            </el-form-item>
 | 
				
			||||||
 | 
					        </el-form>
 | 
				
			||||||
 | 
					        <template #footer>
 | 
				
			||||||
 | 
					            <div class="dialog-footer">
 | 
				
			||||||
 | 
					                <el-button @click="saveValue" type="primary" size="mini">确 定</el-button>
 | 
				
			||||||
 | 
					                <el-button @click="cancel()" size="mini">取 消</el-button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					    </el-dialog>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import { defineComponent, reactive, watch, toRefs } from 'vue';
 | 
				
			||||||
 | 
					import { redisApi } from './api';
 | 
				
			||||||
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					import { isTrue } from '@/common/assert';
 | 
				
			||||||
 | 
					export default defineComponent({
 | 
				
			||||||
 | 
					    name: 'ValueDialog',
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        visible: {
 | 
				
			||||||
 | 
					            type: Boolean,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        title: {
 | 
				
			||||||
 | 
					            type: String,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        keyValue: {
 | 
				
			||||||
 | 
					            type: [String, Object],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    setup(props: any, { emit }) {
 | 
				
			||||||
 | 
					        const state = reactive({
 | 
				
			||||||
 | 
					            visible: false,
 | 
				
			||||||
 | 
					            keyValue: {} as any,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        const cancel = () => {
 | 
				
			||||||
 | 
					            emit('update:visible', false);
 | 
				
			||||||
 | 
					            emit('cancel');
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(
 | 
				
			||||||
 | 
					            () => props.visible,
 | 
				
			||||||
 | 
					            (val) => {
 | 
				
			||||||
 | 
					                state.visible = val;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        watch(
 | 
				
			||||||
 | 
					            () => props.keyValue,
 | 
				
			||||||
 | 
					            (val) => {
 | 
				
			||||||
 | 
					                state.keyValue = val;
 | 
				
			||||||
 | 
					                if (state.keyValue.type != 'string') {
 | 
				
			||||||
 | 
					                    state.keyValue.value = JSON.stringify(val.value, undefined, 2)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // state.keyValue.value = JSON.stringify(val.value, undefined, 2)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const saveValue = async () => {
 | 
				
			||||||
 | 
					            isTrue(state.keyValue.type == 'string', "暂不支持除string外其他类型修改")
 | 
				
			||||||
 | 
					            await redisApi.saveStringValue.request(state.keyValue);
 | 
				
			||||||
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					            cancel();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            ...toRefs(state),
 | 
				
			||||||
 | 
					            saveValue,
 | 
				
			||||||
 | 
					            cancel,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										17
									
								
								mayfly_go_web/src/views/ops/redis/api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								mayfly_go_web/src/views/ops/redis/api.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import Api from '@/common/Api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const redisApi = {
 | 
				
			||||||
 | 
					    redisList : Api.create("/redis", 'get'),
 | 
				
			||||||
 | 
					    redisInfo: Api.create("/redis/{id}/info", 'get'),
 | 
				
			||||||
 | 
					    saveRedis: Api.create("/redis", 'post'),
 | 
				
			||||||
 | 
					    delRedis: Api.create("/redis/{id}", 'delete'),
 | 
				
			||||||
 | 
					    // 获取权限列表
 | 
				
			||||||
 | 
					    scan: Api.create("/redis/{id}/scan/{cursor}/{count}", 'get'),
 | 
				
			||||||
 | 
					    getStringValue: Api.create("/redis/{id}/string-value", 'get'),
 | 
				
			||||||
 | 
					    saveStringValue: Api.create("/redis/{id}/string-value", 'post'),
 | 
				
			||||||
 | 
					    getHashValue: Api.create("/redis/{id}/hash-value", 'get'),
 | 
				
			||||||
 | 
					    getSetValue: Api.create("/redis/{id}/set-value", 'get'),
 | 
				
			||||||
 | 
					    saveHashValue: Api.create("/redis/{id}/hash-value", 'post'),
 | 
				
			||||||
 | 
					    del: Api.create("/redis/{id}/scan/{cursor}/{count}", 'delete'),
 | 
				
			||||||
 | 
					    delKey: Api.create("/redis/{id}/key", 'delete'),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								mayfly_go_web/src/views/ops/redis/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								mayfly_go_web/src/views/ops/redis/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export { default } from './RedisList.vue';
 | 
				
			||||||
@@ -3,14 +3,14 @@
 | 
				
			|||||||
        <el-dialog :title="title" v-model="visible" :show-close="false" width="35%">
 | 
					        <el-dialog :title="title" v-model="visible" :show-close="false" width="35%">
 | 
				
			||||||
            <el-form :model="form" ref="accountForm" :rules="rules" label-width="85px" size="small">
 | 
					            <el-form :model="form" ref="accountForm" :rules="rules" label-width="85px" size="small">
 | 
				
			||||||
                <el-form-item prop="username" label="用户名:" required>
 | 
					                <el-form-item prop="username" label="用户名:" required>
 | 
				
			||||||
                    <el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入用户名" auto-complete="off"></el-input>
 | 
					                    <el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入账号用户名" auto-complete="off"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
                <el-form-item prop="password" label="密码:" required>
 | 
					                <!-- <el-form-item prop="password" label="密码:" required>
 | 
				
			||||||
                    <el-input type="password" v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password"></el-input>
 | 
					                    <el-input type="password" v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
                <el-form-item v-if="!edit" label="确认密码:" required>
 | 
					                <el-form-item v-if="!edit" label="确认密码:" required>
 | 
				
			||||||
                    <el-input type="password" v-model.trim="form.repassword" placeholder="请输入确认密码" autocomplete="new-password"></el-input>
 | 
					                    <el-input type="password" v-model.trim="form.repassword" placeholder="请输入确认密码" autocomplete="new-password"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item> -->
 | 
				
			||||||
            </el-form>
 | 
					            </el-form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <template #footer>
 | 
					            <template #footer>
 | 
				
			||||||
@@ -61,13 +61,13 @@ export default defineComponent({
 | 
				
			|||||||
                        trigger: ['change', 'blur'],
 | 
					                        trigger: ['change', 'blur'],
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                password: [
 | 
					                // password: [
 | 
				
			||||||
                    {
 | 
					                //     {
 | 
				
			||||||
                        required: true,
 | 
					                //         required: true,
 | 
				
			||||||
                        message: '请输入密码',
 | 
					                //         message: '请输入密码',
 | 
				
			||||||
                        trigger: ['change', 'blur'],
 | 
					                //         trigger: ['change', 'blur'],
 | 
				
			||||||
                    },
 | 
					                //     },
 | 
				
			||||||
                ],
 | 
					                // ],
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@
 | 
				
			|||||||
            >
 | 
					            >
 | 
				
			||||||
            <div style="float: right">
 | 
					            <div style="float: right">
 | 
				
			||||||
                <el-input
 | 
					                <el-input
 | 
				
			||||||
 | 
					                    class="mr2"
 | 
				
			||||||
                    placeholder="请输入账号名"
 | 
					                    placeholder="请输入账号名"
 | 
				
			||||||
                    size="small"
 | 
					                    size="small"
 | 
				
			||||||
                    style="width: 140px"
 | 
					                    style="width: 140px"
 | 
				
			||||||
@@ -45,8 +46,13 @@
 | 
				
			|||||||
                    <el-tag v-if="scope.row.status == -1" type="danger" size="mini">禁用</el-tag>
 | 
					                    <el-tag v-if="scope.row.status == -1" type="danger" size="mini">禁用</el-tag>
 | 
				
			||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
            </el-table-column>
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column min-width="160" prop="lastLoginTime" label="最后登录时间">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    {{ $filters.dateFormat(scope.row.lastLoginTime) }}
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- <el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column> -->
 | 
					            <el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column>
 | 
				
			||||||
            <el-table-column min-width="160" prop="createTime" label="创建时间">
 | 
					            <el-table-column min-width="160" prop="createTime" label="创建时间">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
					                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
@@ -58,8 +64,8 @@
 | 
				
			|||||||
					{{ $filters.dateFormat(scope.row.updateTime) }}
 | 
										{{ $filters.dateFormat(scope.row.updateTime) }}
 | 
				
			||||||
				</template>
 | 
									</template>
 | 
				
			||||||
			</el-table-column> -->
 | 
								</el-table-column> -->
 | 
				
			||||||
            <el-table-column min-width="160" prop="lastLoginTime" label="最后登录时间"></el-table-column>
 | 
					
 | 
				
			||||||
            <el-table-column min-width="120" prop="remark" label="备注" show-overflow-tooltip></el-table-column>
 | 
					            <!-- <el-table-column min-width="120" prop="remark" label="备注" show-overflow-tooltip></el-table-column> -->
 | 
				
			||||||
            <el-table-column label="查看更多" min-width="150">
 | 
					            <el-table-column label="查看更多" min-width="150">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
                    <el-link @click.prevent="showRoles(scope.row)" type="success">角色</el-link>
 | 
					                    <el-link @click.prevent="showRoles(scope.row)" type="success">角色</el-link>
 | 
				
			||||||
@@ -70,14 +76,21 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <el-table-column label="操作" min-width="200px">
 | 
					            <el-table-column label="操作" min-width="200px">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
                    <el-button v-auth="'account:changeStatus'" v-if="scope.row.status == 1" type="danger" icom="el-icon-tickets" size="mini" plain
 | 
					                    <el-button
 | 
				
			||||||
 | 
					                        v-auth="'account:changeStatus'"
 | 
				
			||||||
 | 
					                        @click="changeStatus(scope.row)"
 | 
				
			||||||
 | 
					                        v-if="scope.row.status == 1"
 | 
				
			||||||
 | 
					                        type="danger"
 | 
				
			||||||
 | 
					                        icom="el-icon-tickets"
 | 
				
			||||||
 | 
					                        size="mini"
 | 
				
			||||||
 | 
					                        plain
 | 
				
			||||||
                        >禁用</el-button
 | 
					                        >禁用</el-button
 | 
				
			||||||
                    >
 | 
					                    >
 | 
				
			||||||
                    <el-button
 | 
					                    <el-button
 | 
				
			||||||
                        v-auth="'account:changeStatus'"
 | 
					                        v-auth="'account:changeStatus'"
 | 
				
			||||||
                        v-if="scope.row.status == -1"
 | 
					                        v-if="scope.row.status == -1"
 | 
				
			||||||
                        type="success"
 | 
					                        type="success"
 | 
				
			||||||
                        @click="serviceManager(scope.row)"
 | 
					                        @click="changeStatus(scope.row)"
 | 
				
			||||||
                        size="mini"
 | 
					                        size="mini"
 | 
				
			||||||
                        plain
 | 
					                        plain
 | 
				
			||||||
                        >启用</el-button
 | 
					                        >启用</el-button
 | 
				
			||||||
@@ -136,7 +149,7 @@ import RoleEdit from './RoleEdit.vue';
 | 
				
			|||||||
import AccountEdit from './AccountEdit.vue';
 | 
					import AccountEdit from './AccountEdit.vue';
 | 
				
			||||||
import enums from '../enums';
 | 
					import enums from '../enums';
 | 
				
			||||||
import { accountApi } from '../api';
 | 
					import { accountApi } from '../api';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'AccountList',
 | 
					    name: 'AccountList',
 | 
				
			||||||
    components: {
 | 
					    components: {
 | 
				
			||||||
@@ -223,13 +236,13 @@ export default defineComponent({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const changeStatus = async (row: any) => {
 | 
					        const changeStatus = async (row: any) => {
 | 
				
			||||||
            let id = row.id;
 | 
					            let id = row.id;
 | 
				
			||||||
            let status = row.status ? 1 : -1;
 | 
					            let status = row.status == -1 ? 1 : -1;
 | 
				
			||||||
            // await accountApi.changeStatus.request({
 | 
					            await accountApi.changeStatus.request({
 | 
				
			||||||
            // 	id,
 | 
					                id,
 | 
				
			||||||
            // 	status,
 | 
					                status,
 | 
				
			||||||
            // });
 | 
					            });
 | 
				
			||||||
            // ElMessage.success('操作成功');
 | 
					            ElMessage.success('操作成功');
 | 
				
			||||||
            // search();
 | 
					            search();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const handlePageChange = (curPage: number) => {
 | 
					        const handlePageChange = (curPage: number) => {
 | 
				
			||||||
@@ -267,12 +280,17 @@ export default defineComponent({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const deleteAccount = async () => {
 | 
					        const deleteAccount = async () => {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
 | 
					                await ElMessageBox.confirm(`确定删除该账号?`, '提示', {
 | 
				
			||||||
 | 
					                    confirmButtonText: '确定',
 | 
				
			||||||
 | 
					                    cancelButtonText: '取消',
 | 
				
			||||||
 | 
					                    type: 'warning',
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
                await accountApi.del.request({ id: state.chooseId });
 | 
					                await accountApi.del.request({ id: state.chooseId });
 | 
				
			||||||
                ElMessage.success('删除成功');
 | 
					                ElMessage.success('删除成功');
 | 
				
			||||||
 | 
					                state.chooseData = null;
 | 
				
			||||||
 | 
					                state.chooseId = null;
 | 
				
			||||||
                search();
 | 
					                search();
 | 
				
			||||||
            } catch (error) {
 | 
					            } catch (err) {}
 | 
				
			||||||
                ElMessage.error('刪除失败');
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,8 +13,9 @@
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <el-table :data="allRole" border ref="roleTable" @select="select" style="width: 100%">
 | 
					            <el-table :data="allRole" border ref="roleTable" @select="select" style="width: 100%">
 | 
				
			||||||
                <el-table-column type="selection" width="40"></el-table-column>
 | 
					                <el-table-column :selectable="selectable" type="selection" width="40"></el-table-column>
 | 
				
			||||||
                <el-table-column prop="name" label="角色名称"></el-table-column>
 | 
					                <el-table-column prop="name" label="角色名称"></el-table-column>
 | 
				
			||||||
 | 
					                 <el-table-column prop="code" label="角色code"></el-table-column>
 | 
				
			||||||
                <el-table-column prop="remark" label="角色描述">
 | 
					                <el-table-column prop="remark" label="角色描述">
 | 
				
			||||||
                    <template #default="scope">
 | 
					                    <template #default="scope">
 | 
				
			||||||
                        {{ scope.row.remark ? scope.row.remark : '暂无描述' }}
 | 
					                        {{ scope.row.remark ? scope.row.remark : '暂无描述' }}
 | 
				
			||||||
@@ -92,6 +93,11 @@ export default defineComponent({
 | 
				
			|||||||
            search();
 | 
					            search();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const selectable = (row: any) => {
 | 
				
			||||||
 | 
					            // 角色code不以COMMON开头才可勾选
 | 
				
			||||||
 | 
					            return row.code.indexOf('COMMON') != 0;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const select = (val: any, row: any) => {
 | 
					        const select = (val: any, row: any) => {
 | 
				
			||||||
            let roles = state.roles;
 | 
					            let roles = state.roles;
 | 
				
			||||||
            // 如果账号的角色id存在则为取消该角色(删除角色id列表中的该记录id),否则为新增角色
 | 
					            // 如果账号的角色id存在则为取消该角色(删除角色id列表中的该记录id),否则为新增角色
 | 
				
			||||||
@@ -164,6 +170,7 @@ export default defineComponent({
 | 
				
			|||||||
            roleTable,
 | 
					            roleTable,
 | 
				
			||||||
            search,
 | 
					            search,
 | 
				
			||||||
            handlePageChange,
 | 
					            handlePageChange,
 | 
				
			||||||
 | 
					            selectable,
 | 
				
			||||||
            select,
 | 
					            select,
 | 
				
			||||||
            btnOk,
 | 
					            btnOk,
 | 
				
			||||||
            cancel,
 | 
					            cancel,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@ export const accountApi = {
 | 
				
			|||||||
    save: Api.create("/sys/accounts", 'post'),
 | 
					    save: Api.create("/sys/accounts", 'post'),
 | 
				
			||||||
    update: Api.create("/sys/accounts/{id}", 'put'),
 | 
					    update: Api.create("/sys/accounts/{id}", 'put'),
 | 
				
			||||||
    del: Api.create("/sys/accounts/{id}", 'delete'),
 | 
					    del: Api.create("/sys/accounts/{id}", 'delete'),
 | 
				
			||||||
    changeStatus: Api.create("/sys/accounts/{id}/{status}", 'put'),
 | 
					    changeStatus: Api.create("/sys/accounts/change-status/{id}/{status}", 'put'),
 | 
				
			||||||
    roleIds: Api.create("/sys/accounts/{id}/roleIds", 'get'),
 | 
					    roleIds: Api.create("/sys/accounts/{id}/roleIds", 'get'),
 | 
				
			||||||
    roles: Api.create("/sys/accounts/{id}/roles", 'get'),
 | 
					    roles: Api.create("/sys/accounts/{id}/roles", 'get'),
 | 
				
			||||||
    resources: Api.create("/sys/accounts/{id}/resources", 'get'),
 | 
					    resources: Api.create("/sys/accounts/{id}/resources", 'get'),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item prop="code" label="path|code">
 | 
					                <el-form-item prop="code" label="path|code">
 | 
				
			||||||
                    <el-input v-model.trim="form.code" placeholder="菜单为路由path"></el-input>
 | 
					                    <el-input v-model.trim="form.code" placeholder="菜单不带/自动拼接父路径"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item label="序号" prop="weight" required>
 | 
					                <el-form-item label="序号" prop="weight" required>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,9 @@
 | 
				
			|||||||
                <el-form-item label="角色名称:" required>
 | 
					                <el-form-item label="角色名称:" required>
 | 
				
			||||||
                    <el-input v-model="form.name" auto-complete="off"></el-input>
 | 
					                    <el-input v-model="form.name" auto-complete="off"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					                <el-form-item label="角色code:" required>
 | 
				
			||||||
 | 
					                    <el-input :disabled="form.id" v-model="form.code" placeholder="COMMON开头则为所有账号共有角色" auto-complete="off"></el-input>
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
                <el-form-item label="角色描述:">
 | 
					                <el-form-item label="角色描述:">
 | 
				
			||||||
                    <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input>
 | 
					                    <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@
 | 
				
			|||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
            </el-table-column>
 | 
					            </el-table-column>
 | 
				
			||||||
            <el-table-column prop="name" label="角色名称"></el-table-column>
 | 
					            <el-table-column prop="name" label="角色名称"></el-table-column>
 | 
				
			||||||
 | 
					            <el-table-column prop="code" label="角色code"></el-table-column>
 | 
				
			||||||
            <el-table-column prop="remark" label="描述" min-width="180px" show-overflow-tooltip></el-table-column>
 | 
					            <el-table-column prop="remark" label="描述" min-width="180px" show-overflow-tooltip></el-table-column>
 | 
				
			||||||
            <el-table-column prop="createTime" label="创建时间">
 | 
					            <el-table-column prop="createTime" label="创建时间">
 | 
				
			||||||
                <template #default="scope">
 | 
					                <template #default="scope">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
package initialize
 | 
					package initialize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"mayfly-go/base/global"
 | 
						"mayfly-go/base/config"
 | 
				
			||||||
	"mayfly-go/base/middleware"
 | 
						"mayfly-go/base/middleware"
 | 
				
			||||||
	"mayfly-go/mock-server/routers"
 | 
						"mayfly-go/mock-server/routers"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func InitRouter() *gin.Engine {
 | 
					func InitRouter() *gin.Engine {
 | 
				
			||||||
	// server配置
 | 
						// server配置
 | 
				
			||||||
	serverConfig := global.Config.Server
 | 
						serverConfig := config.Conf.Server
 | 
				
			||||||
	gin.SetMode(serverConfig.Model)
 | 
						gin.SetMode(serverConfig.Model)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var router = gin.New()
 | 
						var router = gin.New()
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
app:
 | 
					app:
 | 
				
			||||||
  name: devops
 | 
					  name: mayfly-go
 | 
				
			||||||
  version: 1.0.0
 | 
					  version: 1.0.0
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
server:
 | 
					server:
 | 
				
			||||||
@@ -7,6 +7,10 @@ server:
 | 
				
			|||||||
  model: release
 | 
					  model: release
 | 
				
			||||||
  port: 8888
 | 
					  port: 8888
 | 
				
			||||||
  cors: true
 | 
					  cors: true
 | 
				
			||||||
 | 
					  tls:
 | 
				
			||||||
 | 
					    enable: false
 | 
				
			||||||
 | 
					    key-file: ./default.key
 | 
				
			||||||
 | 
					    cert-file: ./default.pem
 | 
				
			||||||
  # 静态资源
 | 
					  # 静态资源
 | 
				
			||||||
  static:
 | 
					  static:
 | 
				
			||||||
    - relative-path: /assets
 | 
					    - relative-path: /assets
 | 
				
			||||||
@@ -18,6 +22,11 @@ server:
 | 
				
			|||||||
    - relative-path: /favicon.ico
 | 
					    - relative-path: /favicon.ico
 | 
				
			||||||
      filepath: ./static/favicon.ico
 | 
					      filepath: ./static/favicon.ico
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jwt:
 | 
				
			||||||
 | 
					  key: mykey
 | 
				
			||||||
 | 
					  # 过期时间单位分钟
 | 
				
			||||||
 | 
					  expire-time: 1440
 | 
				
			||||||
 | 
					
 | 
				
			||||||
redis:
 | 
					redis:
 | 
				
			||||||
  host: 127.0.0.1
 | 
					  host: 127.0.0.1
 | 
				
			||||||
  port: 6379
 | 
					  port: 6379
 | 
				
			||||||
@@ -28,3 +37,10 @@ mysql:
 | 
				
			|||||||
  password: 111049
 | 
					  password: 111049
 | 
				
			||||||
  db-name: mayfly-job
 | 
					  db-name: mayfly-job
 | 
				
			||||||
  config: charset=utf8&loc=Local&parseTime=true
 | 
					  config: charset=utf8&loc=Local&parseTime=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					log:
 | 
				
			||||||
 | 
					   # 日志等级, trace, debug, info, warn, error, fatal
 | 
				
			||||||
 | 
					  level: info
 | 
				
			||||||
 | 
					  # file:
 | 
				
			||||||
 | 
					  #   path: ./
 | 
				
			||||||
 | 
					  #   name: mayfly.log
 | 
				
			||||||
							
								
								
									
										27
									
								
								server/default.key
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server/default.key
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					-----BEGIN RSA PRIVATE KEY-----
 | 
				
			||||||
 | 
					MIIEpgIBAAKCAQEA0CsawvEZl42Vf+0BlTuZ3Dp10yW8Oty1tjimxUj3s0WPeKil
 | 
				
			||||||
 | 
					6+TehnQELS8vGJfek+yT99nyrt+bkRmg1kxZ57FtQFEuthG4OQZoaMDUz6Ab+8P1
 | 
				
			||||||
 | 
					PQ9VH0XimnnYabxztJiQjl8HdJt6N4WP35kGlcul7qQ+Qc7iwjhSadfAhXVycqVI
 | 
				
			||||||
 | 
					cGQyHiPPfbmYRjueAIC4czmMUxwFKCwjepGYkwzWuGkpMD0hg/SIXpFJE2dcqYPR
 | 
				
			||||||
 | 
					2nCah1gxZZG00lHU1X2pehNmmgeHRkB5S7mrsCdyyV/33SAYk6T6PT7dOqY54bfn
 | 
				
			||||||
 | 
					h3C0k+T7IzvKTXKG76eG63STmxVa6luVoKMvxwIDAQABAoIBAQCI2Y2CUpYMd9us
 | 
				
			||||||
 | 
					edbskH4ZtaT35nrUB3y+Cog4cjvE8xnarKRHa/KOWX7VZYuEk3KTtJeh/Pn51K6k
 | 
				
			||||||
 | 
					uUBvIUqJcq7r9XLL5uJBOuEw3HQK+qrq3GxAc+/12y+Zdji7alR2iUWfEwIHup6i
 | 
				
			||||||
 | 
					GX/38tXNbE/tjrQO9z9Dh1tGkbvS/66tPn/T/oMxsRvZB6mCjB7yuOlEIwYTomYB
 | 
				
			||||||
 | 
					pUFemELt8T5RtfxRa8T1VoITbfuj7zvecqlThW0H8UizsFxvrOCUaga7jtsJOCHo
 | 
				
			||||||
 | 
					bcW5WvWwazoOfQ2BGpksKkBDf1N6pj85e4kOoYcVG9UN03ZwDvAGfQPWUlHB4YzW
 | 
				
			||||||
 | 
					PybMwIQBAoGBAPfuOQ+ukVmkiEKj6wCBe3Z0pYeNqBGec9aj62bKFh79BCE3ZopS
 | 
				
			||||||
 | 
					7JtGs8VfBKkBAaOy+MDuvJ2fvRNRtHT4BYe1U6ZRsmVFqHScACOaO/7TR0tz0ihL
 | 
				
			||||||
 | 
					0QLCkbSwsXExG6bYbwP4jMHkhHArT7Hy8WXvup8PffjSiEs1A1uGvYSBAoGBANbx
 | 
				
			||||||
 | 
					lHo+39nsc1OO8TUAWZChIQUib2hFIwzQYngSzINdfXQaGFT/omOsudAtfdjvp+qO
 | 
				
			||||||
 | 
					Tr7WpwgFEFveDFsdJfZ2Kc2x9a3ty7IYIWaAjK2ghkAKz3Tt4gClreB6qG2SBycP
 | 
				
			||||||
 | 
					4C2ImbY6hMaFHz3ENtTEzzTMdD1ByxQVMvoem3BHAoGBAJdaTmtMXl8jGivUdXnx
 | 
				
			||||||
 | 
					kbVWsFZ4G8nluUGm/+XYKHjybLr6XxbCWL7SApzSzL1/Z8jPURw2od53za0li8x8
 | 
				
			||||||
 | 
					PKQEBfTamtVIGPZW5Z7WYRnHURa2tezzm7zbmqd71lcLa54HMn5yFTuojVEMn7I6
 | 
				
			||||||
 | 
					ZTOdjYfcpUJpA9slmc8eCkQBAoGBAJEIbxRRaoBEQMkH8Y++zbB+WKZ7RssHo4/Y
 | 
				
			||||||
 | 
					6Ch3HtIg+i6mEPcBitRQzww+NeV0SExHe7Dfa9NIf3JNkO7F60CzGJ/3zXtvsftY
 | 
				
			||||||
 | 
					tujQIpxhbVS3NqaCgPXI1VtbyFwupW7hEnYG7xj7wW2mk578z7afmeTZdDGFPH8v
 | 
				
			||||||
 | 
					krccgeuvAoGBAPAwiqbZlXNx+ueI1B3T8VpXnG0ozKxG+l5B71kssZWa7xcv9yRd
 | 
				
			||||||
 | 
					c15l2PSXNtnoT/mBID7+dqQOmfYxsDHAkUdd/BrxhXtdi9FR3AfHSEQz+VKsogAD
 | 
				
			||||||
 | 
					uLyRd7jWTYqqGa2UToF/CBV+c6QyMB+6pzFNk5DmUEm4Gd6jcHDITYeI
 | 
				
			||||||
 | 
					-----END RSA PRIVATE KEY-----
 | 
				
			||||||
							
								
								
									
										21
									
								
								server/default.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								server/default.pem
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					-----BEGIN CERTIFICATE-----
 | 
				
			||||||
 | 
					MIIDejCCAmICCQDQU4ZRt2G46TANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJ6
 | 
				
			||||||
 | 
					aDEPMA0GA1UECAwGbWF5Zmx5MQswCQYDVQQHDAJ4bTEPMA0GA1UECgwGbWF5Zmx5
 | 
				
			||||||
 | 
					MQ8wDQYDVQQLDAZtYXlmbHkxDzANBgNVBAMMBm1heWZseTEfMB0GCSqGSIb3DQEJ
 | 
				
			||||||
 | 
					ARYQOTU0NTM3NDczQHFxLmNvbTAeFw0yMTA2MjQwMzI2MzBaFw0zMTA2MjIwMzI2
 | 
				
			||||||
 | 
					MzBaMH8xCzAJBgNVBAYTAnpoMQ8wDQYDVQQIDAZtYXlmbHkxCzAJBgNVBAcMAnht
 | 
				
			||||||
 | 
					MQ8wDQYDVQQKDAZtYXlmbHkxDzANBgNVBAsMBm1heWZseTEPMA0GA1UEAwwGbWF5
 | 
				
			||||||
 | 
					Zmx5MR8wHQYJKoZIhvcNAQkBFhA5NTQ1Mzc0NzNAcXEuY29tMIIBIjANBgkqhkiG
 | 
				
			||||||
 | 
					9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CsawvEZl42Vf+0BlTuZ3Dp10yW8Oty1tjim
 | 
				
			||||||
 | 
					xUj3s0WPeKil6+TehnQELS8vGJfek+yT99nyrt+bkRmg1kxZ57FtQFEuthG4OQZo
 | 
				
			||||||
 | 
					aMDUz6Ab+8P1PQ9VH0XimnnYabxztJiQjl8HdJt6N4WP35kGlcul7qQ+Qc7iwjhS
 | 
				
			||||||
 | 
					adfAhXVycqVIcGQyHiPPfbmYRjueAIC4czmMUxwFKCwjepGYkwzWuGkpMD0hg/SI
 | 
				
			||||||
 | 
					XpFJE2dcqYPR2nCah1gxZZG00lHU1X2pehNmmgeHRkB5S7mrsCdyyV/33SAYk6T6
 | 
				
			||||||
 | 
					PT7dOqY54bfnh3C0k+T7IzvKTXKG76eG63STmxVa6luVoKMvxwIDAQABMA0GCSqG
 | 
				
			||||||
 | 
					SIb3DQEBCwUAA4IBAQB/e8EO2XEtkYBxebR1w6i50vaegLsxQJR3l5qm7rsHu3Cr
 | 
				
			||||||
 | 
					smJXGsc56axKCAqJ4XvSI65BT51FghAoGn62QNyiQgc0YoS99nwCCGFtnhZ2lmSe
 | 
				
			||||||
 | 
					pfhUHegN/Qo4I8FemEMD+o9kGeAzwrnaIVIT/cNOEQgm+RzrgHHJh5QBn2XgJalU
 | 
				
			||||||
 | 
					NeFTWaimyefwSezSa/vPbyMoAl9HkT6kdvnms/yOth4AOle6+5pM2StWjmMi4yx4
 | 
				
			||||||
 | 
					16y3NvLTku6nAUazaHTOOu/MCqLWL2/qYTk3r7OCop2jr9Rp+HLbg5AfKLUIVXjG
 | 
				
			||||||
 | 
					/1fnXJIuD+2u9qgDLN5PZNgz4MlU86ugtmYPFkVt
 | 
				
			||||||
 | 
					-----END CERTIFICATE-----
 | 
				
			||||||
@@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"mayfly-go/base/ctx"
 | 
						"mayfly-go/base/ctx"
 | 
				
			||||||
	"mayfly-go/base/ginx"
 | 
						"mayfly-go/base/ginx"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
 | 
						"mayfly-go/base/utils"
 | 
				
			||||||
	"mayfly-go/server/devops/apis/form"
 | 
						"mayfly-go/server/devops/apis/form"
 | 
				
			||||||
	"mayfly-go/server/devops/apis/vo"
 | 
						"mayfly-go/server/devops/apis/vo"
 | 
				
			||||||
	"mayfly-go/server/devops/application"
 | 
						"mayfly-go/server/devops/application"
 | 
				
			||||||
@@ -17,45 +18,73 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Db struct {
 | 
					type Db struct {
 | 
				
			||||||
	DbApp application.IDb
 | 
						DbApp application.Db
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @router /api/dbs [get]
 | 
					// @router /api/dbs [get]
 | 
				
			||||||
func (d *Db) Dbs(rc *ctx.ReqCtx) {
 | 
					func (d *Db) Dbs(rc *ctx.ReqCtx) {
 | 
				
			||||||
	m := new(entity.Db)
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
						m := &entity.Db{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
 | 
				
			||||||
 | 
							ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
 | 
				
			||||||
 | 
							Database:  g.Query("database"),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ginx.BindQuery(g, m)
 | 
				
			||||||
	rc.ResData = d.DbApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.SelectDataDbVO))
 | 
						rc.ResData = d.DbApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.SelectDataDbVO))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @router /api/db/:dbId/select [get]
 | 
					func (d *Db) Save(rc *ctx.ReqCtx) {
 | 
				
			||||||
func (d *Db) SelectData(rc *ctx.ReqCtx) {
 | 
						form := &form.DbForm{}
 | 
				
			||||||
 | 
						ginx.BindJsonAndValid(rc.GinCtx, form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc.ReqParam = form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db := new(entity.Db)
 | 
				
			||||||
 | 
						utils.Copy(db, form)
 | 
				
			||||||
 | 
						db.SetBaseInfo(rc.LoginAccount)
 | 
				
			||||||
 | 
						d.DbApp.Save(db)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Db) DeleteDb(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						d.DbApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// @router /api/db/:dbId/exec-sql [get]
 | 
				
			||||||
 | 
					func (d *Db) ExecSql(rc *ctx.ReqCtx) {
 | 
				
			||||||
	g := rc.GinCtx
 | 
						g := rc.GinCtx
 | 
				
			||||||
	// 去除前后空格及换行符
 | 
						// 去除前后空格及换行符
 | 
				
			||||||
	selectSql := strings.TrimFunc(g.Query("selectSql"), func(r rune) bool {
 | 
						sql := strings.TrimFunc(g.Query("sql"), func(r rune) bool {
 | 
				
			||||||
		s := string(r)
 | 
							s := string(r)
 | 
				
			||||||
		return s == " " || s == "\n"
 | 
							return s == " " || s == "\n"
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	rc.ReqParam = selectSql
 | 
						rc.ReqParam = sql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	biz.NotEmpty(selectSql, "selectSql不能为空")
 | 
						biz.NotEmpty(sql, "sql不能为空")
 | 
				
			||||||
	res, err := d.DbApp.GetDbInstance(GetDbId(g)).SelectData(selectSql)
 | 
						if strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select") {
 | 
				
			||||||
 | 
							colNames, res, err := d.DbApp.GetDbInstance(GetDbId(g)).SelectData(sql)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(biz.NewBizErr(fmt.Sprintf("查询失败: %s", err.Error())))
 | 
								panic(biz.NewBizErr(fmt.Sprintf("查询失败: %s", err.Error())))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	rc.ResData = res
 | 
							colAndRes := make(map[string]interface{})
 | 
				
			||||||
}
 | 
							colAndRes["colNames"] = colNames
 | 
				
			||||||
 | 
							colAndRes["res"] = res
 | 
				
			||||||
// @router /api/db/:dbId/exec-sql [post]
 | 
							rc.ResData = colAndRes
 | 
				
			||||||
func (d *Db) ExecSql(g *gin.Context) {
 | 
						} else {
 | 
				
			||||||
	rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("sql执行"))
 | 
							rowsAffected, err := d.DbApp.GetDbInstance(GetDbId(g)).Exec(sql)
 | 
				
			||||||
	rc.Handle(func(rc *ctx.ReqCtx) {
 | 
					 | 
				
			||||||
		selectSql := g.Query("sql")
 | 
					 | 
				
			||||||
		biz.NotEmpty(selectSql, "sql不能为空")
 | 
					 | 
				
			||||||
		num, err := d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).Exec(selectSql)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(biz.NewBizErr(fmt.Sprintf("执行失败: %s", err.Error())))
 | 
								panic(biz.NewBizErr(fmt.Sprintf("执行失败: %s", err.Error())))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		rc.ResData = num
 | 
							res := make([]map[string]string, 0)
 | 
				
			||||||
	})
 | 
							resData := make(map[string]string)
 | 
				
			||||||
 | 
							resData["影响条数"] = fmt.Sprintf("%d", rowsAffected)
 | 
				
			||||||
 | 
							res = append(res, resData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							colAndRes := make(map[string]interface{})
 | 
				
			||||||
 | 
							colAndRes["colNames"] = []string{"影响条数"}
 | 
				
			||||||
 | 
							colAndRes["res"] = res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc.ResData = colAndRes
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @router /api/db/:dbId/t-metadata [get]
 | 
					// @router /api/db/:dbId/t-metadata [get]
 | 
				
			||||||
@@ -74,21 +103,32 @@ func (d *Db) ColumnMA(rc *ctx.ReqCtx) {
 | 
				
			|||||||
// @router /api/db/:dbId/hint-tables [get]
 | 
					// @router /api/db/:dbId/hint-tables [get]
 | 
				
			||||||
func (d *Db) HintTables(rc *ctx.ReqCtx) {
 | 
					func (d *Db) HintTables(rc *ctx.ReqCtx) {
 | 
				
			||||||
	dbi := d.DbApp.GetDbInstance(GetDbId(rc.GinCtx))
 | 
						dbi := d.DbApp.GetDbInstance(GetDbId(rc.GinCtx))
 | 
				
			||||||
 | 
						// 获取所有表
 | 
				
			||||||
	tables := dbi.GetTableMetedatas()
 | 
						tables := dbi.GetTableMetedatas()
 | 
				
			||||||
	res := make(map[string][]string)
 | 
					
 | 
				
			||||||
 | 
						tableNames := make([]string, 0)
 | 
				
			||||||
	for _, v := range tables {
 | 
						for _, v := range tables {
 | 
				
			||||||
		tableName := v["tableName"]
 | 
							tableNames = append(tableNames, v["tableName"])
 | 
				
			||||||
		columnMds := dbi.GetColumnMetadatas(tableName)
 | 
						}
 | 
				
			||||||
		columnNames := make([]string, len(columnMds))
 | 
						// 获取所有表下的所有列信息
 | 
				
			||||||
		for i, v := range columnMds {
 | 
						columnMds := dbi.GetColumnMetadatas(tableNames...)
 | 
				
			||||||
 | 
						// key = 表名,value = 列名数组
 | 
				
			||||||
 | 
						res := make(map[string][]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, v := range columnMds {
 | 
				
			||||||
 | 
							tName := v["tableName"]
 | 
				
			||||||
 | 
							if res[tName] == nil {
 | 
				
			||||||
 | 
								res[tName] = make([]string, 0)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							columnName := fmt.Sprintf("%s  [%s]", v["columnName"], v["columnType"])
 | 
				
			||||||
		comment := v["columnComment"]
 | 
							comment := v["columnComment"]
 | 
				
			||||||
 | 
							// 如果字段备注不为空,则加上备注信息
 | 
				
			||||||
		if comment != "" {
 | 
							if comment != "" {
 | 
				
			||||||
				columnNames[i] = v["columnName"] + " [" + comment + "]"
 | 
								columnName = fmt.Sprintf("%s[%s]", columnName, comment)
 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				columnNames[i] = v["columnName"]
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		}
 | 
					
 | 
				
			||||||
		res[tableName] = columnNames
 | 
							res[tName] = append(res[tName], columnName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	rc.ResData = res
 | 
						rc.ResData = res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								server/devops/apis/form/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								server/devops/apis/form/db.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DbForm struct {
 | 
				
			||||||
 | 
						Id        uint64
 | 
				
			||||||
 | 
						Name      string `binding:"required" json:"name"`
 | 
				
			||||||
 | 
						Type      string `binding:"required" json:"type"` // 类型,mysql oracle等
 | 
				
			||||||
 | 
						Host      string `binding:"required" json:"host"`
 | 
				
			||||||
 | 
						Port      int    `binding:"required" json:"port"`
 | 
				
			||||||
 | 
						Username  string `binding:"required" json:"username"`
 | 
				
			||||||
 | 
						Password  string `binding:"required" json:"password"`
 | 
				
			||||||
 | 
						Database  string `binding:"required" json:"database"`
 | 
				
			||||||
 | 
						ProjectId uint64 `binding:"required" json:"projectId"`
 | 
				
			||||||
 | 
						Project   string `json:"project"`
 | 
				
			||||||
 | 
						Env       string `json:"env"`
 | 
				
			||||||
 | 
						EnvId     uint64 `binding:"required" json:"envId"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,45 +2,46 @@ package form
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type MachineForm struct {
 | 
					type MachineForm struct {
 | 
				
			||||||
	Id   uint64 `json:"id"`
 | 
						Id   uint64 `json:"id"`
 | 
				
			||||||
	Name string `json:"name" valid:"Required"`
 | 
						Name string `json:"name" binding:"required"`
 | 
				
			||||||
	// IP地址
 | 
						// IP地址
 | 
				
			||||||
	Ip string `json:"ip" valid:"Required"`
 | 
						Ip string `json:"ip" binding:"required"`
 | 
				
			||||||
	// 用户名
 | 
						// 用户名
 | 
				
			||||||
	Username string `json:"username" valid:"Required"`
 | 
						Username string `json:"username" binding:"required"`
 | 
				
			||||||
	Password string `json:"password" valid:"Required"`
 | 
						Password string `json:"password" binding:"required"`
 | 
				
			||||||
	// 端口号
 | 
						// 端口号
 | 
				
			||||||
	Port int `json:"port" valid:"Required"`
 | 
						Port int `json:"port" binding:"required"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MachineRunForm struct {
 | 
					type MachineRunForm struct {
 | 
				
			||||||
	MachineId int64  `valid:"Required"`
 | 
						MachineId int64  `binding:"required"`
 | 
				
			||||||
	Cmd       string `valid:"Required"`
 | 
						Cmd       string `binding:"required"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MachineFileForm struct {
 | 
					type MachineFileForm struct {
 | 
				
			||||||
	Id        uint64
 | 
						Id        uint64
 | 
				
			||||||
	Name      string `valid:"Required"`
 | 
						Name      string `binding:"required"`
 | 
				
			||||||
	MachineId uint64 `valid:"Required"`
 | 
						MachineId uint64 `binding:"required"`
 | 
				
			||||||
	Type      int    `valid:"Required"`
 | 
						Type      int    `binding:"required"`
 | 
				
			||||||
	Path      string `valid:"Required"`
 | 
						Path      string `binding:"required"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MachineScriptForm struct {
 | 
					type MachineScriptForm struct {
 | 
				
			||||||
	Id          uint64
 | 
						Id          uint64
 | 
				
			||||||
	Name        string `valid:"Required"`
 | 
						Name        string `binding:"required"`
 | 
				
			||||||
	MachineId   uint64 `valid:"Required"`
 | 
						MachineId   uint64 `binding:"required"`
 | 
				
			||||||
	Type        int    `valid:"Required"`
 | 
						Type        int    `binding:"required"`
 | 
				
			||||||
	Description string `valid:"Required"`
 | 
						Description string `binding:"required"`
 | 
				
			||||||
	Script      string `valid:"Required"`
 | 
						Params      string
 | 
				
			||||||
 | 
						Script      string `binding:"required"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DbSqlSaveForm struct {
 | 
					type DbSqlSaveForm struct {
 | 
				
			||||||
	Sql  string `valid:"Required"`
 | 
						Sql  string `binding:"required"`
 | 
				
			||||||
	Type int    `valid:"Required"`
 | 
						Type int    `binding:"required"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MachineFileUpdateForm struct {
 | 
					type MachineFileUpdateForm struct {
 | 
				
			||||||
	Content string `valid:"Required"`
 | 
						Content string `binding:"required"`
 | 
				
			||||||
	Id      uint64 `valid:"Required"`
 | 
						Id      uint64 `binding:"required"`
 | 
				
			||||||
	Path    string `valid:"Required"`
 | 
						Path    string `binding:"required"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								server/devops/apis/form/redis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								server/devops/apis/form/redis.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					package form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Redis struct {
 | 
				
			||||||
 | 
						Id        uint64
 | 
				
			||||||
 | 
						Host      string `binding:"required" json:"host"`
 | 
				
			||||||
 | 
						Password  string `json:"password"`
 | 
				
			||||||
 | 
						Db        int    `json:"db"`
 | 
				
			||||||
 | 
						ProjectId uint64 `binding:"required" json:"projectId"`
 | 
				
			||||||
 | 
						Project   string `json:"project"`
 | 
				
			||||||
 | 
						Env       string `json:"env"`
 | 
				
			||||||
 | 
						EnvId     uint64 `binding:"required" json:"envId"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type KeyValue struct {
 | 
				
			||||||
 | 
						Key   string      `binding:"required" json:"key"`
 | 
				
			||||||
 | 
						Value interface{} `binding:"required" json:"value"`
 | 
				
			||||||
 | 
						Timed uint64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -26,7 +26,7 @@ var WsUpgrader = websocket.Upgrader{
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Machine struct {
 | 
					type Machine struct {
 | 
				
			||||||
	MachineApp application.IMachine
 | 
						MachineApp application.Machine
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Machine) Machines(rc *ctx.ReqCtx) {
 | 
					func (m *Machine) Machines(rc *ctx.ReqCtx) {
 | 
				
			||||||
@@ -45,6 +45,12 @@ func (m *Machine) SaveMachine(rc *ctx.ReqCtx) {
 | 
				
			|||||||
	m.MachineApp.Save(entity)
 | 
						m.MachineApp.Save(entity)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *Machine) DeleteMachine(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						id := uint64(ginx.PathParamInt(rc.GinCtx, "id"))
 | 
				
			||||||
 | 
						rc.ReqParam = id
 | 
				
			||||||
 | 
						m.MachineApp.Delete(id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// top命令信息
 | 
					// top命令信息
 | 
				
			||||||
func (m *Machine) Top(rc *ctx.ReqCtx) {
 | 
					func (m *Machine) Top(rc *ctx.ReqCtx) {
 | 
				
			||||||
	rc.ResData = m.MachineApp.GetCli(GetMachineId(rc.GinCtx)).GetTop()
 | 
						rc.ResData = m.MachineApp.GetCli(GetMachineId(rc.GinCtx)).GetTop()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MachineFile struct {
 | 
					type MachineFile struct {
 | 
				
			||||||
	MachineFileApp application.IMachineFile
 | 
						MachineFileApp application.MachineFile
 | 
				
			||||||
	MachineApp     application.IMachine
 | 
						MachineApp     application.Machine
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,8 +16,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MachineScript struct {
 | 
					type MachineScript struct {
 | 
				
			||||||
	MachineScriptApp application.IMachineScript
 | 
						MachineScriptApp application.MachineScript
 | 
				
			||||||
	MachineApp       application.IMachine
 | 
						MachineApp       application.Machine
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MachineScript) MachineScripts(rc *ctx.ReqCtx) {
 | 
					func (m *MachineScript) MachineScripts(rc *ctx.ReqCtx) {
 | 
				
			||||||
@@ -57,8 +57,12 @@ func (m *MachineScript) RunMachineScript(rc *ctx.ReqCtx) {
 | 
				
			|||||||
	biz.NotNil(ms, "该脚本不存在")
 | 
						biz.NotNil(ms, "该脚本不存在")
 | 
				
			||||||
	biz.IsTrue(ms.MachineId == application.Common_Script_Machine_Id || ms.MachineId == machineId, "该脚本不属于该机器")
 | 
						biz.IsTrue(ms.MachineId == application.Common_Script_Machine_Id || ms.MachineId == machineId, "该脚本不属于该机器")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vars := g.QueryMap("params")
 | 
						script := ms.Script
 | 
				
			||||||
	res, err := m.MachineApp.GetCli(machineId).Run(utils.TemplateParse(ms.Script, vars))
 | 
						// 如果有脚本参数,则用脚本参数替换脚本中的模板占位符参数
 | 
				
			||||||
 | 
						if params := g.Query("params"); params != "" {
 | 
				
			||||||
 | 
							script = utils.TemplateParse(ms.Script, utils.Json2Map(params))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res, err := m.MachineApp.GetCli(machineId).Run(script)
 | 
				
			||||||
	// 记录请求参数
 | 
						// 记录请求参数
 | 
				
			||||||
	rc.ReqParam = fmt.Sprintf("[machineId: %d, scriptId: %d, name: %s]", machineId, scriptId, ms.Name)
 | 
						rc.ReqParam = fmt.Sprintf("[machineId: %d, scriptId: %d, name: %s]", machineId, scriptId, ms.Name)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										100
									
								
								server/devops/apis/project.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								server/devops/apis/project.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					package apis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/ctx"
 | 
				
			||||||
 | 
						"mayfly-go/base/ginx"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/apis/vo"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/application"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
 | 
						sys_applicaiton "mayfly-go/server/sys/application"
 | 
				
			||||||
 | 
						sys_entity "mayfly-go/server/sys/domain/entity"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Project struct {
 | 
				
			||||||
 | 
						ProjectApp application.Project
 | 
				
			||||||
 | 
						AccountApp sys_applicaiton.Account
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取当前登录用户可以访问的项目列表
 | 
				
			||||||
 | 
					func (p *Project) GetProjectsByLoginAccount(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						// 获取登录用户拥有的项目ids
 | 
				
			||||||
 | 
						projectMembers := &[]entity.ProjectMember{}
 | 
				
			||||||
 | 
						p.ProjectApp.ListMember(&entity.ProjectMember{AccountId: rc.LoginAccount.Id}, projectMembers)
 | 
				
			||||||
 | 
						var pids []uint64
 | 
				
			||||||
 | 
						for _, pm := range *projectMembers {
 | 
				
			||||||
 | 
							pids = append(pids, pm.ProjectId)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取项目信息
 | 
				
			||||||
 | 
						projects := &vo.AccountProjects{}
 | 
				
			||||||
 | 
						p.ProjectApp.ListProjectByIds(pids, projects)
 | 
				
			||||||
 | 
						rc.ResData = projects
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Project) GetProjects(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						condition := &entity.Project{}
 | 
				
			||||||
 | 
						ginx.BindQuery(rc.GinCtx, condition)
 | 
				
			||||||
 | 
						// condition.Name = rc.GinCtx.Query("name")
 | 
				
			||||||
 | 
						rc.ResData = p.ProjectApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]entity.Project))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Project) SaveProject(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						project := &entity.Project{}
 | 
				
			||||||
 | 
						ginx.BindJsonAndValid(rc.GinCtx, project)
 | 
				
			||||||
 | 
						rc.ReqParam = project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						project.SetBaseInfo(rc.LoginAccount)
 | 
				
			||||||
 | 
						p.ProjectApp.SaveProject(project)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取项目下的环境信息
 | 
				
			||||||
 | 
					func (p *Project) GetProjectEnvs(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						projectEnvs := &[]entity.ProjectEnv{}
 | 
				
			||||||
 | 
						p.ProjectApp.ListEnvByProjectId(uint64(ginx.PathParamInt(rc.GinCtx, "projectId")), projectEnvs)
 | 
				
			||||||
 | 
						rc.ResData = projectEnvs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//保存项目下的环境信息
 | 
				
			||||||
 | 
					func (p *Project) SaveProjectEnvs(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						projectEnv := &entity.ProjectEnv{}
 | 
				
			||||||
 | 
						ginx.BindJsonAndValid(rc.GinCtx, projectEnv)
 | 
				
			||||||
 | 
						rc.ReqParam = projectEnv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						projectEnv.SetBaseInfo(rc.LoginAccount)
 | 
				
			||||||
 | 
						p.ProjectApp.SaveProjectEnv(projectEnv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取项目下的成员信息
 | 
				
			||||||
 | 
					func (p *Project) GetProjectMembers(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						projectMems := &[]entity.ProjectMember{}
 | 
				
			||||||
 | 
						rc.ResData = p.ProjectApp.GetMemberPage(&entity.ProjectMember{ProjectId: uint64(ginx.PathParamInt(rc.GinCtx, "projectId"))},
 | 
				
			||||||
 | 
							ginx.GetPageParam(rc.GinCtx), projectMems)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//保存项目的成员信息
 | 
				
			||||||
 | 
					func (p *Project) SaveProjectMember(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						projectMem := &entity.ProjectMember{}
 | 
				
			||||||
 | 
						ginx.BindJsonAndValid(rc.GinCtx, projectMem)
 | 
				
			||||||
 | 
						rc.ReqParam = projectMem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 校验账号,并赋值username
 | 
				
			||||||
 | 
						account := &sys_entity.Account{}
 | 
				
			||||||
 | 
						account.Id = projectMem.AccountId
 | 
				
			||||||
 | 
						biz.ErrIsNil(p.AccountApp.GetAccount(account, "Id", "Username"), "账号不存在")
 | 
				
			||||||
 | 
						projectMem.Username = account.Username
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						projectMem.SetBaseInfo(rc.LoginAccount)
 | 
				
			||||||
 | 
						p.ProjectApp.SaveProjectMember(projectMem)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//删除项目成员
 | 
				
			||||||
 | 
					func (p *Project) DelProjectMember(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
						pid := ginx.PathParamInt(g, "projectId")
 | 
				
			||||||
 | 
						aid := ginx.PathParamInt(g, "accountId")
 | 
				
			||||||
 | 
						rc.ReqParam = fmt.Sprintf("projectId: %d, accountId: %d", pid, aid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.ProjectApp.DeleteMember(uint64(pid), uint64(aid))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										203
									
								
								server/devops/apis/redis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								server/devops/apis/redis.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
				
			|||||||
 | 
					package apis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/ctx"
 | 
				
			||||||
 | 
						"mayfly-go/base/ginx"
 | 
				
			||||||
 | 
						"mayfly-go/base/utils"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/apis/form"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/apis/vo"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/application"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Redis struct {
 | 
				
			||||||
 | 
						RedisApp application.Redis
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) RedisList(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
						m := &entity.Redis{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
 | 
				
			||||||
 | 
							ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ginx.BindQuery(g, m)
 | 
				
			||||||
 | 
						rc.ResData = r.RedisApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.Redis))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) Save(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						form := &form.Redis{}
 | 
				
			||||||
 | 
						ginx.BindJsonAndValid(rc.GinCtx, form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc.ReqParam = form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						redis := new(entity.Redis)
 | 
				
			||||||
 | 
						utils.Copy(redis, form)
 | 
				
			||||||
 | 
						redis.SetBaseInfo(rc.LoginAccount)
 | 
				
			||||||
 | 
						r.RedisApp.Save(redis)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) DeleteRedis(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						r.RedisApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) RedisInfo(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						res, _ := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(rc.GinCtx, "id"))).Cli.Info().Result()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						datas := strings.Split(res, "\r\n")
 | 
				
			||||||
 | 
						i := 0
 | 
				
			||||||
 | 
						length := len(datas)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parseMap := make(map[string]map[string]string, 0)
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							if i >= length {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if strings.Contains(datas[i], "#") {
 | 
				
			||||||
 | 
								key := utils.SubString(datas[i], strings.Index(datas[i], "#")+1, utils.StrLen(datas[i]))
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								key = strings.Trim(key, " ")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sectionMap := make(map[string]string, 0)
 | 
				
			||||||
 | 
								for {
 | 
				
			||||||
 | 
									if i >= length || !strings.Contains(datas[i], ":") {
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									pair := strings.Split(datas[i], ":")
 | 
				
			||||||
 | 
									i++
 | 
				
			||||||
 | 
									if len(pair) != 2 {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									sectionMap[pair[0]] = pair[1]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								parseMap[key] = sectionMap
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc.ResData = parseMap
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// scan获取redis的key列表信息
 | 
				
			||||||
 | 
					func (r *Redis) Scan(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
 | 
				
			||||||
 | 
						keys, cursor := ri.Scan(uint64(ginx.PathParamInt(g, "cursor")), g.Query("match"), int64(ginx.PathParamInt(g, "count")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var keyInfoSplit []string
 | 
				
			||||||
 | 
						if len(keys) > 0 {
 | 
				
			||||||
 | 
							keyInfoLua := `
 | 
				
			||||||
 | 
								local result = {}
 | 
				
			||||||
 | 
								-- KEYS[1]为第1个参数,lua数组下标从1开始
 | 
				
			||||||
 | 
								local ttl = redis.call('ttl', KEYS[1]);
 | 
				
			||||||
 | 
								local keyType = redis.call('type', KEYS[1]);
 | 
				
			||||||
 | 
								for i = 1, #KEYS do
 | 
				
			||||||
 | 
									local ttl = redis.call('ttl', KEYS[i]);
 | 
				
			||||||
 | 
									local keyType = redis.call('type', KEYS[i]);
 | 
				
			||||||
 | 
									table.insert(result, string.format("%d,%s", ttl, keyType['ok']));
 | 
				
			||||||
 | 
								end;
 | 
				
			||||||
 | 
								return table.concat(result, ".");`
 | 
				
			||||||
 | 
							// 通过lua获取 ttl,type.ttl2,type2格式,以便下面切割获取ttl和type。避免多次调用ttl和type函数
 | 
				
			||||||
 | 
							keyInfos, _ := ri.Cli.Eval(keyInfoLua, keys).Result()
 | 
				
			||||||
 | 
							keyInfoSplit = strings.Split(keyInfos.(string), ".")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kis := make([]*vo.KeyInfo, 0)
 | 
				
			||||||
 | 
						for i, k := range keys {
 | 
				
			||||||
 | 
							ttlType := strings.Split(keyInfoSplit[i], ",")
 | 
				
			||||||
 | 
							ttl, _ := strconv.Atoi(ttlType[0])
 | 
				
			||||||
 | 
							ki := &vo.KeyInfo{Key: k, Type: ttlType[1], Ttl: uint64(ttl)}
 | 
				
			||||||
 | 
							kis = append(kis, ki)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size, _ := ri.Cli.DBSize().Result()
 | 
				
			||||||
 | 
						rc.ResData = &vo.Keys{Cursor: cursor, Keys: kis, DbSize: size}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) DeleteKey(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
						key := g.Query("key")
 | 
				
			||||||
 | 
						biz.NotEmpty(key, "key不能为空")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
 | 
				
			||||||
 | 
						rc.ReqParam = key
 | 
				
			||||||
 | 
						ri.Cli.Del(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) checkKey(rc *ctx.ReqCtx) (*application.RedisInstance, string) {
 | 
				
			||||||
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
						key := g.Query("key")
 | 
				
			||||||
 | 
						biz.NotEmpty(key, "key不能为空")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id"))), key
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) GetStringValue(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						ri, key := r.checkKey(rc)
 | 
				
			||||||
 | 
						str, err := ri.Cli.Get(key).Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "获取字符串值失败: %s")
 | 
				
			||||||
 | 
						rc.ResData = str
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) GetHashValue(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						ri, key := r.checkKey(rc)
 | 
				
			||||||
 | 
						res, err := ri.Cli.HGetAll(key).Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "获取hash值失败: %s")
 | 
				
			||||||
 | 
						rc.ResData = res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) GetSetValue(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						ri, key := r.checkKey(rc)
 | 
				
			||||||
 | 
						res, err := ri.Cli.SMembers(key).Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "获取set值失败: %s")
 | 
				
			||||||
 | 
						rc.ResData = res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) Test(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						schema := `{
 | 
				
			||||||
 | 
							"$schema": "http://json-schema.org/draft-04/schema#",
 | 
				
			||||||
 | 
							"title": "Product",
 | 
				
			||||||
 | 
							"description": "A product from Acme's catalog",
 | 
				
			||||||
 | 
							"type": "object",
 | 
				
			||||||
 | 
							"properties": {
 | 
				
			||||||
 | 
								"id": {
 | 
				
			||||||
 | 
									"description": "The unique identifier for a product",
 | 
				
			||||||
 | 
									"type": "integer"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"name": {
 | 
				
			||||||
 | 
									"description": "Name of the product",
 | 
				
			||||||
 | 
									"type": "string"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"price": {
 | 
				
			||||||
 | 
									"type": "number",
 | 
				
			||||||
 | 
									"minimum": 0,
 | 
				
			||||||
 | 
									"exclusiveMinimum": true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"required": ["id", "name", "price"]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
						// 获取请求报文的内容长度
 | 
				
			||||||
 | 
						len := rc.GinCtx.Request.ContentLength
 | 
				
			||||||
 | 
						// 新建一个字节切片,长度与请求报文的内容长度相同
 | 
				
			||||||
 | 
						body := make([]byte, len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 读取 r 的请求主体,并将具体内容读入 body 中
 | 
				
			||||||
 | 
						rc.GinCtx.Request.Body.Read(body)
 | 
				
			||||||
 | 
						err := utils.ValidJsonString(schema, string(body))
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "%s")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Redis) SetStringValue(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						g := rc.GinCtx
 | 
				
			||||||
 | 
						keyValue := new(form.KeyValue)
 | 
				
			||||||
 | 
						ginx.BindJsonAndValid(g, keyValue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
 | 
				
			||||||
 | 
						str, err := ri.Cli.Set(keyValue.Key, keyValue.Value, time.Second*time.Duration(keyValue.Timed)).Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s")
 | 
				
			||||||
 | 
						rc.ResData = str
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,6 +10,11 @@ type SelectDataDbVO struct {
 | 
				
			|||||||
	Port       *int       `json:"port"`
 | 
						Port       *int       `json:"port"`
 | 
				
			||||||
	Type       *string    `json:"type"`
 | 
						Type       *string    `json:"type"`
 | 
				
			||||||
	Database   *string    `json:"database"`
 | 
						Database   *string    `json:"database"`
 | 
				
			||||||
 | 
						Username   *string    `json:"username"`
 | 
				
			||||||
 | 
						ProjectId  *int64     `json:"projectId"`
 | 
				
			||||||
 | 
						Project    *string    `json:"project"`
 | 
				
			||||||
 | 
						Env        *string    `json:"env"`
 | 
				
			||||||
 | 
						EnvId      *int64     `json:"envId"`
 | 
				
			||||||
	CreateTime *time.Time `json:"createTime"`
 | 
						CreateTime *time.Time `json:"createTime"`
 | 
				
			||||||
	Creator    *string    `json:"creator"`
 | 
						Creator    *string    `json:"creator"`
 | 
				
			||||||
	CreatorId  *int64     `json:"creatorId"`
 | 
						CreatorId  *int64     `json:"creatorId"`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								server/devops/apis/vo/project.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								server/devops/apis/vo/project.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package vo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 用户选择项目
 | 
				
			||||||
 | 
					type AccountProject struct {
 | 
				
			||||||
 | 
						Id     uint64 `json:"id"`
 | 
				
			||||||
 | 
						Name   string `json:"name"`
 | 
				
			||||||
 | 
						Remark string `json:"remark"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AccountProjects []AccountProject
 | 
				
			||||||
							
								
								
									
										29
									
								
								server/devops/apis/vo/redis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								server/devops/apis/vo/redis.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					package vo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Redis struct {
 | 
				
			||||||
 | 
						Id *int64 `json:"id"`
 | 
				
			||||||
 | 
						// Name       *string    `json:"name"`
 | 
				
			||||||
 | 
						Host       *string    `json:"host"`
 | 
				
			||||||
 | 
						Db         int        `json:"db"`
 | 
				
			||||||
 | 
						ProjectId  *int64     `json:"projectId"`
 | 
				
			||||||
 | 
						Project    *string    `json:"project"`
 | 
				
			||||||
 | 
						Env        *string    `json:"env"`
 | 
				
			||||||
 | 
						EnvId      *int64     `json:"envId"`
 | 
				
			||||||
 | 
						CreateTime *time.Time `json:"createTime"`
 | 
				
			||||||
 | 
						Creator    *string    `json:"creator"`
 | 
				
			||||||
 | 
						CreatorId  *int64     `json:"creatorId"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Keys struct {
 | 
				
			||||||
 | 
						Cursor uint64     `json:"cursor"`
 | 
				
			||||||
 | 
						Keys   []*KeyInfo `json:"keys"`
 | 
				
			||||||
 | 
						DbSize int64      `json:"dbSize"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type KeyInfo struct {
 | 
				
			||||||
 | 
						Key  string `json:"key"`
 | 
				
			||||||
 | 
						Ttl  uint64 `json:"ttl"`
 | 
				
			||||||
 | 
						Type string `json:"type"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -34,6 +34,7 @@ type MachineScriptVO struct {
 | 
				
			|||||||
	Script      *string `json:"script"`
 | 
						Script      *string `json:"script"`
 | 
				
			||||||
	Type        *int    `json:"type"`
 | 
						Type        *int    `json:"type"`
 | 
				
			||||||
	Description *string `json:"description"`
 | 
						Description *string `json:"description"`
 | 
				
			||||||
 | 
						Params      *string `json:"params"`
 | 
				
			||||||
	MachineId   *uint64 `json:"machineId"`
 | 
						MachineId   *uint64 `json:"machineId"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,22 @@
 | 
				
			|||||||
package application
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
	"mayfly-go/server/devops/domain/entity"
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
	"mayfly-go/server/devops/domain/repository"
 | 
						"mayfly-go/server/devops/domain/repository"
 | 
				
			||||||
	"mayfly-go/server/devops/infrastructure/db"
 | 
					 | 
				
			||||||
	"mayfly-go/server/devops/infrastructure/persistence"
 | 
						"mayfly-go/server/devops/infrastructure/persistence"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IDb interface {
 | 
					type Db interface {
 | 
				
			||||||
	// 分页获取机器脚本信息列表
 | 
						// 分页获取机器脚本信息列表
 | 
				
			||||||
	GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult
 | 
						GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据条件获取
 | 
						// 根据条件获取
 | 
				
			||||||
	GetDbBy(condition *entity.Db, cols ...string) error
 | 
						GetDbBy(condition *entity.Db, cols ...string) error
 | 
				
			||||||
@@ -20,36 +26,260 @@ type IDb interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Save(entity *entity.Db)
 | 
						Save(entity *entity.Db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GetDbInstance(id uint64) *db.DbInstance
 | 
						// 删除数据库信息
 | 
				
			||||||
 | 
						Delete(id uint64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取数据库连接实例
 | 
				
			||||||
 | 
						GetDbInstance(id uint64) *DbInstance
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type dbApp struct {
 | 
					type dbAppImpl struct {
 | 
				
			||||||
	dbRepo    repository.Db
 | 
						dbRepo    repository.Db
 | 
				
			||||||
 | 
						dbSqlRepo repository.DbSql
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Db IDb = &dbApp{dbRepo: persistence.DbDao}
 | 
					var DbApp Db = &dbAppImpl{
 | 
				
			||||||
 | 
						dbRepo:    persistence.DbDao,
 | 
				
			||||||
 | 
						dbSqlRepo: persistence.DbSqlDao,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 分页获取数据库信息列表
 | 
					// 分页获取数据库信息列表
 | 
				
			||||||
func (d *dbApp) GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult {
 | 
					func (d *dbAppImpl) GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
	return d.dbRepo.GetDbList(condition, pageParam, toEntity, orderBy...)
 | 
						return d.dbRepo.GetDbList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据条件获取
 | 
					// 根据条件获取
 | 
				
			||||||
func (d *dbApp) GetDbBy(condition *entity.Db, cols ...string) error {
 | 
					func (d *dbAppImpl) GetDbBy(condition *entity.Db, cols ...string) error {
 | 
				
			||||||
	return d.dbRepo.GetDb(condition, cols...)
 | 
						return d.dbRepo.GetDb(condition, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据id获取
 | 
					// 根据id获取
 | 
				
			||||||
func (d *dbApp) GetById(id uint64, cols ...string) *entity.Db {
 | 
					func (d *dbAppImpl) GetById(id uint64, cols ...string) *entity.Db {
 | 
				
			||||||
	return d.dbRepo.GetById(id, cols...)
 | 
						return d.dbRepo.GetById(id, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *dbApp) Save(entity *entity.Db) {
 | 
					func (d *dbAppImpl) Save(dbEntity *entity.Db) {
 | 
				
			||||||
 | 
						// 默认tcp连接
 | 
				
			||||||
 | 
						dbEntity.Network = "tcp"
 | 
				
			||||||
 | 
						// 测试连接
 | 
				
			||||||
 | 
						TestConnection(dbEntity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 查找是否存在该库
 | 
				
			||||||
 | 
						oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, Database: dbEntity.Database}
 | 
				
			||||||
 | 
						err := d.GetDbBy(oldDb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if dbEntity.Id == 0 {
 | 
				
			||||||
 | 
							biz.IsTrue(err != nil, "该库已存在")
 | 
				
			||||||
 | 
							d.dbRepo.Insert(dbEntity)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// 如果存在该库,则校验修改的库是否为该库
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								biz.IsTrue(oldDb.Id == dbEntity.Id, "该库已存在")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// 先关闭数据库连接
 | 
				
			||||||
 | 
							CloseDb(dbEntity.Id)
 | 
				
			||||||
 | 
							d.dbRepo.Update(dbEntity)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *dbApp) GetDbInstance(id uint64) *db.DbInstance {
 | 
					func (d *dbAppImpl) Delete(id uint64) {
 | 
				
			||||||
	return db.GetDbInstance(id, func(id uint64) *entity.Db {
 | 
						// 关闭连接
 | 
				
			||||||
		return d.dbRepo.GetById(id)
 | 
						CloseDb(id)
 | 
				
			||||||
	})
 | 
						d.dbRepo.Delete(id)
 | 
				
			||||||
 | 
						// 删除该库下用户保存的所有sql信息
 | 
				
			||||||
 | 
						d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (da *dbAppImpl) GetDbInstance(id uint64) *DbInstance {
 | 
				
			||||||
 | 
						// Id不为0,则为需要缓存
 | 
				
			||||||
 | 
						needCache := id != 0
 | 
				
			||||||
 | 
						if needCache {
 | 
				
			||||||
 | 
							load, ok := dbCache.Load(id)
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								return load.(*DbInstance)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d := da.GetById(id)
 | 
				
			||||||
 | 
						biz.NotNil(d, "数据库信息不存在")
 | 
				
			||||||
 | 
						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()
 | 
				
			||||||
 | 
						if perr != nil {
 | 
				
			||||||
 | 
							panic(biz.NewBizErr(fmt.Sprintf("数据库连接失败: %s", perr.Error())))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 最大连接周期,超过时间的连接就close
 | 
				
			||||||
 | 
						DB.SetConnMaxLifetime(100 * time.Second)
 | 
				
			||||||
 | 
						// 设置最大连接数
 | 
				
			||||||
 | 
						DB.SetMaxOpenConns(2)
 | 
				
			||||||
 | 
						// 设置闲置连接数
 | 
				
			||||||
 | 
						DB.SetMaxIdleConns(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbi := &DbInstance{Id: id, Type: d.Type, db: DB}
 | 
				
			||||||
 | 
						if needCache {
 | 
				
			||||||
 | 
							dbCache.LoadOrStore(d.Id, dbi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dbi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var dbCache sync.Map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetDbInstanceByCache(id uint64) *DbInstance {
 | 
				
			||||||
 | 
						if load, ok := dbCache.Load(id); ok {
 | 
				
			||||||
 | 
							return load.(*DbInstance)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConnection(d *entity.Db) {
 | 
				
			||||||
 | 
						biz.NotNil(d, "数据库信息不存在")
 | 
				
			||||||
 | 
						DB, err := sql.Open(d.Type, getDsn(d))
 | 
				
			||||||
 | 
						biz.ErrIsNil(err, "Open %s failed, err:%v\n", d.Type, err)
 | 
				
			||||||
 | 
						defer DB.Close()
 | 
				
			||||||
 | 
						perr := DB.Ping()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(perr, "数据库连接失败: %s")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// db实例
 | 
				
			||||||
 | 
					type DbInstance struct {
 | 
				
			||||||
 | 
						Id   uint64
 | 
				
			||||||
 | 
						Type string
 | 
				
			||||||
 | 
						db   *sql.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 执行查询语句
 | 
				
			||||||
 | 
					// 依次返回 列名数组,结果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") {
 | 
				
			||||||
 | 
							return nil, nil, errors.New("该sql非查询语句")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 没加limit,则默认限制50条
 | 
				
			||||||
 | 
						if !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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
 | 
				
			||||||
 | 
						// 后面的链接过来直接报错或拒绝,实际上也没有起效果
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if rows != nil {
 | 
				
			||||||
 | 
								rows.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						cols, _ := rows.Columns()
 | 
				
			||||||
 | 
						// 这里表示一行填充数据
 | 
				
			||||||
 | 
						scans := make([]interface{}, len(cols))
 | 
				
			||||||
 | 
						// 这里表示一行所有列的值,用[]byte表示
 | 
				
			||||||
 | 
						vals := make([][]byte, len(cols))
 | 
				
			||||||
 | 
						// 这里scans引用vals,把数据填充到[]byte里
 | 
				
			||||||
 | 
						for k := range vals {
 | 
				
			||||||
 | 
							scans[k] = &vals[k]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result := make([]map[string]string, 0)
 | 
				
			||||||
 | 
						// 列名
 | 
				
			||||||
 | 
						colNames := make([]string, 0)
 | 
				
			||||||
 | 
						// 是否第一次遍历,列名数组只需第一次遍历时加入
 | 
				
			||||||
 | 
						isFirst := true
 | 
				
			||||||
 | 
						for rows.Next() {
 | 
				
			||||||
 | 
							// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
 | 
				
			||||||
 | 
							err := rows.Scan(scans...)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// 每行数据
 | 
				
			||||||
 | 
							rowData := make(map[string]string)
 | 
				
			||||||
 | 
							// 把vals中的数据复制到row中
 | 
				
			||||||
 | 
							for k, v := range vals {
 | 
				
			||||||
 | 
								key := cols[k]
 | 
				
			||||||
 | 
								// 如果是密码字段,则脱敏显示
 | 
				
			||||||
 | 
								if key == "password" {
 | 
				
			||||||
 | 
									v = []byte("******")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if isFirst {
 | 
				
			||||||
 | 
									colNames = append(colNames, key)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// 这里把[]byte数据转成string
 | 
				
			||||||
 | 
								rowData[key] = string(v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							//放入结果集
 | 
				
			||||||
 | 
							result = append(result, rowData)
 | 
				
			||||||
 | 
							isFirst = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return colNames, result, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 执行 update, insert, delete,建表等sql
 | 
				
			||||||
 | 
					// 返回影响条数和错误
 | 
				
			||||||
 | 
					func (d *DbInstance) Exec(sql string) (int64, error) {
 | 
				
			||||||
 | 
						res, err := d.db.Exec(sql)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res.RowsAffected()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 关闭连接,并从缓存中移除
 | 
				
			||||||
 | 
					func (d *DbInstance) Close() {
 | 
				
			||||||
 | 
						d.db.Close()
 | 
				
			||||||
 | 
						dbCache.Delete(d.Id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取dataSourceName
 | 
				
			||||||
 | 
					func getDsn(d *entity.Db) string {
 | 
				
			||||||
 | 
						if d.Type == "mysql" {
 | 
				
			||||||
 | 
							return fmt.Sprintf("%s:%s@%s(%s:%d)/%s", d.Username, d.Password, d.Network, d.Host, d.Port, d.Database)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CloseDb(id uint64) {
 | 
				
			||||||
 | 
						if di := GetDbInstanceByCache(id); di != nil {
 | 
				
			||||||
 | 
							di.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//-----------------------------------元数据-------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// mysql 表信息元数据
 | 
				
			||||||
 | 
						MYSQL_TABLE_MA = `SELECT table_name tableName, engine, table_comment tableComment, 
 | 
				
			||||||
 | 
						create_time createTime from information_schema.tables
 | 
				
			||||||
 | 
						WHERE table_schema = (SELECT database())`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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`
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *DbInstance) GetTableMetedatas() []map[string]string {
 | 
				
			||||||
 | 
						var sql string
 | 
				
			||||||
 | 
						if d.Type == "mysql" {
 | 
				
			||||||
 | 
							sql = MYSQL_TABLE_MA
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, res, _ := d.SelectData(sql)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *DbInstance) GetColumnMetadatas(tableNames ...string) []map[string]string {
 | 
				
			||||||
 | 
						var sql, tableName string
 | 
				
			||||||
 | 
						for i := 0; i < len(tableNames); i++ {
 | 
				
			||||||
 | 
							if i != 0 {
 | 
				
			||||||
 | 
								tableName = tableName + ", "
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tableName = tableName + "'" + tableNames[i] + "'"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.Type == "mysql" {
 | 
				
			||||||
 | 
							sql = fmt.Sprintf(MYSQL_COLOUMN_MA, tableName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, res, err := d.SelectData(sql)
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,58 +7,95 @@ import (
 | 
				
			|||||||
	"mayfly-go/server/devops/domain/repository"
 | 
						"mayfly-go/server/devops/domain/repository"
 | 
				
			||||||
	"mayfly-go/server/devops/infrastructure/machine"
 | 
						"mayfly-go/server/devops/infrastructure/machine"
 | 
				
			||||||
	"mayfly-go/server/devops/infrastructure/persistence"
 | 
						"mayfly-go/server/devops/infrastructure/persistence"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IMachine interface {
 | 
					type Machine interface {
 | 
				
			||||||
	// 根据条件获取账号信息
 | 
						// 根据条件获取账号信息
 | 
				
			||||||
	GetMachine(condition *entity.Machine, cols ...string) error
 | 
						GetMachine(condition *entity.Machine, cols ...string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Save(entity *entity.Machine)
 | 
						Save(entity *entity.Machine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Delete(id uint64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据id获取
 | 
						// 根据id获取
 | 
				
			||||||
	GetById(id uint64, cols ...string) *entity.Machine
 | 
						GetById(id uint64, cols ...string) *entity.Machine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 分页获取机器信息列表
 | 
						// 分页获取机器信息列表
 | 
				
			||||||
	GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult
 | 
						GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 获取机器连接
 | 
						// 获取机器连接
 | 
				
			||||||
	GetCli(id uint64) *machine.Cli
 | 
						GetCli(id uint64) *machine.Cli
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type machineApp struct {
 | 
					type machineAppImpl struct {
 | 
				
			||||||
	machineRepo repository.Machine
 | 
						machineRepo repository.Machine
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Machine IMachine = &machineApp{machineRepo: persistence.MachineDao}
 | 
					var MachineApp Machine = &machineAppImpl{machineRepo: persistence.MachineDao}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 分页获取机器信息列表
 | 
					// 分页获取机器信息列表
 | 
				
			||||||
func (m *machineApp) GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult {
 | 
					func (m *machineAppImpl) GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
	return m.machineRepo.GetMachineList(condition, pageParam, toEntity, orderBy...)
 | 
						return m.machineRepo.GetMachineList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据条件获取机器信息
 | 
					// 根据条件获取机器信息
 | 
				
			||||||
func (m *machineApp) Save(entity *entity.Machine) {
 | 
					func (m *machineAppImpl) Save(me *entity.Machine) {
 | 
				
			||||||
	biz.ErrIsNil(machine.TestConn(entity), "该机器无法连接")
 | 
						biz.ErrIsNilAppendErr(machine.TestConn(me), "该机器无法连接: %s")
 | 
				
			||||||
	if entity.Id != 0 {
 | 
					
 | 
				
			||||||
		m.machineRepo.UpdateById(entity)
 | 
						oldMachine := &entity.Machine{Ip: me.Ip, Port: me.Port, Username: me.Username}
 | 
				
			||||||
 | 
						err := m.GetMachine(oldMachine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if me.Id != 0 {
 | 
				
			||||||
 | 
							// 如果存在该库,则校验修改的库是否为该库
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								biz.IsTrue(oldMachine.Id == me.Id, "该机器信息已存在")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// 关闭连接
 | 
				
			||||||
 | 
							machine.Close(me.Id)
 | 
				
			||||||
 | 
							m.machineRepo.UpdateById(me)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		m.machineRepo.Create(entity)
 | 
							biz.IsTrue(err != nil, "该机器信息已存在")
 | 
				
			||||||
 | 
							m.machineRepo.Create(me)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据条件获取机器信息
 | 
					// 根据条件获取机器信息
 | 
				
			||||||
func (m *machineApp) GetMachine(condition *entity.Machine, cols ...string) error {
 | 
					func (m *machineAppImpl) Delete(id uint64) {
 | 
				
			||||||
 | 
						// 关闭连接
 | 
				
			||||||
 | 
						machine.Close(id)
 | 
				
			||||||
 | 
						model.Tx(
 | 
				
			||||||
 | 
							func(db *gorm.DB) error {
 | 
				
			||||||
 | 
								// 删除machine表信息
 | 
				
			||||||
 | 
								return db.Delete(new(entity.Machine), "id = ?", id).Error
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							func(db *gorm.DB) error {
 | 
				
			||||||
 | 
								// 删除machine_file
 | 
				
			||||||
 | 
								machineFile := &entity.MachineFile{MachineId: id}
 | 
				
			||||||
 | 
								return db.Where(machineFile).Delete(machineFile).Error
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							func(db *gorm.DB) error {
 | 
				
			||||||
 | 
								// 删除machine_script
 | 
				
			||||||
 | 
								machineScript := &entity.MachineScript{MachineId: id}
 | 
				
			||||||
 | 
								return db.Where(machineScript).Delete(machineScript).Error
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据条件获取机器信息
 | 
				
			||||||
 | 
					func (m *machineAppImpl) GetMachine(condition *entity.Machine, cols ...string) error {
 | 
				
			||||||
	return m.machineRepo.GetMachine(condition, cols...)
 | 
						return m.machineRepo.GetMachine(condition, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *machineApp) GetById(id uint64, cols ...string) *entity.Machine {
 | 
					func (m *machineAppImpl) GetById(id uint64, cols ...string) *entity.Machine {
 | 
				
			||||||
	return m.machineRepo.GetById(id, cols...)
 | 
						return m.machineRepo.GetById(id, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *machineApp) GetCli(id uint64) *machine.Cli {
 | 
					func (m *machineAppImpl) GetCli(id uint64) *machine.Cli {
 | 
				
			||||||
	cli, err := machine.GetCli(id, func(machineId uint64) *entity.Machine {
 | 
						cli, err := machine.GetCli(id, func(machineId uint64) *entity.Machine {
 | 
				
			||||||
		return m.GetById(machineId)
 | 
							return m.GetById(machineId)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	biz.ErrIsNil(err, "获取客户端错误")
 | 
						biz.ErrIsNilAppendErr(err, "获取客户端错误: %s")
 | 
				
			||||||
	return cli
 | 
						return cli
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,9 +15,9 @@ import (
 | 
				
			|||||||
	"github.com/pkg/sftp"
 | 
						"github.com/pkg/sftp"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IMachineFile interface {
 | 
					type MachineFile interface {
 | 
				
			||||||
	// 分页获取机器文件信息列表
 | 
						// 分页获取机器文件信息列表
 | 
				
			||||||
	GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult
 | 
						GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据条件获取
 | 
						// 根据条件获取
 | 
				
			||||||
	GetMachineFile(condition *entity.MachineFile, cols ...string) error
 | 
						GetMachineFile(condition *entity.MachineFile, cols ...string) error
 | 
				
			||||||
@@ -47,34 +47,34 @@ type IMachineFile interface {
 | 
				
			|||||||
	RemoveFile(fileId uint64, path string)
 | 
						RemoveFile(fileId uint64, path string)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type machineFileApp struct {
 | 
					type machineFileAppImpl struct {
 | 
				
			||||||
	machineFileRepo repository.MachineFile
 | 
						machineFileRepo repository.MachineFile
 | 
				
			||||||
	machineRepo     repository.Machine
 | 
						machineRepo     repository.Machine
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 实现类单例
 | 
					// 实现类单例
 | 
				
			||||||
var MachineFile IMachineFile = &machineFileApp{
 | 
					var MachineFileApp MachineFile = &machineFileAppImpl{
 | 
				
			||||||
	machineRepo:     persistence.MachineDao,
 | 
						machineRepo:     persistence.MachineDao,
 | 
				
			||||||
	machineFileRepo: persistence.MachineFileDao,
 | 
						machineFileRepo: persistence.MachineFileDao,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 分页获取机器脚本信息列表
 | 
					// 分页获取机器脚本信息列表
 | 
				
			||||||
func (m *machineFileApp) GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult {
 | 
					func (m *machineFileAppImpl) GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
	return m.machineFileRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
 | 
						return m.machineFileRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据条件获取
 | 
					// 根据条件获取
 | 
				
			||||||
func (m *machineFileApp) GetMachineFile(condition *entity.MachineFile, cols ...string) error {
 | 
					func (m *machineFileAppImpl) GetMachineFile(condition *entity.MachineFile, cols ...string) error {
 | 
				
			||||||
	return m.machineFileRepo.GetMachineFile(condition, cols...)
 | 
						return m.machineFileRepo.GetMachineFile(condition, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据id获取
 | 
					// 根据id获取
 | 
				
			||||||
func (m *machineFileApp) GetById(id uint64, cols ...string) *entity.MachineFile {
 | 
					func (m *machineFileAppImpl) GetById(id uint64, cols ...string) *entity.MachineFile {
 | 
				
			||||||
	return m.machineFileRepo.GetById(id, cols...)
 | 
						return m.machineFileRepo.GetById(id, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 保存机器文件配置
 | 
					// 保存机器文件配置
 | 
				
			||||||
func (m *machineFileApp) Save(entity *entity.MachineFile) {
 | 
					func (m *machineFileAppImpl) Save(entity *entity.MachineFile) {
 | 
				
			||||||
	biz.NotNil(m.machineRepo.GetById(entity.MachineId, "Name"), "该机器不存在")
 | 
						biz.NotNil(m.machineRepo.GetById(entity.MachineId, "Name"), "该机器不存在")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if entity.Id != 0 {
 | 
						if entity.Id != 0 {
 | 
				
			||||||
@@ -85,11 +85,11 @@ func (m *machineFileApp) Save(entity *entity.MachineFile) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据id删除
 | 
					// 根据id删除
 | 
				
			||||||
func (m *machineFileApp) Delete(id uint64) {
 | 
					func (m *machineFileAppImpl) Delete(id uint64) {
 | 
				
			||||||
	m.machineFileRepo.Delete(id)
 | 
						m.machineFileRepo.Delete(id)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *machineFileApp) ReadDir(fid uint64, path string) []fs.FileInfo {
 | 
					func (m *machineFileAppImpl) ReadDir(fid uint64, path string) []fs.FileInfo {
 | 
				
			||||||
	path, machineId := m.checkAndReturnPathMid(fid, path)
 | 
						path, machineId := m.checkAndReturnPathMid(fid, path)
 | 
				
			||||||
	if !strings.HasSuffix(path, "/") {
 | 
						if !strings.HasSuffix(path, "/") {
 | 
				
			||||||
		path = path + "/"
 | 
							path = path + "/"
 | 
				
			||||||
@@ -101,7 +101,7 @@ func (m *machineFileApp) ReadDir(fid uint64, path string) []fs.FileInfo {
 | 
				
			|||||||
	return fis
 | 
						return fis
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *machineFileApp) ReadFile(fileId uint64, path string) ([]byte, fs.FileInfo) {
 | 
					func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) ([]byte, fs.FileInfo) {
 | 
				
			||||||
	path, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
						path, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
				
			||||||
	sftpCli := m.getSftpCli(machineId)
 | 
						sftpCli := m.getSftpCli(machineId)
 | 
				
			||||||
	// 读取文件内容
 | 
						// 读取文件内容
 | 
				
			||||||
@@ -119,7 +119,7 @@ func (m *machineFileApp) ReadFile(fileId uint64, path string) ([]byte, fs.FileIn
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 写文件内容
 | 
					// 写文件内容
 | 
				
			||||||
func (m *machineFileApp) WriteFileContent(fileId uint64, path string, content []byte) {
 | 
					func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte) {
 | 
				
			||||||
	_, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
						_, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sftpCli := m.getSftpCli(machineId)
 | 
						sftpCli := m.getSftpCli(machineId)
 | 
				
			||||||
@@ -133,7 +133,7 @@ func (m *machineFileApp) WriteFileContent(fileId uint64, path string, content []
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 上传文件
 | 
					// 上传文件
 | 
				
			||||||
func (m *machineFileApp) UploadFile(fileId uint64, path, filename string, content []byte) {
 | 
					func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, content []byte) {
 | 
				
			||||||
	path, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
						path, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
				
			||||||
	if !strings.HasSuffix(path, "/") {
 | 
						if !strings.HasSuffix(path, "/") {
 | 
				
			||||||
		path = path + "/"
 | 
							path = path + "/"
 | 
				
			||||||
@@ -148,7 +148,7 @@ func (m *machineFileApp) UploadFile(fileId uint64, path, filename string, conten
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 删除文件
 | 
					// 删除文件
 | 
				
			||||||
func (m *machineFileApp) RemoveFile(fileId uint64, path string) {
 | 
					func (m *machineFileAppImpl) RemoveFile(fileId uint64, path string) {
 | 
				
			||||||
	path, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
						path, machineId := m.checkAndReturnPathMid(fileId, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sftpCli := m.getSftpCli(machineId)
 | 
						sftpCli := m.getSftpCli(machineId)
 | 
				
			||||||
@@ -164,12 +164,12 @@ func (m *machineFileApp) RemoveFile(fileId uint64, path string) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取sftp client
 | 
					// 获取sftp client
 | 
				
			||||||
func (m *machineFileApp) getSftpCli(machineId uint64) *sftp.Client {
 | 
					func (m *machineFileAppImpl) getSftpCli(machineId uint64) *sftp.Client {
 | 
				
			||||||
	return Machine.GetCli(machineId).GetSftpCli()
 | 
						return MachineApp.GetCli(machineId).GetSftpCli()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 校验并返回实际可访问的文件path
 | 
					// 校验并返回实际可访问的文件path
 | 
				
			||||||
func (m *machineFileApp) checkAndReturnPathMid(fid uint64, inputPath string) (string, uint64) {
 | 
					func (m *machineFileAppImpl) checkAndReturnPathMid(fid uint64, inputPath string) (string, uint64) {
 | 
				
			||||||
	biz.IsTrue(fid != 0, "文件id不能为空")
 | 
						biz.IsTrue(fid != 0, "文件id不能为空")
 | 
				
			||||||
	mf := m.GetById(uint64(fid))
 | 
						mf := m.GetById(uint64(fid))
 | 
				
			||||||
	biz.NotNil(mf, "文件不存在")
 | 
						biz.NotNil(mf, "文件不存在")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,9 +8,9 @@ import (
 | 
				
			|||||||
	"mayfly-go/server/devops/infrastructure/persistence"
 | 
						"mayfly-go/server/devops/infrastructure/persistence"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IMachineScript interface {
 | 
					type MachineScript interface {
 | 
				
			||||||
	// 分页获取机器脚本信息列表
 | 
						// 分页获取机器脚本信息列表
 | 
				
			||||||
	GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult
 | 
						GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据条件获取
 | 
						// 根据条件获取
 | 
				
			||||||
	GetMachineScript(condition *entity.MachineScript, cols ...string) error
 | 
						GetMachineScript(condition *entity.MachineScript, cols ...string) error
 | 
				
			||||||
@@ -23,7 +23,7 @@ type IMachineScript interface {
 | 
				
			|||||||
	Delete(id uint64)
 | 
						Delete(id uint64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type machineScriptApp struct {
 | 
					type machineScriptAppImpl struct {
 | 
				
			||||||
	machineScriptRepo repository.MachineScript
 | 
						machineScriptRepo repository.MachineScript
 | 
				
			||||||
	machineRepo       repository.Machine
 | 
						machineRepo       repository.Machine
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -31,27 +31,27 @@ type machineScriptApp struct {
 | 
				
			|||||||
const Common_Script_Machine_Id = 9999999
 | 
					const Common_Script_Machine_Id = 9999999
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 实现类单例
 | 
					// 实现类单例
 | 
				
			||||||
var MachineScript IMachineScript = &machineScriptApp{
 | 
					var MachineScriptApp MachineScript = &machineScriptAppImpl{
 | 
				
			||||||
	machineRepo:       persistence.MachineDao,
 | 
						machineRepo:       persistence.MachineDao,
 | 
				
			||||||
	machineScriptRepo: persistence.MachineScriptDao}
 | 
						machineScriptRepo: persistence.MachineScriptDao}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 分页获取机器脚本信息列表
 | 
					// 分页获取机器脚本信息列表
 | 
				
			||||||
func (m *machineScriptApp) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult {
 | 
					func (m *machineScriptAppImpl) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
	return m.machineScriptRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
 | 
						return m.machineScriptRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据条件获取
 | 
					// 根据条件获取
 | 
				
			||||||
func (m *machineScriptApp) GetMachineScript(condition *entity.MachineScript, cols ...string) error {
 | 
					func (m *machineScriptAppImpl) GetMachineScript(condition *entity.MachineScript, cols ...string) error {
 | 
				
			||||||
	return m.machineScriptRepo.GetMachineScript(condition, cols...)
 | 
						return m.machineScriptRepo.GetMachineScript(condition, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据id获取
 | 
					// 根据id获取
 | 
				
			||||||
func (m *machineScriptApp) GetById(id uint64, cols ...string) *entity.MachineScript {
 | 
					func (m *machineScriptAppImpl) GetById(id uint64, cols ...string) *entity.MachineScript {
 | 
				
			||||||
	return m.machineScriptRepo.GetById(id, cols...)
 | 
						return m.machineScriptRepo.GetById(id, cols...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 保存机器脚本
 | 
					// 保存机器脚本
 | 
				
			||||||
func (m *machineScriptApp) Save(entity *entity.MachineScript) {
 | 
					func (m *machineScriptAppImpl) Save(entity *entity.MachineScript) {
 | 
				
			||||||
	// 如果机器id不为公共脚本id,则校验机器是否存在
 | 
						// 如果机器id不为公共脚本id,则校验机器是否存在
 | 
				
			||||||
	if machineId := entity.MachineId; machineId != Common_Script_Machine_Id {
 | 
						if machineId := entity.MachineId; machineId != Common_Script_Machine_Id {
 | 
				
			||||||
		biz.NotNil(m.machineRepo.GetById(machineId, "Name"), "该机器不存在")
 | 
							biz.NotNil(m.machineRepo.GetById(machineId, "Name"), "该机器不存在")
 | 
				
			||||||
@@ -65,6 +65,6 @@ func (m *machineScriptApp) Save(entity *entity.MachineScript) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据id删除
 | 
					// 根据id删除
 | 
				
			||||||
func (m *machineScriptApp) Delete(id uint64) {
 | 
					func (m *machineScriptAppImpl) Delete(id uint64) {
 | 
				
			||||||
	m.machineScriptRepo.Delete(id)
 | 
						m.machineScriptRepo.Delete(id)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										95
									
								
								server/devops/application/project_app.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								server/devops/application/project_app.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/infrastructure/persistence"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Project interface {
 | 
				
			||||||
 | 
						// 分页获取项目信息列表
 | 
				
			||||||
 | 
						GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ListProjectByIds(ids []uint64, toEntity interface{}, orderBy ...string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SaveProject(project *entity.Project)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 根据项目id获取所有该项目下的环境信息列表
 | 
				
			||||||
 | 
						ListEnvByProjectId(projectId uint64, listPtr interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 保存项目环境信息
 | 
				
			||||||
 | 
						SaveProjectEnv(projectEnv *entity.ProjectEnv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 根据条件获取项目成员信息
 | 
				
			||||||
 | 
						ListMember(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SaveProjectMember(pm *entity.ProjectMember)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 根据条件获取项目成员信息
 | 
				
			||||||
 | 
						GetMemberPage(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DeleteMember(projectId, accountId uint64)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type projectAppImpl struct {
 | 
				
			||||||
 | 
						projectRepo       repository.Project
 | 
				
			||||||
 | 
						projectEnvRepo    repository.ProjectEnv
 | 
				
			||||||
 | 
						projectMemberRepo repository.ProjectMemeber
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ProjectApp Project = &projectAppImpl{
 | 
				
			||||||
 | 
						projectRepo:       persistence.ProjectRepo,
 | 
				
			||||||
 | 
						projectEnvRepo:    persistence.ProjectEnvRepo,
 | 
				
			||||||
 | 
						projectMemberRepo: persistence.ProjectMemberRepo,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 分页获取项目信息列表
 | 
				
			||||||
 | 
					func (p *projectAppImpl) GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
 | 
						return p.projectRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectAppImpl) ListProjectByIds(ids []uint64, toEntity interface{}, orderBy ...string) {
 | 
				
			||||||
 | 
						p.projectRepo.GetByIdIn(ids, toEntity, orderBy...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectAppImpl) SaveProject(project *entity.Project) {
 | 
				
			||||||
 | 
						if project.Id == 0 {
 | 
				
			||||||
 | 
							p.projectRepo.Save(project)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// 防止误传导致项目名更新
 | 
				
			||||||
 | 
							project.Name = ""
 | 
				
			||||||
 | 
							p.projectRepo.Update(project)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据项目id获取所有该项目下的环境信息列表
 | 
				
			||||||
 | 
					func (p *projectAppImpl) ListEnvByProjectId(projectId uint64, listPtr interface{}) {
 | 
				
			||||||
 | 
						p.projectEnvRepo.ListEnv(&entity.ProjectEnv{ProjectId: projectId}, listPtr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 保存项目环境信息
 | 
				
			||||||
 | 
					func (p *projectAppImpl) SaveProjectEnv(projectEnv *entity.ProjectEnv) {
 | 
				
			||||||
 | 
						p.projectEnvRepo.Save(projectEnv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据条件获取项目成员信息
 | 
				
			||||||
 | 
					func (p *projectAppImpl) ListMember(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string) {
 | 
				
			||||||
 | 
						p.projectMemberRepo.ListMemeber(condition, toEntity, orderBy...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectAppImpl) SaveProjectMember(pm *entity.ProjectMember) {
 | 
				
			||||||
 | 
						pms := new([]entity.ProjectMember)
 | 
				
			||||||
 | 
						p.ListMember(&entity.ProjectMember{ProjectId: pm.ProjectId, AccountId: pm.AccountId}, pms)
 | 
				
			||||||
 | 
						biz.IsTrue(len(*pms) == 0, "该成员已存在")
 | 
				
			||||||
 | 
						p.projectMemberRepo.Save(pm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectAppImpl) GetMemberPage(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
 | 
						return p.projectMemberRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectAppImpl) DeleteMember(projectId, accountId uint64) {
 | 
				
			||||||
 | 
						p.projectMemberRepo.DeleteByPidMid(projectId, accountId)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										146
									
								
								server/devops/application/redis_app.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								server/devops/application/redis_app.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/domain/repository"
 | 
				
			||||||
 | 
						"mayfly-go/server/devops/infrastructure/persistence"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-redis/redis"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Redis interface {
 | 
				
			||||||
 | 
						// 分页获取机器脚本信息列表
 | 
				
			||||||
 | 
						GetPageList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 根据id获取
 | 
				
			||||||
 | 
						GetById(id uint64, cols ...string) *entity.Redis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 根据条件获取
 | 
				
			||||||
 | 
						GetRedisBy(condition *entity.Redis, cols ...string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Save(entity *entity.Redis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 删除数据库信息
 | 
				
			||||||
 | 
						Delete(id uint64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 获取数据库连接实例
 | 
				
			||||||
 | 
						GetRedisInstance(id uint64) *RedisInstance
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type redisAppImpl struct {
 | 
				
			||||||
 | 
						redisRepo repository.Redis
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var RedisApp Redis = &redisAppImpl{
 | 
				
			||||||
 | 
						redisRepo: persistence.RedisDao,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 分页获取机器脚本信息列表
 | 
				
			||||||
 | 
					func (r *redisAppImpl) GetPageList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
 | 
				
			||||||
 | 
						return r.redisRepo.GetRedisList(condition, pageParam, toEntity, orderBy...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据id获取
 | 
				
			||||||
 | 
					func (r *redisAppImpl) GetById(id uint64, cols ...string) *entity.Redis {
 | 
				
			||||||
 | 
						return r.redisRepo.GetById(id, cols...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 根据条件获取
 | 
				
			||||||
 | 
					func (r *redisAppImpl) GetRedisBy(condition *entity.Redis, cols ...string) error {
 | 
				
			||||||
 | 
						return r.redisRepo.GetRedis(condition, cols...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *redisAppImpl) Save(re *entity.Redis) {
 | 
				
			||||||
 | 
						TestRedisConnection(re)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 查找是否存在该库
 | 
				
			||||||
 | 
						oldRedis := &entity.Redis{Host: re.Host, Db: re.Db}
 | 
				
			||||||
 | 
						err := r.GetRedisBy(oldRedis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if re.Id == 0 {
 | 
				
			||||||
 | 
							biz.IsTrue(err != nil, "该库已存在")
 | 
				
			||||||
 | 
							r.redisRepo.Insert(re)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// 如果存在该库,则校验修改的库是否为该库
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								biz.IsTrue(re.Id == re.Id, "该库已存在")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// 先关闭数据库连接
 | 
				
			||||||
 | 
							CloseRedis(re.Id)
 | 
				
			||||||
 | 
							r.redisRepo.Update(re)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 删除Redis信息
 | 
				
			||||||
 | 
					func (r *redisAppImpl) Delete(id uint64) {
 | 
				
			||||||
 | 
						CloseRedis(id)
 | 
				
			||||||
 | 
						r.redisRepo.Delete(id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取数据库连接实例
 | 
				
			||||||
 | 
					func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
 | 
				
			||||||
 | 
						// Id不为0,则为需要缓存
 | 
				
			||||||
 | 
						needCache := id != 0
 | 
				
			||||||
 | 
						if needCache {
 | 
				
			||||||
 | 
							load, ok := redisCache.Load(id)
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								return load.(*RedisInstance)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 缓存不存在,则回调获取redis信息
 | 
				
			||||||
 | 
						re := r.GetById(id)
 | 
				
			||||||
 | 
						biz.NotNil(re, "redis信息不存在")
 | 
				
			||||||
 | 
						rcli := redis.NewClient(&redis.Options{
 | 
				
			||||||
 | 
							Addr:     re.Host,
 | 
				
			||||||
 | 
							Password: re.Password, // no password set
 | 
				
			||||||
 | 
							DB:       re.Db,       // use default DB
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						// 测试连接
 | 
				
			||||||
 | 
						_, e := rcli.Ping().Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(e, "redis连接失败: %s")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ri := &RedisInstance{Id: id, Cli: rcli}
 | 
				
			||||||
 | 
						if needCache {
 | 
				
			||||||
 | 
							redisCache.LoadOrStore(re.Id, ri)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ri
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var redisCache sync.Map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// redis实例
 | 
				
			||||||
 | 
					type RedisInstance struct {
 | 
				
			||||||
 | 
						Id  uint64
 | 
				
			||||||
 | 
						Cli *redis.Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 关闭redis连接
 | 
				
			||||||
 | 
					func CloseRedis(id uint64) {
 | 
				
			||||||
 | 
						if load, ok := redisCache.Load(id); ok {
 | 
				
			||||||
 | 
							load.(*RedisInstance).Cli.Close()
 | 
				
			||||||
 | 
							redisCache.Delete(id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRedisConnection(re *entity.Redis) {
 | 
				
			||||||
 | 
						rcli := redis.NewClient(&redis.Options{
 | 
				
			||||||
 | 
							Addr:     re.Host,
 | 
				
			||||||
 | 
							Password: re.Password, // no password set
 | 
				
			||||||
 | 
							DB:       re.Db,       // use default DB
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						defer rcli.Close()
 | 
				
			||||||
 | 
						// 测试连接
 | 
				
			||||||
 | 
						_, e := rcli.Ping().Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(e, "Redis连接失败: %s")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *RedisInstance) Scan(cursor uint64, match string, count int64) ([]string, uint64) {
 | 
				
			||||||
 | 
						keys, newcursor, err := r.Cli.Scan(cursor, match, count).Result()
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, "scan失败: %s")
 | 
				
			||||||
 | 
						return keys, newcursor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,4 +15,8 @@ type Db struct {
 | 
				
			|||||||
	Username  string `orm:"column(username)" json:"username"`
 | 
						Username  string `orm:"column(username)" json:"username"`
 | 
				
			||||||
	Password  string `orm:"column(password)" json:"-"`
 | 
						Password  string `orm:"column(password)" json:"-"`
 | 
				
			||||||
	Database  string `orm:"column(database)" json:"database"`
 | 
						Database  string `orm:"column(database)" json:"database"`
 | 
				
			||||||
 | 
						ProjectId uint64
 | 
				
			||||||
 | 
						Project   string
 | 
				
			||||||
 | 
						EnvId     uint64
 | 
				
			||||||
 | 
						Env       string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,9 @@ import "mayfly-go/base/model"
 | 
				
			|||||||
type MachineScript struct {
 | 
					type MachineScript struct {
 | 
				
			||||||
	model.Model
 | 
						model.Model
 | 
				
			||||||
	Name        string `json:"name"`
 | 
						Name        string `json:"name"`
 | 
				
			||||||
	// 机器id
 | 
						MachineId   uint64 `json:"machineId"` // 机器id
 | 
				
			||||||
	MachineId uint64 `json:"machineId"`
 | 
					 | 
				
			||||||
	Type        int    `json:"type"`
 | 
						Type        int    `json:"type"`
 | 
				
			||||||
	// 脚本内容
 | 
						Description string `json:"description"` // 脚本描述
 | 
				
			||||||
	Description string `json:"description"`
 | 
						Params      string `json:"params"`      // 参数列表json
 | 
				
			||||||
	// 脚本内容
 | 
						Script      string `json:"script"`      // 脚本内容
 | 
				
			||||||
	Script string `json:"script"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								server/devops/domain/entity/project.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								server/devops/domain/entity/project.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/base/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 项目
 | 
				
			||||||
 | 
					type Project struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
						Name   string `json:"name"`   // 项目名
 | 
				
			||||||
 | 
						Remark string `json:"remark"` // 备注说明
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								server/devops/domain/entity/project_env.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								server/devops/domain/entity/project_env.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/base/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 项目环境
 | 
				
			||||||
 | 
					type ProjectEnv struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
						Name      string `json:"name"`      // 环境名
 | 
				
			||||||
 | 
						ProjectId uint64 `json:"projectId"` // 项目id
 | 
				
			||||||
 | 
						Remark    string `json:"remark"`    // 备注说明
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								server/devops/domain/entity/project_member.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								server/devops/domain/entity/project_member.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/base/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 项目成员,用于对项目下组件的访问控制
 | 
				
			||||||
 | 
					type ProjectMember struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
						AccountId uint64 `json:"accountId"` // 账号
 | 
				
			||||||
 | 
						Username  string `json:"username"`  // 账号用户名
 | 
				
			||||||
 | 
						ProjectId uint64 `json:"projectId"` // 项目id
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								server/devops/domain/entity/redis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								server/devops/domain/entity/redis.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Redis struct {
 | 
				
			||||||
 | 
						model.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Host      string `orm:"column(host)" json:"host"`
 | 
				
			||||||
 | 
						Password  string `orm:"column(password)" json:"-"`
 | 
				
			||||||
 | 
						Db        int    `orm:"column(database)" json:"db"`
 | 
				
			||||||
 | 
						ProjectId uint64
 | 
				
			||||||
 | 
						Project   string
 | 
				
			||||||
 | 
						EnvId     uint64
 | 
				
			||||||
 | 
						Env       string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,11 +7,17 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Db interface {
 | 
					type Db interface {
 | 
				
			||||||
	// 分页获取机器信息列表
 | 
						// 分页获取机器信息列表
 | 
				
			||||||
	GetDbList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) model.PageResult
 | 
						GetDbList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据条件获取账号信息
 | 
						// 根据条件获取账号信息
 | 
				
			||||||
	GetDb(condition *entity.Db, cols ...string) error
 | 
						GetDb(condition *entity.Db, cols ...string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据id获取
 | 
						// 根据id获取
 | 
				
			||||||
	GetById(id uint64, cols ...string) *entity.Db
 | 
						GetById(id uint64, cols ...string) *entity.Db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Insert(db *entity.Db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Update(db *entity.Db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Delete(id uint64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								server/devops/domain/repository/db_sql.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/devops/domain/repository/db_sql.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DbSql interface {
 | 
				
			||||||
 | 
						DeleteBy(condition *entity.DbSql)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user