Files
doucan/app/internal/redis/redis.go
2026-02-20 23:39:49 +08:00

273 lines
6.4 KiB
Go

package redis
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
type Config struct {
Addr string
Password string
DB int
}
type Client struct {
rdb *redis.Client
}
func NewClient(cfg Config) (*Client, error) {
rdb := redis.NewClient(&redis.Options{
Addr: cfg.Addr,
Password: cfg.Password,
DB: cfg.DB,
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := rdb.Ping(ctx).Err(); err != nil {
return nil, fmt.Errorf("failed to connect to redis: %w", err)
}
return &Client{rdb: rdb}, nil
}
func (c *Client) Close() error {
return c.rdb.Close()
}
func (c *Client) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
data, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("failed to marshal value: %w", err)
}
return c.rdb.Set(ctx, key, data, ttl).Err()
}
func (c *Client) Get(ctx context.Context, key string, dest interface{}) error {
data, err := c.rdb.Get(ctx, key).Bytes()
if err != nil {
return err
}
if err := json.Unmarshal(data, dest); err != nil {
return fmt.Errorf("failed to unmarshal value: %w", err)
}
return nil
}
func (c *Client) Exists(ctx context.Context, key string) (bool, error) {
result, err := c.rdb.Exists(ctx, key).Result()
if err != nil {
return false, err
}
return result > 0, nil
}
func (c *Client) Delete(ctx context.Context, keys ...string) error {
return c.rdb.Del(ctx, keys...).Err()
}
func (c *Client) SetTTL(ctx context.Context, key string, ttl time.Duration) error {
return c.rdb.Expire(ctx, key, ttl).Err()
}
func (c *Client) GetTTL(ctx context.Context, key string) (time.Duration, error) {
return c.rdb.TTL(ctx, key).Result()
}
func (c *Client) LPush(ctx context.Context, key string, values ...interface{}) error {
data := make([]interface{}, len(values))
for i, v := range values {
b, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("failed to marshal list value: %w", err)
}
data[i] = b
}
return c.rdb.LPush(ctx, key, data...).Err()
}
func (c *Client) RPush(ctx context.Context, key string, values ...interface{}) error {
data := make([]interface{}, len(values))
for i, v := range values {
b, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("failed to marshal list value: %w", err)
}
data[i] = b
}
return c.rdb.RPush(ctx, key, data...).Err()
}
func (c *Client) LRange(ctx context.Context, key string, start, stop int64, dest interface{}) error {
results, err := c.rdb.LRange(ctx, key, start, stop).Result()
if err != nil {
return err
}
data, err := json.Marshal(results)
if err != nil {
return fmt.Errorf("failed to marshal results: %w", err)
}
return json.Unmarshal(data, dest)
}
func (c *Client) LLen(ctx context.Context, key string) (int64, error) {
return c.rdb.LLen(ctx, key).Result()
}
func (c *Client) LPop(ctx context.Context, key string) (string, error) {
return c.rdb.LPop(ctx, key).Result()
}
func (c *Client) LTrim(ctx context.Context, key string, start, stop int64) error {
return c.rdb.LTrim(ctx, key, start, stop).Err()
}
func (c *Client) LSet(ctx context.Context, key string, index int64, value interface{}) error {
data, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("failed to marshal value: %w", err)
}
return c.rdb.LSet(ctx, key, index, data).Err()
}
func (c *Client) SAdd(ctx context.Context, key string, members ...interface{}) error {
data := make([]interface{}, len(members))
for i, m := range members {
b, err := json.Marshal(m)
if err != nil {
return fmt.Errorf("failed to marshal set member: %w", err)
}
data[i] = b
}
return c.rdb.SAdd(ctx, key, data...).Err()
}
func (c *Client) SMembers(ctx context.Context, key string) ([]string, error) {
return c.rdb.SMembers(ctx, key).Result()
}
func (c *Client) SRem(ctx context.Context, key string, members ...interface{}) error {
data := make([]interface{}, len(members))
for i, m := range members {
b, err := json.Marshal(m)
if err != nil {
return fmt.Errorf("failed to marshal set member: %w", err)
}
data[i] = b
}
return c.rdb.SRem(ctx, key, data...).Err()
}
func (c *Client) HSet(ctx context.Context, key string, field string, value interface{}) error {
data, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("failed to marshal hash value: %w", err)
}
return c.rdb.HSet(ctx, key, field, data).Err()
}
func (c *Client) HGet(ctx context.Context, key, field string, dest interface{}) error {
data, err := c.rdb.HGet(ctx, key, field).Bytes()
if err != nil {
return err
}
return json.Unmarshal(data, dest)
}
func (c *Client) HDel(ctx context.Context, key string, fields ...string) error {
return c.rdb.HDel(ctx, key, fields...).Err()
}
func (c *Client) HGetAll(ctx context.Context, key string) (map[string]string, error) {
return c.rdb.HGetAll(ctx, key).Result()
}
func (c *Client) HExists(ctx context.Context, key, field string) (bool, error) {
return c.rdb.HExists(ctx, key, field).Result()
}
func (c *Client) Keys(ctx context.Context, pattern string) ([]string, error) {
return c.rdb.Keys(ctx, pattern).Result()
}
func (c *Client) ScanKeys(ctx context.Context, pattern string, callback func(key string) error) error {
var cursor uint64
for {
var keys []string
var err error
keys, cursor, err = c.rdb.Scan(ctx, cursor, pattern, 100).Result()
if err != nil {
return err
}
for _, key := range keys {
if err := callback(key); err != nil {
return err
}
}
if cursor == 0 {
break
}
}
return nil
}
const (
RoomTTL = 30 * time.Minute
PlayerTTL = 10 * time.Minute
)
func RoomKey(roomID string) string {
return "room:" + roomID
}
func PlayerKey(playerID string) string {
return "player:" + playerID
}
func RoomPlayersKey(roomID string) string {
return "room:" + roomID + ":players"
}
func RoomDeckKey(roomID string) string {
return "room:" + roomID + ":deck"
}
func RoomDiscardKey(roomID string) string {
return "room:" + roomID + ":discard"
}
func RoomTTLKey(roomID string) string {
return "room:" + roomID + ":ttl"
}
func PlayerTTLKey(playerID string) string {
return "player:" + playerID + ":ttl"
}
func CaptchaKey(captchaID string) string {
return "captcha:" + captchaID
}
func UserKey(username string) string {
return "user:" + username
}
func UserRoomKey(userID string) string {
return "user:" + userID + ":room"
}
func SessionKey(token string) string {
return "session:" + token
}
func UserIDToUsernameKey(userID string) string {
return "user_id:" + userID + ":username"
}