merge dev to WellMapInfoDto-improvement

This commit is contained in:
ngfrolov 2023-06-30 17:45:04 +05:00
commit 5ec6fa2b2e
Signed by untrusted user who does not match committer: ng.frolov
GPG Key ID: E99907A0357B29A7
35 changed files with 33791 additions and 153 deletions

View File

@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data;
/// <summary>
/// Справка по страницам
/// </summary>
public class HelpPageDto : IId
{
/// <summary>
/// Id записи
/// </summary>
public int Id { get; set; }
/// <summary>
/// Id категории файла
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id категории файла не может быть меньше 1")]
public int IdCategory { get; set; }
/// <summary>
/// Url страницы, которой принадлежит справка
/// </summary>
public string UrlPage { get; set; } = null!;
/// <summary>
/// Имя файла
/// </summary>
[StringLength(260, MinimumLength = 1, ErrorMessage = "Допустимое имя файла от 1 до 260 символов")]
public string Name { get; set; } = null!;
/// <summary>
/// Размер файла
/// </summary>
public long Size { get; set; }
}

View File

@ -5,6 +5,11 @@
/// </summary>
public abstract class TrajectoryGeoDto
{
/// <summary>
/// Id скважины
/// </summary>
public int IdWell { get; set; }
/// <summary>
/// Глубина по стволу
/// </summary>
@ -18,13 +23,33 @@ public abstract class TrajectoryGeoDto
/// Азимут Географ.
/// </summary>
public double AzimuthGeo { get; set; }
/// <summary>
/// Азимут Магнитный
/// </summary>
public double? AzimuthMagnetic { get; set; }
/// <summary>
/// Глубина вертикальная
/// </summary>
public double? VerticalDepth { get; set; }
/// <summary>
/// Север отн- но устья
/// </summary>
public double? NorthOrifice { get; set; }
/// <summary>
/// Восток отн- но устья
/// </summary>
public double? EastOrifice { get; set; }
}
/// <summary>
/// Формирование данных по фактической географической траектории
/// </summary>
public class TrajectoryGeoFactDto : TrajectoryGeoDto
{}
{ }

View File

@ -11,11 +11,6 @@ namespace AsbCloudApp.Data
/// </summary>
public int Id { get; set; }
/// <summary>
/// ИД скважины
/// </summary>
public int IdWell { get; set; }
/// <summary>
/// Дата загрузки
/// </summary>
@ -26,31 +21,11 @@ namespace AsbCloudApp.Data
/// </summary>
public int IdUser { get; set; }
/// <summary>
/// Азимут Магнитный
/// </summary>
public double AzimuthMagnetic { get; set; }
/// <summary>
/// Глубина вертикальная
/// </summary>
public double VerticalDepth { get; set; }
/// <summary>
/// Абсолютная отметка
/// </summary>
public double AbsoluteMark { get; set; }
/// <summary>
/// Север отн- но устья
/// </summary>
public double NorthOrifice { get; set; }
/// <summary>
/// Восток отн- но устья
/// </summary>
public double EastOrifice { get; set; }
/// <summary>
/// Восток картографический
/// </summary>

View File

@ -27,7 +27,7 @@ namespace AsbCloudApp.Exceptions
/// преобразование в объект валидации
/// </summary>
/// <returns></returns>
public object ToValaidationErrorObject()
public object ToValidationErrorObject()
=> MakeValidationError(ParamName, Message);
/// <summary>

View File

