diff --git a/AsbCloudApp/Data/NotificationDto.cs b/AsbCloudApp/Data/NotificationDto.cs index 2b47ccc0..2f4b09dd 100644 --- a/AsbCloudApp/Data/NotificationDto.cs +++ b/AsbCloudApp/Data/NotificationDto.cs @@ -1,5 +1,4 @@ using System; -using AsbCloudApp.Data.User; namespace AsbCloudApp.Data; @@ -79,9 +78,4 @@ public class NotificationDto : IId /// DTO категории уведомления /// public NotificationCategoryDto NotificationCategory { get; set; } = null!; - - /// - /// DTO получателя уведомления - /// - public UserDto User { get; set; } = null!; } \ No newline at end of file diff --git a/AsbCloudApp/Services/Notifications/NotificationService.cs b/AsbCloudApp/Services/Notifications/NotificationService.cs index 5be9ab33..6d6fc6d7 100644 --- a/AsbCloudApp/Services/Notifications/NotificationService.cs +++ b/AsbCloudApp/Services/Notifications/NotificationService.cs @@ -16,7 +16,6 @@ namespace AsbCloudApp.Services.Notifications; public class NotificationService { private readonly ICrudRepository notificationCategoryRepository; - private readonly IUserRepository userRepository; private readonly INotificationRepository notificationRepository; private readonly IEnumerable notificationTransportServices; @@ -28,11 +27,9 @@ public class NotificationService /// /// public NotificationService(ICrudRepository notificationCategoryRepository, - IUserRepository userRepository, INotificationRepository notificationRepository, IEnumerable notificationTransportServices) { - this.userRepository = userRepository; this.notificationCategoryRepository = notificationCategoryRepository; this.notificationRepository = notificationRepository; this.notificationTransportServices = notificationTransportServices; @@ -50,14 +47,11 @@ public class NotificationService .GetOrDefaultAsync(request.IdNotificationCategory, cancellationToken) ?? throw new ArgumentInvalidException("Категория уведомления не найдена", nameof(request.IdNotificationCategory)); - var user = await userRepository.GetOrDefaultAsync(request.IdUser, cancellationToken) - ?? throw new ArgumentInvalidException("Пользователь не найден" , nameof(request.IdUser)); - var notification = new NotificationDto { IdUser = request.IdUser, RegistrationDate = DateTime.UtcNow, - IdNotificationCategory = request.IdNotificationCategory, + IdNotificationCategory = notificationCategory.Id, Title = request.Title, Message = request.Message, IdTransportType = request.IdTransportType, @@ -65,8 +59,7 @@ public class NotificationService notification.Id = await notificationRepository.InsertAsync(notification, cancellationToken); notification.NotificationCategory = notificationCategory; - notification.User = user; - + var notificationTransportService = GetNotificationTransportService(request.IdTransportType); await notificationTransportService.SendAsync(notification, cancellationToken); diff --git a/AsbCloudInfrastructure/Background/BackgroundWorker.cs b/AsbCloudInfrastructure/Background/BackgroundWorker.cs index e50b21b8..7c88fade 100644 --- a/AsbCloudInfrastructure/Background/BackgroundWorker.cs +++ b/AsbCloudInfrastructure/Background/BackgroundWorker.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Background { -# nullable enable /// /// Сервис для фонового выполнения работы /// @@ -94,5 +93,4 @@ namespace AsbCloudInfrastructure.Background } } } - } diff --git a/AsbCloudInfrastructure/Background/NotificationBackgroundWorker.cs b/AsbCloudInfrastructure/Background/NotificationBackgroundWorker.cs new file mode 100644 index 00000000..4b68df31 --- /dev/null +++ b/AsbCloudInfrastructure/Background/NotificationBackgroundWorker.cs @@ -0,0 +1,10 @@ +using System; + +namespace AsbCloudInfrastructure.Background; + +public class NotificationBackgroundWorker : BackgroundWorker +{ + public NotificationBackgroundWorker(IServiceProvider serviceProvider) : base(serviceProvider) + { + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 64a2fa49..c40afca9 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -111,6 +111,7 @@ namespace AsbCloudInfrastructure services.AddSingleton(provider => TelemetryDataCache.GetInstance(provider)); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(provider => ReduceSamplingService.GetInstance(configuration)); services.AddTransient(); diff --git a/AsbCloudInfrastructure/Repository/NotificationRepository.cs b/AsbCloudInfrastructure/Repository/NotificationRepository.cs index 37e3e4bd..c3302c95 100644 --- a/AsbCloudInfrastructure/Repository/NotificationRepository.cs +++ b/AsbCloudInfrastructure/Repository/NotificationRepository.cs @@ -8,21 +8,21 @@ using AsbCloudDb; using AsbCloudDb.Model; using Mapster; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Caching.Memory; namespace AsbCloudInfrastructure.Repository; -public class NotificationRepository : CrudCacheRepositoryBase, INotificationRepository +public class NotificationRepository : CrudRepositoryBase, INotificationRepository { private static IQueryable MakeQueryNotification(DbSet dbSet) => dbSet.Include(n => n.NotificationCategory) .Include(n => n.User) .AsNoTracking(); - public NotificationRepository(IAsbCloudDbContext dbContext, IMemoryCache memoryCache) - : base(dbContext, memoryCache, MakeQueryNotification) + public NotificationRepository(IAsbCloudDbContext context) + : base(context, MakeQueryNotification) { } + public async Task> GetNotificationsAsync(int idUser, NotificationRequest request, @@ -33,7 +33,7 @@ public class NotificationRepository : CrudCacheRepositoryBase() + var result = new PaginationContainer { Skip = skip, Take = take, @@ -61,13 +61,12 @@ public class NotificationRepository : CrudCacheRepositoryBase x.NotificationCategory) .Where(n => n.IdUser == idUser); - if (request.IsSent.HasValue) + if (request.IsSent.HasValue) { - if(request.IsSent.Value) - query = query.Where(n => n.SentDate != null); - else - query = query.Where(n => n.SentDate == null); - } + query = request.IsSent.Value ? + query.Where(n => n.SentDate != null) + : query.Where(n => n.SentDate == null); + } if (request.IdTransportType.HasValue) query = query.Where(n => n.IdTransportType == request.IdTransportType); diff --git a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs index 2dd225f6..e0f4ba0e 100644 --- a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs +++ b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs @@ -50,7 +50,7 @@ namespace AsbCloudInfrastructure.Services.Email return Task.CompletedTask; } - var workId = MakeWorkId(notification.User.Email, notification.Title, notification.Message); + var workId = MakeWorkId(notification.IdUser, notification.Title, notification.Message); if (!backgroundWorker.Contains(workId)) { var workAction = MakeEmailSendWorkAction(notification); @@ -73,17 +73,23 @@ namespace AsbCloudInfrastructure.Services.Email private Func MakeEmailSendWorkAction(NotificationDto notification) { - if(!MailAddress.TryCreate(notification.User.Email, out var mailAddress)) - Trace.TraceWarning($"Mail {notification.User.Email} is not correct."); - - if (mailAddress is null) - throw new ArgumentInvalidException($"Mail {notification.User.Email} is not null.", nameof(notification.User.Email)); - if (string.IsNullOrWhiteSpace(notification.Title)) throw new ArgumentInvalidException($"{nameof(notification.Title)} should be set", nameof(notification.Title)); return async (_, serviceProvider, token) => { + var notificationRepository = serviceProvider.GetRequiredService(); + var userRepository = serviceProvider.GetRequiredService(); + + var user = await userRepository.GetOrDefaultAsync(notification.IdUser, token) + ?? throw new ArgumentInvalidException("Пользователь не найден" , nameof(notification.IdUser)); + + if(!MailAddress.TryCreate(user.Email, out var mailAddress)) + Trace.TraceWarning($"Mail {user.Email} is not correct."); + + if (mailAddress is null) + throw new ArgumentInvalidException($"Mail {user.Email} is not null.", nameof(user.Email)); + var from = new MailAddress(sender); var message = new MailMessage { @@ -105,17 +111,15 @@ namespace AsbCloudInfrastructure.Services.Email notification.SentDate = DateTime.UtcNow; - var notificationRepository = serviceProvider.GetRequiredService(); - await notificationRepository.UpdateAsync(notification, token); - Trace.TraceInformation($"Send email to {notification.User.Email} subj:{notification.Title} html body count {notification.Message.Length}"); + Trace.TraceInformation($"Send email to {user.Email} subj:{notification.Title} html body count {notification.Message.Length}"); }; } - private static string MakeWorkId(string address, string subject, string content) + private static string MakeWorkId(int idUser, string subject, string content) { - var hash = address.GetHashCode(); + var hash = idUser.GetHashCode(); hash ^= subject.GetHashCode(); hash ^= content.GetHashCode(); return hash.ToString("x"); diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index 207270c2..55cd361c 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -40,8 +40,14 @@ namespace AsbCloudInfrastructure backgroundWorker.Push(LimitingParameterCalcWorkFactory.MakeWork()); backgroundWorker.Push(MakeMemoryMonitoringWork()); + var notificationBackgroundWorker = provider.GetRequiredService(); + Task.Delay(1_000) - .ContinueWith(async (_) => await backgroundWorker.StartAsync(CancellationToken.None)); + .ContinueWith(async (_) => + { + await backgroundWorker.StartAsync(CancellationToken.None); + await notificationBackgroundWorker.StartAsync(CancellationToken.None); + }); } static WorkPeriodic MakeMemoryMonitoringWork() diff --git a/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs index e84ad6ae..fda6e0f9 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs @@ -145,7 +145,6 @@ namespace AsbCloudWebApi.Tests.ServicesTests .Returns(1); notificationService = new NotificationService(notificationCategoryRepositoryMock.Object, - userRepositoryMock.Object, new Mock().Object, new [] { notificationTransportServiceMock.Object }); diff --git a/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs b/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs index 63fd8f8e..c29ea241 100644 --- a/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs +++ b/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs @@ -6,50 +6,47 @@ using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Repositories; using AsbCloudApp.Services.Notifications; +using AsbCloudInfrastructure.Background; using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; namespace AsbCloudWebApi.SignalR.Services; public class SignalRNotificationTransportService : INotificationTransportService { + private readonly NotificationBackgroundWorker backgroundWorker; private readonly ConnectionManagerService connectionManagerService; private readonly IHubContext notificationHubContext; - private readonly INotificationRepository notificationRepository; - private readonly SemaphoreSlim semaphoreSlim = new (1, 1); - public SignalRNotificationTransportService(ConnectionManagerService connectionManagerService, - IHubContext notificationHubContext, - INotificationRepository notificationRepository) + public SignalRNotificationTransportService(NotificationBackgroundWorker backgroundWorker, + ConnectionManagerService connectionManagerService, + IHubContext notificationHubContext) { + this.backgroundWorker = backgroundWorker; this.connectionManagerService = connectionManagerService; this.notificationHubContext = notificationHubContext; - this.notificationRepository = notificationRepository; } public int IdTransportType => 0; - public async Task SendAsync(NotificationDto notification, + public Task SendAsync(NotificationDto notification, CancellationToken cancellationToken) { - const string method = "receiveNotifications"; + var workId = notification.Id.ToString(); - var connectionId = connectionManagerService.GetConnectionIdByUserId(notification.IdUser); - - if (!string.IsNullOrWhiteSpace(connectionId)) + if (!backgroundWorker.Contains(workId)) { - notification.SentDate = DateTime.UtcNow; - - await notificationHubContext.Clients.Client(connectionId) - .SendAsync(method, - notification, - cancellationToken); + var connectionId = connectionManagerService.GetConnectionIdByUserId(notification.IdUser); - await semaphoreSlim.WaitAsync(cancellationToken); - - await notificationRepository.UpdateAsync(notification, cancellationToken); - - semaphoreSlim.Release(); + if (!string.IsNullOrWhiteSpace(connectionId)) + { + var workAction = MakeSignalRSendWorkAction(notification, connectionId); + var work = new WorkBase(workId, workAction); + backgroundWorker.Push(work); + } } + + return Task.CompletedTask; } public Task SendRangeAsync(IEnumerable notifications, @@ -60,4 +57,24 @@ public class SignalRNotificationTransportService : INotificationTransportService return Task.WhenAll(tasks); } + + private Func MakeSignalRSendWorkAction(NotificationDto notification, + string connectionId) + { + const string method = "receiveNotifications"; + + return async (_, serviceProvider, cancellationToken) => + { + notification.SentDate = DateTime.UtcNow; + + await notificationHubContext.Clients.Client(connectionId) + .SendAsync(method, + notification, + cancellationToken); + + var notificationRepository = serviceProvider.GetRequiredService(); + + await notificationRepository.UpdateAsync(notification, cancellationToken); + }; + } } \ No newline at end of file