170 lines
5.3 KiB
Go
170 lines
5.3 KiB
Go
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)
|
||
}
|