Больше двух параметров — каждый на отдельной строке (закрывающая скобка в конце последнего); два и меньше — в одну строку. Правило добавлено в docs/spec/Код-стайл-Дейл.md; применено к 628 сигнатурам в 253 файлах.
129 lines
8.6 KiB
C#
129 lines
8.6 KiB
C#
using Deal.Contracts.Integrations;
|
||
using Deal.Contracts.Integrations.Models;
|
||
using Deal.Modules.Kanban.Application.ColumnRules;
|
||
using Deal.Modules.Kanban.Application.Models;
|
||
using Deal.Modules.Settings.Application.Abstractions;
|
||
using Deal.Modules.Settings.Application.Models;
|
||
using Deal.Modules.Settings.Application.Registrars;
|
||
using Deal.Modules.Settings.Application.Services;
|
||
// Алиас: статический класс ColumnRules лежит в одноимённом пространстве имён — внутри пространства имён
|
||
// Deal.Modules.Kanban.Application имя ColumnRules резолвится в пространство (CS0234), нужен явный алиас.
|
||
using KanbanColumnRules = Deal.Modules.Kanban.Application.ColumnRules.ColumnRules;
|
||
using Deal.Modules.Kanban.Application.Abstractions;
|
||
using Deal.Modules.Kanban.Application.Extensions;
|
||
using Deal.Modules.Kanban.Application.Registrars;
|
||
|
||
namespace Deal.Modules.Kanban.Application.Services;
|
||
|
||
/// <summary>
|
||
/// Сервис карточек — единый домен карточки (дашборд и «Выбранные»): leads.py L151–279 + L509–551
|
||
/// и projects.py L103–282 (этап 9).
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// Чистый сервис модуля (без EF/HTTP): оркестрирует <see cref="ICardStore"/> (карточки/контейнеры/
|
||
/// комментарии/журнал), <see cref="ISettingsStore"/> (кэш курсов <c>ratesCache</c>, настройка напоминаний,
|
||
/// Ruling 3/7), <see cref="IMlClient"/> (обучающие сигналы move/trash/restore и счётчики counts) и
|
||
/// <see cref="IFileStorage"/> (объекты вложений карточки).
|
||
/// Полные карточки (JSON-поля, comments, human-метка time, matchHits) собирает адаптер (маппинг
|
||
/// строки → CardDto в хранилище); сервис добавляет поведение: валидации с фиксированными текстами прототипа,
|
||
/// пересчёт matchHits при размещении в доску (Ruling 2, ColumnRules), журнал CardMoves и push-сигналы ML,
|
||
/// операции пространства «Выбранные» (создание, патч, ссылки, файлы, перенос по стадии с историей,
|
||
/// напоминания) на той же сущности карточки.
|
||
/// Обучение ML идёт ВСЕГДА и синхронно — выключатель <c>mlEnabled</c> управляет только использованием ML в
|
||
/// пайплайне, а не записью действий пользователя (ml_client.py L6–7: «обучение идёт всегда»; Ruling 4).
|
||
/// 404-семантика «Карточка не найдена» выражается null-результатом методов; тексты 400 — константы класса.
|
||
/// C32: класс разделён на partial-файлы по темам (Operations/Helpers/Selected/Files/Reminders).
|
||
/// </remarks>
|
||
/// <param name="store">Единый порт хранилища карточек/контейнеров/журнала тенанта.</param>
|
||
/// <param name="settings">KV-хранилище настроек тенанта (курсы, напоминания).</param>
|
||
/// <param name="mlClient">Клиент ML: PushAsync — обучающий сигнал действия, StatusAsync — счётчики counts.</param>
|
||
/// <param name="storage">Файловое хранилище вложений карточки (объекты файлов).</param>
|
||
public sealed partial class CardsService
|
||
{
|
||
private readonly ICardStore _store;
|
||
private readonly ISettingsStore _settings;
|
||
private readonly IMlClient _mlClient;
|
||
private readonly IFileStorage _storage;
|
||
|
||
/// <summary>
|
||
/// Создаёт сервис карточек над портами модуля (поле-захват DI-зависимостей).
|
||
/// </summary>
|
||
/// <param name="store">Единый порт хранилища карточек/контейнеров/журнала тенанта.</param>
|
||
/// <param name="settings">KV-хранилище настроек тенанта (курсы, напоминания).</param>
|
||
/// <param name="mlClient">Клиент ML: PushAsync — обучающий сигнал действия, StatusAsync — счётчики counts.</param>
|
||
/// <param name="storage">Файловое хранилище вложений карточки (объекты файлов).</param>
|
||
public CardsService(
|
||
ICardStore store,
|
||
ISettingsStore settings,
|
||
IMlClient mlClient,
|
||
IFileStorage storage)
|
||
{
|
||
_store = store;
|
||
_settings = settings;
|
||
_mlClient = mlClient;
|
||
_storage = storage;
|
||
}
|
||
|
||
// ── Фиксированные строки прототипа (400-детали; leads.py L183–184, L239–240, dashboard_routes L241) ──
|
||
|
||
/// <summary>
|
||
/// 400 move: целевой контейнер не существует (или это «Неразобранное» — оно допустимо).
|
||
/// </summary>
|
||
public const string MoveTargetInvalidDetail = "Переносить можно только в существующий контейнер или в «Неразобранное»";
|
||
|
||
/// <summary>
|
||
/// 400 move: исходная колонка archive/trash — из них карточку выводит только restore_lead
|
||
/// (кнопка «Вернуть»): прямой перенос в доску миновал бы снятие метки «спам» при возврате из корзины
|
||
/// (Ruling 4, PushAsync(spam, −1.0) только в RestoreLeadAsync) и журнал restore.
|
||
/// </summary>
|
||
public const string MoveSourceRestrictedDetail = "Переносить из корзины, архива или «взятых в работу» нельзя — верните карточку на канбан";
|
||
|
||
/// <summary>
|
||
/// 400 clear-col: колонка не trash/archive (clear_col L239–240).
|
||
/// </summary>
|
||
public const string ClearColInvalidDetail = "Очищать можно только корзину или архив";
|
||
|
||
/// <summary>
|
||
/// 400 комментарий: пустой текст после Trim (dashboard_routes L240–241).
|
||
/// </summary>
|
||
public const string EmptyCommentDetail = "Пустой комментарий";
|
||
|
||
// ── Журнал CardMoves: действия (leads.py _log_learning L40–44) ──────────
|
||
|
||
// Действие журнала: перенос на доску/в «Неразобранное» (_move L174).
|
||
private const string ActionMove = "move";
|
||
|
||
// Действие журнала: в корзину (_move action='trash' L196).
|
||
private const string ActionTrash = "trash";
|
||
|
||
// Действие журнала: возврат из архива/корзины (restore_lead L216).
|
||
private const string ActionRestore = "restore";
|
||
|
||
// Действие журнала: добавлен комментарий (add_comment L264).
|
||
private const string ActionComment = "comment";
|
||
|
||
/// <summary>
|
||
/// Автор комментария — «Вы» (свои комментарии, add_comment L262). Единственный источник строки
|
||
/// для комментариев карточки.
|
||
/// </summary>
|
||
public const string CommentAuthor = "Вы";
|
||
|
||
/// <summary>
|
||
/// Human-метка времени свежего комментария (add_comment L259–265; «только что» = возраст < 1 мин).
|
||
/// Единственный источник строки: адаптер KanbanStore считает её в HumanAge.
|
||
/// </summary>
|
||
public const string JustNowLabel = "только что";
|
||
|
||
// Минимальная длина поискового запроса после Trim: q короче → пустой ответ (search L511–512, Ruling 6).
|
||
private const int MinSearchQueryLength = 2;
|
||
|
||
// Ограничение результатов поиска: не больше 12 карточек (search L509, Ruling 6).
|
||
private const int SearchLimit = 12;
|
||
|
||
// Вес сигнала пользователя: действие = истина (ml_client.py USER_WEIGHT L26, Ruling 4).
|
||
private const double PushWeightUser = 1.0;
|
||
|
||
// Вес снятия метки: возврат из корзины (restore_lead L221, delta=-1.0).
|
||
private const double PushWeightUnlearn = -1.0;
|
||
}
|