using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Data.ProcessMaps; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.ProcessMaps; using AsbCloudWebApi.SignalR; using AsbCloudWebApi.SignalR.Clients; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; namespace AsbCloudWebApi.Controllers.ProcessMaps; /// <summary> /// РТК /// </summary> [ApiController] [Route("api/well/{idWell}/[controller]")] [Authorize] public abstract class ProcessMapBaseController<T> : ControllerBase where T : ProcessMapPlanBaseDto { private readonly IHubContext<TelemetryHub, ITelemetryHubClient> telemetryHubContext; private readonly ITelemetryService telemetryService; private readonly IWellService wellService; private readonly IUserRepository userRepository; private readonly ICrudRepository<WellSectionTypeDto> wellSectionRepository; private readonly IProcessMapPlanRepository<T> repository; private readonly IProcessMapPlanService<T> service; protected ProcessMapBaseController(IWellService wellService, IProcessMapPlanRepository<T> repository, IUserRepository userRepository, ICrudRepository<WellSectionTypeDto> wellSectionRepository, IHubContext<TelemetryHub, ITelemetryHubClient> telemetryHubContext, ITelemetryService telemetryService, IProcessMapPlanService<T> service) { this.wellService = wellService; this.repository = repository; this.userRepository = userRepository; this.wellSectionRepository = wellSectionRepository; this.telemetryHubContext = telemetryHubContext; this.telemetryService = telemetryService; this.service = service; } protected abstract string SignalRGroup { get; } protected int IdUser { get { var idUser = User.GetUserId(); if (!idUser.HasValue) throw new ForbidException("Неизвестный пользователь"); return idUser.Value; } } /// <summary> /// Создание плановой РТК /// </summary> /// <param name="processMap">Тело запроса</param> /// <param name="idWell">Id скважины</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpPost] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public virtual async Task<IActionResult> InsertAsync(T processMap, int idWell, CancellationToken cancellationToken) { processMap.IdWell = idWell; processMap.IdUser = IdUser; processMap.LastUpdate = DateTime.UtcNow; await CheckIsExistsWellSectionTypeAsync(processMap.IdWellSectionType, cancellationToken); await AssertUserHasAccessToEditProcessMapAsync(processMap.IdWell, cancellationToken); var result = await repository.InsertAsync(processMap, cancellationToken); await NotifyUsersBySignalR(idWell, cancellationToken); return Ok(result); } /// <summary> /// Обновление плановой РТК /// </summary> /// <param name="processMap">Тело запроса</param> /// <param name="idWell">Id скважины</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpPut] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public virtual async Task<IActionResult> UpdateAsync(T processMap, int idWell, CancellationToken cancellationToken) { processMap.IdWell = idWell; processMap.IdUser = IdUser; processMap.LastUpdate = DateTime.UtcNow; await CheckIsExistsWellSectionTypeAsync(processMap.IdWellSectionType, cancellationToken); await AssertUserHasAccessToEditProcessMapAsync(idWell, cancellationToken); var result = await repository.UpdateAsync(processMap, cancellationToken); if (result == ICrudRepository<T>.ErrorIdNotFound) return this.ValidationBadRequest(nameof(processMap.Id), $"РТК с Id: {processMap.Id} не существует"); await NotifyUsersBySignalR(idWell, cancellationToken); return Ok(result); } /// <summary> /// Удаление плановой РТК /// </summary> /// <param name="id">Id удаляемой РТК</param> /// <param name="idWell">Id скважины</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpDelete] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] public virtual async Task<IActionResult> DeleteAsync(int id, int idWell, CancellationToken cancellationToken) { await AssertUserHasAccessToEditProcessMapAsync(idWell, cancellationToken); var result = await repository.DeleteAsync(id, cancellationToken); await NotifyUsersBySignalR(idWell, cancellationToken); return Ok(result); } /// <summary> /// Получение РТК по Id скважины /// </summary> /// <param name="idWell">Id скважины</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult<IEnumerable<ValidationResultDto<T>>>> GetAsync(int idWell, CancellationToken cancellationToken) { var processMaps = await service.GetAsync(idWell, cancellationToken); if (!processMaps.Any()) return NoContent(); return Ok(processMaps); } /// <summary> /// Получение РТК по телеметрии /// </summary> /// <param name="uid">Уникальный ключ телеметрии</param> /// <param name="updateFrom">Дата с которой требуется получить РТК</param> /// <param name="cancellationToken"></param> /// <returns></returns> [HttpGet("/api/telemetry/{uid}/[controller]")] [AllowAnonymous] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<T>>> GetProcessMapPlanByTelemetry(string uid, DateTime updateFrom, CancellationToken cancellationToken) { var idWell = telemetryService.GetIdWellByTelemetryUid(uid); if (!idWell.HasValue) return this.ValidationBadRequest(nameof(uid), $"Wrong uid {uid}"); var requests = new[] { new ProcessMapPlanRequest { IdWell = idWell.Value, UpdateFrom = updateFrom } }; var processMaps = await repository.GetAsync(requests, cancellationToken); return Ok(processMaps); } protected async Task AssertUserHasAccessToEditProcessMapAsync(int idWell, CancellationToken cancellationToken) { var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) ?? throw new ArgumentInvalidException(nameof(idWell), $"Скважины с {idWell} не существует"); var idCompany = User.GetCompanyId(); if (!idCompany.HasValue || !await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, cancellationToken)) throw new ForbidException("Нет доступа к скважине"); if (well.IdState == 2 && !userRepository.HasPermission(IdUser, "ProcessMap.editCompletedWell")) throw new ForbidException("Недостаточно прав для редактирования РТК завершенной скважины"); } protected async Task NotifyUsersBySignalR(int idWell, CancellationToken cancellationToken) { var dtos = await repository.GetByIdWellAsync(idWell, cancellationToken); await telemetryHubContext.Clients .Group($"{SignalRGroup}_{idWell}") .UpdateProcessMap(dtos, cancellationToken); } private async Task CheckIsExistsWellSectionTypeAsync(int idWellSectionType, CancellationToken cancellationToken) { _ = await wellSectionRepository.GetOrDefaultAsync(idWellSectionType, cancellationToken) ?? throw new ArgumentInvalidException(nameof(ProcessMapPlanWellDrillingDto.IdWellSectionType), $"Тип секции с Id: {idWellSectionType} не найден"); } }