From 6d17500b2746b33e7ff1545193f76dbd53eb8bf0 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 26 Jan 2024 17:23:21 +0500 Subject: [PATCH 1/6] Add ChangeLogServiceAbstract --- .../Repositories/IChangeLogRepository.cs | 64 +++++++++++++---- .../IProcessMapPlanBaseRepository.cs | 52 -------------- .../Repositories/IProcessMapPlanRepository.cs | 2 + .../Services/ChangeLogServiceAbstract.cs | 69 +++++++++++++++++++ .../ProcessMapPlanBaseController.cs | 4 +- 5 files changed, 124 insertions(+), 67 deletions(-) delete mode 100644 AsbCloudApp/Repositories/IProcessMapPlanBaseRepository.cs create mode 100644 AsbCloudApp/Services/ChangeLogServiceAbstract.cs diff --git a/AsbCloudApp/Repositories/IChangeLogRepository.cs b/AsbCloudApp/Repositories/IChangeLogRepository.cs index 9a5ab8c9..97db1dd9 100644 --- a/AsbCloudApp/Repositories/IChangeLogRepository.cs +++ b/AsbCloudApp/Repositories/IChangeLogRepository.cs @@ -1,15 +1,18 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; +using AsbCloudApp.Requests; namespace AsbCloudApp.Repositories; /// /// Репозиторий для записей с историей /// -public interface IChangeLogRepository - where T : ChangeLogAbstract +public interface IChangeLogRepository + where TDto : ChangeLogAbstract + where TRequest : ChangeLogBaseRequest { /// /// Добавление записей @@ -18,7 +21,7 @@ public interface IChangeLogRepository /// /// /// - Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token); + Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token); /// /// Редактирование записей @@ -27,15 +30,50 @@ public interface IChangeLogRepository /// /// /// - Task UpdateRange(int idUser, IEnumerable dtos, CancellationToken token); + Task UpdateRange(int idUser, IEnumerable dtos, CancellationToken token); + + /// + /// Добавление записей с удалением старых (для импорта) + /// + /// + /// + /// + /// + Task Clear(int idUser, TRequest request, CancellationToken token); + + /// + /// Удаление записей + /// + /// + /// + /// + /// + Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token); + + /// + /// Получение дат изменений записей + /// + /// + /// + /// + Task> GetDatesChange(TRequest request, CancellationToken token); + + /// + /// Получение журнала изменений + /// + /// + /// + /// + /// + Task> GetChangeLog(TRequest request, DateOnly? date, CancellationToken token); + + /// + /// Получение записей по параметрам + /// + /// + /// + /// + Task> Get(TRequest request, CancellationToken token); - /// - /// Удаление записей - /// - /// - /// - /// - /// - Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token); } diff --git a/AsbCloudApp/Repositories/IProcessMapPlanBaseRepository.cs b/AsbCloudApp/Repositories/IProcessMapPlanBaseRepository.cs deleted file mode 100644 index 7524002a..00000000 --- a/AsbCloudApp/Repositories/IProcessMapPlanBaseRepository.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using AsbCloudApp.Data.ProcessMapPlan; -using AsbCloudApp.Requests; - -namespace AsbCloudApp.Repositories; - -/// -/// Общий интерфейс для РТК план с учетом истории изменений -/// -/// -public interface IProcessMapPlanBaseRepository: IChangeLogRepository - where T: ProcessMapPlanBaseDto -{ - /// - /// Добавление записей с удалением старых (для импорта) - /// - /// - /// - /// - /// - /// - Task ClearAndInsertRange(int idUser, int idWell, IEnumerable dtos, CancellationToken token); - - /// - /// Получение дат изменений записей - /// - /// - /// - /// - Task> GetDatesChange(int idWell, CancellationToken token); - - /// - /// Получение журнала изменений - /// - /// - /// - /// - /// - Task> GetChangeLog(int idWell, DateOnly? date, CancellationToken token); - - /// - /// Получение записей по параметрам - /// - /// - /// - /// - /// - Task> Get(int idWell, ProcessMapPlanBaseRequest request, CancellationToken token); -} \ No newline at end of file diff --git a/AsbCloudApp/Repositories/IProcessMapPlanRepository.cs b/AsbCloudApp/Repositories/IProcessMapPlanRepository.cs index 1828b3ed..7e41cb58 100644 --- a/AsbCloudApp/Repositories/IProcessMapPlanRepository.cs +++ b/AsbCloudApp/Repositories/IProcessMapPlanRepository.cs @@ -4,12 +4,14 @@ using System.Threading; using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Requests; using AsbCloudApp.Services; +using System; namespace AsbCloudApp.Repositories; /// /// РТК план /// +[Obsolete] public interface IProcessMapPlanRepository : IRepositoryWellRelated where TDto : ProcessMapPlanBaseDto { diff --git a/AsbCloudApp/Services/ChangeLogServiceAbstract.cs b/AsbCloudApp/Services/ChangeLogServiceAbstract.cs new file mode 100644 index 00000000..31202939 --- /dev/null +++ b/AsbCloudApp/Services/ChangeLogServiceAbstract.cs @@ -0,0 +1,69 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; +using System.Linq; +using System.ComponentModel.DataAnnotations; +using AsbCloudApp.Exceptions; + +namespace AsbCloudApp.Services +{ + /// + /// + /// + /// + /// + public abstract class ChangeLogServiceAbstract + where TDto : ChangeLogAbstract + where TRequest : ChangeLogBaseRequest + { + /// + /// Репозиторий + /// + public IChangeLogRepository repository { get; } + + /// + /// ctor + /// + /// + public ChangeLogServiceAbstract(IChangeLogRepository repository) + { + this.repository = repository; + } + + /// + /// Добавляет Dto у которых id == 0, изменяет dto у которых id != 0 + /// + /// + /// + /// + /// + public virtual async Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token) + { + var validationResults = Validate(dtos); + if (validationResults.Any()) + { + var errors = validationResults.SelectMany(r => r.MemberNames.Select(member => $"{member}: {r.ErrorMessage}")); + throw new ArgumentInvalidException(nameof(dtos), $"not valid: {string.Join("\n", errors)}"); + } + + var itemsToInsert = dtos.Where(e => e.Id == 0); + var itemsToUpdate = dtos.Where(e => e.Id != 0); + + var result = await repository.InsertRange(idUser, itemsToInsert, token); + result += await repository.UpdateRange(idUser, itemsToInsert, token); + + return result; + } + + /// + /// Валидация входных данных + /// + /// + /// + protected virtual IEnumerable Validate(IEnumerable dtos) + => Enumerable.Empty(); + } +} diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs index 1237e628..8d954a60 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs @@ -23,10 +23,10 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan; public abstract class ProcessMapPlanBaseController : ControllerBase where TDto : ProcessMapPlanBaseDto { - private readonly IProcessMapPlanBaseRepository repository; + private readonly ProcessMapPlanBaseService repository; private readonly IWellService wellService; - public ProcessMapPlanBaseController(IProcessMapPlanBaseRepository repository, IWellService wellService) + public ProcessMapPlanBaseController(ProcessMapPlanBaseService repository, IWellService wellService) { this.repository = repository; this.wellService = wellService; From acb4e25f128c97d7d6225b6571d9b177aead6944 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Mon, 29 Jan 2024 12:25:58 +0500 Subject: [PATCH 2/6] =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20ProcessMapPlanBaseRepository.=20?= =?UTF-8?q?=D0=9E=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D0=B0=D1=8F=20=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=20ChangeLogRepositoryAbstract.=20=D0=94?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B4=D1=8B=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B?= =?UTF-8?q?=20=D1=81=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D0=BD=D1=8B=D0=BC=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=D0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/IChangeLogRepository.cs | 21 +- .../Requests/ProcessMapPlanBaseRequest.cs | 33 +++ .../Services/ChangeLogServiceAbstract.cs | 69 ----- AsbCloudDb/Model/ChangeLogAbstract.cs | 2 +- AsbCloudInfrastructure/DependencyInjection.cs | 9 +- .../Repository/ChangeLogRepositoryAbstract.cs | 279 ++++++++++++++++++ .../ProcessMapPlanBaseRepository.cs | 244 +-------------- .../Clients/IProcessMapPlanDrillingClient.cs | 5 +- .../ProcessMapPlanDrillingControllerTest.cs | 100 +++++-- .../ProcessMapPlanBaseController.cs | 49 ++- .../ProcessMapPlanDrillingController.cs | 5 +- 11 files changed, 471 insertions(+), 345 deletions(-) delete mode 100644 AsbCloudApp/Services/ChangeLogServiceAbstract.cs create mode 100644 AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs diff --git a/AsbCloudApp/Repositories/IChangeLogRepository.cs b/AsbCloudApp/Repositories/IChangeLogRepository.cs index 97db1dd9..44a6faed 100644 --- a/AsbCloudApp/Repositories/IChangeLogRepository.cs +++ b/AsbCloudApp/Repositories/IChangeLogRepository.cs @@ -32,6 +32,15 @@ public interface IChangeLogRepository /// Task UpdateRange(int idUser, IEnumerable dtos, CancellationToken token); + /// + /// Добавляет Dto у которых id == 0, изменяет dto у которых id != 0 + /// + /// + /// + /// + /// + Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token); + /// /// Добавление записей с удалением старых (для импорта) /// @@ -41,6 +50,16 @@ public interface IChangeLogRepository /// Task Clear(int idUser, TRequest request, CancellationToken token); + /// + /// Очистить и добавить новые + /// + /// + /// + /// + /// + /// + Task ClearAndInsertRange(int idUser, TRequest request, IEnumerable dtos, CancellationToken token); + /// /// Удаление записей /// @@ -62,7 +81,7 @@ public interface IChangeLogRepository /// Получение журнала изменений /// /// - /// + /// Фильтр по дате. Если null - вернет все /// /// Task> GetChangeLog(TRequest request, DateOnly? date, CancellationToken token); diff --git a/AsbCloudApp/Requests/ProcessMapPlanBaseRequest.cs b/AsbCloudApp/Requests/ProcessMapPlanBaseRequest.cs index 74fc0ec4..006c26e5 100644 --- a/AsbCloudApp/Requests/ProcessMapPlanBaseRequest.cs +++ b/AsbCloudApp/Requests/ProcessMapPlanBaseRequest.cs @@ -18,4 +18,37 @@ public class ProcessMapPlanBaseRequest: ChangeLogBaseRequest /// Вернуть данные, которые поменялись с указанной даты /// public DateTimeOffset? UpdateFrom { get; set; } +} + +/// +/// Запрос для получения РТК план по скважине +/// +public class ProcessMapPlanBaseRequestWithWell: ProcessMapPlanBaseRequest +{ + /// + /// Запрос для получения РТК план по скважине + /// + /// + public ProcessMapPlanBaseRequestWithWell(int idWell) + { + IdWell = idWell; + } + + /// + /// Запрос для получения РТК план по скважине + /// + /// + /// + public ProcessMapPlanBaseRequestWithWell(ProcessMapPlanBaseRequest request, int idWell) + { + IdWell=idWell; + IdWellSectionType=request.IdWellSectionType; + UpdateFrom = request.UpdateFrom; + Moment = request.Moment; + } + + /// + /// Id скважины + /// + public int IdWell { get; set; } } \ No newline at end of file diff --git a/AsbCloudApp/Services/ChangeLogServiceAbstract.cs b/AsbCloudApp/Services/ChangeLogServiceAbstract.cs deleted file mode 100644 index 31202939..00000000 --- a/AsbCloudApp/Services/ChangeLogServiceAbstract.cs +++ /dev/null @@ -1,69 +0,0 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Repositories; -using AsbCloudApp.Requests; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Threading; -using System.Linq; -using System.ComponentModel.DataAnnotations; -using AsbCloudApp.Exceptions; - -namespace AsbCloudApp.Services -{ - /// - /// - /// - /// - /// - public abstract class ChangeLogServiceAbstract - where TDto : ChangeLogAbstract - where TRequest : ChangeLogBaseRequest - { - /// - /// Репозиторий - /// - public IChangeLogRepository repository { get; } - - /// - /// ctor - /// - /// - public ChangeLogServiceAbstract(IChangeLogRepository repository) - { - this.repository = repository; - } - - /// - /// Добавляет Dto у которых id == 0, изменяет dto у которых id != 0 - /// - /// - /// - /// - /// - public virtual async Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token) - { - var validationResults = Validate(dtos); - if (validationResults.Any()) - { - var errors = validationResults.SelectMany(r => r.MemberNames.Select(member => $"{member}: {r.ErrorMessage}")); - throw new ArgumentInvalidException(nameof(dtos), $"not valid: {string.Join("\n", errors)}"); - } - - var itemsToInsert = dtos.Where(e => e.Id == 0); - var itemsToUpdate = dtos.Where(e => e.Id != 0); - - var result = await repository.InsertRange(idUser, itemsToInsert, token); - result += await repository.UpdateRange(idUser, itemsToInsert, token); - - return result; - } - - /// - /// Валидация входных данных - /// - /// - /// - protected virtual IEnumerable Validate(IEnumerable dtos) - => Enumerable.Empty(); - } -} diff --git a/AsbCloudDb/Model/ChangeLogAbstract.cs b/AsbCloudDb/Model/ChangeLogAbstract.cs index f4065ea6..d9bc74e0 100644 --- a/AsbCloudDb/Model/ChangeLogAbstract.cs +++ b/AsbCloudDb/Model/ChangeLogAbstract.cs @@ -28,7 +28,7 @@ public abstract class ChangeLogAbstract /// /// Очищено при импорте /// - public const int IdClearedOnImport = 3; + public const int IdCleared = 3; /// /// Ид записи diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 0e88a972..913fdfe4 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -45,6 +45,7 @@ using AsbCloudDb.Model.DailyReports.Blocks.TimeBalance; using AsbCloudDb.Model.WellSections; using AsbCloudInfrastructure.Services.ProcessMaps; using AsbCloudApp.Data.ProcessMapPlan; +using AsbCloudApp.Requests; namespace AsbCloudInfrastructure { @@ -195,10 +196,10 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddScoped(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddScoped(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -222,7 +223,9 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); - services.AddTransient, ProcessMapPlanBaseRepository>(); + services.AddTransient< + IChangeLogRepository, + ProcessMapPlanBaseRepository>(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs new file mode 100644 index 00000000..84506a21 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs @@ -0,0 +1,279 @@ +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudDb.Model; +using Mapster; +using Microsoft.EntityFrameworkCore; +using Npgsql; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository; + +public abstract class ChangeLogRepositoryAbstract : IChangeLogRepository + where TDto : AsbCloudApp.Data.ChangeLogAbstract + where TEntity : ChangeLogAbstract + where TRequest : ChangeLogBaseRequest +{ + protected readonly IAsbCloudDbContext context; + + public ChangeLogRepositoryAbstract(IAsbCloudDbContext context) + { + this.context = context; + } + + public async Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token) + { + var result = 0; + if (dtos.Any()) + { + var entities = dtos.Select(Convert); + var creation = DateTimeOffset.UtcNow; + var dbSet = context.Set(); + foreach (var entity in entities) + { + entity.Id = default; + entity.IdAuthor = idUser; + entity.Creation = creation; + entity.IdState = ChangeLogAbstract.IdStateActual; + entity.IdEditor = null; + entity.IdPrevious = null; + entity.Obsolete = null; + dbSet.Add(entity); + } + + result += await SaveChangesWithExceptionHandling(token); + } + return result; + } + + public async Task UpdateRange(int idUser, IEnumerable dtos, CancellationToken token) + { + if (dtos.Any(d => d.Id == 0)) + throw new ArgumentInvalidException(nameof(dtos), "Отредактированные значения должны иметь id не 0"); + + if (!dtos.Any()) + return 0; + + var updateTime = DateTimeOffset.UtcNow; + + var ids = dtos.Select(d => d.Id); + + using var transaction = context.Database.BeginTransaction(); + var result = 0; + var dbSet = context + .Set(); + + var entitiesToDelete = await dbSet + .Where(e => e.Obsolete == null) + .Where(e => ids.Contains(e.Id)) + .ToArrayAsync(token); + + var entitiesNotFound = dtos.Where(d => !entitiesToDelete.Any(e => e.Id == d.Id)); + if (entitiesNotFound.Any()) + { + var notFoundIds = entitiesNotFound.Select(e => e.Id); + var stringnotFoundIds = string.Join(", ", notFoundIds); + throw new ArgumentInvalidException(nameof(dtos), $"записи с id:[{stringnotFoundIds}] не найдены, или не актуальны."); + } + + foreach (var entity in entitiesToDelete) + { + 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 SaveChangesWithExceptionHandling(token); + await transaction.CommitAsync(token); + return result; + } + + public async Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token) + { + var itemsToInsert = dtos.Where(e => e.Id == 0); + var itemsToUpdate = dtos.Where(e => e.Id != 0); + + var result = 0; + if (itemsToInsert.Any()) + result += await InsertRange(idUser, itemsToInsert, token); + + if (itemsToUpdate.Any()) + result += await UpdateRange(idUser, itemsToUpdate, token); + + return result; + } + + public async Task Clear(int idUser, TRequest request, CancellationToken token) + { + var updateTime = DateTimeOffset.UtcNow; + var query = BuildQuery(request); + query = query.Where(e => e.Obsolete == null); + + var entitiesToDelete = await query.ToArrayAsync(token); + + foreach (var entity in entitiesToDelete) + { + entity.IdState = ChangeLogAbstract.IdCleared; + entity.Obsolete = updateTime; + entity.IdEditor = idUser; + } + + var result = await SaveChangesWithExceptionHandling(token); + return result; + } + + public async Task ClearAndInsertRange(int idUser, TRequest request, IEnumerable dtos, CancellationToken token) + { + var result = 0; + var transaction = await context.Database.BeginTransactionAsync(token); + result += await Clear(idUser, request, token); + result += await InsertRange(idUser, dtos, token); + await transaction.CommitAsync(token); + return result; + } + + public async Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token) + { + var updateTime = DateTimeOffset.UtcNow; + var query = context.Set() + .Where(e => ids.Contains(e.Id)) + .Where(e => e.Obsolete == null); + + var entitiesToDelete = await query.ToArrayAsync(token); + + foreach (var entity in entitiesToDelete) + { + entity.IdState = ChangeLogAbstract.IdStateDeleted; + entity.Obsolete = updateTime; + entity.IdEditor = idUser; + } + + var result = await SaveChangesWithExceptionHandling(token); + return result; + } + + public async Task> GetDatesChange(TRequest request, CancellationToken token) + { + var query = BuildQuery(request); + + var datesCreateQuery = query + .Select(e => e.Creation) + .Distinct(); + + var datesCreate = await datesCreateQuery.ToArrayAsync(token); + + var datesUpdateQuery = query + .Where(e => e.Obsolete != null) + .Select(e => e.Obsolete!.Value) + .Distinct(); + + var datesUpdate = await datesUpdateQuery.ToArrayAsync(token); + + TimeSpan offset = GetTimezoneOffset(request); + + 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> GetChangeLog(TRequest request, DateOnly? date, CancellationToken token) + { + var query = BuildQuery(request); + TimeSpan offset = GetTimezoneOffset(request); + + 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> Get(TRequest request, CancellationToken token) + { + var query = BuildQuery(request); + var entities = await query.ToArrayAsync(token); + + TimeSpan offset = GetTimezoneOffset(request); + var dtos = entities.Select(e => Convert(e, offset)); + return dtos; + } + + protected abstract TimeSpan GetTimezoneOffset(TRequest request); + + protected abstract IQueryable BuildQuery(TRequest request); + + protected virtual TEntity Convert(TDto dto) + { + var entity = dto.Adapt(); + 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(); + dto.Creation = entity.Creation.ToOffset(offset); + + if (entity.Obsolete.HasValue) + dto.Obsolete = entity.Obsolete.Value.ToOffset(offset); + + return dto; + } + + private async Task SaveChangesWithExceptionHandling(CancellationToken token) + { + try + { + var result = await context.SaveChangesAsync(token); + return result; + } + catch (DbUpdateException ex) + { + if (ex.InnerException is PostgresException pgException) + TryConvertPostgresExceptionToValidateException(pgException); + throw; + } + } + + private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException) + { + if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation) + throw new ArgumentInvalidException("dtos", pgException.Message + "\r\n" + pgException.Detail); + } +} diff --git a/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs b/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs index 11b4d0fd..0133b527 100644 --- a/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs +++ b/AsbCloudInfrastructure/Repository/ProcessMapPlanBaseRepository.cs @@ -1,115 +1,36 @@ using AsbCloudApp.Data.ProcessMapPlan; -using AsbCloudApp.Exceptions; -using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudDb.Model.ProcessMapPlan; -using AsbCloudDb.Model.WellSections; -using Mapster; using Microsoft.EntityFrameworkCore; -using Npgsql; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository; -public class ProcessMapPlanBaseRepository : IProcessMapPlanBaseRepository +public class ProcessMapPlanBaseRepository : ChangeLogRepositoryAbstract where TDto : ProcessMapPlanBaseDto where TEntity : ProcessMapPlanBase { - private readonly IAsbCloudDbContext context; private readonly IWellService wellService; - public ProcessMapPlanBaseRepository(IAsbCloudDbContext context, IWellService wellService) + public ProcessMapPlanBaseRepository(IAsbCloudDbContext context, IWellService wellService) + : base(context) { - this.context = context; this.wellService = wellService; } - public async Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token) + protected override IQueryable BuildQuery(ProcessMapPlanBaseRequestWithWell request) { - var result = 0; - if (dtos.Any()) - { - var entities = dtos.Select(Convert); - var creation = DateTimeOffset.UtcNow; - var dbSet = context.Set(); - 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 SaveChangesWithExceptionHandling(token); - } - return result; - } - - public async Task ClearAndInsertRange(int idUser, int idWell, IEnumerable 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; - - var dbSet = context.Set(); - 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 SaveChangesWithExceptionHandling(token); - result += await InsertRange(idUser, dtos, token); - await transaction.CommitAsync(token); - - return result; - } - - public async Task DeleteRange(int idUser, IEnumerable ids, CancellationToken token) - { - var dbSet = context.Set(); - 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 SaveChangesWithExceptionHandling(token); - return result; - } - - public async Task> Get(int idWell, ProcessMapPlanBaseRequest request, CancellationToken token) - { - var timezone = wellService.GetTimezone(idWell); - var offset = TimeSpan.FromHours(timezone.Hours); - var query = context .Set() .Include(e => e.Author) .Include(e => e.Editor) - .Where(e => e.IdWell == idWell); + .Include(e => e.Well) + .Where(e => e.IdWell == request.IdWell); - if(request.IdWellSectionType.HasValue) + if (request.IdWellSectionType.HasValue) query = query.Where(e => e.IdWellSectionType == request.IdWellSectionType); if (request.UpdateFrom.HasValue) @@ -126,156 +47,13 @@ public class ProcessMapPlanBaseRepository : IProcessMapPlanBaseRe .Where(e => e.Obsolete == null || e.Obsolete >= moment); } - var entities = await query.ToArrayAsync(token); - var dtos = entities.Select(e => Convert(e, offset)); - return dtos; + return query; } - public async Task> GetChangeLog(int idWell, DateOnly? date, CancellationToken token) + protected override TimeSpan GetTimezoneOffset(ProcessMapPlanBaseRequestWithWell request) { - var query = context - .Set() - .Include(e => e.Author) - .Include(e => e.Editor) - .Where(e => e.IdWell == idWell); - - var timezone = wellService.GetTimezone(idWell); + var timezone = wellService.GetTimezone(request.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> GetDatesChange(int idWell, CancellationToken token) - { - var wellEntitiesQuery = context - .Set() - .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 UpdateRange(int idUser, IEnumerable 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; - - var ids = dtos.Select(d => d.Id); - var dbSet = context.Set(); - - 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 SaveChangesWithExceptionHandling(token); - await transaction.CommitAsync(token); - return result; - } - - protected virtual TEntity Convert(TDto dto) - { - var entity = dto.Adapt(); - 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(); - dto.Creation = entity.Creation.ToOffset(offset); - - if (entity.Obsolete.HasValue) - dto.Obsolete = entity.Obsolete.Value.ToOffset(offset); - - return dto; - } - - private async Task SaveChangesWithExceptionHandling(CancellationToken token) - { - try - { - var result = await context.SaveChangesAsync(token); - return result; - } - catch (DbUpdateException ex) - { - if (ex.InnerException is PostgresException pgException) - TryConvertPostgresExceptionToValidateException(pgException); - throw; - } - } - - private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException) - { - if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation) - throw new ArgumentInvalidException("dtos", pgException.Message + "\r\n" + pgException.Detail); + return offset; } } diff --git a/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs index 45dfd59a..7c8c65e3 100644 --- a/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs +++ b/AsbCloudWebApi.IntegrationTests/Clients/IProcessMapPlanDrillingClient.cs @@ -18,6 +18,9 @@ public interface IProcessMapPlanDrillingClient [Delete(BaseRoute)] Task> DeleteRange(int idWell, [Body] IEnumerable ids); + [Delete($"{BaseRoute}/clear")] + Task> Clear(int idWell); + [Get(BaseRoute)] Task>> Get(int idWell, ProcessMapPlanBaseRequest request); @@ -28,5 +31,5 @@ public interface IProcessMapPlanDrillingClient Task>> GetDatesChange(int idWell); [Put(BaseRoute)] - Task> UpdateRangeAsync(int idWell, IEnumerable dtos); + Task> UpdateOrInsertRange(int idWell, IEnumerable dtos); } diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs index 7bdf3135..c0ba7093 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlanDrillingControllerTest.cs @@ -179,7 +179,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest Assert.Equal(2, count); var oldEntity = dbset.First(p => p.Id == entry.Entity.Id); - Assert.Equal(ProcessMapPlanBase.IdClearedOnImport, oldEntity.IdState); + Assert.Equal(ProcessMapPlanBase.IdCleared, oldEntity.IdState); Assert.Equal(1, oldEntity.IdEditor); Assert.NotNull(oldEntity.Obsolete); Assert.InRange(oldEntity.Obsolete.Value, startTime, doneTime); @@ -194,44 +194,61 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest } [Fact] - public async Task UpdateRange_returns_success() + public async Task UpdateOrInsertRange_returns_success() { // arrange var startTime = DateTimeOffset.UtcNow; - + var dbset = dbContext.Set(); - + var entry = dbset.Add(entity); dbContext.SaveChanges(); entry.State = EntityState.Detached; - var dtoCopy = dto.Adapt(); - dtoCopy.Id = entry.Entity.Id; - dtoCopy.Comment = "nebuchadnezzar"; - dtoCopy.DeltaPressureLimitMax ++; - dtoCopy.DeltaPressurePlan ++; - dtoCopy.FlowPlan ++; - dtoCopy.FlowLimitMax ++; - dtoCopy.RopPlan ++; - dtoCopy.AxialLoadPlan ++; - dtoCopy.AxialLoadLimitMax ++; - dtoCopy.DepthStart ++; - dtoCopy.DepthEnd ++; - dtoCopy.TopDriveSpeedPlan ++; - dtoCopy.TopDriveSpeedLimitMax ++; - dtoCopy.TopDriveTorquePlan ++; - dtoCopy.TopDriveTorqueLimitMax ++; + var dtoUpdate = dto.Adapt(); + dtoUpdate.Id = entry.Entity.Id; + dtoUpdate.Comment = "nebuchadnezzar"; + dtoUpdate.DeltaPressureLimitMax++; + dtoUpdate.DeltaPressurePlan++; + dtoUpdate.FlowPlan++; + dtoUpdate.FlowLimitMax++; + dtoUpdate.RopPlan++; + dtoUpdate.AxialLoadPlan++; + dtoUpdate.AxialLoadLimitMax++; + dtoUpdate.DepthStart++; + dtoUpdate.DepthEnd++; + dtoUpdate.TopDriveSpeedPlan++; + dtoUpdate.TopDriveSpeedLimitMax++; + dtoUpdate.TopDriveTorquePlan++; + dtoUpdate.TopDriveTorqueLimitMax++; + + var dtoInsert = dtoUpdate.Adapt(); + dtoInsert.Id = 0; + dtoInsert.Comment = "nebuchad"; + dtoInsert.DeltaPressureLimitMax++; + dtoInsert.DeltaPressurePlan++; + dtoInsert.FlowPlan++; + dtoInsert.FlowLimitMax++; + dtoInsert.RopPlan++; + dtoInsert.AxialLoadPlan++; + dtoInsert.AxialLoadLimitMax++; + dtoInsert.DepthStart++; + dtoInsert.DepthEnd++; + dtoInsert.TopDriveSpeedPlan++; + dtoInsert.TopDriveSpeedLimitMax++; + dtoInsert.TopDriveTorquePlan++; + dtoInsert.TopDriveTorqueLimitMax++; // act - var result = await client.UpdateRangeAsync(entity.IdWell, new ProcessMapPlanDrillingDto[] { dtoCopy }); + var result = await client.UpdateOrInsertRange(entity.IdWell, new ProcessMapPlanDrillingDto[] { dtoUpdate, dtoInsert }); // assert var doneTime = DateTimeOffset.UtcNow; Assert.Equal(HttpStatusCode.OK, result.StatusCode); - Assert.Equal(2, result.Content); + Assert.Equal(3, result.Content); var count = dbset.Count(); - Assert.Equal(2, count); + Assert.Equal(3, count); var oldEntity = dbset.First(p => p.Id == entry.Entity.Id); Assert.Equal(ProcessMapPlanBase.IdStateReplaced, oldEntity.IdState); @@ -239,7 +256,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest Assert.NotNull(oldEntity.Obsolete); Assert.InRange(oldEntity.Obsolete.Value, startTime, doneTime); - var newEntity = dbset.First(p => p.Id != entry.Entity.Id); + var newEntity = dbset.First(p => p.Comment == dtoUpdate.Comment); Assert.Equal(ProcessMapPlanBase.IdStateActual, newEntity.IdState); Assert.Equal(1, newEntity.IdAuthor); Assert.Null(newEntity.IdEditor); @@ -247,7 +264,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest Assert.Equal(oldEntity.Id, newEntity.IdPrevious); Assert.InRange(newEntity.Creation, startTime, doneTime); - var expected = dtoCopy.Adapt(); + var expected = dtoUpdate.Adapt(); var excludeProps = new[] { nameof(ProcessMapPlanDrilling.Id), nameof(ProcessMapPlanDrilling.Author), @@ -289,6 +306,39 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest Assert.InRange(actual.Obsolete.Value, startTime, doneTime); } + + [Fact] + public async Task Clear_returns_success() + { + //arrange + var dbset = dbContext.Set(); + + var entry = dbset.Add(entity); + dbContext.SaveChanges(); + entry.State = EntityState.Detached; + + var startTime = DateTimeOffset.UtcNow; + + //act + var response = await client.Clear(dto.IdWell); + + //assert + var doneTime = DateTimeOffset.UtcNow; + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(1, response.Content); + + var actual = dbContext + .Set() + .FirstOrDefault(p => p.Id == entry.Entity.Id); + + Assert.NotNull(actual); + Assert.Equal(ProcessMapPlanBase.IdCleared, actual.IdState); + Assert.Equal(1, actual.IdEditor); + Assert.NotNull(actual.Obsolete); + Assert.InRange(actual.Obsolete.Value, startTime, doneTime); + } + + [Fact] public async Task GetDatesChange_returns_success() { diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs index 8d954a60..c3fd0c49 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanBaseController.cs @@ -23,10 +23,10 @@ namespace AsbCloudWebApi.Controllers.ProcessMapPlan; public abstract class ProcessMapPlanBaseController : ControllerBase where TDto : ProcessMapPlanBaseDto { - private readonly ProcessMapPlanBaseService repository; + private readonly IChangeLogRepository repository; private readonly IWellService wellService; - public ProcessMapPlanBaseController(ProcessMapPlanBaseService repository, IWellService wellService) + public ProcessMapPlanBaseController(IChangeLogRepository repository, IWellService wellService) { this.repository = repository; this.wellService = wellService; @@ -68,7 +68,9 @@ public abstract class ProcessMapPlanBaseController : ControllerBase return this.ValidationBadRequest(nameof(dtos), "all dtos should contain same idWell"); var idUser = await AssertUserHasAccessToWell(idWell, token); - var result = await repository.ClearAndInsertRange(idUser, idWell, dtos, token); + + var request = new ProcessMapPlanBaseRequestWithWell(idWell); + var result = await repository.ClearAndInsertRange(idUser, request, dtos, token); return Ok(result); } @@ -85,10 +87,29 @@ public abstract class ProcessMapPlanBaseController : ControllerBase public async Task DeleteRange([FromRoute]int idWell, IEnumerable ids, CancellationToken token) { var idUser = await AssertUserHasAccessToWell(idWell, token); + var result = await repository.DeleteRange(idUser, ids, token); return Ok(result); } + /// + /// Очистка + /// + /// + /// + /// + [HttpDelete("clear")] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task Clear([FromRoute] int idWell, CancellationToken token) + { + var idUser = await AssertUserHasAccessToWell(idWell, token); + + var request = new ProcessMapPlanBaseRequestWithWell(idWell); + var result = await repository.Clear(idUser, request, token); + return Ok(result); + } + /// /// Получение /// @@ -102,7 +123,9 @@ public abstract class ProcessMapPlanBaseController : ControllerBase public async Task>> Get([FromRoute] int idWell, [FromQuery]ProcessMapPlanBaseRequest request, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); - var result = await repository.Get(idWell, request, token); + + var serviceRequest = new ProcessMapPlanBaseRequestWithWell(request, idWell); + var result = await repository.Get(serviceRequest, token); return Ok(result); } @@ -119,7 +142,9 @@ public abstract class ProcessMapPlanBaseController : ControllerBase public async Task>> GetChangeLog([FromRoute] int idWell, [FromQuery] DateOnly? date, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); - var result = await repository.GetChangeLog(idWell, date, token); + + var serviceRequest = new ProcessMapPlanBaseRequestWithWell(idWell); + var result = await repository.GetChangeLog(serviceRequest, date, token); return Ok(result); } @@ -135,24 +160,26 @@ public abstract class ProcessMapPlanBaseController : ControllerBase public async Task>> GetDatesChange([FromRoute] int idWell, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); - var result = await repository.GetDatesChange(idWell, token); + + var serviceRequest = new ProcessMapPlanBaseRequestWithWell(idWell); + var result = await repository.GetDatesChange(serviceRequest, token); return Ok(result); } /// - /// Редактирование + /// Редактирование или добавление [для пакетного редактирования] /// /// /// /// /// - [HttpPut] + [HttpPut()] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - public async Task UpdateRange([FromRoute] int idWell, IEnumerable dtos, CancellationToken token) + public async Task UpdateOrInsertRange([FromRoute] int idWell, IEnumerable dtos, CancellationToken token) { var first = dtos.FirstOrDefault(); - if(first is null) + if (first is null) return NoContent(); if (idWell == 0 || dtos.Any(d => d.IdWell != idWell)) @@ -160,7 +187,7 @@ public abstract class ProcessMapPlanBaseController : ControllerBase var idUser = await AssertUserHasAccessToWell(idWell, token); - var result = await repository.UpdateRange(idUser, dtos, token); + var result = await repository.UpdateOrInsertRange(idUser, dtos, token); return Ok(result); } diff --git a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs index 907f4ec5..b0a20f1e 100644 --- a/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs +++ b/AsbCloudWebApi/Controllers/ProcessMapPlan/ProcessMapPlanDrillingController.cs @@ -1,12 +1,15 @@ using AsbCloudApp.Data.ProcessMapPlan; using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; using AsbCloudApp.Services; namespace AsbCloudWebApi.Controllers.ProcessMapPlan; public class ProcessMapPlanDrillingController : ProcessMapPlanBaseController { - public ProcessMapPlanDrillingController(IProcessMapPlanBaseRepository repository, IWellService wellService) + public ProcessMapPlanDrillingController( + IChangeLogRepository repository, + IWellService wellService) : base(repository, wellService) { } From 6e55d5b542f64496049e63dab3417eb74fb5ae9b Mon Sep 17 00:00:00 2001 From: Olga Nemt Date: Wed, 31 Jan 2024 11:15:21 +0500 Subject: [PATCH 3/6] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=B3=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20/=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=B2=D0=BE=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BE=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8:=20=D0=B3=D1=80=D1=83=D0=BF?= =?UTF-8?q?=D0=BF=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BE=D1=88=D0=B8?= =?UTF-8?q?=D0=B1=D0=BE=D0=BA=20=D0=BF=D0=BE=20name=20=D0=B2=D0=BD=D1=82?= =?UTF-8?q?=D1=83=D1=80=D0=B8=20=20ValidationBadRequest=20+=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D0=BA=D0=BE=D0=B4,=20=D0=BA=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D1=8B=D0=B9=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D0=BE=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=83=D1=89=D0=B5=D0=B9=20=D0=BE=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B8=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D1=86=20=D0=BF=D1=80=D0=B5=D0=B4=D1=8B=D0=B4=D1=83=D1=89?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repository/WellOperationRepository.cs | 12 ++++++------ AsbCloudWebApi/Extentions.cs | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs index 208e94ac..ea9a6975 100644 --- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs +++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs @@ -383,12 +383,12 @@ public class WellOperationRepository : IWellOperationRepository new[] { nameof(wellOperationDtos) }); } - if (previousDateEnd > currentDateStart) - { - yield return new ValidationResult( - "Предыдущая операция не завершена", - new[] { nameof(wellOperationDtos) }); - } + //if (previousDateEnd > currentDateStart) + //{ + // yield return new ValidationResult( + // "Предыдущая операция не завершена", + // new[] { nameof(wellOperationDtos) }); + //} previous = current; } diff --git a/AsbCloudWebApi/Extentions.cs b/AsbCloudWebApi/Extentions.cs index 5659292c..7299fae5 100644 --- a/AsbCloudWebApi/Extentions.cs +++ b/AsbCloudWebApi/Extentions.cs @@ -70,8 +70,10 @@ namespace Microsoft.AspNetCore.Mvc public static BadRequestObjectResult ValidationBadRequest(this ControllerBase controller, IEnumerable validationResults) { var errors = validationResults - .SelectMany(e => e.MemberNames.Select(name=> new { name, e.ErrorMessage })) - .ToDictionary(e => e.name, e => new[] { e.ErrorMessage ?? string.Empty }); + .SelectMany(e => e.MemberNames.Select(name => new { name, e.ErrorMessage })) + .GroupBy(e => e.name) + .ToDictionary(e => e.Key, e => e.Select(el => el.ErrorMessage ?? string.Empty).ToArray()); + var problem = new ValidationProblemDetails(errors); return controller.BadRequest(problem); } From 514d4fe80a340d7f1bb6977c3b7f72bf95af9f08 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Wed, 31 Jan 2024 12:03:35 +0500 Subject: [PATCH 4/6] ChangeLogRepositoryAbstract Add Ordering --- .../Repository/ChangeLogRepositoryAbstract.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs index 84506a21..64208a8e 100644 --- a/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs +++ b/AsbCloudInfrastructure/Repository/ChangeLogRepositoryAbstract.cs @@ -193,7 +193,8 @@ public abstract class ChangeLogRepositoryAbstract : ICh dates = dates.Select(date => date.ToOffset(offset)); var datesOnly = dates .Select(d => new DateOnly(d.Year, d.Month, d.Day)) - .Distinct(); + .Distinct() + .OrderBy(d => d); return datesOnly; } @@ -214,7 +215,11 @@ public abstract class ChangeLogRepositoryAbstract : ICh query = createdQuery.Union(editedQuery); } - var entities = await query.ToListAsync(token); + var entities = await query + .OrderBy(e => e.Creation) + .ThenBy(e => e.Obsolete) + .ThenBy(e => e.Id) + .ToListAsync(token); var dtos = entities.Select(e => Convert(e, offset)); return dtos; @@ -223,7 +228,11 @@ public abstract class ChangeLogRepositoryAbstract : ICh public async Task> Get(TRequest request, CancellationToken token) { var query = BuildQuery(request); - var entities = await query.ToArrayAsync(token); + var entities = await query + .OrderBy(e => e.Creation) + .ThenBy(e => e.Obsolete) + .ThenBy(e => e.Id) + .ToArrayAsync(token); TimeSpan offset = GetTimezoneOffset(request); var dtos = entities.Select(e => Convert(e, offset)); From 855b483f22d323622ed6b94c72315904bcfb4a8c Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Wed, 31 Jan 2024 13:53:07 +0500 Subject: [PATCH 5/6] fix SlipsStatControllerTest --- .../Controllers/SlipsStatControllerTest.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs index a31c5f19..908d5b95 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs @@ -2,6 +2,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Requests; using AsbCloudDb.Model; using AsbCloudWebApi.IntegrationTests.Clients; +using Microsoft.EntityFrameworkCore; using Xunit; namespace AsbCloudWebApi.IntegrationTests.Controllers; @@ -10,7 +11,7 @@ public class SlipsStatControllerTest : BaseIntegrationTest { private static readonly Schedule schedule = new() { - Id = 1, + Id = 0, IdDriller = Data.Defaults.Drillers[0].Id, IdWell = Data.Defaults.Wells[0].Id, ShiftStart = new TimeOnly(8, 0, 0), @@ -21,7 +22,7 @@ public class SlipsStatControllerTest : BaseIntegrationTest private static readonly DetectedOperation detectedOperation = new() { - Id = 1, + Id = 0, IdTelemetry = Data.Defaults.Telemetries[0].Id, IdCategory = WellOperationCategory.IdSlipsTime, DateStart = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 0, 0, 0, DateTimeKind.Utc)), @@ -34,7 +35,7 @@ public class SlipsStatControllerTest : BaseIntegrationTest private static readonly WellOperation factWellOperation = new() { - Id = 1, + Id = 0, IdWell = Data.Defaults.Wells[0].Id, IdWellSectionType = 1, IdCategory = WellOperationCategory.IdRotor, @@ -51,9 +52,18 @@ public class SlipsStatControllerTest : BaseIntegrationTest public SlipsStatControllerTest(WebAppFactoryFixture factory) : base(factory) { - dbContext.Schedule.Add(schedule); - dbContext.DetectedOperations.Add(detectedOperation); - dbContext.WellOperations.Add(factWellOperation); + var schedules = dbContext.Set(); + var detectedOperations = dbContext.Set(); + var wellOperations = dbContext.Set(); + + schedules.RemoveRange(schedules); + detectedOperations.RemoveRange(detectedOperations); + wellOperations.RemoveRange(wellOperations); + dbContext.SaveChanges(); + + schedules.Add(schedule); + detectedOperations.Add(detectedOperation); + wellOperations.Add(factWellOperation); dbContext.SaveChanges(); client = factory.GetAuthorizedHttpClient(); From 75a40c763cc12affb83f99cd17683c1ad236cb68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 31 Jan 2024 14:43:17 +0500 Subject: [PATCH 6/6] =?UTF-8?q?=D0=A3=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=B0=D0=BA?= =?UTF-8?q?=D0=B5=D1=82=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B5=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=D0=BE=D0=B7=D0=BD=D0=B8=D0=BA=D0=B0=D1=8E=D1=89=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=B2=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D0=B8=20iText7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudInfrastructure/AsbCloudInfrastructure.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj index c607d523..02400268 100644 --- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj +++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj @@ -55,6 +55,7 @@ +