重构: 死代码清理 + 拷贝优化 + 滚动条修复
This commit is contained in:
@@ -1,279 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"u-desk/internal/crypto"
|
||||
"u-desk/internal/dbclient"
|
||||
"u-desk/internal/storage/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ConnectionService 连接管理服务
|
||||
type ConnectionService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewConnectionService 创建连接服务
|
||||
func NewConnectionService() (*ConnectionService, error) {
|
||||
db := GetDB()
|
||||
if db == nil {
|
||||
// 尝试重新初始化
|
||||
var err error
|
||||
db, err = Init()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("数据库初始化失败: %v", err)
|
||||
}
|
||||
}
|
||||
return &ConnectionService{db: db}, nil
|
||||
}
|
||||
|
||||
// SaveConnection 保存连接配置
|
||||
func (s *ConnectionService) SaveConnection(conn *models.DbConnection) error {
|
||||
if conn.Name == "" {
|
||||
return fmt.Errorf("连接名称不能为空")
|
||||
}
|
||||
if conn.Type == "" {
|
||||
return fmt.Errorf("数据库类型不能为空")
|
||||
}
|
||||
if conn.Host == "" {
|
||||
return fmt.Errorf("主机地址不能为空")
|
||||
}
|
||||
|
||||
// 检查名称是否重复(排除当前记录)
|
||||
var count int64
|
||||
query := s.db.Model(&models.DbConnection{}).Where("name = ?", conn.Name)
|
||||
if conn.ID > 0 {
|
||||
query = query.Where("id != ?", conn.ID)
|
||||
}
|
||||
query.Count(&count)
|
||||
if count > 0 {
|
||||
return fmt.Errorf("连接名称已存在")
|
||||
}
|
||||
|
||||
if conn.ID > 0 {
|
||||
// 更新模式
|
||||
updateData := map[string]interface{}{
|
||||
"name": conn.Name,
|
||||
"type": conn.Type,
|
||||
"host": conn.Host,
|
||||
"port": conn.Port,
|
||||
"username": conn.Username,
|
||||
"database": conn.Database,
|
||||
"options": conn.Options,
|
||||
"visible_databases": conn.VisibleDatabases,
|
||||
}
|
||||
|
||||
// 如果提供了新密码,加密后更新
|
||||
if conn.Password != "" {
|
||||
encrypted, err := crypto.EncryptPassword(conn.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
updateData["password"] = encrypted
|
||||
}
|
||||
// 如果密码为空,不更新密码字段(保留原密码)
|
||||
|
||||
return s.db.Model(&models.DbConnection{}).Where("id = ?", conn.ID).Updates(updateData).Error
|
||||
}
|
||||
|
||||
// 新增模式 - 必须提供密码
|
||||
if conn.Password == "" {
|
||||
return fmt.Errorf("新增连接时密码不能为空")
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
encrypted, err := crypto.EncryptPassword(conn.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
conn.Password = encrypted
|
||||
|
||||
return s.db.Create(conn).Error
|
||||
}
|
||||
|
||||
// ListConnections 获取连接列表
|
||||
func (s *ConnectionService) ListConnections() ([]models.DbConnection, error) {
|
||||
var connections []models.DbConnection
|
||||
err := s.db.Order("created_at DESC").Find(&connections).Error
|
||||
return connections, err
|
||||
}
|
||||
|
||||
// GetConnection 获取连接详情
|
||||
func (s *ConnectionService) GetConnection(id uint) (*models.DbConnection, error) {
|
||||
var conn models.DbConnection
|
||||
err := s.db.First(&conn, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &conn, nil
|
||||
}
|
||||
|
||||
// DeleteConnection 删除连接配置
|
||||
func (s *ConnectionService) DeleteConnection(id uint) error {
|
||||
var conn models.DbConnection
|
||||
if err := s.db.First(&conn, id).Error; err != nil {
|
||||
return nil // 连接不存在视为成功
|
||||
}
|
||||
|
||||
// 使用事务删除
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 清理关联数据
|
||||
tx.Where("connection_id = ?", id).Delete(&models.SqlResultHistory{})
|
||||
tx.Where("connection_id = ?", id).Delete(&models.SqlTab{})
|
||||
|
||||
// 删除连接
|
||||
if err := tx.Delete(&conn).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 关闭连接池
|
||||
dbclient.GetPool().CloseConnection(id, conn.Type)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// resolvePassword 解析密码(编辑模式下从已保存连接中获取)
|
||||
func (s *ConnectionService) resolvePassword(id uint, password string) (string, error) {
|
||||
if id > 0 && password == "" {
|
||||
conn, err := s.GetConnection(id)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取连接信息失败: %v", err)
|
||||
}
|
||||
decryptPassword, err := crypto.DecryptPassword(conn.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("密码解密失败: %v", err)
|
||||
}
|
||||
return decryptPassword, nil
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
// parseMongoOptions 解析 MongoDB 连接选项
|
||||
func parseMongoOptions(options string) (authSource, authMechanism string) {
|
||||
if options == "" {
|
||||
return "", ""
|
||||
}
|
||||
var opts map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(options), &opts); err != nil {
|
||||
return "", ""
|
||||
}
|
||||
authSource, _ = opts["authSource"].(string)
|
||||
authMechanism, _ = opts["authMechanism"].(string)
|
||||
return authSource, authMechanism
|
||||
}
|
||||
|
||||
// TestConnection 测试连接(需要根据类型调用不同的测试方法)
|
||||
func (s *ConnectionService) TestConnection(conn *models.DbConnection) error {
|
||||
password, err := crypto.DecryptPassword(conn.Password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码解密失败: %v", err)
|
||||
}
|
||||
|
||||
authSource, authMechanism := parseMongoOptions(conn.Options)
|
||||
|
||||
return s.testConnectionByType(conn.Type, conn.Host, conn.Port, conn.Username, password, conn.Database, authSource, authMechanism)
|
||||
}
|
||||
|
||||
// testConnectionByType 根据类型调用对应的测试方法
|
||||
func (s *ConnectionService) testConnectionByType(dbType, host string, port int, username, password, database, authSource, authMechanism string) error {
|
||||
switch dbType {
|
||||
case "mysql":
|
||||
return testMySQLConnection(host, port, username, password, database)
|
||||
case "redis":
|
||||
return testRedisConnection(host, port, password)
|
||||
case "mongo":
|
||||
return testMongoConnection(host, port, username, password, database, authSource, authMechanism)
|
||||
default:
|
||||
return fmt.Errorf("不支持的数据库类型: %s", dbType)
|
||||
}
|
||||
}
|
||||
|
||||
// testMySQLConnection 测试 MySQL 连接
|
||||
func testMySQLConnection(host string, port int, username, password, database string) error {
|
||||
return dbclient.TestMySQLConnection(host, port, username, password, database)
|
||||
}
|
||||
|
||||
// testRedisConnection 测试 Redis 连接
|
||||
func testRedisConnection(host string, port int, password string) error {
|
||||
return dbclient.TestRedisConnection(host, port, password)
|
||||
}
|
||||
|
||||
// testMongoConnection 测试 MongoDB 连接
|
||||
func testMongoConnection(host string, port int, username, password, database, authSource, authMechanism string) error {
|
||||
return dbclient.TestMongoConnectionWithOptions(host, port, username, password, database, authSource, authMechanism)
|
||||
}
|
||||
|
||||
// TestConnectionWithParams 使用参数测试连接(不保存数据)
|
||||
func (s *ConnectionService) TestConnectionWithParams(dbType, host string, port int, username, password, database, options string, id uint) error {
|
||||
password, err := s.resolvePassword(id, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource, authMechanism := parseMongoOptions(options)
|
||||
return s.testConnectionByType(dbType, host, port, username, password, database, authSource, authMechanism)
|
||||
}
|
||||
|
||||
// LoadAllDatabases 加载全部数据库列表
|
||||
func (s *ConnectionService) LoadAllDatabases(dbType, host string, port int, username, password, database, options string, id uint) ([]string, error) {
|
||||
password, err := s.resolvePassword(id, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authSource, authMechanism := parseMongoOptions(options)
|
||||
|
||||
// 根据类型加载数据库列表
|
||||
switch dbType {
|
||||
case "mysql":
|
||||
return loadMySQLDatabases(host, port, username, password, database)
|
||||
case "mongo":
|
||||
return loadMongoDatabasesWithOptions(host, port, username, password, database, authSource, authMechanism)
|
||||
case "redis":
|
||||
// Redis 没有数据库概念,返回空列表
|
||||
return []string{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的数据库类型: %s", dbType)
|
||||
}
|
||||
}
|
||||
|
||||
// loadMySQLDatabases 加载 MySQL 数据库列表
|
||||
func loadMySQLDatabases(host string, port int, username, password, defaultDatabase string) ([]string, error) {
|
||||
config := &dbclient.MySQLConfig{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Database: defaultDatabase,
|
||||
}
|
||||
client, err := dbclient.NewMySQLClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
return client.ListDatabases(context.Background())
|
||||
}
|
||||
|
||||
// loadMongoDatabasesWithOptions 加载 MongoDB 数据库列表(使用解析后的选项)
|
||||
func loadMongoDatabasesWithOptions(host string, port int, username, password, defaultDatabase, authSource, authMechanism string) ([]string, error) {
|
||||
mongoConfig := &dbclient.MongoConfig{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Database: defaultDatabase,
|
||||
AuthSource: authSource,
|
||||
AuthMechanism: authMechanism,
|
||||
}
|
||||
client, err := dbclient.NewMongoClient(mongoConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
return client.ListDatabases(context.Background())
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SqlFile SQL 文件记录
|
||||
type SqlFile struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"type:varchar(200);not null" json:"name"` // 文件名
|
||||
Path string `gorm:"type:varchar(500);not null;uniqueIndex" json:"path"` // 文件路径
|
||||
Content string `gorm:"type:text" json:"content"` // 文件内容
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (SqlFile) TableName() string {
|
||||
return "sql_file"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// Version 版本信息
|
||||
type Version struct {
|
||||
ID int `gorm:"primaryKey" json:"id"` // 主键ID
|
||||
Version string `gorm:"type:varchar(20);not null;uniqueIndex" json:"version"` // 版本号(语义化版本,如1.0.0)
|
||||
DownloadURL string `gorm:"type:varchar(500)" json:"download_url"` // 下载地址(更新包下载URL)
|
||||
Changelog string `gorm:"type:text" json:"changelog"` // 更新日志(Markdown格式)
|
||||
ForceUpdate int `gorm:"type:tinyint;not null;default:0" json:"force_update"` // 是否强制更新(1:是 0:否)
|
||||
ReleaseDate *time.Time `gorm:"type:date" json:"release_date"` // 发布日期
|
||||
CreatedAt time.Time `gorm:"autoCreateTime:false" json:"created_at"` // 创建时间(由程序设置)
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime:false" json:"updated_at"` // 更新时间(由程序设置)
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Version) TableName() string {
|
||||
return "sys_version"
|
||||
}
|
||||
@@ -5,17 +5,13 @@ import (
|
||||
"u-desk/internal/storage"
|
||||
"u-desk/internal/storage/models"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ResultRepository interface {
|
||||
Save(connectionID uint, database, sql string, resultType string, data interface{}, columns []string, rowsAffected int, executionTime int64) (*models.SqlResultHistory, error)
|
||||
FindByID(id uint) (*models.SqlResultHistory, error)
|
||||
FindByConnection(connectionID uint, limit int) ([]models.SqlResultHistory, error)
|
||||
Search(connectionID *uint, keyword string, limit, offset int) ([]models.SqlResultHistory, int64, error)
|
||||
Delete(id uint) error
|
||||
DeleteByConnection(connectionID uint) error
|
||||
DeleteOld(keepDays int) error
|
||||
}
|
||||
|
||||
type resultRepository struct {
|
||||
@@ -61,15 +57,6 @@ func (r *resultRepository) FindByID(id uint) (*models.SqlResultHistory, error) {
|
||||
return &history, err
|
||||
}
|
||||
|
||||
func (r *resultRepository) FindByConnection(connectionID uint, limit int) ([]models.SqlResultHistory, error) {
|
||||
var histories []models.SqlResultHistory
|
||||
query := r.db.Where("connection_id = ?", connectionID).Order("created_at DESC")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
return histories, query.Find(&histories).Error
|
||||
}
|
||||
|
||||
func (r *resultRepository) Search(connectionID *uint, keyword string, limit, offset int) ([]models.SqlResultHistory, int64, error) {
|
||||
query := r.db.Model(&models.SqlResultHistory{})
|
||||
|
||||
@@ -101,10 +88,3 @@ func (r *resultRepository) Delete(id uint) error {
|
||||
return r.db.Delete(&models.SqlResultHistory{}, id).Error
|
||||
}
|
||||
|
||||
func (r *resultRepository) DeleteByConnection(connectionID uint) error {
|
||||
return r.db.Where("connection_id = ?", connectionID).Delete(&models.SqlResultHistory{}).Error
|
||||
}
|
||||
|
||||
func (r *resultRepository) DeleteOld(keepDays int) error {
|
||||
return r.db.Where("created_at < ?", time.Now().AddDate(0, 0, -keepDays)).Delete(&models.SqlResultHistory{}).Error
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ type TabRepository interface {
|
||||
SaveAll(tabs []models.SqlTab) error
|
||||
FindAll() ([]models.SqlTab, error)
|
||||
Delete(id uint) error
|
||||
DeleteAll() error
|
||||
}
|
||||
|
||||
type tabRepository struct {
|
||||
@@ -50,6 +49,3 @@ func (r *tabRepository) Delete(id uint) error {
|
||||
return r.db.Delete(&models.SqlTab{}, id).Error
|
||||
}
|
||||
|
||||
func (r *tabRepository) DeleteAll() error {
|
||||
return r.db.Where("1=1").Delete(&models.SqlTab{}).Error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user