本篇文章的目的:1.瞭解ABP實時通訊的類型及用法。2.根據實時通訊相關的類、數據庫,去使用
1.ABP中關於Notification的數據結構如下:
在ABP中對應的Dto
NotificationSubscription:用於封裝封裝notification 和subscriptor(User) 的關係的DTO,不是Entity。記錄訂閱某種通知的人。
AbpTenantNotifications存儲通知信息的實體。對應字段信息爲
1.notificationName: 通知的唯一名字(發佈通知時使用相同的值).
2.DataTypeName:通知數據類型。完整類型名稱,包括名稱空間。我們可以在處理通知數據時檢查此類型。
3.EntityTypeName:實體信息,如果這是有關通知的實體。
4.Severity:嚴重性:NotificationSeverity枚舉的值。0: 信息; 1:成功; 2:警告; 3:錯誤; 4: 致命。
實體在程序中相似的Dto是UserNotification:用於封裝User和Notification關係的信息。對應字段信息爲:
1.UserId:接受的用戶Id
2.TenantNotificationId:關聯的消息Id
3.State:閱讀狀態。已讀/未讀。
其中TenantNotification與UserNotification存在的是一對多的關係。TenantNotification存儲發佈的通知的信息(內容,依賴類名等),UserNotification存儲消息的發送與接受對象等信息(接收人,TenantNotificationID,是否已讀等信息)。
二、消息的使用(以下內容來源於官網):
ASP.NET樣板提供的pub / sub基於(發布/訂閱)的實時通知系統。可知系統有兩種向用戶發送通知的方式。
1.訂閱。用戶訂閱特定的通知類型。然後,我們發佈這種類型的通知,該通知傳遞給所有訂閱用戶。這是發佈/訂閱模型
2.直接發佈
通知類型:
①一般通知是任意類型的通知。如果用戶發送了一個友好請求,請通知我“這是此類通知的一個示例。
②實體通知。實體通知與特定實體相關聯。“如果有用戶評論此照片,請通知我”是基於實體的通知,因爲它與特定的照片實體相關聯。用戶可能希望獲取一些照片的通知,但不是所有照片的通知。
通知又分級別,在NotificationSeverity枚舉中定義了5種通知嚴重性級別 :Info,Success,Warn, Error和Fatal。默認值爲Info。對應到AbpTenantNotifications實體的Severity字段。
3.訂閱樣例。
public class MyService : ITransientDependency
{
//ABP封裝好的訂閱管理類
private readonly INotificationSubscriptionManager _notificationSubscriptionManager;
public MyService(INotificationSubscriptionManager notificationSubscriptionManager)
{
_notificationSubscriptionManager = notificationSubscriptionManager;
}
//訂閱一般通知,如用戶希望在某人發送友誼請求時得到通知
public async Task Subscribe_SentFriendshipRequest(int? tenantId, long userId)
{
await _notificationSubscriptionManager.SubscribeAsync(new UserIdentifier(tenantId, userId), "SentFriendshipRequest");
}
//訂閱關聯實體的通知,如用戶希望在有人向指定照片寫評論時得到通知
public async Task Subscribe_CommentPhoto(int? tenantId, long userId, Guid photoId)
{
await _notificationSubscriptionManager.SubscribeAsync(new UserIdentifier(tenantId, userId), "CommentPhoto", new EntityIdentifier(typeof(Photo), photoId));
}
}
該INotificationSubscriptionManager也有UnsubscribeAsync,IsSubscribedAsync,GetSubscriptionsAsync ...方法來管理訂閱。
發佈通知
public class MyService : ITransientDependency
{
private readonly INotificationPublisher _notificationPublisher;
public MyService(INotificationPublisher notificationPublisher)
{
_notificationPublisher = notificationPublisher;
}
//發送一個普通通知
public async Task Publish_SentFrendshipRequest(string senderUserName, string friendshipMessage, UserIdentifier targetUserId)
{
await _notificationPublisher.PublishAsync("SentFrendshipRequest", new SentFrendshipRequestNotificationData(senderUserName, friendshipMessage), userIds: new[] { targetUserId });
}
//發送某個實體的通知到某個用戶
public async Task Publish_CommentPhoto(string commenterUserName, string comment, Guid photoId, UserIdentifier photoOwnerUserId)
{
await _notificationPublisher.PublishAsync("CommentPhoto", new CommentPhotoNotificationData(commenterUserName, comment), new EntityIdentifier(typeof(Photo), photoId), userIds: new[] { photoOwnerUserId });
}
//發送通知到當前租戶會話中的所有用戶
public async Task Publish_LowDisk(int remainingDiskInMb)
{
var data = new LocalizableMessageNotificationData(new LocalizableString("LowDiskWarningMessage", "MyLocalizationSourceName"));
data["remainingDiskInMb"] = remainingDiskInMb;
await _notificationPublisher.PublishAsync("System.LowDisk", data, severity: NotificationSeverity.Warn);
}
}
以上代碼中,第一、二個樣例使用了通知數據類SentFrendshipRequestNotificationData、CommentPhotoNotificationData,第三個樣例使用的是本地化和可參數化的通知信息LocalizableMessageNotificationData與,但其實在數據庫存儲,所有通知數據類都是序列化存儲的。
通知數據類的樣例如下:
[Serializable]
public class SentFrendshipRequestNotificationData : NotificationData
{
public string SenderUserName { get; set; }
public string FriendshipMessage { get; set; }
public SentFrendshipRequestNotificationData(string senderUserName, string friendshipMessage)
{
SenderUserName = senderUserName;
FriendshipMessage = friendshipMessage;
}
}
用戶通知管理器
該IUserNotificationManager用於管理用戶的通知。它具有獲取,更新或刪除用戶通知的方法。您可以使用實現獲取用戶通知列表頁面、消息置爲已讀等功能。
//消息置爲已讀
public async Task SetAllNotificationsAsRead()
{
await _userNotificationManager.UpdateAllUserNotificationStatesAsync(AbpSession.ToUserIdentifier(), UserNotificationState.Read);
}
public async Task SetNotificationAsRead(EntityDto<Guid> input)
{
var userNotification = await _userNotificationManager.GetUserNotificationAsync(AbpSession.TenantId, input.Id);
if (userNotification.UserId != AbpSession.GetUserId())
{
throw new ApplicationException(string.Format("Given user notification id ({0}) is not belong to the current user ({1})", input.Id, AbpSession.GetUserId()));
}
await _userNotificationManager.UpdateUserNotificationStateAsync(AbpSession.TenantId, input.Id, UserNotificationState.Read);
}
//獲取用戶消息列表
[DisableAuditing]
public async Task<GetNotificationsOutput> GetUserNotifications(GetUserNotificationsInput input)
{
var totalCount = await _userNotificationManager.GetUserNotificationCountAsync(
AbpSession.ToUserIdentifier(), input.State
);
var unreadCount = await _userNotificationManager.GetUserNotificationCountAsync(
AbpSession.ToUserIdentifier(), UserNotificationState.Unread
);
var notifications = await _userNotificationManager.GetUserNotificationsAsync(
AbpSession.ToUserIdentifier(), input.State, input.SkipCount, input.MaxResultCount
);
return new GetNotificationsOutput(totalCount, unreadCount, notifications);
}
實時通知
當使用IUserNotificationManager來查詢通知時,我們一般想實時地將通知推送到客戶端。
通知系統使用了IRealTimeNotifier向用戶發送實時通知。這可以使用任何類型的實時通訊系統來實現。ABP在一個單獨的包中使用了 SignalR來實現。ABP官網的起始模板已經安裝了SignalR。
注意:通知系統在一個後臺工作中異步調用IRealTimeNotifier。因此,通知發送時可能伴有輕微的延遲。
public class EmailRealTimeNotifier : IRealTimeNotifier, ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly UserManager _userManager;
public EmailRealTimeNotifier(
IEmailSender emailSender,
UserManager userManager)
{
_emailSender = emailSender;
_userManager = userManager;
}
public async Task SendNotificationsAsync(UserNotification[] userNotifications)
{
foreach (var userNotification in userNotifications)
{
if (userNotification.Notification.Data is MessageNotificationData data)
{
var user = await _userManager.GetUserByIdAsync(userNotification.UserId);
_emailSender.Send(
to: user.EmailAddress,
subject: "You have a new notification!",
body: data.Message,
isBodyHtml: true
);
}
}
}
}
將其添加到模塊的PreInitialize方法中:
Configuration.Notifications.Notifiers.Add<EmailRealTimeNotifier>();
客戶端
當接收到一個實時通知時,ABP會在客戶端觸發一個全局的事件(global event)。若前端使用的是vue,則在main.js函數中,在created方法裏註冊接受通知的事件,如下:
abp.event.on('abp.notifications.received', function (userNotification) {
//這裏寫入收到消息後的操作。
//例如我的操作是,在界面顯示消息內容,並更新用戶通知列表
});
通知定義
您無需定義通知即可使用它。您可以使用任何通知名稱而無需定義它。但是,定義它可能會給您帶來一些額外的好處。例如,您可以隨後 調查應用程序中的所有通知。在這種情況下,我們可以爲我們的模塊定義一個通知提供程序, 如下所示:
public class MyAppNotificationProvider : NotificationProvider
{
public override void SetNotifications(INotificationDefinitionContext context)
{
context.Manager.Add(
new NotificationDefinition(
"App.NewUserRegistered",
displayName: new LocalizableString("NewUserRegisteredNotificationDefinition", "MyLocalizationSourceName"),
permissionDependency: new SimplePermissionDependency("App.Pages.UserManagement")
)
);
}
}
“ App.NewUserRegistered ”是通知的唯一名稱。我們定義了一個可本地化的displayName,以便我們在訂閱UI上的通知時可以顯示它。最後,我們聲明僅當用戶具有“ App.Pages.UserManagement ” 權限時,此通知纔對用戶可用。
您還可以在代碼中研究其他一些參數。注意:通知名稱是通知定義所必需的。
定義了這樣的通知提供程序之後,我們必須在模塊的PreInitialize方法中註冊它 ,如下所示:
public class AbpZeroTemplateCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.Notifications.Providers.Add<MyAppNotificationProvider>();
}
//...
}
最後,您可以在應用程序中注入並使用INotificationDefinitionManager來獲取通知定義。然後,您可能需要準備一個自動頁面,以允許用戶訂閱這些通知。
參考資料: