feat(dal,api): расширена таблица Job, добавлеа маска рабочей группы. Теперь можно генерировать шаблоны с единой экстерриториальной группой

This commit is contained in:
Mikhail Kuznetsov
2025-08-22 11:25:51 +10:00
parent d30093dfef
commit a6385ea39a
12 changed files with 3957 additions and 145 deletions

View File

@@ -12,7 +12,11 @@
public int? MaxValueRelationships { get; set; } public int? MaxValueRelationships { get; set; }
public string? TemplateNameMask { get; set; } public bool? isParentRelationships { get; set; }
public required string TemplateNameMask { get; set; }
public required string WorkGroupMask { get; set; }
public required string WorkName { get; set; } public required string WorkName { get; set; }
} }

View File

@@ -1,4 +1,6 @@
namespace PARR.API.Contracts.V1.Responses using PARR.DAL.Models.Job;
namespace PARR.API.Contracts.V1.Responses
{ {
public class JobBaseResponse public class JobBaseResponse
{ {
@@ -7,7 +9,9 @@
public required string Name { get; set; } public required string Name { get; set; }
public required string TemplateNameMask { get; set; } public required string TemplateNameMask { get; set; }
public TnkResponse? Tnk { get; set; } public TnkResponse? Tnk { get; set; }
public JobGroupBaseResponse? Group { get; set; }
} }
public class JobResponse : JobBaseResponse public class JobResponse : JobBaseResponse

View File

@@ -59,7 +59,7 @@ namespace PARR.API.Controllers.V1
{ {
var paginationFilter = mapper.Map<PaginationFilter>(paginationQuery); var paginationFilter = mapper.Map<PaginationFilter>(paginationQuery);
IQueryable<Job> query = jobService.Get().Include(t => t.Tnk); IQueryable<Job> query = jobService.Get().Include(t => t.Tnk).Include(t=>t.Group);
query = query.OrderBy(t => t.Name); query = query.OrderBy(t => t.Name);
@@ -125,7 +125,9 @@ namespace PARR.API.Controllers.V1
WorkName = request.WorkName.Trim(), WorkName = request.WorkName.Trim(),
MinValueRelationships = request.MinValueRelationships, MinValueRelationships = request.MinValueRelationships,
MaxValueRelationships = request.MaxValueRelationships, MaxValueRelationships = request.MaxValueRelationships,
TemplateNameMask = request.TemplateNameMask?.Trim(), isParentRelationships = request.isParentRelationships,
TemplateNameMask = request.TemplateNameMask.Trim(),
WorkGroupMask = request.WorkGroupMask.Trim(),
TnkId = request.TnkId, TnkId = request.TnkId,
GroupId = request.GroupId GroupId = request.GroupId
}; };
@@ -173,7 +175,9 @@ namespace PARR.API.Controllers.V1
orig.WorkName = request.WorkName.Trim(); orig.WorkName = request.WorkName.Trim();
orig.MinValueRelationships = request.MinValueRelationships; orig.MinValueRelationships = request.MinValueRelationships;
orig.MaxValueRelationships = request.MaxValueRelationships; orig.MaxValueRelationships = request.MaxValueRelationships;
orig.TemplateNameMask = request.TemplateNameMask?.Trim(); orig.isParentRelationships = request.isParentRelationships;
orig.TemplateNameMask = request.TemplateNameMask.Trim();
orig.WorkGroupMask = request.WorkGroupMask.Trim();
orig.TnkId = request.TnkId; orig.TnkId = request.TnkId;
orig.GroupId = request.GroupId; orig.GroupId = request.GroupId;
@@ -275,8 +279,8 @@ namespace PARR.API.Controllers.V1
public class JobStatModel public class JobStatModel
{ {
public TemplateStats TemplateStatistics { get; set; } public required TemplateStats TemplateStatistics { get; set; }
public ScheduleStats ScheduleStatistics { get; set; } public required ScheduleStats ScheduleStatistics { get; set; }
} }
} }

View File