@ -11,6 +11,7 @@ namespace AsbCloudApp.Repositories
/// </summary>
public interface IFileStorageRepository
{
/// <summary>
/// Получение длинны фала и проверка его наличия, если отсутствует падает исключение
/// </summary>
@ -35,10 +36,16 @@ namespace AsbCloudApp.Repositories
Task SaveFileAsync(string filePathRec, Stream fileStreamSrc, CancellationToken token);
/// <summary>
/// Удаление файла
/// Удаление пачки файлов
/// </summary>
/// <param name="filesName"></param>
void DeleteFile(IEnumerable<string> filesName);
void DeleteFiles(IEnumerable<string> filesName);
/// <summary>
/// Удаление одного файла
/// </summary>
/// <param name="fileName"></param>
void DeleteFile(string fileName);
/// <summary>
/// Удаление всех файлов с диска о которых нет информации в базе
@ -55,7 +62,7 @@ namespace AsbCloudApp.Repositories
IEnumerable<FileInfoDto> GetListFilesNotDisc(IEnumerable<FileInfoDto> files);
/// <summary>
/// Получение пути к файлу
/// Создание пути для сохранения файла связанного со скважиной
/// </summary>
/// <param name="idWell"></param>
/// <param name="idCategory"></param>
@ -63,15 +70,34 @@ namespace AsbCloudApp.Repositories
/// <param name="fileId"></param>
/// <returns></returns>
string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId);
/// <summary>
/// Создание пути для сохранения файла
/// </summary>
/// <param name="path1"></param>
/// <param name="path2"></param>
/// <param name="path3"></param>
/// <returns></returns>
string MakeFilePath(string path1, string path2, string path3);
/// <summary>
/// Получить путь для скачивания
/// Получение пути к файлу связанного со скважиной
/// </summary>
/// <param name="idWell"></param>
/// <param name="idCategory"></param>
/// <param name="idFile"></param>
/// <param name="dotExtention"></param>
/// <param name="dotExtenstion"></param>
/// <returns></returns>
string GetUrl(int idWell, int idCategory, int idFile, string dotExtention);
string GetFilePath(int idWell, int idCategory, int idFile, string dotExtenstion);
/// <summary>
/// Получение пути файла лежащего на диске
/// </summary>
/// <param name="path1"></param>
/// <param name="path2"></param>
/// <param name="idFile"></param>
/// <param name="dotExtenstion"></param>
/// <returns></returns>
string GetFilePath(string path1, string path2, int idFile, string dotExtenstion);
}
}

View File

@ -0,0 +1,23 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories;
/// <summary>
/// Интерфейс репозитория справок страниц
/// </summary>
public interface IHelpPageRepository : ICrudRepository<HelpPageDto>
{
/// <summary>
/// Получение справки по url страницы и id категории
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<HelpPageDto?> GetOrDefaultByUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken);
}

View File

@ -121,7 +121,7 @@ namespace AsbCloudApp.Services
return 0;
var filesName = files.Select(x => GetUrl(x.IdWell, x.IdCategory, x.Id, Path.GetExtension(x.Name)));
fileStorageRepository.DeleteFile(filesName);
fileStorageRepository.DeleteFiles(filesName);
return files.Any() ? 1 : 0;
}
@ -143,7 +143,7 @@ namespace AsbCloudApp.Services
/// <param name="dotExtention"></param>
/// <returns></returns>
public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) =>
fileStorageRepository.GetUrl(idWell, idCategory, idFile, dotExtention);
fileStorageRepository.GetFilePath(idWell, idCategory, idFile, dotExtention);
/// <summary>
/// пометить метку файла как удаленную

View File

@ -0,0 +1,37 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services;
/// <summary>
/// Интерфейс сервиса справок страниц
/// </summary>
public interface IHelpPageService
{
/// <summary>
/// Метод обновления или обновления файла справки
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="fileName"></param>
/// <param name="fileStream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<int> AddOrUpdateAsync(string urlPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken);
/// <summary>
/// Метод получения файла справки
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<(Stream stream, string fileName)> GetFileStreamAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_Permissions_WellContact : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_permission",
columns: new[] { "id", "description", "name" },
values: new object[,]
{
{ 519, "Разрешение просматривать список контактов", "WellContact.get" },
{ 520, "Разрешение редактировать список контактов", "WellContact.edit" }
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 519);
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 520);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_HelpPage : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_help_page",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
url_page = table.Column<string>(type: "text", nullable: false, comment: "Url страницы"),
id_category = table.Column<int>(type: "integer", nullable: false, comment: "id категории файла"),
name = table.Column<string>(type: "text", nullable: false, comment: "Название файла"),
file_size = table.Column<long>(type: "bigint", nullable: false, comment: "Размер файла")
},
constraints: table =>
{
table.PrimaryKey("PK_t_help_page", x => x.id);
table.ForeignKey(
name: "FK_t_help_page_t_file_category_id_category",
column: x => x.id_category,
principalTable: "t_file_category",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "Справки");
migrationBuilder.CreateIndex(
name: "IX_t_help_page_id_category",
table: "t_help_page",
column: "id_category");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_help_page");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_New_Init_Value_For_FileCategory : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_file_category",
columns: new[] { "id", "name", "short_name" },
values: new object[] { 20000, "Справки по страницам", null });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_file_category",
keyColumn: "id",
keyValue: 20000);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_New_Init_Value_For_Permission : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "t_permission",
columns: new[] { "id", "description", "name" },
values: new object[] { 521, "Разрешить создание справок по страницам", "HelpPage.edit" });
migrationBuilder.InsertData(
table: "t_relation_user_role_permission",
columns: new[] { "id_permission", "id_user_role" },
values: new object[] { 521, 1 });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "t_relation_user_role_permission",
keyColumns: new[] { "id_permission", "id_user_role" },
keyValues: new object[] { 521, 1 });
migrationBuilder.DeleteData(
table: "t_permission",
keyColumn: "id",
keyValue: 521);
}
}
}

