committed by
GitHub
16 changed files with 523 additions and 595 deletions
@ -1,26 +1,30 @@ |
|||
namespace LINGYUN.Abp.IM.Group |
|||
{ |
|||
public class Group |
|||
{ |
|||
/// <summary>
|
|||
/// 群组名称
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 允许匿名聊天
|
|||
/// </summary>
|
|||
public bool AllowAnonymous { get; set; } |
|||
/// <summary>
|
|||
/// 允许发送消息
|
|||
/// </summary>
|
|||
public bool AllowSendMessage { get; set; } |
|||
/// <summary>
|
|||
/// 最大用户数
|
|||
/// </summary>
|
|||
public int MaxUserLength { get; set; } |
|||
/// <summary>
|
|||
/// 群组用户数
|
|||
/// </summary>
|
|||
public int GroupUserCount { get; set; } |
|||
} |
|||
} |
|||
namespace LINGYUN.Abp.IM.Group |
|||
{ |
|||
public class Group |
|||
{ |
|||
/// <summary>
|
|||
/// 群组标识
|
|||
/// </summary>
|
|||
public string Id { get; set; } |
|||
/// <summary>
|
|||
/// 群组名称
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 允许匿名聊天
|
|||
/// </summary>
|
|||
public bool AllowAnonymous { get; set; } |
|||
/// <summary>
|
|||
/// 允许发送消息
|
|||
/// </summary>
|
|||
public bool AllowSendMessage { get; set; } |
|||
/// <summary>
|
|||
/// 最大用户数
|
|||
/// </summary>
|
|||
public int MaxUserLength { get; set; } |
|||
/// <summary>
|
|||
/// 群组用户数
|
|||
/// </summary>
|
|||
public int GroupUserCount { get; set; } |
|||
} |
|||
} |
|||
|
|||
@ -1,18 +1,32 @@ |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class AbpMessageServiceDomainSharedModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources.Add<MessageServiceResource>("en"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Localization.ExceptionHandling; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class AbpMessageServiceDomainSharedModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpMessageServiceDomainSharedModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<MessageServiceResource>("en") |
|||
.AddVirtualJson("/LINGYUN/Abp/MessageService/Localization/Resources"); |
|||
}); |
|||
|
|||
Configure<AbpExceptionLocalizationOptions>(options => |
|||
{ |
|||
options.MapCodeNamespace(MessageServiceErrorCodes.Namespace, typeof(MessageServiceResource)); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,55 +1,38 @@ |
|||
using LINGYUN.Abp.MessageService.Chat; |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using LINGYUN.Abp.MessageService.Mapper; |
|||
using LINGYUN.Abp.MessageService.ObjectExtending; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Localization.ExceptionHandling; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.ObjectExtending.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAutoMapperModule), |
|||
typeof(AbpCachingModule), |
|||
typeof(AbpMessageServiceDomainSharedModule))] |
|||
public class AbpMessageServiceDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<MessageServiceDomainAutoMapperProfile>(validate: true); |
|||
}); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpMessageServiceDomainModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<MessageServiceResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/MessageService/Localization/Resources"); |
|||
}); |
|||
|
|||
Configure<AbpExceptionLocalizationOptions>(options => |
|||
{ |
|||
options.MapCodeNamespace(MessageServiceErrorCodes.Namespace, typeof(MessageServiceResource)); |
|||
}); |
|||
} |
|||
|
|||
public override void PostConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity( |
|||
MessageServiceModuleExtensionConsts.ModuleName, |
|||
MessageServiceModuleExtensionConsts.EntityNames.Message, |
|||
typeof(Message) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.MessageService.Chat; |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using LINGYUN.Abp.MessageService.Mapper; |
|||
using LINGYUN.Abp.MessageService.ObjectExtending; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Localization.ExceptionHandling; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.ObjectExtending.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAutoMapperModule), |
|||
typeof(AbpCachingModule), |
|||
typeof(AbpMessageServiceDomainSharedModule))] |
|||
public class AbpMessageServiceDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<MessageServiceDomainAutoMapperProfile>(validate: true); |
|||
}); |
|||
} |
|||
|
|||
public override void PostConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity( |
|||
MessageServiceModuleExtensionConsts.ModuleName, |
|||
MessageServiceModuleExtensionConsts.EntityNames.Message, |
|||
typeof(Message) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,37 +0,0 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"LINGYUN.Abp.Message:01400": "Sending the message failed: The message is incomplete!", |
|||
"LINGYUN.Abp.Message:01401": "You have not joined the group and cannot operate!", |
|||
"LINGYUN.Abp.Message:02301": "Group application has been sent, waiting for administrator's approval", |
|||
"LINGYUN.Abp.Message:02302": "You need to validate the questions to join the group chat", |
|||
"LINGYUN.Abp.Message:02400": "The administrator has turned on silence mode!", |
|||
"LINGYUN.Abp.Message:02403": "The administrator has banned you from speaking!", |
|||
"LINGYUN.Abp.Message:02401": "The administrator does not allow anonymous speaking!", |
|||
"LINGYUN.Abp.Message:02404": "Sending the message failed: the group does not exist or is disbanded!", |
|||
"LINGYUN.Abp.Message:03301": "Friend request has been sent, waiting for the other party's approval", |
|||
"LINGYUN.Abp.Message:03302": "You need to verify the problem to add friends", |
|||
"LINGYUN.Abp.Message:03400": "The user has rejected all messages!", |
|||
"LINGYUN.Abp.Message:03401": "The user rejects the message you sent!", |
|||
"LINGYUN.Abp.Message:03402": "Users do not receive anonymous comments!", |
|||
"LINGYUN.Abp.Message:03403": "Sending the message failed: the person needs to agree to add a friend!", |
|||
"LINGYUN.Abp.Message:03404": "Sending the message failed: the user does not exist or is deactivated!", |
|||
"LINGYUN.Abp.Message:03410": "Users refuse to add friends", |
|||
"LINGYUN.Abp.Message:03411": "The other party is already your friend or has sent an authentication request. The operation cannot be repeated!", |
|||
"Notifications:MultiTenancy": "Multi Tenancy", |
|||
"Notifications:Users": "Users", |
|||
"Notifications:NewTenantRegisterd": "Tenant creation notification", |
|||
"Notifications:WelcomeToApplication": "User Welcome Notice", |
|||
"Notifications:IM": "Instant Messaging", |
|||
"Notifications:FriendValidation": "Friend verification notification", |
|||
"Notifications:NewFriend": "New friend notification", |
|||
"Notifications:RequestAddNewFriend": "User {name} has requested that you be added as a friend", |
|||
"Notifications:RequestAddNewFriendDetail": "Description: {description}", |
|||
"Notifications:JoinGroup": "Join group notification", |
|||
"Notifications:ExitGroup": "Exit group notification", |
|||
"Notifications:DissolveGroup": "Dissolve group notification", |
|||
"AddNewFriendBySearchId": "Add by account search", |
|||
"WelcomeToApplicationFormUser": "User :{0} welcome to join us!", |
|||
"Messages:NewFriend": "I have added you as a friend, let's chat together!" |
|||
} |
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"LINGYUN.Abp.Message:01400": "发送消息失败: 消息不完整!", |
|||
"LINGYUN.Abp.Message:01401": "您还未加入群组,不能进行操作!", |
|||
"LINGYUN.Abp.Message:02301": "已发送群组申请,等待管理员同意", |
|||
"LINGYUN.Abp.Message:02302": "你需要验证问题才能加入群聊", |
|||
"LINGYUN.Abp.Message:02400": "管理员已开启全员禁言!", |
|||
"LINGYUN.Abp.Message:02403": "管理员已禁止您发言!", |
|||
"LINGYUN.Abp.Message:02401": "管理员不允许匿名发言!", |
|||
"LINGYUN.Abp.Message:02404": "发送消息失败: 群组不存在或已解散!", |
|||
"LINGYUN.Abp.Message:03301": "已发送好友申请,等待对方同意", |
|||
"LINGYUN.Abp.Message:03302": "你需要验证问题才能添加好友", |
|||
"LINGYUN.Abp.Message:03400": "用户已拒接所有消息!", |
|||
"LINGYUN.Abp.Message:03401": "用户拒绝您发送的消息!", |
|||
"LINGYUN.Abp.Message:03402": "用户不接收匿名发言!", |
|||
"LINGYUN.Abp.Message:03403": "需要对方同意添加好友才能发送消息!", |
|||
"LINGYUN.Abp.Message:03404": "发送消息失败: 用户不存在或已注销账号!", |
|||
"LINGYUN.Abp.Message:03410": "用户拒绝添加好友", |
|||
"LINGYUN.Abp.Message:03411": "对方已是您的好友或已发送验证请求,不能重复操作!", |
|||
"Notifications:MultiTenancy": "租户通知", |
|||
"Notifications:Users": "用户通知", |
|||
"Notifications:NewTenantRegisterd": "租户创建通知", |
|||
"Notifications:WelcomeToApplication": "用户欢迎通知", |
|||
"Notifications:IM": "即时通讯", |
|||
"Notifications:FriendValidation": "好友验证通知", |
|||
"Notifications:NewFriend": "新好友通知", |
|||
"Notifications:RequestAddNewFriend": "用户 {name} 请求添加您为好友", |
|||
"Notifications:RequestAddNewFriendDetail": "附加说明: {description}", |
|||
"Notifications:JoinGroup": "加入群组通知", |
|||
"Notifications:ExitGroup": "退出群组通知", |
|||
"Notifications:DissolveGroup": "群组解散通知", |
|||
"AddNewFriendBySearchId": "通过账号搜索添加", |
|||
"WelcomeToApplicationFormUser": "用户:{0} 欢迎您的加入!", |
|||
"Messages:NewFriend": "我已经添加您为好友,让我们一起聊天吧!" |
|||
} |
|||
} |
|||
@ -1,221 +1,221 @@ |
|||
using LINGYUN.Abp.MessageService.Utils; |
|||
using LINGYUN.Abp.Notifications; |
|||
using LINGYUN.Abp.RealTime; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EventBus.Distributed |
|||
{ |
|||
/// <summary>
|
|||
/// 订阅通知发布事件,统一发布消息
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// 作用在于SignalR客户端只会与一台服务器建立连接,
|
|||
/// 只有启用了SignlR服务端的才能真正将消息发布到客户端
|
|||
/// </remarks>
|
|||
public class NotificationEventHandler : IDistributedEventHandler<NotificationEto<NotificationData>>, ITransientDependency |
|||
{ |
|||
/// <summary>
|
|||
/// Reference to <see cref="ILogger<DefaultNotificationDispatcher>"/>.
|
|||
/// </summary>
|
|||
public ILogger<NotificationEventHandler> Logger { get; set; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="AbpNotificationOptions"/>.
|
|||
/// </summary>
|
|||
protected AbpNotificationOptions Options { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="IJsonSerializer"/>.
|
|||
/// </summary>
|
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="IBackgroundJobManager"/>.
|
|||
/// </summary>
|
|||
protected IBackgroundJobManager BackgroundJobManager { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationStore"/>.
|
|||
/// </summary>
|
|||
protected INotificationStore NotificationStore { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationDefinitionManager"/>.
|
|||
/// </summary>
|
|||
protected INotificationDefinitionManager NotificationDefinitionManager { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationSubscriptionManager"/>.
|
|||
/// </summary>
|
|||
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationPublishProviderManager"/>.
|
|||
/// </summary>
|
|||
protected INotificationPublishProviderManager NotificationPublishProviderManager { get; } |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="NotificationEventHandler"/> class.
|
|||
/// </summary>
|
|||
public NotificationEventHandler( |
|||
IJsonSerializer jsonSerializer, |
|||
IBackgroundJobManager backgroundJobManager, |
|||
IOptions<AbpNotificationOptions> options, |
|||
INotificationStore notificationStore, |
|||
INotificationDefinitionManager notificationDefinitionManager, |
|||
INotificationSubscriptionManager notificationSubscriptionManager, |
|||
INotificationPublishProviderManager notificationPublishProviderManager) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
BackgroundJobManager = backgroundJobManager; |
|||
NotificationStore = notificationStore; |
|||
NotificationDefinitionManager = notificationDefinitionManager; |
|||
NotificationSubscriptionManager = notificationSubscriptionManager; |
|||
NotificationPublishProviderManager = notificationPublishProviderManager; |
|||
|
|||
Logger = NullLogger<NotificationEventHandler>.Instance; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task HandleEventAsync(NotificationEto<NotificationData> eventData) |
|||
{ |
|||
// 如果上面过滤了应用程序,这里可以使用Get方法,否则,最好使用GetOrNull加以判断
|
|||
var notification = NotificationDefinitionManager.GetOrNull(eventData.Name); |
|||
if (notification == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var notificationInfo = new NotificationInfo |
|||
{ |
|||
Name = notification.Name, |
|||
CreationTime = eventData.CreationTime, |
|||
Data = eventData.Data, |
|||
Severity = eventData.Severity, |
|||
Lifetime = notification.NotificationLifetime, |
|||
TenantId = eventData.TenantId, |
|||
Type = notification.NotificationType |
|||
}; |
|||
notificationInfo.SetId(eventData.Id); |
|||
|
|||
// TODO: 可以做成一个接口来序列化消息
|
|||
notificationInfo.Data = NotificationDataConverter.Convert(notificationInfo.Data); |
|||
|
|||
Logger.LogDebug($"Persistent notification {notificationInfo.Name}"); |
|||
|
|||
// 持久化通知
|
|||
await NotificationStore.InsertNotificationAsync(notificationInfo); |
|||
|
|||
var providers = Enumerable |
|||
.Reverse(NotificationPublishProviderManager.Providers); |
|||
|
|||
await PublishFromProvidersAsync(providers, eventData.Users, notificationInfo); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 指定提供者发布通知
|
|||
/// </summary>
|
|||
/// <param name="providers">提供者列表</param>
|
|||
/// <param name="notificationInfo">通知信息</param>
|
|||
/// <returns></returns>
|
|||
protected async Task PublishFromProvidersAsync( |
|||
IEnumerable<INotificationPublishProvider> providers, |
|||
IEnumerable<UserIdentifier> users, |
|||
NotificationInfo notificationInfo) |
|||
{ |
|||
// 检查是够已订阅消息
|
|||
Logger.LogDebug($"Gets a list of user subscriptions {notificationInfo.Name}"); |
|||
List<NotificationSubscriptionInfo> userSubscriptions; |
|||
if (users == null) |
|||
{ |
|||
// 获取用户订阅列表
|
|||
userSubscriptions = await NotificationSubscriptionManager |
|||
.GetUserSubscriptionsAsync(notificationInfo.TenantId, notificationInfo.Name); |
|||
} |
|||
else |
|||
{ |
|||
// 过滤未订阅的用户
|
|||
userSubscriptions = await NotificationSubscriptionManager |
|||
.GetUsersSubscriptionsAsync(notificationInfo.TenantId, notificationInfo.Name, users); |
|||
} |
|||
|
|||
users = userSubscriptions.Select(us => new UserIdentifier(us.UserId, us.UserName)); |
|||
|
|||
if (users.Count() > 0) |
|||
{ |
|||
// 持久化用户通知
|
|||
Logger.LogDebug($"Persistent user notifications {notificationInfo.Name}"); |
|||
await NotificationStore |
|||
.InsertUserNotificationsAsync( |
|||
notificationInfo, |
|||
users.Select(u => u.UserId)); |
|||
|
|||
// 2020-11-02 fix bug, 多个发送提供者处于同一个工作单元之下,不能把删除用户订阅写入到单个通知提供者完成事件中
|
|||
// 而且为了确保一致性,删除订阅移动到发布通知之前
|
|||
if (notificationInfo.Lifetime == NotificationLifetime.OnlyOne) |
|||
{ |
|||
// 一次性通知在发送完成后就取消用户订阅
|
|||
await NotificationStore |
|||
.DeleteUserSubscriptionAsync( |
|||
notificationInfo.TenantId, |
|||
users, |
|||
notificationInfo.Name); |
|||
} |
|||
|
|||
// 发布通知
|
|||
foreach (var provider in providers) |
|||
{ |
|||
await PublishAsync(provider, notificationInfo, users); |
|||
} |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 发布通知
|
|||
/// </summary>
|
|||
/// <param name="provider">通知发布者</param>
|
|||
/// <param name="notificationInfo">通知信息</param>
|
|||
/// <param name="subscriptionUserIdentifiers">订阅用户列表</param>
|
|||
/// <returns></returns>
|
|||
protected async Task PublishAsync( |
|||
INotificationPublishProvider provider, |
|||
NotificationInfo notificationInfo, |
|||
IEnumerable<UserIdentifier> subscriptionUserIdentifiers) |
|||
{ |
|||
try |
|||
{ |
|||
Logger.LogDebug($"Sending notification with provider {provider.Name}"); |
|||
var notifacationDataMapping = Options.NotificationDataMappings |
|||
.GetMapItemOrDefault(notificationInfo.Name, provider.Name); |
|||
if (notifacationDataMapping != null) |
|||
{ |
|||
notificationInfo.Data = notifacationDataMapping.MappingFunc(notificationInfo.Data); |
|||
} |
|||
// 发布
|
|||
await provider.PublishAsync(notificationInfo, subscriptionUserIdentifiers); |
|||
|
|||
Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning($"Send notification error with provider {provider.Name}"); |
|||
Logger.LogWarning($"Error message:{ex.Message}"); |
|||
|
|||
Logger.LogTrace(ex, $"Send notification error with provider { provider.Name}"); |
|||
|
|||
Logger.LogDebug($"Send notification error, notification {notificationInfo.Name} entry queue"); |
|||
// 发送失败的消息进入后台队列
|
|||
await BackgroundJobManager.EnqueueAsync( |
|||
new NotificationPublishJobArgs( |
|||
notificationInfo.GetId(), |
|||
provider.GetType().AssemblyQualifiedName, |
|||
subscriptionUserIdentifiers.ToList(), |
|||
notificationInfo.TenantId)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.MessageService.Utils; |
|||
using LINGYUN.Abp.Notifications; |
|||
using LINGYUN.Abp.RealTime; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EventBus.Distributed |
|||
{ |
|||
/// <summary>
|
|||
/// 订阅通知发布事件,统一发布消息
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// 作用在于SignalR客户端只会与一台服务器建立连接,
|
|||
/// 只有启用了SignlR服务端的才能真正将消息发布到客户端
|
|||
/// </remarks>
|
|||
public class NotificationEventHandler : IDistributedEventHandler<NotificationEto<NotificationData>>, ITransientDependency |
|||
{ |
|||
/// <summary>
|
|||
/// Reference to <see cref="ILogger<DefaultNotificationDispatcher>"/>.
|
|||
/// </summary>
|
|||
public ILogger<NotificationEventHandler> Logger { get; set; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="AbpNotificationOptions"/>.
|
|||
/// </summary>
|
|||
protected AbpNotificationOptions Options { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="IJsonSerializer"/>.
|
|||
/// </summary>
|
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="IBackgroundJobManager"/>.
|
|||
/// </summary>
|
|||
protected IBackgroundJobManager BackgroundJobManager { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationStore"/>.
|
|||
/// </summary>
|
|||
protected INotificationStore NotificationStore { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationDefinitionManager"/>.
|
|||
/// </summary>
|
|||
protected INotificationDefinitionManager NotificationDefinitionManager { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationSubscriptionManager"/>.
|
|||
/// </summary>
|
|||
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationPublishProviderManager"/>.
|
|||
/// </summary>
|
|||
protected INotificationPublishProviderManager NotificationPublishProviderManager { get; } |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="NotificationEventHandler"/> class.
|
|||
/// </summary>
|
|||
public NotificationEventHandler( |
|||
IJsonSerializer jsonSerializer, |
|||
IBackgroundJobManager backgroundJobManager, |
|||
IOptions<AbpNotificationOptions> options, |
|||
INotificationStore notificationStore, |
|||
INotificationDefinitionManager notificationDefinitionManager, |
|||
INotificationSubscriptionManager notificationSubscriptionManager, |
|||
INotificationPublishProviderManager notificationPublishProviderManager) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
BackgroundJobManager = backgroundJobManager; |
|||
NotificationStore = notificationStore; |
|||
NotificationDefinitionManager = notificationDefinitionManager; |
|||
NotificationSubscriptionManager = notificationSubscriptionManager; |
|||
NotificationPublishProviderManager = notificationPublishProviderManager; |
|||
|
|||
Logger = NullLogger<NotificationEventHandler>.Instance; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task HandleEventAsync(NotificationEto<NotificationData> eventData) |
|||
{ |
|||
// 如果上面过滤了应用程序,这里可以使用Get方法,否则,最好使用GetOrNull加以判断
|
|||
var notification = NotificationDefinitionManager.GetOrNull(eventData.Name); |
|||
if (notification == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var notificationInfo = new NotificationInfo |
|||
{ |
|||
Name = notification.Name, |
|||
CreationTime = eventData.CreationTime, |
|||
Data = eventData.Data, |
|||
Severity = eventData.Severity, |
|||
Lifetime = notification.NotificationLifetime, |
|||
TenantId = eventData.TenantId, |
|||
Type = notification.NotificationType |
|||
}; |
|||
notificationInfo.SetId(eventData.Id); |
|||
|
|||
// TODO: 可以做成一个接口来序列化消息
|
|||
notificationInfo.Data = NotificationDataConverter.Convert(notificationInfo.Data); |
|||
|
|||
Logger.LogDebug($"Persistent notification {notificationInfo.Name}"); |
|||
|
|||
// 持久化通知
|
|||
await NotificationStore.InsertNotificationAsync(notificationInfo); |
|||
|
|||
var providers = Enumerable |
|||
.Reverse(NotificationPublishProviderManager.Providers); |
|||
|
|||
await PublishFromProvidersAsync(providers, eventData.Users, notificationInfo); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 指定提供者发布通知
|
|||
/// </summary>
|
|||
/// <param name="providers">提供者列表</param>
|
|||
/// <param name="notificationInfo">通知信息</param>
|
|||
/// <returns></returns>
|
|||
protected async Task PublishFromProvidersAsync( |
|||
IEnumerable<INotificationPublishProvider> providers, |
|||
IEnumerable<UserIdentifier> users, |
|||
NotificationInfo notificationInfo) |
|||
{ |
|||
// 检查是够已订阅消息
|
|||
Logger.LogDebug($"Gets a list of user subscriptions {notificationInfo.Name}"); |
|||
List<NotificationSubscriptionInfo> userSubscriptions; |
|||
if (users == null) |
|||
{ |
|||
// 获取用户订阅列表
|
|||
userSubscriptions = await NotificationSubscriptionManager |
|||
.GetUserSubscriptionsAsync(notificationInfo.TenantId, notificationInfo.Name); |
|||
} |
|||
else |
|||
{ |
|||
// 过滤未订阅的用户
|
|||
userSubscriptions = await NotificationSubscriptionManager |
|||
.GetUsersSubscriptionsAsync(notificationInfo.TenantId, notificationInfo.Name, users); |
|||
} |
|||
|
|||
users = userSubscriptions.Select(us => new UserIdentifier(us.UserId, us.UserName)); |
|||
|
|||
if (users.Any()) |
|||
{ |
|||
// 持久化用户通知
|
|||
Logger.LogDebug($"Persistent user notifications {notificationInfo.Name}"); |
|||
await NotificationStore |
|||
.InsertUserNotificationsAsync( |
|||
notificationInfo, |
|||
users.Select(u => u.UserId)); |
|||
|
|||
// 2020-11-02 fix bug, 多个发送提供者处于同一个工作单元之下,不能把删除用户订阅写入到单个通知提供者完成事件中
|
|||
// 而且为了确保一致性,删除订阅移动到发布通知之前
|
|||
if (notificationInfo.Lifetime == NotificationLifetime.OnlyOne) |
|||
{ |
|||
// 一次性通知在发送完成后就取消用户订阅
|
|||
await NotificationStore |
|||
.DeleteUserSubscriptionAsync( |
|||
notificationInfo.TenantId, |
|||
users, |
|||
notificationInfo.Name); |
|||
} |
|||
|
|||
// 发布通知
|
|||
foreach (var provider in providers) |
|||
{ |
|||
await PublishAsync(provider, notificationInfo, users); |
|||
} |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 发布通知
|
|||
/// </summary>
|
|||
/// <param name="provider">通知发布者</param>
|
|||
/// <param name="notificationInfo">通知信息</param>
|
|||
/// <param name="subscriptionUserIdentifiers">订阅用户列表</param>
|
|||
/// <returns></returns>
|
|||
protected async Task PublishAsync( |
|||
INotificationPublishProvider provider, |
|||
NotificationInfo notificationInfo, |
|||
IEnumerable<UserIdentifier> subscriptionUserIdentifiers) |
|||
{ |
|||
try |
|||
{ |
|||
Logger.LogDebug($"Sending notification with provider {provider.Name}"); |
|||
var notifacationDataMapping = Options.NotificationDataMappings |
|||
.GetMapItemOrDefault(notificationInfo.Name, provider.Name); |
|||
if (notifacationDataMapping != null) |
|||
{ |
|||
notificationInfo.Data = notifacationDataMapping.MappingFunc(notificationInfo.Data); |
|||
} |
|||
// 发布
|
|||
await provider.PublishAsync(notificationInfo, subscriptionUserIdentifiers); |
|||
|
|||
Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning($"Send notification error with provider {provider.Name}"); |
|||
Logger.LogWarning($"Error message:{ex.Message}"); |
|||
|
|||
Logger.LogTrace(ex, $"Send notification error with provider { provider.Name}"); |
|||
|
|||
Logger.LogDebug($"Send notification error, notification {notificationInfo.Name} entry queue"); |
|||
// 发送失败的消息进入后台队列
|
|||
await BackgroundJobManager.EnqueueAsync( |
|||
new NotificationPublishJobArgs( |
|||
notificationInfo.GetId(), |
|||
provider.GetType().AssemblyQualifiedName, |
|||
subscriptionUserIdentifiers.ToList(), |
|||
notificationInfo.TenantId)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,83 +1,72 @@ |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using LINGYUN.Abp.MultiTenancy; |
|||
using LINGYUN.Abp.Notifications; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EventBus.Distributed |
|||
{ |
|||
public class TenantCreateEventHandler : IDistributedEventHandler<CreateEventData>, ITransientDependency |
|||
{ |
|||
protected ILogger<TenantCreateEventHandler> Logger { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected ISettingProvider SettingProvider { get; } |
|||
protected IStringLocalizer StringLocalizer { get; } |
|||
protected INotificationSender NotificationSender { get; } |
|||
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
|||
|
|||
public TenantCreateEventHandler( |
|||
ICurrentTenant currentTenant, |
|||
ISettingProvider settingProvider, |
|||
INotificationSender notificationSender, |
|||
INotificationSubscriptionManager notificationSubscriptionManager, |
|||
IStringLocalizer<MessageServiceResource> stringLocalizer, |
|||
ILogger<TenantCreateEventHandler> logger) |
|||
{ |
|||
Logger = logger; |
|||
CurrentTenant = currentTenant; |
|||
SettingProvider = settingProvider; |
|||
StringLocalizer = stringLocalizer; |
|||
NotificationSender = notificationSender; |
|||
NotificationSubscriptionManager = notificationSubscriptionManager; |
|||
} |
|||
|
|||
public async Task HandleEventAsync(CreateEventData eventData) |
|||
{ |
|||
var tenantAdminUserIdentifier = new UserIdentifier(eventData.AdminUserId, eventData.AdminEmailAddress); |
|||
|
|||
// 租户管理员订阅事件
|
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
eventData.Id, |
|||
tenantAdminUserIdentifier, |
|||
TenantNotificationNames.NewTenantRegistered); |
|||
|
|||
var userDefaultCultureName = await SettingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage); |
|||
if (userDefaultCultureName.IsNullOrWhiteSpace()) |
|||
{ |
|||
userDefaultCultureName = CultureInfo.CurrentUICulture.Name; |
|||
} |
|||
// 使用系统区域语言发布通知
|
|||
using (CultureHelper.Use(userDefaultCultureName, userDefaultCultureName)) |
|||
{ |
|||
var notificationData = new NotificationData(); |
|||
notificationData.WriteStandardData( |
|||
L("NewTenantRegisteredNotificationTitle"), |
|||
L("NewTenantRegisteredNotificationMessage", eventData.Name), |
|||
DateTime.Now, eventData.AdminEmailAddress); |
|||
|
|||
// 发布租户创建通知
|
|||
await NotificationSender |
|||
.SendNofiterAsync( |
|||
TenantNotificationNames.NewTenantRegistered, |
|||
notificationData, |
|||
tenantAdminUserIdentifier, |
|||
eventData.Id, |
|||
NotificationSeverity.Success); |
|||
} |
|||
} |
|||
|
|||
protected string L(string name, params object[] args) |
|||
{ |
|||
return StringLocalizer[name, args]?.Value; |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using LINGYUN.Abp.MultiTenancy; |
|||
using LINGYUN.Abp.Notifications; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EventBus.Distributed |
|||
{ |
|||
public class TenantCreateEventHandler : IDistributedEventHandler<CreateEventData>, ITransientDependency |
|||
{ |
|||
protected ILogger<TenantCreateEventHandler> Logger { get; } |
|||
protected INotificationSender NotificationSender { get; } |
|||
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
|||
|
|||
public TenantCreateEventHandler( |
|||
INotificationSender notificationSender, |
|||
INotificationSubscriptionManager notificationSubscriptionManager, |
|||
ILogger<TenantCreateEventHandler> logger) |
|||
{ |
|||
Logger = logger; |
|||
NotificationSender = notificationSender; |
|||
NotificationSubscriptionManager = notificationSubscriptionManager; |
|||
} |
|||
|
|||
public async Task HandleEventAsync(CreateEventData eventData) |
|||
{ |
|||
var tenantAdminUserIdentifier = new UserIdentifier(eventData.AdminUserId, eventData.AdminEmailAddress); |
|||
|
|||
Logger.LogInformation("tenant administrator subscribes to new tenant events.."); |
|||
// 租户管理员订阅事件
|
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
eventData.Id, |
|||
tenantAdminUserIdentifier, |
|||
TenantNotificationNames.NewTenantRegistered); |
|||
|
|||
var notificationData = new NotificationData(); |
|||
notificationData.WriteLocalizedData( |
|||
new LocalizableStringInfo( |
|||
LocalizationResourceNameAttribute.GetName(typeof(MessageServiceResource)), |
|||
"NewTenantRegisteredNotificationTitle", |
|||
new Dictionary<object, object> |
|||
{ |
|||
{ "User", eventData.Name } |
|||
}), |
|||
new LocalizableStringInfo( |
|||
LocalizationResourceNameAttribute.GetName(typeof(MessageServiceResource)), |
|||
"NewTenantRegisteredNotificationMessage", |
|||
new Dictionary<object, object> |
|||
{ |
|||
{ "User", eventData.Name} |
|||
}), |
|||
DateTime.Now, eventData.AdminEmailAddress); |
|||
|
|||
Logger.LogInformation("publish new tenant notification.."); |
|||
// 发布租户创建通知
|
|||
await NotificationSender |
|||
.SendNofiterAsync( |
|||
TenantNotificationNames.NewTenantRegistered, |
|||
notificationData, |
|||
tenantAdminUserIdentifier, |
|||
eventData.Id, |
|||
NotificationSeverity.Success); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,63 +1,72 @@ |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EventBus |
|||
{ |
|||
public class UserCreateSendWelcomeEventHandler : ILocalEventHandler<EntityCreatedEventData<UserEto>>, ITransientDependency |
|||
{ |
|||
private readonly INotificationSender _notificationSender; |
|||
private readonly INotificationSubscriptionManager _notificationSubscriptionManager; |
|||
public UserCreateSendWelcomeEventHandler( |
|||
INotificationSender notificationSender, |
|||
INotificationSubscriptionManager notificationSubscriptionManager |
|||
) |
|||
{ |
|||
_notificationSender = notificationSender; |
|||
_notificationSubscriptionManager = notificationSubscriptionManager; |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityCreatedEventData<UserEto> eventData) |
|||
{ |
|||
var userIdentifer = new UserIdentifier(eventData.Entity.Id, eventData.Entity.UserName); |
|||
// 订阅用户欢迎消息
|
|||
await _notificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
eventData.Entity.TenantId, |
|||
userIdentifer, |
|||
UserNotificationNames.WelcomeToApplication); |
|||
|
|||
var userWelcomeNotifictionData = new NotificationData(); |
|||
|
|||
//userWelcomeNotifictionData.WriteStandardData(
|
|||
// L("WelcomeToApplicationFormUser", eventData.Entity.Name ?? eventData.Entity.UserName),
|
|||
// L("WelcomeToApplicationFormUser", eventData.Entity.Name ?? eventData.Entity.UserName),
|
|||
// DateTime.Now, eventData.Entity.UserName);
|
|||
|
|||
userWelcomeNotifictionData |
|||
.WriteLocalizedData( |
|||
new LocalizableStringInfo( |
|||
LocalizationResourceNameAttribute.GetName(typeof(MessageServiceResource)), |
|||
"WelcomeToApplicationFormUser"), |
|||
new LocalizableStringInfo( |
|||
LocalizationResourceNameAttribute.GetName(typeof(MessageServiceResource)), |
|||
"WelcomeToApplicationFormUser"), |
|||
DateTime.Now, eventData.Entity.UserName); |
|||
|
|||
await _notificationSender |
|||
.SendNofiterAsync( |
|||
UserNotificationNames.WelcomeToApplication, |
|||
userWelcomeNotifictionData, |
|||
userIdentifer, |
|||
eventData.Entity.TenantId, |
|||
NotificationSeverity.Info |
|||
); |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.MessageService.Localization; |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EventBus |
|||
{ |
|||
public class UserCreateSendWelcomeEventHandler : ILocalEventHandler<EntityCreatedEventData<UserEto>>, ITransientDependency |
|||
{ |
|||
private readonly INotificationSender _notificationSender; |
|||
private readonly INotificationSubscriptionManager _notificationSubscriptionManager; |
|||
public UserCreateSendWelcomeEventHandler( |
|||
INotificationSender notificationSender, |
|||
INotificationSubscriptionManager notificationSubscriptionManager |
|||
) |
|||
{ |
|||
_notificationSender = notificationSender; |
|||
_notificationSubscriptionManager = notificationSubscriptionManager; |
|||
} |
|||
|
|||
public async Task HandleEventAsync(EntityCreatedEventData<UserEto> eventData) |
|||
{ |
|||
var userIdentifer = new UserIdentifier(eventData.Entity.Id, eventData.Entity.UserName); |
|||
// 订阅用户欢迎消息
|
|||
await _notificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
eventData.Entity.TenantId, |
|||
userIdentifer, |
|||
UserNotificationNames.WelcomeToApplication); |
|||
|
|||
var userWelcomeNotifictionData = new NotificationData(); |
|||
|
|||
//userWelcomeNotifictionData.WriteStandardData(
|
|||
// L("WelcomeToApplicationFormUser", eventData.Entity.Name ?? eventData.Entity.UserName),
|
|||
// L("WelcomeToApplicationFormUser", eventData.Entity.Name ?? eventData.Entity.UserName),
|
|||
// DateTime.Now, eventData.Entity.UserName);
|
|||
|
|||
userWelcomeNotifictionData |
|||
.WriteLocalizedData( |
|||
new LocalizableStringInfo( |
|||
LocalizationResourceNameAttribute.GetName(typeof(MessageServiceResource)), |
|||
"WelcomeToApplicationFormUser", |
|||
new Dictionary<object, object> |
|||
{ |
|||
{ "User", eventData.Entity.UserName } |
|||
}), |
|||
new LocalizableStringInfo( |
|||
LocalizationResourceNameAttribute.GetName(typeof(MessageServiceResource)), |
|||
"WelcomeToApplicationFormUser", |
|||
new Dictionary<object, object> |
|||
{ |
|||
{ "User", eventData.Entity.UserName } |
|||
}), |
|||
DateTime.Now, eventData.Entity.UserName); |
|||
|
|||
await _notificationSender |
|||
.SendNofiterAsync( |
|||
UserNotificationNames.WelcomeToApplication, |
|||
userWelcomeNotifictionData, |
|||
userIdentifer, |
|||
eventData.Entity.TenantId, |
|||
NotificationSeverity.Info |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue