feat: 资源操作新增右键菜单操作等

This commit is contained in:
meilin.huang
2025-09-17 21:23:12 +08:00
parent 54d0688571
commit c4d52ce47a
12 changed files with 173 additions and 102 deletions

View File

@@ -6,11 +6,11 @@ require (
gitee.com/chunanyong/dm v1.8.20
gitee.com/liuzongyang/libpq v1.10.11
github.com/antlr4-go/antlr/v4 v4.13.1
github.com/docker/docker v28.3.3+incompatible
github.com/docker/docker v28.4.0+incompatible
github.com/docker/go-connections v0.6.0
github.com/gin-gonic/gin v1.10.1
github.com/glebarez/sqlite v1.11.0
github.com/go-gormigrate/gormigrate/v2 v2.1.4
github.com/go-gormigrate/gormigrate/v2 v2.1.5
github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
@@ -26,11 +26,11 @@ require (
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.9
github.com/pquerna/otp v1.5.0
github.com/redis/go-redis/v9 v9.13.0
github.com/redis/go-redis/v9 v9.14.0
github.com/robfig/cron/v3 v3.0.1 //
github.com/sijms/go-ora/v2 v2.9.0
github.com/spf13/cast v1.9.2
github.com/stretchr/testify v1.10.0
github.com/spf13/cast v1.10.0
github.com/stretchr/testify v1.11.1
github.com/tidwall/gjson v1.18.0
github.com/veops/go-ansiterm v0.0.5
go.mongodb.org/mongo-driver/v2 v2.3.0 // mongo
@@ -41,7 +41,7 @@ require (
gopkg.in/yaml.v3 v3.0.1
// gorm
gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.30.5
gorm.io/gorm v1.31.0
)
require (
@@ -69,7 +69,6 @@ require (
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect

View File

@@ -94,7 +94,7 @@ func (app *dataSyncAppImpl) Delete(ctx context.Context, id uint64) error {
func (app *dataSyncAppImpl) Run(ctx context.Context, id uint64) error {
if app.IsRunning(id) {
logx.Warn("[%d] the db sync task is running...", id)
logx.Warnf("[%d] the db sync task is running...", id)
return nil
}
@@ -136,7 +136,7 @@ func (app *dataSyncAppImpl) Run(ctx context.Context, id uint64) error {
logx.ErrorfContext(ctx, "data source connection unavailable: %s", err.Error())
return
}
srcConn, err := app.dbApp.GetDbConn(ctx, uint64(task.SrcDbId), task.SrcDbName)
srcConn, err := app.dbApp.GetDbConn(context.Background(), uint64(task.SrcDbId), task.SrcDbName)
if err != nil {
logx.ErrorfContext(ctx, "failed to connect to the source database: %s", err.Error())
return
@@ -381,6 +381,7 @@ func (app *dataSyncAppImpl) InitCronJob() {
if err := app.CursorByCond(&entity.DataSyncTaskQuery{Status: entity.DataSyncTaskStatusEnable}, func(dst *entity.DataSyncTask) error {
app.addCronJob(contextx.NewTraceId(), dst)
app.MarkStop(dst.Id)
return nil
}); err != nil {
logx.ErrorTrace("the db data sync task failed to initialize: %v", err)

View File

@@ -7,6 +7,7 @@ import (
"encoding/hex"
"fmt"
"mayfly-go/pkg/utils/collx"
"strconv"
"strings"
"time"
@@ -111,6 +112,12 @@ func ClearNumScale(column *Column) {
column.CharMaxLength = 0
}
func ClearNumPrecision(column *Column) {
column.NumScale = 0
column.NumPrecision = 0
column.CharMaxLength = 0
}
// DataType 数据类型, 对应于go类型如int int64等。可自定义其他类型
type DataType struct {
Name string // 类型名
@@ -173,7 +180,13 @@ func SQLValueString(val any) string {
return fmt.Sprintf("%v", val)
}
return fmt.Sprintf("'%s'", strings.ReplaceAll(strings.ReplaceAll(strVal, "'", "''"), `\`, `\\`))
// 使用 strconv.Quote 来处理所有特殊字符
quoted := strconv.Quote(strVal)
// 去掉 strconv.Quote 添加的外层引号,因为会在最后添加 SQL 的单引号
quoted = quoted[1 : len(quoted)-1]
// 处理 SQL 中的单引号
quoted = strings.ReplaceAll(quoted, "'", "''")
return fmt.Sprintf("'%s'", quoted)
}
var (

View File

@@ -24,17 +24,16 @@ type Meta struct {
func (dm *Meta) GetSqlDb(ctx context.Context, d *dbi.DbInfo) (*sql.DB, error) {
driverName := "dm"
db := d.Database
var dbParam string
dbParam := "?escapeProcess=true"
if db != "" {
// dm database可以使用db/schema表示方便连接指定schema, 若不存在schema则使用默认schema
ss := strings.Split(db, "/")
if len(ss) > 1 {
dbParam = fmt.Sprintf("%s?schema=\"%s\"&escapeProcess=true", ss[0], ss[len(ss)-1])
} else {
dbParam = db + "?escapeProcess=true"
dbParam = fmt.Sprintf("%s&schema=\"%s\"", dbParam, ss[len(ss)-1])
}
} else {
dbParam = "?escapeProcess=true"
}
if d.Params != "" {
dbParam += "&" + d.Params
}
err := d.IfUseSshTunnelChangeIpPort(ctx)

View File

@@ -22,7 +22,7 @@ var (
UnsignedMediumint = dbi.NewDbDataType("unsigned mediumint", dbi.DTInt64).WithCT(dbi.CTUnsignedInt4).WithFixColumn(dbi.ClearNumScale)
Decimal = dbi.NewDbDataType("decimal", dbi.DTDecimal).WithCT(dbi.CTDecimal)
Double = dbi.NewDbDataType("double", dbi.DTNumeric).WithCT(dbi.CTNumeric)
Double = dbi.NewDbDataType("double", dbi.DTNumeric).WithCT(dbi.CTNumeric).WithFixColumn(dbi.ClearNumPrecision)
Float = dbi.NewDbDataType("float", dbi.DTNumeric).WithCT(dbi.CTNumeric)
Varchar = dbi.NewDbDataType("varchar", dbi.DTString).WithCT(dbi.CTVarchar)
@@ -40,9 +40,9 @@ var (
Enum = dbi.NewDbDataType("enum", dbi.DTString).WithCT(dbi.CTEnum)
Set = dbi.NewDbDataType("set", dbi.DTString).WithCT(dbi.CTVarchar)
Blob = dbi.NewDbDataType("blob", dbi.DTBytes).WithCT(dbi.CTBlob)
Mediumblob = dbi.NewDbDataType("mediumblob", dbi.DTBytes).WithCT(dbi.CTMediumblob)
Longblob = dbi.NewDbDataType("longblob", dbi.DTBytes).WithCT(dbi.CTLongblob)
Blob = dbi.NewDbDataType("blob", dbi.DTBytes).WithCT(dbi.CTBlob).WithFixColumn(dbi.ClearNumScale)
Mediumblob = dbi.NewDbDataType("mediumblob", dbi.DTBytes).WithCT(dbi.CTMediumblob).WithFixColumn(dbi.ClearNumScale)
Longblob = dbi.NewDbDataType("longblob", dbi.DTBytes).WithCT(dbi.CTLongblob).WithFixColumn(dbi.ClearNumScale)
Binary = dbi.NewDbDataType("binary", dbi.DTBytes).WithCT(dbi.CTBinary)
Varbinary = dbi.NewDbDataType("varbinary", dbi.DTBytes).WithCT(dbi.CTVarbinary)
)

View File

@@ -30,17 +30,16 @@ func (ucs UserClients) Count() int {
// 连接管理
type ClientManager struct {
UserClientsMap map[UserId]UserClients // 全部的用户连接, key->userid, value->UserClients
RwLock sync.RWMutex // 读写锁
UserClientsMap sync.Map // 全部的用户连接, key->userid, value->UserClients
ConnectChan chan *Client // 连接处理
DisConnectChan chan *Client // 断开连接处理
MsgChan chan *Msg // 消息信息channel通道
MsgChan chan *Msg // 消息信息channel通道
}
func NewClientManager() (clientManager *ClientManager) {
return &ClientManager{
UserClientsMap: make(map[UserId]UserClients),
UserClientsMap: sync.Map{},
ConnectChan: make(chan *Client, 10),
DisConnectChan: make(chan *Client, 10),
MsgChan: make(chan *Msg, 100),
@@ -78,24 +77,30 @@ func (manager *ClientManager) CloseClient(client *Client) {
// 根据用户id关闭客户端连接
func (manager *ClientManager) CloseByUid(userId UserId) {
for _, client := range manager.GetByUid(userId) {
userClients := manager.GetByUid(userId)
for _, client := range userClients {
manager.CloseClient(client)
}
}
// 获取所有的客户端
func (manager *ClientManager) AllUserClient() map[UserId]UserClients {
manager.RwLock.RLock()
defer manager.RwLock.RUnlock()
return manager.UserClientsMap
result := make(map[UserId]UserClients)
manager.UserClientsMap.Range(func(key, value any) bool {
userId := key.(UserId)
userClients := value.(UserClients)
result[userId] = userClients
return true
})
return result
}
// 通过userId获取用户所有客户端信息
func (manager *ClientManager) GetByUid(userId UserId) UserClients {
manager.RwLock.RLock()
defer manager.RwLock.RUnlock()
return manager.UserClientsMap[userId]
if value, ok := manager.UserClientsMap.Load(userId); ok {
return value.(UserClients)
}
return nil
}
// 通过userId和clientId获取客户端信息
@@ -108,9 +113,12 @@ func (manager *ClientManager) GetByUidAndCid(uid UserId, clientId string) *Clien
// 客户端数量
func (manager *ClientManager) Count() int {
manager.RwLock.RLock()
defer manager.RwLock.RUnlock()
return len(manager.UserClientsMap)
count := 0
manager.UserClientsMap.Range(func(key, value any) bool {
count++
return true
})
return count
}
// 发送json数据给指定用户
@@ -139,7 +147,8 @@ func (manager *ClientManager) WriteMessage() {
}
// cid为空则向该用户所有客户端发送该消息
for _, cli := range manager.GetByUid(uid) {
userClients := manager.GetByUid(uid)
for _, cli := range userClients {
if err := cli.WriteMsg(msg); err != nil {
logx.Warnf("ws send message failed - [uid=%d, cid=%s]: %s", uid, cli.ClientId, err.Error())
}
@@ -156,21 +165,23 @@ func (manager *ClientManager) HeartbeatTimer() {
for {
<-ticker.C
//发送心跳
for userId, clis := range manager.AllUserClient() {
manager.UserClientsMap.Range(func(key, value any) bool {
userId := key.(UserId)
clis := value.(UserClients)
for _, cli := range clis {
if cli == nil || cli.WsConn == nil {
continue
}
if err := cli.Ping(); err != nil {
manager.CloseClient(cli)
logx.Debugf("WS - failed to send heartbeat: uid=%v, cid=%s, usercount=%d", userId, cli.ClientId, Manager.Count())
logx.Debugf("WS - failed to send heartbeat: uid=%v, cid=%s, usercount=%d", userId, cli.ClientId, manager.Count())
} else {
logx.Debugf("WS - send heartbeat successfully: uid=%v, cid=%s", userId, cli.ClientId)
}
}
}
return true
})
}
}()
}
@@ -192,31 +203,29 @@ func (manager *ClientManager) doDisconnect(client *Client) {
client.WsConn = nil
}
manager.delUserClient4Map(client)
logx.Debugf("WS client disconnected: uid=%d, cid=%s, usercount=%d", client.UserId, client.ClientId, Manager.Count())
logx.Debugf("WS client disconnected: uid=%d, cid=%s, usercount=%d", client.UserId, client.ClientId, manager.Count())
}
func (manager *ClientManager) addUserClient2Map(client *Client) {
manager.RwLock.Lock()
defer manager.RwLock.Unlock()
userClients := manager.UserClientsMap[client.UserId]
if userClients == nil {
userClients = make(UserClients)
manager.UserClientsMap[client.UserId] = userClients
// 先尝试加载现有的UserClients
if value, ok := manager.UserClientsMap.Load(client.UserId); ok {
userClients := value.(UserClients)
userClients.AddClient(client)
} else {
// 创建新的UserClients
userClients := make(UserClients)
userClients.AddClient(client)
manager.UserClientsMap.Store(client.UserId, userClients)
}
userClients.AddClient(client)
}
func (manager *ClientManager) delUserClient4Map(client *Client) {
manager.RwLock.Lock()
defer manager.RwLock.Unlock()
userClients := manager.UserClientsMap[client.UserId]
if userClients != nil {
if value, ok := manager.UserClientsMap.Load(client.UserId); ok {
userClients := value.(UserClients)
userClients.DeleteByCid(client.ClientId)
// 如果用户所有客户端都关闭则移除manager中的UserClientsMap值
if userClients.Count() == 0 {
delete(manager.UserClientsMap, client.UserId)
manager.UserClientsMap.Delete(client.UserId)
}
}
}