using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.Core.Repositories.Interfaces.JobGroupRepositories; using PARR.Domain.Entities.Base.History; using PARR.Domain.Entities.JobEntities; using PARR.Domain.Entities.JobGroupEntities; using PARR.Domain.Entities.Schedule; namespace PARR.Core.Common.Helpers; /// /// Сервис для полного клонирования JobGroup со всеми вложенными Job, фильтрами, AutoControl и EsppSchValues. /// Шаблоны (Template) не клонируются. Идентификаторы генерируются стратегией EF Core. /// public class JobGroupCloneHelper { private readonly IJobGroupRepository groupRepository; private readonly ILogger logger; public JobGroupCloneHelper( IJobGroupRepository groupRepository, ILogger logger) { this.groupRepository = groupRepository; this.logger = logger; } public async Task CloneAsync( Guid sourceJobGroupId, string? newGroupName, HistoryInitiator initiator, CancellationToken ct = default) { var sourceGroup = await groupRepository.Get() .AsNoTracking() .Include(g => g.Jobs) .ThenInclude(j => j.UnitFilters) .ThenInclude(uf => uf.FieldFilters) .Include(g => g.Jobs) .ThenInclude(j => j.UnitFilters) .ThenInclude(uf => uf.RelationshipFilters) .Include(g => g.Jobs) .ThenInclude(j => j.AutoControl) .Include(g => g.EsppSchValues) .FirstOrDefaultAsync(g => g.Id == sourceJobGroupId, ct); if (sourceGroup == null) { throw new InvalidOperationException($"JobGroup {sourceJobGroupId} not found."); } logger.LogInformation("Начало клонирования JobGroup {JobGroupId}.", sourceJobGroupId); var newGroup = MapJobGroup(sourceGroup, newGroupName); foreach (var sourceJob in sourceGroup.Jobs ?? Enumerable.Empty()) { var newJob = MapJob(sourceJob); newGroup.Jobs.Add(newJob); CloneJobFilters(sourceJob.UnitFilters, newJob); if (sourceJob.AutoControl != null) { newJob.AutoControl = MapAutoControl(sourceJob.AutoControl); } } // Клонирование значений расписания foreach (var sourceSchValue in sourceGroup.EsppSchValues ?? Enumerable.Empty()) { newGroup.EsppSchValues.Add(new EsppSchValue { TypeValueId = sourceSchValue.TypeValueId, TypeConfigId = sourceSchValue.TypeConfigId }); } var isSuccess = await groupRepository.CreateAsync(newGroup); if (!isSuccess) { throw new InvalidOperationException("Не удалось сохранить клон JobGroup в базу данных."); } await groupRepository.CommitAsync(initiator); logger.LogInformation("JobGroup успешно клонирована. Новый ID: {NewGroupId}.", newGroup.Id); return newGroup.Id; } private JobGroup MapJobGroup(JobGroup source, string? newName) { string finalName = string.IsNullOrWhiteSpace(newName) ? $"{source.GroupName?.TrimEnd()} КЛОН" : newName.Trim(); return new JobGroup { GroupName = finalName, ShortDescription = source.ShortDescription, FullDescription = source.FullDescription, Solution = source.Solution, TemplateDuration = source.TemplateDuration, ReferenceDate = source.ReferenceDate, UserTimeZoneOffsetMinutes = source.UserTimeZoneOffsetMinutes, IsResponseAreaTimezone = source.IsResponseAreaTimezone, IsAutoDistributionEnabled = source.IsAutoDistributionEnabled, IsAgent = source.IsAgent, AgentName = source.AgentName, AgentTimeOutSec = source.AgentTimeOutSec, AgentScript = source.AgentScript, GroupTypeId = source.GroupTypeId, GroupingUnitFieldId = source.GroupingUnitFieldId, IsGroupByResponsible = source.IsGroupByResponsible, ScheduleExcludeTypeId = source.ScheduleExcludeTypeId, ScheduleExcludeTypeCalendarId = source.ScheduleExcludeTypeCalendarId, DateCreated = DateTimeOffset.UtcNow, Jobs = new HashSet(), EsppSchValues = new HashSet() }; } private Job MapJob(Job source) { return new Job { Name = source.Name, WorkName = source.WorkName, MinValueRelationships = source.MinValueRelationships, MaxValueRelationships = source.MaxValueRelationships, IsParentRelationships = source.IsParentRelationships, TemplateNameMask = source.TemplateNameMask, WorkGroupMask = source.WorkGroupMask, ResponseAreaMask = source.ResponseAreaMask, TnkId = source.TnkId, DateCreated = DateTimeOffset.UtcNow, UnitFilters = new HashSet() }; } private JobAutoControl MapAutoControl(JobAutoControl source) { return new JobAutoControl { InitUsedTemplateState = source.InitUsedTemplateState, InitUsedScheduleState = source.InitUsedScheduleState }; } private void CloneJobFilters(IEnumerable? sourceFilters, Job targetJob) { if (sourceFilters == null) return; foreach (var sourceFilter in sourceFilters) { var newFilter = new JobUnitFilter { UnitFilter = sourceFilter.UnitFilter, DateCreated = DateTimeOffset.UtcNow, FieldFilters = sourceFilter.FieldFilters?.Select(f => new JobFieldFilter { FieldId = f.FieldId, ValueMask = f.ValueMask, IsInverse = f.IsInverse, DateCreated = DateTimeOffset.UtcNow }).ToList() ?? new List(), RelationshipFilters = sourceFilter.RelationshipFilters?.Select(r => new JobRelationshipFilter { FieldId = r.FieldId, ValueMask = r.ValueMask, IsInverse = r.IsInverse, IsParent = r.IsParent, IsFullMatch = r.IsFullMatch }).ToList() ?? new List() }; targetJob.UnitFilters.Add(newFilter); } } }