package cache import ( "context" "encoding/json" "fmt" "log" "time" "github.com/redis/go-redis/v9" "infogenie-backend/config" ) // Key suffixes(完整 key = KeyPrefix + suffix) const ( KeySite60sDisabled = "site:60s-disabled" KeySite60sSource = "site:60s-source" KeySiteAIModelDisabled = "site:ai-model-disabled" ) func KeySiteFeatureClicks(section string) string { return "site:feature-clicks:" + section } var ( rdb *redis.Client keyPrefix string siteCacheTTL time.Duration redisOn bool ) // Init 在 REDIS_ENABLED 时连接并 Ping;未启用时为空操作 func Init(rc config.RedisConfig) error { if !rc.Enabled { redisOn = false rdb = nil return nil } redisOn = true keyPrefix = rc.KeyPrefix siteCacheTTL = rc.SiteTTL if siteCacheTTL <= 0 { siteCacheTTL = 60 * time.Second } rdb = redis.NewClient(&redis.Options{ Addr: rc.Addr, Password: rc.Password, DB: rc.DB, }) if err := rdb.Ping(context.Background()).Err(); err != nil { return fmt.Errorf("redis: %w", err) } return nil } // Enabled 表示已初始化且客户端可用 func Enabled() bool { return redisOn && rdb != nil } func fullKey(suffix string) string { return keyPrefix + suffix } // GetJSON 命中返回 true;未启用或未命中返回 false(err 仅表示 Redis/JSON 异常) func GetJSON(ctx context.Context, suffix string, dest interface{}) (bool, error) { if !Enabled() { return false, nil } val, err := rdb.Get(ctx, fullKey(suffix)).Bytes() if err == redis.Nil { return false, nil } if err != nil { return false, err } if err := json.Unmarshal(val, dest); err != nil { return false, err } return true, nil } // SetJSON ttl<=0 时使用配置的 SiteTTL func SetJSON(ctx context.Context, suffix string, v interface{}, ttl time.Duration) error { if !Enabled() { return nil } b, err := json.Marshal(v) if err != nil { return err } if ttl <= 0 { ttl = siteCacheTTL } return rdb.Set(ctx, fullKey(suffix), b, ttl).Err() } // Delete 删除若干后缀对应的 key;失败仅打日志 func Delete(ctx context.Context, suffixes ...string) { if !Enabled() || len(suffixes) == 0 { return } keys := make([]string, 0, len(suffixes)) for _, s := range suffixes { keys = append(keys, fullKey(s)) } if err := rdb.Del(ctx, keys...).Err(); err != nil { log.Printf("redis DEL 失败: %v keys=%v", err, keys) } } // Ping 未启用时返回 nil func Ping(ctx context.Context) error { if !Enabled() { return nil } return rdb.Ping(ctx).Err() }