package cache import ( "context" "encoding/json" "time" redislib "github.com/redis/go-redis/v9" "go.uber.org/zap" redisPkg "with_you/internal/pkg/redis" ) // RedisCache Redis缓存实现 type RedisCache struct { client *redisPkg.Client ctx context.Context } // NewRedisCache 创建Redis缓存 func NewRedisCache(client *redisPkg.Client) *RedisCache { return &RedisCache{ client: client, ctx: context.Background(), } } // Set 设置缓存值 func (c *RedisCache) Set(key string, value any, ttl time.Duration) { key = normalizeKey(key) // 将值序列化为JSON data, err := json.Marshal(value) if err != nil { recordSetError() zap.L().Error("Failed to marshal value for key", zap.String("key", key), zap.Error(err), ) return } if err := c.client.Set(c.ctx, key, data, ttl); err != nil { recordSetError() zap.L().Error("Failed to set key", zap.String("key", key), zap.Error(err), ) } } // Get 获取缓存值 func (c *RedisCache) Get(key string) (any, bool) { key = normalizeKey(key) data, err := c.client.Get(c.ctx, key) if err != nil { if err == redislib.Nil { return nil, false } zap.L().Error("Failed to get key", zap.String("key", key), zap.Error(err), ) return nil, false } // 返回原始字符串,由调用侧决定如何解码为目标类型 return data, true } // Delete 删除缓存 func (c *RedisCache) Delete(key string) { key = normalizeKey(key) recordInvalidate() if err := c.client.Del(c.ctx, key); err != nil { zap.L().Error("Failed to delete key", zap.String("key", key), zap.Error(err), ) } } // DeleteByPrefix 根据前缀删除缓存 func (c *RedisCache) DeleteByPrefix(prefix string) { prefix = normalizePrefix(prefix) rdb := c.client.GetClient() var cursor uint64 for { keys, nextCursor, err := rdb.Scan(c.ctx, cursor, prefix+"*", 100).Result() if err != nil { zap.L().Error("Failed to scan keys with prefix", zap.String("prefix", prefix), zap.Error(err), ) return } if len(keys) > 0 { recordInvalidateMultiple(int64(len(keys))) if err := c.client.Del(c.ctx, keys...); err != nil { zap.L().Error("Failed to delete keys with prefix", zap.String("prefix", prefix), zap.Error(err), ) } } cursor = nextCursor if cursor == 0 { break } } } // DeleteBatch 批量删除多个 key(使用 Redis Pipeline 减少网络往返) func (c *RedisCache) DeleteBatch(keys []string) { if len(keys) == 0 { return } normalizedKeys := make([]string, len(keys)) for i, k := range keys { normalizedKeys[i] = normalizeKey(k) } recordInvalidateMultiple(int64(len(normalizedKeys))) // 使用 pipeline 批量删除 pipe := c.client.GetClient().Pipeline() for _, k := range normalizedKeys { pipe.Del(c.ctx, k) } if _, err := pipe.Exec(c.ctx); err != nil { zap.L().Error("Failed to batch delete keys", zap.Int("count", len(normalizedKeys)), zap.Error(err), ) } } // Clear 清空所有缓存 func (c *RedisCache) Clear() { if settings.DisableFlushDB { zap.L().Debug("Skip FlushDB because cache.disable_flushdb=true") return } recordInvalidate() rdb := c.client.GetClient() if err := rdb.FlushDB(c.ctx).Err(); err != nil { zap.L().Error("Failed to clear cache", zap.Error(err), ) } } // Exists 检查键是否存在 func (c *RedisCache) Exists(key string) bool { key = normalizeKey(key) n, err := c.client.Exists(c.ctx, key) if err != nil { zap.L().Error("Failed to check existence of key", zap.String("key", key), zap.Error(err), ) return false } return n > 0 } // Increment 增加计数器的值 func (c *RedisCache) Increment(key string) int64 { return c.IncrementBy(key, 1) } // IncrementBy 增加指定值 func (c *RedisCache) IncrementBy(key string, value int64) int64 { key = normalizeKey(key) rdb := c.client.GetClient() result, err := rdb.IncrBy(c.ctx, key, value).Result() if err != nil { zap.L().Error("Failed to increment key", zap.String("key", key), zap.Error(err), ) return 0 } return result } // ==================== RedisCache Hash 操作 ==================== // HSet 设置 Hash 字段 func (c *RedisCache) HSet(ctx context.Context, key string, field string, value any) error { key = normalizeKey(key) return c.client.HSet(ctx, key, field, value) } // HMSet 批量设置 Hash 字段 func (c *RedisCache) HMSet(ctx context.Context, key string, values map[string]any) error { key = normalizeKey(key) return c.client.HMSet(ctx, key, values) } // HGet 获取 Hash 字段值 func (c *RedisCache) HGet(ctx context.Context, key string, field string) (string, error) { key = normalizeKey(key) return c.client.HGet(ctx, key, field) } // HMGet 批量获取 Hash 字段值 func (c *RedisCache) HMGet(ctx context.Context, key string, fields ...string) ([]any, error) { key = normalizeKey(key) return c.client.HMGet(ctx, key, fields...) } // HGetAll 获取 Hash 所有字段 func (c *RedisCache) HGetAll(ctx context.Context, key string) (map[string]string, error) { key = normalizeKey(key) return c.client.HGetAll(ctx, key) } // HDel 删除 Hash 字段 func (c *RedisCache) HDel(ctx context.Context, key string, fields ...string) error { key = normalizeKey(key) return c.client.HDel(ctx, key, fields...) } // ==================== RedisCache Sorted Set 操作 ==================== // ZAdd 添加 Sorted Set 成员 func (c *RedisCache) ZAdd(ctx context.Context, key string, score float64, member string) error { key = normalizeKey(key) return c.client.ZAdd(ctx, key, score, member) } // ZRangeByScore 按分数范围获取成员(升序) func (c *RedisCache) ZRangeByScore(ctx context.Context, key string, min, max string, offset, count int64) ([]string, error) { key = normalizeKey(key) return c.client.ZRangeByScore(ctx, key, min, max, offset, count) } // ZRevRangeByScore 按分数范围获取成员(降序) func (c *RedisCache) ZRevRangeByScore(ctx context.Context, key string, max, min string, offset, count int64) ([]string, error) { key = normalizeKey(key) return c.client.ZRevRangeByScore(ctx, key, max, min, offset, count) } // ZRevRange 按排名倒序获取成员 func (c *RedisCache) ZRevRange(ctx context.Context, key string, start, stop int64) ([]string, error) { key = normalizeKey(key) return c.client.ZRevRange(ctx, key, start, stop) } // ZReplaceSortedSet 删除后整表重写 ZSET func (c *RedisCache) ZReplaceSortedSet(ctx context.Context, key string, members []redislib.Z) error { key = normalizeKey(key) rdb := c.client.GetClient() if err := rdb.Del(ctx, key).Err(); err != nil { return err } if len(members) == 0 { return nil } return c.client.ZAddArgs(ctx, key, members...) } // ZRem 删除 Sorted Set 成员 func (c *RedisCache) ZRem(ctx context.Context, key string, members ...any) error { key = normalizeKey(key) return c.client.ZRem(ctx, key, members...) } // ZCard 获取 Sorted Set 成员数量 func (c *RedisCache) ZCard(ctx context.Context, key string) (int64, error) { key = normalizeKey(key) return c.client.ZCard(ctx, key) } // ==================== RedisCache 计数器操作 ==================== // Incr 原子递增(返回新值) func (c *RedisCache) Incr(ctx context.Context, key string) (int64, error) { key = normalizeKey(key) return c.client.Incr(ctx, key) } // Expire 设置过期时间 func (c *RedisCache) Expire(ctx context.Context, key string, ttl time.Duration) error { key = normalizeKey(key) _, err := c.client.Expire(ctx, key, ttl) return err } // GetRedisClient 获取Redis客户端 func (c *RedisCache) GetRedisClient() *redisPkg.Client { return c.client }