feat(api): Логика смены статусов для заданий робота. RobotTaskController

This commit is contained in:
Mikhail Trubnikov
2023-10-05 16:56:02 +10:00
parent 4679994600
commit af4a0b60b8
26 changed files with 2779 additions and 126 deletions

View File

@@ -97,6 +97,14 @@
public const string GetAll = Base + "/robot-history-levels/"; public const string GetAll = Base + "/robot-history-levels/";
} }
public static class RobotTask
{
public const string GetByRobotAndStatusTask = Base + "/robot-tasks/" + robotCode + "/statuses/" + taskStatusCode;
public const string robotCode = "{robotCode}";
public const string taskStatusCode = "{taskStatusCode}";
}
//public static class Layer //public static class Layer
//{ //{

View File

@@ -0,0 +1,7 @@
namespace PARR.API.Contracts.V1.Responses
{
public class RobotConfigurationResponse
{
}
}

View File

@@ -0,0 +1,7 @@
namespace PARR.API.Contracts.V1.Responses
{
public class RobotTaskScheduleResponse
{
//TODO:
}
}

View File

@@ -0,0 +1,31 @@
namespace PARR.API.Contracts.V1.Responses
{
public class RobotTaskTemplateResponse
{
public bool IsActive { get; set; }
public required string ClosingCode { get; set; }
public required string FullDescription { get; set; }
public required string ShortDescription { get; set; }
public required string Solution { get; set; }
public required string ResponseArea { get; set; }
public required string TemplateDuration { get; set; }
public required string Initiator { get; set; }
public required string WorkGroup { get; set; }
public required string Ek { get; set; }
public required string Name { get; set; }
public required string Category { get; set; }
public required string ProcessName { get; set; }
public required string ProcessEsppId { get; set; }
public required string SubprocessName { get; set; }
public required string SubprocessEsppId { get; set; }
public required string TnkName { get; set; }
public required string TnkEsppId { get; set; }
public required string WorkName { get; set; }
public required string WorkEsppId { get; set; }
}
}

View File

@@ -0,0 +1,109 @@
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1;
using PARR.API.Contracts.V1.Responses;
using PARR.API.Contracts.V1.Responses.Base;
using PARR.API.Controllers.V1.Base;
using PARR.DAL.Contracts;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
namespace PARR.API.Controllers.V1
{
public class RobotTaskController : BaseApiController
{
private readonly IMapper mapper;
private readonly SettingsFromDb settingsFromDb;
private readonly IRobotConfigurationService robotConfigurationService;
public RobotTaskController(
IMapper mapper,
SettingsFromDb settingsFromDb,
IRobotConfigurationService robotConfigurationService
)
{
this.mapper = mapper;
this.settingsFromDb = settingsFromDb;
this.robotConfigurationService = robotConfigurationService;
}
/// <summary>
/// Получить задание для робота по коду робота и по статусу задания
/// </summary>
/// <param name="robotCode"></param>
/// <param name="taskStatusCode"></param>
/// <returns></returns>
[HttpGet(ApiRoutes.RobotTask.GetByRobotAndStatusTask)]
public async Task<IActionResult> GetByRobotAndStatusTask([FromRoute] RobotsEnum robotCode, [FromRoute] TaskStatusEnum taskStatusCode)
{
//Ищем все задания с превышенным кол-вом попыток и с просроченным временем и ставим им статус ошибки
await robotConfigurationService.FindUnfulfilledTaskAndSetRobotErrorStatusAsync(settingsFromDb.RobotAttemptsNumber, settingsFromDb.RobotWaitTime);
var query = robotConfigurationService.Get()
.Where(t => t.RobotCode == (int)robotCode && t.TaskStatusCode == (int)taskStatusCode)
.AsSplitQuery();
switch (robotCode)
{
case RobotsEnum.TemplateOrder:
query = query.Include(t => t.Template)
.ThenInclude(t => t!.Host).ThenInclude(t => t!.ResponseArea)
.Include(t => t.Template)
.ThenInclude(t => t!.Host)
.Include(t => t.Template)
.ThenInclude(a => a!.ApplicationsInWork)
.ThenInclude(w => w!.Work)
.ThenInclude(t => t!.Tnk)
.ThenInclude(s => s!.Subprocess)
.ThenInclude(p => p!.Process);
break;
case RobotsEnum.ScheduleOrder:
//todo: include
break;
default:
break;
}
RobotConfiguration? task = null;
//ищем задание в ожидании, если нашли, выбираем ее
task = await query.FirstOrDefaultAsync(t => t.RobotStatusCode == (int)RobotStatusEnum.Wait);
if (task == null)
{
//ищем задания в работе, которые можно перезапустить
//Поиск пои `RobotStatusCode` = 22.
//Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`)
//и что текущая попытка не больше разрешенной(берется из настроек, поле `RobotAttemptsNumber`) - если это так, берется эта запись.
var endDate = DateTimeOffset.UtcNow.Add(-settingsFromDb.RobotWaitTime);
task = await query.FirstOrDefaultAsync(t =>
t.RobotStatusCode == (int)RobotStatusEnum.InProgress
&& t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber
&& t.LastStatusUpdated < endDate
);
}
if (task == null)
return NotFound();
//TODO: сделать маппинг!!!
switch (robotCode)
{
case RobotsEnum.TemplateOrder:
//RobotTaskTemplateResponse
return Ok(new Response<RobotTaskTemplateResponse>(mapper.Map<RobotTaskTemplateResponse>(task), true));
case RobotsEnum.ScheduleOrder:
//RobotTaskScheduleResponse
return Ok(new Response<RobotTaskScheduleResponse>(mapper.Map<RobotTaskScheduleResponse>(task), true));
default:
break;
}
return BadRequest();
}
}
}

