commit
This commit is contained in:
169
go_backend/service/cache_service.go
Normal file
169
go_backend/service/cache_service.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"ai_xhs/database"
|
||||
"ai_xhs/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CacheService 缓存管理服务 - 统一管理缓存键和清除策略
|
||||
type CacheService struct{}
|
||||
|
||||
func NewCacheService() *CacheService {
|
||||
return &CacheService{}
|
||||
}
|
||||
|
||||
// 缓存键前缀常量
|
||||
const (
|
||||
CacheKeyPrefixUser = "user:profile:"
|
||||
CacheKeyPrefixAuthor = "author:user:"
|
||||
CacheKeyPrefixXHSStatus = "xhs:status:"
|
||||
CacheKeyPrefixProducts = "products:enterprise:"
|
||||
CacheKeyPrefixRateLimit = "rate:sms:"
|
||||
CacheKeyPrefixLock = "lock:"
|
||||
)
|
||||
|
||||
// GetUserCacheKey 获取用户缓存键
|
||||
func (s *CacheService) GetUserCacheKey(userID int) string {
|
||||
return fmt.Sprintf("%s%d", CacheKeyPrefixUser, userID)
|
||||
}
|
||||
|
||||
// GetAuthorCacheKey 获取作者缓存键
|
||||
func (s *CacheService) GetAuthorCacheKey(userID int) string {
|
||||
return fmt.Sprintf("%s%d", CacheKeyPrefixAuthor, userID)
|
||||
}
|
||||
|
||||
// GetXHSStatusCacheKey 获取小红书状态缓存键
|
||||
func (s *CacheService) GetXHSStatusCacheKey(userID int) string {
|
||||
return fmt.Sprintf("%s%d", CacheKeyPrefixXHSStatus, userID)
|
||||
}
|
||||
|
||||
// GetProductsCacheKey 获取产品列表缓存键
|
||||
func (s *CacheService) GetProductsCacheKey(enterpriseID, page, pageSize int) string {
|
||||
return fmt.Sprintf("%spage:%d:size:%d", CacheKeyPrefixProducts+fmt.Sprintf("%d:", enterpriseID), page, pageSize)
|
||||
}
|
||||
|
||||
// GetRateLimitKey 获取限流键
|
||||
func (s *CacheService) GetRateLimitKey(phone string) string {
|
||||
return fmt.Sprintf("%s%s", CacheKeyPrefixRateLimit, phone)
|
||||
}
|
||||
|
||||
// GetLockKey 获取分布式锁键
|
||||
func (s *CacheService) GetLockKey(resource string) string {
|
||||
return fmt.Sprintf("%s%s", CacheKeyPrefixLock, resource)
|
||||
}
|
||||
|
||||
// ClearUserRelatedCache 清除用户相关的所有缓存
|
||||
func (s *CacheService) ClearUserRelatedCache(ctx context.Context, userID int) error {
|
||||
keys := []string{
|
||||
s.GetUserCacheKey(userID),
|
||||
s.GetAuthorCacheKey(userID),
|
||||
s.GetXHSStatusCacheKey(userID),
|
||||
}
|
||||
|
||||
if err := utils.DelCache(ctx, keys...); err != nil {
|
||||
log.Printf("清除用户缓存失败 (userID=%d): %v", userID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("已清除用户相关缓存: userID=%d", userID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearProductsCache 清除企业的产品列表缓存
|
||||
func (s *CacheService) ClearProductsCache(ctx context.Context, enterpriseID int) error {
|
||||
// 使用模糊匹配删除所有分页缓存
|
||||
pattern := fmt.Sprintf("%s%d:*", CacheKeyPrefixProducts, enterpriseID)
|
||||
|
||||
// 注意: 这需要扫描所有键,生产环境建议记录所有已创建的缓存键
|
||||
// 这里简化处理,实际应该维护一个产品缓存键集合
|
||||
log.Printf("需要清除产品缓存: enterpriseID=%d, pattern=%s", enterpriseID, pattern)
|
||||
log.Printf("建议: 在产品更新时调用此方法")
|
||||
|
||||
// 简化版: 只清除前几页的缓存
|
||||
for page := 1; page <= 10; page++ {
|
||||
for _, pageSize := range []int{10, 20, 50} {
|
||||
key := s.GetProductsCacheKey(enterpriseID, page, pageSize)
|
||||
utils.DelCache(ctx, key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AcquireLock 获取分布式锁
|
||||
func (s *CacheService) AcquireLock(ctx context.Context, resource string, ttl time.Duration) (bool, error) {
|
||||
lockKey := s.GetLockKey(resource)
|
||||
return utils.SetCacheNX(ctx, lockKey, "locked", ttl)
|
||||
}
|
||||
|
||||
// ReleaseLock 释放分布式锁
|
||||
func (s *CacheService) ReleaseLock(ctx context.Context, resource string) error {
|
||||
lockKey := s.GetLockKey(resource)
|
||||
return utils.DelCache(ctx, lockKey)
|
||||
}
|
||||
|
||||
// WithLock 使用分布式锁执行函数
|
||||
func (s *CacheService) WithLock(ctx context.Context, resource string, ttl time.Duration, fn func() error) error {
|
||||
// 尝试获取锁
|
||||
log.Printf("[分布式锁] 尝试获取锁: %s (TTL: %v)", resource, ttl)
|
||||
acquired, err := s.AcquireLock(ctx, resource, ttl)
|
||||
if err != nil {
|
||||
log.Printf("[分布式锁] 获取锁失败: %s, 错误: %v", resource, err)
|
||||
return fmt.Errorf("获取锁失败: %w", err)
|
||||
}
|
||||
|
||||
if !acquired {
|
||||
log.Printf("[分布式锁] 锁已被占用: %s", resource)
|
||||
// 检查锁的剩余时间
|
||||
lockKey := s.GetLockKey(resource)
|
||||
ttl, _ := database.RDB.TTL(ctx, lockKey).Result()
|
||||
return fmt.Errorf("资源被锁定,请稍后重试(剩余时间: %v)", ttl)
|
||||
}
|
||||
|
||||
log.Printf("[分布式锁] 成功获取锁: %s", resource)
|
||||
|
||||
// 确保释放锁
|
||||
defer func() {
|
||||
if err := s.ReleaseLock(ctx, resource); err != nil {
|
||||
log.Printf("[分布式锁] 释放锁失败 (resource=%s): %v", resource, err)
|
||||
} else {
|
||||
log.Printf("[分布式锁] 成功释放锁: %s", resource)
|
||||
}
|
||||
}()
|
||||
|
||||
// 执行函数
|
||||
log.Printf("[分布式锁] 开始执行受保护的函数: %s", resource)
|
||||
return fn()
|
||||
}
|
||||
|
||||
// SetCacheWithNullProtection 设置缓存(带空值保护,防止缓存穿透)
|
||||
func (s *CacheService) SetCacheWithNullProtection(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
|
||||
if value == nil {
|
||||
// 缓存空值,但使用较短的过期时间(1分钟)
|
||||
return utils.SetCache(ctx, key, "NULL", 1*time.Minute)
|
||||
}
|
||||
return utils.SetCache(ctx, key, value, ttl)
|
||||
}
|
||||
|
||||
// GetCacheWithNullCheck 获取缓存(检查空值标记)
|
||||
func (s *CacheService) GetCacheWithNullCheck(ctx context.Context, key string, dest interface{}) (bool, error) {
|
||||
var tempValue interface{}
|
||||
err := utils.GetCache(ctx, key, &tempValue)
|
||||
|
||||
if err != nil {
|
||||
// 缓存不存在
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 检查是否是空值标记
|
||||
if strValue, ok := tempValue.(string); ok && strValue == "NULL" {
|
||||
return true, fmt.Errorf("cached null value")
|
||||
}
|
||||
|
||||
// 正常获取缓存
|
||||
return true, utils.GetCache(ctx, key, dest)
|
||||
}
|
||||
Reference in New Issue
Block a user