Почистить комментарии от упоминаний процесса

Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны
ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками
на процесс удалены; то же в .proto. Правила обновлены в
docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
This commit is contained in:
Rustam Khalimov
2026-09-11 13:39:39 +03:00
parent 5f5538d33b
commit b053d58335
902 changed files with 3902 additions and 12074 deletions
@@ -1,13 +1,12 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «ИИ»: карточка создана/сгенерирована ИИ (агент, генератор, разборщик).
/// Источник «ИИ»: карточка создана/сгенерирована ИИ
/// </summary>
/// <remarks>Может быть составной частью <see cref="ICompositeSource"/>: первоисточник данных + ИИ-обработка.</remarks>
public interface IAiSource : ISource
{
/// <summary>
/// Id провайдера ИИ (deepseek/openai/ollama/…).
/// Id провайдера ИИ
/// </summary>
public string ProviderId { get; }
@@ -17,17 +16,17 @@ public interface IAiSource : ISource
public string Model { get; }
/// <summary>
/// Id ИИ-агента (если источник — агент, ищущий/обрабатывающий данные); null — не агент.
/// Id ИИ-агента
/// </summary>
public string? AgentId { get; }
/// <summary>
/// Ссылка на промпт/шаблон (если применимо); null — нет.
/// Ссылка на промпт/шаблон
/// </summary>
public string? PromptRef { get; }
/// <summary>
/// API, через который работал агент (для составных сценариев); null — нет.
/// API, через который работал агент
/// </summary>
public IApiSource? ViaApi { get; }
}
@@ -1,17 +1,17 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «внешний API»: карточка пришла из интеграции по API.
/// Источник «внешний API»
/// </summary>
public interface IApiSource : ISource
{
/// <summary>
/// Ключ интеграции/провайдера (конфигурация подключения живёт в настройках тенанта).
/// Ключ интеграции/провайдера
/// </summary>
public string ProviderId { get; }
/// <summary>
/// Адрес эндпоинта (или иной указатель в рамках провайдера); null — не применимо.
/// Адрес эндпоинта
/// </summary>
public string? Endpoint { get; }
}
@@ -3,13 +3,12 @@ using Deal.Modules.Cards.Application.Models;
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «атрибуты»: настраиваемые пользователем характеристики (стек, грейд, локация, сроки…).
/// Модуль «атрибуты»
/// </summary>
/// <remarks>Справочник атрибутов живёт в настройках тенанта; карточка хранит только значения по ключам.</remarks>
public interface IAttributedCard
{
/// <summary>
/// Значения атрибутов карточки (пусто — не заполнены).
/// Значения атрибутов карточки
/// </summary>
public IReadOnlyList<CardAttribute> Attributes { get; }
}
@@ -3,7 +3,7 @@ using Deal.Modules.Cards.Application.Models;
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «бюджет/цена»: деньги заявки (валюта исходная + сконвертированная).
/// Модуль «бюджет/цена»
/// </summary>
public interface IBudgetedCard
{
@@ -3,29 +3,20 @@ namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Ядро карточки: единственное, что есть у любой карточки во всех дашбордах.
/// </summary>
/// <remarks>
/// Никаких «полей заявки» в ядре: стек/бюджет/контакты и прочее — опциональные модули-роли
/// (см. <c>IContentCard</c>, <c>IBudgetedCard</c> и др.), которые реализует агрегат карточки
/// по мере наполнения. <see cref="Source"/> — откуда карточка пришла (см. иерархию <c>ISource</c>);
/// у карточки, созданной пайплайном из сообщения канала, источник составной:
/// <c>ICompositeSource { Origin: ITelegramSource, Pipeline: [IAiSource/IMl] }</c>.
/// Типизированный доступ к конкретному источнику — дискриминация через иерархию ISource
/// (паттерн-матчинг), отдельный generic-интерфейс не нужен.
/// </remarks>
public interface ICard
{
/// <summary>
/// Короткий id карточки (единый префикс карточек, напр. <c>c_</c>).
/// Короткий id карточки
/// </summary>
public string Id { get; }
/// <summary>
/// Заголовок карточки (очищенный).
/// Заголовок карточки
/// </summary>
public string Title { get; }
/// <summary>
/// Источник: откуда карточка пришла (полиморфный, не enum).
/// Источник: откуда карточка пришла
/// </summary>
public ISource Source { get; }
}
@@ -6,13 +6,6 @@ namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Единый механизм перехода карточки между контейнерами.
/// </summary>
/// <remarks>
/// Заменяет маршрутизацию перехода по эндпоинтам: одна точка знает и контейнеры-стадии пространства
/// «Выбранные», и контейнеры дашборда (доски/служебные зоны). Валидацию «откуда → куда можно» выполняют
/// политики контейнеров и «ворота» между пространствами (например, дашборд → «Выбранные» — только через
/// действие «взять в работу»; «Выбранные» → дашборд — запрещено). Побочные эффекты (запись истории для
/// стадий, сброс напоминания, журнал/обучение ML для дашборда) выполняет реализация перехода.
/// </remarks>
public interface ICardMover
{
/// <summary>
@@ -21,7 +14,6 @@ public interface ICardMover
/// <param name="cardId">Id карточки.</param>
/// <param name="toContainerId">Id контейнера назначения (стадия «Выбранных» либо дашборд-контейнер).</param>
/// <param name="ctx">Контекст перехода (инициатор, причина, обучение).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Результат: Error (400-текст отказа) | Exists=false (карточки нет, 404) | успех (Exists=true).</returns>
public Task<CardMoveResultDto> MoveAsync(
string cardId,
@@ -8,7 +8,7 @@ namespace Deal.Modules.Cards.Application.Abstractions;
public interface ICommentableCard
{
/// <summary>
/// Комментарии карточки (пусто — комментариев нет).
/// Комментарии карточки
/// </summary>
public IReadOnlyList<CardComment> Comments { get; }
}
@@ -1,13 +1,8 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Составной источник: цепочка «где взято → как обработано/доставлено».
/// Составной источник
/// </summary>
/// <remarks>
/// У карточки из пайплайна источник составной: <see cref="Origin"/> — первоисточник контента
/// (например, <c>ITelegramSource</c>), <see cref="Pipeline"/> — кто/что его обрабатывал
/// (например, ИИ-классификатор). Позволяет строить «третьи» источники без правки ядра.
/// </remarks>
public interface ICompositeSource : ISource
{
/// <summary>
@@ -16,7 +11,7 @@ public interface ICompositeSource : ISource
public ISource Origin { get; }
/// <summary>
/// Цепочка обработки (пусто — обработки не было).
/// Цепочка обработки
/// </summary>
public IReadOnlyList<ISource> Pipeline { get; }
}
@@ -8,12 +8,12 @@ namespace Deal.Modules.Cards.Application.Abstractions;
public interface IContactCard
{
/// <summary>
/// Квалифицированные контакты (пусто — контактов нет).
/// Квалифицированные контакты
/// </summary>
public IReadOnlyList<CardContact> Contacts { get; }
/// <summary>
/// Контактная строка «как в исходных данных» (fallback, если Contacts пуст).
/// Контактная строка «как в исходных данных»
/// </summary>
public string ContactText { get; }
}
@@ -1,44 +1,37 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Контейнер карточки: колонка дашборда, стадия «Выбранных» или служебная зона.
/// Контейнер карточки
/// </summary>
/// <remarks>
/// Общая база колонок/стадий/зон: пользовательские колонки (kind=board, создаёт пользователь/ИИ),
/// стадии (kind=stage, предзаданный каталог), «Неразобранное», архив, корзина, терминальные зоны.
/// Поведение контейнера — через <see cref="Policy"/> (роль, не enum-свойства): правила попадания,
/// допустимость возврата, автоочистка, терминальность. Пространство (дашборд/«Выбранные»/третий вид) —
/// свойство SpaceId: карточке безразличен вид, у неё только ContainerId.
/// </remarks>
public interface IContainer
{
/// <summary>
/// Короткий id контейнера (доски <c>b_…</c>, стадии <c>planned…</c>, служебные inbox/archive/trash).
/// Короткий id контейнера
/// </summary>
public string Id { get; }
/// <summary>
/// Имя для отображения («WPF», «В работе», «Архив»).
/// Имя для отображения
/// </summary>
public string Name { get; }
/// <summary>
/// Цвет контейнера (hex).
/// Цвет контейнера
/// </summary>
public string Color { get; }
/// <summary>
/// Позиция в пространстве (порядок показа слева направо).
/// Позиция в пространстве
/// </summary>
public int Order { get; }
/// <summary>
/// Id пространства (доски), которому принадлежит контейнер.
/// Id пространства
/// </summary>
public string SpaceId { get; }
/// <summary>
/// Политика контейнера: что можно/нельзя и что происходит (роль).
/// Политика контейнера
/// </summary>
public IContainerPolicy Policy { get; }
}
@@ -1,33 +1,27 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Политика контейнера: правила размещения карточек и жизненного цикла зоны.
/// Политика контейнера
/// </summary>
/// <remarks>
/// Роль контейнера (не enum): пользовательская колонка-фильтр, стадия, «Неразобранное», архив/корзина,
/// терминальная зона — каждая реализует политику по-своему. Политика отвечает на два вопроса:
/// (1) можно ли карточке попасть сюда (правила фильтрации) и (2) что происходит с карточкой здесь
/// (возврат, автоочистка, терминальность, напоминания).
/// </remarks>
public interface IContainerPolicy
{
/// <summary>
/// Правила попадания карточки в контейнер (null — фильтра нет, карточки кладутся вручную/ИИ).
/// Правила попадания карточки в контейнер
/// </summary>
public IContainerRules? Rules { get; }
/// <summary>
/// Можно ли вернуть карточку из контейнера на доску пространства (архив/корзина — да; терминальные — нет).
/// Можно ли вернуть карточку из контейнера на доску пространства
/// </summary>
public bool CanRestore { get; }
/// <summary>
/// Терминальная зона: карточка завершила жизненный путь, ручная очистка без возврата.
/// Терминальная зона
/// </summary>
public bool IsTerminal { get; }
/// <summary>
/// Автоочистка контейнера: срок хранения карточек (null — автоочистки нет).
/// Автоочистка контейнера
/// </summary>
public TimeSpan? Retention { get; }
}
@@ -3,38 +3,32 @@ using Deal.Modules.Cards.Application.Models;
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Правила попадания карточки в контейнер (набор опциональных фильтров).
/// Правила попадания карточки в контейнер
/// </summary>
/// <remarks>
/// Соответствует «правилам колонки» (§6.3): направление/стек/ключевые слова/грейд/уровень/цена/бюджет/
/// локация/тип и отрицательные исключения. Все группы опциональны; режим All/Any решает, как группы
/// сочетаются. Пустые правила — контейнер без фильтра (карточки кладутся вручную или ИИ/ML по контексту).
/// Проверка — по содержимому карточки (атрибуты, бюджет, текст), чистая функция.
/// </remarks>
public interface IContainerRules
{
/// <summary>
/// Режим сочетания групп: <c>all</c> — должны совпасть все включённые группы; <c>any</c> — хотя бы одна.
/// Режим сочетания групп
/// </summary>
public string Mode { get; }
/// <summary>
/// Ключевые слова/фразы (пусто — группа не участвует).
/// Ключевые слова/фразы
/// </summary>
public IReadOnlyList<string> Keywords { get; }
/// <summary>
/// Ключевые технологии/стек-атрибуты (пусто — не участвует).
/// Ключевые технологии/стек-атрибуты
/// </summary>
public IReadOnlyList<string> Stack { get; }
/// <summary>
/// Направление/тема (пусто — не участвует).
/// Направление/тема
/// </summary>
public IReadOnlyList<string> Directions { get; }
/// <summary>
/// Грейд/уровень (пусто — не участвует).
/// Грейд/уровень
/// </summary>
public IReadOnlyList<string> Grades { get; }
@@ -49,22 +43,22 @@ public interface IContainerRules
public IReadOnlyList<string> Locations { get; }
/// <summary>
/// Тип заявки: вакансия/фриланс/объявление (пусто — не участвует).
/// Тип заявки: вакансия/фриланс/объявление
/// </summary>
public IReadOnlyList<string> Types { get; }
/// <summary>
/// Бюджетный диапазон (null — не участвует).
/// Бюджетный диапазон
/// </summary>
public CardBudget? Budget { get; }
/// <summary>
/// Диапазон цены (отдельная группа §6.3; null — не участвует).
/// Диапазон цены
/// </summary>
public CardBudget? Prices { get; }
/// <summary>
/// Исключения: карточка не попадает, если в тексте/атрибутах есть хотя бы одно (veto).
/// Исключения: карточка не попадает, если в тексте/атрибутах есть хотя бы одно
/// </summary>
public IReadOnlyList<string> Exclude { get; }
}
@@ -1,16 +1,12 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «содержимое»: структурированный блок «О заявке» карточки.
/// Модуль «содержимое»
/// </summary>
/// <remarks>
/// Единая структура у всех карточек (Компания → Формат → О задаче → Требования → Будет плюсом → Условия);
/// у карточки без разобранного содержимого модуль пуст (Summary = "").
/// </remarks>
public interface IContentCard
{
/// <summary>
/// Блок «О заявке» (строки с метками, ≤2000).
/// Блок «О заявке»
/// </summary>
public string Summary { get; }
}
@@ -3,12 +3,12 @@ using Deal.Modules.Cards.Application.Models;
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «файлы» карточки (медиа/документы; объекты — в S3/MinIO).
/// Модуль «файлы» карточки
/// </summary>
public interface IFileCard
{
/// <summary>
/// Метаданные файлов карточки (пусто — файлов нет).
/// Метаданные файлов карточки
/// </summary>
public IReadOnlyList<CardFile> Files { get; }
}
@@ -1,12 +1,12 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «файл» (карточка создана из загруженного/импортированного файла).
/// Источник «файл»
/// </summary>
public interface IFileSource : ISource
{
/// <summary>
/// Ключ объекта в хранилище (MinIO/локальная папка).
/// Ключ объекта в хранилище
/// </summary>
public string ObjectKey { get; }
@@ -8,7 +8,7 @@ namespace Deal.Modules.Cards.Application.Abstractions;
public interface ILinkCard
{
/// <summary>
/// Прикреплённые ссылки (пусто — ссылок нет).
/// Прикреплённые ссылки
/// </summary>
public IReadOnlyList<CardLink> Links { get; }
}
@@ -1,9 +1,8 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «создано вручную/локально» (карточка без внешнего первоисточника).
/// Источник «создано вручную/локально»
/// </summary>
/// <remarks>Покрывает ручное создание карточки пользователем (в UI «Выбранных» и будущих дашбордов).</remarks>
public interface ILocalSource : ISource
{
/// <summary>
@@ -1,13 +1,8 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «размещение»: контейнер карточки и флаги состояния на доске.
/// Модуль «размещение»
/// </summary>
/// <remarks>
/// Контейнер (колонка/стадия/зона) — единственное «место» карточки; смена контейнера = переход карточки
/// (см. <see cref="ICardMover"/>). «Архив/корзина/отклонено/выполнено» — такие же контейнеры со своими
/// политиками, а не отдельные сущности. PrevContainerId — для возврата из архива/корзины.
/// </remarks>
public interface ILocatedCard
{
/// <summary>
@@ -16,12 +11,12 @@ public interface ILocatedCard
public string ContainerId { get; }
/// <summary>
/// Предыдущий контейнер (для возврата); null — возврат неприменим.
/// Предыдущий контейнер
/// </summary>
public string? PrevContainerId { get; }
/// <summary>
/// Признак «новое» (подсветка на доске; снимается просмотром/переходом).
/// Признак «новое»
/// </summary>
public bool IsNew { get; }
}
@@ -3,7 +3,7 @@ using Deal.Modules.Cards.Application.Models;
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «напоминание»: отложенная карточка (контейнер hold).
/// Модуль «напоминание»
/// </summary>
public interface IRemindableCard
{
@@ -1,17 +1,17 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «импорт данных»: карточка создана из строки/колонки таблицы (csv/sheet/БД).
/// Источник «импорт данных»
/// </summary>
public interface IRowSource : ISource
{
/// <summary>
/// Id таблицы/импорта (внешний идентификатор источника данных).
/// Id таблицы/импорта
/// </summary>
public string TableId { get; }
/// <summary>
/// Id строки в таблице (первичный ключ строки-источника).
/// Id строки в таблице
/// </summary>
public string RowId { get; }
@@ -1,28 +1,22 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник карточки: всё, откуда карточка может прийти.
/// Источник карточки
/// </summary>
/// <remarks>
/// Полиморфная иерархия вместо enum-свойства «тип источника»: конкретные варианты несут свои поля
/// (Telegram — диалог/сообщение/тема, Web — url, AI — провайдер/модель и т.д.), а общий контракт —
/// только подпись, ссылку на оригинал и сырое содержимое. Составной источник (<see cref="ICompositeSource"/>)
/// описывает цепочку «первоисточник → обработка» (например, сообщение Telegram, разобранное ИИ).
/// </remarks>
public interface ISource
{
/// <summary>
/// Подпись источника в UI: «@freelance», «hh.ru», «файл leads.csv»…
/// Подпись источника в UI
/// </summary>
public string DisplayName { get; }
/// <summary>
/// Ссылка на оригинал: t.me/…, https://…, objectKey; null — оригинала нет (локальное создание).
/// Ссылка на оригинал
/// </summary>
public string? OriginRef { get; }
/// <summary>
/// Сырое содержимое (текст/JSON), если хранится; null — не хранится.
/// Сырое содержимое
/// </summary>
public string? RawPayload { get; }
@@ -1,13 +1,8 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «Telegram»: канал/группа/чат/форум, конкретное сообщение.
/// Источник «Telegram»
/// </summary>
/// <remarks>
/// Покрывает все варианты источника Telegram: канал (DialogId=peer, TopicId=null), группа с темами
/// (TopicId задан), личный чат. Ссылка на оригинал строится из PeerHandle/PeerId и MessageId
/// (см. контракт tgSourceUrl текущего фронта).
/// </remarks>
public interface ITelegramSource : ISource
{
/// <summary>
@@ -21,7 +16,7 @@ public interface ITelegramSource : ISource
public long MessageId { get; }
/// <summary>
/// Handle канала (без «@»); null — диалог без username (приватный).
/// Handle канала
/// </summary>
public string? PeerHandle { get; }
@@ -31,7 +26,7 @@ public interface ITelegramSource : ISource
public string PeerName { get; }
/// <summary>
/// Id темы форума (для групп с темами); null — темы нет.
/// Id темы форума
/// </summary>
public string? TopicId { get; }
}
@@ -3,12 +3,12 @@ using Deal.Modules.Cards.Application.Models;
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Модуль «история движения» карточки (создание и смены контейнера).
/// Модуль «история движения» карточки
/// </summary>
public interface ITraceableCard
{
/// <summary>
/// История движения, свежие записи — в конце списка (пусто — истории нет).
/// История движения, свежие записи — в конце списка
/// </summary>
public IReadOnlyList<CardHistoryEntry> History { get; }
}
@@ -1,7 +1,7 @@
namespace Deal.Modules.Cards.Application.Abstractions;
/// <summary>
/// Источник «ссылка на сайт/объявление» (карточка создана из веб-страницы).
/// Источник «ссылка на сайт/объявление»
/// </summary>
public interface IWebSource : ISource
{
@@ -5,11 +5,6 @@ namespace Deal.Modules.Cards.Application.Dtos;
/// <summary>
/// Результат перехода карточки единым механизмом <see cref="ICardMover"/>.
/// </summary>
/// <remarks>
/// Error — 400-текст отказа (несуществующий контейнер/исходная колонка, запрет политики); Exists=false —
/// карточки нет (404). При успехе оба поля пусты/true — снимок карточки эндпоинт перечитывает единым
/// чтением (CardDto), чтобы наружу всегда уходила одна форма.
/// </remarks>
/// <param name="Error">Текст 400-ошибки либо null.</param>
/// <param name="Exists">True — карточка найдена и переход выполнен (либо перенос был no-op).</param>
public sealed record CardMoveResultDto(string? Error, bool Exists);
@@ -3,15 +3,8 @@ using Deal.Modules.Cards.Application.Abstractions;
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Агрегат карточки: ядро + опциональные модули (единая сущность всех дашбордов).
/// Агрегат карточки
/// </summary>
/// <remarks>
/// Один класс на карточку (не иерархия видов): вид определяется контейнером и наполненностью модулей,
/// а не типом. Модули — это секции данных (роли интерфейсов), пустые, пока карточка «входящая»,
/// наполняются, когда карточка «в работе» (файлы/ссылки/ТЗ/история/напоминания). Класс неизменяемый
/// (init/свойства только для чтения) — изменения применяются сервисами через порт и возвращают новый
/// снимок, как единая CardDto (wire 1:1 сохраняется).
/// </remarks>
public sealed class Card :
ICard,
IContentCard,
@@ -1,27 +1,22 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Настраиваемый атрибут карточки: имя, значение и (опционально) единица измерения.
/// Настраиваемый атрибут карточки
/// </summary>
/// <remarks>
/// Стек/грейд/локация/сроки/площадь и прочее — не «зашитые» поля карточки, а атрибуты из справочника
/// тенанта (пользователь настраивает в UI, «стек» — лишь частый атрибут). Один атрибут = одна запись
/// «ключ → значение»; ключом служит строковый id из справочника атрибутов.
/// </remarks>
public sealed record CardAttribute
{
/// <summary>
/// Id атрибута (ключ справочника: <c>stack</c>, <c>grade</c>, <c>location</c>, …).
/// Id атрибута (ключ справочника
/// </summary>
public string Key { get; init; } = string.Empty;
/// <summary>
/// Значение атрибута (например, «Python», «Middle», «Москва»).
/// Значение атрибута
/// </summary>
public string Value { get; init; } = string.Empty;
/// <summary>
/// Единица измерения (₽/₽/час/м²/…); null — безразмерный.
/// Единица измерения
/// </summary>
public string? Unit { get; init; }
}
@@ -1,7 +1,7 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Бюджет/цена карточки в исходной валюте и (опционально) сконвертированный в целевую.
/// Бюджет/цена карточки в исходной валюте и
/// </summary>
public sealed record CardBudget
{
@@ -1,7 +1,7 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Контакт заказчика: квалифицированная запись (тип + значение).
/// Контакт заказчика
/// </summary>
public sealed record CardContact
{
@@ -1,7 +1,7 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Метаданные прикреплённого файла карточки (объект хранится в S3/MinIO или локальной папке).
/// Метаданные прикреплённого файла карточки
/// </summary>
public sealed record CardFile
{
@@ -21,7 +21,7 @@ public sealed record CardFile
public long SizeBytes { get; init; }
/// <summary>
/// Тип контента: image/video/audio/archive/document/other (определяет детектор).
/// Тип контента: image/video/audio/archive/document/other
/// </summary>
public string Kind { get; init; } = "other";
@@ -16,7 +16,7 @@ public sealed record CardHistoryEntry
public long AtMs { get; init; }
/// <summary>
/// Тип события: <c>created</c>|<c>createdLocal</c>|id контейнера назначения (движение).
/// Тип события: <c>created</c>|<c>createdLocal</c>|id контейнера назначения
/// </summary>
public string Type { get; init; } = string.Empty;
}
@@ -1,34 +1,27 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Реестр коротких id модуля Cards и служебных контейнеров (единый владелец карточки).
/// Реестр коротких id модуля Cards и служебных контейнеров
/// </summary>
/// <remarks>
/// Единый префикс карточек <c>c_</c> приходит на смену <c>l_</c> (канбан) и <c>pr_</c> (проекты) — при
/// слиянии таблиц у карточки один id, не зависящий от пространства. Служебные контейнеры
/// (<c>inbox</c>/<c>archive</c>/<c>trash</c>) — фиксированные зоны любого пространства-дашборда.
/// Контейнеры-доски (<c>b_…</c>) создаёт пользователь/ИИ; контейнеры-стадии — предзаданный каталог
/// (см. CardsDefaultContainers). Id генерирует модуль (PrefixId), хранилище получает готовые.
/// </remarks>
public static class CardIds
{
/// <summary>
/// Префикс id карточки (единый для всех дашбордов).
/// Префикс id карточки
/// </summary>
public const string CardPrefix = "c_";
/// <summary>
/// Контейнер «Неразобранное»: новые карточки пайплайна до раскладки по колонкам.
/// Контейнер «Неразобранное»
/// </summary>
public const string Inbox = "inbox";
/// <summary>
/// Контейнер «Архив»: карточки старше срока архивации (автоочистка по политике).
/// Контейнер «Архив»
/// </summary>
public const string Archive = "archive";
/// <summary>
/// Контейнер «Корзина»: удалённые карточки (автоочистка по политике).
/// Контейнер «Корзина»
/// </summary>
public const string Trash = "trash";
}
@@ -11,12 +11,12 @@ public sealed record CardLink
public string Id { get; init; } = string.Empty;
/// <summary>
/// Подпись ссылки (пусто — url как подпись).
/// Подпись ссылки
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// Адрес ссылки (http/https).
/// Адрес ссылки
/// </summary>
public string Url { get; init; } = string.Empty;
}
@@ -1,7 +1,7 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Напоминание об «Отложено» (карточка в контейнере hold).
/// Напоминание об «Отложено»
/// </summary>
public sealed record CardReminder
{
@@ -11,7 +11,7 @@ public sealed record CardReminder
public long AtMs { get; init; }
/// <summary>
/// Признак «напоминание выстрелило» (повторно не срабатывает до переноса/переустановки).
/// Признак «напоминание выстрелило»
/// </summary>
public bool Fired { get; init; }
}
@@ -1,19 +1,12 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Каталог контейнеров по умолчанию пространства «Выбранные» (стадии) и служебных зон.
/// Каталог контейнеров по умолчанию пространства «Выбранные»
/// </summary>
/// <remarks>
/// Приходит на смену ProjectStages модуля Projects: стадии — это контейнеры kind=stage предзаданного
/// каталога (терминальные — finished/rejected). Хранилище провижинит их тенанту как строки Containers;
/// каталог — единственный источник имён/цветов/порядка (как ProjectStages.All). Служебные зоны
/// (inbox/archive/trash) — фиксированные контейнеры CardIds. Доски (kind=board) каталогу не принадлежат:
/// их создаёт пользователь/ИИ.
/// </remarks>
public static class CardsDefaultContainers
{
/// <summary>
/// Id стадии «Запланировано» (цель перехода «взять в работу»).
/// Id стадии «Запланировано»
/// </summary>
public const string Planned = "planned";
@@ -43,7 +36,7 @@ public static class CardsDefaultContainers
public const string Ready = "ready";
/// <summary>
/// Id стадии «Отложено» (напоминания сбрасываются переносом).
/// Id стадии «Отложено»
/// </summary>
public const string Hold = "hold";
@@ -53,12 +46,12 @@ public static class CardsDefaultContainers
public const string Finished = "finished";
/// <summary>
/// Id терминальной стадии «Отклонено» (очистка вручную, без возврата).
/// Id терминальной стадии «Отклонено»
/// </summary>
public const string Rejected = "rejected";
/// <summary>
/// Все контейнеры по умолчанию (стадии пространства «Выбранные»), порядок канбана.
/// Все контейнеры по умолчанию
/// </summary>
public static IReadOnlyList<DefaultContainer> All { get; } = new List<DefaultContainer>
{
@@ -74,12 +67,12 @@ public static class CardsDefaultContainers
};
/// <summary>
/// Id всех контейнеров-стадий «Выбранных» (для фильтра пространства в выборках хранилища).
/// Id всех контейнеров-стадий «Выбранных»
/// </summary>
public static IReadOnlyList<string> Ids { get; } = All.Select(container => container.Id).ToArray();
/// <summary>
/// Проверка: контейнер с таким id есть в каталоге (валидация move/ручного создания).
/// Проверка: контейнер с таким id есть в каталоге
/// </summary>
/// <param name="containerId">Проверяемый id.</param>
/// <returns>True — контейнер известен каталогу.</returns>
@@ -1,7 +1,7 @@
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Описание контейнера по умолчанию (запись каталога CardsDefaultContainers).
/// Описание контейнера по умолчанию
/// </summary>
public sealed record DefaultContainer
{
@@ -3,15 +3,12 @@ using Deal.Modules.Cards.Application.Abstractions;
namespace Deal.Modules.Cards.Application.Models;
/// <summary>
/// Контекст перехода карточки: кто инициировал и что делать с обучением.
/// Контекст перехода карточки
/// </summary>
/// <remarks>Переход — единый механизм (<see cref="ICardMover"/>); контекст отличает действие пользователя
/// (обучать ML), системы (автоархив/очистка) и ИИ/ML (классификация). Reason — для возвратов из отсева
/// и ручных исключений, чтобы ML/ИИ учились на решении.</remarks>
public sealed record TransitionContext
{
/// <summary>
/// Инициатор перехода: <c>user</c>|<c>system</c>|<c>ai</c>|<c>ml</c>.
/// Инициатор перехода
/// </summary>
public required string Actor { get; init; }
@@ -21,7 +18,7 @@ public sealed record TransitionContext
public string? Reason { get; init; }
/// <summary>
/// Обучать ML на этом действии (действия пользователя — да; системные — нет).
/// Обучать ML на этом действии
/// </summary>
public bool Learn { get; init; }
}