View File

@@ -95,29 +95,31 @@ namespace PARR.API.Controllers.V1
if (request.IdSeries == null) if (request.IdSeries == null)
request.IdSeries = Guid.NewGuid(); request.IdSeries = Guid.NewGuid();
var historyItem = new RobotTemplateHistory //var historyItem = new RobotTemplateHistory
{ //{
Id = Guid.NewGuid(), // Id = Guid.NewGuid(),
HistoryLevel = request.HistoryLevel, // HistoryLevel = request.HistoryLevel,
RobotMessage = request.RobotMessage, // RobotMessage = request.RobotMessage,
EsppMessage = request.EsppMessage, // EsppMessage = request.EsppMessage,
TemplateId = request.TemplateId, // TemplateId = request.TemplateId,
TemplateStatusCode = template.StatusCode, // TemplateStatusCode = template.StatusCode,
IdSeries = request.IdSeries.Value // IdSeries = request.IdSeries.Value
}; //};
if (!await historyService.CreateAsync(historyItem) || !await historyService.CommitAsync()) //if (!await historyService.CreateAsync(historyItem) || !await historyService.CommitAsync())
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при добавлении записи в историю" } })); // return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при добавлении записи в историю" } }));
var createdObj = await historyService.Get() //var createdObj = await historyService.Get()
.Include(t => t.Template) // .Include(t => t.Template)
.Include(t => t.RobotHistoryLevel) // .Include(t => t.RobotHistoryLevel)
.FirstOrDefaultAsync(t => t.Id == historyItem.Id); // .FirstOrDefaultAsync(t => t.Id == historyItem.Id);
var response = mapper.Map<RobotTemplateHistoryResponse>(createdObj); //var response = mapper.Map<RobotTemplateHistoryResponse>(createdObj);
var createdUri = uriService.GetAllUri(ApiRoutes.RobotTemplateHistory.GetAll) + $"?{nameof(RobotTemplateHistoryQuery.IdSeries)}={createdObj!.IdSeries}"; //var createdUri = uriService.GetAllUri(ApiRoutes.RobotTemplateHistory.GetAll) + $"?{nameof(RobotTemplateHistoryQuery.IdSeries)}={createdObj!.IdSeries}";
return Created(createdUri, new Response<RobotTemplateHistoryResponse>(response, true)); //return Created(createdUri, new Response<RobotTemplateHistoryResponse>(response, true));
return Ok("Доделать");
} }
} }

View File

@@ -51,14 +51,14 @@ namespace PARR.API.Controllers.V1
{ {
switch (filter.StatusCode.Value) switch (filter.StatusCode.Value)
{ {
case (int)StatusTemplateEnum.Ok: case (int)TaskStatusEnum.Ok:
query = query.Where(t => t.StatusCode == (int)StatusTemplateEnum.Ok); query = query.Where(t => t.StatusCode == (int)TaskStatusEnum.Ok);
break; break;
case (int)StatusTemplateEnum.Creating: case (int)TaskStatusEnum.Creating:
query = query.Where(t => t.StatusCode == (int)StatusTemplateEnum.Creating); query = query.Where(t => t.StatusCode == (int)TaskStatusEnum.Creating);
break; break;
case (int)StatusTemplateEnum.Updating: case (int)TaskStatusEnum.Updating:
query = query.Where(t => t.StatusCode == (int)StatusTemplateEnum.Updating); query = query.Where(t => t.StatusCode == (int)TaskStatusEnum.Updating);
break; break;
default: default:
break; break;
@@ -170,7 +170,7 @@ namespace PARR.API.Controllers.V1
if (template == null) if (template == null)
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден шаблон с id: {id}" } })); return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден шаблон с id: {id}" } }));
template.StatusCode = (int)StatusTemplateEnum.Ok; template.StatusCode = (int)TaskStatusEnum.Ok;
if (!await templateService.CommitAsync()) if (!await templateService.CommitAsync())
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при изменении статуса у шаблона с id: {id}" } })); return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при изменении статуса у шаблона с id: {id}" } }));

View File

@@ -63,7 +63,7 @@ namespace PARR.API.MappingProfiles
// === Template === // === Template ===
CreateMap<StatusTask, StatusTemplateResponse>(); CreateMap<DAL.Models.TaskStatus, StatusTemplateResponse>();
CreateMap<Template, TemplateRobotStatusResponse>() CreateMap<Template, TemplateRobotStatusResponse>()
.ForMember(d => d.Code, o => o.MapFrom(s => s.RobotStatus!.Code)) .ForMember(d => d.Code, o => o.MapFrom(s => s.RobotStatus!.Code))

View File