@@ -175,6 +175,8 @@ namespace PARR.API.Controllers.V1
robotTaskTemplateResponse.TnkName = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.TnkName, task!.Template!.UnitId, task!.Template!.JobId); robotTaskTemplateResponse.TnkName = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.TnkName, task!.Template!.UnitId, task!.Template!.JobId);
if (shortcodesService.isAnyShortcodes(robotTaskTemplateResponse.WorkName)) if (shortcodesService.isAnyShortcodes(robotTaskTemplateResponse.WorkName))
robotTaskTemplateResponse.WorkName = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.WorkName, task!.Template!.UnitId, task!.Template!.JobId); robotTaskTemplateResponse.WorkName = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.WorkName, task!.Template!.UnitId, task!.Template!.JobId);
if (shortcodesService.isAnyShortcodes(robotTaskTemplateResponse.WorkGroup))
robotTaskTemplateResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.WorkGroup, task!.Template!.UnitId, task!.Template!.JobId);
return Ok(new Response<RobotTaskTemplateResponse>(robotTaskTemplateResponse, true)); return Ok(new Response<RobotTaskTemplateResponse>(robotTaskTemplateResponse, true));
case RobotsEnum.ScheduleOrder: case RobotsEnum.ScheduleOrder:
@@ -183,6 +185,8 @@ namespace PARR.API.Controllers.V1
//RobotTaskScheduleResponse //RobotTaskScheduleResponse
var robotTaskScheduleResponse = mapper.Map<RobotTaskScheduleResponse>(task); var robotTaskScheduleResponse = mapper.Map<RobotTaskScheduleResponse>(task);
if (shortcodesService.isAnyShortcodes(robotTaskScheduleResponse.WorkGroup))
robotTaskScheduleResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.WorkGroup, task!.Template!.UnitId, task!.Template!.JobId);
return Ok(new Response<RobotTaskScheduleResponse>(robotTaskScheduleResponse, true)); return Ok(new Response<RobotTaskScheduleResponse>(robotTaskScheduleResponse, true));
default: default:

View File

@@ -164,7 +164,7 @@ namespace PARR.API.MappingProfiles
.ForMember(d => d.TemplateDuration, o => o.MapFrom(s => s.Template!.Job!.Group!.TemplateDuration)) .ForMember(d => d.TemplateDuration, o => o.MapFrom(s => s.Template!.Job!.Group!.TemplateDuration))
.ForMember(d => d.Initiator, o => o.MapFrom<RobotTaskTemplateInitiatorResolver>()) .ForMember(d => d.Initiator, o => o.MapFrom<RobotTaskTemplateInitiatorResolver>())
.ForMember(d => d.Category, o => o.MapFrom<RobotTaskTemplateCategoryResolver>()) .ForMember(d => d.Category, o => o.MapFrom<RobotTaskTemplateCategoryResolver>())
.ForMember(d => d.WorkGroup, o => o.MapFrom(s => s.Template!.Unit!.BaseFields!.WorkGroup)) .ForMember(d => d.WorkGroup, o => o.MapFrom(s => s.Template!.Job!.WorkGroupMask))
.ForMember(d => d.Ek, o => o.MapFrom(s => s.Template!.Unit!.Name)) .ForMember(d => d.Ek, o => o.MapFrom(s => s.Template!.Unit!.Name))
.ForMember(d => d.Name, o => o.MapFrom(s => s.Template!.Name)) .ForMember(d => d.Name, o => o.MapFrom(s => s.Template!.Name))
.ForMember(d => d.ProcessName, o => o.MapFrom(s => s.Template!.Job!.Tnk!.Subprocess!.Process!.Name)) .ForMember(d => d.ProcessName, o => o.MapFrom(s => s.Template!.Job!.Tnk!.Subprocess!.Process!.Name))
@@ -184,7 +184,7 @@ namespace PARR.API.MappingProfiles
.ForMember(d => d.ResponseArea, o => o.MapFrom(s => s.Template!.Unit!.BaseFields!.ResponseArea)) .ForMember(d => d.ResponseArea, o => o.MapFrom(s => s.Template!.Unit!.BaseFields!.ResponseArea))
.ForMember(d => d.TemplateName, o => o.MapFrom(s => s.Template!.Name)) .ForMember(d => d.TemplateName, o => o.MapFrom(s => s.Template!.Name))
.ForMember(d => d.TemplateId, o => o.MapFrom(s => s.Template!.Id)) .ForMember(d => d.TemplateId, o => o.MapFrom(s => s.Template!.Id))
.ForMember(d => d.WorkGroup, o => o.MapFrom(s => s.Template!.Unit!.BaseFields!.WorkGroup)) .ForMember(d => d.WorkGroup, o => o.MapFrom(s => s.Template!.Job!.WorkGroupMask))
.ForMember(d => d.Exclude, o => o.MapFrom<RobotTaskScheduleExcludeResolver>()) .ForMember(d => d.Exclude, o => o.MapFrom<RobotTaskScheduleExcludeResolver>())
.ForMember(d => d.Timezone, o => o.MapFrom<RobotTaskScheduleTimezoneResolver>()) .ForMember(d => d.Timezone, o => o.MapFrom<RobotTaskScheduleTimezoneResolver>())
.ForMember(d => d.RepeatRange, o => o.MapFrom<RobotTaskScheduleRepeatRangeResolver>()) .ForMember(d => d.RepeatRange, o => o.MapFrom<RobotTaskScheduleRepeatRangeResolver>())

