SPMS_API/SPMS.Application/Services/FileService.cs

182 lines
6.6 KiB
C#

using SPMS.Application.DTOs.File;
using SPMS.Application.DTOs.Notice;
using SPMS.Application.Interfaces;
using SPMS.Domain.Common;
using SPMS.Domain.Entities;
using SPMS.Domain.Exceptions;
using SPMS.Domain.Interfaces;
namespace SPMS.Application.Services;
public class FileService : IFileService
{
private readonly IFileRepository _fileRepository;
private readonly IFileStorageService _fileStorageService;
private readonly IServiceRepository _serviceRepository;
private readonly IAdminRepository _adminRepository;
private readonly IUnitOfWork _unitOfWork;
private static readonly HashSet<string> AllowedImageExtensions = new(StringComparer.OrdinalIgnoreCase)
{ ".jpg", ".jpeg", ".png", ".gif" };
private static readonly HashSet<string> AllowedCsvExtensions = new(StringComparer.OrdinalIgnoreCase)
{ ".csv" };
private const long MaxImageSize = 5 * 1024 * 1024; // 5MB
private const long MaxCsvSize = 50 * 1024 * 1024; // 50MB
public FileService(
IFileRepository fileRepository,
IFileStorageService fileStorageService,
IServiceRepository serviceRepository,
IAdminRepository adminRepository,
IUnitOfWork unitOfWork)
{
_fileRepository = fileRepository;
_fileStorageService = fileStorageService;
_serviceRepository = serviceRepository;
_adminRepository = adminRepository;
_unitOfWork = unitOfWork;
}
public async Task<FileUploadResponseDto> UploadAsync(
long serviceId, long adminId, Stream fileStream,
string fileName, long fileSize, string fileType)
{
ValidateFileType(fileType, fileName, fileSize);
var extension = Path.GetExtension(fileName).ToLowerInvariant();
var mimeType = GetMimeType(extension);
var savedPath = await _fileStorageService.SaveAsync(serviceId, fileName, fileStream);
var entity = new FileEntity
{
ServiceId = serviceId,
FileName = fileName,
FilePath = savedPath,
FileSize = fileSize,
FileType = fileType.ToLowerInvariant(),
MimeType = mimeType,
CreatedAt = DateTime.UtcNow,
CreatedBy = adminId
};
await _fileRepository.AddAsync(entity);
await _unitOfWork.SaveChangesAsync();
return new FileUploadResponseDto
{
FileId = entity.Id,
FileName = entity.FileName,
FileUrl = _fileStorageService.GetFileUrl(entity.FilePath),
FileSize = entity.FileSize,
FileType = entity.FileType,
CreatedAt = entity.CreatedAt
};
}
public async Task<FileInfoResponseDto> GetInfoAsync(long serviceId, long fileId)
{
var file = await _fileRepository.GetByIdAndServiceAsync(fileId, serviceId);
if (file == null || file.IsDeleted)
throw new SpmsException(ErrorCodes.FileNotFound, "존재하지 않는 파일입니다.", 404);
var service = await _serviceRepository.GetByIdAsync(file.ServiceId);
var admin = await _adminRepository.GetByIdAsync(file.CreatedBy);
return new FileInfoResponseDto
{
FileId = file.Id,
ServiceCode = service?.ServiceCode ?? string.Empty,
FileName = file.FileName,
FileUrl = _fileStorageService.GetFileUrl(file.FilePath),
FileSize = file.FileSize,
FileType = file.FileType,
UploadedBy = admin?.Email ?? string.Empty,
CreatedAt = file.CreatedAt
};
}
public async Task<FileListResponseDto> GetListAsync(long serviceId, FileListRequestDto request)
{
var (items, totalCount) = await _fileRepository.GetPagedByServiceAsync(
serviceId, request.Page, request.Size, request.FileType);
var totalPages = (int)Math.Ceiling((double)totalCount / request.Size);
return new FileListResponseDto
{
Items = items.Select(f => new FileSummaryDto
{
FileId = f.Id,
FileName = f.FileName,
FileUrl = _fileStorageService.GetFileUrl(f.FilePath),
FileSize = f.FileSize,
FileType = f.FileType,
CreatedAt = f.CreatedAt
}).ToList(),
Pagination = new PaginationDto
{
Page = request.Page,
Size = request.Size,
TotalCount = totalCount,
TotalPages = totalPages
}
};
}
public async Task DeleteAsync(long serviceId, long fileId)
{
var file = await _fileRepository.GetByIdAndServiceAsync(fileId, serviceId);
if (file == null || file.IsDeleted)
throw new SpmsException(ErrorCodes.FileNotFound, "존재하지 않는 파일입니다.", 404);
file.IsDeleted = true;
file.DeletedAt = DateTime.UtcNow;
_fileRepository.Update(file);
await _unitOfWork.SaveChangesAsync();
}
private static void ValidateFileType(string fileType, string fileName, long fileSize)
{
var extension = Path.GetExtension(fileName).ToLowerInvariant();
switch (fileType.ToLowerInvariant())
{
case "image":
if (!AllowedImageExtensions.Contains(extension))
throw new SpmsException(ErrorCodes.FileTypeNotAllowed,
"허용되지 않는 이미지 형식입니다. (jpg, png, gif만 가능)", 400);
if (fileSize > MaxImageSize)
throw new SpmsException(ErrorCodes.FileSizeExceeded,
"이미지 파일은 5MB를 초과할 수 없습니다.", 400);
break;
case "csv":
if (!AllowedCsvExtensions.Contains(extension))
throw new SpmsException(ErrorCodes.FileTypeNotAllowed,
"허용되지 않는 파일 형식입니다. (csv만 가능)", 400);
if (fileSize > MaxCsvSize)
throw new SpmsException(ErrorCodes.FileSizeExceeded,
"CSV 파일은 50MB를 초과할 수 없습니다.", 400);
break;
default:
throw new SpmsException(ErrorCodes.FileTypeNotAllowed,
"유효하지 않은 파일 타입입니다. (image, csv만 가능)", 400);
}
}
private static string GetMimeType(string extension)
{
return extension switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".csv" => "text/csv",
_ => "application/octet-stream"
};
}
}