fixed and tested

This commit is contained in:
ngfrolov 2023-07-17 11:48:52 +05:00
parent 635e4cd7fc
commit 2759a852ca
Signed by: ng.frolov
GPG Key ID: E99907A0357B29A7
11 changed files with 185 additions and 207 deletions

View File

@ -8,7 +8,7 @@ public class NotificationRequest : RequestBase
/// <summary> /// <summary>
/// Получение отправленных/не отправленных уведомлений /// Получение отправленных/не отправленных уведомлений
/// </summary> /// </summary>
public bool IsSent { get; set; } = false; public bool? IsSent { get; set; } = false;
/// <summary> /// <summary>
/// Id типа доставки уведомления /// Id типа доставки уведомления

View File

@ -1,50 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Requests;
namespace AsbCloudApp.Services.Notifications;
/// <summary>
/// Интерфейс для работы с уведомлениями
/// </summary>
public interface INotificationService
{
/// <summary>
/// Отправка нового уведомления
/// </summary>
/// <param name="idUser"></param>
/// <param name="idNotificationCategory"></param>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="idTransportType"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task NotifyAsync(int idUser,
int idNotificationCategory,
string title,
string message,
int idTransportType,
CancellationToken cancellationToken);
/// <summary>
/// Обновление уведомления
/// </summary>
/// <param name="idNotification"></param>
/// <param name="isRead"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task UpdateNotificationAsync(int idNotification,
bool isRead,
CancellationToken cancellationToken);
/// <summary>
/// Отправка уведомлений, которые не были отправлены
/// </summary>
/// <param name="idUser"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task ResendNotificationAsync(int idUser,
NotificationRequest request,
CancellationToken cancellationToken);
}

View File

@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
namespace AsbCloudApp.Services.Notifications;
/// <summary>
/// Сервис для работы с уведомлениями
/// </summary>
public class NotificationService
{
private readonly ICrudRepository<NotificationCategoryDto> notificationCategoryRepository;
private readonly INotificationRepository notificationRepository;
private readonly IEnumerable<INotificationTransportService> notificationTransportServices;
/// <summary>
/// Сервис для работы с уведомлениями
/// </summary>
/// <param name="notificationCategoryRepository"></param>
/// <param name="notificationRepository"></param>
/// <param name="notificationTransportServices"></param>
public NotificationService(ICrudRepository<NotificationCategoryDto> notificationCategoryRepository,
INotificationRepository notificationRepository,
IEnumerable<INotificationTransportService> notificationTransportServices)
{
this.notificationCategoryRepository = notificationCategoryRepository;
this.notificationRepository = notificationRepository;
this.notificationTransportServices = notificationTransportServices;
}
/// <summary>
/// Отправка нового уведомления
/// </summary>
/// <param name="idUser"></param>
/// <param name="idNotificationCategory"></param>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="idTransportType"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task NotifyAsync(int idUser,
int idNotificationCategory,
string title,
string message,
int idTransportType,
CancellationToken cancellationToken)
{
var notificationCategory = await notificationCategoryRepository
.GetOrDefaultAsync(idNotificationCategory, cancellationToken)
?? throw new ArgumentInvalidException("Категория уведомления не найдена", nameof(idNotificationCategory));
var notification = new NotificationDto()
{
IdUser = idUser,
IdNotificationCategory = idNotificationCategory,
Title = title,
Message = message,
IdTransportType = idTransportType
};
notification.Id = await notificationRepository.InsertAsync(notification,
cancellationToken);
notification.NotificationCategory = notificationCategory;
var notificationTransportService = GetNotificationTransportService(idTransportType);
await notificationTransportService.SendAsync(notification, cancellationToken);
await notificationRepository.UpdateAsync(notification,
cancellationToken);
}
/// <summary>
/// Обновление уведомления
/// </summary>
/// <param name="idNotification"></param>
/// <param name="isRead"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task UpdateNotificationAsync(int idNotification,
bool isRead,
CancellationToken cancellationToken)
{
var notification = await notificationRepository.GetOrDefaultAsync(idNotification,
cancellationToken)
?? throw new ArgumentInvalidException("Уведомление не найдено", nameof(idNotification));
if (isRead)
{
if (notification.SentDate == null)
throw new ArgumentInvalidException("Уведомление не может быть прочитано", nameof(isRead));
notification.SentDate = DateTime.UtcNow;
}
await notificationRepository.UpdateAsync(notification,
cancellationToken);
}
/// <summary>
/// Отправка уведомлений, которые не были отправлены
/// </summary>
/// <param name="idUser"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task ResendNotificationAsync(int idUser,
NotificationRequest request,
CancellationToken cancellationToken)
{
if (!request.IdTransportType.HasValue)
throw new ArgumentInvalidException("Id типа доставки уведомления должен иметь значение",
nameof(request.IdTransportType));
var result = await notificationRepository.GetNotificationsAsync(idUser,
request,
cancellationToken);
var notificationTransportService = GetNotificationTransportService(request.IdTransportType.Value);
await notificationTransportService.SendRangeAsync(result.Items,
cancellationToken);
await notificationRepository.UpdateRangeAsync(result.Items,
cancellationToken);
}
private INotificationTransportService GetNotificationTransportService(int idTransportType)
{
var notificationTransportService = notificationTransportServices
.FirstOrDefault(s => s.IdTransportType == idTransportType)
?? throw new ArgumentInvalidException("Доставщик уведомлений не найден", nameof(idTransportType));
return notificationTransportService;
}
}

View File

@ -24,7 +24,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using AsbCloudApp.Services.Notifications; using AsbCloudApp.Services.Notifications;
using AsbCloudInfrastructure.Services.Notifications;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -150,7 +149,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<IGtrRepository, GtrWitsRepository>(); services.AddTransient<IGtrRepository, GtrWitsRepository>();
services.AddTransient<INotificationService, NotificationService>(); services.AddTransient<NotificationService>();
services.AddTransient<INotificationRepository, NotificationRepository>(); services.AddTransient<INotificationRepository, NotificationRepository>();
services.AddTransient<ICrudRepository<NotificationCategoryDto>, CrudCacheRepositoryBase<NotificationCategoryDto, services.AddTransient<ICrudRepository<NotificationCategoryDto>, CrudCacheRepositoryBase<NotificationCategoryDto,
NotificationCategory>>(); NotificationCategory>>();

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -46,7 +47,9 @@ public class NotificationRepository : CrudCacheRepositoryBase<NotificationDto, N
dbContext.Notifications.UpdateRange(entities); dbContext.Notifications.UpdateRange(entities);
return await dbContext.SaveChangesAsync(cancellationToken); var result = await dbContext.SaveChangesAsync(cancellationToken);
DropCache();
return result;
} }
public async Task<PaginationContainer<NotificationDto>> GetNotificationsAsync(int idUser, public async Task<PaginationContainer<NotificationDto>> GetNotificationsAsync(int idUser,
@ -65,7 +68,7 @@ public class NotificationRepository : CrudCacheRepositoryBase<NotificationDto, N
Count = await query.CountAsync(cancellationToken), Count = await query.CountAsync(cancellationToken),
}; };
if (result.Count == 0) if (result.Count < skip)
return result; return result;
result.Items = await query result.Items = await query
@ -84,11 +87,15 @@ public class NotificationRepository : CrudCacheRepositoryBase<NotificationDto, N
{ {
var query = dbContext.Notifications var query = dbContext.Notifications
.Include(x => x.NotificationCategory) .Include(x => x.NotificationCategory)
.Where(n => n.IdUser == idUser) .Where(n => n.IdUser == idUser);
.AsQueryable();
if (!request.IsSent) if (request.IsSent.HasValue)
query = query.Where(n => n.SentDate == null); {
if(request.IsSent.Value)
query = query.Where(n => n.SentDate != null);
else
query = query.Where(n => n.SentDate == null);
}
if (request.IdTransportType.HasValue) if (request.IdTransportType.HasValue)
query = query.Where(n => n.IdTransportType == request.IdTransportType); query = query.Where(n => n.IdTransportType == request.IdTransportType);

View File

@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Notifications;
namespace AsbCloudInfrastructure.Services.Notifications;
public class NotificationService : INotificationService
{
private readonly ICrudRepository<NotificationCategoryDto> notificationCategoryRepository;
private readonly INotificationRepository notificationRepository;
private readonly IEnumerable<INotificationTransportService> notificationTransportServices;
public NotificationService(ICrudRepository<NotificationCategoryDto> notificationCategoryRepository,
INotificationRepository notificationRepository,
IEnumerable<INotificationTransportService> notificationTransportServices)
{
this.notificationCategoryRepository = notificationCategoryRepository;
this.notificationRepository = notificationRepository;
this.notificationTransportServices = notificationTransportServices;
}
public async Task NotifyAsync(int idUser,
int idNotificationCategory,
string title,
string message,
int idTransportType,
CancellationToken cancellationToken)
{
var notificationCategory = await notificationCategoryRepository
.GetOrDefaultAsync(idNotificationCategory, cancellationToken);
if(notificationCategory is null)
throw new ArgumentInvalidException("Категория уведомления не найдена", nameof(idNotificationCategory));
var notification = new NotificationDto()
{
IdUser = idUser,
IdNotificationCategory = idNotificationCategory,
Title = title,
Message = message,
IdTransportType = idTransportType
};
notification.Id = await notificationRepository.InsertAsync(notification,
cancellationToken);
notification.NotificationCategory = notificationCategory;
var notificationTransportService = GetNotificationTransportService(idTransportType);
await notificationTransportService.SendAsync(notification, cancellationToken);
await notificationRepository.UpdateAsync(notification,
cancellationToken);
}
public async Task UpdateNotificationAsync(int idNotification,
bool isRead,
CancellationToken cancellationToken)
{
var notification = await notificationRepository.GetOrDefaultAsync(idNotification,
cancellationToken)
?? throw new ArgumentInvalidException("Уведомление не найдено", nameof(idNotification));
if (isRead)
{
if (notification.SentDate == null)
throw new ArgumentInvalidException("Уведомление не может быть прочитано", nameof(isRead));
notification.SentDate = DateTime.UtcNow;
}
await notificationRepository.UpdateAsync(notification,
cancellationToken);
}
public async Task ResendNotificationAsync(int idUser,
NotificationRequest request,
CancellationToken cancellationToken)
{
if (!request.IdTransportType.HasValue)
throw new ArgumentInvalidException("Id типа доставки уведомления должен иметь значение",
nameof(request.IdTransportType));
var result = await notificationRepository.GetNotificationsAsync(idUser,
request,
cancellationToken);
var notificationTransportService = GetNotificationTransportService(request.IdTransportType.Value);
await notificationTransportService.SendRangeAsync(result.Items,
cancellationToken);
await notificationRepository.UpdateRangeAsync(result.Items,
cancellationToken);
}
private INotificationTransportService GetNotificationTransportService(int idTransportType)
{
var notificationTransportService = notificationTransportServices
.FirstOrDefault(s => s.IdTransportType == idTransportType)
?? throw new ArgumentInvalidException("Доставщик уведомлений не найден", nameof(idTransportType));
return notificationTransportService;
}
}

View File

@ -19,10 +19,10 @@ namespace AsbCloudWebApi.Controllers;
[Route("api/notification")] [Route("api/notification")]
public class NotificationController : ControllerBase public class NotificationController : ControllerBase
{ {
private readonly INotificationService notificationService; private readonly NotificationService notificationService;
private readonly INotificationRepository notificationRepository; private readonly INotificationRepository notificationRepository;
public NotificationController(INotificationService notificationService, public NotificationController(NotificationService notificationService,
INotificationRepository notificationRepository) INotificationRepository notificationRepository)
{ {
this.notificationService = notificationService; this.notificationService = notificationService;

View File

@ -24,7 +24,7 @@ namespace AsbCloudWebApi
{ {
services.AddSwaggerGen(c => services.AddSwaggerGen(c =>
{ {
c.MapType<TimeSpan>(() => new OpenApiSchema { Type = "string", Example = new OpenApiString("1.00:00:00") }); c.MapType<TimeSpan>(() => new OpenApiSchema { Type = "string", Example = new OpenApiString("0.00:00:00") });
c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" }); c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" });
c.MapType<JsonValue>(() => new OpenApiSchema c.MapType<JsonValue>(() => new OpenApiSchema
{ {

View File

@ -13,49 +13,41 @@ namespace AsbCloudWebApi.SignalR;
public class NotificationHub : BaseHub public class NotificationHub : BaseHub
{ {
private readonly ConnectionManagerService connectionManagerService; private readonly ConnectionManagerService connectionManagerService;
private readonly INotificationService notificationService; private readonly NotificationService notificationService;
public NotificationHub(ConnectionManagerService connectionManagerService, public NotificationHub(ConnectionManagerService connectionManagerService,
INotificationService notificationService) NotificationService notificationService)
{ {
this.connectionManagerService = connectionManagerService; this.connectionManagerService = connectionManagerService;
this.notificationService = notificationService; this.notificationService = notificationService;
} }
public async Task OnConnected(NotificationRequest request) public override async Task OnConnectedAsync()
{ {
try var idUser = Context.User?.GetUserId();
{
await base.OnConnectedAsync();
var idUser = Context.User?.GetUserId();
if (!idUser.HasValue) if (!idUser.HasValue)
return; return;
string connectionId = Context.ConnectionId; string connectionId = Context.ConnectionId;
connectionManagerService.AddOrUpdateConnection(idUser.Value, connectionId); connectionManagerService.AddOrUpdateConnection(idUser.Value, connectionId);
await notificationService.ResendNotificationAsync(idUser.Value, await base.OnConnectedAsync();
request,
CancellationToken.None); await notificationService.ResendNotificationAsync(idUser.Value,
} new NotificationRequest { IsSent = false, IdTransportType = 0},
catch (Exception e) CancellationToken.None);
{
Console.WriteLine(e);
}
} }
public async Task OnDisconnected() public override async Task OnDisconnectedAsync(Exception? exception)
{ {
await base.OnDisconnectedAsync(null);
var idUser = Context.User?.GetUserId(); var idUser = Context.User?.GetUserId();
if (!idUser.HasValue) if (!idUser.HasValue)
return; return;
connectionManagerService.RemoveConnection(idUser.Value); connectionManagerService.RemoveConnection(idUser.Value);
await base.OnDisconnectedAsync(exception);
} }
} }

View File

@ -26,7 +26,7 @@ public class SignalRNotificationTransportService : INotificationTransportService
public async Task SendAsync(NotificationDto notification, public async Task SendAsync(NotificationDto notification,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
const string method = "notifications"; const string method = "receiveNotifications";
var connectionId = connectionManagerService.GetConnectionIdByUserId(notification.IdUser); var connectionId = connectionManagerService.GetConnectionIdByUserId(notification.IdUser);
@ -44,8 +44,8 @@ public class SignalRNotificationTransportService : INotificationTransportService
public Task SendRangeAsync(IEnumerable<NotificationDto> notifications, public Task SendRangeAsync(IEnumerable<NotificationDto> notifications,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var tasks = Array.ConvertAll(notifications.ToArray(), notification => var tasks = notifications
SendAsync(notification, cancellationToken)); .Select(notification => SendAsync(notification, cancellationToken));
return Task.WhenAll(tasks); return Task.WhenAll(tasks);
} }

View File

@ -10,7 +10,7 @@ internal class Program
{ {
var connectionBuilder = new HubConnectionBuilder(); var connectionBuilder = new HubConnectionBuilder();
var connection = connectionBuilder var connection = connectionBuilder
.WithUrl("http://test.digitaldrilling.ru/hubs/telemetry", connectionOptions => { .WithUrl("http://localhost:5000/hubs/notifications", connectionOptions => {
connectionOptions.AccessTokenProvider = AccessTokenProvider; connectionOptions.AccessTokenProvider = AccessTokenProvider;
}) })
.WithAutomaticReconnect() .WithAutomaticReconnect()
@ -25,9 +25,10 @@ internal class Program
Console.WriteLine("connecting"); Console.WriteLine("connecting");
connection.StartAsync().Wait(); connection.StartAsync().Wait();
Console.WriteLine("AddToGroup"); //Console.WriteLine("OnConnected");
connection.SendCoreAsync("AddToGroup", new object[] { "well_1" }).Wait(); //connection.SendCoreAsync("OnConnected", new object[] { }, CancellationToken.None).Wait();
var subsction = connection.On<object>("UpdateProcessMap", (str1) => {
var subsction = connection.On<object>("receiveNotifications", (str1) => {
Console.WriteLine(str1); Console.WriteLine(str1);
} ); } );