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

Удалены <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
@@ -16,49 +16,24 @@ using Grpc.Core;
namespace Deal.Api.Telegram;
/// <summary>
/// gRPC-сервер входящего потока telegram-service → ядро (план Task 12, L361377; Ruling 1/7).
///
/// Реализация серверной стороны Deal.Grpc.Telegram.IngressService (telegram.proto, L380395):
/// PushMessage — новое/догоняющее сообщение мониторящегося диалога в очередь пайплайна
/// (<see cref="PipelineIngestService.EnqueueAsync"/>, тот же контракт, что приём сообщений пайплайна) в схеме тенанта
/// + превью (DialogsService.SavePreview: TgMessages + «последнее сообщение» каталога, Ruling 7);
/// SyncDialogs — применение каталога диалогов (DialogsService.SyncFromTelegram) и ответ со списком
/// monitored id (зеркало сервиса); ReportStatus — статус аккаунта в KV (tgStatus/tgAccount) + SSE
/// system_status/тосты на переходах фаз.
/// gRPC-сервер входящего потока telegram-service → ядро.
/// </summary>
/// <remarks>
/// Tenant-id берётся ТОЛЬКО из gRPC-metadata (полю в теле не доверяем — Ruling 1), принадлежность
/// подтверждается реестром тенантов (public.tenants), затем для работы открывается собственный scope
/// с <c>ITenantContext.SetTenant</c> (эталон PipelineWorkerScheduler, L169213): tenant-scoped адаптеры
/// (PipelineStore/SettingsStore) строятся от схемы тенанта. Неизвестный тенант/сбой схемы — RPC не падает:
/// ответ не-принято (accepted=false / ok=false, план Task 12) + лог аудита (Ruling 13); недоступный сервис
/// догоняет упущенное realtime-sweep (контракт README).
/// <para>
/// Полная синхронизация каталога (применение entries к таблице Dialogs, ответ = список monitored id) —
/// модуль Deal.Modules.Telegram (план Task 13): DialogsService.SyncFromTelegram (upsert/удаление, авто-
/// мониторинг новых по autoMonitorNew), превью сообщений — DialogsService.SavePreview (PushMessage).
/// </para>
/// </remarks>
public sealed class TelegramIngressService(
IServiceScopeFactory scopeFactory,
SseBroker broker,
ILogger<TelegramIngressService> logger) : IngressService.IngressServiceBase
{
/// <summary>
/// Ключ gRPC-metadata с id тенанта (единственный источник принадлежности — Ruling 1).
/// Ключ gRPC-metadata с id тенанта.
/// </summary>
public const string TenantIdMetadataKey = "tenant-id";
// Тип SSE-события статуса Telegram (фронт по нему перечитывает GET /api/tg/status, Ruling 7).
private const string SystemStatusEventType = "system_status";
// Тип SSE-события тоста (Ruling 5; api.js L79 слушает 'toast').
private const string ToastEventType = "toast";
// Текст тоста подключения (Ruling 7, 1:1 с прототипом).
private const string ConnectedToastText = "Telegram подключён, сессия сохранена";
// Текст тоста отключения (Ruling 7, 1:1 с прототипом).
private const string DisconnectedToastText = "Telegram отключён";
// Иконка тоста подключения (из набора Icon.vue фронта).
@@ -70,7 +45,6 @@ public sealed class TelegramIngressService(
// Деталь отказа: metadata tenant-id отсутствует (UNAUTHENTICATED, README src/contracts).
private const string MissingTenantIdDetail = "tenant-id отсутствует в metadata";
// Опции JSON KV-статуса: camelCase (1:1 с wire-именами) + терпимость регистра при чтении.
private static readonly JsonSerializerOptions StatusJsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
@@ -78,14 +52,8 @@ public sealed class TelegramIngressService(
};
/// <summary>
/// PushMessage — сообщение диалога в очередь пайплайна тенанта + превью (PushMessageRequest, Ruling 7).
/// PushMessage — сообщение диалога в очередь пайплайна тенанта + превью.
/// </summary>
/// <remarks>Дубль dialog_id+msg_id уже в очереди — duplicate=true, очередь не растёт (гвард
/// PipelineIngestService). Пустой текст/диалог — no-op приёма (accepted=false, контракт proto).
/// После постановки в очередь пишется превью (DialogsService.SavePreview: строка TgMessages
/// «m_&lt;dialog&gt;_&lt;msg&gt;» + «последнее сообщение» каталога — 1:1 _on_message python L270274);
/// сбой превью не влияет на приём (accepted определён очередью, лог дебага).
/// Неизвестный тенант или сбой схемы/БД — не-принято (accepted=false) без исключения RPC.</remarks>
/// <param name="request">Сообщение из потока telegram-service.</param>
/// <param name="context">Контекст вызова (metadata tenant-id + service-token).</param>
/// <returns>accepted — сообщение принято (либо дубль), duplicate — уже было в очереди.</returns>
@@ -140,7 +108,6 @@ public sealed class TelegramIngressService(
catch (Exception exception)
{
// Сбой схемы/БД тенанта (напр. схема ещё не провижинена): RPC не падает — reply not-accepted
// (план Task 12), упущенное сообщение при необходимости догонит realtime-sweep сервиса.
logger.LogWarning(exception, "Аудит: PushMessage {TenantId} → не принято (сбой схемы/БД)", tenant.Id);
return new PushMessageReply();
}
@@ -151,15 +118,8 @@ public sealed class TelegramIngressService(
}
/// <summary>
/// SyncDialogs — синхронизация каталога диалогов аккаунта (Ruling 7, L386390).
/// SyncDialogs — синхронизация каталога диалогов аккаунта.
/// </summary>
/// <remarks>
/// Модуль Deal.Modules.Telegram (план Task 13) применяет entries к таблице Dialogs
/// (DialogsService.SyncFromTelegram: upsert + удаление отсутствующих; авто-мониторинг новых — по
/// настройке autoMonitorNew). Ответ несёт актуальный список monitored id — по нему telegram-service
/// держит своё зеркало мониторинга в памяти (обновляется ответом SyncDialogs и командой SetMonitor,
/// Ruling 7) и фильтрует события realtime.
/// </remarks>
/// <param name="request">Актуальный каталог диалогов (entries).</param>
/// <param name="context">Контекст вызова.</param>
/// <returns>monitored_ids — диалоги с включённым мониторингом после применения каталога.</returns>
@@ -215,14 +175,9 @@ public sealed class TelegramIngressService(
}
/// <summary>
/// ReportStatus — статус аккаунта в KV + SSE system_status/тосты на переходах фаз (Ruling 7).
/// ReportStatus — статус аккаунта в KV + SSE system_status/тосты на переходах фаз.
/// </summary>
/// <remarks>KV tgStatus (снимок без account) и tgAccount (JSON-строка) пишутся в схему тенанта;
/// system_status публикуется на каждый репорт (фронт перечитывает /api/tg/status), тосты — только на
/// переходы connected: false→true «Telegram подключён, сессия сохранена», true→false «Telegram отключён»
/// (сервис шлёт статус по событию и heartbeat'ом — без гарда переходов тосты дублировались бы).
/// Неизвестный тенант/сбой схемы — ok=false без исключения RPC (план Task 12).</remarks>
/// <param name="request">Статус аккаунта из _publish_status прототипа.</param>
/// <param name="request">Статус аккаунта из.</param>
/// <param name="context">Контекст вызова.</param>
/// <returns>ok — статус принят и сохранён.</returns>
public override async Task<ReportStatusReply> ReportStatus(ReportStatusRequest request, ServerCallContext context)
@@ -274,7 +229,6 @@ public sealed class TelegramIngressService(
// Разрешает тенанта запроса: metadata tenant-id → реестр public.tenants.
// Отсутствующий/пустой tenant-id — RPC-отказ UNAUTHENTICATED (README: tenant-id обязателен).
// Id не Guid либо записи нет в реестре — неизвестный тенант: лог аудита и null (RPC отвечает не-принято,
// план Task 12: «для несуществующего тенанта не падает»).
// context: Контекст вызова.
// Возвращает: Запись тенанта реестра либо null (тенант неизвестен).
private async Task<TenantRecordDto?> ResolveTenantAsync(ServerCallContext context)
@@ -312,9 +266,7 @@ public sealed class TelegramIngressService(
}
// Пишет превью принятого сообщения (TgMessages + «последнее сообщение» каталога) без влияния на приём.
// Ruling 7: PushMessage → EnqueueAsync + превью. Сбой превью (нет таблиц/строки каталога и т.п.)
// не роняет RPC и не меняет accepted — очередь уже записана, упущенное догонит realtime-sweep (как
// python: обновление last_text после enqueue в том же обработчике, ошибка не отменяет приём).
// tenantScope: Scope тенанта (TenantDbContext построен на схеме тенанта).
// tenant: Тенант канала (для лога аудита).
// request: Сообщение PushMessage.
@@ -373,7 +325,6 @@ public sealed class TelegramIngressService(
}
}
// Публикует SSE-тост в канал тенанта (без подписчиков — no-op, Ruling 5).
// tenantId: Тенант-получатель.
// text: Текст тоста.
// icon: Иконка тоста (набор Icon.vue фронта).