Browse Source

add notification

master
wwwk 3 years ago
parent
commit
c068006083
  1. 7
      Sanhe.Abp.Framework.sln
  2. 3
      modules/common/Sanhe.Abp.IdGenerator/Sanhe.Abp.IdGenerator.csproj
  3. 4
      modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/AbpIdGeneratorModule.cs
  4. 2
      modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/IDistributedIdGenerator.cs
  5. 2
      modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdGenerator.cs
  6. 2
      modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdOptions.cs
  7. 3
      modules/common/Sanhe.Abp.Notifications/FodyWeavers.xml
  8. 23
      modules/common/Sanhe.Abp.Notifications/Sanhe.Abp.Notifications.csproj
  9. 46
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/AbpNotificationsModule.cs
  10. 29
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/AbpNotificationsOptions.cs
  11. 33
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationDefinitionContext.cs
  12. 34
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationDefinitionManager.cs
  13. 14
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationDefinitionProvider.cs
  14. 22
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationPublishProvider.cs
  15. 15
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationPublishProviderManager.cs
  16. 43
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationSender.cs
  17. 127
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationStore.cs
  18. 123
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationSubscriptionManager.cs
  19. 115
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/Internal/NotificationSender.cs
  20. 103
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/Internal/NotificationSubscriptionManager.cs
  21. 177
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationData.cs
  22. 38
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataConverter.cs
  23. 63
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataMappingDictionary.cs
  24. 30
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataMappingDictionaryItem.cs
  25. 20
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataMappingDictionaryItemExtensions.cs
  26. 94
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinition.cs
  27. 55
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinitionContext.cs
  28. 113
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinitionManager.cs
  29. 15
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinitionProvider.cs
  30. 55
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationEto.cs
  31. 84
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationGroupDefinition.cs
  32. 73
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationInfo.cs
  33. 17
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationLifetime.cs
  34. 24
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationProviderNames.cs
  35. 68
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationPublishProvider.cs
  36. 32
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationPublishProviderManager.cs
  37. 16
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationReadState.cs
  38. 28
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationSeverity.cs
  39. 26
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationSubscriptionInfo.cs
  40. 20
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationType.cs
  41. 189
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NullNotificationStore.cs
  42. 24
      modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/UserIdentifier.cs
  43. 3
      modules/common/Sanhe.Abp.RealTime/Sanhe.Abp.RealTime.csproj
  44. 2
      modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/AbpRealTimeModule.cs
  45. 2
      modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/RealTimeEto.cs

7
Sanhe.Abp.Framework.sln

@ -67,6 +67,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.RealTime", "modul
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.IdGenerator", "modules\common\Sanhe.Abp.IdGenerator\Sanhe.Abp.IdGenerator.csproj", "{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Notifications", "modules\common\Sanhe.Abp.Notifications\Sanhe.Abp.Notifications.csproj", "{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -157,6 +159,10 @@ Global
{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Release|Any CPU.Build.0 = Release|Any CPU
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -188,6 +194,7 @@ Global
{EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF} = {2A768109-31B7-4C52-928C-3023DAB9F254}
{C99CF772-210B-4C21-84FE-089B7D038A28} = {2A768109-31B7-4C52-928C-3023DAB9F254}
{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC} = {2A768109-31B7-4C52-928C-3023DAB9F254}
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983} = {2A768109-31B7-4C52-928C-3023DAB9F254}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AB69BFDE-9DDB-4D16-8CB8-72472C0319CD}

3
modules/common/Sanhe.Abp.IdGenerator/Sanhe.Abp.IdGenerator.csproj

@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>

4
modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/AbpIdGeneratorModule.cs

@ -1,9 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator.Snowflake;
using Sanhe.Abp.IdGenerator.Snowflake;
using Volo.Abp.Modularity;
namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator;
namespace Sanhe.Abp.IdGenerator;
public class AbpIdGeneratorModule: AbpModule
{

2
modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/IDistributedIdGenerator.cs

@ -1,4 +1,4 @@
namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator;
namespace Sanhe.Abp.IdGenerator;
public interface IDistributedIdGenerator
{

2
modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdGenerator.cs

@ -1,7 +1,7 @@
using System;
using Volo.Abp;
namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator.Snowflake;
namespace Sanhe.Abp.IdGenerator.Snowflake;
// reference: https://github.com/dotnetcore/CAP
// reference: https://blog.csdn.net/lq18050010830/article/details/89845790

2
modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdOptions.cs

@ -1,4 +1,4 @@
namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator.Snowflake;
namespace Sanhe.Abp.IdGenerator.Snowflake;
public class SnowflakeIdOptions
{

3
modules/common/Sanhe.Abp.Notifications/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

23
modules/common/Sanhe.Abp.Notifications/Sanhe.Abp.Notifications.csproj

@ -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>

46
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/AbpNotificationsModule.cs

@ -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);
});
}
}

