Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
using Deal.Modules.Kanban.Application.Models;
|
||||
|
||||
namespace Deal.Api.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Публикация SSE-тостов статистики тика правил хранения в канал тенанта (Ruling 8; notify_tick_stats L496–504).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Единый хелпер Api-слоя для POST /api/admin/tick (StorageEndpoints/AdminTickOrchestrator, Task 10) и
|
||||
/// фонового StorageTickScheduler (Task 11): публикует тосты только по ненулевым счётчикам, тексты и иконки
|
||||
/// 1:1 с прототипом; без подписчиков канала публикация — no-op (Ruling 5). Вынесен из StorageEndpoints,
|
||||
/// чтобы ручной и фоновый тики не дублировали логику. Модуль Kanban тосты не публикует (Ruling 5:
|
||||
/// публикации SSE — обязанность Api-слоя).
|
||||
/// </remarks>
|
||||
public sealed class StorageToastPublisher
|
||||
{
|
||||
// Тип SSE-события тоста (Ruling 5; api.js L79 слушает 'toast').
|
||||
private const string ToastEventType = "toast";
|
||||
|
||||
// Текст тоста автоархива: N карточек ушло в архив (notify_tick_stats L498).
|
||||
private const string AutoArchiveToastText = "Автоархив: {0} карточек";
|
||||
|
||||
// Текст тоста очистки архива: N карточек удалено из архива (notify_tick_stats L500).
|
||||
private const string ArchiveClearedToastText = "Архив очищен: {0} (90 дн.)";
|
||||
|
||||
// Текст тоста очистки корзины: N карточек удалено из корзины (notify_tick_stats L502).
|
||||
private const string TrashClearedToastText = "Корзина очищена: {0} (7 дн.)";
|
||||
|
||||
// Текст тоста автоочистки отсева пайплайна: N записей старше 3 суток (notify_tick_stats L503–504).
|
||||
private const string RejectedPurgedToastText = "Отсев очищен: {0} записей (3 дн.)";
|
||||
|
||||
// Иконка тоста автоархива (Ruling 8, 1:1 с прототипом).
|
||||
private const string ClockIcon = "clock";
|
||||
|
||||
// Иконка тостов очисток архива/корзины (Ruling 8, 1:1 с прототипом).
|
||||
private const string TrashIcon = "trash";
|
||||
|
||||
private readonly SseBroker _broker;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт публикатора тостов тика.
|
||||
/// </summary>
|
||||
/// <param name="broker">SSE-брокер каналов тенантов (singleton).</param>
|
||||
public StorageToastPublisher(SseBroker broker)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(broker);
|
||||
_broker = broker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Публикует тосты статистики тика по ненулевым счётчикам (notify_tick_stats L496–504).
|
||||
/// </summary>
|
||||
/// <remarks>Тексты и иконки 1:1 с прототипом; дни в скобках («90 дн.»/«7 дн.»/«3 дн.») — фиксированные
|
||||
/// строки прототипа (не пересчитываются от настроек). Ветка purgedRejected («Отсев очищен: N записей
|
||||
/// (3 дн.)») — план Task 10: счётчик наполняет оркестратор тика (AdminTickOrchestrator) очисткой отсева
|
||||
/// PipelineProcessingService.PurgeExpiredAsync; фоновый цикл Task 11 публикует ту же ветку по своему тику.
|
||||
/// Публикация в канал тенанта; без подписчиков — no-op (Ruling 5).</remarks>
|
||||
/// <param name="tenantId">Тенант-получатель тостов (сессия запроса / канал тенанта цикла).</param>
|
||||
/// <param name="stats">Статистика только что выполненного тика.</param>
|
||||
public void PublishTickToasts(Guid tenantId, StorageTickStatsDto stats)
|
||||
{
|
||||
if (stats.Archived > 0)
|
||||
{
|
||||
_broker.Publish(tenantId, ToastEventType, new { text = string.Format(AutoArchiveToastText, stats.Archived), icon = ClockIcon });
|
||||
}
|
||||
|
||||
if (stats.PurgedArchive > 0)
|
||||
{
|
||||
_broker.Publish(tenantId, ToastEventType, new { text = string.Format(ArchiveClearedToastText, stats.PurgedArchive), icon = TrashIcon });
|
||||
}
|
||||
|
||||
if (stats.PurgedTrash > 0)
|
||||
{
|
||||
_broker.Publish(tenantId, ToastEventType, new { text = string.Format(TrashClearedToastText, stats.PurgedTrash), icon = TrashIcon });
|
||||
}
|
||||
|
||||
if (stats.PurgedRejected > 0)
|
||||
{
|
||||
_broker.Publish(tenantId, ToastEventType, new { text = string.Format(RejectedPurgedToastText, stats.PurgedRejected), icon = TrashIcon });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user