forked from ddrilling/AsbCloudServer
Добавлен сервис для работы с плановыми секциями конструкции скважины
This commit is contained in:
parent
9c68560d93
commit
6db3771cb4
73
AsbCloudApp/Data/WellSections/WellSectionPlanDto.cs
Normal file
73
AsbCloudApp/Data/WellSections/WellSectionPlanDto.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data.WellSections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Секция скважины - план
|
||||||
|
/// </summary>
|
||||||
|
public class WellSectionPlanDto : ItemInfoDto,
|
||||||
|
IId,
|
||||||
|
IWellRelated,
|
||||||
|
IValidatableObject
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int IdWell { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тип секции
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "Поле обязательно для заполнение")]
|
||||||
|
[Range(1, int.MaxValue)]
|
||||||
|
public int IdSectionType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Начальная глубина бурения, м
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "Поле обязательно для заполнение")]
|
||||||
|
[Range(0, 10000, ErrorMessage = "Допустимое значение от 1 до 10000")]
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Конечная глубина бурения, м
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "Поле обязательно для заполнение")]
|
||||||
|
[Range(0, 10000, ErrorMessage = "Допустимое значение от 1 до 10000")]
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Внешний диаметр
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, 10000, ErrorMessage = "Допустимое значение от 1 до 10000")]
|
||||||
|
public double? OuterDiameter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Внутренний диаметр
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, 10000, ErrorMessage = "Допустимое значение от 1 до 10000")]
|
||||||
|
public double? InnerDiameter { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
|
{
|
||||||
|
if (!OuterDiameter.HasValue && !InnerDiameter.HasValue)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (!OuterDiameter.HasValue)
|
||||||
|
yield return new ValidationResult("Поле обязательно для заполнение", new[] { nameof(OuterDiameter) });
|
||||||
|
|
||||||
|
if (!InnerDiameter.HasValue)
|
||||||
|
yield return new ValidationResult("Поле обязательно для заполнение", new[] { nameof(InnerDiameter) });
|
||||||
|
|
||||||
|
if (OuterDiameter <= InnerDiameter)
|
||||||
|
yield return new ValidationResult("Внешний диаметр не должен быть больше или равен внутреннему",
|
||||||
|
new[] { nameof(OuterDiameter) });
|
||||||
|
|
||||||
|
if (InnerDiameter >= OuterDiameter)
|
||||||
|
yield return new ValidationResult("Внутренний диаметр не должен больше или равен внутреннему",
|
||||||
|
new[] { nameof(InnerDiameter) });
|
||||||
|
}
|
||||||
|
}
|
37
AsbCloudApp/Services/WellSections/IWellSectionPlanService.cs
Normal file
37
AsbCloudApp/Services/WellSections/IWellSectionPlanService.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Data.WellSections;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Services.WellSections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Секция скважины - план
|
||||||
|
/// </summary>
|
||||||
|
public interface IWellSectionPlanService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить секцию
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wellSectionPlan"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> InsertAsync(WellSectionPlanDto wellSectionPlan, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновить секцию
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wellSectionPlan"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> UpdateAsync(WellSectionPlanDto wellSectionPlan, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить типы секций
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idWell"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<WellSectionTypeDto>> GetWellSectionTypesAsync(int idWell, CancellationToken cancellationToken);
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Data.WellSections;
|
||||||
|
using AsbCloudApp.Exceptions;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
|
using AsbCloudApp.Services.WellSections;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services.WellSections;
|
||||||
|
|
||||||
|
public class WellSectionPlanService : IWellSectionPlanService
|
||||||
|
{
|
||||||
|
private readonly IRepositoryWellRelated<WellSectionPlanDto> wellSectionPlanRepository;
|
||||||
|
private readonly ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository;
|
||||||
|
|
||||||
|
public WellSectionPlanService(IRepositoryWellRelated<WellSectionPlanDto> wellSectionPlanRepository,
|
||||||
|
ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository)
|
||||||
|
{
|
||||||
|
this.wellSectionPlanRepository = wellSectionPlanRepository;
|
||||||
|
this.wellSectionTypeRepository = wellSectionTypeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> InsertAsync(WellSectionPlanDto wellSectionPlan, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await EnsureUniqueSectionTypeInWellAsync(wellSectionPlan, cancellationToken);
|
||||||
|
|
||||||
|
return await wellSectionPlanRepository.InsertAsync(wellSectionPlan, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> UpdateAsync(WellSectionPlanDto wellSectionPlan, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await EnsureUniqueSectionTypeInWellAsync(wellSectionPlan, cancellationToken);
|
||||||
|
|
||||||
|
wellSectionPlan.LastUpdateDate = DateTime.UtcNow;
|
||||||
|
|
||||||
|
return await wellSectionPlanRepository.UpdateAsync(wellSectionPlan, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<WellSectionTypeDto>> GetWellSectionTypesAsync(int idWell, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var wellSectionTypes = (await wellSectionTypeRepository.GetAllAsync(cancellationToken))
|
||||||
|
.OrderBy(w => w.Order);
|
||||||
|
|
||||||
|
var planWellSections = await wellSectionPlanRepository.GetByIdWellAsync(idWell, cancellationToken);
|
||||||
|
|
||||||
|
if (!planWellSections.Any())
|
||||||
|
return wellSectionTypes;
|
||||||
|
|
||||||
|
return wellSectionTypes.Where(w => planWellSections.Any(s => s.IdSectionType == w.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EnsureUniqueSectionTypeInWellAsync(WellSectionPlanDto section, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var existingWellSectionPlan = (await wellSectionPlanRepository.GetByIdWellAsync(section.IdWell, cancellationToken))
|
||||||
|
.SingleOrDefault(s => s.IdSectionType == section.IdSectionType);
|
||||||
|
|
||||||
|
if (existingWellSectionPlan is not null && existingWellSectionPlan.Id != section.Id)
|
||||||
|
{
|
||||||
|
var sectionType = await wellSectionTypeRepository.GetOrDefaultAsync(section.IdSectionType, cancellationToken);
|
||||||
|
|
||||||
|
throw new ArgumentInvalidException($"Секция '{sectionType?.Caption}' уже добавлена в конструкцию скважины",
|
||||||
|
nameof(section.IdSectionType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Data.WellSections;
|
||||||
|
using AsbCloudApp.Exceptions;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
|
using AsbCloudInfrastructure.Services.WellSections;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace AsbCloudWebApi.Tests.UnitTests.Services.WellSections;
|
||||||
|
|
||||||
|
public class WellSectionPlanServiceTests
|
||||||
|
{
|
||||||
|
private const int idWellSectionPlan = 1;
|
||||||
|
private const int idWell = 3;
|
||||||
|
private const int idWellSectionType = 54;
|
||||||
|
|
||||||
|
private readonly IEnumerable<WellSectionPlanDto> fakePlanWellSections = new WellSectionPlanDto[]
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = idWellSectionPlan,
|
||||||
|
IdWell = idWell,
|
||||||
|
IdSectionType = idWellSectionType
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly IEnumerable<WellSectionTypeDto> fakeWellSectionTypes = new WellSectionTypeDto[]
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = idWellSectionType
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly IRepositoryWellRelated<WellSectionPlanDto> wellSectionPlanRepositoryMock =
|
||||||
|
Substitute.For<IRepositoryWellRelated<WellSectionPlanDto>>();
|
||||||
|
|
||||||
|
private readonly ICrudRepository<WellSectionTypeDto> wellSectionTypeRepositoryMock =
|
||||||
|
Substitute.For<ICrudRepository<WellSectionTypeDto>>();
|
||||||
|
|
||||||
|
private readonly WellSectionPlanService wellSectionPlanService;
|
||||||
|
|
||||||
|
public WellSectionPlanServiceTests()
|
||||||
|
{
|
||||||
|
wellSectionPlanService = new WellSectionPlanService(wellSectionPlanRepositoryMock, wellSectionTypeRepositoryMock);
|
||||||
|
|
||||||
|
wellSectionTypeRepositoryMock.GetAllAsync(Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(fakeWellSectionTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InsertAsync_InsertNewWellSectionTypeWithUniqueSectionTypeInWell_InvokesWellSectionPlanRepositoryInsertAsync()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var insertedWellSection = new WellSectionPlanDto();
|
||||||
|
|
||||||
|
//act
|
||||||
|
await wellSectionPlanService.InsertAsync(insertedWellSection, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
await wellSectionPlanRepositoryMock.Received().InsertAsync(Arg.Any<WellSectionPlanDto>(), Arg.Any<CancellationToken>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InsertAsync_InsertNewWellSectionTypeWithNotUniqueSectionTypeInWell_ReturnsDuplicateException()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var insertedWellSection = new WellSectionPlanDto
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
IdSectionType = idWellSectionType
|
||||||
|
};
|
||||||
|
|
||||||
|
wellSectionPlanRepositoryMock.GetByIdWellAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(fakePlanWellSections);
|
||||||
|
|
||||||
|
//act
|
||||||
|
Task Result() => wellSectionPlanService.InsertAsync(insertedWellSection, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
await Assert.ThrowsAsync<ArgumentInvalidException>(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateAsync_UpdateExistingWellSectionTypeWithUniqueSectionTypeInWell_InvokesWellSectionPlanRepositoryUpdateAsync()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var updatedWellSection = new WellSectionPlanDto
|
||||||
|
{
|
||||||
|
Id = idWellSectionPlan,
|
||||||
|
IdSectionType = idWellSectionType
|
||||||
|
};
|
||||||
|
|
||||||
|
//act
|
||||||
|
await wellSectionPlanService.UpdateAsync(updatedWellSection, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
await wellSectionPlanRepositoryMock.Received().UpdateAsync(Arg.Any<WellSectionPlanDto>(), Arg.Any<CancellationToken>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateAsync_ReturnsLastUpdateDateNotNull()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var updatedWellSection = new WellSectionPlanDto
|
||||||
|
{
|
||||||
|
Id = idWellSectionPlan,
|
||||||
|
IdSectionType = idWellSectionType
|
||||||
|
};
|
||||||
|
|
||||||
|
//act
|
||||||
|
await wellSectionPlanService.UpdateAsync(updatedWellSection, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.NotNull(updatedWellSection.LastUpdateDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateAsync_UpdateExistingWellSectionTypeWithNotUniqueSectionTypeInWell_ReturnsDuplicateException()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var updatedWellSection = new WellSectionPlanDto
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
IdSectionType = idWellSectionType
|
||||||
|
};
|
||||||
|
|
||||||
|
wellSectionPlanRepositoryMock.GetByIdWellAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(fakePlanWellSections);
|
||||||
|
|
||||||
|
//act
|
||||||
|
Task Result() => wellSectionPlanService.UpdateAsync(updatedWellSection, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
await Assert.ThrowsAsync<ArgumentInvalidException>(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetWellSectionTypesAsync_EmptyCollectionWellSectionPlan_ReturnsCollectionWellSectionType()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
wellSectionPlanRepositoryMock.GetByIdWellAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(Enumerable.Empty<WellSectionPlanDto>());
|
||||||
|
|
||||||
|
//act
|
||||||
|
var result = await wellSectionPlanService.GetWellSectionTypesAsync(idWell, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Single(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetWellSectionTypesAsync_NotEmptyCollectionWellSectionPlan_ReturnsCollectionWellSectionType()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
wellSectionPlanRepositoryMock.GetByIdWellAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(fakePlanWellSections);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var result = await wellSectionPlanService.GetWellSectionTypesAsync(idWell, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Single(result);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user