using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.Configuration;
using Abp.Domain.Services;
using Abp.Domain.Uow;
using Abp.Extensions;
using Castle.Core.Internal;
namespace Abp.Notifications
{
///
/// Used to distribute notifications to users.
///
public class DefaultNotificationDistributer : DomainService, INotificationDistributer
{
public IRealTimeNotifier RealTimeNotifier { get; set; }
private readonly INotificationDefinitionManager _notificationDefinitionManager;
private readonly INotificationStore _notificationStore;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IGuidGenerator _guidGenerator;
///
/// Initializes a new instance of the class.
///
public DefaultNotificationDistributer(
INotificationDefinitionManager notificationDefinitionManager,
INotificationStore notificationStore,
IUnitOfWorkManager unitOfWorkManager,
IGuidGenerator guidGenerator)
{
_notificationDefinitionManager = notificationDefinitionManager;
_notificationStore = notificationStore;
_unitOfWorkManager = unitOfWorkManager;
_guidGenerator = guidGenerator;
RealTimeNotifier = NullRealTimeNotifier.Instance;
}
public async Task DistributeAsync(Guid notificationId)
{
var notificationInfo = await _notificationStore.GetNotificationOrNullAsync(notificationId);
if (notificationInfo == null)
{
Logger.Warn("NotificationDistributionJob can not continue since could not found notification by id: " + notificationId);
return;
}
var users = await GetUsers(notificationInfo);
var userNotifications = await SaveUserNotifications(users, notificationInfo);
await _notificationStore.DeleteNotificationAsync(notificationInfo);
try
{
await RealTimeNotifier.SendNotificationsAsync(userNotifications.ToArray());
}
catch (Exception ex)
{
Logger.Warn(ex.ToString(), ex);
}
}
[UnitOfWork]
protected virtual async Task GetUsers(NotificationInfo notificationInfo)
{
List userIds;
if (!notificationInfo.UserIds.IsNullOrEmpty())
{
//Directly get from UserIds
userIds = notificationInfo
.UserIds
.Split(",")
.Select(uidAsStr => UserIdentifier.Parse(uidAsStr))
.Where(uid => SettingManager.GetSettingValueForUser(NotificationSettingNames.ReceiveNotifications, uid.TenantId, uid.UserId))
.ToList();
}
else
{
//Get subscribed users
var tenantIds = GetTenantIds(notificationInfo);
List subscriptions;
if (tenantIds.IsNullOrEmpty() ||
(tenantIds.Length == 1 && tenantIds[0] == NotificationInfo.AllTenantIds.To()))
{
//Get all subscribed users of all tenants
subscriptions = await _notificationStore.GetSubscriptionsAsync(
notificationInfo.NotificationName,
notificationInfo.EntityTypeName,
notificationInfo.EntityId
);
}
else
{
//Get all subscribed users of specified tenant(s)
subscriptions = await _notificationStore.GetSubscriptionsAsync(
tenantIds,
notificationInfo.NotificationName,
notificationInfo.EntityTypeName,
notificationInfo.EntityId
);
}
//Remove invalid subscriptions
var invalidSubscriptions = new Dictionary();
//TODO: Group subscriptions per tenant for potential performance improvement
foreach (var subscription in subscriptions)
{
using (CurrentUnitOfWork.SetTenantId(subscription.TenantId))
{
if (!await _notificationDefinitionManager.IsAvailableAsync(notificationInfo.NotificationName, new UserIdentifier(subscription.TenantId, subscription.UserId)) ||
!SettingManager.GetSettingValueForUser(NotificationSettingNames.ReceiveNotifications, subscription.TenantId, subscription.UserId))
{
invalidSubscriptions[subscription.Id] = subscription;
}
}
}
subscriptions.RemoveAll(s => invalidSubscriptions.ContainsKey(s.Id));
//Get user ids
userIds = subscriptions
.Select(s => new UserIdentifier(s.TenantId, s.UserId))
.ToList();
}
if (!notificationInfo.ExcludedUserIds.IsNullOrEmpty())
{
//Exclude specified users.
var excludedUserIds = notificationInfo
.ExcludedUserIds
.Split(",")
.Select(uidAsStr => UserIdentifier.Parse(uidAsStr))
.ToList();
userIds.RemoveAll(uid => excludedUserIds.Any(euid => euid.Equals(uid)));
}
return userIds.ToArray();
}
private static int?[] GetTenantIds(NotificationInfo notificationInfo)
{
if (notificationInfo.TenantIds.IsNullOrEmpty())
{
return null;
}
return notificationInfo
.TenantIds
.Split(",")
.Select(tenantIdAsStr => tenantIdAsStr == "null" ? (int?)null : (int?)tenantIdAsStr.To())
.ToArray();
}
[UnitOfWork]
protected virtual async Task> SaveUserNotifications(UserIdentifier[] users, NotificationInfo notificationInfo)
{
var userNotifications = new List();
var tenantGroups = users.GroupBy(user => user.TenantId);
foreach (var tenantGroup in tenantGroups)
{
using (_unitOfWorkManager.Current.SetTenantId(tenantGroup.Key))
{
var tenantNotificationInfo = new TenantNotificationInfo(_guidGenerator.Create(), tenantGroup.Key, notificationInfo);
await _notificationStore.InsertTenantNotificationAsync(tenantNotificationInfo);
await _unitOfWorkManager.Current.SaveChangesAsync(); //To get tenantNotification.Id.
var tenantNotification = tenantNotificationInfo.ToTenantNotification();
foreach (var user in tenantGroup)
{
var userNotification = new UserNotificationInfo(_guidGenerator.Create())
{
TenantId = tenantGroup.Key,
UserId = user.UserId,
TenantNotificationId = tenantNotificationInfo.Id
};
await _notificationStore.InsertUserNotificationAsync(userNotification);
userNotifications.Add(userNotification.ToUserNotification(tenantNotification));
}
await CurrentUnitOfWork.SaveChangesAsync(); //To get Ids of the notifications
}
}
return userNotifications;
}
}
}