feat(api,core,domain): Изменен контроллер RobotTaskRobotStatusController, метод изменения статуса задания вынесен в сервисы. Добавлена логика успрешного переименования шаблона.

This commit is contained in:
Mikhail Trubnikov
2026-07-23 14:39:55 +10:00
parent 58b4d98b16
commit b3879062a6
17 changed files with 313 additions and 55 deletions

View File

@@ -1,40 +1,34 @@
using AutoMapper; using AutoMapper;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1; using PARR.API.Contracts.V1;
using PARR.API.Contracts.V1.Requests; using PARR.API.Contracts.V1.Requests;
using PARR.API.Contracts.V1.Responses; using PARR.API.Contracts.V1.Responses;
using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Contracts.V1.Responses.Base;
using PARR.API.Controllers.V1.Base; using PARR.API.Controllers.V1.Base;
using PARR.API.Services.Interfaces; using PARR.API.Services.Interfaces;
using PARR.Core.Repositories.Interfaces; using PARR.Core.Services.RobotTaskRobotStatus.Interfaces;
using PARR.Domain.Common.Roles; using PARR.Domain.Common.Roles;
using PARR.Domain.Entities; using PARR.Domain.DTOs.RobotTaskRobotStatus;
using PARR.Domain.Entities.RobotEntities;
using PARR.Domain.Enums;
namespace PARR.API.Controllers.V1 namespace PARR.API.Controllers.V1
{ {
[Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)] [Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)]
public class RobotTaskRobotStatusController : BaseApiController public class RobotTaskRobotStatusController : BaseApiController
{ {
private readonly IRobotConfigurationRepository robotConfigurationService; private readonly IMapper _mapper;
private readonly IRobotHistoryRepository robotHistoryService; private readonly IClientService _clientService;
private readonly IMapper mapper; private readonly IRobotTaskRobotStatusService _robotTaskRobotStatusService;
private readonly IClientService clientService;
public RobotTaskRobotStatusController( public RobotTaskRobotStatusController(
IRobotConfigurationRepository robotConfigurationService,
IRobotHistoryRepository robotHistoryService,
IMapper mapper, IMapper mapper,
IClientService clientService IClientService clientService,
IRobotTaskRobotStatusService robotTaskRobotStatusService
) )
{ {
this.robotConfigurationService = robotConfigurationService; _mapper = mapper;
this.robotHistoryService = robotHistoryService; _clientService = clientService;
this.mapper = mapper; _robotTaskRobotStatusService = robotTaskRobotStatusService;
this.clientService = clientService;
} }
@@ -46,47 +40,58 @@ namespace PARR.API.Controllers.V1
[HttpPut(ApiRoutes.RobotTaskRobotStatus.ChangeRobotStatus)] [HttpPut(ApiRoutes.RobotTaskRobotStatus.ChangeRobotStatus)]
public async Task<IActionResult> ChangeStatus([FromRoute] Guid taskId, [FromBody] RobotTaskChangeRobotStatusRequest request) public async Task<IActionResult> ChangeStatus([FromRoute] Guid taskId, [FromBody] RobotTaskChangeRobotStatusRequest request)
{ {
var config = await robotConfigurationService.Get() #region Old
.FirstOrDefaultAsync(t => t.Id == taskId);
if (config == null) //var config = await _robotConfigurationRepository.Get()
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найдено задание с id: {taskId}" } })); // .FirstOrDefaultAsync(t => t.Id == taskId);
//изменение статуса робота //if (config == null)
robotConfigurationService.ChangeRobotStatus(request.RobotStatusCode, config); // return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найдено задание с id: {taskId}" } }));
//если успех, изменяем статус задания на успех ////изменение статуса робота
if (request.RobotStatusCode == RobotStatusEnum.Complete) //_robotConfigurationRepository.ChangeRobotStatus(request.RobotStatusCode, config);
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Ok, config);
if (!await robotConfigurationService.CommitAsync()) ////если успех, изменяем статус задания на успех
return BadRequest("Ошибка при изменении статуса работы робота."); //if (request.RobotStatusCode == RobotStatusEnum.Complete)
// _robotConfigurationRepository.ChangeTaskStatus(TaskStatusEnum.Ok, config);
//записываем в лог робота //if (!await _robotConfigurationRepository.CommitAsync())
if (request.RobotStatusCode == RobotStatusEnum.InProgress || request.RobotStatusCode == RobotStatusEnum.Complete) // return BadRequest("Ошибка при изменении статуса работы робота.");
{
var historyLevel = request.RobotStatusCode == RobotStatusEnum.InProgress ? RobotHistoryLevelEnum.Start : RobotHistoryLevelEnum.Complete;
var history = new RobotHistory ////записываем в лог робота
{ //if (request.RobotStatusCode == RobotStatusEnum.InProgress || request.RobotStatusCode == RobotStatusEnum.Complete)
Id = Guid.NewGuid(), //{
HistoryLevel = (int)historyLevel, // var historyLevel = request.RobotStatusCode == RobotStatusEnum.InProgress ? RobotHistoryLevelEnum.Start : RobotHistoryLevelEnum.Complete;
TaskStatusCode = config.TaskStatusCode,
RobotConfigurationId = config.Id,
RobotIp = clientService.GetClientIp()?.ToString(),
RobotId = request.RobotId
};
await robotHistoryService.CreateAsync(history);
await robotHistoryService.CommitAsync();
}
var configToResponse = await robotConfigurationService.Get() // var history = new RobotHistory
.Include(t => t.Robot) // {
.Include(t => t.TaskStatus) // Id = Guid.NewGuid(),
.Include(t => t.RobotStatus) // HistoryLevel = (int)historyLevel,
.FirstOrDefaultAsync(t => t.Id == taskId); // TaskStatusCode = config.TaskStatusCode,
// RobotConfigurationId = config.Id,
// RobotIp = _clientService.GetClientIp()?.ToString(),
// RobotId = request.RobotId
// };
// await _robotHistoryRepository.CreateAsync(history);
// await _robotHistoryRepository.CommitAsync();
//}
var response = mapper.Map<RobotConfigurationResponse>(configToResponse); //var configToResponse = await _robotConfigurationRepository.Get()
// .Include(t => t.Robot)
// .Include(t => t.TaskStatus)
// .Include(t => t.RobotStatus)
// .FirstOrDefaultAsync(t => t.Id == taskId);
//var response = _mapper.Map<RobotConfigurationResponse>(configToResponse);
//return Ok(new Response<RobotConfigurationResponse>(response, true));
#endregion
var changeRequest = new ChangeRobotStatus(taskId, request.RobotStatusCode, request.RobotId, _clientService.GetClientIp()?.ToString());
var result = await _robotTaskRobotStatusService.ChangeStatusAsync(changeRequest);
var response = _mapper.Map<RobotConfigurationResponse>(result);
return Ok(new Response<RobotConfigurationResponse>(response, true)); return Ok(new Response<RobotConfigurationResponse>(response, true));
} }

View File

@@ -8,6 +8,8 @@ using PARR.Domain.DTOs.Matching;
using PARR.Domain.DTOs.RobotMetrics; using PARR.Domain.DTOs.RobotMetrics;
using PARR.Domain.DTOs.RobotSnapshotDTO; using PARR.Domain.DTOs.RobotSnapshotDTO;
using PARR.Domain.DTOs.RobotTask; using PARR.Domain.DTOs.RobotTask;
using PARR.Domain.DTOs.RobotTaskRobotStatus;
using PARR.Domain.DTOs.Shared;
using PARR.Domain.DTOs.Shortcode; using PARR.Domain.DTOs.Shortcode;
using PARR.Domain.DTOs.TaskDTO; using PARR.Domain.DTOs.TaskDTO;
using PARR.Domain.DTOs.User; using PARR.Domain.DTOs.User;
@@ -258,10 +260,17 @@ namespace PARR.API.MappingProfiles
CreateMap<PARR.Domain.Entities.RobotEntities.TaskStatus, TaskStatusResponse>(); CreateMap<PARR.Domain.Entities.RobotEntities.TaskStatus, TaskStatusResponse>();
CreateMap<RobotConfiguration, RobotConfigurationResponse>() //CreateMap<RobotConfiguration, RobotConfigurationResponse>()
.ForMember(d => d.Robot, o => o.MapFrom(s => s.Robot)) // .ForMember(d => d.Robot, o => o.MapFrom(s => s.Robot))
.ForMember(d => d.TaskStatus, o => o.MapFrom(s => s.TaskStatus)) // .ForMember(d => d.TaskStatus, o => o.MapFrom(s => s.TaskStatus))
.ForMember(d => d.RobotStatus, o => o.MapFrom(s => s.RobotStatus)); // .ForMember(d => d.RobotStatus, o => o.MapFrom(s => s.RobotStatus));
CreateMap<RobotDto, RobotResponse>();
CreateMap<RobotTaskStatusDto, TaskStatusResponse>();
CreateMap<RobotStatusDto, RobotStatusResponse>();
CreateMap<RobotConfigurationResult, RobotConfigurationResponse>();
// === RobotConfiguration === // === RobotConfiguration ===
#endregion #endregion

View File

@@ -12,6 +12,8 @@ using PARR.Core.Services.RobotMetrics;
using PARR.Core.Services.RobotSnapshotServices; using PARR.Core.Services.RobotSnapshotServices;
using PARR.Core.Services.RobotTask.Implementations; using PARR.Core.Services.RobotTask.Implementations;
using PARR.Core.Services.RobotTask.Interfaces; using PARR.Core.Services.RobotTask.Interfaces;
using PARR.Core.Services.RobotTaskRobotStatus.Implemetations;
using PARR.Core.Services.RobotTaskRobotStatus.Interfaces;
using PARR.Core.Services.Shortcodes; using PARR.Core.Services.Shortcodes;
using PARR.Core.Services.Shortcodes.Handlers; using PARR.Core.Services.Shortcodes.Handlers;
using PARR.Core.Services.Snapshots.Implementations; using PARR.Core.Services.Snapshots.Implementations;
@@ -111,6 +113,7 @@ namespace PARR.Core
services.AddScoped<IRobotTaskService, RobotTaskService>(); services.AddScoped<IRobotTaskService, RobotTaskService>();
services.AddScoped<IRobotSnapshotService, RobotSnapshotService>(); services.AddScoped<IRobotSnapshotService, RobotSnapshotService>();
services.AddScoped<IRobotTaskRobotStatusService, RobotTaskRobotStatusService>();
services.AddScoped<IUnitService, UnitService>(); services.AddScoped<IUnitService, UnitService>();
services.AddScoped<UnitCacheService>(); services.AddScoped<UnitCacheService>();

View File

@@ -0,0 +1,17 @@
using AutoMapper;
using PARR.Domain.DTOs.RobotTaskRobotStatus;
using PARR.Domain.Entities;
namespace PARR.Core.Infrastructure.Mapping.RobotTaskRobotStatus
{
internal class RobotConfigurationResultMappingProfile : Profile
{
public RobotConfigurationResultMappingProfile()
{
CreateMap<RobotConfiguration, RobotConfigurationResult>()
.ForMember(d => d.Robot, o => o.MapFrom(s => s.Robot))
.ForMember(d => d.TaskStatus, o => o.MapFrom(s => s.TaskStatus))
.ForMember(d => d.RobotStatus, o => o.MapFrom(s => s.RobotStatus));
}
}
}

View File

@@ -0,0 +1,14 @@
using AutoMapper;
using PARR.Domain.DTOs.Shared;
using PARR.Domain.Entities.RobotEntities;
namespace PARR.Core.Infrastructure.Mapping.Shared
{
public class RobotDtoMappingProfile : Profile
{
public RobotDtoMappingProfile()
{
CreateMap<Robot, RobotDto>();
}
}
}

View File

@@ -0,0 +1,14 @@
using AutoMapper;
using PARR.Domain.DTOs.Shared;
using PARR.Domain.Entities.RobotEntities;
namespace PARR.Core.Infrastructure.Mapping.Shared
{
public class RobotStatusDtoMappingProfile : Profile
{
public RobotStatusDtoMappingProfile()
{
CreateMap<RobotStatus, RobotStatusDto>();
}
}
}

View File

@@ -0,0 +1,13 @@
using AutoMapper;
using PARR.Domain.DTOs.Shared;
namespace PARR.Core.Infrastructure.Mapping.Shared
{
public class RobotTaskStatusDtoMappingProfile : Profile
{
public RobotTaskStatusDtoMappingProfile()
{
CreateMap<PARR.Domain.Entities.RobotEntities.TaskStatus, RobotTaskStatusDto>();
}
}
}

View File

@@ -6,5 +6,6 @@ namespace PARR.Core.Repositories.Interfaces.TemplateRepositories
{ {
Task<bool> CreateAsync(TemplateRenamePending obj); Task<bool> CreateAsync(TemplateRenamePending obj);
IQueryable<TemplateRenamePending> Get(); IQueryable<TemplateRenamePending> Get();
void Remove(TemplateRenamePending obj);
} }
} }