@@ -33,7 +33,7 @@ namespace PARR.DAL.Context
public DbSet<Models.AIHIT.Setting> Settings { get; set; } public DbSet<Models.AIHIT.Setting> Settings { get; set; }
public DbSet<RawDataEK> RawDataEKs { get; set; } public DbSet<RawDataEK> RawDataEKs { get; set; }
public DbSet<Template> Templates { get; set; } public DbSet<Template> Templates { get; set; }
public DbSet<StatusTask> StatusTasks { get; set; } public DbSet<Models.TaskStatus> TaskStatuses { get; set; }
public DbSet<RobotStatus> RobotStatuses { get; set; } public DbSet<RobotStatus> RobotStatuses { get; set; }
public DbSet<Process> Processes { get; set; } public DbSet<Process> Processes { get; set; }
@@ -111,13 +111,13 @@ namespace PARR.DAL.Context
}); });
modelBuilder.Entity<StatusTask>(f => modelBuilder.Entity<Models.TaskStatus>(f =>
{ {
f.HasData( f.HasData(
new() { Code = (int)StatusTemplateEnum.Creating, Name = StatusTemplateEnum.Creating.ToString(), Description = "Создали шаблон в ПАРР" }, new() { Code = (int)TaskStatusEnum.Creating, Name = TaskStatusEnum.Creating.ToString(), Description = "Задание на создание объекта в ЕСПП" },
new() { Code = (int)StatusTemplateEnum.Updating, Name = StatusTemplateEnum.Updating.ToString(), Description = "Выявлено несоответствие. Требуется привлечение робота" }, new() { Code = (int)TaskStatusEnum.Updating, Name = TaskStatusEnum.Updating.ToString(), Description = "Задание на обновление объекта в ЕСПП" },
new() { Code = (int)StatusTemplateEnum.Ok, Name = StatusTemplateEnum.Ok.ToString(), Description = "Нормальное состояние шаблона в ЕСПП и ПАРР. Шаблон в ПАРР соответствует шаблону в ЕСПП" } new() { Code = (int)TaskStatusEnum.Ok, Name = TaskStatusEnum.Ok.ToString(), Description = "Объект в ЕСПП соответствует объекту в ПАРР" }
); );
}); });

View File

