ABP中的實時通訊Notification

本篇文章的目的: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種通知嚴重性級別 :InfoSuccessWarn, ErrorFatal。默認值爲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來獲取通知定義。然後,您可能需要準備一個自動頁面,以允許用戶訂閱這些通知。

參考資料:

abp源碼分析

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章