forked from ddrilling/AsbCloudServer
Рефакторинг транспорта уведомлений
1. Создал отдельный бекграунд сервис для уведомлений. 2. Сделал отправку уведомлений с помощью SignalR с использованием бекграунд сервиса. 3. Убрал из NotificationDto свойство User. Данное свойство избыточно в данном Dto. 4. В транспорте отправки уведомлений по e-mail добавил получение пользователя. 5. Поправил NotificationRepository, избавился от использования кэша.
This commit is contained in:
parent
4226d6366c
commit
ff65869341
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using AsbCloudApp.Data.User;
|
||||
|
||||
namespace AsbCloudApp.Data;
|
||||
|
||||
@ -79,9 +78,4 @@ public class NotificationDto : IId
|
||||
/// DTO категории уведомления
|
||||
/// </summary>
|
||||
public NotificationCategoryDto NotificationCategory { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// DTO получателя уведомления
|
||||
/// </summary>
|
||||
public UserDto User { get; set; } = null!;
|
||||
}
|
@ -16,7 +16,6 @@ namespace AsbCloudApp.Services.Notifications;
|
||||
public class NotificationService
|
||||
{
|
||||
private readonly ICrudRepository<NotificationCategoryDto> notificationCategoryRepository;
|
||||
private readonly IUserRepository userRepository;
|
||||
private readonly INotificationRepository notificationRepository;
|
||||
private readonly IEnumerable<INotificationTransportService> notificationTransportServices;
|
||||
|
||||
@ -28,11 +27,9 @@ public class NotificationService
|
||||
/// <param name="notificationRepository"></param>
|
||||
/// <param name="notificationTransportServices"></param>
|
||||
public NotificationService(ICrudRepository<NotificationCategoryDto> notificationCategoryRepository,
|
||||
IUserRepository userRepository,
|
||||
INotificationRepository notificationRepository,
|
||||
IEnumerable<INotificationTransportService> 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);
|
||||
|
@ -7,7 +7,6 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Background
|
||||
{
|
||||
# nullable enable
|
||||
/// <summary>
|
||||
/// Сервис для фонового выполнения работы
|
||||
/// </summary>
|
||||
@ -94,5 +93,4 @@ namespace AsbCloudInfrastructure.Background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace AsbCloudInfrastructure.Background;
|
||||
|
||||
public class NotificationBackgroundWorker : BackgroundWorker
|
||||
{
|
||||
public NotificationBackgroundWorker(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||
{
|
||||
}
|
||||
}
|
@ -111,6 +111,7 @@ namespace AsbCloudInfrastructure
|
||||
services.AddSingleton(provider => TelemetryDataCache<TelemetryDataSpinDto>.GetInstance<TelemetryDataSpin>(provider));
|
||||
services.AddSingleton<IRequerstTrackerService, RequestTrackerService>();
|
||||
services.AddSingleton<BackgroundWorker>();
|
||||
services.AddSingleton<NotificationBackgroundWorker>();
|
||||
services.AddSingleton<IReduceSamplingService>(provider => ReduceSamplingService.GetInstance(configuration));
|
||||
|
||||
services.AddTransient<IAuthService, AuthService>();
|
||||
|
@ -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<NotificationDto, Notification>, INotificationRepository
|
||||
public class NotificationRepository : CrudRepositoryBase<NotificationDto, Notification>, INotificationRepository
|
||||
{
|
||||
private static IQueryable<Notification> MakeQueryNotification(DbSet<Notification> 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<PaginationContainer<NotificationDto>> GetNotificationsAsync(int idUser,
|
||||
NotificationRequest request,
|
||||
@ -33,7 +33,7 @@ public class NotificationRepository : CrudCacheRepositoryBase<NotificationDto, N
|
||||
|
||||
var query = BuildQuery(idUser, request);
|
||||
|
||||
var result = new PaginationContainer<NotificationDto>()
|
||||
var result = new PaginationContainer<NotificationDto>
|
||||
{
|
||||
Skip = skip,
|
||||
Take = take,
|
||||
@ -61,13 +61,12 @@ public class NotificationRepository : CrudCacheRepositoryBase<NotificationDto, N
|
||||
.Include(x => 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);
|
||||
|
@ -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<string, IServiceProvider, CancellationToken, Task> 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<INotificationRepository>();
|
||||
var userRepository = serviceProvider.GetRequiredService<IUserRepository>();
|
||||
|
||||
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<INotificationRepository>();
|
||||
|
||||
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");
|
||||
|
@ -40,8 +40,14 @@ namespace AsbCloudInfrastructure
|
||||
backgroundWorker.Push(LimitingParameterCalcWorkFactory.MakeWork());
|
||||
backgroundWorker.Push(MakeMemoryMonitoringWork());
|
||||
|
||||
var notificationBackgroundWorker = provider.GetRequiredService<NotificationBackgroundWorker>();
|
||||
|
||||
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()
|
||||
|
@ -145,7 +145,6 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
||||
.Returns(1);
|
||||
|
||||
notificationService = new NotificationService(notificationCategoryRepositoryMock.Object,
|
||||
userRepositoryMock.Object,
|
||||
new Mock<INotificationRepository>().Object,
|
||||
new [] { notificationTransportServiceMock.Object });
|
||||
|
||||
|
@ -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<NotificationHub> notificationHubContext;
|
||||
private readonly INotificationRepository notificationRepository;
|
||||
private readonly SemaphoreSlim semaphoreSlim = new (1, 1);
|
||||
|
||||
public SignalRNotificationTransportService(ConnectionManagerService connectionManagerService,
|
||||
IHubContext<NotificationHub> notificationHubContext,
|
||||
INotificationRepository notificationRepository)
|
||||
public SignalRNotificationTransportService(NotificationBackgroundWorker backgroundWorker,
|
||||
ConnectionManagerService connectionManagerService,
|
||||
IHubContext<NotificationHub> 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<NotificationDto> notifications,
|
||||
@ -60,4 +57,24 @@ public class SignalRNotificationTransportService : INotificationTransportService
|
||||
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private Func<string, IServiceProvider, CancellationToken, Task> 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<INotificationRepository>();
|
||||
|
||||
await notificationRepository.UpdateAsync(notification, cancellationToken);
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user