- StatisticInternalProvider: TrackEvent/BatchTrackEvent
- StatisticCombinedProvider: all 9 RPCs (7 dashboard + 2 event) on single service
- materializer: 4 MV REFRESH CONCURRENTLY + pg_try_advisory_lock + refresh_log
- dashboard_repo: 7 aggregation SQLs (week_rank / 7d curve / top5 / level dist / upgrade progress)
- dashboard_service: 7 RPCs with Redis 5min TTL + cache miss protection (1min empty)
- Cache wrapper: JSON serialize + format dash:{rpc}:{starID}:{userID}
- main.go: integrated workers + Dubbo triple server :20009
- cross-service userService.GetFanProfile (for crystal_balance)
- client/user_rpc_client.go
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
1.4 KiB
Go
65 lines
1.4 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()
|
||
}
|
||
|
||
// CacheKey 看板缓存 key 格式
|
||
func CacheKey(rpc string, starID, userID int64) string {
|
||
return fmt.Sprintf("dash:%s:%d:%d", rpc, starID, userID)
|
||
}
|