View File

@ -19,7 +19,7 @@ namespace AsbCloudDb.Migrations
#pragma warning disable 612, 618
modelBuilder
.UseCollation("Russian_Russia.1251")
.HasAnnotation("ProductVersion", "6.0.7")
.HasAnnotation("ProductVersion", "6.0.19")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
@ -773,6 +773,11 @@ namespace AsbCloudDb.Migrations
{
Id = 10043,
Name = "Фактические данные бурения (вставляются в паспорт скважины)"
},
new
{
Id = 20000,
Name = "Справки по страницам"
});
});
@ -972,6 +977,46 @@ namespace AsbCloudDb.Migrations
b.HasComment("таблица данных ГТИ с типом значения string");
});
modelBuilder.Entity("AsbCloudDb.Model.HelpPage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("IdCategory")
.HasColumnType("integer")
.HasColumnName("id_category")
.HasComment("id категории файла");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name")
.HasComment("Название файла");
b.Property<long>("Size")
.HasColumnType("bigint")
.HasColumnName("file_size")
.HasComment("Размер файла");
b.Property<string>("UrlPage")
.IsRequired()
.HasColumnType("text")
.HasColumnName("url_page")
.HasComment("Url страницы");
b.HasKey("Id");
b.HasIndex("IdCategory");
b.ToTable("t_help_page");
b.HasComment("Справки");
});
modelBuilder.Entity("AsbCloudDb.Model.LimitingParameter", b =>
{
b.Property<int>("Id")
@ -2002,6 +2047,24 @@ namespace AsbCloudDb.Migrations
Id = 518,
Description = "Разрешение удалять вопрос",
Name = "FaqStatistics.delete"
},
new
{
Id = 519,
Description = "Разрешение просматривать список контактов",
Name = "WellContact.get"
},
new
{
Id = 520,
Description = "Разрешение редактировать список контактов",
Name = "WellContact.edit"
},
new
{
Id = 521,
Description = "Разрешить создание справок по страницам",
Name = "HelpPage.edit"
});
});
@ -3569,6 +3632,21 @@ namespace AsbCloudDb.Migrations
{
IdUserRole = 1,
IdPermission = 518
},
new
{
IdUserRole = 1,
IdPermission = 519
},
new
{
IdUserRole = 1,
IdPermission = 520
},
new
{
IdUserRole = 1,
IdPermission = 521
});
});
@ -7440,6 +7518,17 @@ namespace AsbCloudDb.Migrations
b.Navigation("Telemetry");
});
modelBuilder.Entity("AsbCloudDb.Model.HelpPage", b =>
{
b.HasOne("AsbCloudDb.Model.FileCategory", "FileCategory")
.WithMany()
.HasForeignKey("IdCategory")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FileCategory");
});
modelBuilder.Entity("AsbCloudDb.Model.LimitingParameter", b =>
{
b.HasOne("AsbCloudDb.Model.Telemetry", "Telemetry")

View File

@ -75,6 +75,8 @@ namespace AsbCloudDb.Model
public DbSet<Faq> Faqs => Set<Faq>();
public DbSet<HelpPage> HelpPages => Set<HelpPage>();
public AsbCloudDbContext() : base()
{
Interlocked.Increment(ref referenceCount);

View File

@ -72,6 +72,8 @@
new () {Id = 10041, Name = "Паспорт ОУС (заполняется геологами)"},
new () {Id = 10042, Name = "Паспорт скважины (заполняется геологами)"},
new () {Id = 10043, Name = "Фактические данные бурения (вставляются в паспорт скважины)"},
new () {Id = 20000, Name = "Справки по страницам"}
};
}
}

