diff --git a/pkg/cache/common/common.go b/pkg/cache/common/common.go new file mode 100644 index 0000000..7a8f777 --- /dev/null +++ b/pkg/cache/common/common.go @@ -0,0 +1,122 @@ +package common + +import ( + "encoding/json" + "errors" + + "gitea.ddegame.cn/open/servicebase/pkg/cache" + "github.com/redis/go-redis/v9" +) + +type DBCacheFetcher[T any] func(fieldKey string) (*T, error) +type DBCacheFetcherMany[T any] func(fieldKeys []string) (map[string]*T, error) + +func GetOrCacheOne[T any](cli *redis.Client, hashKey string, fieldKey string, dbFetcher DBCacheFetcher[T]) (*T, error) { + if len(fieldKey) == 0 { + return nil, nil + } + var cachedStr string + var err error + if cachedStr, err = cli.HGet(cache.Ctx(), hashKey, fieldKey).Result(); err != nil && !errors.Is(err, redis.Nil) { + return nil, err + } + var model *T + if len(cachedStr) > 0 { + model = new(T) + if err = json.Unmarshal([]byte(cachedStr), model); err == nil { + return model, nil + } + } + if model, err = dbFetcher(fieldKey); err != nil { + return nil, err + } + if model == nil { + return nil, nil + } + bytes, marshalErr := json.Marshal(model) + if marshalErr != nil { + return model, nil + } + _, _ = cli.HSet(cache.Ctx(), hashKey, fieldKey, string(bytes)).Result() + return model, nil +} + +func GetOrCacheMany[T any](cli *redis.Client, hashKey string, fieldKeyList []string, dbFetcherMany DBCacheFetcherMany[T]) ([]*T, error) { + if len(fieldKeyList) == 0 { + return make([]*T, 0), nil + } + finalResults := make([]*T, len(fieldKeyList)) + missingKeysMap := make(map[string]int) + missingKeysList := make([]string, 0) + values, err := cli.HMGet(cache.Ctx(), hashKey, fieldKeyList...).Result() + if err != nil && !errors.Is(err, redis.Nil) { + return nil, err + } + for i, fieldKey := range fieldKeyList { + value := values[i] + if value == nil { + missingKeysMap[fieldKey] = i + missingKeysList = append(missingKeysList, fieldKey) + continue + } + cachedStr, ok := value.(string) + if !ok { + missingKeysMap[fieldKey] = i + missingKeysList = append(missingKeysList, fieldKey) + continue + } + model := new(T) + if jsonErr := json.Unmarshal([]byte(cachedStr), model); jsonErr == nil { + finalResults[i] = model + } else { + missingKeysMap[fieldKey] = i + missingKeysList = append(missingKeysList, fieldKey) + } + } + if len(missingKeysList) == 0 { + return finalResults, nil + } + dbModelsMap, err := dbFetcherMany(missingKeysList) + if err != nil { + return nil, err + } + cacheMap := make(map[string]any) + for fieldKey, model := range dbModelsMap { + originalIndex, found := missingKeysMap[fieldKey] + if !found { + continue + } + finalResults[originalIndex] = model + if model != nil { + if bytes, marshalErr := json.Marshal(model); marshalErr == nil { + cacheMap[fieldKey] = string(bytes) + } + } + } + if len(cacheMap) > 0 { + _, _ = cli.HSet(cache.Ctx(), hashKey, cacheMap).Result() + } + return finalResults, nil +} + +func DelCacheFields(cli *redis.Client, hashKey string, fieldKeyList ...string) error { + if len(fieldKeyList) == 0 { + return nil + } + _, err := cli.HDel(cache.Ctx(), hashKey, fieldKeyList...).Result() + if err != nil && !errors.Is(err, redis.Nil) { + return err + } + return nil +} + +func DelCacheHash(cli *redis.Client, hashKey string) error { + if len(hashKey) == 0 { + return nil + } + _, err := cli.Del(cache.Ctx(), hashKey).Result() + if err != nil && !errors.Is(err, redis.Nil) { + return err + } + return nil +}