- 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
105 lines
3.8 KiB
C#
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
|
|
};
|
|
}
|