diff --git a/AsbCloudWebApi/Controllers/DailyReportController.cs b/AsbCloudWebApi/Controllers/DailyReportController.cs index dc740619..4041d732 100644 --- a/AsbCloudWebApi/Controllers/DailyReportController.cs +++ b/AsbCloudWebApi/Controllers/DailyReportController.cs @@ -1,196 +1,224 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Data.DailyReport; -using AsbCloudApp.Exceptions; -using AsbCloudApp.Services; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System; +using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; +using AsbCloudApp.Data; +using AsbCloudApp.Data.DailyReport; +using AsbCloudApp.Data.DailyReport.Blocks; +using AsbCloudApp.Data.DailyReport.Blocks.Sign; +using AsbCloudApp.Data.DailyReport.Blocks.Subsystems; +using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance; +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudApp.Services.DailyReport; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; -namespace AsbCloudWebApi.Controllers +namespace AsbCloudWebApi.Controllers; + +/// +/// Суточный отчёт +/// +[ApiController] +[Route("api/well/{idWell:int}/[controller]")] +[Authorize] +public class DailyReportController : ControllerBase { + private readonly IWellOperationRepository wellOperationRepository; + private readonly IDailyReportService dailyReportService; + private readonly IDailyReportExportService dailyReportExportService; + private readonly IWellService wellService; - /// - /// Суточный рапорт - /// - [Route("api/well/{idWell}/[controller]")] - [ApiController] - [Authorize] - public class DailyReportController : ControllerBase - { - private readonly IDailyReportService dailyReportService; - private readonly IWellService wellService; + public DailyReportController(IWellOperationRepository wellOperationRepository, + IDailyReportService dailyReportService, + IDailyReportExportService dailyReportExportService, + IWellService wellService) + { + this.wellOperationRepository = wellOperationRepository; + this.dailyReportService = dailyReportService; + this.dailyReportExportService = dailyReportExportService; + this.wellService = wellService; + } - public DailyReportController( - IDailyReportService dailyReportService, - IWellService wellService) - { - this.dailyReportService = dailyReportService; - this.wellService = wellService; - } + private int IdUser + { + get + { + var idUser = User.GetUserId(); - /// - /// Список наборов данных для формирования рапорта - /// - /// - /// - /// - /// - /// - [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] - public async Task GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token) - { - var result = await dailyReportService.GetListAsync(idWell, begin, end, token); - return Ok(result); - } + if (!idUser.HasValue) + throw new ForbidException("Неизвестный пользователь"); - /// - /// Создание суточного рапорта - /// - /// - /// - /// - /// - [HttpPost] - [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public async Task AddAsync(int idWell, [Required] DateOnly startDate, CancellationToken token) - { - if (!await UserHasAccesToWellAsync(idWell, token)) - return Forbid(); + return idUser.Value; + } + } - var idUser = User.GetUserId()!.Value; + /// + /// Создать суточный отчёт + /// + /// Id скважины + /// Дата формирования суточного отчёта + /// + /// + [HttpPost] + [Permission] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task InsertAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken) + { + await AssertUserAccessToWell(idWell, cancellationToken); - var result = await dailyReportService.AddAsync(idWell, startDate, idUser, token); - return Ok(result); - } + var id = await dailyReportService.InsertAsync(idWell, dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), + cancellationToken); - /// - /// Сохранение изменений набора данных для формирования рапорта (заголовок) - /// - /// - /// Дата без учета времени - /// - /// - /// - [HttpPut("{date}/head")] - [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateHeadAsync(int idWell, [Required] DateOnly date, [Required] HeadDto dto, CancellationToken token) - => UpdateReportBlockAsync(idWell, date, dto, token); + return Ok(id); + } - /// - /// Сохранение изменений набора данных для формирования рапорта (блок КНБК) - /// - /// - /// Дата без учета времени - /// - /// - /// - [HttpPut("{date}/bha")] - [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateBhaAsync(int idWell, [Required] DateOnly date, [Required] BhaDto dto, CancellationToken token) - => UpdateReportBlockAsync(idWell, date, dto, token); + /// + /// Обновить подпись + /// + /// Id скважины + /// Id суточного отчёта + /// Обновляемый блок + /// + /// + [HttpPut("sign")] + [Permission] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public Task + UpdateSignBlockAsync(int idWell, int idDailyReport, SignBlockDto signBlock, CancellationToken cancellationToken) => + UpdateBlockAsync(idWell, idDailyReport, signBlock, cancellationToken); - /// - /// Сохранение изменений набора данных для формирования рапорта (безметражные работы) - /// - /// - /// Дата без учета времени - /// - /// - /// - [HttpPut("{date}/noDrilling")] - [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateNoDrillingAsync(int idWell, [Required] DateOnly date, [Required] NoDrillingDto dto, CancellationToken token) - => UpdateReportBlockAsync(idWell, date, dto, token); + /// + /// Обновить наработку подсистем + /// + /// Id скважины + /// Id суточного отчёта + /// Обновляемый блок + /// + /// + [HttpPut("subsystem")] + [Permission] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public Task UpdateSubsystemBlockAsync(int idWell, int idDailyReport, SubsystemBlockDto subsystemBlock, + CancellationToken cancellationToken) + { + var validSubsystemIds = new[] { 100000, 100001 }; - /// - /// Сохранение изменений набора данных для формирования рапорта (САУБ) - /// - /// - /// Дата без учета времени - /// - /// - /// - [HttpPut("{date}/saub")] - [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateSaubAsync(int idWell, [Required] DateOnly date, [Required] SaubDto dto, CancellationToken token) - => UpdateReportBlockAsync(idWell, date, dto, token); + if (subsystemBlock.Modules.Any(m => !validSubsystemIds.Contains(m.IdSubsystem))) + throw new ArgumentInvalidException($"Возможно добавить модули только с Id: {string.Join(", ", validSubsystemIds)}", + nameof(subsystemBlock.Modules)); - /// - /// Сохранение изменений набора данных для формирования рапорта (подпись) - /// - /// - /// Дата без учета времени - /// - /// - /// - [HttpPut("{date}/sign")] - [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)] - public Task UpdateSignAsync(int idWell, [Required] DateOnly date, [Required] SignDto dto, CancellationToken token) - => UpdateReportBlockAsync(idWell, date, dto, token); + return UpdateBlockAsync(idWell, idDailyReport, subsystemBlock, cancellationToken); + } - /// - /// Обновление блока суточного рапорта - /// - /// ключ скважины - /// дата суточного рапорта - /// - /// - /// - private async Task UpdateReportBlockAsync(int idWell, DateOnly date, ItemInfoDto dto, CancellationToken token) - { - if (!await UserHasAccesToWellAsync(idWell, token)) - return Forbid(); + /// + /// Обновить баланс времени + /// + /// Id скважины + /// Id суточного отчёта + /// Обновляемый блок + /// + /// + [HttpPut("timeBalance")] + [Permission] + [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public Task UpdateTimeBalanceBlockAsync(int idWell, int idDailyReport, TimeBalanceBlockDto timeBalanceBlock, + CancellationToken cancellationToken) + { + var validWellOperationsIds = new[] { 4001, 4002, 4004, 4012 }; - dto.IdUser = User.GetUserId(); - dto.LastUpdateDate = DateTimeOffset.Now; + if (timeBalanceBlock.WellOperations.Any(o => !validWellOperationsIds.Contains(o.IdWellOperation))) + throw new ArgumentInvalidException($"Возможно добавить операции только с Id: {string.Join(",", validWellOperationsIds)}", + nameof(timeBalanceBlock.WellOperations)); + + var wellSections = wellOperationRepository.GetSectionTypes(); - var result = await dailyReportService.UpdateBlockAsync(idWell, date, dto, token); - return Ok(result); - } + if (wellSections.All(s => s.Id != timeBalanceBlock.IdSection)) + throw new ArgumentInvalidException($"Секция с Id: {timeBalanceBlock.IdSection} не найдена", nameof(timeBalanceBlock.IdSection)); - /// - /// Сформировать и скачать рапорт в формате excel - /// - /// - /// - /// - /// - [HttpGet("{date}/excel")] - [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task DownloadAsync(int idWell, DateOnly date, CancellationToken token) - { - if (!await UserHasAccesToWellAsync(idWell, token)) - return Forbid(); + return UpdateBlockAsync(idWell, idDailyReport, timeBalanceBlock, cancellationToken); + } - var well = await wellService.GetOrDefaultAsync(idWell, token); - if (well is null) - return this.ValidationBadRequest(nameof(idWell), $"Скважина c id:{idWell} не найдена"); + /// + /// Получить список суточных отчётов по скважине + /// + /// Идентификатор скважины + /// Параметры запроса + /// + /// + [HttpGet] + [ProducesResponseType(typeof(PaginationContainer), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task GetAsync(int idWell, [FromQuery] FileReportRequest request, CancellationToken cancellationToken) + { + await AssertUserAccessToWell(idWell, cancellationToken); - var stream = await dailyReportService.MakeReportAsync(idWell, date, token); - if (stream is null) - return NoContent(); - - var fileName = $"Суточный рапорт по скважине {well.Caption} куст {well.Cluster}.xlsx"; - return File(stream, "application/octet-stream", fileName); + var dailyReports = await dailyReportService.GetAsync(idWell, request, cancellationToken); - } + return Ok(dailyReports); + } + + /// + /// Получить диапазон дат по которым возможно сформировать суточный отчёты + /// + /// Id скважины + /// + /// + [HttpGet("datesRange")] + [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)] + public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken) + { + await AssertUserAccessToWell(idWell, cancellationToken); - protected async Task UserHasAccesToWellAsync(int idWell, CancellationToken token) - { - var idCompany = User.GetCompanyId(); - if (idCompany is not null && - await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token) - .ConfigureAwait(false)) - return true; - return false; - } - } + var datesRanges = await dailyReportService.GetDatesRangeAsync(idWell, cancellationToken); -} + return Ok(datesRanges); + } + + /// + /// Экспорт суточного рапорта + /// + /// Id скважины + /// Дата формирования суточного отчёта + /// + /// + [HttpGet("{dailyReportDateStart}")] + [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + public async Task ExportAsync(int idWell, DateOnly dailyReportDateStart, CancellationToken cancellationToken) + { + await AssertUserAccessToWell(idWell, cancellationToken); + + var dailyReport = await dailyReportExportService.ExportAsync(idWell, + dailyReportDateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken); + + return File(dailyReport.File, "application/octet-stream", dailyReport.FileName); + } + + private async Task UpdateBlockAsync(int idWell, int idDailyReport, TBlock block, + CancellationToken cancellationToken) + where TBlock : EditableBlock + { + await AssertUserAccessToWell(idWell, cancellationToken); + + var id = await dailyReportService.UpdateBlockAsync(idDailyReport, IdUser, block, cancellationToken); + + return Ok(id); + } + + private async Task AssertUserAccessToWell(int idWell, CancellationToken cancellationToken) + { + var idCompany = User.GetCompanyId(); + + if (!idCompany.HasValue || !await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, cancellationToken)) + throw new ForbidException("Нет доступа к скважине"); + } +} \ No newline at end of file