From 99242f69954e657f003d5460cb7695a6756484fe Mon Sep 17 00:00:00 2001 From: KharchenkoVV Date: Fri, 23 Jul 2021 17:40:31 +0500 Subject: [PATCH] =?UTF-8?q?CS2-38:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C,=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=20?= =?UTF-8?q?=D0=B8=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B8=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/FilePropertiesDto.cs | 13 ++ AsbCloudApp/Services/IFileService.cs | 18 +++ AsbCloudDb/Model/AsbCloudDbContext.cs | 50 +++++-- AsbCloudDb/Model/File.cs | 30 ++++ AsbCloudDb/Model/FileCategory.cs | 17 +++ AsbCloudDb/Model/IAsbCloudDbContext.cs | 2 + AsbCloudInfrastructure/DependencyInjection.cs | 1 + .../Services/FileService.cs | 69 +++++++++ AsbCloudWebApi/Controllers/FileController.cs | 133 ++++++++++++++++++ 9 files changed, 318 insertions(+), 15 deletions(-) create mode 100644 AsbCloudApp/Data/FilePropertiesDto.cs create mode 100644 AsbCloudApp/Services/IFileService.cs create mode 100644 AsbCloudDb/Model/File.cs create mode 100644 AsbCloudDb/Model/FileCategory.cs create mode 100644 AsbCloudInfrastructure/Services/FileService.cs create mode 100644 AsbCloudWebApi/Controllers/FileController.cs diff --git a/AsbCloudApp/Data/FilePropertiesDto.cs b/AsbCloudApp/Data/FilePropertiesDto.cs new file mode 100644 index 00000000..816cbb86 --- /dev/null +++ b/AsbCloudApp/Data/FilePropertiesDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace AsbCloudApp.Data +{ + public class FilePropertiesDto + { + public int Id { get; set; } + public string Name { get; set; } + public int IdCategory { get; set; } + public DateTime UploadDate { get; set; } + public string UserName { get; set; } + } +} diff --git a/AsbCloudApp/Services/IFileService.cs b/AsbCloudApp/Services/IFileService.cs new file mode 100644 index 00000000..0f6ffea8 --- /dev/null +++ b/AsbCloudApp/Services/IFileService.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using AsbCloudApp.Data; + +namespace AsbCloudApp.Services +{ + public interface IFileService + { + string RootPath { get; } + IDictionary SaveFilesPropertiesToDb(int wellId, + int idCategory, IEnumerable<(string fileName, int idCategory, + DateTime date, int idUser)> filesInfo); + + IEnumerable GetFilesInfo(int wellId, int idCategory); + + (int Id, string Name, int IdCategory)? GetFileInfo(int fileId); + } +} diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index f662c054..caf1e0c7 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -25,6 +25,8 @@ namespace AsbCloudDb.Model public virtual DbSet UserRoles { get; set; } public virtual DbSet Wells { get; set; } public virtual DbSet Reports { get; set; } + public virtual DbSet Files { get; set; } + public virtual DbSet FileCategories { get; set; } public virtual DbSet Operations { get; set; } public virtual DbSet TelemetryAnalysis { get; set; } public virtual DbSet SectionAnalysis { get; set; } @@ -209,6 +211,24 @@ namespace AsbCloudDb.Model new Operation {Id = 17, Name = "На поверхности" } }); }); + + modelBuilder.Entity(entity => + { + entity.HasData(new List { + new FileCategory {Id = 1, Name = "Растворный сервис"}, + new FileCategory {Id = 2, Name = "Цементирование"}, + new FileCategory {Id = 3, Name = "ННБ"}, + new FileCategory {Id = 4, Name = "ГТИ"}, + new FileCategory {Id = 5, Name = "Документы по скважине"}, + new FileCategory {Id = 6, Name = "Супервайзер"}, + new FileCategory {Id = 7, Name = "Мастер"}, + new FileCategory {Id = 8, Name = "Последние данные"}, + new FileCategory {Id = 9, Name = "Последний замер бурового раствора"}, + new FileCategory {Id = 10, Name = "Шламограмма"}, + new FileCategory {Id = 11, Name = "Последние данные ННБ"}, + new FileCategory {Id = 12, Name = "Рапорт"} + }); + }); } private static void FillDemoData(ModelBuilder modelBuilder) @@ -240,21 +260,6 @@ namespace AsbCloudDb.Model }); }); - modelBuilder.Entity(entity => - { - entity.HasData(new List { - new Well{Id = 1, IdCluster = 1, Caption = "скв 16314", Latitude = 60.8705722222222, Longitude = 70.3811888888889}, - new Well{Id = 2, IdCluster = 1, Caption = "скв 16311", Latitude = 60.8705722222222, Longitude = 70.3811888888889}, - new Well{Id = 3, IdCluster = 2, Caption = "скв 16315", Latitude = 60.8205750000000, Longitude = 70.1343833333334}, - new Well{Id = 4, IdCluster = 2, Caption = "скв 16318", Latitude = 60.8205750000000, Longitude = 70.1343833333334}, - new Well{Id = 5, IdCluster = 3, Caption = "скв 16310", Latitude = 60.8100666666667, Longitude = 69.7778388888889}, - new Well{Id = 6, IdCluster = 4, Caption = "скв 16316", Latitude = 60.8928805555556, Longitude = 70.3272055555556}, - new Well{Id = 7, IdCluster = 5, Caption = "скв 16312", Latitude = 60.6672055555556, Longitude = 69.6603861111111}, - new Well{Id = 8, IdCluster = 5, Caption = "скв 16313", Latitude = 60.6672055555556, Longitude = 69.6603861111111}, - new Well{Id = 9, IdCluster = 5, Caption = "скв 42669", Latitude = 60.6672055555556, Longitude = 69.6603861111111}, - }); - }); - modelBuilder.Entity(entity => { entity.HasData(new List{ @@ -278,6 +283,21 @@ namespace AsbCloudDb.Model }); }); + modelBuilder.Entity(entity => + { + entity.HasData(new List { + new Well{Id = 1, IdCluster = 1, IdTelemetry = 1, Caption = "скв 16314", Latitude = 60.8705722222222, Longitude = 70.3811888888889}, + new Well{Id = 2, IdCluster = 1, Caption = "скв 16311", Latitude = 60.8705722222222, Longitude = 70.3811888888889}, + new Well{Id = 3, IdCluster = 2, Caption = "скв 16315", Latitude = 60.8205750000000, Longitude = 70.1343833333334}, + new Well{Id = 4, IdCluster = 2, Caption = "скв 16318", Latitude = 60.8205750000000, Longitude = 70.1343833333334}, + new Well{Id = 5, IdCluster = 3, Caption = "скв 16310", Latitude = 60.8100666666667, Longitude = 69.7778388888889}, + new Well{Id = 6, IdCluster = 4, Caption = "скв 16316", Latitude = 60.8928805555556, Longitude = 70.3272055555556}, + new Well{Id = 7, IdCluster = 5, Caption = "скв 16312", Latitude = 60.6672055555556, Longitude = 69.6603861111111}, + new Well{Id = 8, IdCluster = 5, Caption = "скв 16313", Latitude = 60.6672055555556, Longitude = 69.6603861111111}, + new Well{Id = 9, IdCluster = 5, Caption = "скв 42669", Latitude = 60.6672055555556, Longitude = 69.6603861111111}, + }); + }); + modelBuilder.Entity(entity => { entity.HasData(new List { new RelationCompanyWell{ IdWell = 1, IdCompany = 1}, diff --git a/AsbCloudDb/Model/File.cs b/AsbCloudDb/Model/File.cs new file mode 100644 index 00000000..c557a95f --- /dev/null +++ b/AsbCloudDb/Model/File.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AsbCloudDb.Model +{ + [Table("t_files"), Comment("Файлы всех категорий")] + public class File : IId + { + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("name"), Comment("Название файла")] + public string Name { get; set; } + + [Column("id_well"), Comment("id скважины")] + public int IdWell { get; set; } + + [Column("id_category"), Comment("id категории файла")] + public int IdCategory { get; set; } + + [Column("date", TypeName = "timestamp with time zone")] + public DateTime Date { get; set; } + + [Column("id_user"), Comment("Id пользователя, загрузившего файл")] + public int IdUser { get; set; } + } +} \ No newline at end of file diff --git a/AsbCloudDb/Model/FileCategory.cs b/AsbCloudDb/Model/FileCategory.cs new file mode 100644 index 00000000..42079a87 --- /dev/null +++ b/AsbCloudDb/Model/FileCategory.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AsbCloudDb.Model +{ + [Table("t_file_categories"), Comment("Категории файлов")] + public class FileCategory : IId + { + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("name"), Comment("Название категории")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs index 693e9546..d5bb5949 100644 --- a/AsbCloudDb/Model/IAsbCloudDbContext.cs +++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs @@ -21,6 +21,8 @@ namespace AsbCloudDb.Model DbSet Wells { get; set; } DbSet UserRoles { get; set; } DbSet Reports { get; set; } + DbSet Files { get; set; } + DbSet FileCategories { get; set; } DbSet Operations { get; set; } DbSet TelemetryAnalysis { get; set; } diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 5d820b15..af90edfb 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -37,6 +37,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); return services; } diff --git a/AsbCloudInfrastructure/Services/FileService.cs b/AsbCloudInfrastructure/Services/FileService.cs new file mode 100644 index 00000000..e1828818 --- /dev/null +++ b/AsbCloudInfrastructure/Services/FileService.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using AsbCloudDb.Model; + +namespace AsbCloudInfrastructure.Services +{ + public class FileService : IFileService + { + public string RootPath { get; private set; } + private readonly IAsbCloudDbContext db; + + public FileService(IAsbCloudDbContext db) + { + RootPath = "files"; + this.db = db; + } + + public IDictionary SaveFilesPropertiesToDb(int wellId, int idCategory, + IEnumerable<(string fileName, int idCategory, DateTime date, int idUser)> filesInfo) + { + var fileIdsToNames = new Dictionary(); + + foreach(var fileInfo in filesInfo) + { + var file = new File() + { + Name = fileInfo.fileName, + IdCategory = fileInfo.idCategory, + Date = fileInfo.date, + IdUser = fileInfo.idUser + }; + + db.Files.Add(file); + db.SaveChanges(); + fileIdsToNames.Add(file.Name, file.Id); + } + + return fileIdsToNames; + } + + public IEnumerable GetFilesInfo(int wellId, int idCategory) + { + var fileInfoFromDb = db.Files.Where(f => f.IdWell == wellId && + f.IdCategory == idCategory).Select(file => new FilePropertiesDto + { + Id = file.Id, + Name = file.Name, + IdCategory = file.IdCategory, + UploadDate = file.Date, + UserName = file.IdUser.ToString() + }).ToList(); + + return fileInfoFromDb; + } + + public (int Id, string Name, int IdCategory)? GetFileInfo(int fileId) + { + var fileInfo = db.Files.FirstOrDefault(f => f.Id == fileId); + + if (fileInfo is null) + return null; + + return (fileInfo.Id, fileInfo.Name, fileInfo.IdCategory); + } + } +} diff --git a/AsbCloudWebApi/Controllers/FileController.cs b/AsbCloudWebApi/Controllers/FileController.cs new file mode 100644 index 00000000..bdc24449 --- /dev/null +++ b/AsbCloudWebApi/Controllers/FileController.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; + +namespace AsbCloudWebApi.Controllers +{ + [Route("api/files")] + [ApiController] + [Authorize] + public class FileController : ControllerBase + { + private readonly IFileService fileService; + private readonly IWellService wellService; + + public FileController(IFileService fileService, IWellService wellService) + { + this.fileService = fileService; + this.wellService = wellService; + } + + /// + /// Сохраняет переданные файлы и информацию о них + /// + /// id скважины + /// id категории файла + /// id отправившего файл пользователя + /// Коллекция файлов + /// + [HttpPost] + [Route("{wellId}/files")] + [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] + public IActionResult SaveFiles(int wellId, int idCategory, int idUser, + [FromForm] IFormFileCollection files) + { + int? idCompany = User.GetCompanyId(); + + if (idCompany is null) + return Forbid(); + + if (!wellService.CheckWellOwnership((int)idCompany, wellId)) + return Forbid(); + + var fileInfoCollection = files.Select(f => + (f.FileName, idCategory, DateTime.Now, idUser)); + + var fileNamesAndIds = fileService.SaveFilesPropertiesToDb(wellId, + idCategory, fileInfoCollection); + + foreach (var file in files) + { + var fileExtension = Path.GetExtension(file.FileName); + + var fileId = fileNamesAndIds[file.FileName]; + + var relativePath = Path.Combine(fileService.RootPath, $"{wellId}", + $"{idCategory}", $"{fileId}" + $"{fileExtension}"); + + Directory.CreateDirectory(Path.GetDirectoryName(relativePath)); + using var fileStream = new FileStream(relativePath, FileMode.Create); + file.CopyTo(fileStream); + } + + return Ok(); + } + + /// + /// Возвращает информацию о файлах для скважины в выбраной категории + /// + /// id скважины + /// id категории файла + /// Список информации о файлах в этой категории + [HttpGet] + [Route("{wellId}/filesInfo")] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public IActionResult GetFilesInfo([FromRoute] int wellId, int idCategory) + { + int? idCompany = User.GetCompanyId(); + + if (idCompany is null || !wellService.CheckWellOwnership((int)idCompany, wellId)) + return Forbid(); + + var filesInfo = fileService.GetFilesInfo(wellId, idCategory); + + if (!filesInfo.Any()) + return NoContent(); + + return Ok(filesInfo); + } + + /// + /// Возвращает файл с диска на сервере + /// + /// id скважины + /// id запрашиваемого файла + /// Запрашиваемый файл + [HttpGet] + [Route("{wellId}/{fileName}")] + [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)] + public IActionResult GetFile([FromRoute] int wellId, int fileId) + { + try + { + int? idCompany = User.GetCompanyId(); + + if (idCompany is null) + return Forbid(); + + if (!wellService.CheckWellOwnership((int)idCompany, wellId)) + return Forbid(); + + var fileInfo = fileService.GetFileInfo(fileId); + + if (fileInfo is null) + throw new FileNotFoundException(); + + // TODO: словарь content typoв + var relativePath = Path.Combine(fileService.RootPath, $"{wellId}", $"{fileInfo.Value.IdCategory}", + $"{fileInfo.Value.Id}", Path.GetExtension($"{fileInfo.Value.Name}")); + return PhysicalFile(Path.GetFullPath(relativePath), "application/octet-stream", fileInfo.Value.Name); + } + catch (FileNotFoundException ex) + { + return NotFound($"Файл не найден. Текст ошибки: {ex.Message}"); + } + } + } +}