Files
Deal/src/core/Deal.Modules.Kanban/Application/ColumnRules/RulesDescriber.cs
T
stepan 1fbe29a838 Вынести магические строки статусов, видов и ключей в каталоги
Литералы статусов/видов/фаз и wire-ключей заменены каталогами: значения сущностей и репозиториев — TenantStatuses/TenantLimitPeriods/InviteStatuses/ContainerKinds/ContainerSpaces/CardIds/Discovery*/PipelineQueueStatuses; контакты и виды вложений — CardContactTypes/CardFileKinds; источники и этапы отсева — PipelineRejectStages/PipelineRejectSources; ключи промптов/провайдеров — SettingsFieldKeys; wire-ключи канбана — KanbanWireKeys; режимы правил — ColumnRuleModes; операции/фазы/виды диалогов/контакты Telegram и общий BoolText — в Deal.Contracts/Deal.SharedKernel; имена метрик — DealMetrics. Значения не менялись.
2026-09-13 20:51:36 +03:00

115 lines
3.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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));
}
}