@@ -1,6 +1,6 @@
namespace PARR.DAL.Contracts namespace PARR.DAL.Contracts
{ {
public enum StatusTemplateEnum public enum TaskStatusEnum
{ {
Creating = 10, Creating = 10,
Updating = 20, Updating = 20,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,268 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace PARR.DAL.Migrations
{
/// <inheritdoc />
public partial class TblUpdTemplates : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_RobotConfigurations_StatusTemplates_TaskStatusCode",
table: "RobotConfigurations");
migrationBuilder.DropForeignKey(
name: "FK_RobotHistories_StatusTemplates_TaskStatusCode",
table: "RobotHistories");
migrationBuilder.DropForeignKey(
name: "FK_RobotTemplateHistories_StatusTemplates_TemplateStatusCode",
table: "RobotTemplateHistories");
migrationBuilder.DropForeignKey(
name: "FK_Templates_RobotStatuses_RobotStatusCode",
table: "Templates");
migrationBuilder.DropForeignKey(
name: "FK_Templates_StatusTemplates_StatusCode",
table: "Templates");
migrationBuilder.DropTable(
name: "StatusTemplates");
migrationBuilder.RenameColumn(
name: "IsActive",
table: "Templates",
newName: "IsActiveTemplate");
migrationBuilder.AlterColumn<int>(
name: "StatusCode",
table: "Templates",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<int>(
name: "RobotStatusCode",
table: "Templates",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<int>(
name: "RobotAttemptsNumber",
table: "Templates",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AddColumn<bool>(
name: "IsActiveSchedule",
table: "Templates",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "TaskStatuses",
columns: table => new
{
Code = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TaskStatuses", x => x.Code);
});
migrationBuilder.InsertData(
table: "TaskStatuses",
columns: new[] { "Code", "Description", "Name" },
values: new object[,]
{
{ 10, "Задание на создание объекта в ЕСПП", "Creating" },
{ 20, "Задание на обновление объекта в ЕСПП", "Updating" },
{ 30, "Объект в ЕСПП соответствует объекту в ПАРР", "Ok" }
});
migrationBuilder.AddForeignKey(
name: "FK_RobotConfigurations_TaskStatuses_TaskStatusCode",
table: "RobotConfigurations",
column: "TaskStatusCode",
principalTable: "TaskStatuses",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_RobotHistories_TaskStatuses_TaskStatusCode",
table: "RobotHistories",
column: "TaskStatusCode",
principalTable: "TaskStatuses",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_RobotTemplateHistories_TaskStatuses_TemplateStatusCode",
table: "RobotTemplateHistories",
column: "TemplateStatusCode",
principalTable: "TaskStatuses",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Templates_RobotStatuses_RobotStatusCode",
table: "Templates",
column: "RobotStatusCode",
principalTable: "RobotStatuses",
principalColumn: "Code");
migrationBuilder.AddForeignKey(
name: "FK_Templates_TaskStatuses_StatusCode",
table: "Templates",
column: "StatusCode",
principalTable: "TaskStatuses",
principalColumn: "Code");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_RobotConfigurations_TaskStatuses_TaskStatusCode",
table: "RobotConfigurations");
migrationBuilder.DropForeignKey(
name: "FK_RobotHistories_TaskStatuses_TaskStatusCode",
table: "RobotHistories");
migrationBuilder.DropForeignKey(
name: "FK_RobotTemplateHistories_TaskStatuses_TemplateStatusCode",
table: "RobotTemplateHistories");
migrationBuilder.DropForeignKey(
name: "FK_Templates_RobotStatuses_RobotStatusCode",
table: "Templates");
migrationBuilder.DropForeignKey(
name: "FK_Templates_TaskStatuses_StatusCode",
table: "Templates");
migrationBuilder.DropTable(
name: "TaskStatuses");
migrationBuilder.DropColumn(
name: "IsActiveSchedule",
table: "Templates");
migrationBuilder.RenameColumn(
name: "IsActiveTemplate",
table: "Templates",
newName: "IsActive");
migrationBuilder.AlterColumn<int>(
name: "StatusCode",
table: "Templates",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "RobotStatusCode",
table: "Templates",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "RobotAttemptsNumber",
table: "Templates",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.CreateTable(
name: "StatusTemplates",
columns: table => new
{
Code = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Description = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_StatusTemplates", x => x.Code);
});
migrationBuilder.InsertData(
table: "StatusTemplates",
columns: new[] { "Code", "Description", "Name" },
values: new object[,]
{
{ 10, "Создали шаблон в ПАРР", "Creating" },
{ 20, "Выявлено несоответствие. Требуется привлечение робота", "Updating" },
{ 30, "Нормальное состояние шаблона в ЕСПП и ПАРР. Шаблон в ПАРР соответствует шаблону в ЕСПП", "Ok" }
});
migrationBuilder.AddForeignKey(
name: "FK_RobotConfigurations_StatusTemplates_TaskStatusCode",
table: "RobotConfigurations",
column: "TaskStatusCode",
principalTable: "StatusTemplates",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_RobotHistories_StatusTemplates_TaskStatusCode",
table: "RobotHistories",
column: "TaskStatusCode",
principalTable: "StatusTemplates",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_RobotTemplateHistories_StatusTemplates_TemplateStatusCode",
table: "RobotTemplateHistories",
column: "TemplateStatusCode",
principalTable: "StatusTemplates",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Templates_RobotStatuses_RobotStatusCode",
table: "Templates",
column: "RobotStatusCode",
principalTable: "RobotStatuses",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Templates_StatusTemplates_StatusCode",
table: "Templates",
column: "StatusCode",
principalTable: "StatusTemplates",
principalColumn: "Code",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@@ -1049,47 +1049,6 @@ namespace PARR.DAL.Migrations
}); });
}); });
modelBuilder.Entity("PARR.DAL.Models.StatusTask", b =>
{
b.Property<int>("Code")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Code"));
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Code");
b.ToTable("StatusTemplates");
b.HasData(
new
{
Code = 10,
Description = "Создали шаблон в ПАРР",
Name = "Creating"
},
new
{
Code = 20,
Description = "Выявлено несоответствие. Требуется привлечение робота",
Name = "Updating"
},
new
{
Code = 30,
Description = "Нормальное состояние шаблона в ЕСПП и ПАРР. Шаблон в ПАРР соответствует шаблону в ЕСПП",
Name = "Ok"
});
});
modelBuilder.Entity("PARR.DAL.Models.Subprocess", b => modelBuilder.Entity("PARR.DAL.Models.Subprocess", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@@ -1119,6 +1078,47 @@ namespace PARR.DAL.Migrations
b.ToTable("Subprocesses"); b.ToTable("Subprocesses");
}); });
modelBuilder.Entity("PARR.DAL.Models.TaskStatus", b =>
{
b.Property<int>("Code")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Code"));
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Code");
b.ToTable("TaskStatuses");
b.HasData(
new
{
Code = 10,
Description = "Задание на создание объекта в ЕСПП",
Name = "Creating"
},
new
{
Code = 20,
Description = "Задание на обновление объекта в ЕСПП",
Name = "Updating"
},
new
{
Code = 30,
Description = "Объект в ЕСПП соответствует объекту в ПАРР",
Name = "Ok"
});
});
modelBuilder.Entity("PARR.DAL.Models.Template", b => modelBuilder.Entity("PARR.DAL.Models.Template", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@@ -1137,23 +1137,26 @@ namespace PARR.DAL.Migrations
b.Property<Guid>("HostId") b.Property<Guid>("HostId")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<bool>("IsActive") b.Property<bool>("IsActiveSchedule")
.HasColumnType("boolean");
b.Property<bool>("IsActiveTemplate")
.HasColumnType("boolean"); .HasColumnType("boolean");
b.Property<string>("Name") b.Property<string>("Name")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<int>("RobotAttemptsNumber") b.Property<int?>("RobotAttemptsNumber")
.HasColumnType("integer"); .HasColumnType("integer");
b.Property<DateTimeOffset?>("RobotLastStatusUpdated") b.Property<DateTimeOffset?>("RobotLastStatusUpdated")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
b.Property<int>("RobotStatusCode") b.Property<int?>("RobotStatusCode")
.HasColumnType("integer"); .HasColumnType("integer");
b.Property<int>("StatusCode") b.Property<int?>("StatusCode")
.HasColumnType("integer"); .HasColumnType("integer");
b.HasKey("Id"); b.HasKey("Id");
@@ -1659,7 +1662,7 @@ namespace PARR.DAL.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("PARR.DAL.Models.StatusTask", "StatusTask") b.HasOne("PARR.DAL.Models.TaskStatus", "StatusTask")
.WithMany() .WithMany()
.HasForeignKey("TaskStatusCode") .HasForeignKey("TaskStatusCode")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@@ -1694,7 +1697,7 @@ namespace PARR.DAL.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("PARR.DAL.Models.StatusTask", "StatusTask") b.HasOne("PARR.DAL.Models.TaskStatus", "StatusTask")
.WithMany() .WithMany()
.HasForeignKey("TaskStatusCode") .HasForeignKey("TaskStatusCode")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@@ -1721,7 +1724,7 @@ namespace PARR.DAL.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("PARR.DAL.Models.StatusTask", "StatusTemplate") b.HasOne("PARR.DAL.Models.TaskStatus", "StatusTemplate")
.WithMany("RobotTemplateHistories") .WithMany("RobotTemplateHistories")
.HasForeignKey("TemplateStatusCode") .HasForeignKey("TemplateStatusCode")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@@ -1761,15 +1764,11 @@ namespace PARR.DAL.Migrations
b.HasOne("PARR.DAL.Models.RobotStatus", "RobotStatus") b.HasOne("PARR.DAL.Models.RobotStatus", "RobotStatus")
.WithMany("Templates") .WithMany("Templates")
.HasForeignKey("RobotStatusCode") .HasForeignKey("RobotStatusCode");
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PARR.DAL.Models.StatusTask", "StatusTask") b.HasOne("PARR.DAL.Models.TaskStatus", "StatusTask")
.WithMany("Templates") .WithMany("Templates")
.HasForeignKey("StatusCode") .HasForeignKey("StatusCode");
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ApplicationsInWork"); b.Navigation("ApplicationsInWork");
@@ -1962,18 +1961,18 @@ namespace PARR.DAL.Migrations
b.Navigation("Templates"); b.Navigation("Templates");
}); });
modelBuilder.Entity("PARR.DAL.Models.StatusTask", b => modelBuilder.Entity("PARR.DAL.Models.Subprocess", b =>
{
b.Navigation("Tnks");
});
modelBuilder.Entity("PARR.DAL.Models.TaskStatus", b =>
{ {
b.Navigation("RobotTemplateHistories"); b.Navigation("RobotTemplateHistories");
b.Navigation("Templates"); b.Navigation("Templates");
}); });
modelBuilder.Entity("PARR.DAL.Models.Subprocess", b =>
{
b.Navigation("Tnks");
});
modelBuilder.Entity("PARR.DAL.Models.Template", b => modelBuilder.Entity("PARR.DAL.Models.Template", b =>
{ {
b.Navigation("RobotConfigurations"); b.Navigation("RobotConfigurations");

View File

@@ -51,7 +51,7 @@ namespace PARR.DAL.Models
public Robot? Robot { get; set; } public Robot? Robot { get; set; }
[ForeignKey(nameof(TaskStatusCode))] [ForeignKey(nameof(TaskStatusCode))]
public StatusTask? StatusTask { get; set; } public TaskStatus? StatusTask { get; set; }
[ForeignKey(nameof(RobotStatusCode))] [ForeignKey(nameof(RobotStatusCode))]
public RobotStatus? RobotStatus { get; set; } public RobotStatus? RobotStatus { get; set; }

View File

@@ -39,6 +39,6 @@ namespace PARR.DAL.Models
public RobotConfiguration? RobotConfiguration { get; set; } public RobotConfiguration? RobotConfiguration { get; set; }
[ForeignKey(nameof(TaskStatusCode))] [ForeignKey(nameof(TaskStatusCode))]
public StatusTask? StatusTask { get; set; } public TaskStatus? StatusTask { get; set; }
} }
} }

