2023-08-10 11:45:05 +05:00
|
|
|
|
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
|
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
private const int IdFileCategory = 30000;
|
|
|
|
|
|
2023-08-10 11:45:05 +05:00
|
|
|
|
private readonly IEnumerable<string> validExtensions = new[]
|
|
|
|
|
{
|
|
|
|
|
".pdf",
|
|
|
|
|
".mp4"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private readonly string directoryFiles;
|
|
|
|
|
private readonly IFileStorageRepository fileStorageRepository;
|
2023-09-08 10:38:51 +05:00
|
|
|
|
private readonly IManualDirectoryRepository manualDirectoryRepository;
|
|
|
|
|
private readonly ICrudRepository<ManualDto> manualRepository;
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
|
|
|
|
public ManualCatalogService(IFileStorageRepository fileStorageRepository,
|
2023-09-08 10:38:51 +05:00
|
|
|
|
IManualDirectoryRepository manualDirectoryRepository,
|
|
|
|
|
ICrudRepository<ManualDto> manualRepository,
|
2023-08-10 11:45:05 +05:00
|
|
|
|
IConfiguration configuration)
|
|
|
|
|
{
|
|
|
|
|
this.fileStorageRepository = fileStorageRepository;
|
2023-09-08 10:38:51 +05:00
|
|
|
|
this.manualDirectoryRepository = manualDirectoryRepository;
|
2023-08-10 11:45:05 +05:00
|
|
|
|
this.manualRepository = manualRepository;
|
2024-05-24 10:55:05 +05:00
|
|
|
|
directoryFiles = configuration.GetValue<string>("DirectoryManualFiles", "manuals")!;
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
public async Task<int> SaveFileAsync(int idDirectory, int idAuthor, string name, Stream stream, CancellationToken cancellationToken)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
|
|
|
|
var extension = Path.GetExtension(name);
|
|
|
|
|
|
|
|
|
|
if (!validExtensions.Contains(extension))
|
|
|
|
|
throw new ArgumentInvalidException(
|
2023-09-29 12:06:46 +05:00
|
|
|
|
nameof(name),
|
|
|
|
|
$"Невозможно загрузить файл с расширением '{extension}'. Допустимые форматы файлов: {string.Join(", ", validExtensions)}");
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var path = await BuildFilePathAsync(idDirectory, name, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
await fileStorageRepository.SaveFileAsync(path, stream, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
|
|
|
|
var manual = new ManualDto
|
|
|
|
|
{
|
|
|
|
|
Name = name,
|
2024-03-19 17:52:45 +05:00
|
|
|
|
DateDownload = DateTimeOffset.UtcNow,
|
2023-09-08 10:38:51 +05:00
|
|
|
|
IdDirectory = idDirectory,
|
|
|
|
|
IdCategory = IdFileCategory,
|
|
|
|
|
IdAuthor = idAuthor
|
2023-08-10 11:45:05 +05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return await manualRepository.InsertAsync(manual, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
public async Task<int> AddDirectoryAsync(string name, int? idParent, CancellationToken cancellationToken)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
if (idParent.HasValue && !await manualDirectoryRepository.IsExistsAsync(idParent.Value, cancellationToken))
|
2023-09-29 12:06:46 +05:00
|
|
|
|
throw new ArgumentInvalidException(nameof(idParent), "Родительской директории не существует");
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var directory = new ManualDirectoryDto
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
|
|
|
|
Name = name,
|
|
|
|
|
IdParent = idParent,
|
|
|
|
|
};
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
if (await IsExistDirectoryAsync(directory, cancellationToken))
|
2023-09-29 12:06:46 +05:00
|
|
|
|
throw new ArgumentInvalidException(name, "Директория с таким названием уже существует");
|
2023-09-08 10:38:51 +05:00
|
|
|
|
|
|
|
|
|
return await manualDirectoryRepository.InsertAsync(directory, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
public async Task UpdateDirectoryAsync(int id, string name, CancellationToken cancellationToken)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var directory = await manualDirectoryRepository.GetOrDefaultAsync(id, cancellationToken)
|
2023-09-29 12:06:46 +05:00
|
|
|
|
?? throw new ArgumentInvalidException(nameof(id), $"Директории с Id: {id} не существует");
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
directory.Name = name;
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
if (await IsExistDirectoryAsync(directory, cancellationToken))
|
2023-09-29 12:06:46 +05:00
|
|
|
|
throw new ArgumentInvalidException(name, "Директория с таким названием уже существует");
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
await manualDirectoryRepository.UpdateAsync(directory, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
public async Task<int> DeleteDirectoryAsync(int id, CancellationToken cancellationToken)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var directory = await manualDirectoryRepository.GetOrDefaultAsync(id, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
if (directory is null)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
return 0;
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var path = fileStorageRepository.MakeFilePath(directoryFiles, IdFileCategory.ToString(),
|
|
|
|
|
await BuildDirectoryPathAsync(id, cancellationToken));
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-14 11:33:46 +05:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
fileStorageRepository.DeleteDirectory(path, true);
|
|
|
|
|
}
|
|
|
|
|
catch (InvalidOperationException ex)
|
|
|
|
|
{
|
2023-09-29 12:06:46 +05:00
|
|
|
|
throw new ArgumentInvalidException(nameof(id), ex.Message);
|
2023-09-14 11:33:46 +05:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
return await manualDirectoryRepository.DeleteAsync(directory.Id, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<int> DeleteFileAsync(int id, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var manual = await manualRepository.GetOrDefaultAsync(id, cancellationToken);
|
|
|
|
|
|
|
|
|
|
if (manual is null)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var filePath = await BuildFilePathAsync(manual.IdDirectory, manual.Name, cancellationToken);
|
|
|
|
|
|
2023-08-10 11:45:05 +05:00
|
|
|
|
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;
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var path = await BuildFilePathAsync(manual.IdDirectory, manual.Name, cancellationToken);
|
|
|
|
|
var fileStream = new FileStream(path, FileMode.Open);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
return (fileStream, manual.Name);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
private async Task<bool> IsExistDirectoryAsync(ManualDirectoryDto directory, CancellationToken cancellationToken)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var existingDirectory = await manualDirectoryRepository.GetOrDefaultAsync(directory.Name, directory.IdParent,
|
2023-08-10 11:45:05 +05:00
|
|
|
|
cancellationToken);
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
return existingDirectory is not null && directory.Id != existingDirectory.Id;
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
private async Task<string> BuildDirectoryPathAsync(int idDirectory, CancellationToken cancellationToken)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var directiories = await manualDirectoryRepository.GetAllAsync(cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var directory = directiories.FirstOrDefault(d => d.Id == idDirectory)
|
2023-09-29 12:06:46 +05:00
|
|
|
|
?? throw new ArgumentInvalidException(nameof(idDirectory), $"Директории с Id: {idDirectory} не существует");
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
var pathSegments = new List<int> { directory.Id };
|
|
|
|
|
|
|
|
|
|
while (directory.IdParent.HasValue)
|
2023-08-10 11:45:05 +05:00
|
|
|
|
{
|
2023-09-08 10:38:51 +05:00
|
|
|
|
directory = directiories.FirstOrDefault(d => d.Id == directory.IdParent.Value);
|
2023-09-29 12:06:46 +05:00
|
|
|
|
pathSegments.Insert(0, directory!.Id);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
return string.Join("/", pathSegments);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> BuildFilePathAsync(int idDirectory, string name, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var directoryPath = await BuildDirectoryPathAsync(idDirectory, cancellationToken);
|
2023-08-10 11:45:05 +05:00
|
|
|
|
|
2023-09-08 10:38:51 +05:00
|
|
|
|
return fileStorageRepository.MakeFilePath(directoryFiles, IdFileCategory.ToString(), Path.Combine(directoryPath, name));
|
2023-08-10 11:45:05 +05:00
|
|
|
|
}
|
|
|
|
|
}
|