mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-12 06:49:41 +08:00
- 后端扩展 RedisClient 接口,新增 StreamEntry 与 Stream 操作定义 - Redis 实现新增 XADD/XDEL/XRANGE 封装并接入 RedisGetValue 的 stream 分支 - App 层新增 RedisStreamAdd 与 RedisStreamDelete 方法并返回操作结果 - 前端新增 stream 类型视图,支持消息新增、删除与字段复制 - refs #92
516 lines
17 KiB
Go
516 lines
17 KiB
Go
package app
|
||
|
||
import (
|
||
"crypto/sha256"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"strings"
|
||
"sync"
|
||
|
||
"GoNavi-Wails/internal/connection"
|
||
"GoNavi-Wails/internal/logger"
|
||
"GoNavi-Wails/internal/redis"
|
||
)
|
||
|
||
// Redis client cache
|
||
var (
|
||
redisCache = make(map[string]redis.RedisClient)
|
||
redisCacheMu sync.Mutex
|
||
)
|
||
|
||
// getRedisClient gets or creates a Redis client from cache
|
||
func (a *App) getRedisClient(config connection.ConnectionConfig) (redis.RedisClient, error) {
|
||
key := getRedisClientCacheKey(config)
|
||
shortKey := key
|
||
if len(shortKey) > 12 {
|
||
shortKey = shortKey[:12]
|
||
}
|
||
logger.Infof("获取 Redis 连接:%s 缓存Key=%s", formatRedisConnSummary(config), shortKey)
|
||
|
||
redisCacheMu.Lock()
|
||
defer redisCacheMu.Unlock()
|
||
|
||
if client, ok := redisCache[key]; ok {
|
||
logger.Infof("命中 Redis 连接缓存,开始检测可用性:缓存Key=%s", shortKey)
|
||
if err := client.Ping(); err == nil {
|
||
logger.Infof("缓存 Redis 连接可用:缓存Key=%s", shortKey)
|
||
return client, nil
|
||
} else {
|
||
logger.Error(err, "缓存 Redis 连接不可用,准备重建:缓存Key=%s", shortKey)
|
||
}
|
||
client.Close()
|
||
delete(redisCache, key)
|
||
}
|
||
|
||
logger.Infof("创建 Redis 客户端实例:缓存Key=%s", shortKey)
|
||
client := redis.NewRedisClient()
|
||
if err := client.Connect(config); err != nil {
|
||
logger.Error(err, "Redis 连接失败:%s 缓存Key=%s", formatRedisConnSummary(config), shortKey)
|
||
return nil, err
|
||
}
|
||
|
||
redisCache[key] = client
|
||
logger.Infof("Redis 连接成功并写入缓存:%s 缓存Key=%s", formatRedisConnSummary(config), shortKey)
|
||
return client, nil
|
||
}
|
||
|
||
func getRedisClientCacheKey(config connection.ConnectionConfig) string {
|
||
if !config.UseSSH {
|
||
config.SSH = connection.SSHConfig{}
|
||
}
|
||
b, _ := json.Marshal(config)
|
||
sum := sha256.Sum256(b)
|
||
return hex.EncodeToString(sum[:])
|
||
}
|
||
|
||
func formatRedisConnSummary(config connection.ConnectionConfig) string {
|
||
timeoutSeconds := config.Timeout
|
||
if timeoutSeconds <= 0 {
|
||
timeoutSeconds = 30
|
||
}
|
||
|
||
var b strings.Builder
|
||
b.WriteString("类型=redis 地址=")
|
||
b.WriteString(config.Host)
|
||
b.WriteString(":")
|
||
b.WriteString(string(rune(config.Port + '0')))
|
||
b.WriteString(" DB=")
|
||
b.WriteString(string(rune(config.RedisDB + '0')))
|
||
|
||
if config.UseSSH {
|
||
b.WriteString(" SSH=")
|
||
b.WriteString(config.SSH.Host)
|
||
b.WriteString(":")
|
||
b.WriteString(string(rune(config.SSH.Port + '0')))
|
||
b.WriteString(" 用户=")
|
||
b.WriteString(config.SSH.User)
|
||
}
|
||
|
||
return b.String()
|
||
}
|
||
|
||
// RedisConnect tests a Redis connection
|
||
func (a *App) RedisConnect(config connection.ConnectionConfig) connection.QueryResult {
|
||
config.Type = "redis"
|
||
_, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
logger.Error(err, "RedisConnect 连接失败:%s", formatRedisConnSummary(config))
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
logger.Infof("RedisConnect 连接成功:%s", formatRedisConnSummary(config))
|
||
return connection.QueryResult{Success: true, Message: "连接成功"}
|
||
}
|
||
|
||
// RedisTestConnection tests a Redis connection (alias for RedisConnect)
|
||
func (a *App) RedisTestConnection(config connection.ConnectionConfig) connection.QueryResult {
|
||
return a.RedisConnect(config)
|
||
}
|
||
|
||
// RedisScanKeys scans keys matching a pattern
|
||
func (a *App) RedisScanKeys(config connection.ConnectionConfig, pattern string, cursor uint64, count int64) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
result, err := client.ScanKeys(pattern, cursor, count)
|
||
if err != nil {
|
||
logger.Error(err, "RedisScanKeys 扫描失败:pattern=%s", pattern)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: result}
|
||
}
|
||
|
||
// RedisGetValue gets the value of a key
|
||
func (a *App) RedisGetValue(config connection.ConnectionConfig, key string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
value, err := client.GetValue(key)
|
||
if err != nil {
|
||
logger.Error(err, "RedisGetValue 获取失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: value}
|
||
}
|
||
|
||
// RedisSetString sets a string value
|
||
func (a *App) RedisSetString(config connection.ConnectionConfig, key, value string, ttl int64) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.SetString(key, value, ttl); err != nil {
|
||
logger.Error(err, "RedisSetString 设置失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "设置成功"}
|
||
}
|
||
|
||
// RedisSetHashField sets a field in a hash
|
||
func (a *App) RedisSetHashField(config connection.ConnectionConfig, key, field, value string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.SetHashField(key, field, value); err != nil {
|
||
logger.Error(err, "RedisSetHashField 设置失败:key=%s field=%s", key, field)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "设置成功"}
|
||
}
|
||
|
||
// RedisDeleteKeys deletes one or more keys
|
||
func (a *App) RedisDeleteKeys(config connection.ConnectionConfig, keys []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
deleted, err := client.DeleteKeys(keys)
|
||
if err != nil {
|
||
logger.Error(err, "RedisDeleteKeys 删除失败:keys=%v", keys)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: map[string]int64{"deleted": deleted}}
|
||
}
|
||
|
||
// RedisSetTTL sets the TTL of a key
|
||
func (a *App) RedisSetTTL(config connection.ConnectionConfig, key string, ttl int64) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.SetTTL(key, ttl); err != nil {
|
||
logger.Error(err, "RedisSetTTL 设置失败:key=%s ttl=%d", key, ttl)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "设置成功"}
|
||
}
|
||
|
||
// RedisExecuteCommand executes a raw Redis command
|
||
func (a *App) RedisExecuteCommand(config connection.ConnectionConfig, command string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
// Parse command string into args
|
||
args := parseRedisCommand(command)
|
||
if len(args) == 0 {
|
||
return connection.QueryResult{Success: false, Message: "命令不能为空"}
|
||
}
|
||
|
||
result, err := client.ExecuteCommand(args)
|
||
if err != nil {
|
||
logger.Error(err, "RedisExecuteCommand 执行失败:command=%s", command)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: result}
|
||
}
|
||
|
||
// parseRedisCommand parses a Redis command string into arguments
|
||
func parseRedisCommand(command string) []string {
|
||
command = strings.TrimSpace(command)
|
||
if command == "" {
|
||
return nil
|
||
}
|
||
|
||
var args []string
|
||
var current strings.Builder
|
||
inQuote := false
|
||
quoteChar := rune(0)
|
||
|
||
for _, ch := range command {
|
||
if inQuote {
|
||
if ch == quoteChar {
|
||
inQuote = false
|
||
args = append(args, current.String())
|
||
current.Reset()
|
||
} else {
|
||
current.WriteRune(ch)
|
||
}
|
||
} else {
|
||
if ch == '"' || ch == '\'' {
|
||
inQuote = true
|
||
quoteChar = ch
|
||
} else if ch == ' ' || ch == '\t' {
|
||
if current.Len() > 0 {
|
||
args = append(args, current.String())
|
||
current.Reset()
|
||
}
|
||
} else {
|
||
current.WriteRune(ch)
|
||
}
|
||
}
|
||
}
|
||
|
||
if current.Len() > 0 {
|
||
args = append(args, current.String())
|
||
}
|
||
|
||
return args
|
||
}
|
||
|
||
// RedisGetServerInfo returns server information
|
||
func (a *App) RedisGetServerInfo(config connection.ConnectionConfig) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
info, err := client.GetServerInfo()
|
||
if err != nil {
|
||
logger.Error(err, "RedisGetServerInfo 获取失败")
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: info}
|
||
}
|
||
|
||
// RedisGetDatabases returns information about all databases
|
||
func (a *App) RedisGetDatabases(config connection.ConnectionConfig) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
dbs, err := client.GetDatabases()
|
||
if err != nil {
|
||
logger.Error(err, "RedisGetDatabases 获取失败")
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Data: dbs}
|
||
}
|
||
|
||
// RedisSelectDB selects a database
|
||
func (a *App) RedisSelectDB(config connection.ConnectionConfig, dbIndex int) connection.QueryResult {
|
||
config.Type = "redis"
|
||
config.RedisDB = dbIndex
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.SelectDB(dbIndex); err != nil {
|
||
logger.Error(err, "RedisSelectDB 切换失败:db=%d", dbIndex)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "切换成功"}
|
||
}
|
||
|
||
// RedisRenameKey renames a key
|
||
func (a *App) RedisRenameKey(config connection.ConnectionConfig, oldKey, newKey string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.RenameKey(oldKey, newKey); err != nil {
|
||
logger.Error(err, "RedisRenameKey 重命名失败:%s -> %s", oldKey, newKey)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "重命名成功"}
|
||
}
|
||
|
||
// RedisDeleteHashField deletes fields from a hash
|
||
func (a *App) RedisDeleteHashField(config connection.ConnectionConfig, key string, fields []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.DeleteHashField(key, fields...); err != nil {
|
||
logger.Error(err, "RedisDeleteHashField 删除失败:key=%s fields=%v", key, fields)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "删除成功"}
|
||
}
|
||
|
||
// RedisListPush pushes values to a list
|
||
func (a *App) RedisListPush(config connection.ConnectionConfig, key string, values []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.ListPush(key, values...); err != nil {
|
||
logger.Error(err, "RedisListPush 添加失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "添加成功"}
|
||
}
|
||
|
||
// RedisListSet sets a value at an index in a list
|
||
func (a *App) RedisListSet(config connection.ConnectionConfig, key string, index int64, value string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.ListSet(key, index, value); err != nil {
|
||
logger.Error(err, "RedisListSet 设置失败:key=%s index=%d", key, index)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "设置成功"}
|
||
}
|
||
|
||
// RedisSetAdd adds members to a set
|
||
func (a *App) RedisSetAdd(config connection.ConnectionConfig, key string, members []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.SetAdd(key, members...); err != nil {
|
||
logger.Error(err, "RedisSetAdd 添加失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "添加成功"}
|
||
}
|
||
|
||
// RedisSetRemove removes members from a set
|
||
func (a *App) RedisSetRemove(config connection.ConnectionConfig, key string, members []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.SetRemove(key, members...); err != nil {
|
||
logger.Error(err, "RedisSetRemove 删除失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "删除成功"}
|
||
}
|
||
|
||
// RedisZSetAdd adds members to a sorted set
|
||
func (a *App) RedisZSetAdd(config connection.ConnectionConfig, key string, members []redis.ZSetMember) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.ZSetAdd(key, members...); err != nil {
|
||
logger.Error(err, "RedisZSetAdd 添加失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "添加成功"}
|
||
}
|
||
|
||
// RedisZSetRemove removes members from a sorted set
|
||
func (a *App) RedisZSetRemove(config connection.ConnectionConfig, key string, members []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.ZSetRemove(key, members...); err != nil {
|
||
logger.Error(err, "RedisZSetRemove 删除失败:key=%s", key)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "删除成功"}
|
||
}
|
||
|
||
// RedisStreamAdd adds an entry to a stream
|
||
func (a *App) RedisStreamAdd(config connection.ConnectionConfig, key string, fields map[string]string, id string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
newID, err := client.StreamAdd(key, fields, id)
|
||
if err != nil {
|
||
logger.Error(err, "RedisStreamAdd 添加失败:key=%s id=%s", key, id)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "添加成功", Data: map[string]string{"id": newID}}
|
||
}
|
||
|
||
// RedisStreamDelete deletes stream entries by IDs
|
||
func (a *App) RedisStreamDelete(config connection.ConnectionConfig, key string, ids []string) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
deleted, err := client.StreamDelete(key, ids...)
|
||
if err != nil {
|
||
logger.Error(err, "RedisStreamDelete 删除失败:key=%s ids=%v", key, ids)
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "删除成功", Data: map[string]int64{"deleted": deleted}}
|
||
}
|
||
|
||
// RedisFlushDB flushes the current database
|
||
func (a *App) RedisFlushDB(config connection.ConnectionConfig) connection.QueryResult {
|
||
config.Type = "redis"
|
||
client, err := a.getRedisClient(config)
|
||
if err != nil {
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
if err := client.FlushDB(); err != nil {
|
||
logger.Error(err, "RedisFlushDB 清空失败")
|
||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||
}
|
||
|
||
return connection.QueryResult{Success: true, Message: "清空成功"}
|
||
}
|
||
|
||
// CloseAllRedisClients closes all cached Redis clients (called on shutdown)
|
||
func CloseAllRedisClients() {
|
||
redisCacheMu.Lock()
|
||
defer redisCacheMu.Unlock()
|
||
|
||
for key, client := range redisCache {
|
||
if client != nil {
|
||
client.Close()
|
||
logger.Infof("已关闭 Redis 连接:%s", key[:12])
|
||
}
|
||
}
|
||
redisCache = make(map[string]redis.RedisClient)
|
||
}
|