View File

@@ -41,6 +41,6 @@ namespace PARR.DAL.Models
public Template? Template { get; set; } public Template? Template { get; set; }
[ForeignKey(nameof(TemplateStatusCode))] [ForeignKey(nameof(TemplateStatusCode))]
public StatusTask? StatusTemplate { get; set; } public TaskStatus? StatusTemplate { get; set; }
} }
} }

View File

@@ -6,8 +6,8 @@ namespace PARR.DAL.Models
/// <summary> /// <summary>
/// Статус шаблона. Что нужно сделать роботу в ЕСПП /// Статус шаблона. Что нужно сделать роботу в ЕСПП
/// </summary> /// </summary>
[Table("StatusTemplates")] [Table("TaskStatuses")]
public class StatusTask public class TaskStatus
{ {
[Key] [Key]
public int Code { get; set; } public int Code { get; set; }

View File

@@ -18,22 +18,26 @@ namespace PARR.DAL.Models
public required string Name { get; set; } public required string Name { get; set; }
public bool IsActive { get; set; } public bool IsActiveTemplate { get; set; }
public bool IsActiveSchedule { get; set; }
#region удалить эти поля
/// <summary> /// <summary>
/// Статус шаблона. Что нужно сделать роботу в ЕСПП /// Статус шаблона. Что нужно сделать роботу в ЕСПП
/// </summary> /// </summary>
public int StatusCode { get; set; } public int? StatusCode { get; set; }
/// <summary> /// <summary>
/// Статус работы робота /// Статус работы робота
/// </summary> /// </summary>
public int RobotStatusCode { get; set; } public int? RobotStatusCode { get; set; }
/// <summary> /// <summary>
/// Количество попыток выполнения задания роботом /// Количество попыток выполнения задания роботом
/// </summary> /// </summary>
public int RobotAttemptsNumber { get; set; } public int? RobotAttemptsNumber { get; set; }
/// <summary> /// <summary>
/// Последняя дата обновления статуса RobotStatusCode роботом /// Последняя дата обновления статуса RobotStatusCode роботом
@@ -42,7 +46,14 @@ namespace PARR.DAL.Models
[ForeignKey(nameof(StatusCode))] [ForeignKey(nameof(StatusCode))]
public StatusTask? StatusTask { get; set; } public TaskStatus? StatusTask { get; set; }
[ForeignKey(nameof(RobotStatusCode))]
public RobotStatus? RobotStatus { get; set; }
public ICollection<RobotTemplateHistory> RobotTemplateHistories { get; set; } = new HashSet<RobotTemplateHistory>();
#endregion
public Guid ApplicationInWorkId { get; set; } public Guid ApplicationInWorkId { get; set; }
@@ -56,12 +67,6 @@ namespace PARR.DAL.Models
[ForeignKey(nameof(HostId))] [ForeignKey(nameof(HostId))]
public Host? Host { get; set; } public Host? Host { get; set; }
[ForeignKey(nameof(RobotStatusCode))]
public RobotStatus? RobotStatus { get; set; }
public ICollection<RobotTemplateHistory> RobotTemplateHistories { get; set; } = new HashSet<RobotTemplateHistory>();
public ICollection<RobotConfiguration> RobotConfigurations { get; set; } = new HashSet<RobotConfiguration>(); public ICollection<RobotConfiguration> RobotConfigurations { get; set; } = new HashSet<RobotConfiguration>();
} }