View File

@ -149,6 +149,11 @@
new (){ Id = 516, Name="FaqStatistics.get", Description="Разрешение просматривать статистику вопросов"},
new (){ Id = 517, Name="FaqStatistics.edit", Description="Разрешение редактировать вопрос"},
new (){ Id = 518, Name="FaqStatistics.delete", Description="Разрешение удалять вопрос"},
new (){ Id = 519, Name="WellContact.get", Description="Разрешение просматривать список контактов"},
new (){ Id = 520, Name="WellContact.edit", Description="Разрешение редактировать список контактов"},
new() { Id = 521, Name = "HelpPage.edit", Description = "Разрешить создание справок по страницам" }
};
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace AsbCloudDb.Model;
[Table("t_help_page"), Comment("Справки")]
public class HelpPage : IId
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("url_page"), Comment("Url страницы")]
public string UrlPage { get; set; } = null!;
[Column("id_category"), Comment("Id категории файла")]
public int IdCategory { get; set; }
[Column("name"), Comment("Название файла")]
public string Name { get; set; } = null!;
[Column("file_size"), Comment("Размер файла")]
public long Size { get; set; }
[JsonIgnore]
[ForeignKey(nameof(IdCategory))]
public virtual FileCategory FileCategory { get; set; } = null!;
}

View File

@ -67,6 +67,7 @@ namespace AsbCloudDb.Model
DbSet<Record50> Record50 { get; }
DbSet<Record60> Record60 { get; }
DbSet<Record61> Record61 { get; }
DbSet<HelpPage> HelpPages { get; }
DatabaseFacade Database { get; }

View File

@ -137,6 +137,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<IProcessMapReportMakerService, ProcessMapReportMakerService>();
services.AddTransient<IProcessMapReportService, ProcessMapReportService>();
services.AddTransient<WellInfoService>();
services.AddTransient<IHelpPageService, HelpPageService>();
services.AddTransient<TrajectoryService>();
@ -170,6 +171,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<ICrudRepository<DrillerDto>, CrudCacheRepositoryBase<DrillerDto, Driller>>();
services.AddTransient<IHelpPageRepository, HelpPageRepository>();
services.AddTransient<IFileRepository, FileRepository>();
services.AddTransient<IFileStorageRepository, FileStorageRepository>();
services.AddTransient<IWellCompositeRepository, WellCompositeRepository>();

View File

