ABP理论学习之通知系统

时间:2022-09-14 10:40:17

返回总目录


本篇目录

介绍

通知(Notification)用于告知用户系统中的特定事件。ABP提供了基于实时通知基础设施的发布订阅模型(pub/sub)。

发送模型

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

  • 首先用户订阅特定的通知类型,然后我们发布这种类型的通知,这种类型的通知会传递给所有已经订阅的用户。这就是发布订阅(pub/sub)模型。
  • 直接给目标用户发送通知。

通知类型

通知类型也有两种:

  • 一般通知:是任意类型的通知。“如果一个用户给我发送了添加好友的请求就通知我”是这种通知类型的一个例子。
  • 实体通知:和特定的实体相关联。“如果一个用户在 这张图片上评论那么通知我”是基于实体的通知,因为它和特定的实体相关联。用户可能想获得某些图片的通知而不是所有的图片通知。

通知数据

一个通知一般都会包括一个通知数据。比如,“如果一个用户给我发送了添加好友的请求就通知我”,这个通知可能有两个数据属性:发送者用户名(那哪一个用户发送的这个添加好友的请求)和请求内容(该用户在这个请求中写的东西)。很明显,该通知数据类型紧耦合于通知类型。不同的通知类型有不同的数据类型。

  • 通知数据是可选的。某些通知可能不需要数据。一些预定义的通知数据类型可能对于大多数情况够用了。 MessageNotificationData可以用于简单的信息, LocalizableMessageNotificationData可以用于本地化的,带参数的通知信息。

通知安全

通知的安全性有5个级别,它们定义在NotificationSeverity枚举类中,分别是 Info,Success,Warn,Error和Fatal。默认值是Info。

订阅通知

INotificationSubscriptionManager 提供了订阅通知的API。例如:

public class MyService : ITransientDependency
{
private readonly INotificationSubscriptionManager _notificationSubscriptionManager; public MyService(INotificationSubscriptionManager notificationSubscriptionManager)
{
_notificationSubscriptionManager = notificationSubscriptionManager;
} //订阅一个一般通知
public async Task Subscribe_SentFrendshipRequest(int? tenantId, long userId)
{
await _notificationSubscriptionManager.SubscribeAsync(tenantId, userId, "SentFrendshipRequest");
} //订阅一个实体通知
public async Task Subscribe_CommentPhoto(int? tenantId, long userId, Guid photoId)
{
await _notificationSubscriptionManager.SubscribeAsync(tenantId, userId, "CommentPhoto", new EntityIdentifier(typeof(Photo), photoId));
}
}

首先,我们注入了INotificationSubscriptionManager。第一个方法订阅了一个一般通知。当某人发送了一个添加好友的请求时,用户想得到通知。第二个方法订阅了一个和特定实体(Photo)相关的通知。如果任何人对这个特定的图片进行了评论,那么用户就会收到通知。

每一个通知应该有唯一的名字(比如例子中的SentFrendshipRequest和 CommentPhoto)。

INotificationSubscriptionManager还有很多方法来管理通知,比如UnsubscribeAsync, IsSubscribedAsync, GetSubscriptionsAsync等方法。

发布通知

INotificationPublisher用于发布通知,例如:

public class MyService : ITransientDependency
{
private readonly INotificationPublisher _notiticationPublisher; public MyService(INotificationPublisher notiticationPublisher)
{
_notiticationPublisher = notiticationPublisher;
} //给特定的用户发送一个一般通知
public async Task Publish_SentFrendshipRequest(string senderUserName, string friendshipMessage, long targetUserId)
{
await _notiticationPublisher.PublishAsync("SentFrendshipRequest", new SentFrendshipRequestNotificationData(senderUserName, friendshipMessage), userIds: new[] { targetUserId });
} //给特定的用户发送一个实体通知
public async Task Publish_CommentPhoto(string commenterUserName, string comment, Guid photoId, long photoOwnerUserId)
{
await _notiticationPublisher.PublishAsync("CommentPhoto", new CommentPhotoNotificationData(commenterUserName, comment), new EntityIdentifier(typeof(Photo), photoId), userIds: new[] { photoOwnerUserId });
} //给具特定严重级别程度的所有订阅用户发送一个一般通知
public async Task Publish_LowDisk(int remainingDiskInMb)
{
//例如 "LowDiskWarningMessage"的英文内容 -> "Attention! Only {remainingDiskInMb} MBs left on the disk!"
var data = new LocalizableMessageNotificationData(new LocalizableString("LowDiskWarningMessage", "MyLocalizationSourceName"));
data["remainingDiskInMb"] = remainingDiskInMb; await _notiticationPublisher.PublishAsync("System.LowDisk", data, severity: NotificationSeverity.Warn);
}
}

在第一个例子中,我们向单个用户发布了一个通知。SentFrendshipRequestNotificationData应该从 NotificationData继承,比如:

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

