Add ProcessMapPlanDrilling.

This commit is contained in:
ngfrolov 2024-01-19 17:48:45 +05:00
parent 657c542cb2
commit 6178061b49
Signed by untrusted user who does not match committer: ng.frolov
GPG Key ID: E99907A0357B29A7
20 changed files with 10823 additions and 5 deletions

View File

@ -0,0 +1,58 @@
using System;
namespace AsbCloudApp.Data;
/// <summary>
/// Часть записи описывающая изменение
/// </summary>
public abstract class ChangeLogAbstract
{
/// <summary>
/// ИД записи
/// </summary>
public int Id { get; set; }
/// <summary>
/// Автор изменения
/// </summary>
public int IdAuthor { get; set; }
/// <summary>
/// Редактор
/// </summary>
public int? IdEditor { get; set; }
/// <summary>
/// Дата создания записи
/// </summary>
public DateTimeOffset Creation { get; set; }
/// <summary>
/// Дата устаревания (например при удалении)
/// </summary>
public DateTimeOffset? Obsolete { get; set; }
/// <summary>
/// ИД состояния записи:
/// <list type="table">
/// <item>
/// <term>0</term>
/// <description>актуальная запись</description>
/// </item>
/// <item>
/// <term>1</term>
/// <description>замененная запись</description>
/// </item>
/// <item>
/// <term>2</term>
/// <description>удаленная запись</description>
/// </item>
/// </list>
/// </summary>
public int IdState { get; set; }
/// <summary>
/// Id заменяемой записи
/// </summary>
public int? IdPrevious { get; set; }
}

View File

