/
d.alekseev
/
SmartMenuBot
Обзор
Документация
Войти
/
d.alekseev
/
SmartMenuBot
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ConsoleBot/Infrastructure/Services/NotificationService.cs
102 строки
4 KB
d.alekseev
feat: нотификации и фоновые задачи доставки уведомлений
26 фев 2026, 10:55
26 фев 2026, 10:55
4338916
Код
Авторство
О чём код?
using LinqToDB; using LinqToDB.Async; using Npgsql; using SmartMenuBot.Core.Entities; using SmartMenuBot.Core.Services.Interfaces; using SmartMenuBot.Infrastructure.DataAccess; namespace SmartMenuBot.Infrastructure.Services { public class NotificationService(IDataContextFactory<ToDoDataContext> factory) : INotificationService { public async Task<bool> ScheduleNotification( Guid userId, string type, string text, DateTime scheduledAt, CancellationToken ct) { if (userId == Guid.Empty) throw new ArgumentException("Поле userId не может быть пустым GUID", nameof(userId)); if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Поле type не может быть пустым", nameof(type)); if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Поле text не может быть пустым", nameof(text)); using var dbContext = factory.CreateDataContext(); var exists = await dbContext.Notifications .AnyAsync(n => n.UserId == userId && n.Type == type, ct); if (exists) return false; var model = new Core.DataAccess.Models.NotificationModel { Id = Guid.NewGuid(), UserId = userId, Type = type, Text = text, ScheduledAt = scheduledAt, IsNotified = false, NotifiedAt = null }; try { await dbContext.InsertAsync(model, token: ct); return true; } catch (PostgresException ex) when (ex.SqlState == PostgresErrorCodes.UniqueViolation) { return false; } } public async Task<IReadOnlyList<Notification>> GetScheduledNotification(DateTime scheduledBefore, CancellationToken ct) { using var dbContext = factory.CreateDataContext(); var models = await dbContext.Notifications .LoadWith(n => n.User) .Where(n => !n.IsNotified && n.ScheduledAt <= scheduledBefore) .OrderBy(n => n.ScheduledAt) .ToListAsync(ct); return models .Select(MapFromModel) .ToList() .AsReadOnly(); } public async Task MarkNotified(Guid notificationId, CancellationToken ct) { if (notificationId == Guid.Empty) throw new ArgumentException("Поле notificationId не может быть пустым GUID", nameof(notificationId)); using var dbContext = factory.CreateDataContext(); var notifiedAt = DateTime.UtcNow; await dbContext.Notifications .Where(n => n.Id == notificationId) .Set(n => n.IsNotified, true) .Set(n => n.NotifiedAt, notifiedAt) .UpdateAsync(ct); } private static Notification MapFromModel(Core.DataAccess.Models.NotificationModel model) { ArgumentNullException.ThrowIfNull(model.User); var user = ModelMapper.MapFromModel(model.User); return Notification.Restore( model.Id, user, model.Type, model.Text, model.ScheduledAt, model.IsNotified, model.NotifiedAt); } } }