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; } } }