feat(api): в Works добавлена маска динамической генерации имени шаблона; добавлены Shortcodes; актализированы валидация, запросы,ответы; миграция БД - запрет приёма нулевых значений в маску

This commit is contained in:
Mikhail Kuznetsov
2025-04-17 16:38:35 +10:00
parent ecd3bdac9b
commit a89e7f7b86
14 changed files with 3375 additions and 8 deletions

View File

@@ -3,6 +3,7 @@
public class WorkRequest
{
public required string Name { get; set; }
public required string TemplateNameMask { get; set; }
public string? TemplateSuffix { get;set; }
public int EsppId { get; set; }
public Guid TnkId { get; set; }

View File

@@ -6,6 +6,8 @@
public required string Name { get; set; }
public required string TemplateNameMask { get; set; }
public string? TemplateSuffix { get; set; }
public int EsppId { get; set; }

View File

@@ -137,6 +137,7 @@ namespace PARR.API.Controllers.V1
{
Id = Guid.NewGuid(),
Name = request.Name.Trim(),
TemplateNameMask = request.TemplateNameMask,
EsppId = request.EsppId,
TnkId = request.TnkId,
TemplateSuffix = request.TemplateSuffix
@@ -179,6 +180,7 @@ namespace PARR.API.Controllers.V1
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при изменении вида работ. Не найдена работа Id: {id}" } }));
orig.Name = request.Name.Trim();
orig.TemplateNameMask = request.TemplateNameMask.Trim();
orig.EsppId = request.EsppId;
orig.TnkId = request.TnkId;
orig.DateModified = DateTimeOffset.UtcNow;

View File

@@ -1,5 +1,7 @@
using FluentValidation;
using PARR.API.Contracts.V1.Requests;
using PARR.BLL.Services.Interfaces;
using PARR.Constants.Shortcodes;
using PARR.DAL.Services.Interfaces;
namespace PARR.API.Validators
@@ -11,13 +13,27 @@ namespace PARR.API.Validators
private bool? isValid = null;
public WorkRequestValidator(
ITnkService tnkService
ITnkService tnkService,
ITemplateMaskValidator templateMaskValidator
)
{
this.tnkService = tnkService;
RuleFor(t => t.Name).NotEmpty().NotNull();
RuleFor(t => t.TemplateNameMask)
.NotEmpty()
.NotNull();
RuleFor(t => t.TemplateNameMask)
.Must((entity, value, c) => templateMaskValidator
.IsValid(entity.TemplateNameMask))
.WithMessage($"Неверно указана маска имени шаблонов. Обязательными условиями являются" +
$" наличие кодового слова \"ПАРР\" и обязательное использование динамической составляющей" +
$" %ЭК%. Доступные значения динамических составляющих:" +
$" {string.Join(", ", Shortcodes.GetAll().Select(sc => sc.Name + " - " + sc.Description))}"
);
RuleFor(t => t.EsppId).NotEmpty().NotNull().WithMessage("EsppId не может быть пустым");
RuleFor(t => t.TnkId).NotEmpty().NotNull();

View File

@@ -22,6 +22,7 @@ namespace PARR.BLL
services.AddTransient<ITransformService, TransformService>();
services.AddTransient<IIntervalService, IntervalService>();
services.AddTransient<ICalendarService, CalendarService>();
services.AddTransient<ITemplateMaskValidator, TemplateMaskValidator>();
}
}
}

View File

@@ -0,0 +1,43 @@
using PARR.BLL.Services.Interfaces;
using PARR.Constants.Shortcodes;
using System.Text.RegularExpressions;
namespace PARR.BLL.Services.Implementations
{
internal class TemplateMaskValidator : ITemplateMaskValidator
{
public bool IsValid(string mask)
{
if (!mask.Contains("ПАРР"))
return false;
var shortcodePattern = "%[^%\\s]+%";
var shortcodesInMask = Regex.Matches(mask, shortcodePattern).ToList();
if (shortcodesInMask.Count() == 0)
return false;
var validShortcodes = GetShortcodesNames();
foreach (var shortcode in shortcodesInMask)
{
if (!validShortcodes.Contains(shortcode.ToString().ToUpper()))
return false;
}
return true;
}
private List<string> GetShortcodesNames()
{
var result = new List<string>();
var shortcodes = Enum.GetValues(typeof(ShortcodeEnum)).Cast<ShortcodeEnum>();
foreach (var item in shortcodes)
{
result.Add(Shortcodes.GetShortcodeName(item).ToUpper());
}
return result;
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PARR.BLL.Services.Interfaces
{
public interface ITemplateMaskValidator
{
bool IsValid(string mask);
}
}

View File

@@ -3,7 +3,8 @@
public enum ShortcodeEnum
{
EK,
ZO
ZO,
Work
//!!!Не забыть добавить в справочник имен (ShortcodeName) строковое значение
}
}

View File

@@ -1,13 +1,12 @@
using System.Data.Common;
namespace PARR.Constants.Shortcodes
namespace PARR.Constants.Shortcodes
{
public static class Shortcodes
{
private static readonly List<ShortcodeItem> ShortcodesList = new List<ShortcodeItem>()
{
new ShortcodeItem { ShortcodeEnum = ShortcodeEnum.EK, Name = "%ЭК%", Description = "Элемент конфигурации"},
new ShortcodeItem { ShortcodeEnum = ShortcodeEnum.ZO, Name = "%ЗО%", Description = "Зона ответственности"}
new ShortcodeItem { ShortcodeEnum = ShortcodeEnum.ZO, Name = "%ЗО%", Description = "Зона ответственности"},
new ShortcodeItem { ShortcodeEnum = ShortcodeEnum.Work, Name = "%РАБОТА%", Description = "Наименование работ"}
};
@@ -16,5 +15,9 @@ namespace PARR.Constants.Shortcodes
return ShortcodesList.First(s => s.ShortcodeEnum == shortcode).Name;
}
public static List<ShortcodeItem> GetAll()
{
return ShortcodesList;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PARR.DAL.Migrations
{
/// <inheritdoc />
public partial class tblWorksClmnTemplateNameMaskIsNotNull : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "TemplateNameMask",
table: "Works",
type: "text",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "TemplateNameMask",
table: "Works",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "text");
}
}
}

View File

@@ -2611,6 +2611,7 @@ namespace PARR.DAL.Migrations
.HasColumnType("text");
b.Property<string>("TemplateNameMask")
.IsRequired()
.HasColumnType("text");
b.Property<string>("TemplateSuffix")

View File

@@ -21,7 +21,7 @@ namespace PARR.DAL.Models
/// <summary>
/// Маска для динамического формирования имени шаблона
/// </summary>
public string? TemplateNameMask { get; set; }
public required string TemplateNameMask { get; set; }
/// <summary>
/// Короткое имя, для формирования имени шаблона

View File

@@ -100,7 +100,8 @@ namespace PARR.MockData.Services
DateCreated = DateTimeOffset.UtcNow,
Name = "Прочее(работы)",
EsppId = 292917,
TnkId = tnk.Id
TnkId = tnk.Id,
TemplateNameMask= "%ЗО%-ЭИТИ-ПТК-ПАРР__%ЭК%__%РАБОТА%"
};
if (!await workService.CreateAsync(work) || !await workService.CommitAsync())
{