package service import ( "context" "time" "carrot_bbs/internal/cache" "carrot_bbs/internal/model" "carrot_bbs/internal/repository" ) // 缓存TTL常量 const ( NotificationUnreadCountTTL = 30 * time.Second // 通知未读数缓存30秒 NotificationNullTTL = 5 * time.Second NotificationCacheJitter = 0.1 ) // NotificationService 通知服务 type NotificationService struct { notificationRepo *repository.NotificationRepository cache cache.Cache } // NewNotificationService 创建通知服务 func NewNotificationService(notificationRepo *repository.NotificationRepository) *NotificationService { return &NotificationService{ notificationRepo: notificationRepo, cache: cache.GetCache(), } } // Create 创建通知 func (s *NotificationService) Create(ctx context.Context, userID string, notificationType model.NotificationType, title, content string) (*model.Notification, error) { notification := &model.Notification{ UserID: userID, Type: notificationType, Title: title, Content: content, IsRead: false, } err := s.notificationRepo.Create(notification) if err != nil { return nil, err } // 失效未读数缓存 cache.InvalidateUnreadSystem(s.cache, userID) return notification, nil } // GetByUserID 获取用户通知 func (s *NotificationService) GetByUserID(ctx context.Context, userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error) { return s.notificationRepo.GetByUserID(userID, page, pageSize, unreadOnly) } // MarkAsRead 标记为已读 func (s *NotificationService) MarkAsRead(ctx context.Context, id string) error { err := s.notificationRepo.MarkAsRead(id) if err != nil { return err } // 注意:这里无法获取userID,所以不在缓存中失效 // 调用方应该使用MarkAsReadWithUserID方法 return nil } // MarkAsReadWithUserID 标记为已读(带用户ID,用于缓存失效) func (s *NotificationService) MarkAsReadWithUserID(ctx context.Context, id, userID string) error { err := s.notificationRepo.MarkAsRead(id) if err != nil { return err } // 失效未读数缓存 cache.InvalidateUnreadSystem(s.cache, userID) return nil } // MarkAllAsRead 标记所有为已读 func (s *NotificationService) MarkAllAsRead(ctx context.Context, userID string) error { err := s.notificationRepo.MarkAllAsRead(userID) if err != nil { return err } // 失效未读数缓存 cache.InvalidateUnreadSystem(s.cache, userID) return nil } // Delete 删除通知 func (s *NotificationService) Delete(ctx context.Context, id string) error { return s.notificationRepo.Delete(id) } // GetUnreadCount 获取未读数量(带缓存) func (s *NotificationService) GetUnreadCount(ctx context.Context, userID string) (int64, error) { cacheSettings := cache.GetSettings() unreadTTL := cacheSettings.UnreadCountTTL if unreadTTL <= 0 { unreadTTL = NotificationUnreadCountTTL } nullTTL := cacheSettings.NullTTL if nullTTL <= 0 { nullTTL = NotificationNullTTL } jitter := cacheSettings.JitterRatio if jitter <= 0 { jitter = NotificationCacheJitter } // 生成缓存键 cacheKey := cache.UnreadSystemKey(userID) return cache.GetOrLoadTyped[int64]( s.cache, cacheKey, unreadTTL, jitter, nullTTL, func() (int64, error) { return s.notificationRepo.GetUnreadCount(userID) }, ) } // DeleteNotification 删除通知(带用户验证) func (s *NotificationService) DeleteNotification(ctx context.Context, id, userID string) error { // 先检查通知是否属于该用户 notification, err := s.notificationRepo.GetByID(id) if err != nil { return err } if notification.UserID != userID { return ErrUnauthorizedNotification } err = s.notificationRepo.Delete(id) if err != nil { return err } // 失效未读数缓存 cache.InvalidateUnreadSystem(s.cache, userID) return nil } // ClearAllNotifications 清空所有通知 func (s *NotificationService) ClearAllNotifications(ctx context.Context, userID string) error { err := s.notificationRepo.DeleteAllByUserID(userID) if err != nil { return err } // 失效未读数缓存 cache.InvalidateUnreadSystem(s.cache, userID) return nil } // 错误定义 var ErrUnauthorizedNotification = &ServiceError{Code: 403, Message: "unauthorized to delete this notification"}