Литералы статусов/видов/фаз и wire-ключей заменены каталогами: значения сущностей и репозиториев — TenantStatuses/TenantLimitPeriods/InviteStatuses/ContainerKinds/ContainerSpaces/CardIds/Discovery*/PipelineQueueStatuses; контакты и виды вложений — CardContactTypes/CardFileKinds; источники и этапы отсева — PipelineRejectStages/PipelineRejectSources; ключи промптов/провайдеров — SettingsFieldKeys; wire-ключи канбана — KanbanWireKeys; режимы правил — ColumnRuleModes; операции/фазы/виды диалогов/контакты Telegram и общий BoolText — в Deal.Contracts/Deal.SharedKernel; имена метрик — DealMetrics. Значения не менялись.
115 lines
3.9 KiB
C#
115 lines
3.9 KiB
C#
using System.Globalization;
|
||
using Deal.Modules.Kanban.Application.Models;
|
||
|
||
namespace Deal.Modules.Kanban.Application.ColumnRules;
|
||
|
||
/// <summary>
|
||
/// Человекочитаемое описание правил колонки для note/подсказки.
|
||
/// </summary>
|
||
public static class RulesDescriber
|
||
{
|
||
/// <summary>
|
||
/// Описывает правила колонки.
|
||
/// </summary>
|
||
/// <param name="rules">Правила колонки (null/пустые → «без правил (решает ИИ/ML)»).</param>
|
||
/// <returns>Строка-расшифровка для UI/промпта.</returns>
|
||
public static string Describe(ContainerRulesDto? rules)
|
||
{
|
||
if (rules is null)
|
||
{
|
||
return NoRulesText;
|
||
}
|
||
|
||
var parts = new List<string>();
|
||
if (rules.Direction.Count > 0)
|
||
{
|
||
parts.Add($"направление: {JoinLimited(rules.Direction, DirectionLimit)}");
|
||
}
|
||
|
||
if (rules.Stack.Count > 0)
|
||
{
|
||
parts.Add($"стек: {JoinLimited(rules.Stack, TermsLimit)}");
|
||
}
|
||
|
||
if (rules.Keywords.Count > 0)
|
||
{
|
||
parts.Add($"слова: {JoinLimited(rules.Keywords, TermsLimit)}");
|
||
}
|
||
|
||
if (rules.Grade.Count > 0)
|
||
{
|
||
parts.Add($"грейд: {JoinLimited(rules.Grade, TermsLimit)}");
|
||
}
|
||
|
||
if (rules.Levels is { Count: > 0 })
|
||
{
|
||
parts.Add($"уровень: {JoinLimited(rules.Levels, TermsLimit)}");
|
||
}
|
||
|
||
if (rules.Locations is { Count: > 0 })
|
||
{
|
||
parts.Add($"локация: {JoinLimited(rules.Locations, TermsLimit)}");
|
||
}
|
||
|
||
if (rules.Types is { Count: > 0 })
|
||
{
|
||
parts.Add($"тип: {JoinLimited(rules.Types, TermsLimit)}");
|
||
}
|
||
|
||
if (rules.Exclude.Count > 0)
|
||
{
|
||
parts.Add($"исключено: {JoinLimited(rules.Exclude, TermsLimit)}");
|
||
}
|
||
|
||
AddRangePart(parts, rules.Budget, "бюджет");
|
||
AddRangePart(parts, rules.Prices, "цена");
|
||
|
||
if (parts.Count == 0)
|
||
{
|
||
return NoRulesText;
|
||
}
|
||
|
||
string mode = string.Equals(rules.Mode, ColumnRuleModes.Any, StringComparison.OrdinalIgnoreCase)
|
||
? AnyModePrefix
|
||
: AllModePrefix;
|
||
return mode + " · " + string.Join("; ", parts);
|
||
}
|
||
|
||
private const string NoRulesText = "без правил (решает ИИ/ML)";
|
||
|
||
private const string AllModePrefix = "все условия";
|
||
|
||
// Префикс режима «любое из условий».
|
||
private const string AnyModePrefix = "любое из условий";
|
||
|
||
private const int DirectionLimit = 6;
|
||
|
||
private const int TermsLimit = 8;
|
||
|
||
// Добавляет в описание диапазон группы (бюджет/цена), если задана хотя бы одна граница.
|
||
// parts: Накопитель частей описания.
|
||
// range: Диапазон группы (null — группа выключена).
|
||
// title: Подпись группы («бюджет»/«цена»).
|
||
private static void AddRangePart(
|
||
List<string> parts,
|
||
BudgetRangeDto? range,
|
||
string title)
|
||
{
|
||
if (range is null || (range.From is null && range.To is null))
|
||
{
|
||
return;
|
||
}
|
||
|
||
string lo = range.From is not null ? range.From.Value.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
||
string hi = range.To is not null ? range.To.Value.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
||
string cur = range.Cur ?? string.Empty;
|
||
parts.Add($"{title}: {lo}–{hi} {cur}".Replace("– ", "–").Replace(" –", "–"));
|
||
}
|
||
|
||
private static string JoinLimited(IReadOnlyList<string> terms, int limit)
|
||
{
|
||
int count = Math.Min(terms.Count, limit);
|
||
return string.Join(", ", terms.Take(count));
|
||
}
|
||
}
|