Files
2026-07-29 22:10:36 +08:00

220 lines
4.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cache
import (
"context"
"encoding/json"
"errors"
"strings"
"time"
"github.com/redis/go-redis/v9"
"server/internal/config"
)
type Caches struct {
rdb *redis.Client
keyPrefix string
}
func NewCaches(rdb *redis.Client, cfg *config.Config) *Caches {
return &Caches{
rdb: rdb,
keyPrefix: cfg.Redis.KeyPrefix,
}
}
func (c *Caches) key(key string) string {
if c.keyPrefix == "" {
return key
}
return c.keyPrefix + key
}
func (c *Caches) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
return c.rdb.Set(ctx, c.key(key), value, expiration).Err()
}
func (c *Caches) Get(ctx context.Context, key string) (string, bool, error) {
val, err := c.rdb.Get(ctx, c.key(key)).Result()
if errors.Is(err, redis.Nil) {
return "", false, nil
}
if err != nil {
return "", false, err
}
return val, true, nil
}
func (c *Caches) SetJSON(ctx context.Context, key string, value any, expiration time.Duration) error {
b, err := json.Marshal(value)
if err != nil {
return err
}
return c.rdb.Set(ctx, c.key(key), b, expiration).Err()
}
func GetJSON[T any](ctx context.Context, c *Caches, key string) (*T, bool, error) {
val, err := c.rdb.Get(ctx, c.key(key)).Bytes()
if errors.Is(err, redis.Nil) {
return nil, false, nil
}
if err != nil {
return nil, false, err
}
var result T
if err = json.Unmarshal(val, &result); err != nil {
return nil, false, err
}
return &result, true, nil
}
func GetOrSetJSON[T any](ctx context.Context, c *Caches, key string, expiration time.Duration, fetch func() (T, error)) (T, error) {
var zero T
cacheKey := c.key(key)
val, err := c.rdb.Get(ctx, cacheKey).Bytes()
if err == nil {
var result T
if err = json.Unmarshal(val, &result); err == nil {
return result, nil
}
}
if !errors.Is(err, redis.Nil) {
return zero, err
}
result, err := fetch()
if err != nil {
return zero, err
}
b, err := json.Marshal(result)
if err != nil {
return zero, err
}
_ = c.rdb.Set(ctx, cacheKey, b, expiration).Err()
return result, nil
}
func (c *Caches) Del(ctx context.Context, keys ...string) error {
for i := range keys {
keys[i] = c.key(keys[i])
}
return c.rdb.Del(ctx, keys...).Err()
}
func (c *Caches) Exists(ctx context.Context, key string) (bool, error) {
n, err := c.rdb.Exists(ctx, c.key(key)).Result()
if err != nil {
return false, err
}
return n > 0, nil
}
// SAdd 向 Set 中添加成员
func (c *Caches) SAdd(ctx context.Context, key string, members ...any) error {
return c.rdb.SAdd(ctx, c.key(key), members...).Err()
}
// SMembers 获取 Set 中所有成员
func (c *Caches) SMembers(ctx context.Context, key string) ([]string, error) {
return c.rdb.SMembers(ctx, c.key(key)).Result()
}
// DelSetMembers 删除 Set 中记录的所有成员对应的缓存 key并删除 Set 本身
func (c *Caches) DelSetMembers(ctx context.Context, key string) error {
members, err := c.SMembers(ctx, key)
if err != nil {
return err
}
keys := append(members, key)
return c.Del(ctx, keys...)
}
// SRem 从 Set 中移除成员
func (c *Caches) SRem(ctx context.Context, key string, members ...any) error {
return c.rdb.SRem(ctx, c.key(key), members...).Err()
}
// SIsMember 判断 member 是否在 Set 中
func (c *Caches) SIsMember(ctx context.Context, key string, member any) (bool, error) {
return c.rdb.SIsMember(ctx, c.key(key), member).Result()
}
// MGet 批量获取多个 key 的值
func (c *Caches) MGet(ctx context.Context, keys ...string) (map[string]string, error) {
if len(keys) == 0 {
return map[string]string{}, nil
}
redisKeys := make([]string, len(keys))
for i, key := range keys {
redisKeys[i] = c.key(key)
}
values, err := c.rdb.MGet(ctx, redisKeys...).Result()
if err != nil {
return nil, err
}
result := make(map[string]string, len(keys))
for i, value := range values {
if value == nil {
continue
}
str, ok := value.(string)
if !ok {
continue
}
result[keys[i]] = str
}
return result, nil
}
// DelByPrefix 批量删除指定前缀的缓存键
func (c *Caches) DelByPrefix(ctx context.Context, prefix string) error {
prefix = c.key(prefix)
if !strings.HasSuffix(prefix, "*") {
prefix += "*"
}
var cursor uint64
var keys []string
var err error
for {
keys, cursor, err = c.rdb.Scan(ctx, cursor, prefix, 100).Result()
if err != nil {
return err
}
if len(keys) > 0 {
if err = c.rdb.Del(ctx, keys...).Err(); err != nil {
return err
}
}
if cursor == 0 {
break
}
}
return nil
}
func (c *Caches) Expire(ctx context.Context, key string, expiration time.Duration) error {
return c.rdb.Expire(ctx, c.keyPrefix+key, expiration).Err()
}