View File

@@ -1,5 +1,4 @@
using AutoMapper; using AutoMapper;
using InfluxDB.Client.Api.Domain;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PARR.BLL.Helpers; using PARR.BLL.Helpers;
@@ -9,7 +8,6 @@ using PARR.Core.Services.NextRunServices;
using PARR.Core.Services.RobotTask.Interfaces; using PARR.Core.Services.RobotTask.Interfaces;
using PARR.Core.Services.RobotTask.Models; using PARR.Core.Services.RobotTask.Models;
using PARR.Core.Services.Shortcodes; using PARR.Core.Services.Shortcodes;
using PARR.Domain.Common.Template;
using PARR.Domain.DTOs.RobotTask; using PARR.Domain.DTOs.RobotTask;
using PARR.Domain.Entities; using PARR.Domain.Entities;
using PARR.Domain.Entities.Base.History; using PARR.Domain.Entities.Base.History;

View File

@@ -0,0 +1,98 @@
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.TemplateRepositories;
using PARR.Core.Services.RobotTaskRobotStatus.Interfaces;
using PARR.Domain.DTOs.RobotTaskRobotStatus;
using PARR.Domain.Entities.RobotEntities;
using PARR.Domain.Enums;
using PARR.Domain.Exceptions;
namespace PARR.Core.Services.RobotTaskRobotStatus.Implemetations
{
internal class RobotTaskRobotStatusService : IRobotTaskRobotStatusService
{
private readonly ILogger<RobotTaskRobotStatusService> _logger;
private readonly IRobotConfigurationRepository _robotConfigurationRepository;
private readonly IRobotHistoryRepository _robotHistoryRepository;
private readonly ITemplateRenamePendingRepository _templateRenamePendingRepository;
private readonly IMapper _mapper;
public RobotTaskRobotStatusService(
ILogger<RobotTaskRobotStatusService> logger,
IRobotConfigurationRepository robotConfigurationRepository,
IRobotHistoryRepository robotHistoryRepository,
ITemplateRenamePendingRepository templateRenamePendingRepository,
IMapper mapper
)
{
_logger = logger;
_robotConfigurationRepository = robotConfigurationRepository;
_robotHistoryRepository = robotHistoryRepository;
_templateRenamePendingRepository = templateRenamePendingRepository;
_mapper = mapper;
}
public async Task<RobotConfigurationResult> ChangeStatusAsync(ChangeRobotStatus request)
{
var config = await _robotConfigurationRepository.Get()
.FirstOrDefaultAsync(t => t.Id == request.TaskId);
if (config == null)
throw new NotFoundException($"Не найдено задание с id: {request.TaskId}");
// изменение статуса робота
_robotConfigurationRepository.ChangeRobotStatus(request.RobotStatusCode, config);
// если успех, изменяем статус задания на успех
if (request.RobotStatusCode == RobotStatusEnum.Complete)
{
_robotConfigurationRepository.ChangeTaskStatus(TaskStatusEnum.Ok, config);
// Тут нужно посмотреть, если этот шаблон был на переименование, удалить у него старое название, так как он успешно переименовался
if (config.RobotCode == (int)RobotsEnum.TemplateOrder)
{
var renaming = await _templateRenamePendingRepository.Get().FirstOrDefaultAsync(t => t.TemplateId == config.TemplateId);
if (renaming != null)
{
// Удаляем
_templateRenamePendingRepository.Remove(renaming);
_logger.LogInformation("Шаблон {TemplateId} успешно переименован. Запись TemplateRenamePending удалена.", config.TemplateId);
}
}
}
if (!await _robotConfigurationRepository.CommitAsync())
throw new DbErrorException("Ошибка при сохранении в БД");
//записываем в лог робота
if (request.RobotStatusCode == RobotStatusEnum.InProgress || request.RobotStatusCode == RobotStatusEnum.Complete)
{
var historyLevel = request.RobotStatusCode == RobotStatusEnum.InProgress ? RobotHistoryLevelEnum.Start : RobotHistoryLevelEnum.Complete;
var history = new RobotHistory
{
Id = Guid.NewGuid(),
HistoryLevel = (int)historyLevel,
TaskStatusCode = config.TaskStatusCode,
RobotConfigurationId = config.Id,
RobotIp = request.RobotIp,
RobotId = request.RobotId
};
await _robotHistoryRepository.CreateAsync(history);
await _robotHistoryRepository.CommitAsync();
}
var configToResponse = await _robotConfigurationRepository.Get()
.AsNoTracking()
.Include(t => t.Robot)
.Include(t => t.TaskStatus)
.Include(t => t.RobotStatus)
.FirstOrDefaultAsync(t => t.Id == request.TaskId);
return _mapper.Map<RobotConfigurationResult>(configToResponse);
}
}
}

