8.1 ABP实时服务 - 通知系统

8.1.1 简介

在系统中,通知是用来告知用户特定事件的。ABP提供了一个基于实时通知的基础设施 pub/sub.

8.1.2 发送模式

有两种方法来发送通知给用户:

  • 用户 订阅 一个特定的通知类型。当我们发布这个类型的通知时,该通知会被投递给所有的订阅用户。这就是 pub/sub 模式。

  • 我们能直接的发送通知给目标用户。

8.1.3 通知类型

通知类型也有两种:

  • 常规通知 是任意类型的通知。例如:"如果某个用户发送给我一个交友请求,那么通知我。"

  • 实体通知 是被关联到一个指定的实体。"如果某个用户评论了这个照片,那么通知我。",这是一个基于实体的通知,因为它被关联到一个特定的照片实体。用户可以得到对某些照片评论的通知,而不是所有照片的。

8.1.4 通知数据

通常来说通知中包含了通知数据。例如:"如果某个用户发送给我一个友好的请求,那么通知我。",该通知可以有两个数据属性:发送者的用户名(那个用户发送了这个交友请求)和请求信息(用户发送请求的具体信息)。很显然,这个通知的数据类型是和通知类型紧密耦合的。不同的通知类型有不同的数据类型。

通知数据是可选的。有些通知不需要数据。ABP预定义了一些通知数据类型,这些类型适合于大多数情况下。MessageNotificationData 能够用于一些简单的消息,而 LocalizableMessageNotificationData 可以用于本地化和参数化的通知消息。在后面的部分我们会看到一些有用的例子。

8.1.5 通知的程度

ABP有5种程度的通知等级,它被定义在枚举类型 NotificationSeverity 中: Info, Success, Warn, Error 以及 Fatal。 默认是 Info。很显然,这个通知的数据类型是和通知类型紧密耦合的。不同的通知类型有不同的数据类型。

关于通知持久化

了解更多请看 Notification Store

8.1.6 订阅通知

INotificationSubscriptionManager 接口中提供了订阅通知的API,例如:

首先,我们 注入 INotificationSubscriptionManager 接口。第一个方法是定义 常规通知,用户想得到某些用户发来的交友请求的通知。第二个方法是订阅与 特定实体(Photo) 有关的通知。用户想得到某些用户对特定的照片写了评论的通知。

每个通知的类型应该有唯一的名字(就像例子中的 SentFrendshipRequest 和 CommentPhoto 一样)。

INotificationSubscriptionManager 还有其它的方法来管理订阅,如: UnsubscribeAsync, IsSubscribedAsync, GetSubscriptionsAsync等等。

8.1.7 发布通知

INotificationPublisher 被用来发布通知,例如:

在第一个例子中,我们对一个用户发布了一条通知。SentFrendshipRequestNotificationData 应该派生自 NotificationData 如下所示:

在第二个例子中,我们发送了一个特定实体的通知给了特定的用户。通知数据类不需要被 序列化 (因为默认被序列化为JSON)。但是还是建议你将特性:Serializable 加在数据类上,因为你可能需要在应用之间移动通知,也可能在将来使用二进制序列化。此外,正如之前声明的那样,通知数据是可选的,而且对于所有的通知可能不是必须的。

注意:如果我们发布通知给特定的用户,那么他们不需要订阅那些通知。

在第三个例子中,我们没有定义一个专门的通知数据类。相反,直接使用了内置的基于字典类型的 LocalizableMessageNotificationData 类,并以 Warn 等级来发布通知。 LocalizableMessageNotificationData 能存储基于字典的任意数据(这是由于自定义通知数据类型是继承 NotificationData 类)。我们在本地化时使用 remainingDiskInMb 作为参数。本地化消息可以包含这些参数(就像例子中的"Attention! Only {remainingDiskInMb} MBs left on the disk!")。我们将会在客户端看到如何本地化。

8.1.8 用户通知管理

IUserNotificationManager 被用来管理用户通知。有这些方法来为用户 get, update 或 delete 通知。你可以使用它们为你的应用程序来准备一个通知列表页面。

8.1.9 实时通知

当你使用 IUserNotificationManager 来查询通知时,通常我们是想实时推送通知到客户端。

