mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-22 08:53:46 +08:00
✨ feat(redis): 支持 List 数据倒序显示
- 为索引列增加倒序优先排序,保持默认正序不变 - 倒序读取真实尾部窗口并保留原始 Redis 索引 - 补充前后端回归测试并同步 Wails 绑定 Refs #751
This commit is contained in:
@@ -1043,6 +1043,57 @@ func (a *App) RedisGetValue(config connection.ConnectionConfig, key string) conn
|
||||
return connection.QueryResult{Success: true, Data: value}
|
||||
}
|
||||
|
||||
// RedisGetListValue gets a list in its requested display order.
|
||||
func (a *App) RedisGetListValue(config connection.ConnectionConfig, key string, descending bool) 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, "RedisGetListValue 获取失败:key=%s", key)
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if value.Type != "list" {
|
||||
return connection.QueryResult{Success: false, Message: a.appText("redis.backend.error.argument_invalid_type", map[string]any{"name": "Redis Key"})}
|
||||
}
|
||||
|
||||
values, ok := value.Value.([]string)
|
||||
if !ok {
|
||||
return connection.QueryResult{Success: false, Message: a.appText("redis.backend.error.argument_invalid_type", map[string]any{"name": "Redis List"})}
|
||||
}
|
||||
if !descending {
|
||||
return connection.QueryResult{Success: true, Data: value}
|
||||
}
|
||||
|
||||
windowSize := int64(len(values))
|
||||
needsTailFetch := value.Length > windowSize
|
||||
if windowSize == 0 && value.Length > 0 {
|
||||
windowSize = 1000
|
||||
if value.Length < windowSize {
|
||||
windowSize = value.Length
|
||||
}
|
||||
needsTailFetch = true
|
||||
}
|
||||
if needsTailFetch {
|
||||
values, err = client.GetList(key, value.Length-windowSize, -1)
|
||||
if err != nil {
|
||||
logger.Error(err, "RedisGetListValue 获取尾部数据失败:key=%s", key)
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
} else {
|
||||
values = append([]string(nil), values...)
|
||||
}
|
||||
for left, right := 0, len(values)-1; left < right; left, right = left+1, right-1 {
|
||||
values[left], values[right] = values[right], values[left]
|
||||
}
|
||||
value.Value = values
|
||||
|
||||
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"
|
||||
|
||||
@@ -17,6 +17,12 @@ type capturingRedisClient struct {
|
||||
deletedHashFields []string
|
||||
removedListKey string
|
||||
removedListValue string
|
||||
valueResult *redislib.RedisValue
|
||||
listResult []string
|
||||
listKey string
|
||||
listStart int64
|
||||
listStop int64
|
||||
listCalls int
|
||||
closed int
|
||||
closeErr error
|
||||
}
|
||||
@@ -50,6 +56,9 @@ func (c *capturingRedisClient) RenameKey(oldKey, newKey string) error { return n
|
||||
func (c *capturingRedisClient) KeyExists(key string) (bool, error) { return false, nil }
|
||||
|
||||
func (c *capturingRedisClient) GetValue(key string) (*redislib.RedisValue, error) {
|
||||
if c.valueResult != nil {
|
||||
return c.valueResult, nil
|
||||
}
|
||||
return &redislib.RedisValue{}, nil
|
||||
}
|
||||
|
||||
@@ -70,7 +79,11 @@ func (c *capturingRedisClient) DeleteHashField(key string, fields ...string) err
|
||||
}
|
||||
|
||||
func (c *capturingRedisClient) GetList(key string, start, stop int64) ([]string, error) {
|
||||
return nil, nil
|
||||
c.listKey = key
|
||||
c.listStart = start
|
||||
c.listStop = stop
|
||||
c.listCalls++
|
||||
return append([]string(nil), c.listResult...), nil
|
||||
}
|
||||
|
||||
func (c *capturingRedisClient) ListPush(key string, values ...string) error { return nil }
|
||||
@@ -855,6 +868,57 @@ func TestRedisDeleteHashFieldAcceptsStringSlice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedisGetListValueDescendingReadsAndReversesTailWindow(t *testing.T) {
|
||||
app := NewAppWithSecretStore(newFakeAppSecretStore())
|
||||
app.configDir = t.TempDir()
|
||||
|
||||
CloseAllRedisClients()
|
||||
client := &capturingRedisClient{
|
||||
valueResult: &redislib.RedisValue{
|
||||
Type: "list",
|
||||
TTL: -1,
|
||||
Value: []string{"item-0", "item-1", "item-2"},
|
||||
Length: 5,
|
||||
},
|
||||
listResult: []string{"item-2", "item-3", "item-4"},
|
||||
}
|
||||
originalNewRedisClientFunc := newRedisClientFunc
|
||||
originalResolveDialConfigWithProxyFunc := resolveDialConfigWithProxyFunc
|
||||
defer func() {
|
||||
newRedisClientFunc = originalNewRedisClientFunc
|
||||
resolveDialConfigWithProxyFunc = originalResolveDialConfigWithProxyFunc
|
||||
CloseAllRedisClients()
|
||||
}()
|
||||
newRedisClientFunc = func() redislib.RedisClient {
|
||||
return client
|
||||
}
|
||||
resolveDialConfigWithProxyFunc = func(raw connection.ConnectionConfig) (connection.ConnectionConfig, error) {
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
result := app.RedisGetListValue(connection.ConnectionConfig{
|
||||
Type: "redis",
|
||||
Host: "redis.local",
|
||||
Port: 6379,
|
||||
}, "tasks", true)
|
||||
if !result.Success {
|
||||
t.Fatalf("RedisGetListValue returned failure: %+v", result)
|
||||
}
|
||||
if client.listCalls != 1 || client.listKey != "tasks" || client.listStart != 2 || client.listStop != -1 {
|
||||
t.Fatalf("unexpected list tail request: calls=%d key=%q start=%d stop=%d", client.listCalls, client.listKey, client.listStart, client.listStop)
|
||||
}
|
||||
value, ok := result.Data.(*redislib.RedisValue)
|
||||
if !ok {
|
||||
t.Fatalf("expected *redis.RedisValue result, got %T", result.Data)
|
||||
}
|
||||
if !reflect.DeepEqual(value.Value, []string{"item-4", "item-3", "item-2"}) {
|
||||
t.Fatalf("unexpected descending list values: %#v", value.Value)
|
||||
}
|
||||
if value.Length != 5 || value.TTL != -1 {
|
||||
t.Fatalf("expected list metadata to remain unchanged, got length=%d ttl=%d", value.Length, value.TTL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedisListRemoveDeletesOneValue(t *testing.T) {
|
||||
app := NewAppWithSecretStore(newFakeAppSecretStore())
|
||||
app.configDir = t.TempDir()
|
||||
|
||||
Reference in New Issue
Block a user