10c3057eff
Раньше шаг маршрута мог нести ровно одно условие (field_name/operation/ value). Теперь условий может быть сколько угодно, а их объединение задаётся явно — ConditionsLogic.And (все условия) или .Or (любое из условий). Бэкенд: - Новая сущность TemplateRouteStepCondition (дочерняя таблица template_route_step_conditions, FK на шаг, cascade delete) вместо трёх колонок на TemplateRouteStep. Миграция AddMultipleStepConditions переносит существующие условия шагов в новую таблицу перед удалением старых колонок, чтобы не потерять уже настроенные в деве/проде условия. - TemplateRouteService.MatchesConditions: несколько условий шага объединяются по ConditionsLogic (Any/All); HasUnevaluatableManagerCondition проверяет условие на IsManager в списке условий, а не единственное поле. - TemplateRouteStepService: дубликаты шагов теперь сравниваются по всему набору условий (без учёта порядка) и, если условий два и более, по логике их объединения — сравнение в памяти, а не в SQL, кандидатов на дубликат в пределах маршрута всегда мало. - Валидация условия: раньше «все три поля или ни одного», теперь — в каждом элементе списка все три поля обязательны. Фронтенд: - RouteStepFormModal: условие — динамический список строк (добавить/удалить), у каждой строки свой набор допустимых операторов по типу колонки; селектор И/ИЛИ появляется, когда условий два и больше. - describeStepCondition в таблице шагов склеивает все условия через « И »/« ИЛИ ». Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
81 lines
4.1 KiB
C#
81 lines
4.1 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Tnsu.ApprovalRoute.Domain.Entities;
|
|
using Tnsu.ApprovalRoute.Infrastructure.Persistence.Entities;
|
|
|
|
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence;
|
|
|
|
public class ApprovalRouteDbContext(DbContextOptions<ApprovalRouteDbContext> options) : DbContext(options)
|
|
{
|
|
public DbSet<RequestType> RequestTypes => Set<RequestType>();
|
|
public DbSet<RequestListPartition> RequestListPartitions => Set<RequestListPartition>();
|
|
public DbSet<TemplateRoute> TemplateRoutes => Set<TemplateRoute>();
|
|
public DbSet<TemplateRouteStep> TemplateRouteSteps => Set<TemplateRouteStep>();
|
|
public DbSet<TemplateRouteStepCondition> TemplateRouteStepConditions => Set<TemplateRouteStepCondition>();
|
|
public DbSet<AppSetting> Settings => Set<AppSetting>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<AppSetting>(e =>
|
|
{
|
|
// Имя таблицы задано явно: соглашение по именам сделало бы из
|
|
// AppSetting «app_settings», а таблица должна называться так же, как
|
|
// в api-tnsu-portal, — settings.
|
|
e.ToTable("settings");
|
|
e.HasKey(x => x.OptionName);
|
|
e.Property(x => x.OptionName).HasMaxLength(128);
|
|
});
|
|
|
|
modelBuilder.Entity<RequestType>(e =>
|
|
{
|
|
e.HasKey(x => x.Id);
|
|
e.Property(x => x.Name).HasMaxLength(256).IsRequired();
|
|
e.Property(x => x.PageUrl).HasMaxLength(512).IsRequired();
|
|
e.Property(x => x.ListId).HasMaxLength(512);
|
|
e.Property(x => x.SiteUrl).HasMaxLength(512);
|
|
e.Property(x => x.Section).HasMaxLength(256).IsRequired();
|
|
});
|
|
|
|
modelBuilder.Entity<RequestListPartition>(e =>
|
|
{
|
|
e.HasKey(x => x.Id);
|
|
e.Property(x => x.ListId).HasMaxLength(512).IsRequired();
|
|
e.Property(x => x.Title).HasMaxLength(512);
|
|
e.HasOne(x => x.RequestType).WithMany(x => x.ListPartitions).HasForeignKey(x => x.RequestTypeId);
|
|
// Номер уникален в пределах типа заявок — вторая попытка записать тот же
|
|
// номер (например, при двойном запуске задачи) упадёт на этом индексе,
|
|
// а не создаст в реестре дубль.
|
|
e.HasIndex(x => new { x.RequestTypeId, x.Number }).IsUnique();
|
|
});
|
|
|
|
modelBuilder.Entity<TemplateRoute>(e =>
|
|
{
|
|
e.HasKey(x => x.Id);
|
|
e.HasOne(x => x.RequestType).WithMany(x => x.Routes).HasForeignKey(x => x.RequestTypeId);
|
|
e.HasIndex(x => new { x.RequestTypeId, x.DepartmentId, x.CompanyId, x.ByProject }).IsUnique();
|
|
});
|
|
|
|
modelBuilder.Entity<TemplateRouteStep>(e =>
|
|
{
|
|
e.HasKey(x => x.Id);
|
|
e.Property(x => x.ExecutorEmail).HasMaxLength(256);
|
|
e.Property(x => x.Position).HasMaxLength(128);
|
|
e.Property(x => x.ProjectRole).HasConversion<string>().HasMaxLength(32);
|
|
e.Property(x => x.HeadType).HasConversion<string>().HasMaxLength(32);
|
|
e.Property(x => x.Status).HasConversion<string>().HasMaxLength(32).IsRequired();
|
|
e.Property(x => x.ConditionsLogic).HasConversion<string>().HasMaxLength(16).IsRequired();
|
|
e.HasOne(x => x.Route).WithMany(x => x.Steps).HasForeignKey(x => x.RouteId);
|
|
e.HasIndex(x => new { x.RouteId, x.OrderNo });
|
|
});
|
|
|
|
modelBuilder.Entity<TemplateRouteStepCondition>(e =>
|
|
{
|
|
e.HasKey(x => x.Id);
|
|
e.Property(x => x.FieldName).HasMaxLength(128).IsRequired();
|
|
e.Property(x => x.Operation).HasConversion<string>().HasMaxLength(32).IsRequired();
|
|
e.Property(x => x.Value).HasMaxLength(256).IsRequired();
|
|
e.HasOne(x => x.Step).WithMany(x => x.Conditions).HasForeignKey(x => x.StepId).OnDelete(DeleteBehavior.Cascade);
|
|
e.HasIndex(x => new { x.StepId, x.OrderNo });
|
|
});
|
|
}
|
|
}
|