@ -1,4 +1,8 @@
namespace AsbCloudApp.Data
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data
{
/// <summary>
/// Плановое значение и максимально допустимое ограничение
@ -15,5 +19,66 @@
/// </summary>
public double LimitMax { get; set; }
/// <summary>
/// Валидация
/// </summary>
/// <param name="commonRange">Общий диапазон для плана и ограничения</param>
/// <param name="paramName"></param>
/// <returns></returns>
public virtual IEnumerable<ValidationResult> Validate((double GE, double LE) commonRange, string paramName)
=> Validate(commonRange, commonRange, paramName);
/// <summary>
/// Валидация
/// </summary>
/// <param name="planRange"></param>
/// <param name="limitMaxRange"></param>
/// <param name="paramName">Название параметра для которого задается план и ограничение</param>
/// <returns></returns>
public virtual IEnumerable<ValidationResult> Validate((double GE, double LE) planRange, (double GE, double LE) limitMaxRange, string paramName)
{
if (Plan < planRange.GE || Plan > planRange.LE)
yield return new ValidationResult($"{paramName} плановое значение должно быть в диапазоне [{planRange.GE}; {planRange.LE}].");
if (Plan < planRange.GE || Plan > planRange.LE)
yield return new ValidationResult($"{paramName} ограничивающее значение должно быть в диапазоне [{limitMaxRange.GE}; {limitMaxRange.LE}].");
}
}
/// <summary>
/// Реализация RangeAttribute для PlanLimitDto
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
AllowMultiple = false)]
public class RangePlanLimitAttribute : ValidationAttribute
{
private readonly double minimum;
private readonly double maximum;
/// <summary>
/// Реализация RangeAttribute для PlanLimitDto
/// </summary>
/// <param name="minimum"></param>
/// <param name="maximum"></param>
public RangePlanLimitAttribute(double minimum, double maximum)
{
this.minimum = minimum;
this.maximum = maximum;
}
/// <inheritdoc/>
public override bool IsValid(object? value)
{
try
{
if(value is PlanLimitDto dto)
{
var isPlanValid = dto.Plan <= maximum && dto.Plan >= minimum;
var isLimitMaxValid = dto.LimitMax <= maximum && dto.LimitMax >= minimum;
return isPlanValid && isLimitMaxValid;
}
}catch{}
return false;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data.ProcessMapPlan;
/// <inheritdoc/>
public abstract class ProcessMapPlanBaseDto : ChangeLogAbstract, IId, IWellRelated, IValidatableObject
{
/// <summary>
/// Id скважины
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")]
public int IdWell { get; set; }
/// <summary>
/// Тип секции
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id секции скважины не может быть меньше 1")]
public int IdWellSectionType { get; set; }
/// <summary>
/// Глубина по стволу от, м
/// <para>
/// на начало интервала
/// </para>
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Глубина не может быть отрицательной")]
public double DepthStart { get; set; }
/// <summary>
/// Глубина по стволу до, м
/// <para>
/// на конец интервала
/// </para>
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Глубина не может быть отрицательной")]
public double DepthEnd { get; set; }
/// <inheritdoc/>
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(DepthEnd <= DepthStart)
yield return new ("глубина окончания должна быть больше глубины начала", new string[] {nameof(DepthEnd), nameof(DepthStart) });
}
}

View File

@ -0,0 +1,98 @@
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data.ProcessMapPlan;
/// <summary>
/// РТК план бурение скважины
/// </summary>
public class ProcessMapPlanDrillingDto : ProcessMapPlanBaseDto
{
/// <summary>
/// Id режима 1-ротор, 2 - слайд
/// </summary>
[Range(1, 2, ErrorMessage = "Id режима должен быть либо 1-ротор либо 2-слайд")]
public int IdMode { get; set; }
/// <summary>
/// Осевая нагрузка, т план
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Осевая нагрузка, т должна быть в пределах от 0 до 99999.9")]
public double AxialLoadPlan { get; set; }
/// <summary>
/// Осевая нагрузка, т ограничение
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Осевая нагрузка, т должна быть в пределах от 0 до 99999.9")]
public double AxialLoadLimitMax { get; set; }
/// <summary>
/// Перепад давления, атм план
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Перепад давления, атм должна быть в пределах от 0 до 99999.9")]
public double DeltaPressurePlan { get; set; }
/// <summary>
/// Перепад давления, атм ограничение
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Перепад давления, атм должна быть в пределах от 0 до 99999.9")]
public double DeltaPressureLimitMax { get; set; }
/// <summary>
/// Момент на ВСП, кН*м план
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Момент на ВСП, кН*м должна быть в пределах от 0 до 99999.9")]
public double TopDriveTorquePlan { get; set; }
/// <summary>
/// Момент на ВСП, кН*м ограничение
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Момент на ВСП, кН*м должна быть в пределах от 0 до 99999.9")]
public double TopDriveTorqueLimitMax { get; set; }
/// <summary>
/// Обороты на ВСП, об/мин план
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Обороты на ВСП, об/мин должна быть в пределах от 0 до 99999.9")]
public double TopDriveSpeedPlan { get; set; }
/// <summary>
/// Обороты на ВСП, об/мин ограничение
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Обороты на ВСП, об/мин должна быть в пределах от 0 до 99999.9")]
public double TopDriveSpeedLimitMax { get; set; }
/// <summary>
/// Расход, л/с план
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Расход, л/с должна быть в пределах от 0 до 99999.9")]
public double FlowPlan { get; set; }
/// <summary>
/// Расход, л/с ограничение
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Расход, л/с должна быть в пределах от 0 до 99999.9")]
public double FlowLimitMax { get; set; }
/// <summary>
/// Плановая механическая скорость, м/ч
/// </summary>
[Range(0, 99999.9, ErrorMessage = "Плановая механическая скорость, м/ч должно быть в пределах от 0 до 99999.9")]
public double RopPlan { get; set; }
/// <summary>
/// Плановый процент использования АКБ
/// </summary>
[Range(0, 100, ErrorMessage = "Процент использования АКБ должен быть в пределах от 0 до 100")]
public double UsageSaub { get; set; }
/// <summary>
/// Плановый процент использования spin master
/// </summary>
[Range(0, 100, ErrorMessage = "Процент использования spin master должен быть в пределах от 0 до 100")]
public double UsageSpin { get; set; }
/// <summary>
/// Комментарий
/// </summary>
public string Comment { get; set; } = string.Empty;
}

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
namespace AsbCloudApp.Repositories;
/// <summary>
/// Репозиторий для записей с историей
/// </summary>
public interface IChangeLogRepository<T>
where T : ChangeLogAbstract
{
/// <summary>
/// Добавление записей
/// </summary>
/// <param name="idUser"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> InsertRangeAsync(int idUser, IEnumerable<T> dtos, CancellationToken token);
/// <summary>
/// Редактирование записей
/// </summary>
/// <param name="idUser"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateRangeAsync(int idUser, IEnumerable<T> dtos, CancellationToken token);
/// <summary>
/// Удаление записей
/// </summary>
/// <param name="idUser"></param>
/// <param name="ids"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteRangeAsync(int idUser, IEnumerable<int> ids, CancellationToken token);
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Requests;
namespace AsbCloudApp.Repositories;
/// <summary>
/// Общий интерфейс для РТК план с учетом истории изменений
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IProcessMapPlanBaseRepository<T>: IChangeLogRepository<T>
where T: ProcessMapPlanBaseDto
{
/// <summary>
/// Добавление записей с удалением старых (для импорта)
/// </summary>
/// <param name="idUser"></param>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> ClearAndInsertRangeAsync(int idUser, int idWell, IEnumerable<T> dtos, CancellationToken token);
/// <summary>
/// Получение дат изменений записей
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<DateOnly>> GetDatesChangeAsync(int idWell, CancellationToken token);
/// <summary>
/// Получение журнала изменений
/// </summary>
/// <param name="idWell"></param>
/// <param name="date"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<T>> GetChangeLogAsync(int idWell, DateOnly? date, CancellationToken token);
/// <summary>
/// Получение записей по параметрам
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<T>> GetAsync(ProcessMapPlanBaseRequest request, CancellationToken token);
}

View File

@ -0,0 +1,14 @@
using System;
namespace AsbCloudApp.Requests;
/// <summary>
/// Базовый запрос актуальных данных
/// </summary>
public class ChangeLogBaseRequest
{
/// <summary>
/// Дата/время на которую записи были актуальны. Если не задано, то не фильтруется
/// </summary>
public DateTimeOffset? Moment { get; set; }
}

View File

@ -0,0 +1,27 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Requests;
/// <summary>
/// Запрос для получения РТК план
/// </summary>
public class ProcessMapPlanBaseRequest: ChangeLogBaseRequest
{
/// <summary>
/// Идентификатор скважины
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id скважины - положительное число")]
public int IdWell { get; set; }
/// <summary>
/// Тип секции
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id секции - положительное число")]
public int? IdWellSectionType { get; set; }
/// <summary>
/// Вернуть данные, которые поменялись с указанной даты
/// </summary>
public DateTimeOffset? UpdateFrom { get; set; }
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace AsbCloudDb.Migrations
{
public partial class Add_processmap_plan_drilling_with_changeLog : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "t_process_map_plan_drilling",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false, comment: "Идентификатор")
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
id_mode = table.Column<int>(type: "integer", nullable: false, comment: "Id режима (1- ротор, 2 слайд)"),
axial_load_plan = table.Column<double>(type: "double precision", nullable: false, comment: "Осевая нагрузка, т, план"),
axial_load_limit_max = table.Column<double>(type: "double precision", nullable: false, comment: "Осевая нагрузка, т, допустимый максимум"),
delta_pressure_plan = table.Column<double>(type: "double precision", nullable: false, comment: "Перепад давления, атм, план"),
delta_pressure_limit_max = table.Column<double>(type: "double precision", nullable: false, comment: "Перепад давления, атм, допустимый максимум"),
top_drive_torque_plan = table.Column<double>(type: "double precision", nullable: false, comment: "Момент на ВСП, план"),
top_drive_torque_limit_max = table.Column<double>(type: "double precision", nullable: false, comment: "Момент на ВСП, допустимый максимум"),
top_drive_speed_plan = table.Column<double>(type: "double precision", nullable: false, comment: "Обороты на ВСП, план"),
top_drive_speed_limit_max = table.Column<double>(type: "double precision", nullable: false, comment: "Обороты на ВСП, допустимый максимум"),
flow_plan = table.Column<double>(type: "double precision", nullable: false, comment: "Расход, л/с, план"),
flow_limit_max = table.Column<double>(type: "double precision", nullable: false, comment: "Расход, л/с, допустимый максимум"),
rop_plan = table.Column<double>(type: "double precision", nullable: false, comment: "Плановая механическая скорость, м/ч"),
usage_saub = table.Column<double>(type: "double precision", nullable: false, comment: "Плановый процент использования АКБ"),
usage_spin = table.Column<double>(type: "double precision", nullable: false, comment: "Плановый процент использования spin master"),
comment = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false, comment: "Комментарий"),
id_author = table.Column<int>(type: "integer", nullable: false, comment: "Автор"),
id_editor = table.Column<int>(type: "integer", nullable: true, comment: "Редактор"),
creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "дата создания"),
obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "дата устаревания"),
id_state = table.Column<int>(type: "integer", nullable: false, comment: "ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная"),
id_previous = table.Column<int>(type: "integer", nullable: true, comment: "ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная"),
id_well = table.Column<int>(type: "integer", nullable: false, comment: "Id скважины"),
id_wellsection_type = table.Column<int>(type: "integer", nullable: false, comment: "Тип секции"),
depth_start = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина по стволу от, м"),
depth_end = table.Column<double>(type: "double precision", nullable: false, comment: "Глубина по стволу до, м")
},
constraints: table =>
{
table.PrimaryKey("PK_t_process_map_plan_drilling", x => x.id);
table.ForeignKey(
name: "FK_t_process_map_plan_drilling_t_process_map_plan_drilling_id_~",
column: x => x.id_previous,
principalTable: "t_process_map_plan_drilling",
principalColumn: "id");
table.ForeignKey(
name: "FK_t_process_map_plan_drilling_t_user_id_author",
column: x => x.id_author,
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_t_process_map_plan_drilling_t_user_id_editor",
column: x => x.id_editor,
principalTable: "t_user",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_t_process_map_plan_drilling_t_well_id_well",
column: x => x.id_well,
principalTable: "t_well",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_t_process_map_plan_drilling_t_well_section_type_id_wellsect~",
column: x => x.id_wellsection_type,
principalTable: "t_well_section_type",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
},
comment: "РТК план бурение");
migrationBuilder.CreateIndex(
name: "IX_t_process_map_plan_drilling_id_author",
table: "t_process_map_plan_drilling",
column: "id_author");
migrationBuilder.CreateIndex(
name: "IX_t_process_map_plan_drilling_id_editor",
table: "t_process_map_plan_drilling",
column: "id_editor");
migrationBuilder.CreateIndex(
name: "IX_t_process_map_plan_drilling_id_previous",
table: "t_process_map_plan_drilling",
column: "id_previous");
migrationBuilder.CreateIndex(
name: "IX_t_process_map_plan_drilling_id_well",
table: "t_process_map_plan_drilling",
column: "id_well");
migrationBuilder.CreateIndex(
name: "IX_t_process_map_plan_drilling_id_wellsection_type",
table: "t_process_map_plan_drilling",
column: "id_wellsection_type");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "t_process_map_plan_drilling");
}
}
}

