202 lines
5.4 KiB
Go
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()
|
|
}
|