Files
cache_manager/manager/cache_manager.go
T
2026-05-16 19:54:12 +08:00

202 lines
5.4 KiB
Go

package manager
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"time"
"gorm.io/gorm"
)
// 缓存抽象管理器
//
// 作用
// 1. 组装好缓存上下Key,简化调用负担
// 2. 封装了批量操作的逻辑,提升性能
// CacheManager 针对特定类型的缓存管理器
type CacheManager[T any] struct {
tx *gorm.DB
options *managerOptions
cache ICache
prefix string
idGetter func(*T) int64
}
// NewCacheManager 创建特定类型的管理器
func NewCacheManager[T any](tx *gorm.DB, c ICache, prefix string, idGetter func(*T) int64, ops ...OptionFunc) *CacheManager[T] {
options := defaultManagerOptions()
for _, op := range ops {
op(options)
}
return &CacheManager[T]{
options: options,
cache: c,
prefix: prefix,
idGetter: idGetter,
tx: tx,
}
}
// Get 获取数据(带类型断言)
func (m *CacheManager[T]) Get(ctx context.Context, key string) (*T, error) {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
t := new(T)
err := m.cache.Get(ctx, fullKey, t)
if err != nil {
return nil, err
}
return t, nil
}
func (m *CacheManager[T]) GetList(ctx context.Context, key string) ([]T, error) {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
t := make([]T, 0)
err := m.cache.Get(ctx, fullKey, &t)
if err != nil {
return nil, err
}
return t, nil
}
func (m *CacheManager[T]) SetList(ctx context.Context, key string, data []T, ttl time.Duration) error {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
return m.cache.Set(ctx, fullKey, data, ttl, []int64{})
}
// Set 设置数据
func (m *CacheManager[T]) Set(ctx context.Context, key string, data *T, ttl time.Duration, ids []int64) error {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
return m.cache.Set(ctx, fullKey, data, ttl, ids)
}
// GetByAny data注意必须指针
func (m *CacheManager[T]) GetByAny(ctx context.Context, key string, data any) error {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
return m.cache.Get(ctx, fullKey, data)
}
// SetByAny 设置数据
func (m *CacheManager[T]) SetByAny(ctx context.Context, key string, data any, ttl time.Duration, ids []int64) error {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
return m.cache.Set(ctx, fullKey, data, ttl, ids)
}
// Del 删除数据
func (m *CacheManager[T]) Del(ctx context.Context, key string) error {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
return m.cache.Del(ctx, fullKey)
}
func (m *CacheManager[T]) Remove(ctx context.Context, ids []int64) error {
return m.cache.Remove(ctx, ids)
}
func (m *CacheManager[T]) BatchDel(ctx context.Context, keys []string) error {
fullKeys := make([]string, 0, len(keys))
for _, key := range keys {
fullKey := fmt.Sprintf("%s:%s", m.prefix, key)
m.options.logger.Infof(ctx, "delete redis key:%s", fullKey)
fullKeys = append(fullKeys, fullKey)
}
return m.cache.BatchDel(ctx, fullKeys)
}
// BatchGet 批量获取,只会获取到存在的key,返回的map中不存在的key表示缓存未命中
func (m *CacheManager[T]) BatchGet(ctx context.Context, keys []string) (map[string]*T, error) {
// key转换
keyMap := make(map[string]string)
newKeys := make([]string, 0, len(keys))
for _, key := range keys {
newKey := fmt.Sprintf("%s:%s", m.prefix, key)
keyMap[newKey] = key
newKeys = append(newKeys, newKey)
}
m.options.logger.Infof(ctx, "BatchGet keyMap:%+v", keyMap)
var resp map[string]*T
var err error
switch cache := m.cache.(type) {
case *CacheRedis:
resp, err = BatchGetRedis[T](cache, ctx, newKeys)
case *CacheLocal:
resp, err = BatchGetLocal[T](cache, ctx, newKeys)
case *CacheRedisHash:
resp, err = BatchGetRedisHash[T](cache, ctx, newKeys)
default:
return nil, errors.New("unsupported cache type for batch get")
}
if err != nil {
m.options.logger.Errorf(ctx, "BatchGet error: %v", err)
return nil, err
}
// m.options.logger.Infof(ctx, "BatchGet resp:%+v", resp)
var result = make(map[string]*T)
for k, v := range resp {
// m.options.logger.Infof(ctx, "BatchGet k:%v,v:%v", k, keyMap[k])
result[keyMap[k]] = v
}
return result, err
}
func (l *CacheManager[T]) GetByFields(ctx context.Context, fields map[string]interface{}, ttl time.Duration) ([]*T, error) {
key := l.fieldsToKey(fields)
resp := make([]*T, 0)
err := l.cache.Get(ctx, key, &resp)
if err != nil {
return nil, err
}
return resp, nil
}
func (l *CacheManager[T]) UpdateByFields(ctx context.Context, fields map[string]interface{}, ttl time.Duration) ([]*T, error) {
var data []*T
err := l.tx.WithContext(ctx).Where(fields).Find(&data).Error
if err != nil {
l.options.logger.Errorf(ctx, "db query error: %v", err)
return nil, err
}
ids := make([]int64, 0, len(data))
for _, v := range data {
ids = append(ids, l.idGetter(v))
}
key := l.fieldsToKey(fields)
// l.options.logger.Infof(ctx, "UpdateByFields key:%v", key)
l.cache.Set(ctx, key, data, ttl, ids)
return data, nil
}
func (l *CacheManager[T]) DelByFields(ctx context.Context, fields map[string]interface{}) error {
return l.cache.Del(ctx, l.fieldsToKey(fields))
}
func (l *CacheManager[T]) fieldsToKey(fields map[string]interface{}) string {
// 需要取出来,排序,然后组装成字符串
keys := make([]string, 0, len(fields))
for k := range fields {
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
buf.WriteString(l.prefix + ":{")
for i, key := range keys {
if i != 0 {
buf.WriteString(",")
}
buf.WriteString(key)
buf.WriteString(":")
buf.WriteString(fmt.Sprintf("%v", fields[key]))
}
buf.WriteString("}")
return buf.String()
}