View File

@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using PARR.DAL.Context; using PARR.DAL.Context;
using PARR.DAL.Contracts;
using PARR.DAL.Models; using PARR.DAL.Models;
using PARR.DAL.Services.Abstracts; using PARR.DAL.Services.Abstracts;
using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces;
@@ -10,14 +11,107 @@ namespace PARR.DAL.Services.Implementations
internal class RobotConfigurationService : BaseService<RobotConfiguration>, IRobotConfigurationService internal class RobotConfigurationService : BaseService<RobotConfiguration>, IRobotConfigurationService
{ {
private readonly DataContext dataContext; private readonly DataContext dataContext;
private readonly ILogger<RobotConfigurationService> logger;
public RobotConfigurationService(DataContext dataContext, ILogger<RobotConfigurationService> logger) : base(logger) public RobotConfigurationService(DataContext dataContext, ILogger<RobotConfigurationService> logger) : base(logger)
{ {
this.dataContext = dataContext; this.dataContext = dataContext;
this.logger = logger;
} }
protected override DbSet<RobotConfiguration> EntitySet => dataContext.RobotConfigurations; protected override DbSet<RobotConfiguration> EntitySet => dataContext.RobotConfigurations;
protected override DataContext EntitiContext => dataContext; protected override DataContext EntitiContext => dataContext;
public void ChangeTaskStatus(TaskStatusEnum taskStatus, ref RobotConfiguration configuration)
{
configuration.TaskStatusCode = (int)taskStatus;
switch (taskStatus)
{
case TaskStatusEnum.Creating:
ChangeRobotStatus(RobotStatusEnum.Wait, ref configuration);
break;
case TaskStatusEnum.Updating:
ChangeRobotStatus(RobotStatusEnum.Wait, ref configuration);
break;
case TaskStatusEnum.Ok:
break;
default:
break;
}
}
public void ChangeRobotStatus(RobotStatusEnum robotStatus, ref RobotConfiguration configuration)
{
configuration.RobotStatusCode = (int)robotStatus;
switch (robotStatus)
{
case RobotStatusEnum.InProgress:
configuration.AttemptsNumber++;
configuration.LastStatusUpdated = DateTimeOffset.UtcNow;
break;
//case RobotStatusEnum.Error:
// break;
case RobotStatusEnum.Complete:
configuration.LastStatusUpdated = DateTimeOffset.UtcNow;
break;
case RobotStatusEnum.Wait:
configuration.LastStatusUpdated = null;
configuration.AttemptsNumber = 0;
break;
default:
break;
}
}
public RobotConfiguration GetFromTemplateByRobotCode(RobotsEnum robotsEnum, ref Template template)
{
var config = template.RobotConfigurations.FirstOrDefault(t => t.RobotCode == (int)robotsEnum);
if (config == null)
{
logger.LogError($"У шаблона нет конфигурации роботов. TemplateId: {template.Id}");
throw new Exception($"У шаблона нет конфигурации роботов. TemplateId: {template.Id}");
}
return config;
}
// Поиск невыполненных заданий и установка им статуса ошибки робота
public async Task FindUnfulfilledTaskAndSetRobotErrorStatusAsync(int robotAttemptsNumber, TimeSpan robotWaitTime)
{
//Ищем `RobotStatusCode` = 22 и `LastStatusUpdated` истекло и `AttemptsNumber` >= допустимого значения из настроек,
//ставим всем этим записям `RobotStatusCode`= 33
var endDate = DateTimeOffset.UtcNow.Add(-robotWaitTime);
var configObjs = await EntitySet.Where(t =>
t.RobotStatusCode == (int)RobotStatusEnum.InProgress
&& t.AttemptsNumber >= robotAttemptsNumber
&& t.LastStatusUpdated <= endDate
).ToListAsync();
if (!configObjs.Any())
return;
configObjs.ForEach(item =>
{
ChangeRobotStatus(RobotStatusEnum.Error, ref item);
logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}");
});
var result = await CommitAsync();
if (!result)
logger.LogError($"Ошибка при сохранении изменений RobotStatus для RobotConfigurationId: item.Id, RobotStatus: {RobotStatusEnum.Error}");
}
} }
} }

View File

@@ -13,9 +13,9 @@ namespace PARR.DAL.Services.Implementations
this.dataContext = dataContext; this.dataContext = dataContext;
} }
public IQueryable<StatusTask> Get() public IQueryable<Models.TaskStatus> Get()
{ {
return dataContext.StatusTasks; return dataContext.TaskStatuses;
} }
} }
} }

View File