View File

@@ -1,128 +0,0 @@
using Microsoft.EntityFrameworkCore;
using PARR.DAL.Contracts;
using PARR.DAL.DomainServices.Interfaces;
using PARR.DAL.Services.Interfaces.Job;
using PARR.DAL.Services.Interfaces.Unit;
using System.Text.RegularExpressions;
namespace PARR.DAL.DomainServices.Implementations
{
internal class TemplateNameGeneratorService : ITemplateNameGeneratorService
{
private readonly SettingsFromDb settingsFromDb;
private readonly IJobService jobService;
private readonly IUnitService unitService;
public TemplateNameGeneratorService(
SettingsFromDb settingsFromDb,
IJobService jobService,
IUnitService unitService
)
{
this.settingsFromDb = settingsFromDb;
this.jobService = jobService;
this.unitService = unitService;
}
public async Task<string> GetTemplateNameAsync(Guid jobId, Guid unitId)
{
var nameConstants = settingsFromDb.TemplateNameConstantPartsList;
var job = await jobService.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Id == jobId);
var unit = await unitService.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Id == unitId);
if (job != null && unit != null && job.TemplateNameMask != null)
{
var resultName = job.TemplateNameMask;
var shortcodesInMask = GetShortCodes(resultName);
//Проверяем и меняем наличие статичных частей в маске имени шаблона
if (shortcodesInMask.Any(x => nameConstants.Select(x => "%" + x.Name + "%").ToList().Contains(x.Value)))
{
resultName = ReplaceConstants(nameConstants, resultName);
}
//Проверяем и меняем Shortcodes в маске имени шаблона
var shortCodes = GetShortcodesNames();
if (shortcodesInMask.Any(x => shortCodes.Select(x => x).ToList().Contains(x.Value)))
{
resultName = ReplaceShortcodes(job, unit, resultName, shortcodesInMask);
}
//Если остались %переменные% проверяем совпадение по имени поля
shortcodesInMask = GetShortCodes(resultName);
if (shortcodesInMask.Count > 0)
{
resultName = await ReplaceFieldValues(unitId, resultName, shortcodesInMask);
}
return resultName;
}
return "";
}
private async Task<string> ReplaceFieldValues(Guid unitId, string resultName, List<Match> shortcodesInMask)
{
var unitWithFields = await unitService.Get()
.AsNoTracking()
.Include(u => u.UnitValues)
.ThenInclude(uv => uv.Field)
.Include(u => u.UnitValues)
.ThenInclude(uv => uv.Value)
.FirstOrDefaultAsync(t => t.Id == unitId);
foreach (var item in shortcodesInMask)
{
var fieldName = item.Value.Replace("%", "").ToUpper();
var value = unitWithFields!.UnitValues!.FirstOrDefault(t => t.Field!.AihitName!.ToUpper() == fieldName!);
if (value != null)
resultName = resultName.Replace(item.Value, value!.Value!.Value);
}
return resultName;
}
private static string ReplaceShortcodes(Models.Job.Job job, Models.Unit.Unit unit, string resultName, List<Match> shortcodesInMask)
{
if (shortcodesInMask.Any(x => x.Value == "%РАБОТА%"))
resultName = resultName.Replace("%РАБОТА%", job.WorkName);
if (shortcodesInMask.Any(x => x.Value == "%ЭК%"))
resultName = resultName.Replace("%ЭК%", unit.Name);
return resultName;
}
private static string ReplaceConstants(List<BLL.Domain.TemplateNameConstantPart> nameConstants, string resultName)
{
foreach (var item in nameConstants)
{
resultName = resultName.Replace($"%{item.Name}%", item.Value);
}
return resultName;
}
private static List<Match> GetShortCodes(string resultName)
{
var shortcodePattern = "%[^%\\s]+%";
var shortcodesInMask = Regex.Matches(resultName, shortcodePattern).ToList();
return shortcodesInMask;
}
private List<string> GetShortcodesNames()
{
var result = new List<string> {
"%ЭК%",
"%ЗО%",
"%РАБОТА%"
};
return result;
}
}
}

View File