29
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/AbpNotificationsOptions.cs

@ -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();
}
}

33
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationDefinitionContext.cs

@ -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);
}

34
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationDefinitionManager.cs

@ -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();
}

14
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationDefinitionProvider.cs

@ -0,0 +1,14 @@
namespace Sanhe.Abp.Notifications
{
/// <summary>
/// 通知定义提供者接口
/// </summary>
public interface INotificationDefinitionProvider
{
/// <summary>
/// 定义
/// </summary>
/// <param name="context">通知定义上下文</param>
void Define(INotificationDefinitionContext context);
}
}

22
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationPublishProvider.cs

@ -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);
}

15
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationPublishProviderManager.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Sanhe.Abp.Notifications
{
/// <summary>
/// 通知发布提供者管理
/// </summary>
public interface INotificationPublishProviderManager
{
/// <summary>
/// 提供者列表
/// </summary>
List<INotificationPublishProvider> Providers { get; }
}
}

43
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationSender.cs

@ -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);
}

127
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationStore.cs

@ -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);
}

123
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/INotificationSubscriptionManager.cs

@ -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);
}

115
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/Internal/NotificationSender.cs

@ -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();
}
}
}

103
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/Internal/NotificationSubscriptionManager.cs

@ -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);
}
}
}

177
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationData.cs

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

38
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataConverter.cs

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

63
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataMappingDictionary.cs

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

30
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataMappingDictionaryItem.cs

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

20
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDataMappingDictionaryItemExtensions.cs

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

94
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinition.cs

@ -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}]";
}
}

55
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinitionContext.cs

@ -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);
}
}

113
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinitionManager.cs

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

15
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationDefinitionProvider.cs

@ -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);
}

55
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationEto.cs

@ -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)
{
}
}

84
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationGroupDefinition.cs

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

73
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationInfo.cs

@ -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);
}
}

17
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationLifetime.cs

@ -0,0 +1,17 @@
namespace Sanhe.Abp.Notifications;
/// <summary>
/// 通知存活时间
/// 发送之后取消用户订阅,类似于微信小程序
/// </summary>
public enum NotificationLifetime
{
/// <summary>
/// 持久化
/// </summary>
Persistent = 0,
/// <summary>
/// 一次性
/// </summary>
OnlyOne = 1
}

24
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationProviderNames.cs

@ -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";
}

68
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationPublishProvider.cs

@ -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);
}

32
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationPublishProviderManager.cs

@ -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
);
}
}

16
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationReadState.cs

@ -0,0 +1,16 @@
namespace Sanhe.Abp.Notifications;
/// <summary>
/// 读取状态
/// </summary>
public enum NotificationReadState
{
/// <summary>
/// 已读
/// </summary>
Read = 0,
/// <summary>
/// 未读
/// </summary>
UnRead = 1
}

28
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationSeverity.cs

@ -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
}

26
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationSubscriptionInfo.cs

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

20
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NotificationType.cs

@ -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
}

189
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/NullNotificationStore.cs

@ -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);
}
}

24
modules/common/Sanhe.Abp.Notifications/Sanhe/Abp/Notifications/UserIdentifier.cs

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

3
modules/common/Sanhe.Abp.RealTime/Sanhe.Abp.RealTime.csproj

@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>

2
modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/AbpRealTimeModule.cs

@ -1,7 +1,7 @@
using Volo.Abp.EventBus.Abstractions;
using Volo.Abp.Modularity;
namespace Sanhe.Abp.RealTime.Sanhe.Abp.RealTime;
namespace Sanhe.Abp.RealTime;
[DependsOn(typeof(AbpEventBusAbstractionsModule))]
public class AbpRealTimeModule : AbpModule

2
modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/RealTimeEto.cs

@ -2,7 +2,7 @@
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
namespace Sanhe.Abp.RealTime.Sanhe.Abp.RealTime;
namespace Sanhe.Abp.RealTime;
[Serializable]
[GenericEventName(Prefix = "abp.realtime.")]

Loading…
Cancel
Save