84 lines
2.1 KiB
Go
84 lines
2.1 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/redis/go-redis/v9"
|
||
)
|
||
|
||
// Cache Redis 缓存封装(5min TTL + 1min 空值 TTL 防穿透)
|
||
type Cache struct {
|
||
rdb *redis.Client
|
||
ttl time.Duration
|
||
emptyTTL time.Duration
|
||
missedHits int64
|
||
}
|
||
|
||
// NewCache 构造
|
||
func NewCache(rdb *redis.Client) *Cache {
|
||
return &Cache{
|
||
rdb: rdb,
|
||
ttl: 5 * time.Minute,
|
||
emptyTTL: 1 * time.Minute,
|
||
}
|
||
}
|
||
|
||
// GetJSON 取缓存 + JSON 反序列化
|
||
func (c *Cache) GetJSON(ctx context.Context, key string, dst interface{}) (bool, error) {
|
||
v, err := c.rdb.Get(ctx, key).Result()
|
||
if err == redis.Nil {
|
||
return false, nil
|
||
}
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
if v == "null" {
|
||
return false, nil // 缓存穿透防护的空标记
|
||
}
|
||
if err := json.Unmarshal([]byte(v), dst); err != nil {
|
||
return false, err
|
||
}
|
||
return true, nil
|
||
}
|
||
|
||
// SetJSON 序列化 + 缓存(5min TTL)
|
||
func (c *Cache) SetJSON(ctx context.Context, key string, value interface{}) error {
|
||
b, err := json.Marshal(value)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.rdb.Set(ctx, key, b, c.ttl).Err()
|
||
}
|
||
|
||
// SetEmpty 缓存空值(1min TTL,防穿透)
|
||
func (c *Cache) SetEmpty(ctx context.Context, key string) error {
|
||
return c.rdb.Set(ctx, key, "null", c.emptyTTL).Err()
|
||
}
|
||
|
||
// Delete 删除单个缓存 key
|
||
func (c *Cache) Delete(ctx context.Context, key string) error {
|
||
return c.rdb.Del(ctx, key).Err()
|
||
}
|
||
|
||
// DeleteDashboardCache 删除指定 (starID, userID) 的所有看板缓存
|
||
// 在事件落库后调用,确保下次看板请求能读到最新数据
|
||
func (c *Cache) DeleteDashboardCache(ctx context.Context, starID, userID int64) error {
|
||
rpcKeys := []string{
|
||
"today_overview", "7day_income_curve", "exhibition_summary",
|
||
"like_income_by_level", "top_assets", "level_distribution", "upgrade_progress",
|
||
}
|
||
keys := make([]string, len(rpcKeys))
|
||
for i, rpc := range rpcKeys {
|
||
keys[i] = CacheKey(rpc, starID, userID)
|
||
}
|
||
return c.rdb.Del(ctx, keys...).Err()
|
||
}
|
||
|
||
// CacheKey 看板缓存 key 格式
|
||
func CacheKey(rpc string, starID, userID int64) string {
|
||
return fmt.Sprintf("dash:%s:%d:%d", rpc, starID, userID)
|
||
}
|