View File

@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using System.Collections.Generic;
#nullable disable
@ -20,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.22")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
@ -388,7 +387,7 @@ namespace AsbCloudDb.Migrations
.HasColumnName("enabled_subsystems")
.HasComment("флаги включенных подсистем");
b.Property<IDictionary<string, object>>("ExtraData")
b.Property<string>("ExtraData")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("extra_data")
@ -2464,6 +2463,160 @@ namespace AsbCloudDb.Migrations
});
});
modelBuilder.Entity("AsbCloudDb.Model.ProcessMaps.ProcessMapPlanDrilling", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id")
.HasComment("Идентификатор");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("AxialLoadLimitMax")
.HasColumnType("double precision")
.HasColumnName("axial_load_limit_max")
.HasComment("Осевая нагрузка, т, допустимый максимум");
b.Property<double>("AxialLoadPlan")
.HasColumnType("double precision")
.HasColumnName("axial_load_plan")
.HasComment("Осевая нагрузка, т, план");
b.Property<string>("Comment")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("character varying(1024)")
.HasColumnName("comment")
.HasComment("Комментарий");
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasColumnName("creation")
.HasComment("дата создания");
b.Property<double>("DeltaPressureLimitMax")
.HasColumnType("double precision")
.HasColumnName("delta_pressure_limit_max")
.HasComment("Перепад давления, атм, допустимый максимум");
b.Property<double>("DeltaPressurePlan")
.HasColumnType("double precision")
.HasColumnName("delta_pressure_plan")
.HasComment("Перепад давления, атм, план");
b.Property<double>("DepthEnd")
.HasColumnType("double precision")
.HasColumnName("depth_end")
.HasComment("Глубина по стволу до, м");
b.Property<double>("DepthStart")
.HasColumnType("double precision")
.HasColumnName("depth_start")
.HasComment("Глубина по стволу от, м");
b.Property<double>("FlowLimitMax")
.HasColumnType("double precision")
.HasColumnName("flow_limit_max")
.HasComment("Расход, л/с, допустимый максимум");
b.Property<double>("FlowPlan")
.HasColumnType("double precision")
.HasColumnName("flow_plan")
.HasComment("Расход, л/с, план");
b.Property<int>("IdAuthor")
.HasColumnType("integer")
.HasColumnName("id_author")
.HasComment("Автор");
b.Property<int?>("IdEditor")
.HasColumnType("integer")
.HasColumnName("id_editor")
.HasComment("Редактор");
b.Property<int>("IdMode")
.HasColumnType("integer")
.HasColumnName("id_mode")
.HasComment("Id режима (1- ротор, 2 слайд)");
b.Property<int?>("IdPrevious")
.HasColumnType("integer")
.HasColumnName("id_previous")
.HasComment("ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная");
b.Property<int>("IdState")
.HasColumnType("integer")
.HasColumnName("id_state")
.HasComment("ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная");
b.Property<int>("IdWell")
.HasColumnType("integer")
.HasColumnName("id_well")
.HasComment("Id скважины");
b.Property<int>("IdWellSectionType")
.HasColumnType("integer")
.HasColumnName("id_wellsection_type")
.HasComment("Тип секции");
b.Property<DateTimeOffset?>("Obsolete")
.HasColumnType("timestamp with time zone")
.HasColumnName("obsolete")
.HasComment("дата устаревания");
b.Property<double>("RopPlan")
.HasColumnType("double precision")
.HasColumnName("rop_plan")
.HasComment("Плановая механическая скорость, м/ч");
b.Property<double>("TopDriveSpeedLimitMax")
.HasColumnType("double precision")
.HasColumnName("top_drive_speed_limit_max")
.HasComment("Обороты на ВСП, допустимый максимум");
b.Property<double>("TopDriveSpeedPlan")
.HasColumnType("double precision")
.HasColumnName("top_drive_speed_plan")
.HasComment("Обороты на ВСП, план");
b.Property<double>("TopDriveTorqueLimitMax")
.HasColumnType("double precision")
.HasColumnName("top_drive_torque_limit_max")
.HasComment("Момент на ВСП, допустимый максимум");
b.Property<double>("TopDriveTorquePlan")
.HasColumnType("double precision")
.HasColumnName("top_drive_torque_plan")
.HasComment("Момент на ВСП, план");
b.Property<double>("UsageSaub")
.HasColumnType("double precision")
.HasColumnName("usage_saub")
.HasComment("Плановый процент использования АКБ");
b.Property<double>("UsageSpin")
.HasColumnType("double precision")
.HasColumnName("usage_spin")
.HasComment("Плановый процент использования spin master");
b.HasKey("Id");
b.HasIndex("IdAuthor");
b.HasIndex("IdEditor");
b.HasIndex("IdPrevious");
b.HasIndex("IdWell");
b.HasIndex("IdWellSectionType");
b.ToTable("t_process_map_plan_drilling");
b.HasComment("РТК план бурение");
});
modelBuilder.Entity("AsbCloudDb.Model.ProcessMaps.ProcessMapWellDrilling", b =>
{
b.Property<int>("Id")
@ -8400,6 +8553,46 @@ namespace AsbCloudDb.Migrations
b.Navigation("Well");
});
modelBuilder.Entity("AsbCloudDb.Model.ProcessMaps.ProcessMapPlanDrilling", b =>
{
b.HasOne("AsbCloudDb.Model.User", "Author")
.WithMany()
.HasForeignKey("IdAuthor")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("AsbCloudDb.Model.User", "Editor")
.WithMany()
.HasForeignKey("IdEditor")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("AsbCloudDb.Model.ProcessMaps.ProcessMapPlanDrilling", "Previous")
.WithMany()
.HasForeignKey("IdPrevious");
b.HasOne("AsbCloudDb.Model.Well", "Well")
.WithMany()
.HasForeignKey("IdWell")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AsbCloudDb.Model.WellSectionType", "WellSectionType")
.WithMany()
.HasForeignKey("IdWellSectionType")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
b.Navigation("Editor");
b.Navigation("Previous");
b.Navigation("Well");
b.Navigation("WellSectionType");
});
modelBuilder.Entity("AsbCloudDb.Model.ProcessMaps.ProcessMapWellDrilling", b =>
{
b.HasOne("AsbCloudDb.Model.User", "User")

View File

@ -21,6 +21,7 @@ namespace AsbCloudDb.Model
public virtual DbSet<TrajectoryPlan> TrajectoriesPlan => Set<TrajectoryPlan>();
public virtual DbSet<ProcessMapWellDrilling> ProcessMapWellDrillings => Set<ProcessMapWellDrilling>();
public virtual DbSet<ProcessMapWellReam> ProcessMapWellReams => Set<ProcessMapWellReam>();
public virtual DbSet<ProcessMapPlanDrilling> ProcessMapPlanDrilling => Set<ProcessMapPlanDrilling>();
public virtual DbSet<DrillingProgramPart> DrillingProgramParts => Set<DrillingProgramPart>();
public virtual DbSet<FileCategory> FileCategories => Set<FileCategory>();
public virtual DbSet<FileInfo> Files => Set<FileInfo>();
@ -440,6 +441,16 @@ namespace AsbCloudDb.Model
.HasIndex(w => new { w.IdWell, w.IdSectionType })
.IsUnique();
modelBuilder.Entity<ProcessMapPlanDrilling>()
.HasOne(p => p.Author)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ProcessMapPlanDrilling>()
.HasOne(p => p.Editor)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
DefaultData.DefaultContextData.Fill(modelBuilder);
}

View File

@ -0,0 +1,89 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model;
/// <summary>
/// Часть записи описывающая изменение
/// </summary>
public abstract class ChangeLogAbstract
{
/// <summary>
/// Актуальная
/// </summary>
public const int IdStateActual = 0;
/// <summary>
/// Замененная
/// </summary>
public const int IdStateReplaced = 1;
/// <summary>
/// Удаленная
/// </summary>
public const int IdStateDeleted = 2;
/// <summary>
/// Очищено при импорте
/// </summary>
public const int IdClearedOnImport = 3;
/// <summary>
/// Ид записи
/// </summary>
[Key]
[Column("id"), Comment("Идентификатор")]
public int Id { get; set; }
/// <summary>
/// Автор изменения
/// </summary>
[Column("id_author"), Comment("Автор")]
public int IdAuthor { get; set; }
/// <summary>
/// Редактор
/// </summary>
[Column("id_editor"), Comment("Редактор")]
public int? IdEditor { get; set; }
/// <summary>
/// Дата создания записи
/// </summary>
[Column("creation"), Comment("дата создания")]
public DateTimeOffset Creation { get; set; }
/// <summary>
/// Дата устаревания (например при удалении)
/// </summary>
[Column("obsolete"), Comment("дата устаревания")]
public DateTimeOffset? Obsolete { get; set; }
/// <summary>
/// ИД состояния записи:
/// <list type="table">
/// <item>
/// <term>0</term>
/// <description>актуальная запись</description>
/// </item>
/// <item>
/// <term>1</term>
/// <description>замененная запись</description>
/// </item>
/// <item>
/// <term>2</term>
/// <description>удаленная запись</description>
/// </item>
/// </list>
/// </summary>
[Column("id_state"), Comment("ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная")]
public int IdState { get; set; }
/// <summary>
/// Id заменяемой записи
/// </summary>
[Column("id_previous"), Comment("ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная")]
public int? IdPrevious { get; set; }
}

View File

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model.ProcessMapPlan;
public abstract class ProcessMapPlanBase : ChangeLogAbstract, IId, IWellRelated
{
[Column("id_well"), Comment("Id скважины")]
public int IdWell { get; set; }
[Column("id_wellsection_type"), Comment("Тип секции")]
public int IdWellSectionType { get; set; }
[Column("depth_start"), Comment("Глубина по стволу от, м")]
public double DepthStart { get; set; }
[Column("depth_end"), Comment("Глубина по стволу до, м")]
public double DepthEnd { get; set; }
[ForeignKey(nameof(IdWell))]
public virtual Well Well { get; set; } = null!;
[ForeignKey(nameof(IdAuthor))]
public virtual User Author { get; set; } = null!;
[ForeignKey(nameof(IdEditor))]
public virtual User? Editor { get; set; } = null!;
[ForeignKey(nameof(IdWellSectionType))]
public virtual WellSectionType WellSectionType { get; set; } = null!;
}

View File

@ -0,0 +1,58 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using AsbCloudDb.Model.ProcessMapPlan;
using Microsoft.EntityFrameworkCore;
namespace AsbCloudDb.Model.ProcessMaps;
[Table("t_process_map_plan_drilling"), Comment("РТК план бурение")]
public class ProcessMapPlanDrilling : ProcessMapPlanBase
{
[Column("id_mode"), Comment("Id режима (1- ротор, 2 слайд)")]
public int IdMode { get; set; }
[Column("axial_load_plan"), Comment("Осевая нагрузка, т, план")]
public double AxialLoadPlan { get; set; }
[Column("axial_load_limit_max"), Comment("Осевая нагрузка, т, допустимый максимум")]
public double AxialLoadLimitMax { get; set; }
[Column("delta_pressure_plan"), Comment("Перепад давления, атм, план")]
public double DeltaPressurePlan { get; set; }
[Column("delta_pressure_limit_max"), Comment("Перепад давления, атм, допустимый максимум")]
public double DeltaPressureLimitMax { get; set; }
[Column("top_drive_torque_plan"), Comment("Момент на ВСП, план")]
public double TopDriveTorquePlan { get; set; }
[Column("top_drive_torque_limit_max"), Comment("Момент на ВСП, допустимый максимум")]
public double TopDriveTorqueLimitMax { get; set; }
[Column("top_drive_speed_plan"), Comment("Обороты на ВСП, план")]
public double TopDriveSpeedPlan { get; set; }
[Column("top_drive_speed_limit_max"), Comment("Обороты на ВСП, допустимый максимум")]
public double TopDriveSpeedLimitMax { get; set; }
[Column("flow_plan"), Comment("Расход, л/с, план")]
public double FlowPlan { get; set; }
[Column("flow_limit_max"), Comment("Расход, л/с, допустимый максимум")]
public double FlowLimitMax { get; set; }
[Column("rop_plan"), Comment("Плановая механическая скорость, м/ч")]
public double RopPlan { get; set; }
[Column("usage_saub"), Comment("Плановый процент использования АКБ")]
public double UsageSaub { get; set; }
[Column("usage_spin"), Comment("Плановый процент использования spin master")]
public double UsageSpin { get; set; }
[Column("comment"), Comment("Комментарий"), StringLength(1024)]
public string Comment { get; set; } = string.Empty;
[ForeignKey(nameof(IdPrevious))]
public virtual ProcessMapPlanDrilling? Previous { get; set; }
}

View File

@ -44,10 +44,10 @@ using AsbCloudApp.Services.DailyReport;
using AsbCloudDb.Model.DailyReports.Blocks.TimeBalance;
using AsbCloudDb.Model.WellSections;
using AsbCloudInfrastructure.Services.ProcessMaps;
using AsbCloudApp.Data.ProcessMapPlan;
namespace AsbCloudInfrastructure
{
public static class DependencyInjection
{
public static void MapsterSetup()
@ -222,6 +222,8 @@ namespace AsbCloudInfrastructure
services.AddTransient<IHelpPageService, HelpPageService>();
services.AddTransient<IScheduleReportService, ScheduleReportService>();
services.AddTransient<IProcessMapPlanBaseRepository<ProcessMapPlanDrillingDto>, ProcessMapPlanBaseRepository<ProcessMapPlanDrillingDto, ProcessMapPlanDrilling>>();
services.AddTransient<TrajectoryService>();
services.AddTransient<IGtrRepository, GtrWitsRepository>();

View File

@ -0,0 +1,270 @@
using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudDb.Model.ProcessMapPlan;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository;
public class ProcessMapPlanBaseRepository<TDto, TEntity> : IProcessMapPlanBaseRepository<TDto>
where TDto : ProcessMapPlanBaseDto
where TEntity : ProcessMapPlanBase
{
private readonly IAsbCloudDbContext context;
private readonly IWellService wellService;
public ProcessMapPlanBaseRepository(IAsbCloudDbContext context, IWellService wellService)
{
this.context = context;
this.wellService = wellService;
}
public async Task<int> InsertRangeAsync(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
{
var result = 0;
if (dtos.Any())
{
var entities = dtos.Select(Convert);
var creation = DateTimeOffset.UtcNow;
var dbSet = context.Set<TEntity>();
foreach (var entity in entities) {
entity.Id = default;
entity.IdAuthor = idUser;
entity.Creation = creation;
entity.IdState = ChangeLogAbstract.IdStateActual;
entity.IdEditor = null;
entity.Editor = null;
entity.IdPrevious = null;
entity.Obsolete = null;
dbSet.Add(entity);
}
result += await context.SaveChangesAsync(token);
}
return result;
}
public async Task<int> ClearAndInsertRangeAsync(int idUser, int idWell, IEnumerable<TDto> dtos, CancellationToken token)
{
if (dtos.Any(d => d.IdWell != idWell))
throw new ArgumentInvalidException(nameof(dtos), $"Все записи должны относиться к скважине idWell = {idWell}");
using var transaction = context.Database.BeginTransaction();
var result = 0;
try
{
var dbSet = context.Set<TEntity>();
var entitiesToMarkDeleted = dbSet
.Where(e => e.IdWell == idWell)
.Where(e => e.Obsolete == null);
var obsolete = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToMarkDeleted)
{
entity.IdState = ChangeLogAbstract.IdClearedOnImport;
entity.Obsolete = obsolete;
entity.IdEditor = idUser;
}
result += await context.SaveChangesAsync(token);
result += await InsertRangeAsync(idUser, dtos, token);
await transaction.CommitAsync(token);
}
catch
{
await transaction.RollbackAsync(CancellationToken.None);
throw;
}
return result;
}
public async Task<int> DeleteRangeAsync(int idUser, IEnumerable<int> ids, CancellationToken token)
{
var dbSet = context.Set<TEntity>();
var entitiesToMarkDeleted = dbSet
.Where(e => ids.Contains(e.Id))
.Where(e => e.Obsolete == null);
var obsolete = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToMarkDeleted)
{
entity.IdState = ChangeLogAbstract.IdStateDeleted;
entity.Obsolete = obsolete;
entity.IdEditor = idUser;
}
var result = await context.SaveChangesAsync(token);
return result;
}
public async Task<IEnumerable<TDto>> GetAsync(ProcessMapPlanBaseRequest request, CancellationToken token)
{
var timezone = wellService.GetTimezone(request.IdWell);
var offset = TimeSpan.FromHours(timezone.Hours);
var query = context
.Set<TEntity>()
.Where(e => e.IdWell == request.IdWell);
if(request.IdWellSectionType.HasValue)
query = query.Where(e => e.IdWellSectionType == request.IdWellSectionType);
if (request.UpdateFrom.HasValue)
{
var from = request.UpdateFrom.Value.ToUniversalTime();
query = query.Where(e => e.Creation > from || e.Obsolete > from);
}
if (request.Moment.HasValue)
{
var moment = request.Moment.Value.ToUniversalTime();
query = query
.Where(e => e.Creation < moment)
.Where(e => e.Obsolete == null || e.Obsolete > moment);
}
var entities = await query.ToArrayAsync(token);
var dtos = entities.Select(e => Convert(e, offset));
return dtos;
}
public async Task<IEnumerable<TDto>> GetChangeLogAsync(int idWell, DateOnly? date, CancellationToken token)
{
var query = context
.Set<TEntity>()
.Where(e => e.IdWell == idWell);
var timezone = wellService.GetTimezone(idWell);
var offset = TimeSpan.FromHours(timezone.Hours);
if (date.HasValue)
{
var min = new DateTimeOffset(date.Value.Year, date.Value.Month, date.Value.Day, 0, 0, 0, offset).ToUniversalTime();
var max = min.AddDays(1);
var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max);
var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
query = createdQuery.Union(editedQuery);
}
var entities = await query.ToListAsync(token);
var dtos = entities.Select(e => Convert(e, offset));
return dtos;
}
public async Task<IEnumerable<DateOnly>> GetDatesChangeAsync(int idWell, CancellationToken token)
{
var wellEntitiesQuery = context
.Set<TEntity>()
.Where(e => e.IdWell == idWell);
var datesCreateQuery = wellEntitiesQuery
.Select(e => e.Creation)
.Distinct();
var datesCreate = await datesCreateQuery.ToArrayAsync(token);
var datesUpdateQuery = wellEntitiesQuery
.Where(e => e.Obsolete != null)
.Select(e => e.Obsolete!.Value)
.Distinct();
var datesUpdate = await datesUpdateQuery.ToArrayAsync(token);
var timezone = wellService.GetTimezone(idWell);
var offset = TimeSpan.FromHours(timezone.Hours);
var dates = Enumerable.Concat( datesCreate, datesUpdate);
dates = dates.Select(date => date.ToOffset(offset));
var datesOnly = dates
.Select(d => new DateOnly(d.Year, d.Month, d.Day))
.Distinct();
return datesOnly;
}
public async Task<int> UpdateRangeAsync(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
{
if (dtos.Any(d => d.Id == 0))
throw new ArgumentInvalidException(nameof(dtos), "Отредактированные значения должны иметь id больше 0");
if (!dtos.Any())
return 0;
using var transaction = context.Database.BeginTransaction();
var result = 0;
try
{
var ids = dtos.Select(d => d.Id);
var dbSet = context.Set<TEntity>();
var entitiesToDelete = dbSet
.Where(e => ids.Contains(e.Id));
var updateTime = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToDelete)
{
if(entity.Obsolete is not null)
throw new ArgumentInvalidException(nameof(dtos), "Недопустимо редактировать устаревшие записи");
entity.IdState = ChangeLogAbstract.IdStateReplaced;
entity.Obsolete = updateTime;
entity.IdEditor = idUser;
}
result += await context.SaveChangesAsync(token);
var entitiesNew = dtos.Select(Convert);
foreach (var entity in entitiesNew)
{
entity.IdPrevious = entity.Id;
entity.Id = default;
entity.Creation = updateTime;
entity.IdAuthor = idUser;
entity.Obsolete = null;
entity.IdEditor = null;
entity.IdState = ChangeLogAbstract.IdStateActual;
dbSet.Add(entity);
}
result += await context.SaveChangesAsync(token);
await transaction.CommitAsync(token);
}
catch
{
await transaction.RollbackAsync(CancellationToken.None);
throw;
}
return result;
}
protected virtual TEntity Convert(TDto dto)
{
var entity = dto.Adapt<TEntity>();
entity.Creation = entity.Creation.ToUniversalTime();
if(entity.Obsolete.HasValue)
entity.Obsolete = entity.Obsolete.Value.ToUniversalTime();
return entity;
}
protected virtual TDto Convert(TEntity entity, TimeSpan offset)
{
var dto = entity.Adapt<TDto>();
dto.Creation = entity.Creation.ToOffset(offset);
if (entity.Obsolete.HasValue)
dto.Obsolete = entity.Obsolete.Value.ToOffset(offset);
return dto;
}
}

View File

@ -0,0 +1,198 @@
using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.AspNetCore.Http;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Requests;
using System;
using AsbCloudApp.Services;
using System.Linq;
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
/// <summary>
/// РТК план
/// </summary>
[ApiController]
[Route("api/well/{idWell}/[controller]")]
[Authorize]
public abstract class ProcessMapPlanBaseController<TDto> : ControllerBase
where TDto : ProcessMapPlanBaseDto
{
private readonly IProcessMapPlanBaseRepository<TDto> repository;
private readonly IWellService wellService;
public ProcessMapPlanBaseController(IProcessMapPlanBaseRepository<TDto> repository, IWellService wellService)
{
this.repository = repository;
this.wellService = wellService;
}
/// <summary>
/// Добавление
/// </summary>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> InsertRangeAsync([FromRoute] int idWell, IEnumerable<TDto> dtos, CancellationToken token)
{
if (idWell == 0 || dtos.Any(d => d.IdWell != idWell))
return this.ValidationBadRequest(nameof(dtos), "all dtos should contain same idWell");
var idUser = await AssertUserHasAccessToWellAsync(idWell, token);
var result = await repository.InsertRangeAsync(idUser, dtos, token);
return Ok(result);
}
/// <summary>
/// Удалить все по скважине и добавить новые
/// </summary>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("replace")]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ClearAndInsertRangeAsync([FromRoute] int idWell, IEnumerable<TDto> dtos, CancellationToken token)
{
if (idWell == 0 || dtos.Any(d => d.IdWell != idWell))
return this.ValidationBadRequest(nameof(dtos), "all dtos should contain same idWell");
var idUser = await AssertUserHasAccessToWellAsync(idWell, token);
var result = await repository.ClearAndInsertRangeAsync(idUser, idWell, dtos, token);
return Ok(result);
}
/// <summary>
/// Удаление
/// </summary>
/// <param name="idWell"></param>
/// <param name="ids"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpDelete]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> DeleteRangeAsync([FromRoute]int idWell, IEnumerable<int> ids, CancellationToken token)
{
var idUser = await AssertUserHasAccessToWellAsync(idWell, token);
var result = await repository.DeleteRangeAsync(idUser, ids, token);
return Ok(result);
}
/// <summary>
/// Получение
/// </summary>
/// <param name="idWell"></param>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<IEnumerable<TDto>>> GetAsync([FromRoute] int idWell, [FromQuery]ProcessMapPlanBaseRequest request, CancellationToken token)
{
request.IdWell = idWell;
await AssertUserHasAccessToWellAsync(request.IdWell, token);
var result = await repository.GetAsync(request, token);
return Ok(result);
}
/// <summary>
/// Изменения за определенную дату
/// </summary>
/// <param name="idWell"></param>
/// <param name="date"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("changeLog")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<IEnumerable<TDto>>> GetChangeLogAsync([FromRoute] int idWell, [FromQuery] DateOnly? date, CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
var result = await repository.GetChangeLogAsync(idWell, date, token);
return Ok(result);
}
/// <summary>
/// Даты за которые есть изменения
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("dates")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<IEnumerable<DateOnly>>> GetDatesChangeAsync([FromRoute] int idWell, CancellationToken token)
{
await AssertUserHasAccessToWellAsync(idWell, token);
var result = await repository.GetDatesChangeAsync(idWell, token);
return Ok(result);
}
/// <summary>
/// Редактирование
/// </summary>
/// <param name="idWell"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPut]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateRangeAsync([FromRoute] int idWell, IEnumerable<TDto> dtos, CancellationToken token)
{
var first = dtos.FirstOrDefault();
if(first is null)
return NoContent();
if (idWell == 0 || dtos.Any(d => d.IdWell != idWell))
return this.ValidationBadRequest(nameof(dtos), "all dtos should contain same idWell");
var idUser = await AssertUserHasAccessToWellAsync(idWell, token);
var result = await repository.UpdateRangeAsync(idUser, dtos, token);
return Ok(result);
}
/// <summary>
/// returns user id, if he has access to well
/// </summary>
/// <param name="idWell"></param>
/// <param name="token"></param>
/// <returns></returns>
/// <exception cref="ForbidException"></exception>
private async Task<int> AssertUserHasAccessToWellAsync(int idWell, CancellationToken token)
{
var idUser = GetUserId();
var idCompany = User.GetCompanyId();
if (!idCompany.HasValue)
throw new ForbidException("Нет доступа к скважине");
if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token))
throw new ForbidException("Нет доступа к скважине");
return idUser;
}
/// <summary>
/// returns user id or throw
/// </summary>
/// <returns></returns>
/// <exception cref="ForbidException"></exception>
private int GetUserId()
{
var idUser = User.GetUserId() ?? throw new ForbidException("Неизвестный пользователь");
return idUser;
}
}

