feat(api): Job - в респонс добавлены групповые фильтры. Обновлена валидация с учетом IsAllowJobUnitFilter

This commit is contained in:
Mikhail Trubnikov
2026-09-28 12:22:30 +10:00
parent b475d1e10c
commit 3818a470ce
5 changed files with 75 additions and 81 deletions

View File

@@ -50,6 +50,11 @@
// public int? AgentTimeOutSec { get; set; }
// public string? AgentScript { get; set; }
/// <summary>
/// Юнит фильтры
/// </summary>
public List<UnitFilterResponse>? UnitFilters { get; set; }
}
public class JobGroupResponse : JobGroupWithDistributionConfigResponse
@@ -71,10 +76,10 @@
/// </summary>
public ScheduleExcludeTypeCalendarResponse? ScheduleExcludeTypeCalendar { get; set; }
/// <summary>
/// Юнит фильтры
/// </summary>
public List<UnitFilterResponse>? UnitFilters { get; set; }
///// <summary>
///// Юнит фильтры
///// </summary>
//public List<UnitFilterResponse>? UnitFilters { get; set; }
}
public class JobGroupAutoControlResponse

View File

@@ -30,6 +30,9 @@
public int? TemplatesCount { get; set; }
/// <summary>
/// Юнит фильтры Работы
/// </summary>
public List<UnitFilterResponse>? UnitFilters { get; set; }
public JobAutoControlResponse? AutoControl { get; set; }

View File

@@ -82,6 +82,7 @@ namespace PARR.API.Controllers.V1
var paginationFilter = _mapper.Map<PaginationFilter>(paginationQuery);
IQueryable<Job> query = _jobRepository.Get()
.AsNoTracking()
.Include(t => t.AutoControl);
@@ -152,9 +153,22 @@ namespace PARR.API.Controllers.V1
{
var job = await _jobRepository.Get()
.Include(t => t.Tnk)
.Include(t => t.Group).ThenInclude(t => t.GroupType)
.Include(t => t.Group).ThenInclude(t => t.AutoControl)
.Include(t => t.Group).ThenInclude(t => t.GroupingUnitField)
.Include(t => t.Group)
.ThenInclude(t => t.GroupType)
.Include(t => t.Group)
.ThenInclude(t => t.AutoControl)
.Include(t => t.Group)
.ThenInclude(t => t.GroupingUnitField)
//--- Фильтры в группе
.Include(t => t.Group)
.ThenInclude(t => t.UnitFilters)
.ThenInclude(t => t.FieldFilters)
.ThenInclude(t => t.UnitField)
.Include(t => t.Group)
.ThenInclude(t => t.UnitFilters)
.ThenInclude(t => t.RelationshipFilters)
.ThenInclude(t=>t.UnitField)
//---
.Include(t => t.UnitFilters)
.ThenInclude(t => t.FieldFilters)
.ThenInclude(t => t.UnitField)
@@ -162,6 +176,7 @@ namespace PARR.API.Controllers.V1
.ThenInclude(t => t.RelationshipFilters)
.ThenInclude(t => t.UnitField)
.Include(t => t.AutoControl)
.AsNoTracking()
.FirstOrDefaultAsync(t => t.Id == id);
if (job == null)
@@ -169,12 +184,9 @@ namespace PARR.API.Controllers.V1
var response = _mapper.Map<JobResponse>(job);
response.TemplatesCount = await GetCountTemplatesAsync(id); //await templateService.Get().CountAsync(t => t.JobId == id);
response.TemplatesCount = await GetCountTemplatesAsync(id);
response.MatchingStatus = await GetMatchingStatusAsync(id);
//var statistics = await GetStatisticsAsync(response.Id);
//BindStatistics(response, statistics);
return Ok(new Response<JobResponse>(response, true));
}
@@ -244,6 +256,16 @@ namespace PARR.API.Controllers.V1
.Include(t => t.UnitFilters)
.ThenInclude(t => t.RelationshipFilters)
.Include(t => t.AutoControl)
//--- Фильтры в группе
.Include(t => t.Group)
.ThenInclude(t => t.UnitFilters)
.ThenInclude(t => t.FieldFilters)
.ThenInclude(t => t.UnitField)
.Include(t => t.Group)
.ThenInclude(t => t.UnitFilters)
.ThenInclude(t => t.RelationshipFilters)
.ThenInclude(t => t.UnitField)
//---
.FirstOrDefaultAsync(t => t.Id == job.Id);
var locationUri = _uriService.GetUri(ApiRoutes.Job.Get, ApiRoutes.Job.getParam, createdJob!.Id);
@@ -365,6 +387,16 @@ namespace PARR.API.Controllers.V1
.Include(t => t.UnitFilters)
.ThenInclude(t => t.RelationshipFilters)
.Include(t => t.AutoControl)
//--- Фильтры в группе
.Include(t => t.Group)
.ThenInclude(t => t.UnitFilters)
.ThenInclude(t => t.FieldFilters)
.ThenInclude(t => t.UnitField)
.Include(t => t.Group)
.ThenInclude(t => t.UnitFilters)
.ThenInclude(t => t.RelationshipFilters)
.ThenInclude(t => t.UnitField)
//---
.FirstAsync(t => t.Id == orig.Id);
var response = _mapper.Map<JobResponse>(updatedJob);

View File

