45 changed files with 2009 additions and 9 deletions
			
			
		| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,23 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 	 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.IdGenerator\Sanhe.Abp.IdGenerator.csproj" /> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.RealTime\Sanhe.Abp.RealTime.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| 	 | ||||
| </Project> | ||||
| @ -0,0 +1,46 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Sanhe.Abp.IdGenerator; | ||||
| using Sanhe.Abp.RealTime; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp.BackgroundJobs; | ||||
| using Volo.Abp.BackgroundWorkers; | ||||
| using Volo.Abp.Json; | ||||
| using Volo.Abp.Modularity; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| // TODO: 需要重命名 AbpNotificationsModule | ||||
| [DependsOn( | ||||
|     typeof(AbpBackgroundWorkersModule), | ||||
|     typeof(AbpBackgroundJobsAbstractionsModule), | ||||
|     typeof(AbpIdGeneratorModule), | ||||
|     typeof(AbpJsonModule), | ||||
|     typeof(AbpRealTimeModule))] | ||||
| public class AbpNotificationsModule : AbpModule | ||||
| { | ||||
|     public override void PreConfigureServices(ServiceConfigurationContext context) | ||||
|     { | ||||
|         AutoAddDefinitionProviders(context.Services); | ||||
|     } | ||||
| 
 | ||||
|     private void AutoAddDefinitionProviders(IServiceCollection services) | ||||
|     { | ||||
|         var definitionProviders = new List<Type>(); | ||||
| 
 | ||||
|         services.OnRegistred(context => | ||||
|         { | ||||
|             if (typeof(INotificationDefinitionProvider).IsAssignableFrom(context.ImplementationType)) | ||||
|             { | ||||
|                 definitionProviders.Add(context.ImplementationType); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         var preActions = services.GetPreConfigureActions<AbpNotificationsOptions>(); | ||||
|         Configure<AbpNotificationsOptions>(options => | ||||
|         { | ||||
|             preActions.Configure(options); | ||||
|             options.DefinitionProviders.AddIfNotContains(definitionProviders); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,29 @@ | ||||
| using Volo.Abp.Collections; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知选项 | ||||
| /// </summary> | ||||
| public class AbpNotificationsOptions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 自定义通知集合 | ||||
|     /// </summary> | ||||
|     public ITypeList<INotificationDefinitionProvider> DefinitionProviders { get; } | ||||
|     /// <summary> | ||||
|     /// 发布者集合 | ||||
|     /// </summary> | ||||
|     public ITypeList<INotificationPublishProvider> PublishProviders { get; } | ||||
|     /// <summary> | ||||
|     /// 可以自定义某个通知的格式 | ||||
|     /// </summary> | ||||
|     public NotificationDataMappingDictionary NotificationDataMappings { get; } | ||||
| 
 | ||||
|     public AbpNotificationsOptions() | ||||
|     { | ||||
|         PublishProviders = new TypeList<INotificationPublishProvider>(); | ||||
|         DefinitionProviders = new TypeList<INotificationDefinitionProvider>(); | ||||
|         NotificationDataMappings = new NotificationDataMappingDictionary(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| using JetBrains.Annotations; | ||||
| using Volo.Abp.Localization; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知定义上下文 | ||||
| /// </summary> | ||||
| public interface INotificationDefinitionContext | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 添加通知组定义 | ||||
|     /// </summary> | ||||
|     /// <param name="name"></param> | ||||
|     /// <param name="displayName"></param> | ||||
|     /// <param name="allowSubscriptionToClients"></param> | ||||
|     /// <returns></returns> | ||||
|     NotificationGroupDefinition AddGroup( | ||||
|         [NotNull] string name, | ||||
|         ILocalizableString displayName = null, | ||||
|         bool allowSubscriptionToClients = true); | ||||
|     /// <summary> | ||||
|     /// 获取通知组定义 | ||||
|     /// </summary> | ||||
|     /// <param name="name"></param> | ||||
|     /// <returns></returns> | ||||
|     NotificationGroupDefinition GetGroupOrNull(string name); | ||||
|     /// <summary> | ||||
|     /// 移除通知组 | ||||
|     /// </summary> | ||||
|     /// <param name="name"></param> | ||||
|     void RemoveGroup(string name); | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知定义管理者接口 | ||||
| /// </summary> | ||||
| public interface INotificationDefinitionManager | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 获取通知定义 | ||||
|     /// </summary> | ||||
|     /// <param name="name"></param> | ||||
|     /// <returns></returns> | ||||
|     [NotNull] | ||||
|     NotificationDefinition Get([NotNull] string name); | ||||
|     /// <summary> | ||||
|     /// 获取所有通知定义 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     IReadOnlyList<NotificationDefinition> GetAll(); | ||||
|     /// <summary> | ||||
|     /// 获取通知定义,如果为空返回Null | ||||
|     /// </summary> | ||||
|     /// <param name="name"></param> | ||||
|     /// <returns></returns> | ||||
|     NotificationDefinition GetOrNull(string name); | ||||
|     /// <summary> | ||||
|     /// 获取通知定义分组列表 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     IReadOnlyList<NotificationGroupDefinition> GetGroups(); | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| namespace Sanhe.Abp.Notifications | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 通知定义提供者接口 | ||||
|     /// </summary> | ||||
|     public interface INotificationDefinitionProvider | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 定义 | ||||
|         /// </summary> | ||||
|         /// <param name="context">通知定义上下文</param> | ||||
|         void Define(INotificationDefinitionContext context); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知发布提供者接口 | ||||
| /// </summary> | ||||
| public interface INotificationPublishProvider | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 名称 | ||||
|     /// </summary> | ||||
|     string Name { get; } | ||||
|     /// <summary> | ||||
|     /// 发布通知 | ||||
|     /// </summary> | ||||
|     /// <param name="notification">通知信息</param> | ||||
|     /// <param name="identifiers">接收用户列表</param> | ||||
|     /// <returns></returns> | ||||
|     Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers); | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 通知发布提供者管理 | ||||
|     /// </summary> | ||||
|     public interface INotificationPublishProviderManager | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 提供者列表 | ||||
|         /// </summary> | ||||
|         List<INotificationPublishProvider> Providers { get; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,43 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 发送通知接口 | ||||
| /// </summary> | ||||
| public interface INotificationSender | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 发送通知 | ||||
|     /// </summary> | ||||
|     /// <param name="name">名称</param> | ||||
|     /// <param name="data">数据</param> | ||||
|     /// <param name="userId">用户,为空标识发给所有订阅用户</param> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="severity">严重级别</param> | ||||
|     /// <returns>通知标识</returns> | ||||
|     Task<string> SendNofiterAsync( | ||||
|         string name, | ||||
|         NotificationData data, | ||||
|         UserIdentifier user = null, | ||||
|         Guid? tenantId = null, | ||||
|         NotificationSeverity severity = NotificationSeverity.Info); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 发送通知 | ||||
|     /// </summary> | ||||
|     /// <param name="name">名称</param> | ||||
|     /// <param name="data">数据</param> | ||||
|     /// <param name="users">用户列表,为空标识发给所有订阅用户</param> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="severity">严重级别</param> | ||||
|     /// <returns>通知标识</returns> | ||||
|     Task<string> SendNofitersAsync( | ||||
|         string name, | ||||
|         NotificationData data, | ||||
|         IEnumerable<UserIdentifier> users = null, | ||||
|         Guid? tenantId = null, | ||||
|         NotificationSeverity severity = NotificationSeverity.Info); | ||||
| } | ||||
| @ -0,0 +1,127 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知相关储存处 | ||||
| /// </summary> | ||||
| public interface INotificationStore | ||||
| { | ||||
|     Task InsertUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         UserIdentifier identifier, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task InsertUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task DeleteUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task DeleteAllUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task DeleteUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         string notificationName, | ||||
|         IEnumerable<UserIdentifier> identifiers = null, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         string userName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<bool> IsSubscribedAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task InsertNotificationAsync( | ||||
|         NotificationInfo notification, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task DeleteNotificationAsync( | ||||
|         NotificationInfo notification, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task DeleteNotificationAsync( | ||||
|         int batchCount, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task InsertUserNotificationAsync( | ||||
|         NotificationInfo notification, | ||||
|         Guid userId, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task InsertUserNotificationsAsync( | ||||
|         NotificationInfo notification, | ||||
|         IEnumerable<Guid> userIds, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task DeleteUserNotificationAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         long notificationId, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<NotificationInfo> GetNotificationOrNullAsync( | ||||
|         Guid? tenantId, | ||||
|         long notificationId, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<List<NotificationInfo>> GetUserNotificationsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         NotificationReadState? readState = null, | ||||
|         int maxResultCount = 10, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<int> GetUserNotificationsCountAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string filter = "", | ||||
|         NotificationReadState? readState = null, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task<List<NotificationInfo>> GetUserNotificationsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string filter = "", | ||||
|         string sorting = nameof(NotificationInfo.CreationTime), | ||||
|         NotificationReadState? readState = null, | ||||
|         int skipCount = 1, | ||||
|         int maxResultCount = 10, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     Task ChangeUserNotificationReadStateAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         long notificationId, | ||||
|         NotificationReadState readState, | ||||
|         CancellationToken cancellationToken = default); | ||||
| } | ||||
| @ -0,0 +1,123 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知订阅管理器 | ||||
| /// </summary> | ||||
| public interface INotificationSubscriptionManager | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 是否已订阅 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="userId">用户标识</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <returns></returns> | ||||
|     Task<bool> IsSubscribedAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 订阅通知 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="identifier">用户标识</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <returns></returns> | ||||
|     Task SubscribeAsync( | ||||
|         Guid? tenantId, | ||||
|         UserIdentifier identifier, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 订阅通知 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="identifiers">用户标识列表</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <returns></returns> | ||||
|     Task SubscribeAsync( | ||||
|         Guid? tenantId, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 取消所有用户订阅 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <returns></returns> | ||||
|     Task UnsubscribeAllAsync( | ||||
|         Guid? tenantId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 取消订阅 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="identifier">用户标识</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <returns></returns> | ||||
|     Task UnsubscribeAsync( | ||||
|         Guid? tenantId, | ||||
|         UserIdentifier identifier, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 取消订阅 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="identifiers">用户标识列表</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <returns></returns> | ||||
|     Task UnsubscribeAsync( | ||||
|         Guid? tenantId, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 获取通知被订阅用户列表 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="notificationName">通知名称</param> | ||||
|     /// <param name="identifiers">需要检查的用户列表</param> | ||||
|     /// <returns></returns> | ||||
|     Task<List<NotificationSubscriptionInfo>> GetUsersSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         string notificationName, | ||||
|         IEnumerable<UserIdentifier> identifiers = null, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 获取用户订阅列表 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="userId">用户标识</param> | ||||
|     /// <returns></returns> | ||||
|     Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 获取用户订阅列表 | ||||
|     /// </summary> | ||||
|     /// <param name="tenantId">租户</param> | ||||
|     /// <param name="userName">用户名</param> | ||||
|     /// <returns></returns> | ||||
|     Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         string userName, | ||||
|         CancellationToken cancellationToken = default); | ||||
| } | ||||
| @ -0,0 +1,115 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Logging.Abstractions; | ||||
| using Microsoft.Extensions.Options; | ||||
| using Sanhe.Abp.IdGenerator; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| using Volo.Abp.EventBus.Distributed; | ||||
| using Volo.Abp.Uow; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications.Internal | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 默认实现通过分布式事件发送通知 | ||||
|     /// 可替换实现来发送实时通知 | ||||
|     /// </summary> | ||||
|     public class NotificationSender : INotificationSender, ITransientDependency | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Reference to <see cref="ILogger<NotificationSender>"/>. | ||||
|         /// </summary> | ||||
|         public ILogger<NotificationSender> Logger { get; set; } | ||||
|         /// <summary> | ||||
|         /// Reference to <see cref="IDistributedEventBus"/>. | ||||
|         /// </summary> | ||||
|         public IDistributedEventBus DistributedEventBus { get; } | ||||
|         /// <summary> | ||||
|         /// Reference to <see cref="IDistributedIdGenerator"/>. | ||||
|         /// </summary> | ||||
|         protected IDistributedIdGenerator DistributedIdGenerator { get; } | ||||
|         /// <summary> | ||||
|         /// Reference to <see cref="IUnitOfWorkManager"/>. | ||||
|         /// </summary> | ||||
|         protected IUnitOfWorkManager UnitOfWorkManager { get; } | ||||
|         /// <summary> | ||||
|         /// 通知选项 | ||||
|         /// </summary> | ||||
|         protected AbpNotificationsOptions Options { get; } | ||||
| 
 | ||||
|         public NotificationSender( | ||||
|            IDistributedEventBus distributedEventBus, | ||||
|            IDistributedIdGenerator distributedIdGenerator, | ||||
|            IUnitOfWorkManager unitOfWorkManager, | ||||
|            IOptions<AbpNotificationsOptions> optionsAccessor) | ||||
|         { | ||||
|             Options = optionsAccessor.Value; | ||||
|             DistributedEventBus = distributedEventBus; | ||||
|             DistributedIdGenerator = distributedIdGenerator; | ||||
|             UnitOfWorkManager = unitOfWorkManager; | ||||
|             Logger = NullLogger<NotificationSender>.Instance; | ||||
|         } | ||||
| 
 | ||||
|         public async Task<string> SendNofiterAsync( | ||||
|             string name, | ||||
|             NotificationData data, | ||||
|             UserIdentifier user = null, | ||||
|             Guid? tenantId = null, | ||||
|             NotificationSeverity severity = NotificationSeverity.Info) | ||||
|         { | ||||
|             if (user == null) | ||||
|             { | ||||
|                 return await PublishNofiterAsync(name, data, null, tenantId, severity); | ||||
| 
 | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return await PublishNofiterAsync(name, data, new List<UserIdentifier> { user }, tenantId, severity); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async Task<string> SendNofitersAsync( | ||||
|             string name, | ||||
|             NotificationData data, | ||||
|             IEnumerable<UserIdentifier> users = null, | ||||
|             Guid? tenantId = null, | ||||
|             NotificationSeverity severity = NotificationSeverity.Info) | ||||
|         { | ||||
|             return await PublishNofiterAsync(name, data, users, tenantId, severity); | ||||
|         } | ||||
| 
 | ||||
|         protected async Task<string> PublishNofiterAsync( | ||||
|             string name, | ||||
|             NotificationData data, | ||||
|             IEnumerable<UserIdentifier> users = null, | ||||
|             Guid? tenantId = null, | ||||
|             NotificationSeverity severity = NotificationSeverity.Info) | ||||
|         { | ||||
|             var eto = new NotificationEto<NotificationData>(data) | ||||
|             { | ||||
|                 Id = DistributedIdGenerator.Create(), | ||||
|                 TenantId = tenantId, | ||||
|                 Users = users?.ToList(), | ||||
|                 Name = name, | ||||
|                 CreationTime = DateTime.Now, | ||||
|                 Severity = severity | ||||
|             }; | ||||
| 
 | ||||
|             if (UnitOfWorkManager.Current != null) | ||||
|             { | ||||
|                 UnitOfWorkManager.Current.OnCompleted(async () => | ||||
|                 { | ||||
|                     await DistributedEventBus.PublishAsync(eto); | ||||
|                 }); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await DistributedEventBus.PublishAsync(eto); | ||||
|             } | ||||
| 
 | ||||
|             return eto.Id.ToString(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,103 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications.Internal | ||||
| { | ||||
|     internal class NotificationSubscriptionManager : INotificationSubscriptionManager, ITransientDependency | ||||
|     { | ||||
|         private readonly INotificationStore _store; | ||||
| 
 | ||||
|         public NotificationSubscriptionManager(INotificationStore store) | ||||
|         { | ||||
|             _store = store; | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task<List<NotificationSubscriptionInfo>> GetUsersSubscriptionsAsync( | ||||
|             Guid? tenantId, | ||||
|             string notificationName, | ||||
|             IEnumerable<UserIdentifier> identifiers = null, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return await _store.GetUserSubscriptionsAsync(tenantId, notificationName, identifiers, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|             Guid? tenantId, | ||||
|             Guid userId, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return await _store.GetUserSubscriptionsAsync(tenantId, userId, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|             Guid? tenantId, | ||||
|             string userName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return await _store.GetUserSubscriptionsAsync(tenantId, userName, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task<bool> IsSubscribedAsync( | ||||
|             Guid? tenantId, | ||||
|             Guid userId, | ||||
|             string notificationName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return await _store.IsSubscribedAsync(tenantId, userId, notificationName, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task SubscribeAsync( | ||||
|             Guid? tenantId, | ||||
|             UserIdentifier identifier, | ||||
|             string notificationName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             if (await IsSubscribedAsync(tenantId, identifier.UserId, notificationName, cancellationToken)) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             await _store.InsertUserSubscriptionAsync(tenantId, identifier, notificationName, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task SubscribeAsync( | ||||
|             Guid? tenantId, | ||||
|             IEnumerable<UserIdentifier> identifiers, | ||||
|             string notificationName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             foreach (var identifier in identifiers) | ||||
|             { | ||||
|                 await SubscribeAsync(tenantId, identifier, notificationName, cancellationToken); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task UnsubscribeAsync( | ||||
|             Guid? tenantId, | ||||
|             UserIdentifier identifier, | ||||
|             string notificationName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             await _store.DeleteUserSubscriptionAsync(tenantId, identifier.UserId, notificationName, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task UnsubscribeAllAsync( | ||||
|             Guid? tenantId, | ||||
|             string notificationName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             await _store.DeleteAllUserSubscriptionAsync(tenantId, notificationName, cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public async virtual Task UnsubscribeAsync( | ||||
|             Guid? tenantId, | ||||
|             IEnumerable<UserIdentifier> identifiers, | ||||
|             string notificationName, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             await _store.DeleteUserSubscriptionAsync(tenantId, identifiers, notificationName, cancellationToken); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,177 @@ | ||||
| using Sanhe.Abp.RealTime.Localization; | ||||
| using System; | ||||
| using Volo.Abp.Data; | ||||
| using Volo.Abp.EventBus; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知数据 | ||||
| /// </summary> | ||||
| /// <remarks> | ||||
| /// 把通知的标题和内容设计为 <see cref="LocalizableStringInfo"/> 让客户端自行本地化 | ||||
| /// </remarks> | ||||
| [Serializable] | ||||
| [EventName("notifications")] | ||||
| public class NotificationData : IHasExtraProperties | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 用来标识是否需要本地化的信息 | ||||
|     /// </summary> | ||||
|     public const string LocalizerKey = "L"; | ||||
|     /// <summary> | ||||
|     /// <see cref="Type"/>类型完全名称 | ||||
|     /// </summary> | ||||
|     public virtual string Type => GetType().FullName; | ||||
|     /// <summary> | ||||
|     /// 获取或设置扩展属性 | ||||
|     /// </summary> | ||||
|     /// <param name="key"></param> | ||||
|     /// <returns></returns> | ||||
|     public object this[string key] { | ||||
|         get { | ||||
|             return this.GetProperty(key); | ||||
|         } | ||||
|         set { | ||||
|             this.SetProperty(key, value); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 扩展属性 | ||||
|     /// </summary> | ||||
|     public ExtraPropertyDictionary ExtraProperties { get; set; } | ||||
| 
 | ||||
|     public NotificationData() | ||||
|     { | ||||
|         ExtraProperties = new ExtraPropertyDictionary(); | ||||
|         this.SetDefaultsForExtraProperties(); | ||||
| 
 | ||||
|         TrySetData(LocalizerKey, false); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 写入本地化的消息数据 | ||||
|     /// </summary> | ||||
|     /// <param name="title"></param> | ||||
|     /// <param name="message"></param> | ||||
|     /// <param name="createTime"></param> | ||||
|     /// <param name="formUser"></param> | ||||
|     /// <param name="description"></param> | ||||
|     /// <returns></returns> | ||||
|     public NotificationData WriteLocalizedData( | ||||
|         LocalizableStringInfo title, | ||||
|         LocalizableStringInfo message, | ||||
|         DateTime createTime, | ||||
|         string formUser, | ||||
|         LocalizableStringInfo description = null) | ||||
|     { | ||||
|         TrySetData("title", title); | ||||
|         TrySetData("message", message); | ||||
|         TrySetData("formUser", formUser); | ||||
|         TrySetData("createTime", createTime); | ||||
|         TrySetData(LocalizerKey, true); | ||||
| 
 | ||||
|         if (description != null) | ||||
|         { | ||||
|             TrySetData("description", description); | ||||
|         } | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 写入标准数据 | ||||
|     /// </summary> | ||||
|     /// <param name="title">标题</param> | ||||
|     /// <param name="message">内容</param> | ||||
|     /// <param name="createTime">创建时间</param> | ||||
|     /// <param name="formUser">来源用户</param> | ||||
|     /// <param name="description">附加说明</param> | ||||
|     /// <returns></returns> | ||||
|     public NotificationData WriteStandardData(string title, string message, DateTime createTime, string formUser, string description = "") | ||||
|     { | ||||
|         TrySetData("title", title); | ||||
|         TrySetData("message", message); | ||||
|         TrySetData("description", description); | ||||
|         TrySetData("formUser", formUser); | ||||
|         TrySetData("createTime", createTime); | ||||
|         TrySetData(LocalizerKey, false); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 写入标准数据 | ||||
|     /// </summary> | ||||
|     /// <param name="prefix">数据前缀</param> | ||||
|     /// <param name="key">标识</param> | ||||
|     /// <param name="value">数据内容</param> | ||||
|     /// <returns></returns> | ||||
|     public NotificationData WriteStandardData(string prefix, string key, object value) | ||||
|     { | ||||
|         TrySetData(string.Concat(prefix, key), value); | ||||
|         TrySetData(LocalizerKey, false); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 转换为标准数据 | ||||
|     /// </summary> | ||||
|     /// <param name="sourceData">原始数据</param> | ||||
|     /// <returns></returns> | ||||
|     public static NotificationData ToStandardData(NotificationData sourceData) | ||||
|     { | ||||
|         var data = new NotificationData(); | ||||
|         data.TrySetData("title", sourceData.TryGetData("title")); | ||||
|         data.TrySetData("message", sourceData.TryGetData("message")); | ||||
|         data.TrySetData("description", sourceData.TryGetData("description")); | ||||
|         data.TrySetData("formUser", sourceData.TryGetData("formUser")); | ||||
|         data.TrySetData("createTime", sourceData.TryGetData("createTime")); | ||||
|         data.TrySetData(LocalizerKey, sourceData.TryGetData(LocalizerKey)); | ||||
|         return data; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 转换为标准数据 | ||||
|     /// </summary> | ||||
|     /// <param name="prefix">数据前缀</param> | ||||
|     /// <param name="sourceData">原始数据</param> | ||||
|     /// <returns></returns> | ||||
|     public static NotificationData ToStandardData(string prefix, NotificationData sourceData) | ||||
|     { | ||||
|         var data = ToStandardData(sourceData); | ||||
| 
 | ||||
|         foreach (var property in sourceData.ExtraProperties) | ||||
|         { | ||||
|             if (property.Key.StartsWith(prefix)) | ||||
|             { | ||||
|                 var key = property.Key.Replace(prefix, ""); | ||||
|                 data.TrySetData(key, property.Value); | ||||
|             } | ||||
|         } | ||||
|         return data; | ||||
|     } | ||||
| 
 | ||||
|     public object TryGetData(string key) | ||||
|     { | ||||
|         return this.GetProperty(key); | ||||
|     } | ||||
| 
 | ||||
|     public void TrySetData(string key, object value) | ||||
|     { | ||||
|         this.SetProperty(key, value); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 需要本地化 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public bool NeedLocalizer() | ||||
|     { | ||||
|         var localizer = TryGetData(LocalizerKey); | ||||
|         if (localizer != null && localizer is bool needLocalizer) | ||||
|         { | ||||
|             return needLocalizer; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| using Sanhe.Abp.RealTime.Localization; | ||||
| using Newtonsoft.Json; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| public class NotificationDataConverter | ||||
| { | ||||
|     public static NotificationData Convert(NotificationData notificationData) | ||||
|     { | ||||
|         if (notificationData != null) | ||||
|         { | ||||
|             if (notificationData.NeedLocalizer()) | ||||
|             { | ||||
|                 // 潜在的空对象引用修复 | ||||
|                 if (notificationData.ExtraProperties.TryGetValue("title", out var title) && title != null) | ||||
|                 { | ||||
|                     var titleObj = JsonConvert.DeserializeObject<LocalizableStringInfo>(title.ToString()); | ||||
|                     notificationData.TrySetData("title", titleObj); | ||||
|                 } | ||||
|                 if (notificationData.ExtraProperties.TryGetValue("message", out var message) && message != null) | ||||
|                 { | ||||
|                     var messageObj = JsonConvert.DeserializeObject<LocalizableStringInfo>(message.ToString()); | ||||
|                     notificationData.TrySetData("message", messageObj); | ||||
|                 } | ||||
| 
 | ||||
|                 if (notificationData.ExtraProperties.TryGetValue("description", out var description) && description != null) | ||||
|                 { | ||||
|                     notificationData.TrySetData("description", JsonConvert.DeserializeObject<LocalizableStringInfo>(description.ToString())); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             notificationData = new NotificationData(); | ||||
|         } | ||||
|         return notificationData; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,63 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications | ||||
| { | ||||
|     public class NotificationDataMappingDictionary : Dictionary<string, List<NotificationDataMappingDictionaryItem>> | ||||
|     { | ||||
|         public static string DefaultKey { get; set; } = "Default"; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 处理某个通知的数据 | ||||
|         /// 特定于一个提供程序 | ||||
|         /// </summary> | ||||
|         /// <param name="provider"></param> | ||||
|         /// <param name="name"></param> | ||||
|         /// <param name="func"></param> | ||||
|         public void Mapping(string provider, string name, Func<NotificationData, NotificationData> func) | ||||
|         { | ||||
|             if (!ContainsKey(provider)) | ||||
|             { | ||||
|                 this[provider] = new List<NotificationDataMappingDictionaryItem>(); | ||||
|             } | ||||
| 
 | ||||
|             var mapItem = this[provider].FirstOrDefault(item => item.Name.Equals(name)); | ||||
| 
 | ||||
|             if (mapItem == null) | ||||
|             { | ||||
|                 this[provider].Add(new NotificationDataMappingDictionaryItem(name, func)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 mapItem.Replace(func); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 处理所有通知的数据 | ||||
|         /// 特定于一个提供程序 | ||||
|         /// </summary> | ||||
|         /// <param name="provider"></param> | ||||
|         /// <param name="func"></param> | ||||
|         public void MappingDefault(string provider, Func<NotificationData, NotificationData> func) | ||||
|         { | ||||
|             Mapping(provider, DefaultKey, func); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 获取需要处理数据的方法 | ||||
|         /// </summary> | ||||
|         /// <param name="name"></param> | ||||
|         /// <param name="provider"></param> | ||||
|         /// <returns></returns> | ||||
|         public NotificationDataMappingDictionaryItem GetMapItemOrDefault(string provider, string name) | ||||
|         { | ||||
|             if (ContainsKey(provider)) | ||||
|             { | ||||
|                 return this[provider].GetOrNullDefault(name); | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| public class NotificationDataMappingDictionaryItem | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 通知名称 | ||||
|     /// </summary> | ||||
|     public string Name { get; } | ||||
|     /// <summary> | ||||
|     /// 转换方法 | ||||
|     /// </summary> | ||||
|     public Func<NotificationData, NotificationData> MappingFunc { get; private set; } | ||||
| 
 | ||||
|     public NotificationDataMappingDictionaryItem(string name, Func<NotificationData, NotificationData> func) | ||||
|     { | ||||
|         Name = name; | ||||
|         MappingFunc = func; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 替换 | ||||
|     /// </summary> | ||||
|     /// <param name="func"></param> | ||||
|     public void Replace(Func<NotificationData, NotificationData> func) | ||||
|     { | ||||
|         MappingFunc = func; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications | ||||
| { | ||||
|     public static class NotificationDataMappingDictionaryItemExtensions | ||||
|     { | ||||
|         public static NotificationDataMappingDictionaryItem GetOrNullDefault( | ||||
|             this IEnumerable<NotificationDataMappingDictionaryItem> items, | ||||
|             string name) | ||||
|         { | ||||
|             var item = items.FirstOrDefault(i => i.Name.Equals(name)); | ||||
|             if (item == null) | ||||
|             { | ||||
|                 return items.FirstOrDefault(i => i.Name.Equals(NotificationDataMappingDictionary.DefaultKey)); | ||||
|             } | ||||
|             return item; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,94 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Localization; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知定义 | ||||
| /// </summary> | ||||
| public class NotificationDefinition | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 通知名称 | ||||
|     /// </summary> | ||||
|     [NotNull] | ||||
|     public string Name { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知显示名称 | ||||
|     /// </summary> | ||||
|     [NotNull] | ||||
|     public ILocalizableString DisplayName { | ||||
|         get => _displayName; | ||||
|         set => _displayName = Check.NotNull(value, nameof(value)); | ||||
|     } | ||||
|     private ILocalizableString _displayName; | ||||
|     /// <summary> | ||||
|     /// 通知说明 | ||||
|     /// </summary> | ||||
|     [CanBeNull] | ||||
|     public ILocalizableString Description { get; set; } | ||||
|     /// <summary> | ||||
|     /// 允许客户端显示订阅 | ||||
|     /// </summary> | ||||
|     public bool AllowSubscriptionToClients { get; set; } | ||||
|     /// <summary> | ||||
|     /// 存活类型 | ||||
|     /// </summary> | ||||
|     public NotificationLifetime NotificationLifetime { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知类型 | ||||
|     /// </summary> | ||||
|     public NotificationType NotificationType { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知提供者 | ||||
|     /// </summary> | ||||
|     public List<string> Providers { get; } | ||||
|     /// <summary> | ||||
|     /// 额外属性 | ||||
|     /// </summary> | ||||
|     [NotNull] | ||||
|     public Dictionary<string, object> Properties { get; } | ||||
| 
 | ||||
|     public NotificationDefinition( | ||||
|        string name, | ||||
|        ILocalizableString displayName = null, | ||||
|        ILocalizableString description = null, | ||||
|        NotificationType notificationType = NotificationType.Application, | ||||
|        NotificationLifetime lifetime = NotificationLifetime.Persistent, | ||||
|        bool allowSubscriptionToClients = false) | ||||
|     { | ||||
|         Name = name; | ||||
|         DisplayName = displayName ?? new FixedLocalizableString(name); | ||||
|         Description = description; | ||||
|         NotificationLifetime = lifetime; | ||||
|         NotificationType = notificationType; | ||||
|         AllowSubscriptionToClients = allowSubscriptionToClients; | ||||
| 
 | ||||
|         Providers = new List<string>(); | ||||
|         Properties = new Dictionary<string, object>(); | ||||
|     } | ||||
| 
 | ||||
|     public virtual NotificationDefinition WithProviders(params string[] providers) | ||||
|     { | ||||
|         if (!providers.IsNullOrEmpty()) | ||||
|         { | ||||
|             Providers.AddRange(providers); | ||||
|         } | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public virtual NotificationDefinition WithProperty(string key, object value) | ||||
|     { | ||||
|         Properties[key] = value; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return $"[{nameof(NotificationDefinition)} {Name}]"; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Localization; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| public class NotificationDefinitionContext : INotificationDefinitionContext | ||||
| { | ||||
|     internal Dictionary<string, NotificationGroupDefinition> Groups { get; } | ||||
| 
 | ||||
|     public NotificationDefinitionContext() | ||||
|     { | ||||
|         Groups = new Dictionary<string, NotificationGroupDefinition>(); | ||||
|     } | ||||
| 
 | ||||
|     public NotificationGroupDefinition AddGroup( | ||||
|         [NotNull] string name,  | ||||
|         ILocalizableString displayName = null, | ||||
|         bool allowSubscriptionToClients = true) | ||||
|     { | ||||
|         Check.NotNull(name, nameof(name)); | ||||
| 
 | ||||
|         if (Groups.ContainsKey(name)) | ||||
|         { | ||||
|             throw new AbpException($"There is already an existing notification group with name: {name}"); | ||||
|         } | ||||
| 
 | ||||
|         return Groups[name] = new NotificationGroupDefinition(name, displayName, allowSubscriptionToClients); | ||||
|     } | ||||
| 
 | ||||
|     public NotificationGroupDefinition GetGroupOrNull(string name) | ||||
|     { | ||||
|         Check.NotNull(name, nameof(name)); | ||||
| 
 | ||||
|         if (!Groups.ContainsKey(name)) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         return Groups[name]; | ||||
|     } | ||||
| 
 | ||||
|     public void RemoveGroup(string name) | ||||
|     { | ||||
|         Check.NotNull(name, nameof(name)); | ||||
| 
 | ||||
|         if (!Groups.ContainsKey(name)) | ||||
|         { | ||||
|             throw new AbpException($"Undefined notification group: '{name}'."); | ||||
|         } | ||||
| 
 | ||||
|         Groups.Remove(name); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,113 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Options; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知定义管理者接口 | ||||
| /// </summary> | ||||
| public class NotificationDefinitionManager : INotificationDefinitionManager, ISingletonDependency | ||||
| { | ||||
|     protected AbpNotificationsOptions Options { get; } | ||||
| 
 | ||||
|     protected IDictionary<string, NotificationGroupDefinition> NotificationGroupDefinitions => _lazyNotificationGroupDefinitions.Value; | ||||
|     private readonly Lazy<Dictionary<string, NotificationGroupDefinition>> _lazyNotificationGroupDefinitions; | ||||
| 
 | ||||
|     protected IDictionary<string, NotificationDefinition> NotificationDefinitions => _lazyNotificationDefinitions.Value; | ||||
|     private readonly Lazy<Dictionary<string, NotificationDefinition>> _lazyNotificationDefinitions; | ||||
| 
 | ||||
|     private readonly IServiceScopeFactory _serviceScopeFactory; | ||||
| 
 | ||||
|     public NotificationDefinitionManager( | ||||
|         IOptions<AbpNotificationsOptions> optionsAccessor, | ||||
|         IServiceScopeFactory serviceScopeFactory) | ||||
|     { | ||||
|         _serviceScopeFactory = serviceScopeFactory; | ||||
|         Options = optionsAccessor.Value; | ||||
| 
 | ||||
|         _lazyNotificationDefinitions = new Lazy<Dictionary<string, NotificationDefinition>>( | ||||
|                 CreateNotificationDefinitions, | ||||
|                 isThreadSafe: true | ||||
|             ); | ||||
| 
 | ||||
|         _lazyNotificationGroupDefinitions = new Lazy<Dictionary<string, NotificationGroupDefinition>>( | ||||
|             CreateNotificationGroupDefinitions, | ||||
|             isThreadSafe: true | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public virtual NotificationDefinition Get(string name) | ||||
|     { | ||||
|         Check.NotNull(name, nameof(name)); | ||||
| 
 | ||||
|         var feature = GetOrNull(name); | ||||
| 
 | ||||
|         if (feature == null) | ||||
|         { | ||||
|             throw new AbpException("Undefined notification: " + name); | ||||
|         } | ||||
| 
 | ||||
|         return feature; | ||||
|     } | ||||
| 
 | ||||
|     public virtual IReadOnlyList<NotificationDefinition> GetAll() | ||||
|     { | ||||
|         return NotificationDefinitions.Values.ToImmutableList(); | ||||
|     } | ||||
| 
 | ||||
|     public virtual NotificationDefinition GetOrNull(string name) | ||||
|     { | ||||
|         return NotificationDefinitions.GetOrDefault(name); | ||||
|     } | ||||
| 
 | ||||
|     public IReadOnlyList<NotificationGroupDefinition> GetGroups() | ||||
|     { | ||||
|         return NotificationGroupDefinitions.Values.ToImmutableList(); | ||||
|     } | ||||
| 
 | ||||
|     protected virtual Dictionary<string, NotificationDefinition> CreateNotificationDefinitions() | ||||
|     { | ||||
|         var notifications = new Dictionary<string, NotificationDefinition>(); | ||||
| 
 | ||||
|         foreach (var groupDefinition in NotificationGroupDefinitions.Values) | ||||
|         { | ||||
|             foreach (var notification in groupDefinition.Notifications) | ||||
|             { | ||||
|                 if (notifications.ContainsKey(notification.Name)) | ||||
|                 { | ||||
|                     throw new AbpException("Duplicate notification name: " + notification.Name); | ||||
|                 } | ||||
| 
 | ||||
|                 notifications[notification.Name] = notification; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return notifications; | ||||
|     } | ||||
| 
 | ||||
|     protected virtual Dictionary<string, NotificationGroupDefinition> CreateNotificationGroupDefinitions() | ||||
|     { | ||||
|         var context = new NotificationDefinitionContext(); | ||||
| 
 | ||||
|         using (var scope = _serviceScopeFactory.CreateScope()) | ||||
|         { | ||||
|             var providers = Options | ||||
|                 .DefinitionProviders | ||||
|                 .Select(p => scope.ServiceProvider.GetRequiredService(p) as INotificationDefinitionProvider) | ||||
|                 .ToList(); | ||||
| 
 | ||||
|             foreach (var provider in providers) | ||||
|             { | ||||
|                 provider.Define(context); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return context.Groups; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知定义提供者抽象类 | ||||
| /// </summary> | ||||
| public abstract class NotificationDefinitionProvider : INotificationDefinitionProvider, ITransientDependency | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 定义 | ||||
|     /// </summary> | ||||
|     /// <param name="context"></param> | ||||
|     public abstract void Define(INotificationDefinitionContext context); | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| using Sanhe.Abp.RealTime; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp.EventBus; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知事件传输对象 | ||||
| /// </summary> | ||||
| /// <typeparam name="T"></typeparam> | ||||
| [Serializable] | ||||
| [GenericEventName(Prefix = "abp.realtime.")] | ||||
| public class NotificationEto<T> : RealTimeEto<T>, IMultiTenant | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 通知标识 | ||||
|     /// 自动计算 | ||||
|     /// </summary> | ||||
|     public long Id { get; set; } | ||||
|     /// <summary> | ||||
|     /// 租户 | ||||
|     /// </summary> | ||||
|     public Guid? TenantId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知名称 | ||||
|     /// </summary> | ||||
|     public string Name { get; set; } | ||||
|     /// <summary> | ||||
|     /// 创建时间 | ||||
|     /// </summary> | ||||
|     public DateTime CreationTime { get; set; } | ||||
|     /// <summary> | ||||
|     /// 紧急级别 | ||||
|     /// </summary> | ||||
|     public NotificationSeverity Severity { get; set; } | ||||
|     /// <summary> | ||||
|     /// 指定的接收用户信息集合 | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// 注:<br/> | ||||
|     /// 如果指定了用户列表,应该在事件订阅程序中通过此集合过滤订阅用户<br/> | ||||
|     /// 如果未指定用户列表,应该在事件订阅程序中过滤所有订阅此通知的用户 | ||||
|     /// </remarks> | ||||
|     public List<UserIdentifier> Users { get; set; } = new List<UserIdentifier>(); | ||||
| 
 | ||||
|     public NotificationEto() : base() | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public NotificationEto(T data) : base(data) | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,84 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Localization; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知组定义 | ||||
| /// </summary> | ||||
| public class NotificationGroupDefinition | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 通知组名称 | ||||
|     /// </summary> | ||||
|     [NotNull] | ||||
|     public string Name { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知组显示名称 | ||||
|     /// </summary> | ||||
|     [NotNull] | ||||
|     public ILocalizableString DisplayName { | ||||
|         get => _displayName; | ||||
|         set => _displayName = Check.NotNull(value, nameof(value)); | ||||
|     } | ||||
|     private ILocalizableString _displayName; | ||||
|     /// <summary> | ||||
|     /// 通知组说明 | ||||
|     /// </summary> | ||||
|     [CanBeNull] | ||||
|     public ILocalizableString Description { get; set; } | ||||
|     /// <summary> | ||||
|     /// 允许客户端显示订阅 | ||||
|     /// </summary> | ||||
|     public bool AllowSubscriptionToClients { get; set; } | ||||
| 
 | ||||
|     public IReadOnlyList<NotificationDefinition> Notifications => _notifications.ToImmutableList(); | ||||
|     private readonly List<NotificationDefinition> _notifications; | ||||
| 
 | ||||
|     protected internal NotificationGroupDefinition( | ||||
|         string name, | ||||
|         ILocalizableString displayName = null, | ||||
|         bool allowSubscriptionToClients = false) | ||||
|     { | ||||
|         Name = name; | ||||
|         DisplayName = displayName ?? new FixedLocalizableString(Name); | ||||
|         AllowSubscriptionToClients = allowSubscriptionToClients; | ||||
| 
 | ||||
|         _notifications = new List<NotificationDefinition>(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 添加通知 | ||||
|     /// </summary> | ||||
|     /// <param name="name">通知组名称</param> | ||||
|     /// <param name="displayName">通知组显示名称</param> | ||||
|     /// <param name="description">通知组说明</param> | ||||
|     /// <param name="notificationType">通知类型</param> | ||||
|     /// <param name="lifetime">通知存活时间</param> | ||||
|     /// <param name="allowSubscriptionToClients">允许客户端显示订阅</param> | ||||
|     /// <returns></returns> | ||||
|     public virtual NotificationDefinition AddNotification( | ||||
|        string name, | ||||
|        ILocalizableString displayName = null, | ||||
|        ILocalizableString description = null, | ||||
|        NotificationType notificationType = NotificationType.Application, | ||||
|        NotificationLifetime lifetime = NotificationLifetime.Persistent, | ||||
|        bool allowSubscriptionToClients = false) | ||||
|     { | ||||
|         var notification = new NotificationDefinition( | ||||
|             name, | ||||
|             displayName, | ||||
|             description, | ||||
|             notificationType, | ||||
|             lifetime, | ||||
|             allowSubscriptionToClients | ||||
|         ); | ||||
| 
 | ||||
|         _notifications.Add(notification); | ||||
| 
 | ||||
|         return notification; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知信息 | ||||
| /// </summary> | ||||
| public class NotificationInfo | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 租户Id | ||||
|     /// </summary> | ||||
|     public Guid? TenantId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 名称 | ||||
|     /// </summary> | ||||
|     public string Name { get; set; } | ||||
|     /// <summary> | ||||
|     /// Id | ||||
|     /// </summary> | ||||
|     public string Id { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知数据 | ||||
|     /// </summary> | ||||
|     public NotificationData Data { get; set; } | ||||
|     /// <summary> | ||||
|     /// 创建时间 | ||||
|     /// </summary> | ||||
|     public DateTime CreationTime { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知存活时间 | ||||
|     /// </summary> | ||||
|     public NotificationLifetime Lifetime { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知类型 | ||||
|     /// </summary> | ||||
|     public NotificationType Type { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知严重级别 | ||||
|     /// </summary> | ||||
|     public NotificationSeverity Severity { get; set; } | ||||
| 
 | ||||
|     public NotificationInfo() | ||||
|     { | ||||
|         Data = new NotificationData(); | ||||
|         Lifetime = NotificationLifetime.Persistent; | ||||
|         Type = NotificationType.Application; | ||||
|         Severity = NotificationSeverity.Info; | ||||
| 
 | ||||
|         CreationTime = DateTime.Now; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 设置Id | ||||
|     /// </summary> | ||||
|     /// <param name="id"></param> | ||||
|     public void SetId(long id) | ||||
|     { | ||||
|         if (Id.IsNullOrWhiteSpace()) | ||||
|         { | ||||
|             Id = id.ToString(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 获取Id | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public long GetId() | ||||
|     { | ||||
|         return long.Parse(Id); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知存活时间 | ||||
| /// 发送之后取消用户订阅,类似于微信小程序 | ||||
| /// </summary> | ||||
| public enum NotificationLifetime | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 持久化 | ||||
|     /// </summary> | ||||
|     Persistent = 0, | ||||
|     /// <summary> | ||||
|     /// 一次性 | ||||
|     /// </summary> | ||||
|     OnlyOne = 1 | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 内置通知提供者 | ||||
| /// </summary> | ||||
| public static class NotificationProviderNames | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SignalR 实时通知 | ||||
|     /// </summary> | ||||
|     public const string SignalR = "SignalR"; | ||||
|     /// <summary> | ||||
|     /// 短信通知 | ||||
|     /// </summary> | ||||
|     public const string Sms = "Sms"; | ||||
|     /// <summary> | ||||
|     /// 邮件通知 | ||||
|     /// </summary> | ||||
|     public const string Emailing = "Emailing"; | ||||
|     /// <summary> | ||||
|     /// 微信小程序模板通知 | ||||
|     /// </summary> | ||||
|     public const string WechatMiniProgram = "WeChat.MiniProgram"; | ||||
| } | ||||
| @ -0,0 +1,68 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Logging.Abstractions; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| using Volo.Abp.Threading; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知发布提供者抽象类 | ||||
| /// </summary> | ||||
| public abstract class NotificationPublishProvider : INotificationPublishProvider, ITransientDependency | ||||
| { | ||||
|     public abstract string Name { get; } | ||||
| 
 | ||||
|     protected IServiceProvider ServiceProvider { get; } | ||||
| 
 | ||||
|     protected readonly object ServiceProviderLock = new(); | ||||
| 
 | ||||
|     public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); | ||||
|     private ILoggerFactory _loggerFactory; | ||||
| 
 | ||||
|     protected ILogger Logger => LazyLogger.Value; | ||||
|     private Lazy<ILogger> LazyLogger => new(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); | ||||
| 
 | ||||
| 
 | ||||
|     protected TService LazyGetRequiredService<TService>(ref TService reference) | ||||
|     { | ||||
|         if (reference == null) | ||||
|         { | ||||
|             lock (ServiceProviderLock) | ||||
|             { | ||||
|                 if (reference == null) | ||||
|                 { | ||||
|                     reference = ServiceProvider.GetRequiredService<TService>(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return reference; | ||||
|     } | ||||
| 
 | ||||
|     public ICancellationTokenProvider CancellationTokenProvider { get; set; } | ||||
| 
 | ||||
|     protected NotificationPublishProvider(IServiceProvider serviceProvider) | ||||
|     { | ||||
|         ServiceProvider = serviceProvider; | ||||
|         CancellationTokenProvider = NullCancellationTokenProvider.Instance; | ||||
|     } | ||||
| 
 | ||||
|     public async Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers) | ||||
|     { | ||||
|         await PublishAsync(notification, identifiers, CancellationTokenProvider.Token); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 重写实现通知发布 | ||||
|     /// </summary> | ||||
|     /// <param name="notification"></param> | ||||
|     /// <param name="identifiers"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     protected abstract Task PublishAsync(NotificationInfo notification, IEnumerable<UserIdentifier> identifiers, CancellationToken cancellationToken = default); | ||||
| } | ||||
| @ -0,0 +1,32 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Options; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| public class NotificationPublishProviderManager : INotificationPublishProviderManager, ISingletonDependency | ||||
| { | ||||
|     public List<INotificationPublishProvider> Providers => _lazyProviders.Value; | ||||
| 
 | ||||
|     protected AbpNotificationsOptions Options { get; } | ||||
| 
 | ||||
|     private readonly Lazy<List<INotificationPublishProvider>> _lazyProviders; | ||||
| 
 | ||||
|     public NotificationPublishProviderManager( | ||||
|         IServiceProvider serviceProvider, | ||||
|         IOptions<AbpNotificationsOptions> optionsAccessor) | ||||
|     { | ||||
|         Options = optionsAccessor.Value; | ||||
| 
 | ||||
|         _lazyProviders = new Lazy<List<INotificationPublishProvider>>( | ||||
|             () => Options | ||||
|                 .PublishProviders | ||||
|                 .Select(type => serviceProvider.GetRequiredService(type) as INotificationPublishProvider) | ||||
|                 .ToList(), | ||||
|             true | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 读取状态 | ||||
| /// </summary> | ||||
| public enum NotificationReadState | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 已读 | ||||
|     /// </summary> | ||||
|     Read = 0, | ||||
|     /// <summary> | ||||
|     /// 未读 | ||||
|     /// </summary> | ||||
|     UnRead = 1 | ||||
| } | ||||
| @ -0,0 +1,28 @@ | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 严重级别 | ||||
| /// </summary> | ||||
| public enum NotificationSeverity : sbyte | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 成功 | ||||
|     /// </summary> | ||||
|     Success = 0, | ||||
|     /// <summary> | ||||
|     /// 信息 | ||||
|     /// </summary> | ||||
|     Info = 10, | ||||
|     /// <summary> | ||||
|     /// 警告 | ||||
|     /// </summary> | ||||
|     Warn = 20, | ||||
|     /// <summary> | ||||
|     /// 错误 | ||||
|     /// </summary> | ||||
|     Error = 30, | ||||
|     /// <summary> | ||||
|     /// 致命错误 | ||||
|     /// </summary> | ||||
|     Fatal = 40 | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知订阅信息 | ||||
| /// </summary> | ||||
| public class NotificationSubscriptionInfo | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 租户Id | ||||
|     /// </summary> | ||||
|     public Guid? TenantId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 用户Id | ||||
|     /// </summary> | ||||
|     public Guid UserId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 用户名 | ||||
|     /// </summary> | ||||
|     public string UserName { get; set; } | ||||
|     /// <summary> | ||||
|     /// 通知名 | ||||
|     /// </summary> | ||||
|     public string NotificationName { get; set; } | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 通知类型 | ||||
| /// </summary> | ||||
| public enum NotificationType | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 应用(对应租户) | ||||
|     /// </summary> | ||||
|     Application = 0, | ||||
|     /// <summary> | ||||
|     /// 系统(对应宿主) | ||||
|     /// </summary> | ||||
|     System = 10, | ||||
|     /// <summary> | ||||
|     /// 用户(对应用户) | ||||
|     /// </summary> | ||||
|     User = 20 | ||||
| } | ||||
| @ -0,0 +1,189 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 空实现通知相关储存处 | ||||
| /// </summary> | ||||
| [Dependency(TryRegister = true)] | ||||
| public class NullNotificationStore : INotificationStore, ISingletonDependency | ||||
| { | ||||
|     public Task ChangeUserNotificationReadStateAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         long notificationId, | ||||
|         NotificationReadState readState, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task DeleteAllUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task DeleteNotificationAsync( | ||||
|         NotificationInfo notification, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task DeleteNotificationAsync( | ||||
|         int batchCount, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task DeleteUserNotificationAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         long notificationId, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task DeleteUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task DeleteUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task<NotificationInfo> GetNotificationOrNullAsync( | ||||
|         Guid? tenantId, | ||||
|         long notificationId, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(new NotificationInfo()); | ||||
|     } | ||||
| 
 | ||||
|     public Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         string notificationName, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(new List<NotificationSubscriptionInfo>()); | ||||
|     } | ||||
| 
 | ||||
|     public Task<List<NotificationInfo>> GetUserNotificationsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         NotificationReadState? readState = null, | ||||
|         int maxResultCount = 10, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(new List<NotificationInfo>()); | ||||
|     } | ||||
| 
 | ||||
|     public Task<int> GetUserNotificationsCountAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string filter = "", | ||||
|         NotificationReadState? readState = null, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(0); | ||||
|     } | ||||
| 
 | ||||
|     public Task<List<NotificationInfo>> GetUserNotificationsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string filter = "", | ||||
|         string sorting = nameof(NotificationInfo.CreationTime), | ||||
|         NotificationReadState? readState = null, | ||||
|         int skipCount = 1, | ||||
|         int maxResultCount = 10, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(new List<NotificationInfo>()); | ||||
|     } | ||||
| 
 | ||||
|     public Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(new List<NotificationSubscriptionInfo>()); | ||||
|     } | ||||
| 
 | ||||
|     public Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync( | ||||
|         Guid? tenantId, | ||||
|         string userName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(new List<NotificationSubscriptionInfo>()); | ||||
|     } | ||||
| 
 | ||||
|     public Task InsertNotificationAsync( | ||||
|         NotificationInfo notification, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task InsertUserNotificationAsync( | ||||
|         NotificationInfo notification, | ||||
|         Guid userId, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task InsertUserNotificationsAsync( | ||||
|         NotificationInfo notification, | ||||
|         IEnumerable<Guid> userIds, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task InsertUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         UserIdentifier identifier, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task InsertUserSubscriptionAsync( | ||||
|         Guid? tenantId, | ||||
|         IEnumerable<UserIdentifier> identifiers, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public Task<bool> IsSubscribedAsync( | ||||
|         Guid? tenantId, | ||||
|         Guid userId, | ||||
|         string notificationName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(false); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Notifications; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 用户信息 | ||||
| /// </summary> | ||||
| public class UserIdentifier | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 用户标识 | ||||
|     /// </summary> | ||||
|     public Guid UserId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 用户名 | ||||
|     /// </summary> | ||||
|     public string UserName { get; set; } | ||||
| 
 | ||||
|     public UserIdentifier(Guid userId, string userName) | ||||
|     { | ||||
|         UserId = userId; | ||||
|         UserName = userName; | ||||
|     } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue