diff --git a/AsbCloudApp/AsbCloudApp.csproj b/AsbCloudApp/AsbCloudApp.csproj
index 79ec9867..512a0c0a 100644
--- a/AsbCloudApp/AsbCloudApp.csproj
+++ b/AsbCloudApp/AsbCloudApp.csproj
@@ -18,4 +18,8 @@
+
+
+
+
diff --git a/AsbCloudApp/Repositories/INotificationRepository.cs b/AsbCloudApp/Repositories/INotificationRepository.cs
index 74f0b81c..74ab9dd9 100644
--- a/AsbCloudApp/Repositories/INotificationRepository.cs
+++ b/AsbCloudApp/Repositories/INotificationRepository.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using AsbCloudApp.Data;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
@@ -22,6 +23,25 @@ public interface INotificationRepository : ICrudRepository
NotificationRequest request,
CancellationToken cancellationToken);
+ ///
+ /// Получение всех уведомлений
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetAllAsync(int? idUser, bool? isSent, int? idTransportType,
+ CancellationToken cancellationToken);
+
+ ///
+ /// Обновление уведомлений
+ ///
+ ///
+ ///
+ ///
+ Task UpdateRangeAsync(IEnumerable notifications, CancellationToken cancellationToken);
+
///
/// Удаление уведомлений по параметрам
///
diff --git a/AsbCloudApp/Services/Notifications/INotificationTransportService.cs b/AsbCloudApp/Services/Notifications/INotificationTransportService.cs
index fba70d4b..a18f4246 100644
--- a/AsbCloudApp/Services/Notifications/INotificationTransportService.cs
+++ b/AsbCloudApp/Services/Notifications/INotificationTransportService.cs
@@ -16,7 +16,7 @@ public interface INotificationTransportService
int IdTransportType { get; }
///
- /// Отправка одного уведомлений
+ /// Отправка одного уведомления
///
///
///
diff --git a/AsbCloudApp/Services/Notifications/NotificationService.cs b/AsbCloudApp/Services/Notifications/NotificationService.cs
index 33ff10db..b8916b15 100644
--- a/AsbCloudApp/Services/Notifications/NotificationService.cs
+++ b/AsbCloudApp/Services/Notifications/NotificationService.cs
@@ -7,6 +7,7 @@ using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
+using AsbCloudDb.Model;
namespace AsbCloudApp.Services.Notifications;
@@ -42,8 +43,7 @@ public class NotificationService
public async Task NotifyAsync(NotifyRequest request,
CancellationToken cancellationToken)
{
- var notificationCategory = await notificationCategoryRepository
- .GetOrDefaultAsync(request.IdNotificationCategory, cancellationToken)
+ var notificationCategory = await notificationCategoryRepository.GetOrDefaultAsync(request.IdNotificationCategory, cancellationToken)
?? throw new ArgumentInvalidException("Категория уведомления не найдена", nameof(request.IdNotificationCategory));
var notification = new NotificationDto
@@ -59,7 +59,7 @@ public class NotificationService
notification.Id = await notificationRepository.InsertAsync(notification, cancellationToken);
notification.NotificationCategory = notificationCategory;
- var notificationTransportService = GetNotificationTransportService(request.IdTransportType);
+ var notificationTransportService = GetTransportService(request.IdTransportType);
await notificationTransportService.SendAsync(notification, cancellationToken);
}
@@ -71,12 +71,11 @@ public class NotificationService
///
///
///
- public async Task UpdateNotificationAsync(int idNotification,
+ public async Task UpdateAsync(int idNotification,
bool isRead,
CancellationToken cancellationToken)
{
- var notification = await notificationRepository.GetOrDefaultAsync(idNotification,
- cancellationToken)
+ var notification = await notificationRepository.GetOrDefaultAsync(idNotification, cancellationToken)
?? throw new ArgumentInvalidException("Уведомление не найдено", nameof(idNotification));
if(isRead && !notification.SentDate.HasValue)
@@ -95,25 +94,19 @@ public class NotificationService
///
///
///
- public async Task ResendNotificationAsync(int idUser,
- NotificationRequest request,
- CancellationToken cancellationToken)
+ public async Task RenotifyAsync(int idUser, CancellationToken cancellationToken)
{
- if (!request.IdTransportType.HasValue)
- throw new ArgumentInvalidException("Id типа доставки уведомления должен иметь значение",
- nameof(request.IdTransportType));
-
- var result = await notificationRepository.GetNotificationsAsync(idUser,
- request,
+ var notifications = await notificationRepository.GetAllAsync(idUser, false,
+ Notification.IdTransportTypeSignalR,
cancellationToken);
- var notificationTransportService = GetNotificationTransportService(request.IdTransportType.Value);
+ var notificationTransportService = GetTransportService(Notification.IdTransportTypeSignalR);
- await notificationTransportService.SendRangeAsync(result.Items,
+ await notificationTransportService.SendRangeAsync(notifications,
cancellationToken);
}
- private INotificationTransportService GetNotificationTransportService(int idTransportType)
+ private INotificationTransportService GetTransportService(int idTransportType)
{
var notificationTransportService = notificationTransportServices
.FirstOrDefault(s => s.IdTransportType == idTransportType)
diff --git a/AsbCloudDb/Model/Notification.cs b/AsbCloudDb/Model/Notification.cs
index 09f718b1..e363d3d2 100644
--- a/AsbCloudDb/Model/Notification.cs
+++ b/AsbCloudDb/Model/Notification.cs
@@ -8,6 +8,9 @@ namespace AsbCloudDb.Model;
[Table("t_notification"), Comment("Уведомления")]
public class Notification : IId
{
+ public const int IdTransportTypeSignalR = 0;
+ public const int IdTransportTypeTypeEmail = 1;
+
[Key]
[Column("id")]
public int Id { get; set; }
diff --git a/AsbCloudInfrastructure/Repository/NotificationRepository.cs b/AsbCloudInfrastructure/Repository/NotificationRepository.cs
index ea639c33..6f1e4a98 100644
--- a/AsbCloudInfrastructure/Repository/NotificationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/NotificationRepository.cs
@@ -1,9 +1,12 @@
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
+using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
@@ -54,10 +57,55 @@ public class NotificationRepository : CrudRepositoryBase DeleteAsync(NotificationDeleteRequest request, CancellationToken cancellationToken)
+ public async Task> GetAllAsync(int? idUser, bool? isSent, int? idTransportType,
+ CancellationToken cancellationToken)
{
var query = dbContext.Notifications.AsQueryable();
+ if (idUser.HasValue)
+ query = query.Where(n => n.IdUser == idUser);
+
+ if(isSent.HasValue)
+ query = isSent.Value ?
+ query.Where(n => n.SentDate != null)
+ : query.Where(n => n.SentDate == null);
+
+ if (idTransportType.HasValue)
+ query = query.Where(n => n.IdTransportType == idTransportType);
+
+ return await query.AsNoTracking().Select(n => n.Adapt())
+ .ToArrayAsync(cancellationToken);
+ }
+
+ public async Task UpdateRangeAsync(IEnumerable notifications, CancellationToken cancellationToken)
+ {
+ if (!notifications.Any())
+ return 0;
+
+ var ids = notifications.Select(d => d.Id);
+
+ var existingEntities = await dbSet
+ .Where(d => ids.Contains(d.Id))
+ .AsNoTracking()
+ .Select(d => d.Id)
+ .ToArrayAsync(cancellationToken);
+
+ if (ids.Count() > existingEntities.Length)
+ return ICrudRepository.ErrorIdNotFound;
+
+ var entities = notifications.Select(Convert);
+
+ dbContext.Notifications.UpdateRange(entities);
+
+ return await dbContext.SaveChangesAsync(cancellationToken);
+ }
+
+ public Task DeleteAsync(NotificationDeleteRequest request, CancellationToken cancellationToken)
+ {
+ var query = dbContext.Notifications
+ .Include(n => n.NotificationCategory)
+ .AsQueryable();
+
if (request.IdCategory.HasValue)
query = query.Where(n => n.IdNotificationCategory == request.IdCategory.Value);
@@ -72,16 +120,15 @@ public class NotificationRepository : CrudRepositoryBase GetUnreadCountAsync(int idUser, int idTransportType, CancellationToken cancellationToken)
+ public Task GetUnreadCountAsync(int idUser, int idTransportType, CancellationToken cancellationToken)
{
- var count = await dbContext.Notifications
- .Where(n => n.ReadDate == null)
- .Where(n => n.IdUser == idUser)
- .Where(n => n.IdTransportType == idTransportType)
- .CountAsync(cancellationToken);
-
- return count;
- }
+ return dbContext.Notifications
+ .Where(n => !n.ReadDate.HasValue &&
+ n.SentDate.HasValue &&
+ n.IdUser == idUser &&
+ n.IdTransportType == idTransportType)
+ .CountAsync(cancellationToken);
+ }
private IQueryable BuildQuery(int idUser,
NotificationRequest request)
diff --git a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs
index e0f4ba0e..4211c73e 100644
--- a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs
+++ b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs
@@ -9,6 +9,7 @@ using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services.Notifications;
+using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -40,7 +41,7 @@ namespace AsbCloudInfrastructure.Services.Email
this.backgroundWorker = backgroundWorker;
}
- public int IdTransportType => 1;
+ public int IdTransportType => Notification.IdTransportTypeTypeEmail;
public Task SendAsync(NotificationDto notification, CancellationToken cancellationToken)
{
diff --git a/AsbCloudWebApi/Controllers/NotificationController.cs b/AsbCloudWebApi/Controllers/NotificationController.cs
index 31203292..9e3b7b1f 100644
--- a/AsbCloudWebApi/Controllers/NotificationController.cs
+++ b/AsbCloudWebApi/Controllers/NotificationController.cs
@@ -6,6 +6,7 @@ using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services.Notifications;
+using AsbCloudWebApi.SignalR.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -21,12 +22,15 @@ public class NotificationController : ControllerBase
{
private readonly NotificationService notificationService;
private readonly INotificationRepository notificationRepository;
+ private readonly NotificationPublisher notificationPublisher;
public NotificationController(NotificationService notificationService,
- INotificationRepository notificationRepository)
+ INotificationRepository notificationRepository,
+ NotificationPublisher notificationPublisher)
{
this.notificationService = notificationService;
this.notificationRepository = notificationRepository;
+ this.notificationPublisher = notificationPublisher;
}
///
@@ -55,10 +59,17 @@ public class NotificationController : ControllerBase
[Required] bool isRead,
CancellationToken cancellationToken)
{
- await notificationService.UpdateNotificationAsync(idNotification,
+ var idUser = User.GetUserId();
+
+ if (!idUser.HasValue)
+ return Forbid();
+
+ await notificationService.UpdateAsync(idNotification,
isRead,
cancellationToken);
+ await notificationPublisher.PublishCountUnreadAsync(idUser.Value, cancellationToken);
+
return Ok();
}
@@ -125,28 +136,7 @@ public class NotificationController : ControllerBase
return Ok();
}
- ///
- /// Получение количества непрочитанных уведомлений
- ///
- ///
- ///
- ///
- [HttpGet]
- [Route("unreadNotificationCount")]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public async Task GetUnreadCountAsync([FromQuery] int idTransportType, CancellationToken cancellationToken)
- {
- int? idUser = User.GetUserId();
-
- if (!idUser.HasValue)
- return Forbid();
-
- var result = await notificationRepository.GetUnreadCountAsync(idUser.Value, idTransportType, cancellationToken);
-
- return Ok(result);
- }
-
- ///
+ ///
/// Удаление уведомлений
///
/// Параметры запроса
diff --git a/AsbCloudWebApi/DependencyInjection.cs b/AsbCloudWebApi/DependencyInjection.cs
index f64f5b2c..4117e5ef 100644
--- a/AsbCloudWebApi/DependencyInjection.cs
+++ b/AsbCloudWebApi/DependencyInjection.cs
@@ -141,6 +141,8 @@ namespace AsbCloudWebApi
{
services.AddTransient();
services.AddTransient();
+
+ services.AddTransient();
}
}
}
diff --git a/AsbCloudWebApi/SignalR/Messages/NotificationMessage.cs b/AsbCloudWebApi/SignalR/Messages/NotificationMessage.cs
new file mode 100644
index 00000000..66efaee0
--- /dev/null
+++ b/AsbCloudWebApi/SignalR/Messages/NotificationMessage.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using AsbCloudApp.Data;
+
+namespace AsbCloudWebApi.SignalR.Messages;
+
+public class NotificationMessage
+{
+ public IEnumerable? Notifications { get; set; }
+
+ public int CountUnread { get; set; }
+}
\ No newline at end of file
diff --git a/AsbCloudWebApi/SignalR/NotificationHub.cs b/AsbCloudWebApi/SignalR/NotificationHub.cs
index 5422e30e..b85ce74b 100644
--- a/AsbCloudWebApi/SignalR/NotificationHub.cs
+++ b/AsbCloudWebApi/SignalR/NotificationHub.cs
@@ -1,7 +1,6 @@
using System;
using System.Threading;
using System.Threading.Tasks;
-using AsbCloudApp.Requests;
using AsbCloudApp.Services.Notifications;
using AsbCloudWebApi.SignalR.Services;
using Microsoft.AspNetCore.Authorization;
@@ -14,12 +13,15 @@ public class NotificationHub : BaseHub
{
private readonly ConnectionManagerService connectionManagerService;
private readonly NotificationService notificationService;
+ private readonly NotificationPublisher notificationPublisher;
public NotificationHub(ConnectionManagerService connectionManagerService,
- NotificationService notificationService)
+ NotificationService notificationService,
+ NotificationPublisher notificationPublisher)
{
this.connectionManagerService = connectionManagerService;
this.notificationService = notificationService;
+ this.notificationPublisher = notificationPublisher;
}
public override async Task OnConnectedAsync()
@@ -35,9 +37,10 @@ public class NotificationHub : BaseHub
await base.OnConnectedAsync();
- await notificationService.ResendNotificationAsync(idUser.Value,
- new NotificationRequest { IsSent = false, IdTransportType = 0},
- CancellationToken.None);
+ await notificationPublisher.PublishCountUnreadAsync(idUser.Value, CancellationToken.None);
+
+ await notificationService.RenotifyAsync(idUser.Value,
+ CancellationToken.None);
}
public override async Task OnDisconnectedAsync(Exception? exception)
diff --git a/AsbCloudWebApi/SignalR/Services/NotificationPublisher.cs b/AsbCloudWebApi/SignalR/Services/NotificationPublisher.cs
new file mode 100644
index 00000000..5c1d1e50
--- /dev/null
+++ b/AsbCloudWebApi/SignalR/Services/NotificationPublisher.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Repositories;
+using AsbCloudDb.Model;
+using AsbCloudWebApi.SignalR.Messages;
+using Microsoft.AspNetCore.SignalR;
+
+namespace AsbCloudWebApi.SignalR.Services;
+
+public class NotificationPublisher
+{
+ private readonly ConnectionManagerService connectionManagerService;
+ private readonly IHubContext notificationHubContext;
+ private readonly INotificationRepository notificationRepository;
+
+ public NotificationPublisher(ConnectionManagerService connectionManagerService,
+ IHubContext notificationHubContext,
+ INotificationRepository notificationRepository)
+ {
+ this.connectionManagerService = connectionManagerService;
+ this.notificationHubContext = notificationHubContext;
+ this.notificationRepository = notificationRepository;
+ }
+
+ public async Task PublishAsync(IGrouping groupedNotifications, CancellationToken cancellationToken)
+ {
+ var connectionId = connectionManagerService.GetConnectionIdByUserId(groupedNotifications.Key);
+
+ if (!string.IsNullOrWhiteSpace(connectionId))
+ {
+ foreach (var notification in groupedNotifications)
+ {
+ notification.SentDate = DateTime.UtcNow;
+ }
+
+ var notifications = groupedNotifications.Select(n => n);
+
+ await notificationRepository.UpdateRangeAsync(notifications,
+ cancellationToken);
+
+ await PublishMessageAsync(connectionId, new NotificationMessage
+ {
+ Notifications = notifications,
+ CountUnread = await notificationRepository.GetUnreadCountAsync(groupedNotifications.Key,
+ Notification.IdTransportTypeSignalR,
+ cancellationToken)
+ }, cancellationToken);
+ }
+ }
+
+ public async Task PublishCountUnreadAsync(int idUser, CancellationToken cancellationToken)
+ {
+ var connectionId = connectionManagerService.GetConnectionIdByUserId(idUser);
+
+ if (!string.IsNullOrWhiteSpace(connectionId))
+ {
+ await PublishMessageAsync(connectionId, new NotificationMessage
+ {
+ CountUnread = await notificationRepository.GetUnreadCountAsync(idUser, 0,
+ cancellationToken)
+ }, cancellationToken);
+ }
+ }
+
+ private async Task PublishMessageAsync(string connectionId, NotificationMessage message,
+ CancellationToken cancellationToken)
+ {
+ await notificationHubContext.Clients.Client(connectionId)
+ .SendAsync("receiveNotifications",
+ message,
+ cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs b/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs
index c29ea241..08258bef 100644
--- a/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs
+++ b/AsbCloudWebApi/SignalR/Services/SignalRNotificationTransportService.cs
@@ -4,10 +4,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
-using AsbCloudApp.Repositories;
using AsbCloudApp.Services.Notifications;
+using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background;
-using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace AsbCloudWebApi.SignalR.Services;
@@ -15,66 +14,44 @@ namespace AsbCloudWebApi.SignalR.Services;
public class SignalRNotificationTransportService : INotificationTransportService
{
private readonly NotificationBackgroundWorker backgroundWorker;
- private readonly ConnectionManagerService connectionManagerService;
- private readonly IHubContext notificationHubContext;
- public SignalRNotificationTransportService(NotificationBackgroundWorker backgroundWorker,
- ConnectionManagerService connectionManagerService,
- IHubContext notificationHubContext)
+ public SignalRNotificationTransportService(NotificationBackgroundWorker backgroundWorker)
{
this.backgroundWorker = backgroundWorker;
- this.connectionManagerService = connectionManagerService;
- this.notificationHubContext = notificationHubContext;
}
- public int IdTransportType => 0;
+ public int IdTransportType => Notification.IdTransportTypeSignalR;
- public Task SendAsync(NotificationDto notification,
- CancellationToken cancellationToken)
- {
- var workId = notification.Id.ToString();
-
- if (!backgroundWorker.Contains(workId))
- {
- var connectionId = connectionManagerService.GetConnectionIdByUserId(notification.IdUser);
-
- if (!string.IsNullOrWhiteSpace(connectionId))
- {
- var workAction = MakeSignalRSendWorkAction(notification, connectionId);
- var work = new WorkBase(workId, workAction);
- backgroundWorker.Push(work);
- }
- }
-
- return Task.CompletedTask;
- }
+ public Task SendAsync(NotificationDto notification,
+ CancellationToken cancellationToken) => SendRangeAsync(new[] { notification }, cancellationToken);
public Task SendRangeAsync(IEnumerable notifications,
CancellationToken cancellationToken)
{
- var tasks = notifications
- .Select(notification => SendAsync(notification, cancellationToken));
+ var workId = HashCode.Combine(notifications.Select(n => n.Id)).ToString("x");
- return Task.WhenAll(tasks);
+ if (backgroundWorker.Contains(workId))
+ return Task.CompletedTask;
+
+ var workAction = MakeSignalRSendWorkAction(notifications);
+ var work = new WorkBase(workId, workAction);
+ backgroundWorker.Push(work);
+
+ return Task.CompletedTask;
}
- private Func MakeSignalRSendWorkAction(NotificationDto notification,
- string connectionId)
+ private Func MakeSignalRSendWorkAction(IEnumerable notifications)
{
- 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();
+ var notificationPublisher = serviceProvider.GetRequiredService();
- await notificationRepository.UpdateAsync(notification, cancellationToken);
+ var groupedNotificationsByUsers = notifications.GroupBy(n => n.IdUser);
+
+ foreach (var groupedNotificationByUser in groupedNotificationsByUsers)
+ {
+ await notificationPublisher.PublishAsync(groupedNotificationByUser, cancellationToken);
+ }
};
}
}
\ No newline at end of file