using AsbCloudApp.Data; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data.WellOperation; using AsbCloudApp.Requests.ExportOptions; using AsbCloudApp.Requests.ParserOptions; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.WellOperations.Factories; using System.Linq; namespace AsbCloudWebApi.Controllers; /// <summary> /// Буровые операции (вводимые вручную) /// </summary> [Route("api/well/{idWell}/wellOperations")] [ApiController] [Authorize] public class WellOperationController : ControllerBase { private readonly IDictionary<int, string> templateNames = new Dictionary<int, string> { { WellOperation.IdOperationTypeFact, "ЕЦП_шаблон_файла_фактические_операции.xlsx" }, { WellOperation.IdOperationTypePlan, "ЕЦП_шаблон_файла_плановые_операции.xlsx" } }; private readonly IUserRepository userRepository; private readonly IWellOperationRepository wellOperationRepository; private readonly IWellOperationCategoryRepository wellOperationCategoryRepository; private readonly IWellService wellService; private readonly WellOperationParserFactory wellOperationParserFactory; private readonly WellOperationExportServiceFactory wellOperationExportServiceFactory; public WellOperationController(IWellOperationRepository wellOperationRepository, IWellOperationCategoryRepository wellOperationCategoryRepository, IWellService wellService, IUserRepository userRepository, WellOperationParserFactory wellOperationParserFactory, WellOperationExportServiceFactory wellOperationExportServiceFactory) { this.wellOperationRepository = wellOperationRepository; this.wellOperationCategoryRepository = wellOperationCategoryRepository; this.wellService = wellService; this.userRepository = userRepository; this.wellOperationParserFactory = wellOperationParserFactory; this.wellOperationExportServiceFactory = wellOperationExportServiceFactory; } /// <summary> /// Добавляет новые операции на скважине /// </summary> /// <param name="idWell">Id скважины</param> /// <param name="dtos">Добавляемые операции</param> /// <param name="deleteBeforeInsert">Удалить операции перед сохранением</param> /// <param name="cancellationToken"></param> /// <returns>Количество добавленных в БД записей</returns> [HttpPost("{deleteBeforeInsert:bool}")] [Permission] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<IActionResult> InsertRangeAsync( [Range(1, int.MaxValue, ErrorMessage = "Id скважины не может быть меньше 1")] int idWell, bool deleteBeforeInsert, [FromBody] IEnumerable<WellOperationDto> dtos, CancellationToken cancellationToken) { if (!await CanUserAccessToWellAsync(idWell, cancellationToken)) return Forbid(); if (!await CanUserEditWellOperationsAsync(idWell, cancellationToken)) return Forbid(); foreach (var dto in dtos) { dto.IdWell = idWell; dto.LastUpdateDate = null; dto.IdUser = User.GetUserId(); } var result = await wellOperationRepository.InsertRangeAsync(dtos, deleteBeforeInsert, cancellationToken); return Ok(result); } /// <summary> /// Обновляет выбранную операцию на скважине /// </summary> /// <param name="idWell">id скважины</param> /// <param name="dtos"></param> /// <param name="token">Токен отмены задачи</param> /// <returns>Количество обновленных в БД строк</returns> [HttpPut] [Permission] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] public async Task<IActionResult> UpdateRangeAsync(int idWell, [FromBody] IEnumerable<WellOperationDto> dtos, CancellationToken token) { if (!await CanUserAccessToWellAsync(idWell, token)) return Forbid(); if (!await CanUserEditWellOperationsAsync(idWell, token)) return Forbid(); foreach (var dto in dtos) { dto.IdWell = idWell; dto.IdUser = User.GetUserId(); dto.LastUpdateDate = DateTimeOffset.UtcNow; } var result = await wellOperationRepository.UpdateRangeAsync(dtos, token); return Ok(result); } /// <summary> /// Возвращает словарь типов секций /// </summary> /// <returns></returns> [HttpGet("sectionTypes")] [Permission] [ProducesResponseType(typeof(IEnumerable<WellSectionTypeDto>), StatusCodes.Status200OK)] public IActionResult GetSectionTypes() { var result = wellOperationRepository.GetSectionTypes(); return Ok(result); } /// <summary> /// Статистика операций по скважине, группированная по категориям /// </summary> /// <param name="idWell">id скважины</param> /// <param name="request"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet("groupStat")] [Permission] [ProducesResponseType(typeof(IEnumerable<WellGroupOpertionDto>), (int)System.Net.HttpStatusCode.OK)] public async Task<IActionResult> GetGroupOperationsAsync( [FromRoute] int idWell, [FromQuery] WellOperationRequestBase request, CancellationToken token) { if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) return Forbid(); var requestToservice = new WellOperationRequest(request, new[] { idWell }); var result = await wellOperationRepository.GetGroupOperationsStatAsync(requestToservice, token); return Ok(result); } /// <summary> /// Возвращает список имен типов операций на скважине /// </summary> /// <param name="includeParents">флаг, нужно ли включать родителей в список</param> /// <returns></returns> [HttpGet("categories")] [Permission] [ProducesResponseType(typeof(IEnumerable<WellOperationCategoryDto>), StatusCodes.Status200OK)] public IActionResult GetCategories(bool includeParents = true) { var result = wellOperationCategoryRepository.Get(includeParents, false); return Ok(result); } /// <summary> /// Постраничный список операций на скважине. /// </summary> /// <param name="idWell">id скважины</param> /// <param name="request"></param> /// <param name="token"></param> /// <returns>Список операций на скважине</returns> [HttpGet] [Permission] [ProducesResponseType(typeof(PaginationContainer<WellOperationDto>), StatusCodes.Status200OK)] public async Task<IActionResult> GetPageOperationsAsync( [FromRoute] int idWell, [FromQuery] WellOperationRequestBase request, CancellationToken token) { if (!await CanUserAccessToWellAsync(idWell, token)) return Forbid(); var requestToService = new WellOperationRequest(request, new[] { idWell }); var result = await wellOperationRepository.GetPageAsync(requestToService, token); return Ok(result); } /// <summary> /// Создает excel файл с "сетевым графиком" /// </summary> /// <param name="idWell">id скважины</param> /// <param name="scheduleReportService"></param> /// <param name="token"></param> /// <returns>Запрашиваемый файл</returns> [HttpGet("scheduleReport")] [Permission] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK)] public async Task<IActionResult> ScheduleReportAsync([FromRoute] int idWell, [FromServices] IScheduleReportService scheduleReportService, CancellationToken token) { var idCompany = User.GetCompanyId(); if (idCompany is null) return Forbid(); if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token)) return Forbid(); var stream = await scheduleReportService.MakeReportAsync(idWell, token); var fileName = await wellService.GetWellCaptionByIdAsync(idWell, token) + "_ScheduleReport.xlsx"; return File(stream, "application/octet-stream", fileName); } /// <summary> /// Удаляет выбранную операцию на скважине /// </summary> /// <param name="idWell">id скважины</param> /// <param name="idOperation">id выбранной операции</param> /// <param name="token">Токен отмены задачи</param> /// <returns>Количество удаленных из БД строк</returns> [HttpDelete("{idOperation}")] [Permission] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] public async Task<IActionResult> DeleteAsync(int idWell, int idOperation, CancellationToken token) { if (!await CanUserAccessToWellAsync(idWell, token)) return Forbid(); if (!await CanUserEditWellOperationsAsync(idWell, token)) return Forbid(); var result = await wellOperationRepository.DeleteRangeAsync(new[] { idOperation }, token); return Ok(result); } /// <summary> /// Удаляет выбранные операции по скважине /// </summary> /// <param name="idWell">id скважины</param> /// <param name="ids">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) { if (!await CanUserAccessToWellAsync(idWell, token)) return Forbid(); if (!await CanUserEditWellOperationsAsync(idWell, token)) return Forbid(); if (!ids.Any()) return this.ValidationBadRequest(nameof(ids), "Пустой список операций"); var result = await wellOperationRepository.DeleteRangeAsync(ids, token); if(result == ICrudRepository<WellOperationDto>.ErrorIdNotFound) return this.ValidationBadRequest(nameof(ids), "Минимум одна из операций не найдена в базе"); return Ok(result); } /// <summary> /// Формирование excel файла с операциями на скважине /// </summary> /// <param name="idWell"></param> /// <param name="idType"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet("export")] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")] public async Task<IActionResult> ExportAsync(int idWell, int idType, CancellationToken token) { var options = new WellOperationExportRequest(idWell, idType); var exportService = wellOperationExportServiceFactory.CreateExportService<WellOperationExportRequest>(idType); var (fileName, file) = await exportService.ExportAsync(options, token); return File(file, "application/octet-stream", fileName); } /// <summary> /// Парсинг ГГД из excel (xlsx) файла /// </summary> /// <param name="idWell"></param> /// <param name="idType"></param> /// <param name="file"></param> /// <param name="token"></param> /// <returns></returns> [HttpPost("parse/{idType}")] [Permission] [ProducesResponseType(typeof(ParserResultDto<WellOperationDto>), StatusCodes.Status200OK)] public async Task<IActionResult> ParseAsync(int idWell, int idType, [Required] IFormFile file, CancellationToken token) { if (!await CanUserAccessToWellAsync(idWell, token)) return Forbid(); if (!await CanUserEditWellOperationsAsync(idWell, token)) return Forbid(); var stream = file.GetExcelFile(); try { var timezone = wellService.GetTimezone(idWell); var options = new WellOperationParserRequest(idWell, idType, timezone); var parser = wellOperationParserFactory.CreateParser<WellOperationParserRequest>(idType); var result = parser.Parse(stream, options); return Ok(result); } catch (FileFormatException ex) { return this.ValidationBadRequest(nameof(file), ex.Message); } } /// <summary> /// Получение шаблона для заполнения ГГД /// </summary> /// <returns></returns> [HttpGet("template")] [AllowAnonymous] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")] public IActionResult GetTemplate(int idType) { var parser = wellOperationParserFactory.CreateParser<WellOperationParserRequest>(idType); var stream = parser.GetTemplateFile(); return File(stream, "application/octet-stream", templateNames[idType]); } private async Task<bool> CanUserAccessToWellAsync(int idWell, CancellationToken token) { var idCompany = User.GetCompanyId(); return idCompany is not null && await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token).ConfigureAwait(false); } private async Task<bool> CanUserEditWellOperationsAsync(int idWell, CancellationToken token) { var idUser = User.GetUserId(); if (!idUser.HasValue) return false; var well = await wellService.GetOrDefaultAsync(idWell, token); if (well is null) return false; return well.IdState != 2 || userRepository.HasPermission(idUser.Value, "WellOperation.editCompletedWell"); } }