View File

@ -0,0 +1,13 @@
using AsbCloudApp.Data.ProcessMapPlan;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
namespace AsbCloudWebApi.Controllers.ProcessMapPlan;
public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController<ProcessMapPlanDrillingDto>
{
public ProcessMapPlanDrillingController(IProcessMapPlanBaseRepository<ProcessMapPlanDrillingDto> repository, IWellService wellService)
: base(repository, wellService)
{
}
}

View File

@ -0,0 +1,132 @@
@baseUrl = http://127.0.0.1:5000
@contentType = application/json
@contentTypeForFiles = application/octet-stream
@auth = Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2OTc0MzcwMzEsImV4cCI6MTcyODk5NDYzMSwiaXNzIjoiYSIsImF1ZCI6ImEifQ.vB7Qb3K9gG77iP8y25zB3RcZIQk9cHkq3I1SkcooYJs
@uid = 20210101_000000000
@id = 1
@idWell = 1
### ïîëó÷åíèå äàííûõ drill test ñ ïàíåëè è ñîõðàíåíèå èõ â ÅÖÏ
POST {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling
Content-Type: {{contentType}}
accept: */*
Authorization: {{auth}}
[
{
"id": 0,
"idAuthor": 0,
"idEditor": 0,
"creation": "2024-01-18T12:45:58.205Z",
"obsolete": "2024-01-18T12:45:58.205Z",
"idState": 0,
"idPrevious": 0,
"idWell": 1,
"idWellSectionType": 1,
"depthStart": 0,
"depthEnd": 10,
"idMode": 2,
"axialLoad": {
"plan": 0,
"limitMax": 0
},
"deltaPressure": {
"plan": 0,
"limitMax": 0
},
"topDriveTorque": {
"plan": 0,
"limitMax": 0
},
"topDriveSpeed": {
"plan": 0,
"limitMax": 0
},
"flow": {
"plan": 0,
"limitMax": 0
},
"ropPlan": 99999.9,
"usageSaub": 100,
"usageSpin": 100,
"comment": ""
}
]
### Ïîëó÷åíèå âñåõ
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling
accept: */*
Authorization: {{auth}}
### çàìåíà ñòàðûõ çàïèñåé íîâûìè
POST {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling/replace
Content-Type: {{contentType}}
accept: */*
Authorization: {{auth}}
[
{
"id": 0,
"idAuthor": 0,
"idEditor": 0,
"creation": "2024-01-19T05:31:54.762Z",
"obsolete": "2024-01-19T05:31:54.762Z",
"idState": 0,
"idPrevious": 0,
"idWell": 1,
"idWellSectionType": 1,
"depthStart": 0,
"depthEnd": 10,
"idMode": 2,
"axialLoad": {
"plan": 0,
"limitMax": 0
},
"deltaPressure": {
"plan": 0,
"limitMax": 0
},
"topDriveTorque": {
"plan": 0,
"limitMax": 0
},
"topDriveSpeed": {
"plan": 0,
"limitMax": 0
},
"flow": {
"plan": 0,
"limitMax": 0
},
"ropPlan": 99999.9,
"usageSaub": 100,
"usageSpin": 100,
"comment": "There is no spoon"
}
]
### Ïîëó÷åíèå òîëüêî àêòóàëüíûõ
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling?Moment=3000-01-01
accept: */*
Authorization: {{auth}}
### Ïîëó÷åíèå
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling/dates
accept: */*
Authorization: {{auth}}
### Ïîëó÷åíèå èçìåíåíèé çà äàòó
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling/changeLog?date=2024-01-19
accept: */*
Authorization: {{auth}}
### óäàëåíèå
DELETE {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling
Content-Type: {{contentType}}
accept: */*
Authorization: {{auth}}
[
1
]