161 lines
4.8 KiB
Go
161 lines
4.8 KiB
Go
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,
|
|
})
|
|
}
|