@@ -388,7 +388,8 @@ namespace PARR.API.MappingProfiles
.ForMember(d => d.GroupType, o => o.MapFrom(s => s.GroupType))
.ForMember(d => d.GroupingUnitField, o => o.MapFrom(s => s.GroupingUnitField))
.ForMember(d => d.IsWorkGroupTimezone, o => o.MapFrom(s => s.IsResponseAreaTimezone))
.ForMember(d => d.AutoControl, o => o.MapFrom(s => s.AutoControl));
.ForMember(d => d.AutoControl, o => o.MapFrom(s => s.AutoControl))
.ForMember(d => d.UnitFilters, o => o.MapFrom(s => s.UnitFilters));
CreateMap<JobGroup, JobGroupBaseResponse>()
.ForMember(d => d.Name, o => o.MapFrom(s => s.GroupName))
@@ -399,8 +400,7 @@ namespace PARR.API.MappingProfiles
CreateMap<JobGroup, JobGroupResponse>()
.ForMember(d => d.ScheduleExcludeType, o => o.MapFrom(s => s.ScheduleExcludeType))
.ForMember(d => d.ScheduleExcludeTypeCalendar, o => o.MapFrom(s => s.ScheduleExcludeTypeCalendar))
.ForMember(d => d.UnitFilters, o => o.MapFrom(s => s.UnitFilters));
.ForMember(d => d.ScheduleExcludeTypeCalendar, o => o.MapFrom(s => s.ScheduleExcludeTypeCalendar));
//.ForMember(d => d.DistributionConfig, o => o.MapFrom(s => s.DistributionConfig));
//------- unit filters

View File

@@ -12,7 +12,6 @@ namespace PARR.API.Validators
private readonly ITnkRepository _tnkRepository;
private readonly IJobGroupRepository _jobGroupRepository;
private readonly IUnitFieldRepository _unitFieldRepository;
//private JobGroup? jobGroup;
public JobRequestValidator(
ITnkRepository tnkRepository,
@@ -39,15 +38,28 @@ namespace PARR.API.Validators
.MustAsync(async (entity, value, c) => await IsGroupExist(entity))
.WithMessage("Указан несуществующий Id группы работ({PropertyValue})");
//RuleForEach(t => t.UnitFilters)
// .NotNull().NotEmpty();
#region UnitFiters
When(t => t.UnitFilters != null && t.UnitFilters.Any(), () =>
{
RuleFor(t => t.UnitFilters)
.MustAsync(async (entity, value, c) =>
{
// Проверяем флаг в БД: разрешены ли фильтры для этой группы
return await jobGroupRepository.Get()
.AnyAsync(t => t.Id == entity.GroupId && t.GroupType!.IsAllowJobUnitFilter == true, c);
})
.WithMessage("Дополнительные фильтры запрещены")
// Stop делает так, что если флаг равен false, FluentValidation остановится
// и НЕ пойдет выполнять Include базового валидатора (сэкономим запрос к БД)
.DependentRules(() =>
{
// Этот валидатор сработает ПОСЛЕ успешной проверки флага IsAllowJobUnitFilter
Include(unitFilterValidator);
});
});
// Валидация фильтров (они могут быть пустые, мы так договорились)
Include(unitFilterValidator);
//RuleForEach(t => t.UnitFilters)
// .MustAsync(async (entity, value, c) => await IsUnitFiltersCorrect(entity))
// .WithMessage("Неверно заданы параметры фильтров.");
#endregion
RuleFor(t => t.Relationships)
.MustAsync(async (entity, value, c) =>
@@ -99,64 +111,6 @@ namespace PARR.API.Validators
.WithMessage("Некорректные параметры автоконтроля");
}
//private async Task<bool> IsUnitFiltersCorrect(JobRequest entity)
//{
// // 1. Проверим маски, они не должны быть пустыми
// foreach (var unitFilter in entity.UnitFilters)
// {
// if (string.IsNullOrEmpty(unitFilter.UnitFilterMask))
// return false;
// }
// // 2. Собираем все уникальные FieldId из всех фильтров в памяти
// var requestedFieldIds = entity.UnitFilters
// .SelectMany(uf =>
// (uf.FieldFilters ?? Enumerable.Empty<FieldFilterRequest>()).Select(ff => ff.FieldId)
// .Concat((uf.RelationshipFilters ?? Enumerable.Empty<RelationshipFilterRequest>()).Select(rf => rf.FieldId)))
// .Distinct()
// .ToList();
// // Если фильтров с ID нет, то и проверять в БД нечего
// if (!requestedFieldIds.Any())
// return true;
// // 3. Выполняем ОДИН запрос к БД.
// var existingFieldIds = await _unitFieldRepository.Get()
// .Where(field => requestedFieldIds.Contains(field.Id))
// .Select(field => field.Id)
// .Distinct()
// .ToListAsync();
// // 4. Если количество найденных ID в БД меньше, чем мы запрашивали,
// // значит какого-то fieldId не существует
// return requestedFieldIds.Count == existingFieldIds.Count;
// #region Old
// //foreach (var unitFilter in entity.UnitFilters)
// //{
// // if (string.IsNullOrEmpty(unitFilter.UnitFilterMask))
// // return false;
// // if (unitFilter.FieldFilters != null && unitFilter.FieldFilters.Any())
// // foreach (var fieldFilter in unitFilter.FieldFilters)
// // {
// // var fieldId = fieldFilter.FieldId;
// // if (await _unitFieldRepository.GetAsync(fieldId) == null)
// // return false;
// // }
// // if (unitFilter.RelationshipFilters != null && unitFilter.RelationshipFilters.Any())
// // foreach (var relationshipFilter in unitFilter.RelationshipFilters!)
// // {
// // var fieldId = relationshipFilter.FieldId;
// // if (await _unitFieldRepository.GetAsync(fieldId) == null)
// // return false;
// // }
// //}
// //return true;
// #endregion
//}
private async Task<bool> IsGroupExist(JobRequest entity)
{