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

Удалены <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
@@ -8,32 +8,23 @@ using Deal.Modules.Settings.Application.Models;
namespace Deal.Modules.Discovery.Application.Services;
/// <summary>
/// Сервис задач поиска Discovery — create/patch/delete/start/pause/advance/bump (план Task 17, 1:1 discovery.py L234–381).
/// Сервис задач поиска Discovery — create/patch/delete/start/pause/advance/bump.
/// </summary>
/// <remarks>
/// Чистый оркестратор поверх портов <see cref="IDiscoveryStore"/> (таблицы DiscTasks схемы тенанта),
/// <see cref="ISettingsStore"/> (дефолты discEvalThreshold/discEvalSample) и <see cref="DiscoveryPlanGuard"/>
/// (план-бюджет, Ruling 9). 404-семантика — null-результат (эндпоинт Task 19 отвечает «Задача не найдена»);
/// 400-семантика — <see cref="DiscoveryValidationException"/> с текстом python (создание без имени/бюджет,
/// start без ключевых слов). Прогресс поиска (SearchIdx/SearchDone/cчётчики) ведут методы
/// <see cref="AdvanceSearchAsync"/> и <see cref="BumpCounterAsync"/> — их зовут кандидатный сервис и воркер (Task 18).
/// </remarks>
public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGuard planGuard, ISettingsStore settings)
{
/// <summary>
/// 400 create: пустое название после Trim (create_task L242–243).
/// 400 create: пустое название после Trim.
/// </summary>
public const string NameRequiredDetail = "Укажите название задачи";
/// <summary>
/// 400 start: у задачи нет ключевых слов (start_task L330).
/// 400 start: у задачи нет ключевых слов.
/// </summary>
public const string NoKeywordsDetail = "Нет ключевых слов для поиска — добавьте их в задачу";
/// <summary>
/// Все задачи, старые первыми (list_tasks L189–191; воркер берёт самую старую running).
/// Все задачи, старые первыми.
/// </summary>
/// <param name="ct">Токен отмены.</param>
/// <returns>Задачи в порядке создания.</returns>
public Task<IReadOnlyList<DiscoveryTaskDto>> ListAsync(CancellationToken ct)
{
@@ -41,10 +32,9 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Одна задача по id (get_task L194–196).
/// Одна задача по id.
/// </summary>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Задача или null (404 «Задача не найдена» у эндпоинта).</returns>
public Task<DiscoveryTaskDto?> GetAsync(string taskId, CancellationToken ct)
{
@@ -52,16 +42,9 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Создаёт задачу поиска (create_task L234–282: имя, план 1..discJoinLimit, бюджет активных задач).
/// Создаёт задачу поиска.
/// </summary>
/// <remarks>
/// 1:1 с прототипом: name обязательное (после Trim); planJoins — дефолт 1 + <see cref="DiscoveryPlanGuard"/>;
/// threshold/sampleSize — из настроек discEvalThreshold/discEvalSample (дефолты 40/10); границы значений
/// клампятся как _validate_task_values L213–231. Задача создаётся draft, поиск не запущен; ключи ИИ-генерации
/// при создании НЕ запрашиваются (endpoint generate-keywords — Task 19, прототип discovery_routes L189–211).
/// </remarks>
/// <param name="draft">Поля новой задачи (см. <see cref="DiscoveryTaskDraft"/>).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Созданная задача (полный DTO).</returns>
/// <exception cref="DiscoveryValidationException">Пустое имя / план вне 1..discJoinLimit / бюджет исчерпан.</exception>
public async Task<DiscoveryTaskDto> CreateAsync(DiscoveryTaskDraft draft, CancellationToken ct)
@@ -115,14 +98,10 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Обновляет поля задачи (patch_task L285–311; рост plan_joins — с проверкой бюджета).
/// Обновляет поля задачи.
/// </summary>
/// <remarks>Поля патча нормализуются как _validate_task_values L213–231 (клампы/Trim); увеличение PlanJoins
/// сверх текущего значения проходит <see cref="DiscoveryPlanGuard.AssertBudgetAsync"/> с исключением задачи.
/// Пустой/полностью null патч — возврат текущей задачи без записи (python L289–290).</remarks>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="patch">Изменяемые поля (null — не меняется).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Обновлённая задача либо null (404 «Задача не найдена»).</returns>
/// <exception cref="DiscoveryValidationException">Новый план вне границ / бюджет исчерпан.</exception>
public async Task<DiscoveryTaskDto?> PatchAsync(
@@ -148,7 +127,6 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
if (!normalized.HasChanges())
{
// python L289–290: пустой патч — возврат текущей задачи без записи (updated_at не бампается).
return current;
}
@@ -157,10 +135,9 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Удаляет задачу вместе с кандидатами и логом (delete_task L314–318; чёрный список общий).
/// Удаляет задачу вместе с кандидатами и логом.
/// </summary>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>True — задача удалена; false — строки нет (404 у эндпоинта).</returns>
public async Task<bool> DeleteAsync(string taskId, CancellationToken ct)
{
@@ -174,14 +151,11 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Запускает поиск (start_task L321–346): keywords непустые; status=running.
/// Запускает поиск
/// </summary>
/// <remarks>Повторный старт завершённой/упавшей (done/failed) сбрасывает прогресс поиска (свежий проход по
/// ключам, L332–339); продолжение из paused сохраняет search_idx/счётчики (L342–344).</remarks>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Задача в running либо null (404).</returns>
/// <exception cref="DiscoveryValidationException">Ключевых слов нет (текст start_task L330).</exception>
/// <exception cref="DiscoveryValidationException">Ключевых слов нет.</exception>
public async Task<DiscoveryTaskDto?> StartAsync(string taskId, CancellationToken ct)
{
DiscoveryTaskDto? current = await store.GetTaskAsync(taskId, ct).ConfigureAwait(false);
@@ -201,10 +175,9 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Ставит задачу на паузу (pause_task L349–356; прогресс поиска сохраняется).
/// Ставит задачу на паузу.
/// </summary>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>Задача в paused либо null (404).</returns>
public async Task<DiscoveryTaskDto?> PauseAsync(string taskId, CancellationToken ct)
{
@@ -219,12 +192,11 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Увеличивает счётчик прогресса задачи (bump_counter L359–368; n ≤ 0 — no-op, python max(0, n)).
/// Увеличивает счётчик прогресса задачи
/// </summary>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="field">Счётчик (found/evaluated/joined/rejected).</param>
/// <param name="n">Приращение (по умолчанию 1).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>True — счётчик увеличен; false — задачи нет.</returns>
public Task<bool> BumpCounterAsync(
string taskId,
@@ -236,10 +208,9 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
}
/// <summary>
/// Продвигает индекс поиска (advance_search L371–380): search_idx+1; конец ключей → search_done.
/// Продвигает индекс поиска
/// </summary>
/// <param name="taskId">Id задачи (<c>dt_...</c>).</param>
/// <param name="ct">Токен отмены.</param>
/// <returns>True — задача обновлена; false — строки нет.</returns>
public async Task<bool> AdvanceSearchAsync(string taskId, CancellationToken ct)
{
@@ -254,7 +225,6 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
return await store.AdvanceSearchAsync(taskId, nextIndex, searchDone, ct).ConfigureAwait(false);
}
// Нормализует патч задачи (1:1 _validate_task_values L213–231: Trim/клампы/язык/ключи).
// patch: Патч из запроса.
// Возвращает: Патч с нормализованными значениями (null-поля сохраняются как «не менять»).
private static DiscoveryTaskPatch NormalizeTaskPatch(DiscoveryTaskPatch patch)
@@ -279,7 +249,6 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
};
}
// Очищает список ключей: Trim + без пустых (python L226–227).
// keywords: Сырые ключи (null — пусто).
// Возвращает: Список непустых ключей.
private static IReadOnlyList<string> CleanKeywords(IReadOnlyList<string>? keywords)
@@ -289,7 +258,6 @@ public sealed class DiscoveryTasksService(IDiscoveryStore store, DiscoveryPlanGu
.ToList() ?? new List<string>();
}
// Кламп значения в границы [min, max] (эталон clamp python).
private static int Clamp(
int value,
int min,