package service import ( "errors" "fmt" "accounting-app/internal/models" "accounting-app/internal/repository" ) // Notification service errors var ( ErrNotificationNotFound = errors.New("notification not found") ) // NotificationService handles business logic for notifications type NotificationService struct { repo *repository.NotificationRepository } // NewNotificationService creates a new NotificationService instance func NewNotificationService(repo *repository.NotificationRepository) *NotificationService { return &NotificationService{repo: repo} } // CreateNotificationInput represents input for creating a notification type CreateNotificationInput struct { UserID uint `json:"user_id" binding:"required"` Type models.NotificationType `json:"type" binding:"required"` Title string `json:"title" binding:"required"` Content string `json:"content" binding:"required"` RelatedID *uint `json:"related_id"` } // CreateNotification creates a new notification func (s *NotificationService) CreateNotification(input CreateNotificationInput) (*models.Notification, error) { notification := &models.Notification{ UserID: input.UserID, Type: input.Type, Title: input.Title, Content: input.Content, RelatedID: input.RelatedID, IsRead: false, } if err := s.repo.Create(notification); err != nil { return nil, fmt.Errorf("failed to create notification: %w", err) } return notification, nil } // GetNotification retrieves a notification by ID func (s *NotificationService) GetNotification(userID, id uint) (*models.Notification, error) { notification, err := s.repo.GetByID(userID, id) if err != nil { if errors.Is(err, repository.ErrNotificationNotFound) { return nil, ErrNotificationNotFound } return nil, err } return notification, nil } // NotificationListInput represents input for listing notifications type NotificationListInput struct { Type *models.NotificationType IsRead *bool Offset int Limit int } // ListNotifications retrieves notifications with pagination func (s *NotificationService) ListNotifications(userID uint, input NotificationListInput) (*repository.NotificationListResult, error) { limit := input.Limit if limit <= 0 { limit = 20 } options := repository.NotificationListOptions{ Type: input.Type, IsRead: input.IsRead, Offset: input.Offset, Limit: limit, } return s.repo.List(userID, options) } // MarkAsRead marks a notification as read func (s *NotificationService) MarkAsRead(userID, id uint) error { err := s.repo.MarkAsRead(userID, id) if err != nil { if errors.Is(err, repository.ErrNotificationNotFound) { return ErrNotificationNotFound } return err } return nil } // MarkAllAsRead marks all notifications as read func (s *NotificationService) MarkAllAsRead(userID uint) error { return s.repo.MarkAllAsRead(userID) } // DeleteNotification deletes a notification func (s *NotificationService) DeleteNotification(userID, id uint) error { err := s.repo.Delete(userID, id) if err != nil { if errors.Is(err, repository.ErrNotificationNotFound) { return ErrNotificationNotFound } return err } return nil } // GetUnreadCount returns the count of unread notifications func (s *NotificationService) GetUnreadCount(userID uint) (int64, error) { return s.repo.GetUnreadCount(userID) } // SendBudgetAlert creates a budget alert notification func (s *NotificationService) SendBudgetAlert(userID uint, budgetName string, usedPercentage float64, budgetID uint) (*models.Notification, error) { title := fmt.Sprintf("预算提醒: %s", budgetName) content := fmt.Sprintf("您的「%s」预算已使用 %.1f%%,请注意控制支出。", budgetName, usedPercentage) return s.CreateNotification(CreateNotificationInput{ UserID: userID, Type: models.NotificationTypeBudgetAlert, Title: title, Content: content, RelatedID: &budgetID, }) } // SendRecurringReminder creates a recurring transaction reminder func (s *NotificationService) SendRecurringReminder(userID uint, recurringName string, amount float64, recurringID uint) (*models.Notification, error) { title := fmt.Sprintf("周期交易提醒: %s", recurringName) content := fmt.Sprintf("您的周期交易「%s」(¥%.2f) 已自动执行。", recurringName, amount) return s.CreateNotification(CreateNotificationInput{ UserID: userID, Type: models.NotificationTypeRecurring, Title: title, Content: content, RelatedID: &recurringID, }) } // SendSystemNotification creates a system notification func (s *NotificationService) SendSystemNotification(userID uint, title, content string) (*models.Notification, error) { return s.CreateNotification(CreateNotificationInput{ UserID: userID, Type: models.NotificationTypeSystem, Title: title, Content: content, }) }