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", "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( nameof(name), $"Невозможно загрузить файл с расширением '{extension}'. Допустимые форматы файлов: {string.Join(", ", validExtensions)}"); var path = await BuildFilePathAsync(idDirectory, name, cancellationToken); await fileStorageRepository.SaveFileAsync(path, stream, cancellationToken); var manual = new ManualDto { Name = name, DateDownload = DateTimeOffset.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(nameof(id), $"Директории с Id: {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(nameof(id), ex.Message); } 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(nameof(idDirectory), $"Директории с Id: {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)); } }