diff --git a/AsbCloudApp/Data/FileInfoDto.cs b/AsbCloudApp/Data/FileInfoDto.cs
index 63abba8f..badcd5a4 100644
--- a/AsbCloudApp/Data/FileInfoDto.cs
+++ b/AsbCloudApp/Data/FileInfoDto.cs
@@ -39,6 +39,11 @@ namespace AsbCloudApp.Data
///
public long Size { get; set; }
+ ///
+ /// Помечен как удаленный
+ ///
+ public bool IsDeleted { get; set; }
+
///
/// DTO автора
///
diff --git a/AsbCloudApp/Repositories/IFileRepository.cs b/AsbCloudApp/Repositories/IFileRepository.cs
index e36d1001..a05cf909 100644
--- a/AsbCloudApp/Repositories/IFileRepository.cs
+++ b/AsbCloudApp/Repositories/IFileRepository.cs
@@ -1,42 +1,33 @@
-using AsbCloudApp.Data;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Data;
using AsbCloudApp.Services;
-using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
+#nullable enable
///
/// Сервис доступа к файлам
///
public interface IFileRepository : ICrudService
{
///
- /// Получить файлы определенной категории
+ /// Получение файлов по скважине
///
- ///
- ///
+ ///
///
///
- Task> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token);
+ Task> GetInfosAsync(FileRequest request, CancellationToken token);
///
/// Получить список файлов в контейнере
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ ///
///
///
- Task> GetInfosAsync(int idWell,
- int idCategory, string companyName = default, string fileName = default, DateTime begin = default,
- DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default);
+ Task> GetInfosPaginatedAsync(FileRequest request, CancellationToken token = default);
///
/// Пометить файл как удаленный
@@ -87,12 +78,6 @@ namespace AsbCloudApp.Repositories
///
Task MarkFileMarkAsDeletedAsync(IEnumerable idsMarks, CancellationToken token);
- ///
- /// Получение файлов по скважине
- ///
- ///
- ///
- ///
- Task> GetInfosByWellIdAsync(int idWell, CancellationToken token);
}
+#nullable disable
}
diff --git a/AsbCloudApp/Repositories/IFileStorageRepository.cs b/AsbCloudApp/Repositories/IFileStorageRepository.cs
index 9d821861..0d867db0 100644
--- a/AsbCloudApp/Repositories/IFileStorageRepository.cs
+++ b/AsbCloudApp/Repositories/IFileStorageRepository.cs
@@ -1,25 +1,23 @@
-using System.IO;
+using AsbCloudApp.Data;
+using System.Collections.Generic;
+using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
+#nullable enable
///
/// Репозиторий хранения фалов
///
public interface IFileStorageRepository
{
- ///
- /// Директория хранения файлов
- ///
- string RootPath { get; }
-
///
/// Получение длинны фала и проверка его наличия, если отсутствует падает исключение
///
///
///
- long GetLengthFile(string srcFilePath);
+ long GetFileLength(string srcFilePath);
///
/// Перемещение файла
@@ -31,21 +29,52 @@ namespace AsbCloudApp.Repositories
///
/// Копирование файла
///
+ ///
+ ///
+ ///
///
- Task CopyFileAsync(string filePath, Stream fileStream, CancellationToken token);
+ Task SaveFileAsync(string filePathRec, Stream fileStreamSrc, CancellationToken token);
///
/// Удаление файла
///
- ///
- void DeleteFile(string fileName);
+ ///
+ void DeleteFile(IEnumerable filesName);
///
- /// Проверка наличия файла
+ /// Удаление всех файлов с диска о которых нет информации в базе
///
- ///
- ///
+ ///
+ ///
+ int DeleteFilesNotInList(int idWell, IEnumerable idsFiles);
+
+ ///
+ /// Вывод списка всех файлов из базы, для которых нет файла на диске
+ ///
+ ///
+ ///
///
- bool FileExists(string fullPath, string fileName);
+ IEnumerable GetListFilesNotDisc(IEnumerable files);
+
+ ///
+ /// Получение пути к файлу
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId);
+
+ ///
+ /// Получить путь для скачивания
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ string GetUrl(int idWell, int idCategory, int idFile, string dotExtention);
}
+#nullable disable
}
diff --git a/AsbCloudApp/Requests/FileRequest.cs b/AsbCloudApp/Requests/FileRequest.cs
new file mode 100644
index 00000000..14a799e0
--- /dev/null
+++ b/AsbCloudApp/Requests/FileRequest.cs
@@ -0,0 +1,50 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace AsbCloudApp.Requests
+{
+#nullable enable
+ ///
+ /// Параметры запроса для файлового сервиса
+ ///
+ public class FileRequest : RequestBase
+ {
+ ///
+ /// Идентификатор скважины
+ ///
+ [Required]
+ public int IdWell { get; set; }
+
+ ///
+ /// Идентификатор категории файла
+ ///
+ [Required]
+ public int? IdCategory { get; set; }
+
+ ///
+ /// Наименование компании
+ ///
+ public string? CompanyNamePart { get; set; }
+
+ ///
+ /// Имя файла
+ ///
+ public string? FileNamePart { get; set; }
+
+ ///
+ /// Дата начала периода
+ ///
+ public DateTime? Begin { get; set; }
+
+ ///
+ /// Дата окончания периода
+ ///
+ public DateTime? End { get; set; }
+
+ ///
+ /// Признак удаления
+ ///
+ public bool? IsDeleted { get; set; }
+ }
+#nullable disable
+}
diff --git a/AsbCloudApp/Services/FileService.cs b/AsbCloudApp/Services/FileService.cs
index b27c4383..3ba250aa 100644
--- a/AsbCloudApp/Services/FileService.cs
+++ b/AsbCloudApp/Services/FileService.cs
@@ -1,6 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
-using System;
+using AsbCloudApp.Requests;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
namespace AsbCloudApp.Services
{
+#nullable enable
///
/// Сервис доступа к файлам
///
@@ -38,12 +39,12 @@ namespace AsbCloudApp.Services
///
///
///
- public async Task MoveAsync(int idWell, int? idUser, int idCategory,
+ public async Task MoveAsync(int idWell, int? idUser, int idCategory,
string destinationFileName, string srcFilePath, CancellationToken token = default)
{
destinationFileName = Path.GetFileName(destinationFileName);
srcFilePath = Path.GetFullPath(srcFilePath);
- var fileSize = fileStorageRepository.GetLengthFile(srcFilePath);
+ var fileSize = fileStorageRepository.GetFileLength(srcFilePath);
//save info to db
var dto = new FileInfoDto {
@@ -56,10 +57,10 @@ namespace AsbCloudApp.Services
var fileId = await fileRepository.InsertAsync(dto, token)
.ConfigureAwait(false);
- string filePath = MakeFilePath(idWell, idCategory, destinationFileName, fileId);
+ string filePath = fileStorageRepository.MakeFilePath(idWell, idCategory, destinationFileName, fileId);
fileStorageRepository.MoveFile(srcFilePath, filePath);
-
- return await GetInfoAsync(fileId, token);
+
+ return await GetOrDefaultAsync(fileId, token);
}
///
@@ -72,7 +73,7 @@ namespace AsbCloudApp.Services
///
///
///
- public async Task SaveAsync(int idWell, int? idUser, int idCategory,
+ public async Task SaveAsync(int idWell, int? idUser, int idCategory,
string fileFullName, Stream fileStream, CancellationToken token)
{
//save info to db
@@ -82,37 +83,17 @@ namespace AsbCloudApp.Services
IdAuthor = idUser,
IdCategory = idCategory,
Name = Path.GetFileName(fileFullName),
- Size = fileStream?.Length ?? 0
+ Size = fileStream.Length
};
var fileId = await fileRepository.InsertAsync(dto, token)
.ConfigureAwait(false);
//save stream to disk
- string filePath = MakeFilePath(idWell, idCategory, fileFullName, fileId);
- await fileStorageRepository.CopyFileAsync(filePath, fileStream, token);
+ string filePath = fileStorageRepository.MakeFilePath(idWell, idCategory, fileFullName, fileId);
+ await fileStorageRepository.SaveFileAsync(filePath, fileStream, token);
- return await GetInfoAsync(fileId, token);
- }
-
- ///
- /// Инфо о файле
- ///
- ///
- ///
- ///
- public async Task GetInfoAsync(int idFile,
- CancellationToken token)
- {
- var dto = await fileRepository.GetOrDefaultAsync(idFile, token).ConfigureAwait(false);
-
- var ext = Path.GetExtension(dto.Name);
-
- var relativePath = GetUrl(dto.IdWell, dto.IdCategory, dto.Id, ext);
- var fullPath = Path.GetFullPath(relativePath);
- fileStorageRepository.FileExists(fullPath, relativePath);
-
- return dto;
+ return await GetOrDefaultAsync(fileId, token);
}
///
@@ -140,27 +121,12 @@ namespace AsbCloudApp.Services
if (files is null || !files.Any())
return 0;
- foreach (var file in files)
- {
- var fileName = GetUrl(file.IdWell, file.IdCategory, file.Id, Path.GetExtension(file.Name));
- fileStorageRepository.DeleteFile(fileName);
- }
+ var filesName = files.Select(x => GetUrl(x.IdWell, x.IdCategory, x.Id, Path.GetExtension(x.Name)));
+ fileStorageRepository.DeleteFile(filesName);
return files.Any() ? 1 : 0;
}
- ///
- /// получить путь для скачивания
- ///
- ///
- ///
- public async Task GetUrl(int idFile)
- {
- var fileInfo = await fileRepository.GetOrDefaultAsync(idFile, CancellationToken.None).ConfigureAwait(false);
-
- return GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name));
- }
-
///
/// получить путь для скачивания
///
@@ -178,7 +144,7 @@ namespace AsbCloudApp.Services
///
///
public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) =>
- Path.Combine(fileStorageRepository.RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
+ fileStorageRepository.GetUrl(idWell, idCategory, idFile, dotExtention);
///
/// пометить метку файла как удаленную
@@ -206,30 +172,28 @@ namespace AsbCloudApp.Services
var ext = Path.GetExtension(entity.Name);
var relativePath = GetUrl(entity.IdWell, entity.IdCategory, entity.Id, ext);
var fullPath = Path.GetFullPath(relativePath);
- fileStorageRepository.FileExists(fullPath, relativePath);
}
return result;
}
///
- /// Получить список файлов в контейнере
+ /// Получить файлы определенной категории
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ ///
///
///
- public async Task> GetInfosAsync(int idWell,
- int idCategory, string companyName = default, string fileName = default, DateTime begin = default,
- DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default)
- => await fileRepository.GetInfosAsync(idWell, idCategory, companyName, fileName, begin, end, skip, take, token)
- .ConfigureAwait(false);
+ public Task> GetInfosAsync(FileRequest request, CancellationToken token)
+ => fileRepository.GetInfosAsync(request, token);
+
+ ///
+ /// Получить список файлов в контейнере
+ ///
+ ///
+ ///
+ ///
+ public Task> GetInfosPaginatedAsync(FileRequest request, CancellationToken token)
+ => fileRepository.GetInfosPaginatedAsync(request, token);
///
/// Пометить файл как удаленный
@@ -237,9 +201,8 @@ namespace AsbCloudApp.Services
///
///
///
- public async Task MarkAsDeletedAsync(int idFile, CancellationToken token = default)
- => await fileRepository.MarkAsDeletedAsync(idFile, token)
- .ConfigureAwait(false);
+ public Task MarkAsDeletedAsync(int idFile, CancellationToken token = default)
+ => fileRepository.MarkAsDeletedAsync(idFile, token);
///
/// добавить метку на файл
@@ -248,9 +211,8 @@ namespace AsbCloudApp.Services
///
///
///
- public async Task CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token)
- => await fileRepository.CreateFileMarkAsync(fileMarkDto, idUser, token)
- .ConfigureAwait(false);
+ public Task CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token)
+ => fileRepository.CreateFileMarkAsync(fileMarkDto, idUser, token);
///
/// Получить запись по id
@@ -258,9 +220,8 @@ namespace AsbCloudApp.Services
///
///
///
- public async Task GetOrDefaultAsync(int id, CancellationToken token)
- => await fileRepository.GetOrDefaultAsync(id, token)
- .ConfigureAwait(false);
+ public Task GetOrDefaultAsync(int id, CancellationToken token)
+ => fileRepository.GetOrDefaultAsync(id, token);
///
/// получить инфо о файле по метке
@@ -268,45 +229,84 @@ namespace AsbCloudApp.Services
///
///
///
- public async Task GetByMarkId(int idMark, CancellationToken token)
- => await fileRepository.GetByMarkId(idMark, token)
- .ConfigureAwait(false);
+ public Task GetByMarkId(int idMark, CancellationToken token)
+ => fileRepository.GetByMarkId(idMark, token);
///
/// получить инфо о файле по метке
///
- ///
+ ///
///
///
- public async Task MarkFileMarkAsDeletedAsync(IEnumerable idsMarks, CancellationToken token)
- => await fileRepository.MarkFileMarkAsDeletedAsync(idsMarks, token)
- .ConfigureAwait(false);
+ public Task MarkFileMarkAsDeletedAsync(IEnumerable idsMarks, CancellationToken token)
+ => fileRepository.MarkFileMarkAsDeletedAsync(idsMarks, token);
///
- /// Получение файлов по скважине
+ /// Удаление всех файлов по скважине помеченных как удаленные
///
///
///
///
- public async Task> GetInfosByWellIdAsync(int idWell, CancellationToken token)
- => await fileRepository.GetInfosByWellIdAsync(idWell, token)
- .ConfigureAwait(false);
-
- ///
- /// Получить файлы определенной категории
- ///
- ///
- ///
- ///
- ///
- public async Task> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token)
- => await fileRepository.GetInfosByCategoryAsync(idWell, idCategory, token)
- .ConfigureAwait(false);
-
- private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId)
+ public async Task DeleteFilesFromDbMarkedDeletionByIdWell(int idWell, CancellationToken token)
{
- return Path.Combine(fileStorageRepository.RootPath, $"{idWell}",
- $"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}");
+ var files = await fileRepository.GetInfosAsync(
+ new FileRequest
+ {
+ IdWell = idWell,
+ IsDeleted = true
+ },
+ token);
+ var result = await DeleteAsync(files.Select(x => x.Id), token);
+ return result;
+ }
+
+ ///
+ /// Удаление всех файлов с диска о которых нет информации в базе
+ ///
+ ///
+ ///
+ public async Task DeleteFilesNotExistStorage(int idWell, CancellationToken token)
+ {
+ var files = await fileRepository.GetInfosAsync(
+ new FileRequest
+ {
+ IdWell = idWell
+ },
+ token);
+ var result = await Task.FromResult(fileStorageRepository.DeleteFilesNotInList(idWell, files.Select(x => x.Id)));
+ return result;
+ }
+
+ ///
+ /// Вывод списка всех файлов из базы, для которых нет файла на диске
+ ///
+ ///
+ ///
+ ///
+ public async Task> GetListFilesNotDisc(int idWell, CancellationToken token)
+ {
+ var files = await fileRepository.GetInfosAsync(
+ new FileRequest
+ {
+ IdWell = idWell
+ },
+ token);
+ var result = fileStorageRepository.GetListFilesNotDisc(files);
+ return result;
+ }
+
+ ///
+ /// Получить файловый поток по идентификатору файла
+ ///
+ ///
+ ///
+ public Stream GetFileStream(FileInfoDto fileInfo)
+ {
+ var relativePath = GetUrl(fileInfo);
+ var fileStream = new FileStream(Path.GetFullPath(relativePath), FileMode.Open);
+
+ return fileStream;
}
}
+#nullable disable
}
diff --git a/AsbCloudDb/EFExtentions.cs b/AsbCloudDb/EFExtentions.cs
index f0523294..3cbf3509 100644
--- a/AsbCloudDb/EFExtentions.cs
+++ b/AsbCloudDb/EFExtentions.cs
@@ -117,6 +117,17 @@ namespace AsbCloudDb
}
return stat;
}
+
+ public static IQueryable SkipTake(this IQueryable query, int? skip, int? take)
+ {
+ if (skip > 0)
+ query = query.Skip((int)skip);
+
+ if (take > 0)
+ query = query.Take((int)take);
+
+ return query;
+ }
}
interface IQueryStringFactory { }
diff --git a/AsbCloudInfrastructure/Repository/FileRepository.cs b/AsbCloudInfrastructure/Repository/FileRepository.cs
index 0cca5b8b..deb43445 100644
--- a/AsbCloudInfrastructure/Repository/FileRepository.cs
+++ b/AsbCloudInfrastructure/Repository/FileRepository.cs
@@ -1,20 +1,27 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using AsbCloudDb;
using AsbCloudDb.Model;
+using AsbCloudInfrastructure.Services;
+using DocumentFormat.OpenXml.Drawing.Charts;
+using DocumentFormat.OpenXml.Wordprocessing;
using Mapster;
using Microsoft.EntityFrameworkCore;
+using Org.BouncyCastle.Asn1.Ocsp;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
+#nullable enable
public class FileRepository : IFileRepository
{
- private readonly IQueryable dbSetConfigured;
+ private readonly IQueryable dbSetConfigured;
private readonly IAsbCloudDbContext db;
public FileRepository(IAsbCloudDbContext db)
@@ -29,10 +36,55 @@ namespace AsbCloudInfrastructure.Repository
.Include(f => f.Well);
}
- public async Task> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token)
+ private IQueryable BuildQuery(FileRequest request)
{
- var entities = await dbSetConfigured
- .Where(e => e.IdWell == idWell && e.IdCategory == idCategory && e.IsDeleted == false)
+ var query = dbSetConfigured
+ .Where(e => e.IdWell == request.IdWell);
+
+ double timezoneOffsetHours = query.FirstOrDefault()
+ ?.Well.Timezone.Hours ?? 5d;
+
+ if (request.IdCategory is not null)
+ query = query.Where(x => x.IdCategory == request.IdCategory);
+
+ if (request.IsDeleted is not null)
+ query = query.Where(x => x.IsDeleted == request.IsDeleted);
+
+ if (request.CompanyNamePart is not null)
+ query = query.Where(e => e.Author.Company.Caption.ToLower().Contains(request.CompanyNamePart.ToLower()));
+
+ if (request.FileNamePart is not null)
+ query = query.Where(e => e.Name.ToLower().Contains(request.FileNamePart.ToLower()));
+
+ if (request.Begin is not null)
+ {
+ var beginUtc = request.Begin.Value.ToUtcDateTimeOffset(timezoneOffsetHours);
+ query = query.Where(e => e.UploadDate >= beginUtc);
+ }
+
+ if (request.End is not null)
+ {
+ var endUtc = request.End.Value.ToUtcDateTimeOffset(timezoneOffsetHours);
+ query = query.Where(e => e.UploadDate <= endUtc);
+ }
+
+ if (request?.SortFields?.Any() == true)
+ {
+ query = query.SortBy(request.SortFields);
+ }
+ else
+ query = query
+ .OrderBy(o => o.UploadDate);
+
+ return query;
+ }
+
+ public async Task> GetInfosAsync(FileRequest request, CancellationToken token)
+ {
+ var query = BuildQuery(request);
+
+ var entities = await query
+ .SkipTake(request.Skip, request.Take)
.AsNoTracking()
.ToListAsync(token)
.ConfigureAwait(false);
@@ -41,76 +93,37 @@ namespace AsbCloudInfrastructure.Repository
return dtos;
}
- public async Task> GetInfosAsync(int idWell,
- int idCategory, string companyName = default, string fileName = default, DateTime begin = default,
- DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default)
+ public async Task> GetInfosPaginatedAsync(FileRequest request, CancellationToken token = default)
{
- var query = dbSetConfigured
- .Where(e => e.IdWell == idWell &&
- e.IdCategory == idCategory &&
- !e.IsDeleted);
+ var skip = request.Skip ?? 0;
+ var take = request.Take ?? 32;
- if (!string.IsNullOrEmpty(companyName))
- query = query.Where(e => (e.Author == null) ||
- (e.Author.Company == null) ||
- e.Author.Company.Caption.Contains(companyName));
+ var query = BuildQuery(request);
- if (!string.IsNullOrEmpty(fileName))
- query = query.Where(e => e.Name.ToLower().Contains(fileName.ToLower()));
-
- var firstFile = await query.FirstOrDefaultAsync(token);
- if (firstFile is null)
- return new PaginationContainer()
- {
- Skip = skip,
- Take = take,
- Count = 0,
- };
-
- var timezoneOffset = firstFile.Well.Timezone?.Hours ?? 5;
-
- if (begin != default)
- {
- var beginUtc = begin.ToUtcDateTimeOffset(timezoneOffset);
- query = query.Where(e => e.UploadDate >= beginUtc);
- }
-
- if (end != default)
- {
- var endUtc = end.ToUtcDateTimeOffset(timezoneOffset);
- query = query.Where(e => e.UploadDate <= endUtc);
- }
-
- var count = await query.CountAsync(token).ConfigureAwait(false);
-
- var result = new PaginationContainer(count)
+ var result = new PaginationContainer()
{
Skip = skip,
Take = take,
- Count = count,
+ Count = await query.CountAsync(token),
};
- if (count <= skip)
+ if (result.Count == 0)
return result;
- query = query.OrderBy(e => e.UploadDate);
-
- if (skip > 0)
- query = query.Skip(skip);
- query = query.Take(take);
-
var entities = await query
- .Take(take).AsNoTracking().ToListAsync(token)
+ .SkipTake(skip, take)
+ .AsNoTracking()
+ .ToListAsync(token)
.ConfigureAwait(false);
- var dtos = entities.Select(e => Convert(e, timezoneOffset));
- result.Items.AddRange(dtos);
+ result.Items = entities.Select(e => Convert(e)).ToList();
+
return result;
}
public async Task> GetInfoByIdsAsync(IEnumerable idsFile, CancellationToken token)
{
- var result = new List();
+ var result = Enumerable.Empty();
var entities = await dbSetConfigured
.AsNoTracking()
@@ -118,14 +131,8 @@ namespace AsbCloudInfrastructure.Repository
.ToListAsync(token)
.ConfigureAwait(false);
- foreach (var entity in entities)
- {
- if (entity is null)
- {
- throw new FileNotFoundException($"fileId:{entity.Id} not found");
- }
- result.Add(Convert(entity));
- }
+ if (entities is not null)
+ result = entities.Select(entity => Convert(entity));
return result;
}
@@ -163,7 +170,7 @@ namespace AsbCloudInfrastructure.Repository
.FirstOrDefaultAsync(f => f.FileMarks.Any(m => m.Id == idMark), token)
.ConfigureAwait(false);
- FileInfoDto dto = Convert(entity);
+ FileInfoDto dto = Convert(entity!);
return dto;
}
@@ -201,44 +208,13 @@ namespace AsbCloudInfrastructure.Repository
return await db.SaveChangesAsync(token);
}
- public async Task> GetInfosByWellIdAsync(int idWell, CancellationToken token)
- {
- var entities = await dbSetConfigured
- .Where(e => e.IdWell == idWell && e.IsDeleted == false)
- .AsNoTracking()
- .ToListAsync(token)
- .ConfigureAwait(false);
-
- var dtos = entities.Select(e => Convert(e));
- return dtos;
- }
-
- private static FileInfoDto Convert(AsbCloudDb.Model.FileInfo entity)
- {
- var timezoneOffset = entity.Well.Timezone?.Hours ?? 5;
- return Convert(entity, timezoneOffset);
- }
-
- private static FileInfoDto Convert(AsbCloudDb.Model.FileInfo entity, double timezoneOffset)
- {
- var dto = entity.Adapt();
- dto.UploadDate = entity.UploadDate.ToRemoteDateTime(timezoneOffset);
- dto.FileMarks = entity.FileMarks.Select(m =>
- {
- var mark = m.Adapt();
- mark.DateCreated = m.DateCreated.ToRemoteDateTime(timezoneOffset);
- return mark;
- });
- return dto;
- }
-
public async Task> GetAllAsync(CancellationToken token)
=> await dbSetConfigured.AsNoTracking()
.Select(x => Convert(x))
.ToListAsync(token)
.ConfigureAwait(false);
- public async Task GetOrDefaultAsync(int id, CancellationToken token)
+ public async Task GetOrDefaultAsync(int id, CancellationToken token)
{
var entity = await dbSetConfigured
.AsNoTracking()
@@ -246,24 +222,20 @@ namespace AsbCloudInfrastructure.Repository
.ConfigureAwait(false);
if (entity is null)
- {
- throw new FileNotFoundException($"fileId:{id} not found");
- }
+ return null;
var dto = Convert(entity);
return dto;
}
- public FileInfoDto GetOrDefault(int id)
+ public FileInfoDto? GetOrDefault(int id)
{
var entity = dbSetConfigured
.AsNoTracking()
.FirstOrDefault(f => f.Id == id);
if (entity is null)
- {
- throw new FileNotFoundException($"fileId:{id} not found");
- }
+ return null;
var dto = Convert(entity);
return dto;
@@ -271,7 +243,7 @@ namespace AsbCloudInfrastructure.Repository
public async Task InsertAsync(FileInfoDto newItem, CancellationToken token)
{
- var fileInfo = new AsbCloudDb.Model.FileInfo()
+ var fileInfo = new FileInfo()
{
IdWell = newItem.IdWell,
IdAuthor = newItem.IdAuthor,
@@ -301,5 +273,25 @@ namespace AsbCloudInfrastructure.Repository
{
throw new NotImplementedException();
}
+
+ private static FileInfoDto Convert(FileInfo entity)
+ {
+ var timezoneOffset = entity.Well.Timezone?.Hours ?? 5;
+ return Convert(entity, timezoneOffset);
+ }
+
+ private static FileInfoDto Convert(FileInfo entity, double timezoneOffset)
+ {
+ var dto = entity.Adapt();
+ dto.UploadDate = entity.UploadDate.ToRemoteDateTime(timezoneOffset);
+ dto.FileMarks = entity.FileMarks.Select(m =>
+ {
+ var mark = m.Adapt();
+ mark.DateCreated = m.DateCreated.ToRemoteDateTime(timezoneOffset);
+ return mark;
+ });
+ return dto;
+ }
}
+#nullable disable
}
diff --git a/AsbCloudInfrastructure/Repository/FileStorageRepository.cs b/AsbCloudInfrastructure/Repository/FileStorageRepository.cs
index 7466f3a3..8bd61e2f 100644
--- a/AsbCloudInfrastructure/Repository/FileStorageRepository.cs
+++ b/AsbCloudInfrastructure/Repository/FileStorageRepository.cs
@@ -1,38 +1,43 @@
-using AsbCloudApp.Exceptions;
+using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
+#nullable enable
public class FileStorageRepository : IFileStorageRepository
{
- public string RootPath { get; private set; }
+ ///
+ /// Директория хранения файлов
+ ///
+ private readonly string RootPath = "files";
public FileStorageRepository()
{
- RootPath = "files";
}
- public async Task CopyFileAsync(string filePath, Stream fileStream, CancellationToken token)
+ public async Task SaveFileAsync(string filePathRec, Stream fileStreamSrc, CancellationToken token)
{
- CreateDirectory(filePath);
- using var newfileStream = new FileStream(filePath, FileMode.Create);
- await fileStream.CopyToAsync(newfileStream, token).ConfigureAwait(false);
+ CreateDirectory(filePathRec);
+ using var newfileStream = new FileStream(filePathRec, FileMode.Create);
+ await fileStreamSrc.CopyToAsync(newfileStream, token).ConfigureAwait(false);
}
- public void DeleteFile(string fileName)
+ public void DeleteFile(IEnumerable filesName)
{
- if (File.Exists(fileName))
- File.Delete(fileName);
+ foreach (var fileName in filesName)
+ {
+ if (File.Exists(fileName))
+ File.Delete(fileName);
+ }
}
- public long GetLengthFile(string srcFilePath)
+ public long GetFileLength(string srcFilePath)
{
- if (!File.Exists(srcFilePath))
- throw new ArgumentInvalidException($"file {srcFilePath} doesn't exist", nameof(srcFilePath));
-
var sysFileInfo = new FileInfo(srcFilePath);
return sysFileInfo.Length;
}
@@ -43,17 +48,74 @@ namespace AsbCloudInfrastructure.Repository
File.Move(srcFilePath, filePath);
}
- public bool FileExists(string fullPath, string fileName)
+ public string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId)
{
- if (!File.Exists(fullPath))
- throw new FileNotFoundException("not found", fileName);
-
- return true;
+ return Path.Combine(RootPath, $"{idWell}",
+ $"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}");
}
- private void CreateDirectory(string filePath)
+ public int DeleteFilesNotInList(int idWell, IEnumerable idsFilesList)
{
- Directory.CreateDirectory(Path.GetDirectoryName(filePath));
+ var allFilesPath = GetFilesPath(idWell);
+ var result = 0;
+
+ foreach (var filePath in allFilesPath)
+ {
+ if (int.TryParse(Path.GetFileNameWithoutExtension(filePath), out int idFile)
+ || !idsFilesList.Any(x => x == idFile))
+ {
+ File.Delete(filePath);
+ result++;
+ }
+ }
+
+ return result;
+ }
+
+ public IEnumerable GetListFilesNotDisc(IEnumerable files)
+ {
+ var resutl = new List();
+ var groupFiles = files.GroupBy(x => x.IdWell);
+
+ foreach (var itemGroupFiles in groupFiles)
+ {
+ var idsFilesStorage = GetIdsFiles(itemGroupFiles.Key);
+ foreach (var file in files)
+ {
+ if (!idsFilesStorage.Any(x => x == file.Id))
+ resutl.Add(file);
+ }
+ }
+
+ return resutl;
+ }
+
+ public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) =>
+ Path.Combine(RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
+
+ private IEnumerable GetIdsFiles(int idWell)
+ {
+ var result = new List();
+ var allFilesPath = GetFilesPath(idWell);
+
+ foreach (var filePath in allFilesPath)
+ if(int.TryParse(Path.GetFileNameWithoutExtension(filePath), out int idFile))
+ result.Add(idFile);
+
+ return result;
+ }
+
+ private IEnumerable GetFilesPath(int idWell)
+ {
+ var path = Path.Combine(RootPath, $"{idWell}");
+ return Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
+ }
+
+ private static void CreateDirectory(string filePath)
+ {
+ var directoryName = Path.GetDirectoryName(filePath)!;
+ Directory.CreateDirectory(directoryName);
}
}
+#nullable disable
}
diff --git a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs
index ca0827b0..e926a886 100644
--- a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs
+++ b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs
@@ -282,7 +282,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
fileMarkDto.IdMarkType != idMarkTypeReject)
throw new ArgumentInvalidException($"В этом методе допустимы только отметки о принятии или отклонении.", nameof(fileMarkDto));
- var fileInfo = await fileService.GetInfoAsync(fileMarkDto.IdFile, token)
+ var fileInfo = await fileService.GetOrDefaultAsync(fileMarkDto.IdFile, token)
.ConfigureAwait(false);
if (fileInfo is null)
@@ -357,7 +357,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private async Task NotifyPublisherOnFullAccepAsync(FileMarkDto fileMark, CancellationToken token)
{
- var file = await fileService.GetInfoAsync(fileMark.IdFile, token);
+ var file = await fileService.GetOrDefaultAsync(fileMark.IdFile, token);
var well = await wellService.GetOrDefaultAsync(file.IdWell, token);
var user = file.Author;
var factory = new DrillingMailBodyFactory(configuration);
@@ -369,7 +369,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
private async Task NotifyPublisherOnRejectAsync(FileMarkDto fileMark, CancellationToken token)
{
- var file = await fileService.GetInfoAsync(fileMark.IdFile, token);
+ var file = await fileService.GetOrDefaultAsync(fileMark.IdFile, token);
var well = await wellService.GetOrDefaultAsync(file.IdWell, token);
var user = file.Author;
var factory = new DrillingMailBodyFactory(configuration);
@@ -473,7 +473,6 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
var well = await wellService.GetOrDefaultAsync(idWell, token);
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.xlsx";
var tempResultFilePath = Path.Combine(Path.GetTempPath(), "drillingProgram", resultFileName);
- var mailService = new EmailService(backgroundWorker, configuration);
async Task funcProgramMake(string id, CancellationToken token)
{
var contextOptions = new DbContextOptionsBuilder()
diff --git a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs
index 62d82522..4d44f38c 100644
--- a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs
+++ b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs
@@ -1,5 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
+using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
@@ -96,7 +97,7 @@ namespace AsbCloudInfrastructure.Services
.Select(g => g.Key);
var files = (await fileService
- .GetInfosByWellIdAsync(idWell, token)
+ .GetInfosAsync(new FileRequest { IdWell = idWell}, token)
.ConfigureAwait(false))
.Where(f => categoriesIds.Contains(f.IdCategory))
.ToArray();
@@ -162,7 +163,12 @@ namespace AsbCloudInfrastructure.Services
public async Task GetFilesHistoryByIdCategory(int idWell, int idCategory, CancellationToken token)
{
- var files = await fileService.GetInfosByCategoryAsync(idWell, idCategory, token).ConfigureAwait(false);
+ var request = new FileRequest
+ {
+ IdWell = idWell,
+ IdCategory = idCategory,
+ };
+ var files = await fileService.GetInfosAsync(request, token).ConfigureAwait(false);
return new WellFinalDocumentsHistoryDto {
IdWell = idWell,
diff --git a/AsbCloudInfrastructure/Services/_Readme.md b/AsbCloudInfrastructure/Services/_Readme.md
new file mode 100644
index 00000000..d05ae29e
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/_Readme.md
@@ -0,0 +1,9 @@
+# Создание репозитория для сервися
+
+1. Создать интерфейс репозитория в AsbCloudApp.Services
+2. Создать репозиторий в AsbCloudInfrastructure.Repository, наследоваться от созданного интерфейса, в нем добавить работу с БД
+3. Добавить репозиторий в AsbCloudInfrastructure.DependencyInjection
+4. Добавить в конструктор сервиса новый репозиторий и использовать его методы
+5. Перенести сервис из AsbCloudInfrastructure.Services в AsbCloudApp.Services
+6. Добавить или поправить тесты на изменяемый сервис используя AsbCloudWebApi.Tests.RepositoryFactory
+7. В тестах сделать мок данных для репозитория
\ No newline at end of file
diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs
index fa72df35..a1e275b9 100644
--- a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs
+++ b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs
@@ -220,7 +220,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
{
ConfigureNotApproved();
fileServiceMock
- .Setup(s => s.GetInfoAsync(It.IsAny(), It.IsAny()))
+ .Setup(s => s.GetOrDefaultAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(file1002.Adapt()));
fileServiceMock
@@ -251,7 +251,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
{
ConfigureNotApproved();
fileServiceMock
- .Setup(s => s.GetInfoAsync(It.IsAny(), It.IsAny()))
+ .Setup(s => s.GetOrDefaultAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(file1002.Adapt()));
fileServiceMock
diff --git a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs
index 025cc7bb..698f83cd 100644
--- a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs
+++ b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs
@@ -1,21 +1,15 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
using AsbCloudApp.Services;
-using AsbCloudDb.Model;
-using AsbCloudInfrastructure.Repository;
-using AsbCloudInfrastructure.Services;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.Wordprocessing;
using Moq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
-using static AsbCloudWebApi.Tests.TestHelpter;
namespace AsbCloudWebApi.Tests.ServicesTests
{
@@ -115,15 +109,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests
return Task.FromResult(result);
});
- repositoryMock.Setup(x => x.GetInfosByWellIdAsync(It.IsAny(), It.IsAny()))
- .Returns((int idWell, CancellationToken token) => {
- var data = Files.Where(x => x.IdWell == idWell);
- return Task.FromResult(data);
- });
-
- repositoryMock.Setup(x => x.GetInfosByCategoryAsync(It.IsAny(), It.IsAny(), It.IsAny()))
- .Returns((int idWell, int idCategory, CancellationToken token) => {
- var data = Files.Where(x => x.IdWell == idWell && x.IdCategory == idCategory);
+ repositoryMock.Setup(x => x.GetInfosAsync(It.IsAny(), It.IsAny()))
+ .Returns((FileRequest request, CancellationToken token) => {
+ var data = Files.Where(x => x.IdWell == request.IdWell);
return Task.FromResult(data);
});
@@ -139,8 +127,10 @@ namespace AsbCloudWebApi.Tests.ServicesTests
});
var storageRepositoryMock = new Mock();
-
- storageRepositoryMock.Setup(x => x.RootPath).Returns("files");
+ storageRepositoryMock.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns((int idWell, int idCategory, int idFile, string dotExtention) => {
+ return Path.Combine("files", idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
+ });
fileService = new FileService(repositoryMock.Object, storageRepositoryMock.Object);
}
@@ -153,9 +143,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests
}
[Fact]
- public async Task GetInfoAsync_returns_FileInfo()
+ public async Task GetOrDefaultAsync_returns_FileInfo()
{
- var data = await fileService.GetInfoAsync(1742, CancellationToken.None);
+ var data = await fileService.GetOrDefaultAsync(1742, CancellationToken.None);
Assert.NotNull(data);
}
@@ -195,20 +185,6 @@ namespace AsbCloudWebApi.Tests.ServicesTests
Assert.True(result > 0);
}
- [Fact]
- public async Task GetInfosByWellIdAsync_returns_FileInfo()
- {
- var data = await fileService.GetInfosByWellIdAsync(90, CancellationToken.None);
- Assert.NotNull(data);
- }
-
- [Fact]
- public async Task GetInfosByCategoryAsync_returns_FileInfo()
- {
- var data = await fileService.GetInfosByCategoryAsync(90, 10040, CancellationToken.None);
- Assert.NotNull(data);
- }
-
[Fact]
public async Task CreateFileMarkAsync()
{
diff --git a/AsbCloudWebApi/Controllers/FileController.cs b/AsbCloudWebApi/Controllers/FileController.cs
index 378b5a50..d4dfad33 100644
--- a/AsbCloudWebApi/Controllers/FileController.cs
+++ b/AsbCloudWebApi/Controllers/FileController.cs
@@ -1,17 +1,17 @@
using AsbCloudApp.Data;
+using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-using System;
-using System.Collections;
-using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudDb.Model;
namespace AsbCloudWebApi.Controllers
{
+#nullable enable
///
/// Хранение файлов
///
@@ -70,38 +70,27 @@ namespace AsbCloudWebApi.Controllers
///
/// Возвращает информацию о файлах для скважины в выбраной категории
///
- /// id скважины
- /// id категории файла
- /// id компаний для фильтрации возвращаемых файлов
- /// часть имени файла для поиска
- /// дата начала
- /// дата окончания
- /// для пагинации кол-во записей пропустить
- /// для пагинации кол-во записей взять
+ ///
/// Токен отмены задачи
/// Список информации о файлах в этой категории
[HttpGet]
+ [Route("/api/files")]
[Permission]
[ProducesResponseType(typeof(PaginationContainer), (int)System.Net.HttpStatusCode.OK)]
public async Task GetFilesInfoAsync(
- [FromRoute] int idWell,
- int idCategory = default,
- string companyName = default,
- string fileName = default,
- DateTime begin = default,
- DateTime end = default,
- int skip = 0,
- int take = 32,
+ [FromQuery] FileRequest request,
CancellationToken token = default)
{
int? idCompany = User.GetCompanyId();
- if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
- idWell, token).ConfigureAwait(false))
+ if (request.IdCategory is null || idCompany is null)
return Forbid();
- var filesInfo = await fileService.GetInfosAsync(idWell, idCategory,
- companyName, fileName, begin, end, skip, take, token).ConfigureAwait(false);
+ if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value,
+ request.IdWell, token).ConfigureAwait(false))
+ return Forbid();
+
+ var filesInfo = await fileService.GetInfosPaginatedAsync(request, token).ConfigureAwait(false);
return Ok(filesInfo);
}
@@ -110,36 +99,33 @@ namespace AsbCloudWebApi.Controllers
/// Возвращает файл с диска на сервере
///
/// id скважины
- /// id запрашиваемого файла
+ /// id запрашиваемого файла
/// Токен отмены задачи
/// Запрашиваемый файл
[HttpGet]
- [Route("{fileId}")]
+ [Route("{idFile}")]
[Permission]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)]
public async Task GetFileAsync([FromRoute] int idWell,
- int fileId, CancellationToken token = default)
+ int idFile, CancellationToken token = default)
{
int? idCompany = User.GetCompanyId();
if (idCompany is null)
return Forbid();
+ var fileInfo = await fileService.GetOrDefaultAsync(idFile, token);
+
+ if (fileInfo is null)
+ return NotFound(idFile);
+
if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
- idWell, token).ConfigureAwait(false))
+ fileInfo.IdWell, token).ConfigureAwait(false))
return Forbid();
- try
- {
- var fileInfo = await fileService.GetInfoAsync(fileId, token);
+ var fileStream = fileService.GetFileStream(fileInfo);
- var relativePath = fileService.GetUrl(fileInfo);
- return PhysicalFile(Path.GetFullPath(relativePath), "application/octet-stream", fileInfo.Name);
- }
- catch (FileNotFoundException ex)
- {
- return NotFound(ex.FileName);
- }
+ return File(fileStream, "application/octet-stream", fileInfo.Name);
}
///
@@ -161,13 +147,16 @@ namespace AsbCloudWebApi.Controllers
int? idCompany = User.GetCompanyId();
- if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
+ if (idUser is null || idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
idWell, token).ConfigureAwait(false))
return Forbid();
- var file = await fileService.GetInfoAsync((int)idFile, token);
+ var fileInfo = await fileService.GetOrDefaultAsync(idFile, token);
- if (!userService.HasPermission((int)idUser, $"File.edit{file.IdCategory}"))
+ if (fileInfo is null)
+ return NotFound(idFile);
+
+ if (!userService.HasPermission((int)idUser, $"File.edit{fileInfo?.IdCategory}"))
return Forbid();
var result = await fileService.MarkAsDeletedAsync(idFile, token);
@@ -254,4 +243,5 @@ namespace AsbCloudWebApi.Controllers
}
}
}
+#nullable disable
}