using Microsoft.AspNetCore.SignalR;
namespace foodmarket.Api.Realtime;
/// Тонкий обёрточный сервис: контроллеры/сервисы вызывают
/// , а доставка идёт через
/// в группу org:{Id}.
///
/// Метод JSON-сериализуется в SignalR'е автоматически (System.Text.Json).
/// Имя метода = ключ для подписки на клиенте.
public interface INotificationsPublisher
{
/// Шлём всем подключённым сессиям одной org-и событие.
Task PublishAsync(Guid orgId, string method, object payload);
}
public sealed class NotificationsPublisher : INotificationsPublisher
{
private readonly IHubContext _hub;
public NotificationsPublisher(IHubContext hub) => _hub = hub;
public Task PublishAsync(Guid orgId, string method, object payload)
=> _hub.Clients.Group(NotificationsHub.GroupName(orgId)).SendAsync(method, payload);
}
/// DTO событий — стабильные имена для контракта с фронтом.
public static class NotificationEvents
{
/// Сразу после успешного post-a розничной продажи.
public const string SalePosted = "SalePosted";
/// Сразу после успешного post-a приёмки.
public const string SupplyPosted = "SupplyPosted";
/// Создано персистентное уведомление (центр уведомлений). Фронт по
/// событию перечитывает счётчик непрочитанных и список. Шлётся фоновым
/// джобом уведомлений об отрицательных остатках.
public const string NotificationCreated = "NotificationCreated";
/// Когда после движения остаток продукта стал ниже MinStock.
/// Один event на каждый продукт-склад, в котором факт пересечения
/// порога. Шлётся только когда было «выше» и стало «ниже» — иначе на
/// каждой продаже сыпались бы тосты.
public const string LowStock = "LowStock";
}
public sealed record SalePostedPayload(
Guid SaleId,
string Number,
decimal Total,
Guid StoreId,
string? CashierName,
Guid? RetailPointId,
DateTime PostedAt);
public sealed record SupplyPostedPayload(
Guid SupplyId,
string Number,
decimal Total,
Guid SupplierId,
string? SupplierName,
DateTime PostedAt);
public sealed record LowStockPayload(
Guid ProductId,
string ProductName,
Guid StoreId,
string? StoreName,
decimal Quantity,
decimal MinStock);
public sealed record NotificationCreatedPayload(
string Type,
string Title,
Guid StoreId,
int Count,
bool Escalation);