- 添加通用查询选项、分类过滤选项及动态缓存键生成 - 新增分类创建与更新时间字段 当前实现尚未完成,留待后续继续设计: - 明确通用查询选项、模型过滤条件与缓存策略的边界 - 补齐数据库回源,修正分类返回类型及过滤条件生效问题 - 修正分页偏移计算,增加参数边界校验和稳定排序 - 完善缓存故障降级、空结果缓存及写后失效策略 - 修正缓存键参数编码歧义,补齐查询名和版本前缀 - 补充测试并验证编译与查询行为
86 lines
2.0 KiB
Go
86 lines
2.0 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/json/v2"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
type Entry[T any] struct {
|
|
Key string
|
|
Value T
|
|
TTL time.Duration
|
|
}
|
|
|
|
type Cache struct {
|
|
client *redis.Client
|
|
operationTimeout time.Duration
|
|
}
|
|
|
|
func NewCache(client *redis.Client, timeout time.Duration) *Cache {
|
|
return &Cache{
|
|
client: client,
|
|
operationTimeout: timeout,
|
|
}
|
|
}
|
|
|
|
func (c *Cache) Get[T any](ctx context.Context, key string) (T, bool, error) {
|
|
var zero T
|
|
operationCtx, cancel := context.WithTimeout(ctx, c.operationTimeout)
|
|
defer cancel()
|
|
|
|
payload, err := c.client.Get(operationCtx, key).Result()
|
|
cancel()
|
|
if errors.Is(err, redis.Nil) {
|
|
return zero, false, nil
|
|
}
|
|
if err != nil {
|
|
return zero, false, fmt.Errorf("读取缓存失败:%w", err)
|
|
}
|
|
var value T
|
|
if err := json.Unmarshal([]byte(payload), &value); err != nil {
|
|
return zero, false, fmt.Errorf("缓存解码失败:%w", err)
|
|
}
|
|
return value, true, nil
|
|
}
|
|
|
|
func (c *Cache) Set[T any](ctx context.Context, entry Entry[T]) error {
|
|
if entry.TTL < 0 {
|
|
return fmt.Errorf("缓存过期时间必须大于等于零: %v", entry.TTL)
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return fmt.Errorf("写入缓存失败:%w", err)
|
|
}
|
|
payload, err := json.Marshal(entry.Value)
|
|
if err != nil {
|
|
return fmt.Errorf("缓存编码失败:%w", err)
|
|
}
|
|
operationCtx, cancel := context.WithTimeout(ctx, c.operationTimeout)
|
|
defer cancel()
|
|
if err := c.client.Set(operationCtx, entry.Key, payload, entry.TTL).Err(); err != nil {
|
|
return fmt.Errorf("写入缓存失败:%w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GenCacheKey(table, sql string, args ...any) (key string, err error) {
|
|
prefix := fmt.Sprintf("lyra:cache:%s:", table)
|
|
h := md5.New()
|
|
// 写入查询语句
|
|
h.Write([]byte(sql))
|
|
// 序列化并写入每个参数
|
|
for _, arg := range args {
|
|
jsonData, err := json.Marshal(arg)
|
|
if err != nil {
|
|
return "", fmt.Errorf("序列化参数失败:%w", err)
|
|
}
|
|
h.Write(jsonData)
|
|
}
|
|
return prefix + fmt.Sprintf("%x", h.Sum(nil)), nil
|
|
}
|