Добавлен сервис для работы с плановыми секциями конструкции скважины

This commit is contained in:
Степанов Дмитрий 2023-12-04 17:05:57 +05:00
parent 9c68560d93
commit 6db3771cb4
4 changed files with 347 additions and 0 deletions

View 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) });
}
}

View 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);
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}