View File

@@ -0,0 +1,17 @@
using PARR.Domain.DTOs.RobotTaskRobotStatus;
namespace PARR.Core.Services.RobotTaskRobotStatus.Interfaces
{
/// <summary>
/// Сервис по изменению статуса выполнения задания роботами
/// </summary>
public interface IRobotTaskRobotStatusService
{
/// <summary>
/// Изменить статус выполнения задания роботом по ИД задания
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<RobotConfigurationResult> ChangeStatusAsync(ChangeRobotStatus request);
}
}

View File

@@ -25,6 +25,12 @@ namespace PARR.DAL.Repositories.TemplateRepositories
return _dataContext.TemplateRenamePendings; return _dataContext.TemplateRenamePendings;
} }
public void Remove(TemplateRenamePending obj)
{
_dataContext.TemplateRenamePendings.Remove(obj);
}
public async Task<bool> CreateAsync(TemplateRenamePending obj) public async Task<bool> CreateAsync(TemplateRenamePending obj)
{ {
try try

View File

@@ -0,0 +1,13 @@
using PARR.Domain.Enums;
namespace PARR.Domain.DTOs.RobotTaskRobotStatus
{
/// <summary>
/// Изменить статус задания робота
/// </summary>
/// <param name="TaskId"></param>
/// <param name="RobotStatusCode"></param>
/// <param name="RobotId">Идентификатор робота</param>
/// <param name="RobotIp">IP адрес робота</param>
public record ChangeRobotStatus(Guid TaskId, RobotStatusEnum RobotStatusCode, string? RobotId, string? RobotIp);
}

View File

@@ -0,0 +1,23 @@
using PARR.Domain.DTOs.Shared;
namespace PARR.Domain.DTOs.RobotTaskRobotStatus
{
public record RobotConfigurationResult
{
public Guid Id { get; init; }
public DateTimeOffset DateCreated { get; init; }
public Guid TemplateId { get; init; }
public RobotDto? Robot { get; init; }
public RobotTaskStatusDto? TaskStatus { get; init; }
public RobotStatusDto? RobotStatus { get; init; }
public int AttemptsNumber { get; init; }
public DateTimeOffset? LastRobotStatusUpdated { get; init; }
}
}

View File

@@ -0,0 +1,9 @@
namespace PARR.Domain.DTOs.Shared
{
public record RobotDto
{
public int Code { get; init; }
public required string Name { get; init; }
public required string Description { get; init; }
}
}

View File

@@ -0,0 +1,9 @@
namespace PARR.Domain.DTOs.Shared
{
public record RobotStatusDto
{
public int Code { get; init; }
public string? Name { get; init; }
public string? Description { get; init; }
}
}

View File

@@ -0,0 +1,9 @@
namespace PARR.Domain.DTOs.Shared
{
public record RobotTaskStatusDto
{
public int Code { get; init; }
public required string Name { get; init; }
public required string Description { get; init; }
}
}