在第二个例子中,我们向特定的用户发送了一个特定实体的通知。通知数据类CommentPhotoNotificationData一般不需要serializble(因为默认使用了JSON序列化)。但是建议把它标记为serializable,因为你可能需要在应用之间移动通知,也可能在未来使用二进制的序列。此外,正如之前声明的那样,通知数据是可选的,而且对于所有的通知可能不是必须的。

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

在第三个例子中,我们没有定义一个专用的通知数据类。相反,我们直接使用了内置的具有基于字典数据的LocalizableMessageNotificationData,并且以 Warn发布了通知。LocalizableMessageNotificationData可以存储基于字典的任意数据(这对于自定义的通知数据类也是成立的,因为它们也从 NotificationData类继承)。在本地化时我们使用了“ remaingDiskInMb”作为参数。本地化信息可以包含这些参数(比如例子中的"Attention! Only {remainingDiskInMb} MBs left on the disk!")

用户通知管理者

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

实时通知

当使用IUserNotificationManager来查询通知时,我们一般想实时地将通知推送到客户端。

通知系统使用了IRealTimeNotifier向用户发送实时通知。这可以使用任何类型的实时通讯系统来实现。ABP在一个单独的包中使用了 SignalR来实现。ABP官网的起始模板已经安装了SignalR。

注意:通知系统在一个后台工作中异步调用IRealTimeNotifier。因此,通知发送时可能伴有轻微的延迟。

客户端

当接收到一个实时通知时,ABP会在客户端触发一个全局的事件(global event)。你可以像下面那样注册来获取通知:

abp.event.on('abp.notifications.received', function (userNotification) {
console.log(userNotification);
});

abp.notifications.received事件对于每个接收到的实时通知都会触发。你可以像上面那样注册该事件来获取通知。查看javascript事件总线文档获取更多信息。对于上面的“System.LowDisk”例子,下面是接收到的通知消息的JSON数据:

{
"userId": 2,
"state": 0,
"notification": {
"notificationName": "System.LowDisk",
"data": {
"message": {
"sourceName": "MyLocalizationSourceName",
"name": "LowDiskWarningMessage"
},
"type": "Abp.Notifications.LocalizableMessageNotificationData",
"properties": {
"remainingDiskInMb": "42"
}
},
"entityType": null,
"entityTypeName": null,
"entityId": null,
"severity": 0,
"creationTime": "2016-02-09T17:03:32.13",
"id": "0263d581-3d8a-476b-8e16-4f6a6f10a632"
},
"id": "4a546baf-bf17-4924-b993-32e420a8d468"
}

在这个对象中,

  • userId:当前的用户Id。一般来说不需要这个,因为你知道当前的用户。
  • stateUserNotificationState枚举的值。0:未读,1:已读。
  • notification:通知细节。
    • notificationName:通知的唯一名称。(当发布该通知时使用相同的值)
    • data:通知数据。在本例中,我们使用了**LocalizableMessageNotificationData **(正如之前的例子中发布的)。
    • message:本地的信息通知。我们可以使用 sourceNamename来本地化UI上的信息。
    • type:通知数据类型。全类型名称,包括命名空间。当处理该通知数据时我们可以检查该类型。
    • properties:基于字典的自定义属性。
    • entityType, entityTypeName和entityId:和通知相关的实体的信息。
    • severityNotificationSeverity枚举的值。0: Info, 1: Success, 2: Warn, 3: Error, 4: Fatal。
    • creationTime:该通知创建的时间。
    • id:通知的Id。
  • id:用户的通知id。

当然,你不仅可以记录该通知。你还可以使用通知数据将通知信息展示给该用户。例如:

abp.event.on('abp.notifications.received', function (userNotification) {
if (userNotification.notification.data.type === 'Abp.Notifications.LocalizableMessageNotificationData') {
var localizedText = abp.localization.localize(
userNotification.notification.data.message.name,
userNotification.notification.data.message.sourceName
); $.each(userNotification.notification.data.properties, function (key, value) {
localizedText = localizedText.replace('{' + key + '}', value);
}); alert('New localized notification: ' + localizedText);
} else if (userNotification.notification.data.type === 'Abp.Notifications.MessageNotificationData') {
alert('New simple notification: ' + userNotification.notification.data.message);
}
});

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

如果你要实现这么上面的逻辑,那么有一种更简单且可伸缩的方式。当接收到一个push通知时,你可以只使用一句代码来显示UI通知:

abp.event.on('abp.notifications.received', function (userNotification) {
abp.notifications.showUiNotifyForUserNotification(userNotification);
});

这句代码会展示一个UI通知如下所示(上面System.LowDisk通知的例子):

ABP理论学习之通知系统

对于内置的通知数据类型(LocalizableMessageNotificationData和 MessageNotificationData)是有效的。如果你有自定义的通知数据类型,那么你应该注册数据格式化器,如下所示:


abp.notifications.messageFormatters['MyProject.MyNotificationDataType'] = function(userNotification) {
return ...; //format and return message here
};

这样,showUiNotifyForUserNotification就可以对你的数据类型创建要显示的信息了。如果你只需要格式化信息,那么你可以直接使用 abp.notifications.getFormattedMessageFromUserNotification(userNotification),它内部还是使用了showUiNotifyForUserNotification。

