-feat 修复部分bug

This commit is contained in:
2026-01-27 18:42:35 +08:00
parent a339a9adce
commit 6422f2c45f
10 changed files with 1001 additions and 1 deletions

View File

@@ -0,0 +1,160 @@
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
Type models.NotificationType
Title string
Content string
RelatedID *uint
}
// 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,
})
}