package cache import ( "context" "encoding/json/v2" "errors" "fmt" "time" "github.com/redis/go-redis/v9" ) type Entry[T any] struct { Key string Value T TTL time.Duration } type Cache struct { client *redis.Client operationTimeout time.Duration } func NewCache(client *redis.Client, timeout time.Duration) *Cache { return &Cache{ client: client, operationTimeout: timeout, } } func (c *Cache) Get[T any](ctx context.Context, key string) (T, bool, error) { var zero T operationCtx, cancel := context.WithTimeout(ctx, c.operationTimeout) defer cancel() payload, err := c.client.Get(operationCtx, key).Result() cancel() if errors.Is(err, redis.Nil) { return zero, false, nil } if err != nil { return zero, false, fmt.Errorf("读取缓存失败:%w", err) } var value T if err := json.Unmarshal([]byte(payload), &value); err != nil { return zero, false, fmt.Errorf("缓存解码失败:%w", err) } return value, true, nil } func (c *Cache) Set[T any](ctx context.Context, entry Entry[T]) error { if entry.TTL < 0 { return fmt.Errorf("缓存过期时间必须大于等于零: %v", entry.TTL) } if err := ctx.Err(); err != nil { return fmt.Errorf("写入缓存失败:%w", err) } payload, err := json.Marshal(entry.Value) if err != nil { return fmt.Errorf("缓存编码失败:%w", err) } operationCtx, cancel := context.WithTimeout(ctx, c.operationTimeout) defer cancel() if err := c.client.Set(operationCtx, entry.Key, payload, entry.TTL).Err(); err != nil { return fmt.Errorf("写入缓存失败:%w", err) } return nil }