using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.Manuals; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Services; using Microsoft.Extensions.Configuration; namespace AsbCloudInfrastructure.Services; public class ManualCatalogService : IManualCatalogService { private const int IdFileCategory = 30000; private readonly IEnumerable validExtensions = new[] { ".pdf", ".mp4" }; private readonly string directoryFiles; private readonly IFileStorageRepository fileStorageRepository; private readonly IManualDirectoryRepository manualDirectoryRepository; private readonly ICrudRepository manualRepository; public ManualCatalogService(IFileStorageRepository fileStorageRepository, IManualDirectoryRepository manualDirectoryRepository, ICrudRepository manualRepository, IConfiguration configuration) { this.fileStorageRepository = fileStorageRepository; this.manualDirectoryRepository = manualDirectoryRepository; this.manualRepository = manualRepository; directoryFiles = configuration.GetValue("DirectoryManualFiles"); if (string.IsNullOrWhiteSpace(directoryFiles)) directoryFiles = "manuals"; } public async Task SaveFileAsync(int idDirectory, int idAuthor, string name, Stream stream, CancellationToken cancellationToken) { var extension = Path.GetExtension(name); if (!validExtensions.Contains(extension)) throw new ArgumentInvalidException( $"Невозможно загрузить файл с расширением '{extension}'. Допустимые форматы файлов: {string.Join(", ", validExtensions)}", extension); var path = await BuildFilePathAsync(idDirectory, name, cancellationToken); await fileStorageRepository.SaveFileAsync(path, stream, cancellationToken); var manual = new ManualDto { Name = name, DateDownload = DateTime.UtcNow, IdDirectory = idDirectory, IdCategory = IdFileCategory, IdAuthor = idAuthor }; return await manualRepository.InsertAsync(manual, cancellationToken); } public async Task AddDirectoryAsync(string name, int? idParent, CancellationToken cancellationToken) { if (idParent.HasValue && !await manualDirectoryRepository.IsExistsAsync(idParent.Value, cancellationToken)) throw new ArgumentInvalidException("Родительской директории не существует", nameof(idParent)); var directory = new ManualDirectoryDto { Name = name, IdParent = idParent, }; if (await IsExistDirectoryAsync(directory, cancellationToken)) throw new ArgumentInvalidException("Директория с таким названием уже существует", name); return await manualDirectoryRepository.InsertAsync(directory, cancellationToken); } public async Task UpdateDirectoryAsync(int id, string name, CancellationToken cancellationToken) { var directory = await manualDirectoryRepository.GetOrDefaultAsync(id, cancellationToken) ?? throw new ArgumentInvalidException($"Директории с Id: {id} не существует", nameof(id)); directory.Name = name; if (await IsExistDirectoryAsync(directory, cancellationToken)) throw new ArgumentInvalidException("Директория с таким названием уже существует", name); await manualDirectoryRepository.UpdateAsync(directory, cancellationToken); } public async Task DeleteDirectoryAsync(int id, CancellationToken cancellationToken) { var directory = await manualDirectoryRepository.GetOrDefaultAsync(id, cancellationToken); if (directory is null) return 0; var path = fileStorageRepository.MakeFilePath(directoryFiles, IdFileCategory.ToString(), await BuildDirectoryPathAsync(id, cancellationToken)); try { fileStorageRepository.DeleteDirectory(path, true); } catch (InvalidOperationException ex) { throw new ArgumentInvalidException(ex.Message, nameof(id)); } return await manualDirectoryRepository.DeleteAsync(directory.Id, cancellationToken); } public async Task DeleteFileAsync(int id, CancellationToken cancellationToken) { var manual = await manualRepository.GetOrDefaultAsync(id, cancellationToken); if (manual is null) return 0; var filePath = await BuildFilePathAsync(manual.IdDirectory, manual.Name, cancellationToken); fileStorageRepository.DeleteFile(filePath); return await manualRepository.DeleteAsync(manual.Id, cancellationToken); } public async Task<(Stream stream, string fileName)?> GetFileAsync(int id, CancellationToken cancellationToken) { var manual = await manualRepository.GetOrDefaultAsync(id, cancellationToken); if (manual is null) return null; var path = await BuildFilePathAsync(manual.IdDirectory, manual.Name, cancellationToken); var fileStream = new FileStream(path, FileMode.Open); return (fileStream, manual.Name); } private async Task IsExistDirectoryAsync(ManualDirectoryDto directory, CancellationToken cancellationToken) { var existingDirectory = await manualDirectoryRepository.GetOrDefaultAsync(directory.Name, directory.IdParent, cancellationToken); return existingDirectory is not null && directory.Id != existingDirectory.Id; } private async Task BuildDirectoryPathAsync(int idDirectory, CancellationToken cancellationToken) { var directiories = await manualDirectoryRepository.GetAllAsync(cancellationToken); var directory = directiories.FirstOrDefault(d => d.Id == idDirectory) ?? throw new ArgumentInvalidException($"Директории с Id: {idDirectory} не существует", nameof(idDirectory)); var pathSegments = new List { directory.Id }; while (directory.IdParent.HasValue) { directory = directiories.FirstOrDefault(d => d.Id == directory.IdParent.Value); pathSegments.Insert(0, directory.Id); } return string.Join("/", pathSegments); } private async Task BuildFilePathAsync(int idDirectory, string name, CancellationToken cancellationToken) { var directoryPath = await BuildDirectoryPathAsync(idDirectory, cancellationToken); return fileStorageRepository.MakeFilePath(directoryFiles, IdFileCategory.ToString(), Path.Combine(directoryPath, name)); } }