业务型网站使用过程中,消息通知是一个不可或缺的功能,采用站内通知、短信通知、邮件通知、微信通知等等各种方式都有,ABP框架对这部分工作已经封装的很好了,站在巨人的肩膀上,一览全貌,带来的就是心情舒畅。
ABP官网地址:https://aspnetboilerplate.com/
一、明确概念及设计
一次完整的消息发送/接收过程中,会存在几个必要的点,也正如同现实生活中的场景,有人发送,有人接收,发送的消息本身也有类型。
1、消息类型定义
在ABP中已经提供了消息类型定义的相关类,但是需要我们去实现,在领域层新建一个Notifications文件夹并添加一个定义常用消息类型名称的静态类如AppNotificationNames(名称可随意),在其中开始定义消息类型名称。
/// <summary> /// 设置应用程序中常用通知唯一的名称常量 /// </summary> public static class AppNotificationNames { #region 新的任务 public const string NewTask = "App.NewTask"; #endregion #region 简单消息 public const string SimpleMessage = "App.SimpleMessage"; #endregion }
其次,开始消息类型定义,新建一个NotificationProvider的类,完成消息类型定义,管理消息类型时,可以针对消息设置权限,这样方便不同的角色拥有不同的消息通知。
/// <summary> /// 消息类型定义 /// </summary> public class AppNotificationProvider : NotificationProvider { /// <summary> /// 设置消息类型 /// </summary> /// <param name="context"></param> public override void SetNotifications(INotificationDefinitionContext context) { #region 任务提醒 context.Manager.Add( new NotificationDefinition( AppNotificationNames.NewTask, displayName: L("NewTask"), permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_TaskManage) ) ); #endregion } private static ILocalizableString L(string name) { return new LocalizableString(name, SurroundConsts.LocalizationSourceName); } }
最后在领域层的Module.PreInitialize()中完成加入。
//通知定义 Configuration.Notifications.Providers.Add<AppNotificationProvider>();
2、消息订阅方式
对于消息订阅环节,是存在两种情形的,第一种,发送方发出的消息,可以指定所有订阅了的人员接收,比如,只有拥有任务管理权限的人才会接收到新的任务下发的通知;第二种就是指定到具体人员接收,比如文件上传完毕的消息通知是通知到负责文件上传的人。
消息订阅的管理,在ABP框架中已经封装好了,我们可以通过构造函数注入直接使用,通过获取系统内的提前定义的所有消息类型,我们可以在界面上完成订阅工作,为当前用户订阅在他角色权限内的消息通知及无权限限制的消息通知等。
private readonly INotificationDefinitionManager _notificationDefinitionManager; private readonly INotificationSubscriptionManager _notificationSubscriptionManager; public NotificationAppService( INotificationDefinitionManager notificationDefinitionManager, INotificationSubscriptionManager notificationSubscriptionManager) { _notificationDefinitionManager = notificationDefinitionManager; _notificationSubscriptionManager = notificationSubscriptionManager; }
3、消息发布方式
消息发布时,可以是嵌入在其它操作中,比如制定完成一个新的任务后,发送消息给相关人员,或是在文件上传完毕后,发送给文件上传人员,也可以是定时任务中,指定时间点发送消息,对于这部分来讲,在领域层建立一个消息通知的类,来负责处理消息通知,而具体实现消息通知的过程ABP框架中已经实现了,如同消息订阅一样,只需要在构造函数中注入即可,其次定义具体的消息发送方法,使得具体操作结束后调用具体的消息发送来发送消息。
/// <summary> /// 消息通知发布实例 /// </summary> public class AppNotifier : DomainService, IAppNotifier { #region 初始化 private readonly INotificationPublisher _notificationPublisher; public AppNotifier(INotificationPublisher notificationPublisher) { _notificationPublisher = notificationPublisher; } #endregion #region 消息发送 public async Task SendMessageAsync(UserIdentifier user, string message, NotificationSeverity severity = NotificationSeverity.Info) { await _notificationPublisher.PublishAsync( AppNotificationNames.SimpleMessage, new MessageNotificationData(message), severity: severity, userIds: new[] { user } ); } #endregion #region 任务提醒 public async Task NewTaskAsync(string message, NotificationSeverity severity = NotificationSeverity.Info) { await PublishMessage(AppNotificationNames.NewTask, message); } #endregion #region 辅助方法 private async Task PublishMessage(string appNotificationName, string message) { await _notificationPublisher.PublishAsync(appNotificationName, new MessageNotificationData(message)); } #endregion }
二、完成用户订阅消息
在MVC层,在用户控制器中完成对消息通知设置,消息本身是独立的,只应有了用户才活跃起来,因此把消息的设定挂钩在用户身上,应该算是合理的,并同时完成页面的设计。
/// <summary> /// 通知设置 /// </summary> /// <returns></returns> public async Task<ActionResult> NotificationSetting() { var notificationSettings = await _notificationAppService.GetNotificationSettings(); return View(notificationSettings); } /// <summary> /// 更新通知订阅 /// </summary> /// <returns></returns> [HttpPost] public async Task<JsonResult> UpdateNotificationSetting([FromBody]UpdateNotificationSettingsInput input) { await _notificationAppService.UpdateNotificationSettings(input); return Json(new ResponseParamViewModel("通知设置已更新")); }
页面设计只需要对提前在领域层中定义好的消息类型展示出来即可,用户自主勾选哪些想要接收的消息提示。
三、发布消息通知用户
在指定操作完毕,可以调用消息服务来推送消息到目标接收人员,此处,更改了原先数据字典创建处的代码,当创建数据字典成功后,给与一个消息提示来模拟发布消息通知用户的过程。
var existedDataDictionary = await _dataDictionaryRepository.GetAll().Where(d => d.TypeName == input.DataDictionary.TypeName).FirstOrDefaultAsync(); if (existedDataDictionary != null) { throw new UserFriendlyException(L("该字典类型已存在,无法添加")); } var dataDictionary = ObjectMapper.Map<DataDictionary>(input.DataDictionary); await _dataDictionaryRepository.InsertAsync(dataDictionary); //模拟测试消息通知 await _appNotifier.NewTaskAsync("字典类型已完成添加,可以开始使用了");
该消息触发的前提是,需要订阅该消息,不然有人发,无人收,总是很难为情。因此提前在通知设置中开启类型为“新的任务”的开关。
对于消息发送到了浏览器中呈现,这个过程采用了SignalR,ABP框架也完成了前后端的消息通知,因此可以直接使用即可。
至此,站内消息通知的设计完毕,对于业务需求中可能面对的更为丰富的功能就得花更多时间来完成了。
代码地址:https://gitee.com/530521314/Partner.Surround.git
2019-07-27,望技术有成后能回来看见自己的脚步