当接收到一个push通知时,启动模板包含了展示UI通知的代码。

通知存储

通知系统使用了INotificationStore来持久化通知。为了使通知系统合适地工作,应该实现这个接口。你可以自己实现或者使用module-zero(它已经实现了该接口)。

通知定义

在使用之前你不必定义一个通知。你无需定义任何类型的通知名就可以使用它们。但是,定义通知名可能会给你带来额外的好处。比如,你以后要研究应用程序中所有的通知,在这种情况下,我们可以给我们的模块定义一个通知提供器(notification provider ),如下所示:

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理论学习之通知系统的更多相关文章

  1. ABP文档 - 通知系统

    文档目录 本节内容: 简介 发送模式 通知类型 通知数据 通知重要性 关于通知持久化 订阅通知 发布通知 用户通知管理器 实时通知 客户端 通知存储 通知定义 简介 通知用来告知用户系统里特定的事件发 ...

  2. &lbrack;ABP实战开源项目&rsqb;---ABP实时服务-通知系统&period;发布模式

    简介 在ABP中,提供了通知服务.它是一个基于实时通知的基础设施.分为订阅模式和发布模式. 本次会在项目中使用发布模式来演示一个用户注册后,收到的欢迎信息. 发布模式 首先我们在领域层建立" ...

  3. ABP理论学习之模块系统

    返回总目录 本篇目录 模块介绍 生命周期事件 模块依赖 自定义模块方法 模块介绍 ABP提供了构建模块并将这些模块组合起来创建应用的基础设施.一个模块可以依赖另一个模块.一般来说,一个程序集可以认为是 ...

  4. ABP官方文档翻译 8&period;1 通知系统

    通知系统 介绍 发送模型 通知类型 通知数据 通知严重性 关于通知持久化 订阅通知 发布通知 用户通知管理 实时通知 客户端 通知存储 通知定义 介绍 在系统中通知用来基于特定的事件告知用户.ABP提 ...

  5. ABP理论学习之事件总线和领域事件

    返回总目录 本篇目录 事件总线 定义事件 触发事件 处理事件 句柄注册 取消注册 在C#中,我们可以在一个类中定义自己的事件,而其他的类可以注册该事件,当某些事情发生时,可以通知到该类.这对于桌面应用 ...

  6. ABP理论学习之Javascript API&lpar;理论完结篇&rpar;

    返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了20 ...

  7. ABP理论学习之SignalR集成

    返回总目录 本篇目录 介绍 安装 建立连接 内置功能 你自己的SignaR代码 介绍 Abp.Web.SignalR 使得在基于ABP的应用程序中使用 SignalR相当容易.查看SignalR文档获 ...

  8. ABP理论学习之发布说明

    返回总目录 查看更详细信息以及下载源代码请查看原文档 ABP v0.9.2.0 | [更新日期:2016/6/6 11:21:28 ] 解决方案转换成xproj/project.json格式. 添加了 ...

  9. ABP理论学习之数据过滤器

    返回总目录 本篇目录 介绍 预定义过滤器 关闭过滤器 开启过滤器 设置过滤器参数 定义自定义过滤器 其他ORM 介绍 软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除 ...

随机推荐

  1. 购物车相关 js

    <div class="caigou"> <form action="" method="post"> <di ...

  2. Power BI for Office 365(七) Power BI站点

    报表分享是Power BI for Office 365比较关键的一个功能,其中提供的Power BI站点将给这些报表一个安全的"归宿",同时也可以跟任何想要分享的人进行协作--- ...

  3. 如何在发布博客时插入复杂公式&mdash&semi;&mdash&semi;Open Live Writer

    1.http://latex.codecogs.com/eqneditor/editor.php 2.使用Word发布

  4. HDU 5615 Jam&&num;39&semi;s math problem

    Jam's math problem Problem Description Jam has a math problem. He just learned factorization.He is t ...

  5. 泛型Dictionary的用法详解

    泛型最常见的用途是泛型集合,命名空间System.Collections.Generic 中包含了一些基于泛型的集合类,使用泛型集合类可以提供更高的类型安全性,还有更高的性能,避免了非泛型集合的重复的 ...

  6. HTML5入门4---HTML5 与 HTML4 同一网页的不同写法

    HTML4写法 css: body { font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana ...

  7. HDU-1012&lpar;水题&rpar;

    http://acm.hdu.edu.cn/showproblem.php?pid=1012 分析:就按题目给的公式一步步输出就行了. #include<stdio.h> #include ...

  8. boost 无锁队列

    一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...

  9. 替换 window&period;location当中的某个参数的值(而其它值不变)JS代码

    在后台与前台的交互操作中,需要替换location当中的某个参数的值(而其它值不变)时,会用到以下函数: 说明: win:传入的窗口句柄,如,window或window.parent等forceAdd ...

  10. Java 反射实现实体转Map时,父类元素丢失

    public class BeanToMap { public static Map<String, Object> ConvertObjToMap(Object obj) { Map&l ...