@@ -43,6 +43,8 @@ namespace PARR.DAL.Services.Implementations
.ThenInclude(p => p!.Process); .ThenInclude(p => p!.Process);
} }
//TODO: удалить этот метод
#region delete
public async Task CheckAndSetErrorRobotStatusAsync(int robotAttemptsNumber, TimeSpan robotWaitTime) public async Task CheckAndSetErrorRobotStatusAsync(int robotAttemptsNumber, TimeSpan robotWaitTime)
{ {
//Ищем `RobotStatusCode` = 22 и `RobotLastStatusUpdated` истекло и `RobotAttemptsNumber` >= допустимого значения из настроек, //Ищем `RobotStatusCode` = 22 и `RobotLastStatusUpdated` истекло и `RobotAttemptsNumber` >= допустимого значения из настроек,
@@ -74,6 +76,7 @@ namespace PARR.DAL.Services.Implementations
logger.LogError($"Ошибка при сохранении изменений RobotStatus у шаблонов на {RobotStatusEnum.Error}"); logger.LogError($"Ошибка при сохранении изменений RobotStatus у шаблонов на {RobotStatusEnum.Error}");
} }
#endregion
public void ChangeRobotStatus(RobotStatusEnum status, ref Template template) public void ChangeRobotStatus(RobotStatusEnum status, ref Template template)
{ {
@@ -98,5 +101,39 @@ namespace PARR.DAL.Services.Implementations
break; break;
} }
} }
public override Task<bool> CreateAsync(Template obj)
{
// добавление роботов для шаблона
obj.RobotConfigurations = new List<RobotConfiguration>
{
// робот по управлению шаблоном
new RobotConfiguration
{
Id = Guid.NewGuid(),
DateCreated = DateTimeOffset.UtcNow,
TemplateId = obj.Id,
RobotCode = (int)RobotsEnum.TemplateOrder,
TaskStatusCode = (int)TaskStatusEnum.Creating,
RobotStatusCode = (int)RobotStatusEnum.Wait,
AttemptsNumber = 0,
LastStatusUpdated = null
},
// робот по управлениею расписанием
new RobotConfiguration
{
Id = Guid.NewGuid(),
DateCreated = DateTimeOffset.UtcNow,
TemplateId = obj.Id,
RobotCode = (int)RobotsEnum.ScheduleOrder,
TaskStatusCode = (int)TaskStatusEnum.Creating,
RobotStatusCode = (int)RobotStatusEnum.Wait,
AttemptsNumber = 0,
LastStatusUpdated = null
}
};
return base.CreateAsync(obj);
}
} }
} }

View File

@@ -1,9 +1,39 @@
using PARR.DAL.Models; using PARR.DAL.Contracts;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces.Base; using PARR.DAL.Services.Interfaces.Base;
namespace PARR.DAL.Services.Interfaces namespace PARR.DAL.Services.Interfaces
{ {
public interface IRobotConfigurationService : IBaseService<RobotConfiguration> public interface IRobotConfigurationService : IBaseService<RobotConfiguration>
{ {
/// <summary>
/// Обновить статус работы робота
/// </summary>
/// <param name="robotStatus"></param>
/// <param name="configuration"></param>
void ChangeRobotStatus(RobotStatusEnum robotStatus, ref RobotConfiguration configuration);
/// <summary>
/// Обновить статус задания (автоматически обновляется статус робота)
/// </summary>
/// <param name="taskStatus"></param>
/// <param name="configuration"></param>
void ChangeTaskStatus(TaskStatusEnum taskStatus, ref RobotConfiguration configuration);
/// <summary>
/// Поиск невыполненных заданий и установка им статуса ошибки робота
/// </summary>
/// <param name="robotAttemptsNumber"></param>
/// <param name="robotWaitTime"></param>
/// <returns></returns>
Task FindUnfulfilledTaskAndSetRobotErrorStatusAsync(int robotAttemptsNumber, TimeSpan robotWaitTime);
/// <summary>
/// Получить конфигурацию из шаблона по RobotCode. У шаблона обязательно должен быть Include таблицы RobotConfiguration
/// </summary>
/// <param name="robotsEnum"></param>
/// <param name="template"></param>
/// <returns></returns>
RobotConfiguration GetFromTemplateByRobotCode(RobotsEnum robotsEnum, ref Template template);
} }
} }

View File

@@ -4,6 +4,6 @@ namespace PARR.DAL.Services.Interfaces
{ {
public interface IStatusTemplateService public interface IStatusTemplateService
{ {
IQueryable<StatusTask> Get(); IQueryable<Models.TaskStatus> Get();
} }
} }

View File

@@ -11,7 +11,7 @@ namespace PARR.EsppTemplateSync.MappingProfiles
CreateMap<Template, EsppTemplate>() CreateMap<Template, EsppTemplate>()
.ForMember(d => d.Name, o => o.MapFrom(s => s.Name)) .ForMember(d => d.Name, o => o.MapFrom(s => s.Name))
.ForMember(d => d.IsActive, o => o.MapFrom(s => s.IsActive)) .ForMember(d => d.IsActive, o => o.MapFrom(s => s.IsActiveTemplate))
.ForMember(d => d.WorkGroup, o => o.MapFrom(s => s.Host!.WorkGroup)) .ForMember(d => d.WorkGroup, o => o.MapFrom(s => s.Host!.WorkGroup))
.ForMember(d => d.ShortDescription, o => o.MapFrom(s => s.ApplicationsInWork!.ShortDescription)) .ForMember(d => d.ShortDescription, o => o.MapFrom(s => s.ApplicationsInWork!.ShortDescription))
.ForMember(d => d.ResponseArea, o => o.MapFrom(s => s.ApplicationsInWork!.ShortDescription)) .ForMember(d => d.ResponseArea, o => o.MapFrom(s => s.ApplicationsInWork!.ShortDescription))