@ -6,116 +6,126 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
namespace AsbCloudInfrastructure.Repository;
public class FileStorageRepository : IFileStorageRepository
{
/// <summary>
/// Директория хранения файлов
/// </summary>
private readonly string RootPath = "files";
public class FileStorageRepository : IFileStorageRepository
public FileStorageRepository()
{
/// <summary>
/// Директория хранения файлов
/// </summary>
private readonly string RootPath = "files";
}
public FileStorageRepository()
public async Task SaveFileAsync(string filePathRec, Stream fileStreamSrc, CancellationToken token)
{
CreateDirectory(filePathRec);
using var newfileStream = new FileStream(filePathRec, FileMode.Create);
await fileStreamSrc.CopyToAsync(newfileStream, token).ConfigureAwait(false);
}
public void DeleteFiles(IEnumerable<string> filesName)
{
foreach (var fileName in filesName)
{
}
public async Task SaveFileAsync(string filePathRec, Stream fileStreamSrc, CancellationToken token)
{
CreateDirectory(filePathRec);
using var newfileStream = new FileStream(filePathRec, FileMode.Create);
await fileStreamSrc.CopyToAsync(newfileStream, token).ConfigureAwait(false);
}
public void DeleteFile(IEnumerable<string> filesName)
{
foreach (var fileName in filesName)
{
if (File.Exists(fileName))
File.Delete(fileName);
}
}
public long GetFileLength(string srcFilePath)
{
var sysFileInfo = new FileInfo(srcFilePath);
return sysFileInfo.Length;
}
public void MoveFile(string srcFilePath, string filePath)
{
CreateDirectory(filePath);
File.Move(srcFilePath, filePath);
}
public string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId)
{
return Path.Combine(RootPath, $"{idWell}",
$"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}");
}
public int DeleteFilesNotInList(int idWell, IEnumerable<int> idsFilesList)
{
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<FileInfoDto> GetListFilesNotDisc(IEnumerable<FileInfoDto> files)
{
var resutl = new List<FileInfoDto>();
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<int> GetIdsFiles(int idWell)
{
var result = new List<int>();
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<string> 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);
DeleteFile(fileName);
}
}
public void DeleteFile(string fileName)
{
if (File.Exists(fileName))
File.Delete(fileName);
}
public long GetFileLength(string srcFilePath)
{
var sysFileInfo = new FileInfo(srcFilePath);
return sysFileInfo.Length;
}
public void MoveFile(string srcFilePath, string filePath)
{
CreateDirectory(filePath);
File.Move(srcFilePath, filePath);
}
public string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId) =>
MakeFilePath($"{idWell}",
$"{idCategory}",
$"{fileId}" + $"{Path.GetExtension(fileFullName)}");
public string MakeFilePath(string path1, string path2, string path3) =>
Path.Combine(RootPath,
path1,
path2,
path3);
public int DeleteFilesNotInList(int idWell, IEnumerable<int> idsFilesList)
{
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<FileInfoDto> GetListFilesNotDisc(IEnumerable<FileInfoDto> files)
{
var resutl = new List<FileInfoDto>();
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 GetFilePath(int idWell, int idCategory, int idFile, string dotExtenstion) =>
GetFilePath(idWell.ToString(), idCategory.ToString(), idFile, dotExtenstion);
public string GetFilePath(string path1, string path2, int idFile, string dotExtenstion) =>
Path.Combine(RootPath, path1, path2, $"{idFile}{dotExtenstion}");
private IEnumerable<int> GetIdsFiles(int idWell)
{
var result = new List<int>();
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<string> 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);
}
}

View File

@ -0,0 +1,34 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository;
public class HelpPageRepository : CrudRepositoryBase<HelpPageDto, HelpPage>,
IHelpPageRepository
{
public HelpPageRepository(IAsbCloudDbContext context)
: base(context)
{
}
public async Task<HelpPageDto?> GetOrDefaultByUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken)
{
var helpPage = await dbSet.AsNoTracking()
.SingleOrDefaultAsync(x =>
x.UrlPage == urlPage &&
x.IdCategory == idCategory,
cancellationToken);
if (helpPage is null)
return null;
return helpPage.Adapt<HelpPageDto>();
}
}

View File

