Files
xiaolajiaoyyds 6f71df4849 Initial open-source release
多平台账号批量注册系统,支持 OpenAI / Grok / Kiro / Gemini 平台的全自动注册。

Features:
- 用户名/密码注册登录(首个用户自动管理员)
- JWT 认证 + bcrypt 密码加密
- 积分系统与多用户管理
- HuggingFace Space 弹性 Worker 节点管理
- Cloudflare Worker 负载均衡
- 实时日志推送(SSE/WebSocket)
- 管理后台(用户管理、系统设置、代理池、卡池)

Tech Stack: Go 1.25 / Gin / GORM / PostgreSQL / Vue 3 / Vite / TailwindCSS
2026-03-22 03:44:42 +08:00

133 lines
3.2 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"
"time"
"github.com/redis/go-redis/v9"
"github.com/rs/zerolog/log"
)
// RedisCache Redis 缓存层(L2)
// 设计为可选组件:REDIS_URL 为空时所有方法静默返回 miss,不影响业务
type RedisCache struct {
client *redis.Client
prefix string // key 前缀,如 "regp:"
}
// NewRedisCache 创建 Redis 缓存
// redisURL 为空则返回空壳实例(所有操作静默降级)
func NewRedisCache(redisURL, prefix string) *RedisCache {
rc := &RedisCache{prefix: prefix}
if redisURL == "" {
log.Info().Msg("REDIS_URL 未配置,L2 缓存已禁用(纯内存模式)")
return rc
}
opts, err := redis.ParseURL(redisURL)
if err != nil {
log.Error().Err(err).Str("url", redisURL).Msg("Redis URL 解析失败,L2 缓存已禁用")
return rc
}
client := redis.NewClient(opts)
// 连接测试(3 秒超时)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := client.Ping(ctx).Err(); err != nil {
log.Error().Err(err).Msg("Redis 连接失败,L2 缓存已禁用")
return rc
}
rc.client = client
log.Info().Str("addr", opts.Addr).Msg("Redis L2 缓存已连接")
return rc
}
// Available 检查 Redis 是否可用
func (c *RedisCache) Available() bool {
return c.client != nil
}
// key 拼接完整 key
func (c *RedisCache) key(k string) string {
return c.prefix + k
}
// GetString 获取字符串值
func (c *RedisCache) GetString(ctx context.Context, key string) (string, bool) {
if c.client == nil {
return "", false
}
val, err := c.client.Get(ctx, c.key(key)).Result()
if err != nil {
return "", false
}
return val, true
}
// SetString 设置字符串值
func (c *RedisCache) SetString(ctx context.Context, key, value string, ttl time.Duration) {
if c.client == nil {
return
}
if err := c.client.Set(ctx, c.key(key), value, ttl).Err(); err != nil {
log.Warn().Err(err).Str("key", key).Msg("Redis SET 失败")
}
}
// GetJSON 获取 JSON 序列化的值
func (c *RedisCache) GetJSON(ctx context.Context, key string, dest interface{}) bool {
if c.client == nil {
return false
}
val, err := c.client.Get(ctx, c.key(key)).Bytes()
if err != nil {
return false
}
if err := json.Unmarshal(val, dest); err != nil {
log.Warn().Err(err).Str("key", key).Msg("Redis JSON 反序列化失败")
return false
}
return true
}
// SetJSON 设置 JSON 序列化的值
func (c *RedisCache) SetJSON(ctx context.Context, key string, value interface{}, ttl time.Duration) {
if c.client == nil {
return
}
data, err := json.Marshal(value)
if err != nil {
log.Warn().Err(err).Str("key", key).Msg("Redis JSON 序列化失败")
return
}
if err := c.client.Set(ctx, c.key(key), data, ttl).Err(); err != nil {
log.Warn().Err(err).Str("key", key).Msg("Redis SET 失败")
}
}
// Del 删除 key
func (c *RedisCache) Del(ctx context.Context, keys ...string) {
if c.client == nil {
return
}
fullKeys := make([]string, len(keys))
for i, k := range keys {
fullKeys[i] = c.key(k)
}
if err := c.client.Del(ctx, fullKeys...).Err(); err != nil {
log.Warn().Err(err).Msg("Redis DEL 失败")
}
}
// Close 关闭连接
func (c *RedisCache) Close() error {
if c.client == nil {
return nil
}
return c.client.Close()
}