Разбить модули Deal.Modules.* по назначению
Application проектов Discovery, Kanban, Pipeline, Settings, Tenants разделён на Abstractions/Exceptions/Extensions/Models/Registrars/Services; namespace приведён к путям, using потребителей мигрированы и дедуплицированы (169 файлов), cref/FQN обновлены.
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user