通知系统使用 IRealTimeNotifier 接口发送实时通知给用户。任何类型的实时通信系统都能实现它。它已经被实现在一个独立的 SignalR 包中。在启动模板中已经安装了SignalR。详情请参考集成SignalR

注意:在后台作业中,通信系统异步调用 IRealTimeNotifier。所以,通知可能会延迟一小会才会被发送。

8.1.10 客户端

当实时通知被接受的时候,ABP在客户端触发了一个 全局事件 。你可以像下面一样注册它来获取通知:

每次接受到实时通知 abp.notifications.received 事件会被触发。你能够想上面展示的那样注册这个事件来获取通知。详情请参考事件总线arrow-up-right。下面是一个传入的JSON格式的"System.LowDisk"示例:

在这个对象中:

  • userId:当前用户Id。通常你不需要知道这个,因为你知道那个是当前用户。

  • state:枚举类型 UserNotificationState 的值。 0:Unread,1:Read

  • notification:通知详细信息:

    1. notificationName: 通知的唯一名字(发布通知时使用相同的值)。

    2. data:通知数据。在上面例子中,我们使用了 LocalizableMessageNotificationData (在之前的例子中我们使用它来发布的)。

      • message: 本地化信息。在UI端,我们可以使用 sourceName 和 name 来本地化信息。

      • type:通知数据类型。类型的全名称,包含名称空间。当处理这个通知数据的时候,我们可以检查这个类型。

      • properties:自定义属的基于字典类型的属性。

    3. entityType,entityTypeName 和 entityId:实体信息,如果这是一个与实体相关的通知。

    4. severity:枚举类型 NotificationSeverity 的值。0: Info, 1: Success, 2: Warn, 3: Error, 4: Fatal

    5. creationTime:表示通知被创建的时间。

    6. id:通知的id。

  • id:用户通知id。

当然你不会去记录这个通知。你可以使用通知数据来显示通知信息给用户。例如:

为了能够处理通知数据,我们应该检查数据类型。这个简单的例子展示的是从通知数据中取得消息。对于本地化消息(LocalizableMessageNotificationData),我们要本地化该消息并替换掉参数。对于简单消息(MessageNotificationData),我们直接的取得该消息。当然,在一个真实的项目中,我们不会使用alert函数。我们会使用 abp.notify API来展示漂亮的UI通知。

如果你需要实现上面所展示的逻辑,这里有一个更简单且可伸缩性的方法。当推送通知被接收到的时候,你可以仅使用一行代码来显示 UI通知

下面展示的是一个 UI 通知 (上面System.LowDisk示例)。

它对于内置的通知数据类型(LocalizableMessageNotificationData 和 MessageNotificationData)可以很好的工作。如果你有自定义的通知数据类型,那么你应该像下面一样使用格式化器来注册这个数据:

因此,showUiNotifyForUserNotification 能对你的数据类型来创建并显示该消息。如果你仅需要格式化消息,你可以直接的使用 abp.notifications.getFormattedMessageFromUserNotification(userNotification),这是由showUiNotifyForUserNotification内部使用的。

当你接受到推送通知的时候,启动模板已经包含了显示UI通知的代码。

8.1.11 存储通知

通知系统使用 INotificationStore 来持久化通知。为了使通知系统正确的工作,我们应该实现这个接口。你可以自己实现它,或者使用已经实现了该接口的 module-zero

8.1.12 通知定义

使用之前,你不需要定义一个通知。你可以使用任何的没有被定义过的通知名字。但是,定义通知名字可以给你带来额外的好处。例如:你可能需要在你的应用程序中调研所有的通知。在这种情况下,我们需要为我们模块 通知提供器(notification provider) ,如下所示:

App.NewUserRegistered 是通知的唯一名字。我们定义了一个本地化的 displayName(当我们订阅了该通知时,我们可以在UI上显示它)。最后,我们声明了 App.Pages.UserManagement 权限,只有当用户具有该权限的时候,该通知才会显示给用户。

当然这里还有其它一些参数。你可以在代码中研究它们。对于通知定义只有通知的名字是必须的。

在你定义了如上所述的通知提供器后,我们应该在模块的 PreInitialize 方法中注册它。如下所示:

最后,你可以在你的应用程序中注入并使用 INotificationDefinitionManager 接口来获取通知定义。

Last updated