@ -1,5 +1,4 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
@ -20,24 +19,30 @@ namespace AsbCloudInfrastructure.Repository
this.db = db;
this.wellService = wellService;
}
public async Task<IEnumerable<TrajectoryGeoFactDto>> GetAsync(int idWell, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell);
if (well is null || well.Timezone is null)
throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell));
var well = await wellService.GetOrDefaultAsync(idWell,
token);
if (well is null)
return Enumerable.Empty<TrajectoryGeoFactDto>();
var entities = await db.Record7
.AsNoTracking()
.Where(x => x.IdTelemetry == well.IdTelemetry)
.Where(coord => coord.Deptsvym != null && coord.Svyinc != null && coord.Svyazc != null)
.OrderBy(e => e.Deptsvym)
.Select(x => new { x.Deptsvym, x.Svyinc, x.Svyazc })
.ToArrayAsync(token);
var result = entities
.Select(coord => new TrajectoryGeoFactDto
{
IdWell = idWell,
AzimuthMagnetic = coord.Svymtf,
VerticalDepth = coord.Deptsvyv,
NorthOrifice = coord.Svyns,
EastOrifice = coord.Svyew,
WellboreDepth = coord.Deptsvym!.Value,
ZenithAngle = coord.Svyinc!.Value,
AzimuthGeo = coord.Svyazc!.Value

View File

@ -0,0 +1,167 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using AsbCloudApp.Exceptions;
namespace AsbCloudInfrastructure.Services;
/// <summary>
/// Реализация сервиса для справок по страницам
/// </summary>
public class HelpPageService : IHelpPageService
{
private readonly string directoryNameHelpPageFiles;
private readonly IHelpPageRepository helpPageRepository;
private readonly IFileStorageRepository fileStorageRepository;
/// <summary>
/// Конструктор класса
/// </summary>
/// <param name="helpPageRepository"></param>
/// <param name="fileStorageRepository"></param>
/// <param name="configuration"></param>
public HelpPageService(IHelpPageRepository helpPageRepository,
IFileStorageRepository fileStorageRepository,
IConfiguration configuration)
{
this.helpPageRepository = helpPageRepository;
this.fileStorageRepository = fileStorageRepository;
directoryNameHelpPageFiles = configuration.GetValue<string>("DirectoryNameHelpPageFiles");
}
/// <summary>
/// Метод обновления или обновления файла справки
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="fileName"></param>
/// <param name="fileStream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<int> AddOrUpdateAsync(string urlPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken)
{
var helpPage = await helpPageRepository.GetOrDefaultByUrlPageAndIdCategoryAsync(urlPage,
idCategory,
cancellationToken);
if(helpPage is not null)
{
await UpdateFileAsync(helpPage,
idCategory,
fileName,
fileStream,
cancellationToken);
return helpPage.Id;
}
return await SaveFileAsync(urlPage,
idCategory,
fileName,
fileStream,
cancellationToken);
}
/// <summary>
/// Метод получения файла справки
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="ArgumentInvalidException"></exception>
public async Task<(Stream stream, string fileName)> GetFileStreamAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken)
{
var helpPage = await helpPageRepository.GetOrDefaultByUrlPageAndIdCategoryAsync(urlPage,
idCategory,
cancellationToken) ?? throw new ArgumentInvalidException("Справки не существует", nameof(idCategory));
string filePath = fileStorageRepository.GetFilePath(directoryNameHelpPageFiles,
helpPage.IdCategory.ToString(),
helpPage.Id,
Path.GetExtension(helpPage.Name));
var fileStream = new FileStream(Path.GetFullPath(filePath), FileMode.Open);
return (fileStream, helpPage.Name);
}
private async Task<int> SaveFileAsync(string urlPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken)
{
HelpPageDto helpPage = new()
{
UrlPage = urlPage,
IdCategory = idCategory,
Name = Path.GetFileName(fileName),
Size = fileStream.Length,
};
int idFile = await helpPageRepository.InsertAsync(helpPage,
cancellationToken);
await SaveFileAsync(idCategory,
fileName,
fileStream,
idFile,
cancellationToken);
return idFile;
}
private async Task UpdateFileAsync(HelpPageDto helpPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken)
{
helpPage.Name = Path.GetFileName(fileName);
helpPage.IdCategory = idCategory;
helpPage.Size = fileStream.Length;
string fileFullName = fileStorageRepository.GetFilePath(directoryNameHelpPageFiles,
idCategory.ToString(),
helpPage.Id,
Path.GetExtension(helpPage.Name));
await helpPageRepository.UpdateAsync(helpPage,
cancellationToken);
fileStorageRepository.DeleteFile(fileFullName);
await SaveFileAsync(helpPage.IdCategory,
fileName,
fileStream,
helpPage.Id,
cancellationToken);
}
private async Task SaveFileAsync(int idCategory,
string fileName,
Stream fileStream,
int fileId,
CancellationToken cancellationToken)
{
string filePath = fileStorageRepository.MakeFilePath(directoryNameHelpPageFiles,
idCategory.ToString(),
$"{fileId}" + $"{Path.GetExtension(fileName)}");
await fileStorageRepository.SaveFileAsync(filePath,
fileStream,
cancellationToken);
}
}

View File

@ -128,7 +128,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
});
var storageRepositoryMock = new Mock<IFileStorageRepository>();
storageRepositoryMock.Setup(x => x.GetUrl(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>()))
storageRepositoryMock.Setup(x => x.GetFilePath(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>()))
.Returns((int idWell, int idCategory, int idFile, string dotExtention) => {
return Path.Combine("files", idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
});

View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services;
using Microsoft.Extensions.Configuration;
using Moq;
using Xunit;
namespace AsbCloudWebApi.Tests.ServicesTests;
public class HelpPageServiceTest
{
private static Dictionary<string, string> configSettings = new (){
{"DirectoryNameHelpPageFiles", "helpPages"}
};
private static List<HelpPageDto> HelpPages = new()
{
new()
{
Id = 123,
IdCategory = 20000,
Name = "Справка1.pdf",
Size = 54000,
UrlPage = "test"
},
new()
{
Id = 134,
IdCategory = 20000,
Name = "Справка2.pdf",
Size = 51000,
UrlPage = "test1"
},
new()
{
Id = 178,
IdCategory = 10000,
Name = "Справка3.pdf",
Size = 49000,
UrlPage = "test2"
}
};
private readonly Mock<IHelpPageRepository> helpPageRepository = new();
private readonly Mock<IFileStorageRepository> fileStorageRepository = new();
private readonly IHelpPageService helpPageService;
public HelpPageServiceTest()
{
IConfiguration configuration = new ConfigurationBuilder()
.AddInMemoryCollection(configSettings)
.Build();
helpPageService = new HelpPageService(helpPageRepository.Object,
fileStorageRepository.Object,
configuration);
}
[Fact]
public async Task AddOrUpdateAsync_ShouldReturn_NewHelpPage()
{
//arrange
int idHelpPage = new Random().Next(1, 100);
string urlPage = "test";
int idCategory = 20000;
string fileName = "test.pdf";
MemoryStream fileStream = new MemoryStream(Array.Empty<byte>());
helpPageRepository.Setup(x => x.GetOrDefaultByUrlPageAndIdCategoryAsync(It.IsAny<string>(),
It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(() =>
{
var helpPage = HelpPages.FirstOrDefault(x =>
x.UrlPage == urlPage &&
x.IdCategory == idCategory);
return Task.FromResult(helpPage);
});
helpPageRepository.Setup(x => x.InsertAsync(It.IsAny<HelpPageDto>(),
It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(idHelpPage));
fileStorageRepository.Setup(x => x.SaveFileAsync(It.IsAny<string>(),
It.IsAny<Stream>(),
It.IsAny<CancellationToken>()));
//act
int result = await helpPageService.AddOrUpdateAsync(urlPage,
idCategory,
fileName,
fileStream,
CancellationToken.None);
//assert
Assert.True(result > 0);
}
[Fact]
public async Task UpdateAsync_ShouldReturn_UpdatedHelpPage()
{
//arrange
int idHelpPage = new Random().Next(1, 100);
string urlPage = "test";
int newIdCategory = 20000;
string newFileName = "test.pdf";
MemoryStream newFileStream = new MemoryStream(Array.Empty<byte>());
HelpPageDto existingHelpPage = HelpPages.First(x =>
x.UrlPage == urlPage &&
x.IdCategory == newIdCategory);
helpPageRepository.Setup(x => x.GetOrDefaultByUrlPageAndIdCategoryAsync(It.IsAny<string>(),
It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(existingHelpPage)!);
helpPageRepository.Setup(x => x.InsertAsync(It.IsAny<HelpPageDto>(),
It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(idHelpPage));
fileStorageRepository.Setup(x => x.SaveFileAsync(It.IsAny<string>(),
It.IsAny<Stream>(),
It.IsAny<CancellationToken>()));
//act
await helpPageService.AddOrUpdateAsync(urlPage,
newIdCategory,
newFileName,
newFileStream,
CancellationToken.None);
//assert
Assert.Equal(newFileName, existingHelpPage.Name);
Assert.Equal(newIdCategory, existingHelpPage.IdCategory);
Assert.Equal(newFileStream.Length, existingHelpPage.Size);
}
}

View File

@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers;
/// <summary>
/// Фактическая траектория
/// </summary>
[Authorize]
[ApiController]
[Route("api/well/{idWell}/[controller]")]
public class FactTrajectoryController : ControllerBase
{
private readonly ITrajectoryFactRepository trajectoryFactRepository;
public FactTrajectoryController(ITrajectoryFactRepository trajectoryFactRepository)
{
this.trajectoryFactRepository = trajectoryFactRepository;
}
/// <summary>
/// Метод получения всех строк траекторий по id скважины
/// </summary>
/// <param name="idWell">Id скважины</param>
/// <param name="cancellationToken">Токен отмены операции</param>
/// <returns></returns>
[HttpGet]
[Route("getRows")]
[ProducesResponseType(typeof(IEnumerable<TrajectoryGeoPlanDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetRowsAsync([FromRoute] int idWell,
CancellationToken cancellationToken)
{
var factTrajectories = await trajectoryFactRepository.GetAsync(idWell,
cancellationToken);
return Ok(factTrajectories);
}
}

View File

@ -0,0 +1,97 @@
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Authorization;
using System.Net;
namespace AsbCloudWebApi.Controllers;
/// <summary>
/// Справки по страницам
/// </summary>
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class HelpPageController : ControllerBase
{
private readonly IHelpPageService helpPageService;
private readonly IUserRepository userRepository;
public HelpPageController(IHelpPageService helpPageService,
IUserRepository userRepository)
{
this.helpPageService = helpPageService;
this.userRepository = userRepository;
}
/// <summary>
/// Загрузка файла справки
/// </summary>
/// <param name="urlPage">Url страницы</param>
/// <param name="idCategory">Id категории файла</param>
/// <param name="file">Файл справки</param>
/// <param name="cancellationToken">Токен для отмены задачи</param>
/// <returns></returns>
[HttpPost]
[Permission]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> UploadAsync(
string urlPage,
[Range(minimum: 20000, maximum: 20000, ErrorMessage = "Категория файла недопустима. Допустимые: 20000")]
int idCategory,
[Required] IFormFile file,
CancellationToken cancellationToken)
{
int? idUser = User.GetUserId();
if(!idUser.HasValue)
return Forbid();
if (!userRepository.HasPermission(idUser.Value, $"HelpPage.edit"))
return Forbid();
using var fileStream = file.OpenReadStream();
int helpPageId = await helpPageService.AddOrUpdateAsync(urlPage,
idCategory,
file.FileName,
fileStream,
cancellationToken);
return Ok(helpPageId);
}
/// <summary>
/// Получение файла справки
/// </summary>
/// <param name="urlPage">Url страницы</param>
/// <param name="idCategory">Id категории файла</param>
/// <param name="cancellationToken">Токен для отмены задачи</param>
/// <returns></returns>
[HttpGet]
[Route("{urlPage}/{idCategory}")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> GetFileAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken)
{
var file = await helpPageService.GetFileStreamAsync(urlPage,
idCategory,
cancellationToken);
using var fileStream = file.stream;
var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream,
cancellationToken);
memoryStream.Position = 0;
return File(memoryStream, "application/octet-stream", file.fileName);
}
}

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -60,8 +61,8 @@ namespace AsbCloudWebApi.Controllers
var idWell = telemetryService.GetIdWellByTelemetryUid(uid);
if (idWell is null)
return BadRequest($"Wrong uid {uid}");
throw new NotImplementedException();
#warning implement Process map get method
return Ok(Enumerable.Empty<ProcessMapPlanDto>());
}
/// <summary>

View File

@ -1,5 +1,4 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.User;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

View File

@ -1,7 +1,6 @@
using AsbCloudApp.Exceptions;
using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Middlewares
@ -36,12 +35,15 @@ namespace AsbCloudWebApi.Middlewares
context.Response.Clear();
context.Response.StatusCode = 403;
}
catch (TaskCanceledException ex)
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex) // TODO: find explicit exception. Use Trace. Add body size to message.
{
context.Response.Clear();
context.Response.StatusCode = 500;
if (ex.Message.Contains("Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate."))
Console.WriteLine("Reading the request body timed out due to data arriving too slowly.");
else
@ -51,7 +53,7 @@ namespace AsbCloudWebApi.Middlewares
private static string MakeJsonBody(ArgumentInvalidException ex)
{
object error = ex.ToValaidationErrorObject();
object error = ex.ToValidationErrorObject();
var buffer = System.Text.Json.JsonSerializer.Serialize(error);
return buffer;
}

View File

@ -26,6 +26,7 @@
"companyName": "ООО \"Цифровое бурение\"",
"supportMail": "support@digitaldrilling.ru"
},
"DirectoryNameHelpPageFiles": "helpPages",
"Urls": "http://0.0.0.0:5000" //;https://0.0.0.0:5001" //,
// See https man: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-6.0
//"Kestrel": {