Files
backend/internal/service/notification_service.go
lan d9aa4b46c3
All checks were successful
Build Backend / build (push) Successful in 4m55s
Build Backend / build-docker (push) Successful in 10m34s
refactor(server): decouple services and improve architecture
- Introduce interfaces for all major services (JWT, PostAI, Comment, Message, Notification, QRCodeLogin, Upload, Vote, etc.) to support dependency inversion.
- Move query parameters from `internal/dto` to a new `internal/query` package to separate request payloads from data transfer objects.
- Refactor `internal/router` to use embedded `RouterDeps` for cleaner dependency management.
- Decouple handlers from repositories by injecting services instead of direct repository access, ensuring proper layering.
- Improve database initialization by moving it from `internal/model` to `internal/database`.
- Optimize message decryption by implementing a more efficient `BatchDecrypt` method in `MessageEncryptor` using a worker pool.
- Enhance error handling and security by implementing fail-fast checks for encryption key length during startup.
- Clean up unused code, including the `avatar` package and several unused DTOs.
2026-06-15 03:41:59 +08:00

178 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"context"
"time"
"with_you/internal/cache"
apperrors "with_you/internal/errors"
"with_you/internal/model"
"with_you/internal/pkg/cursor"
"with_you/internal/repository"
)
// 缓存TTL常量
const (
NotificationUnreadCountTTL = 30 * time.Second // 通知未读数缓存30秒
NotificationNullTTL = 5 * time.Second
NotificationCacheJitter = 0.1
)
// NotificationService 通知服务接口
type NotificationService interface {
Create(ctx context.Context, userID string, notificationType model.NotificationType, title, content string) (*model.Notification, error)
GetByUserID(ctx context.Context, userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error)
MarkAsReadWithUserID(ctx context.Context, id, userID string) error
MarkAllAsRead(ctx context.Context, userID string) error
GetUnreadCount(ctx context.Context, userID string) (int64, error)
DeleteNotification(ctx context.Context, id, userID string) error
ClearAllNotifications(ctx context.Context, userID string) error
GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error)
}
// notificationService 通知服务实现
type notificationService struct {
notificationRepo repository.NotificationRepository
cache cache.Cache
}
// NewNotificationService 创建通知服务
func NewNotificationService(notificationRepo repository.NotificationRepository, cacheBackend cache.Cache) NotificationService {
return &notificationService{
notificationRepo: notificationRepo,
cache: cacheBackend,
}
}
// 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)
}
// MarkAsReadWithUserID 标记为已读带用户ID验证所有权
func (s *notificationService) MarkAsReadWithUserID(ctx context.Context, id, userID string) error {
notification, err := s.notificationRepo.GetByID(id)
if err != nil {
return err
}
if notification.UserID != userID {
return apperrors.ErrForbidden
}
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
}
// 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
}
// GetNotificationsByCursor 游标分页获取用户通知
func (s *notificationService) GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error) {
return s.notificationRepo.GetNotificationsByCursor(ctx, userID, unreadOnly, req)
}
// 错误定义
var ErrUnauthorizedNotification = apperrors.ErrUnauthorizedNotification