@@ -97,8 +97,12 @@ namespace PARR.DAL.DomainServices.Implementations
} }
if (job.Group?.IsUmbrella == true) if (job.Group?.IsUmbrella == true)
query = query.Where(t => t.ChildUnits.Count() >= job.MinValueRelationships && t.ChildUnits.Count() <= job.MaxValueRelationships); {
if (job.isParentRelationships == true)
query = query.Where(t => t.ParentUnits.Count() >= job.MinValueRelationships && t.ParentUnits.Count() <= job.MaxValueRelationships);
else
query = query.Where(t => t.ChildUnits.Count() >= job.MinValueRelationships && t.ChildUnits.Count() <= job.MaxValueRelationships);
}
var newUnits = await query.Select(t => t.Id).ToListAsync(); var newUnits = await query.Select(t => t.Id).ToListAsync();
result.AddRange(newUnits); result.AddRange(newUnits);
} }

View File

@@ -0,0 +1,63 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PARR.DAL.Migrations
{
/// <inheritdoc />
public partial class tblJobAddClmnisParentRelationshipsAndWorkGroupMask : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "TemplateNameMask",
schema: "job",
table: "Jobs",
type: "text",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "WorkGroupMask",
schema: "job",
table: "Jobs",
type: "text",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<bool>(
name: "isParentRelationships",
schema: "job",
table: "Jobs",
type: "boolean",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "WorkGroupMask",
schema: "job",
table: "Jobs");
migrationBuilder.DropColumn(
name: "isParentRelationships",
schema: "job",
table: "Jobs");
migrationBuilder.AlterColumn<string>(
name: "TemplateNameMask",
schema: "job",
table: "Jobs",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "text");
}
}
}

View File

@@ -1602,15 +1602,23 @@ namespace PARR.DAL.Migrations
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("TemplateNameMask") b.Property<string>("TemplateNameMask")
.IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<Guid>("TnkId") b.Property<Guid>("TnkId")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<string>("WorkGroupMask")
.IsRequired()
.HasColumnType("text");
b.Property<string>("WorkName") b.Property<string>("WorkName")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<bool?>("isParentRelationships")
.HasColumnType("boolean");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GroupId"); b.HasIndex("GroupId");

View File

@@ -27,21 +27,34 @@ namespace PARR.DAL.Models.Job
/// </summary> /// </summary>
public required string WorkName { get; set; } public required string WorkName { get; set; }
/// <summary> /// <summary>
/// Минимальное значение дочерних связей группового ЭК /// Минимальное значение дочерних или родительских связей группового ЭК
/// </summary> /// </summary>
public int? MinValueRelationships { get; set; } public int? MinValueRelationships { get; set; }
/// <summary> /// <summary>
/// Максимальное значение дочерних связей группового ЭК /// Максимальное значение дочерних или родительских связей группового ЭК
/// </summary> /// </summary>
public int? MaxValueRelationships { get; set; } public int? MaxValueRelationships { get; set; }
/// <summary>
/// Для подсчёта связей групового ЭК использовать родительсике или дочерние связи
/// </summary>
/// <value>
/// true - диапазон связей относится к родительским связям<br/>
/// false, null - диапазон относится к дочерним связям
/// </value>
public bool? isParentRelationships { get; set; }
/// <summary> /// <summary>
/// Маска для динамического формирования имени шаблона /// Маска для динамического формирования имени шаблона
/// </summary> /// </summary>
public string? TemplateNameMask { get; set; } public required string TemplateNameMask { get; set; }
/// <summary>
/// Маска для динамического формирования рабочей группы исполнителей шаблона
/// </summary
public required string WorkGroupMask { get; set; }
public Guid TnkId { get; set; } public Guid TnkId { get; set; }

View File

@@ -100,7 +100,6 @@ namespace PARR.DAL
services.AddTransient<IUnitService, UnitService>(); services.AddTransient<IUnitService, UnitService>();
services.AddTransient<IUnitFieldValueService, UnitFieldValueService>(); services.AddTransient<IUnitFieldValueService, UnitFieldValueService>();
services.AddTransient<IUnitFieldService, UnitFieldService>(); services.AddTransient<IUnitFieldService, UnitFieldService>();
services.AddTransient<IUnitFilterService, UnitFilterService>();
#endregion #endregion
@@ -119,8 +118,8 @@ namespace PARR.DAL
services.AddTransient<INextRunModifierService, NextRunModifierService>(); services.AddTransient<INextRunModifierService, NextRunModifierService>();
#region DomainServces #region DomainServces
services.AddTransient<ITemplateNameGeneratorService, TemplateNameGeneratorService>();
services.AddTransient<IShortcodesService, ShortcodesService>(); services.AddTransient<IShortcodesService, ShortcodesService>();
services.AddTransient<IUnitFilterService, UnitFilterService>();
#endregion #endregion
} }