View File

@@ -126,7 +126,7 @@ namespace PARR.EsppTemplateSync.Services
private void SetUpdateStatus(ITemplateService templateService, Template template) private void SetUpdateStatus(ITemplateService templateService, Template template)
{ {
template.StatusCode = (int)StatusTemplateEnum.Updating; template.StatusCode = (int)TaskStatusEnum.Updating;
templateService.ChangeRobotStatus(RobotStatusEnum.Wait, ref template); templateService.ChangeRobotStatus(RobotStatusEnum.Wait, ref template);
} }

View File

@@ -15,18 +15,21 @@ namespace PARR.GeneratorTemplates.Services
private readonly IHostService hostService; private readonly IHostService hostService;
private readonly ILogger<TemplateManager> logger; private readonly ILogger<TemplateManager> logger;
private readonly SettingsFromDb settingsFromDb; private readonly SettingsFromDb settingsFromDb;
private readonly IRobotConfigurationService robotConfigurationService;
public TemplateManager( public TemplateManager(
ITemplateService templateService, ITemplateService templateService,
IHostService hostService, IHostService hostService,
ILogger<TemplateManager> logger, ILogger<TemplateManager> logger,
SettingsFromDb settingsFromDb SettingsFromDb settingsFromDb,
IRobotConfigurationService robotConfigurationService
) )
{ {
this.templateService = templateService; this.templateService = templateService;
this.hostService = hostService; this.hostService = hostService;
this.logger = logger; this.logger = logger;
this.settingsFromDb = settingsFromDb; this.settingsFromDb = settingsFromDb;
this.robotConfigurationService = robotConfigurationService;
} }
public async Task CreateTemplates(GeneratorTemplateMq query) public async Task CreateTemplates(GeneratorTemplateMq query)
@@ -84,8 +87,9 @@ namespace PARR.GeneratorTemplates.Services
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Name = TemplateHelpers.GenerateTemplateName(settingsFromDb.TemplatePrefixName, host.Ek, appInWork.Work!.Name), Name = TemplateHelpers.GenerateTemplateName(settingsFromDb.TemplatePrefixName, host.Ek, appInWork.Work!.Name),
IsActive = true, IsActiveTemplate = true,
StatusCode = (int)StatusTemplateEnum.Creating, IsActiveSchedule = true,
StatusCode = (int)TaskStatusEnum.Creating,
ApplicationInWorkId = appInWork.Id, ApplicationInWorkId = appInWork.Id,
HostId = host.Id, HostId = host.Id,
RobotAttemptsNumber = 0, RobotAttemptsNumber = 0,
@@ -100,7 +104,7 @@ namespace PARR.GeneratorTemplates.Services
else else
{ {
logger.LogInformation($"Создан шаблон: Name: {template.Name}, ApplicationInWorkId: {template.ApplicationInWorkId}, " + logger.LogInformation($"Создан шаблон: Name: {template.Name}, ApplicationInWorkId: {template.ApplicationInWorkId}, " +
$"HostId: {template.HostId}, IsActive: {template.IsActive}, StatusCode: {template.StatusCode}"); $"HostId: {template.HostId}, IsActive: {template.IsActiveTemplate}, StatusCode: {template.StatusCode}");
} }
} }
} }
@@ -115,8 +119,9 @@ namespace PARR.GeneratorTemplates.Services
var queryTemplates = templateService.Get() var queryTemplates = templateService.Get()
.Include(t => t.Host) .Include(t => t.Host)
.Include(t => t.ApplicationsInWork) .Include(t => t.ApplicationsInWork)
.Include(t => t.RobotConfigurations)
.Where(t => .Where(t =>
t.IsActive == true (t.IsActiveTemplate == true || t.IsActiveSchedule == true)
&& t.ApplicationsInWork!.ApplicationId == query.ApplicationId && t.ApplicationsInWork!.ApplicationId == query.ApplicationId
&& t.ApplicationsInWork!.WorkId == query.WorkId && t.ApplicationsInWork!.WorkId == query.WorkId
&& EF.Functions.Like(t.Host!.Ek.ToLower(), ekPattern) && EF.Functions.Like(t.Host!.Ek.ToLower(), ekPattern)
@@ -138,12 +143,23 @@ namespace PARR.GeneratorTemplates.Services
existTemplates.ForEach(item => existTemplates.ForEach(item =>
{ {
item.IsActive = false;
item.StatusCode = (int)StatusTemplateEnum.Updating;
item.DateModified = DateTimeOffset.UtcNow; item.DateModified = DateTimeOffset.UtcNow;
item.RobotAttemptsNumber = 0;
item.RobotLastStatusUpdated = null; if (item.IsActiveTemplate)
item.RobotStatusCode = (int)RobotStatusEnum.Wait; {
item.IsActiveTemplate = false;
//необходимо обновить шаблон
var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, ref item);
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, ref config);
}
if (item.IsActiveSchedule)
{
item.IsActiveSchedule = false;
//необходимо обновить расписание
var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.ScheduleOrder, ref item);
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, ref config);
}
}); });
if (await templateService.CommitAsync()) if (await templateService.CommitAsync())