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() }