using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Requests.ExportOptions; using AsbCloudApp.Requests.ParserOptions; using AsbCloudApp.Services; using AsbCloudApp.Services.Export; using AsbCloudApp.Services.Parsers; using AsbCloudDb.Model.ProcessMapPlan; 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.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudWebApi.Controllers.ProcessMaps; /// <summary> /// РТК план базовый /// </summary> [ApiController] [Route("api/well/{idWell}/[controller]")] [Authorize] public abstract class ProcessMapPlanBaseController<TEntity, TDto> : ControllerBase where TEntity : ProcessMapPlanBase where TDto : ProcessMapPlanBaseDto { private readonly IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository; private readonly IWellService wellService; private readonly IParserService<TDto, WellRelatedParserRequest> parserService; private readonly ITelemetryService telemetryService; private readonly IExportService<WellRelatedExportRequest> processMapPlanExportService; protected ProcessMapPlanBaseController(IChangeLogRepository<TDto, ProcessMapPlanBaseRequestWithWell> repository, IWellService wellService, IParserService<TDto, WellRelatedParserRequest> parserService, IExportService<WellRelatedExportRequest> processMapPlanExportService, ITelemetryService telemetryService) { this.repository = repository; this.wellService = wellService; this.parserService = parserService; this.telemetryService = telemetryService; this.processMapPlanExportService = processMapPlanExportService; } protected abstract string TemplateFileName { get; } /// <summary> /// Добавление /// </summary> /// <param name="idWell"></param> /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> [HttpPost] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<IActionResult> InsertRange([FromRoute][Range(0, int.MaxValue)] int idWell, IEnumerable<TDto> dtos, CancellationToken token) { var idUser = await AssertUserHasAccessToWell(idWell, token); foreach (var dto in dtos) dto.IdWell = idWell; var result = await repository.InsertRange(idUser, dtos, token); return Ok(result); } /// <summary> /// Удалить все по скважине и добавить новые /// </summary> /// <param name="idWell"></param> /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> [HttpPost("replace")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<IActionResult> ClearAndInsertRange([FromRoute] int idWell, IEnumerable<TDto> dtos, CancellationToken token) { var idUser = await AssertUserHasAccessToWell(idWell, token); foreach (var dto in dtos) dto.IdWell = idWell; var request = new ProcessMapPlanBaseRequestWithWell(idWell); var result = await repository.ClearAndInsertRange(idUser, request, dtos, token); return Ok(result); } /// <summary> /// Пометить записи как удаленные /// </summary> /// <param name="idWell"></param> /// <param name="ids"></param> /// <param name="token"></param> /// <returns></returns> [HttpDelete] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<IActionResult> MarkAsDeleted([FromRoute] int idWell, IEnumerable<int> ids, CancellationToken token) { var idUser = await AssertUserHasAccessToWell(idWell, token); var result = await repository.MarkAsDeleted(idUser, ids, token); return Ok(result); } /// <summary> /// Очистка /// </summary> /// <param name="idWell"></param> /// <param name="token"></param> /// <returns></returns> [HttpDelete("clear")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<IActionResult> 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); } /// <summary> /// Получение текущих записей по параметрам /// </summary> /// <param name="idWell"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<TDto>>> GetCurrent([FromRoute] int idWell, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); var serviceRequest = new ProcessMapPlanBaseRequestWithWell(idWell); var result = await repository.GetCurrent(serviceRequest, token); return Ok(result); } /// <summary> /// Получение измененных записей за определенную дату по ключу скважины /// </summary> /// <param name="idWell"></param> /// <param name="moment"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet("changelogByMoment")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<ChangeLogDto<TDto>>>> GetChangeLogByMoment([FromRoute] int idWell, DateTimeOffset? moment, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); var dataRequest = new ProcessMapPlanBaseRequestWithWell(idWell); var changeLogRequest = new ChangeLogRequest() { Moment = moment }; var builder = repository .GetQueryBuilder(changeLogRequest) .ApplyRequest(dataRequest); var dtos = await builder.GetChangeLogData(token); return Ok(dtos); } /// <summary> /// Получение записей за определенную дату по uid /// </summary> /// <param name="uid"></param> /// <param name="updateFrom"></param> /// <param name="token"></param> /// <returns></returns> [AllowAnonymous] [HttpGet("/api/telemetry/{uid}/[controller]")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<ChangeLogDto<TDto>>>> GetByUid(string uid, DateTimeOffset? updateFrom, CancellationToken token) { var idWell = telemetryService.GetIdWellByTelemetryUid(uid) ?? -1; if (idWell < 0) return this.ValidationBadRequest(nameof(uid), "Скважина по uid не найдена"); var serviceRequest = new ProcessMapPlanBaseRequestWithWell(idWell) { UpdateFrom = updateFrom, }; var result = await repository.GetChangeLogForDate(serviceRequest, null, token); return Ok(result); } /// <summary> /// Изменения за определенную дату /// </summary> /// <param name="idWell"></param> /// <param name="date"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet("changeLogForDate")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<ChangeLogDto<TDto>>>> GetChangeLogForDate([FromRoute] int idWell, [FromQuery] DateOnly? date, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); var serviceRequest = new ProcessMapPlanBaseRequestWithWell(idWell); var result = await repository.GetChangeLogForDate(serviceRequest, date, token); return Ok(result); } /// <summary> /// Даты за которые есть изменения /// </summary> /// <param name="idWell"></param> /// <param name="token"></param> /// <returns></returns> [HttpGet("dates")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<DateOnly>>> GetDatesChange([FromRoute] int idWell, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); var serviceRequest = new ProcessMapPlanBaseRequestWithWell(idWell); var result = await repository.GetDatesChange(serviceRequest, token); return Ok(result); } /// <summary> /// Редактирование или добавление [для пакетного редактирования] /// </summary> /// <param name="idWell"></param> /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> [HttpPut()] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<IActionResult> UpdateOrInsertRange([FromRoute] int idWell, IEnumerable<TDto> dtos, CancellationToken token) { if (!dtos.Any()) return NoContent(); var idUser = await AssertUserHasAccessToWell(idWell, token); foreach (var dto in dtos) dto.IdWell = idWell; var result = await repository.UpdateOrInsertRange(idUser, dtos, token); return Ok(result); } /// <summary> /// Импорт РТК из excel (xlsx) файла /// </summary> /// <param name="idWell"></param> /// <param name="file"></param> /// <param name="token"></param> /// <returns></returns> [HttpPost("parse")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<ParserResultDto<TDto>>> Parse(int idWell, [Required] IFormFile file, CancellationToken token) { await AssertUserHasAccessToWell(idWell, token); var stream = file.GetExcelFile(); try { var options = new WellRelatedParserRequest(idWell); var dto = parserService.Parse(stream, options); return Ok(dto); } catch (FileFormatException ex) { return this.ValidationBadRequest(nameof(file), ex.Message); } } /// <summary> /// Получение шаблона для заполнения РТК /// </summary> /// <returns></returns> [HttpGet("template")] [AllowAnonymous] [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")] [ProducesResponseType(StatusCodes.Status204NoContent)] public IActionResult GetTemplate() { var stream = parserService.GetTemplateFile(); return File(stream, "application/octet-stream", TemplateFileName); } /// <summary> /// returns user id, if he has access to well /// </summary> /// <param name="idWell"></param> /// <param name="token"></param> /// <returns></returns> /// <exception cref="ForbidException"></exception> private async Task<int> AssertUserHasAccessToWell(int idWell, CancellationToken token) { var idUser = GetUserId(); var idCompany = User.GetCompanyId(); if (!idCompany.HasValue) throw new ForbidException("Нет доступа к скважине"); if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token)) throw new ForbidException("Нет доступа к скважине"); return idUser; } /// <summary> /// Формируем excel файл с текущими строками РТК /// </summary> /// <param name="idWell">id скважины</param> /// <param name="token"></param> /// <returns>Запрашиваемый файл</returns> [HttpGet("export")] [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task<IActionResult> ExportAsync([FromRoute] int idWell, CancellationToken token) { var exportOptions = new WellRelatedExportRequest(idWell); var (fileName, file) = await processMapPlanExportService.ExportAsync(exportOptions, token); return File(file, "application/octet-stream", fileName); } /// <summary> /// returns user id or throw /// </summary> /// <returns></returns> /// <exception cref="ForbidException"></exception> private int GetUserId() { var idUser = User.GetUserId() ?? throw new ForbidException("Неизвестный пользователь"); return idUser; } }