package cache import ( "context" "encoding/json" "time" redis "github.com/redis/go-redis/v9" ) type RedisCacheStruct[T any] struct { Redis *redis.Client Ctx context.Context HashKey string Result T DefaultExpiration time.Duration CleanupInterval time.Duration } type RedisValue[T any] struct { ExpirationTimeStamp int64 IsExpiration bool // 是否有过期时间 false // 不过期 Value T } // cache.New(5*time.Minute, 60*time.Second) func NewRedisCache[T any](redisDb *redis.Client, hashKey string, defaultExpiration time.Duration, cleanupInterval time.Duration) *RedisCacheStruct[T] { obj := RedisCacheStruct[T]{ Redis: redisDb, Ctx: context.Background(), HashKey: hashKey, DefaultExpiration: defaultExpiration, CleanupInterval: cleanupInterval, } // 创建定时器判断是否过期 if obj.CleanupInterval.Seconds() > 0 { go obj.expirationVerification() } return &obj } func (r *RedisCacheStruct[T]) Set(k string, v T, d time.Duration) { valueEncode := "" value := RedisValue[T]{} // 设置过期时间 if d.Seconds() > 0 { value.IsExpiration = true value.ExpirationTimeStamp = time.Now().Add(d).Unix() } else { value.IsExpiration = false // 不过期 } value.Value = v if j, e := json.Marshal(value); e == nil { valueEncode = string(j) } r.Redis.HSet(r.Ctx, r.HashKey, k, valueEncode) // second := d.Seconds() // if second > 0 { // // 设置过期时间 // err := r.Redis.Do(r.Ctx, "SETEX", r.HashKey+k, second, valueEncode).Err() // fmt.Println("设置结果", err) // } else { // r.Redis.HSet(r.Ctx, r.HashKey, k, valueEncode) // } } func (r *RedisCacheStruct[T]) Get(k string) (T, bool) { var valueEncode []byte value := RedisValue[T]{} cmd := r.Redis.HGet(r.Ctx, r.HashKey, k) if err := cmd.Scan(&valueEncode); err != nil { // log.Println(err) return r.Result, false } if err := json.Unmarshal(valueEncode, &value); err != nil { // log.Println(err) return r.Result, false } // 已过期,清理掉key if value.IsExpiration && time.Now().Unix() > value.ExpirationTimeStamp { r.Delete(k) return r.Result, false } return value.Value, true } // 设置cache 无时间参数 func (r *RedisCacheStruct[T]) SetDefault(k string, v T) { r.Set(k, v, r.DefaultExpiration) } // 设置并保持原始的过期时间 func (r *RedisCacheStruct[T]) SetKeepExpiration(k string, v T) { var valueEncode []byte value := RedisValue[T]{} cmd := r.Redis.HGet(r.Ctx, r.HashKey, k) if err := cmd.Scan(&valueEncode); err != nil { // fmt.Println("使用默认的过期时间") r.SetDefault(k, v) return } if err := json.Unmarshal(valueEncode, &value); err != nil { // fmt.Println("使用默认的过期时间") r.SetDefault(k, v) return } now := time.Now() timeDiffer := value.ExpirationTimeStamp - now.Unix() // 如果设置了过期时间并且过期时间大于现在将保留原始的过期时间 if value.IsExpiration && timeDiffer > 0 { // fmt.Println("重新计算过期时间") // fmt.Println("旧的过期时间", value.ExpirationTimeStamp) // fmt.Println("时间限制差", timeDiffer) // fmt.Println("新的过期时间", now.Unix()+timeDiffer) r.Set(k, v, time.Second*time.Duration(timeDiffer)) } else { // fmt.Println("使用默认的过期时间") r.SetDefault(k, v) } } // 删除 cache func (r *RedisCacheStruct[T]) Delete(k string) { r.Redis.HDel(r.Ctx, r.HashKey, k) } // Add() 加入缓存 // func (r *RedisCacheStruct[T]) Add(k string, v T, d time.Duration) { // c.gocahce.Add(k, x, d) // } // IncrementInt() 对已存在的key 值自增n // func (r *RedisCacheStruct[T]) IncrementInt(k string, n int) (num int, err error) { // if err := r.Redis.HIncrBy(r.Ctx, r.HashKey, k, int64(n)).Err(); err != nil { // return num, err // } // if v, ok := r.Get(k); ok { // switch T { // case int: // } // if vint, okint := v.(int); okint { // } // } // return c.gocahce.IncrementInt(k, n) // } // ItemCount 获取已存在key的数量 func (r *RedisCacheStruct[T]) ItemCount() (int64, error) { if count, err := r.Redis.HLen(r.Ctx, r.HashKey).Result(); err != nil { return 0, err } else { return count, nil } } // Flush 删除当前已存在的所有key func (r *RedisCacheStruct[T]) Flush() { r.Redis.Del(r.Ctx, r.HashKey) } // 定时清理过期验证 func (r *RedisCacheStruct[T]) expirationVerification() { ticker := time.NewTicker(r.CleanupInterval) for { select { case <-ticker.C: if fields, err := r.Redis.HKeys(r.Ctx, r.HashKey).Result(); err == nil { for _, v := range fields { // r.Redis.HGet(r.Ctx, r.HashKey, v) r.Get(v) // fmt.Println("redis定时器", v) } } // case <-j.stop: // ticker.Stop() // return } } }