2023-09-01 12:40:41 +10:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2026-01-16 18:26:06 +10:00
|
|
|
|
using Npgsql;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Core.Repositories.Interfaces;
|
2023-09-01 12:40:41 +10:00
|
|
|
|
using PARR.DAL.Context;
|
2026-04-14 09:56:31 +10:00
|
|
|
|
using PARR.DAL.Repositories.Base;
|
2026-04-30 10:35:31 +10:00
|
|
|
|
using PARR.Domain.Entities;
|
2026-04-13 16:58:30 +10:00
|
|
|
|
using PARR.Domain.Entities.Base.History;
|
|
|
|
|
|
using PARR.Domain.Enums;
|
2023-09-01 12:40:41 +10:00
|
|
|
|
|
2026-04-30 10:35:31 +10:00
|
|
|
|
namespace PARR.DAL.Repositories
|
2023-09-01 12:40:41 +10:00
|
|
|
|
{
|
2026-04-30 10:35:31 +10:00
|
|
|
|
internal class TemplateRepository : BaseRepository<Template>, ITemplateRepository
|
2023-09-01 12:40:41 +10:00
|
|
|
|
{
|
2026-04-30 10:35:31 +10:00
|
|
|
|
public TemplateRepository(DataContext dataContext, ILogger<TemplateRepository> logger) : base(logger, dataContext) { }
|
2023-09-01 16:19:32 +10:00
|
|
|
|
|
|
|
|
|
|
public async Task<Template?> GetTemplateByNameAsync(string name)
|
|
|
|
|
|
{
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogDebug("Поиск шаблона по имени: {TemplateName}", name);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
|
|
|
|
|
|
var template = await GetWithIncludes()
|
2024-06-04 12:03:15 +10:00
|
|
|
|
.Include(t => t.RobotConfigurations)
|
2023-10-06 17:09:25 +10:00
|
|
|
|
.FirstOrDefaultAsync(t => t.Name == name);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
|
|
|
|
|
|
if (template != null)
|
|
|
|
|
|
{
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogDebug("Шаблон найден: {TemplateId}, имя: {TemplateName}", template.Id, template.Name);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogDebug("Шаблон с именем {TemplateName} не найден", name);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return template;
|
2023-09-01 16:19:32 +10:00
|
|
|
|
}
|
2023-09-19 16:42:06 +10:00
|
|
|
|
|
|
|
|
|
|
public IQueryable<Template> GetWithIncludes()
|
|
|
|
|
|
{
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogDebug("Получаю шаблоны с include связями");
|
2026-02-09 12:00:46 +10:00
|
|
|
|
|
2023-09-19 16:42:06 +10:00
|
|
|
|
return Get()
|
2025-06-25 14:17:34 +10:00
|
|
|
|
.Include(h => h.Unit)
|
2025-08-06 10:33:55 +10:00
|
|
|
|
.ThenInclude(t => t!.UnitValues)
|
|
|
|
|
|
.ThenInclude(t => t.Value)
|
|
|
|
|
|
.Include(h => h.Unit)
|
|
|
|
|
|
.ThenInclude(t => t!.UnitValues)
|
|
|
|
|
|
.ThenInclude(t => t.Field)
|
2025-06-25 14:17:34 +10:00
|
|
|
|
.Include(a => a.Job)
|
2023-09-19 16:42:06 +10:00
|
|
|
|
.ThenInclude(t => t!.Tnk)
|
|
|
|
|
|
.ThenInclude(s => s!.Subprocess)
|
2025-06-27 13:42:13 +10:00
|
|
|
|
.ThenInclude(p => p!.Process)
|
2025-08-06 10:33:55 +10:00
|
|
|
|
.Include(t => t.Job)
|
2026-01-13 14:36:07 +10:00
|
|
|
|
.ThenInclude(t => t!.Group)
|
2026-01-22 14:05:48 +10:00
|
|
|
|
.ThenInclude(t => t!.GroupType)
|
|
|
|
|
|
.Include(t => t.Job)
|
|
|
|
|
|
.ThenInclude(t => t!.Group)
|
|
|
|
|
|
.ThenInclude(t => t.DistributionConfig)
|
|
|
|
|
|
.ThenInclude(t => t.DistributionPeriod);
|
2023-09-19 16:42:06 +10:00
|
|
|
|
}
|
2023-10-04 12:00:43 +10:00
|
|
|
|
|
2023-10-05 16:56:02 +10:00
|
|
|
|
public override Task<bool> CreateAsync(Template obj)
|
|
|
|
|
|
{
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogDebug("Создание шаблона: {TemplateName}", obj.Name);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
|
2023-10-05 16:56:02 +10:00
|
|
|
|
// добавление роботов для шаблона
|
|
|
|
|
|
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,
|
2023-10-06 11:38:33 +10:00
|
|
|
|
LastRobotStatusUpdated = null
|
2023-10-05 16:56:02 +10:00
|
|
|
|
},
|
|
|
|
|
|
// робот по управлениею расписанием
|
|
|
|
|
|
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,
|
2023-10-06 11:38:33 +10:00
|
|
|
|
LastRobotStatusUpdated = null
|
2023-10-05 16:56:02 +10:00
|
|
|
|
}
|
2026-07-23 09:34:59 +10:00
|
|
|
|
|
2023-10-05 16:56:02 +10:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogDebug("Добавлены роботы для шаблона {TemplateName}", obj.Name);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
|
2023-10-05 16:56:02 +10:00
|
|
|
|
return base.CreateAsync(obj);
|
|
|
|
|
|
}
|
2026-01-16 18:26:06 +10:00
|
|
|
|
|
|
|
|
|
|
|
2026-03-27 15:02:36 +10:00
|
|
|
|
public async Task<Guid?> ReserveUnusedTemplateAsync(Guid newUnitId, HistoryInitiator initiator)
|
2026-01-16 18:26:06 +10:00
|
|
|
|
{
|
2026-07-27 09:06:36 +10:00
|
|
|
|
_logger.LogDebug("Резервирую неиспользуемый шаблон для UnitId: {UnitId}", newUnitId);
|
2026-07-23 08:36:17 +10:00
|
|
|
|
|
|
|
|
|
|
// Явная транзакция гарантирует атомарность UPDATE + подзапроса
|
|
|
|
|
|
await using var transaction = await EntityContext.Database.BeginTransactionAsync();
|
2026-02-09 12:00:46 +10:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-07-23 08:36:17 +10:00
|
|
|
|
var sql = @"
|
|
|
|
|
|
UPDATE ""Templates""
|
|
|
|
|
|
SET ""StatusTypeId"" = @NewStatus,
|
|
|
|
|
|
""DateModified"" = @DateModified,
|
|
|
|
|
|
""InitiatorIp"" = @InitiatorIp,
|
|
|
|
|
|
""InitiatorParrComponentId"" = @InitiatorComponent,
|
|
|
|
|
|
""InitiatorComment"" = @InitiatorComment
|
|
|
|
|
|
WHERE ""Id"" = (
|
|
|
|
|
|
SELECT t.""Id""
|
|
|
|
|
|
FROM ""Templates"" t
|
|
|
|
|
|
WHERE t.""StatusTypeId"" = @OldStatus
|
|
|
|
|
|
AND t.""UnitId"" != @NewUnitId
|
|
|
|
|
|
AND EXISTS (
|
|
|
|
|
|
SELECT 1 FROM ""RobotConfigurations"" rc
|
|
|
|
|
|
WHERE rc.""TemplateId"" = t.""Id""
|
|
|
|
|
|
AND rc.""RobotCode"" = @RobotCode1
|
|
|
|
|
|
AND rc.""TaskStatusCode"" = @TaskStatus
|
|
|
|
|
|
AND rc.""RobotStatusCode"" = @RobotStatus
|
|
|
|
|
|
)
|
|
|
|
|
|
AND EXISTS (
|
|
|
|
|
|
SELECT 1 FROM ""RobotConfigurations"" rc
|
|
|
|
|
|
WHERE rc.""TemplateId"" = t.""Id""
|
|
|
|
|
|
AND rc.""RobotCode"" = @RobotCode2
|
|
|
|
|
|
AND rc.""TaskStatusCode"" = @TaskStatus
|
|
|
|
|
|
AND rc.""RobotStatusCode"" = @RobotStatus
|
|
|
|
|
|
)
|
2026-07-23 09:34:59 +10:00
|
|
|
|
ORDER BY t.""DateModified"" ASC NULLS FIRST
|
2026-07-23 08:36:17 +10:00
|
|
|
|
LIMIT 1
|
|
|
|
|
|
FOR UPDATE SKIP LOCKED
|
|
|
|
|
|
)
|
|
|
|
|
|
RETURNING ""Id"";";
|
|
|
|
|
|
|
|
|
|
|
|
var parameters = new[]
|
|
|
|
|
|
{
|
|
|
|
|
|
new NpgsqlParameter("@NewStatus", (int)TemplateStatusTypeEnum.Updating),
|
|
|
|
|
|
new NpgsqlParameter("@DateModified", DateTimeOffset.UtcNow),
|
|
|
|
|
|
new NpgsqlParameter("@InitiatorIp", initiator.InitiatorIp ?? (object)DBNull.Value),
|
|
|
|
|
|
new NpgsqlParameter("@InitiatorComponent",
|
|
|
|
|
|
initiator.InitiatorParrComponentId.HasValue
|
|
|
|
|
|
? (object)(int)initiator.InitiatorParrComponentId.Value
|
|
|
|
|
|
: DBNull.Value),
|
|
|
|
|
|
new NpgsqlParameter("@InitiatorComment", initiator.InitiatorComment ?? (object)DBNull.Value),
|
|
|
|
|
|
new NpgsqlParameter("@OldStatus", (int)TemplateStatusTypeEnum.Unused),
|
|
|
|
|
|
new NpgsqlParameter("@NewUnitId", newUnitId),
|
|
|
|
|
|
new NpgsqlParameter("@RobotCode1", (int)RobotsEnum.TemplateOrder),
|
|
|
|
|
|
new NpgsqlParameter("@RobotCode2", (int)RobotsEnum.ScheduleOrder),
|
|
|
|
|
|
new NpgsqlParameter("@TaskStatus", (int)TaskStatusEnum.Ok),
|
|
|
|
|
|
new NpgsqlParameter("@RobotStatus", (int)RobotStatusEnum.Complete)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-04-14 09:56:31 +10:00
|
|
|
|
var result = await EntityContext.Database
|
2026-02-09 12:00:46 +10:00
|
|
|
|
.SqlQueryRaw<Guid>(sql, parameters)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
2026-07-23 08:36:17 +10:00
|
|
|
|
await transaction.CommitAsync();
|
|
|
|
|
|
|
2026-02-09 12:00:46 +10:00
|
|
|
|
var reservedTemplateId = result.FirstOrDefault();
|
|
|
|
|
|
|
2026-05-07 14:35:11 +10:00
|
|
|
|
if (reservedTemplateId != Guid.Empty)
|
2026-02-09 12:00:46 +10:00
|
|
|
|
{
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogInformation("Успешно зарезервирован шаблон с ID: {TemplateId} для UnitId: {UnitId}",
|
2026-03-27 15:02:36 +10:00
|
|
|
|
reservedTemplateId, newUnitId);
|
2026-05-07 14:35:11 +10:00
|
|
|
|
return reservedTemplateId;
|
2026-02-09 12:00:46 +10:00
|
|
|
|
}
|
2026-07-23 08:36:17 +10:00
|
|
|
|
|
2026-07-27 09:06:36 +10:00
|
|
|
|
_logger.LogDebug("Не удалось зарезервировать шаблон для UnitId: {UnitId}", newUnitId);
|
2026-07-23 08:36:17 +10:00
|
|
|
|
return null;
|
2026-02-09 12:00:46 +10:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-07-23 08:36:17 +10:00
|
|
|
|
await transaction.RollbackAsync();
|
2026-07-23 10:48:59 +10:00
|
|
|
|
_logger.LogError(ex, "Ошибка при резервировании шаблона для UnitId: {UnitId}", newUnitId);
|
2026-02-09 12:00:46 +10:00
|
|
|
|
throw;
|
|
|
|
|
|
}
|
2026-01-16 18:26:06 +10:00
|
|
|
|
}
|
2023-09-01 12:40:41 +10:00
|
|
|
|
}
|
2026-02-09 12:00:46 +10:00
|
|
|
|
}
|