refactor: introduce Wire dependency injection and interface-based architecture
- Add Google Wire for compile-time dependency injection - Replace concrete service types with interfaces across handlers - Remove global state (DB, cache) in favor of constructor injection - Split monolithic files into focused modules: - config: separate files for each config domain - dto: converters split by domain (user, post, message, group) - cache: separate metrics.go and redis_cache.go - Introduce unified apperrors package with string-based error codes - Add transaction support with context-aware repository methods - Upgrade golang.org/x/crypto from 0.17.0 to 0.26.0
This commit is contained in:
226
internal/cache/redis_cache.go
vendored
Normal file
226
internal/cache/redis_cache.go
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
redisPkg "carrot_bbs/internal/pkg/redis"
|
||||
)
|
||||
|
||||
// RedisCache Redis缓存实现
|
||||
type RedisCache struct {
|
||||
client *redisPkg.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewRedisCache 创建Redis缓存
|
||||
func NewRedisCache(client *redisPkg.Client) *RedisCache {
|
||||
return &RedisCache{
|
||||
client: client,
|
||||
ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
// Set 设置缓存值
|
||||
func (c *RedisCache) Set(key string, value interface{}, ttl time.Duration) {
|
||||
key = normalizeKey(key)
|
||||
// 将值序列化为JSON
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
recordSetError()
|
||||
log.Printf("[RedisCache] Failed to marshal value for key %s: %v", key, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.client.Set(c.ctx, key, data, ttl); err != nil {
|
||||
recordSetError()
|
||||
log.Printf("[RedisCache] Failed to set key %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get 获取缓存值
|
||||
func (c *RedisCache) Get(key string) (interface{}, bool) {
|
||||
key = normalizeKey(key)
|
||||
data, err := c.client.Get(c.ctx, key)
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return nil, false
|
||||
}
|
||||
log.Printf("[RedisCache] Failed to get key %s: %v", key, err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// 返回原始字符串,由调用侧决定如何解码为目标类型
|
||||
return data, true
|
||||
}
|
||||
|
||||
// Delete 删除缓存
|
||||
func (c *RedisCache) Delete(key string) {
|
||||
key = normalizeKey(key)
|
||||
recordInvalidate()
|
||||
if err := c.client.Del(c.ctx, key); err != nil {
|
||||
log.Printf("[RedisCache] Failed to delete key %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteByPrefix 根据前缀删除缓存
|
||||
func (c *RedisCache) DeleteByPrefix(prefix string) {
|
||||
prefix = normalizePrefix(prefix)
|
||||
// 使用原生客户端执行SCAN命令
|
||||
rdb := c.client.GetClient()
|
||||
var cursor uint64
|
||||
for {
|
||||
keys, nextCursor, err := rdb.Scan(c.ctx, cursor, prefix+"*", 100).Result()
|
||||
if err != nil {
|
||||
log.Printf("[RedisCache] Failed to scan keys with prefix %s: %v", prefix, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(keys) > 0 {
|
||||
recordInvalidateMultiple(int64(len(keys)))
|
||||
if err := c.client.Del(c.ctx, keys...); err != nil {
|
||||
log.Printf("[RedisCache] Failed to delete keys with prefix %s: %v", prefix, err)
|
||||
}
|
||||
}
|
||||
|
||||
cursor = nextCursor
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear 清空所有缓存
|
||||
func (c *RedisCache) Clear() {
|
||||
if settings.DisableFlushDB {
|
||||
log.Printf("[RedisCache] Skip FlushDB because cache.disable_flushdb=true")
|
||||
return
|
||||
}
|
||||
recordInvalidate()
|
||||
rdb := c.client.GetClient()
|
||||
if err := rdb.FlushDB(c.ctx).Err(); err != nil {
|
||||
log.Printf("[RedisCache] Failed to clear cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Exists 检查键是否存在
|
||||
func (c *RedisCache) Exists(key string) bool {
|
||||
key = normalizeKey(key)
|
||||
n, err := c.client.Exists(c.ctx, key)
|
||||
if err != nil {
|
||||
log.Printf("[RedisCache] Failed to check existence of key %s: %v", key, err)
|
||||
return false
|
||||
}
|
||||
return n > 0
|
||||
}
|
||||
|
||||
// Increment 增加计数器的值
|
||||
func (c *RedisCache) Increment(key string) int64 {
|
||||
return c.IncrementBy(key, 1)
|
||||
}
|
||||
|
||||
// IncrementBy 增加指定值
|
||||
func (c *RedisCache) IncrementBy(key string, value int64) int64 {
|
||||
key = normalizeKey(key)
|
||||
rdb := c.client.GetClient()
|
||||
result, err := rdb.IncrBy(c.ctx, key, value).Result()
|
||||
if err != nil {
|
||||
log.Printf("[RedisCache] Failed to increment key %s: %v", key, err)
|
||||
return 0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ==================== RedisCache Hash 操作 ====================
|
||||
|
||||
// HSet 设置 Hash 字段
|
||||
func (c *RedisCache) HSet(ctx context.Context, key string, field string, value interface{}) error {
|
||||
key = normalizeKey(key)
|
||||
return c.client.HSet(ctx, key, field, value)
|
||||
}
|
||||
|
||||
// HMSet 批量设置 Hash 字段
|
||||
func (c *RedisCache) HMSet(ctx context.Context, key string, values map[string]interface{}) error {
|
||||
key = normalizeKey(key)
|
||||
return c.client.HMSet(ctx, key, values)
|
||||
}
|
||||
|
||||
// HGet 获取 Hash 字段值
|
||||
func (c *RedisCache) HGet(ctx context.Context, key string, field string) (string, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.HGet(ctx, key, field)
|
||||
}
|
||||
|
||||
// HMGet 批量获取 Hash 字段值
|
||||
func (c *RedisCache) HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.HMGet(ctx, key, fields...)
|
||||
}
|
||||
|
||||
// HGetAll 获取 Hash 所有字段
|
||||
func (c *RedisCache) HGetAll(ctx context.Context, key string) (map[string]string, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.HGetAll(ctx, key)
|
||||
}
|
||||
|
||||
// HDel 删除 Hash 字段
|
||||
func (c *RedisCache) HDel(ctx context.Context, key string, fields ...string) error {
|
||||
key = normalizeKey(key)
|
||||
return c.client.HDel(ctx, key, fields...)
|
||||
}
|
||||
|
||||
// ==================== RedisCache Sorted Set 操作 ====================
|
||||
|
||||
// ZAdd 添加 Sorted Set 成员
|
||||
func (c *RedisCache) ZAdd(ctx context.Context, key string, score float64, member string) error {
|
||||
key = normalizeKey(key)
|
||||
return c.client.ZAdd(ctx, key, score, member)
|
||||
}
|
||||
|
||||
// ZRangeByScore 按分数范围获取成员(升序)
|
||||
func (c *RedisCache) ZRangeByScore(ctx context.Context, key string, min, max string, offset, count int64) ([]string, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.ZRangeByScore(ctx, key, min, max, offset, count)
|
||||
}
|
||||
|
||||
// ZRevRangeByScore 按分数范围获取成员(降序)
|
||||
func (c *RedisCache) ZRevRangeByScore(ctx context.Context, key string, max, min string, offset, count int64) ([]string, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.ZRevRangeByScore(ctx, key, max, min, offset, count)
|
||||
}
|
||||
|
||||
// ZRem 删除 Sorted Set 成员
|
||||
func (c *RedisCache) ZRem(ctx context.Context, key string, members ...interface{}) error {
|
||||
key = normalizeKey(key)
|
||||
return c.client.ZRem(ctx, key, members...)
|
||||
}
|
||||
|
||||
// ZCard 获取 Sorted Set 成员数量
|
||||
func (c *RedisCache) ZCard(ctx context.Context, key string) (int64, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.ZCard(ctx, key)
|
||||
}
|
||||
|
||||
// ==================== RedisCache 计数器操作 ====================
|
||||
|
||||
// Incr 原子递增(返回新值)
|
||||
func (c *RedisCache) Incr(ctx context.Context, key string) (int64, error) {
|
||||
key = normalizeKey(key)
|
||||
return c.client.Incr(ctx, key)
|
||||
}
|
||||
|
||||
// Expire 设置过期时间
|
||||
func (c *RedisCache) Expire(ctx context.Context, key string, ttl time.Duration) error {
|
||||
key = normalizeKey(key)
|
||||
_, err := c.client.Expire(ctx, key, ttl)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetRedisClient 获取Redis客户端
|
||||
func (c *RedisCache) GetRedisClient() *redisPkg.Client {
|
||||
return c.client
|
||||
}
|
||||
Reference in New Issue
Block a user