SPMS_API/SPMS.Application/Services/NotificationService.cs
SEAN c29a48163d improvement: Notification 도메인 구축 (#247)
- Domain: NotificationCategory enum, Notification entity, INotificationRepository
- Infrastructure: NotificationConfiguration, NotificationRepository, AppDbContext/DI 등록
- Migration: AddNotificationTable 생성 및 적용
- Application: DTO 7개, INotificationService, NotificationService, DI 등록
- API: NotificationController (summary, list, read, read-all)

Closes #247
2026-02-26 09:44:28 +09:00

105 lines
3.8 KiB
C#

using SPMS.Application.DTOs.Notice;
using SPMS.Application.DTOs.Notification;
using SPMS.Application.Interfaces;
using SPMS.Domain.Common;
using SPMS.Domain.Enums;
using SPMS.Domain.Exceptions;
using SPMS.Domain.Interfaces;
namespace SPMS.Application.Services;
public class NotificationService : INotificationService
{
private readonly INotificationRepository _notificationRepository;
private readonly IUnitOfWork _unitOfWork;
public NotificationService(
INotificationRepository notificationRepository,
IUnitOfWork unitOfWork)
{
_notificationRepository = notificationRepository;
_unitOfWork = unitOfWork;
}
public async Task<NotificationSummaryResponseDto> GetSummaryAsync(long adminId, NotificationSummaryRequestDto request)
{
var limit = request.Limit > 0 ? request.Limit : 5;
var recentItems = await _notificationRepository.GetRecentAsync(adminId, limit);
var unreadCount = await _notificationRepository.GetUnreadCountAsync(adminId);
return new NotificationSummaryResponseDto
{
RecentItems = recentItems.Select(MapToItemDto).ToList(),
UnreadCount = unreadCount
};
}
public async Task<NotificationListResponseDto> GetListAsync(long adminId, NotificationListRequestDto request)
{
// category 문자열 → enum 파싱
NotificationCategory? category = null;
if (!string.IsNullOrWhiteSpace(request.Category))
{
if (!Enum.TryParse<NotificationCategory>(request.Category, true, out var parsed))
throw new SpmsException(ErrorCodes.BadRequest, $"유효하지 않은 카테고리: {request.Category}");
category = parsed;
}
var (items, totalCount) = await _notificationRepository.GetPagedAsync(
adminId, category, request.From, request.To, request.IsRead, request.Page, request.Size);
var unreadCount = await _notificationRepository.GetUnreadCountAsync(adminId);
var totalPages = (int)Math.Ceiling((double)totalCount / request.Size);
return new NotificationListResponseDto
{
Items = items.Select(MapToItemDto).ToList(),
Pagination = new PaginationDto
{
Page = request.Page,
Size = request.Size,
TotalCount = totalCount,
TotalPages = totalPages
},
UnreadCount = unreadCount
};
}
public async Task<NotificationReadResponseDto> MarkAsReadAsync(long adminId, NotificationReadRequestDto request)
{
var notification = await _notificationRepository.GetByIdAndAdminAsync(request.NotificationId, adminId);
if (notification == null)
throw new SpmsException(ErrorCodes.NotFound, "알림을 찾을 수 없습니다.", 404);
// 멱등성: 이미 읽은 알림은 스킵
if (!notification.IsRead)
{
notification.IsRead = true;
notification.ReadAt = DateTime.UtcNow;
_notificationRepository.Update(notification);
await _unitOfWork.SaveChangesAsync();
}
var unreadCount = await _notificationRepository.GetUnreadCountAsync(adminId);
return new NotificationReadResponseDto { UnreadCount = unreadCount };
}
public async Task<NotificationReadResponseDto> MarkAllAsReadAsync(long adminId)
{
await _notificationRepository.MarkAllAsReadAsync(adminId);
return new NotificationReadResponseDto { UnreadCount = 0 };
}
private static NotificationItemDto MapToItemDto(Domain.Entities.Notification n) => new()
{
NotificationId = n.Id,
Category = n.Category.ToString().ToLower(),
Title = n.Title,
Content = n.Content,
LinkUrl = n.LinkUrl,
IsRead = n.IsRead,
ReadAt = n.ReadAt,
CreatedAt = n.CreatedAt
};
}