199 changed files with 6557 additions and 2883 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.1.6" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.SignalR" Version="3.2.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,22 @@ |
|||
using Microsoft.AspNetCore.SignalR; |
|||
using Microsoft.AspNetCore.SignalR.Protocol; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Volo.Abp.AspNetCore.SignalR; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.SignalR.Protocol.Json |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreSignalRModule))] |
|||
public class AbpAspNetCoreSignalRProtocolJsonModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var newtonsoftJsonOptions = new NewtonsoftJsonHubProtocolOptions(); |
|||
context.Services.ExecutePreConfiguredActions(newtonsoftJsonOptions); |
|||
|
|||
context.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, NewtonsoftJsonHubProtocol>()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Notifications:Exception": "Exception", |
|||
"Notifications:ExceptionNotifier": "Exception Notifier" |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Notifications:Exception": "异常通知", |
|||
"Notifications:ExceptionNotifier": "异常推送" |
|||
} |
|||
} |
|||
@ -1,8 +1,18 @@ |
|||
using Volo.Abp.Modularity; |
|||
using LINGYUN.Abp.ExceptionHandling.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class AbpExceptionHandlingModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources.Add<ExceptionHandlingResource>("en"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling.Localization |
|||
{ |
|||
[LocalizationResourceName("AbpExceptionHandling")] |
|||
public class ExceptionHandlingResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.EventBus; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.IM |
|||
{ |
|||
[DependsOn(typeof(AbpEventBusModule))] |
|||
public class AbpIMModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using LINGYUN.Abp.IM.Messages; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace LINGYUN.Abp.IM |
|||
{ |
|||
public class AbpIMOptions |
|||
{ |
|||
public ITypeList<IMessageSenderProvider> Providers { get; } |
|||
|
|||
public AbpIMOptions() |
|||
{ |
|||
Providers = new TypeList<IMessageSenderProvider>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public interface IMessageSenderProvider |
|||
{ |
|||
string Name { get; } |
|||
Task SendMessageAsync(ChatMessage chatMessage); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public interface IMessageSenderProviderManager |
|||
{ |
|||
List<IMessageSenderProvider> Providers { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public class MessageSender : IMessageSender, ITransientDependency |
|||
{ |
|||
protected IDistributedEventBus EventBus { get; } |
|||
public MessageSender(IDistributedEventBus eventBus) |
|||
{ |
|||
EventBus = eventBus; |
|||
} |
|||
|
|||
public virtual async Task SendMessageAsync(ChatMessage chatMessage) |
|||
{ |
|||
chatMessage.SetProperty(nameof(ChatMessage.IsAnonymous), chatMessage.IsAnonymous); |
|||
|
|||
// 如果先存储的话,就紧耦合消息处理模块了
|
|||
// await Store.StoreMessageAsync(chatMessage);
|
|||
|
|||
await EventBus.PublishAsync(chatMessage); |
|||
} |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public abstract class MessageSenderBase : IMessageSender |
|||
{ |
|||
protected IMessageStore Store { get; } |
|||
protected ILogger Logger { get; } |
|||
protected MessageSenderBase( |
|||
IMessageStore store, |
|||
ILogger logger) |
|||
{ |
|||
Store = store; |
|||
Logger = logger; |
|||
} |
|||
|
|||
public virtual async Task SendMessageAsync(ChatMessage chatMessage) |
|||
{ |
|||
// 持久化
|
|||
await Store.StoreMessageAsync(chatMessage); |
|||
|
|||
try |
|||
{ |
|||
if (!chatMessage.GroupId.IsNullOrWhiteSpace()) |
|||
{ |
|||
await SendMessageToGroupAsync(chatMessage); |
|||
} |
|||
else |
|||
{ |
|||
await SendMessageToUserAsync(chatMessage); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning("Could not send message, group: {0}, formUser: {1}, toUser: {2}", |
|||
chatMessage.GroupId, chatMessage.FormUserName, |
|||
chatMessage.ToUserId.HasValue ? chatMessage.ToUserId.ToString() : "None"); |
|||
Logger.LogWarning("Send group message error: {0}", ex.Message); |
|||
} |
|||
} |
|||
|
|||
protected abstract Task SendMessageToGroupAsync(ChatMessage chatMessage); |
|||
protected abstract Task SendMessageToUserAsync(ChatMessage chatMessage); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public abstract class MessageSenderProviderBase : IMessageSenderProvider, ITransientDependency |
|||
{ |
|||
public abstract string Name { get; } |
|||
|
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
protected readonly object ServiceProviderLock = new object(); |
|||
|
|||
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); |
|||
private ILoggerFactory _loggerFactory; |
|||
|
|||
protected ILogger Logger => _lazyLogger.Value; |
|||
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => 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; |
|||
} |
|||
|
|||
protected MessageSenderProviderBase(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public virtual async Task SendMessageAsync(ChatMessage chatMessage) |
|||
{ |
|||
try |
|||
{ |
|||
if (!chatMessage.GroupId.IsNullOrWhiteSpace()) |
|||
{ |
|||
await SendMessageToGroupAsync(chatMessage); |
|||
} |
|||
else |
|||
{ |
|||
await SendMessageToUserAsync(chatMessage); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning("Could not send message, group: {0}, formUser: {1}, toUser: {2}", |
|||
chatMessage.GroupId, chatMessage.FormUserName, |
|||
chatMessage.ToUserId.HasValue ? chatMessage.ToUserId.ToString() : "None"); |
|||
Logger.LogWarning("Send group message error: {0}", ex.Message); |
|||
} |
|||
} |
|||
|
|||
protected abstract Task SendMessageToGroupAsync(ChatMessage chatMessage); |
|||
protected abstract Task SendMessageToUserAsync(ChatMessage chatMessage); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public class MessageSenderProviderManager : IMessageSenderProviderManager, ISingletonDependency |
|||
{ |
|||
public List<IMessageSenderProvider> Providers => _lazyProviders.Value; |
|||
|
|||
protected AbpIMOptions Options { get; } |
|||
|
|||
private readonly Lazy<List<IMessageSenderProvider>> _lazyProviders; |
|||
|
|||
public MessageSenderProviderManager( |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AbpIMOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
|
|||
_lazyProviders = new Lazy<List<IMessageSenderProvider>>( |
|||
() => Options |
|||
.Providers |
|||
.Select(type => serviceProvider.GetRequiredService(type) as IMessageSenderProvider) |
|||
.ToList(), |
|||
true |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public class NullMessageSender : MessageSenderBase, ITransientDependency |
|||
{ |
|||
public NullMessageSender(IMessageStore store, ILogger<NullMessageSender> logger) |
|||
: base(store, logger) |
|||
{ |
|||
} |
|||
|
|||
protected override Task SendMessageToGroupAsync(ChatMessage chatMessage) |
|||
{ |
|||
Logger.LogWarning("No IMessageSender Interface implementation!"); |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected override Task SendMessageToUserAsync(ChatMessage chatMessage) |
|||
{ |
|||
Logger.LogWarning("No IMessageSender Interface implementation!"); |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using LINGYUN.Abp.WeChat.Features; |
|||
using LINGYUN.Abp.WeChat.Localization; |
|||
using Volo.Abp.Features; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Validation.StringValues; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp.Features |
|||
{ |
|||
public class WeChatWeAppFeatureDefinitionProvider : FeatureDefinitionProvider |
|||
{ |
|||
public override void Define(IFeatureDefinitionContext context) |
|||
{ |
|||
var wechatGroup = context.GetGroupOrNull(WeChatFeatures.GroupName); |
|||
if (wechatGroup != null) |
|||
{ |
|||
var weappFeature = wechatGroup |
|||
.AddFeature( |
|||
WeChatWeAppFeatures.GroupName, |
|||
true.ToString(), |
|||
L("Features:WeApp"), |
|||
L("Features:WeAppDescription"), |
|||
new ToggleStringValueType(new BooleanValueValidator())); |
|||
|
|||
|
|||
var weappNofitication = weappFeature |
|||
.CreateChild( |
|||
WeChatWeAppFeatures.Notifications.Default, |
|||
true.ToString(), |
|||
L("Features:Notifications"), |
|||
L("Features:Notifications"), |
|||
new ToggleStringValueType(new BooleanValueValidator())); |
|||
weappNofitication |
|||
.CreateChild( |
|||
WeChatWeAppFeatures.Notifications.PublishLimit, |
|||
WeChatWeAppFeatures.Notifications.DefaultPublishLimit.ToString(), |
|||
L("Features:PublishLimit"), |
|||
L("Features:PublishLimitDescription"), |
|||
new ToggleStringValueType(new NumericValueValidator(0, 100000))); |
|||
weappNofitication |
|||
.CreateChild( |
|||
WeChatWeAppFeatures.Notifications.PublishLimitInterval, |
|||
WeChatWeAppFeatures.Notifications.DefaultPublishLimitInterval.ToString(), |
|||
L("Features:PublishLimitInterval"), |
|||
L("Features:PublishLimitIntervalDescription"), |
|||
new ToggleStringValueType(new NumericValueValidator(1, 12))); |
|||
} |
|||
} |
|||
|
|||
protected LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<WeChatResource>(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using LINGYUN.Abp.WeChat.Features; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp.Features |
|||
{ |
|||
public static class WeChatWeAppFeatures |
|||
{ |
|||
public const string GroupName = WeChatFeatures.GroupName + ".WeApp"; |
|||
|
|||
public static class Notifications |
|||
{ |
|||
public const string Default = GroupName + ".Notifications"; |
|||
/// <summary>
|
|||
/// 发布次数上限
|
|||
/// </summary>
|
|||
public const string PublishLimit = Default + ".PublishLimit"; |
|||
/// <summary>
|
|||
/// 发布次数上限时长
|
|||
/// </summary>
|
|||
public const string PublishLimitInterval = Default + ".PublishLimitInterval"; |
|||
/// <summary>
|
|||
/// 默认发布次数上限
|
|||
/// </summary>
|
|||
public const int DefaultPublishLimit = 1000; |
|||
/// <summary>
|
|||
/// 默认发布次数上限时长
|
|||
/// </summary>
|
|||
public const int DefaultPublishLimitInterval = 1; |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +1,10 @@ |
|||
using System.Threading.Tasks; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp |
|||
{ |
|||
public interface IWeChatWeAppNotificationSender |
|||
{ |
|||
Task SendAsync(WeChatWeAppSendNotificationData notificationData); |
|||
Task SendAsync(WeChatWeAppSendNotificationData notificationData, CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
|
|||
@ -1,9 +1,18 @@ |
|||
namespace LINGYUN.Abp.Notifications |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public interface INotificationDefinitionContext |
|||
{ |
|||
NotificationDefinition GetOrNull(string category); |
|||
NotificationGroupDefinition AddGroup( |
|||
[NotNull] string name, |
|||
ILocalizableString displayName = null, |
|||
bool allowSubscriptionToClients = true); |
|||
|
|||
NotificationGroupDefinition GetGroupOrNull(string name); |
|||
|
|||
void Add(params NotificationDefinition[] definitions); |
|||
void RemoveGroup(string name); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.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>
|
|||
Task 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>
|
|||
Task SendNofitersAsync( |
|||
string name, |
|||
NotificationData data, |
|||
IEnumerable<UserIdentifier> users = null, |
|||
Guid? tenantId = null, |
|||
NotificationSeverity severity = NotificationSeverity.Info); |
|||
} |
|||
} |
|||
@ -1,45 +1,126 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public interface INotificationStore |
|||
{ |
|||
Task InsertUserSubscriptionAsync(Guid? tenantId, UserIdentifier identifier, string notificationName); |
|||
|
|||
Task InsertUserSubscriptionAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName); |
|||
|
|||
Task DeleteUserSubscriptionAsync(Guid? tenantId, Guid userId, string notificationName); |
|||
|
|||
Task DeleteAllUserSubscriptionAsync(Guid? tenantId, string notificationName); |
|||
|
|||
Task DeleteUserSubscriptionAsync(Guid? tenantId, IEnumerable<UserIdentifier> identifiers, string notificationName); |
|||
|
|||
Task<List<NotificationSubscriptionInfo>> GetSubscriptionsAsync(Guid? tenantId, string notificationName); |
|||
|
|||
Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId); |
|||
|
|||
Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, string userName); |
|||
|
|||
Task<bool> IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName); |
|||
|
|||
Task InsertNotificationAsync(NotificationInfo notification); |
|||
|
|||
Task DeleteNotificationAsync(NotificationInfo notification); |
|||
|
|||
Task DeleteNotificationAsync(int batchCount); |
|||
|
|||
Task InsertUserNotificationAsync(NotificationInfo notification, Guid userId); |
|||
|
|||
Task InsertUserNotificationsAsync(NotificationInfo notification, IEnumerable<Guid> userIds); |
|||
|
|||
Task DeleteUserNotificationAsync(Guid? tenantId, Guid userId, long notificationId); |
|||
|
|||
Task<NotificationInfo> GetNotificationOrNullAsync(Guid? tenantId, long notificationId); |
|||
|
|||
Task<List<NotificationInfo>> GetUserNotificationsAsync(Guid? tenantId, Guid userId, NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10); |
|||
|
|||
Task ChangeUserNotificationReadStateAsync(Guid? tenantId, Guid userId, long notificationId, NotificationReadState readState); |
|||
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 = NotificationReadState.UnRead, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<int> GetUserNotificationsCountAsync( |
|||
Guid? tenantId, |
|||
Guid userId, |
|||
string filter = "", |
|||
NotificationReadState readState = NotificationReadState.UnRead, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<NotificationInfo>> GetUserNotificationsAsync( |
|||
Guid? tenantId, |
|||
Guid userId, |
|||
string filter = "", |
|||
string sorting = nameof(NotificationInfo.CreationTime), |
|||
bool reverse = true, |
|||
NotificationReadState readState = NotificationReadState.UnRead, |
|||
int skipCount = 1, |
|||
int maxResultCount = 10, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task ChangeUserNotificationReadStateAsync( |
|||
Guid? tenantId, |
|||
Guid userId, |
|||
long notificationId, |
|||
NotificationReadState readState, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
|
|||
@ -1,220 +0,0 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Implements <see cref="INotificationDispatcher"/>.
|
|||
/// </summary>
|
|||
internal class DefaultNotificationDispatcher : INotificationDispatcher |
|||
{ |
|||
/// <summary>
|
|||
/// Reference to <see cref="ILogger<DefaultNotificationDispatcher>"/>.
|
|||
/// </summary>
|
|||
public ILogger<DefaultNotificationDispatcher> Logger { get; set; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="IDistributedEventBus"/>.
|
|||
/// </summary>
|
|||
public IDistributedEventBus DistributedEventBus { get; set; } |
|||
/// <summary>
|
|||
/// Reference to <see cref="AbpNotificationOptions"/>.
|
|||
/// </summary>
|
|||
private readonly AbpNotificationOptions _notificationOptions; |
|||
/// <summary>
|
|||
/// Reference to <see cref="IBackgroundJobManager"/>.
|
|||
/// </summary>
|
|||
private readonly IBackgroundJobManager _backgroundJobManager; |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationStore"/>.
|
|||
/// </summary>
|
|||
private readonly INotificationStore _notificationStore; |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationDefinitionManager"/>.
|
|||
/// </summary>
|
|||
private readonly INotificationDefinitionManager _notificationDefinitionManager; |
|||
/// <summary>
|
|||
/// Reference to <see cref="INotificationPublishProviderManager"/>.
|
|||
/// </summary>
|
|||
private readonly INotificationPublishProviderManager _notificationPublishProviderManager; |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DefaultNotificationDispatcher"/> class.
|
|||
/// </summary>
|
|||
public DefaultNotificationDispatcher( |
|||
IBackgroundJobManager backgroundJobManager, |
|||
IOptions<AbpNotificationOptions> options, |
|||
INotificationStore notificationStore, |
|||
INotificationDefinitionManager notificationDefinitionManager, |
|||
INotificationPublishProviderManager notificationPublishProviderManager) |
|||
{ |
|||
_backgroundJobManager = backgroundJobManager; |
|||
_notificationOptions = options.Value; |
|||
_notificationStore = notificationStore; |
|||
_notificationDefinitionManager = notificationDefinitionManager; |
|||
_notificationPublishProviderManager = notificationPublishProviderManager; |
|||
|
|||
DistributedEventBus = NullDistributedEventBus.Instance; |
|||
Logger = NullLogger<DefaultNotificationDispatcher>.Instance; |
|||
} |
|||
/// <summary>
|
|||
/// 发送通知
|
|||
/// </summary>
|
|||
/// <param name="notificationName">通知名称</param>
|
|||
/// <param name="data">通知数据</param>
|
|||
/// <param name="tenantId">租户</param>
|
|||
/// <param name="notificationSeverity">级别</param>
|
|||
/// <returns></returns>
|
|||
public virtual async Task DispatchAsync(NotificationName notificationName, NotificationData data, Guid? tenantId = null, |
|||
NotificationSeverity notificationSeverity = NotificationSeverity.Info) |
|||
{ |
|||
// 获取自定义的通知
|
|||
var defineNotification = _notificationDefinitionManager.Get(notificationName.CateGory); |
|||
|
|||
//// 没有定义的通知,应该也要能发布、订阅,
|
|||
//// 比如订单之类的,是以订单编号为通知名称,这是动态的,没法自定义
|
|||
//if(defineNotification == null)
|
|||
//{
|
|||
// defineNotification = new NotificationDefinition(notificationName.CateGory);
|
|||
//}
|
|||
|
|||
var notificationInfo = new NotificationInfo |
|||
{ |
|||
CateGory = notificationName.CateGory, |
|||
Name = notificationName.Name, |
|||
CreationTime = DateTime.Now, |
|||
NotificationSeverity = notificationSeverity, |
|||
Lifetime = defineNotification.NotificationLifetime, |
|||
NotificationType = defineNotification.NotificationType, |
|||
TenantId = tenantId, |
|||
Data = data |
|||
}; |
|||
|
|||
var providers = Enumerable |
|||
.Reverse(_notificationPublishProviderManager.Providers); |
|||
|
|||
if (defineNotification.Providers.Any()) |
|||
{ |
|||
providers = providers.Where(p => defineNotification.Providers.Contains(p.Name)); |
|||
} |
|||
|
|||
await PublishFromProvidersAsync(providers, notificationInfo); |
|||
|
|||
if (notificationInfo.Lifetime == NotificationLifetime.OnlyOne) |
|||
{ |
|||
// 一次性通知在发送完成后就取消用户订阅
|
|||
await _notificationStore.DeleteAllUserSubscriptionAsync(notificationInfo.TenantId, |
|||
notificationInfo.Name); |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 发送通知事件
|
|||
/// </summary>
|
|||
/// <param name="notificationName"></param>
|
|||
/// <param name="data"></param>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="notificationSeverity"></param>
|
|||
/// <returns></returns>
|
|||
public virtual async Task DispatchEventAsync(NotificationName notificationName, NotificationData data, Guid? tenantId = null, |
|||
NotificationSeverity notificationSeverity = NotificationSeverity.Info) |
|||
{ |
|||
// 获取自定义的通知
|
|||
var defineNotification = _notificationDefinitionManager.Get(notificationName.CateGory); |
|||
|
|||
var notificationEventData = new NotificationEventData |
|||
{ |
|||
CateGory = notificationName.CateGory, |
|||
Name = notificationName.Name, |
|||
CreationTime = DateTime.Now, |
|||
NotificationSeverity = notificationSeverity, |
|||
Lifetime = defineNotification.NotificationLifetime, |
|||
NotificationType = defineNotification.NotificationType, |
|||
TenantId = tenantId, |
|||
Data = data |
|||
}; |
|||
// 发布分布式通知事件,让消息中心统一处理
|
|||
await DistributedEventBus.PublishAsync(notificationEventData); |
|||
} |
|||
/// <summary>
|
|||
/// 指定提供者发布通知
|
|||
/// </summary>
|
|||
/// <param name="providers">提供者列表</param>
|
|||
/// <param name="notificationInfo">通知信息</param>
|
|||
/// <returns></returns>
|
|||
protected async Task PublishFromProvidersAsync(IEnumerable<INotificationPublishProvider> providers, |
|||
NotificationInfo notificationInfo) |
|||
{ |
|||
Logger.LogDebug($"Persistent notification {notificationInfo.Name}"); |
|||
// 持久化通知
|
|||
await _notificationStore.InsertNotificationAsync(notificationInfo); |
|||
|
|||
Logger.LogDebug($"Gets a list of user subscriptions {notificationInfo.Name}"); |
|||
// 获取用户订阅列表
|
|||
var userSubscriptions = await _notificationStore.GetSubscriptionsAsync(notificationInfo.TenantId, notificationInfo.Name); |
|||
|
|||
Logger.LogDebug($"Persistent user notifications {notificationInfo.Name}"); |
|||
// 持久化用户通知
|
|||
var subscriptionUserIdentifiers = userSubscriptions.Select(us => new UserIdentifier(us.UserId, us.UserName)); |
|||
|
|||
await _notificationStore.InsertUserNotificationsAsync(notificationInfo, |
|||
subscriptionUserIdentifiers.Select(u => u.UserId)); |
|||
|
|||
// 发布通知
|
|||
foreach (var provider in providers) |
|||
{ |
|||
await PublishAsync(provider, notificationInfo, subscriptionUserIdentifiers); |
|||
} |
|||
|
|||
// TODO: 需要计算队列大小,根据情况是否需要并行发布消息
|
|||
//Parallel.ForEach(providers, async (provider) =>
|
|||
//{
|
|||
// await PublishAsync(provider, notificationInfo, subscriptionUserIdentifiers);
|
|||
//});
|
|||
} |
|||
/// <summary>
|
|||
/// 发布通知
|
|||
/// </summary>
|
|||
/// <param name="provider">通知发布者</param>
|
|||
/// <param name="notificationInfo">通知信息</param>
|
|||
/// <param name="subscriptionUserIdentifiers">订阅用户列表</param>
|
|||
/// <returns></returns>
|
|||
protected async Task PublishAsync(INotificationPublishProvider provider, NotificationInfo notificationInfo, |
|||
IEnumerable<UserIdentifier> subscriptionUserIdentifiers) |
|||
{ |
|||
try |
|||
{ |
|||
Logger.LogDebug($"Sending notification with provider {provider.Name}"); |
|||
var notifacationDataMapping = _notificationOptions.NotificationDataMappings |
|||
.GetMapItemOrNull(notificationInfo.CateGory, provider.Name); |
|||
if (notifacationDataMapping != null) |
|||
{ |
|||
notificationInfo.Data = notifacationDataMapping.MappingFunc(notificationInfo.Data); |
|||
} |
|||
// 发布
|
|||
await provider.PublishAsync(notificationInfo, subscriptionUserIdentifiers); |
|||
|
|||
Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning($"Send notification error with provider {provider.Name}"); |
|||
Logger.LogWarning($"Error message:{ex.Message}"); |
|||
|
|||
Logger.LogTrace(ex, $"Send notification error with provider { provider.Name}"); |
|||
|
|||
Logger.LogDebug($"Send notification error, notification {notificationInfo.Name} entry queue"); |
|||
// 发送失败的消息进入后台队列
|
|||
await _backgroundJobManager.EnqueueAsync( |
|||
new NotificationPublishJobArgs(notificationInfo.GetId(), |
|||
provider.GetType().AssemblyQualifiedName, |
|||
subscriptionUserIdentifiers.ToList(), |
|||
notificationInfo.TenantId)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
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; } |
|||
|
|||
public NotificationSender( |
|||
IDistributedEventBus distributedEventBus) |
|||
{ |
|||
DistributedEventBus = distributedEventBus; |
|||
Logger = NullLogger<NotificationSender>.Instance; |
|||
} |
|||
|
|||
public async Task SendNofiterAsync( |
|||
string name, |
|||
NotificationData data, |
|||
UserIdentifier user = null, |
|||
Guid? tenantId = null, |
|||
NotificationSeverity severity = NotificationSeverity.Info) |
|||
{ |
|||
if (user == null) |
|||
{ |
|||
await PublishNofiterAsync(name, data, null, tenantId, severity); |
|||
|
|||
} |
|||
else |
|||
{ |
|||
await PublishNofiterAsync(name, data, new List<UserIdentifier> { user }, tenantId, severity); |
|||
} |
|||
} |
|||
|
|||
public async Task SendNofitersAsync( |
|||
string name, |
|||
NotificationData data, |
|||
IEnumerable<UserIdentifier> users = null, |
|||
Guid? tenantId = null, |
|||
NotificationSeverity severity = NotificationSeverity.Info) |
|||
{ |
|||
await PublishNofiterAsync(name, data, users, tenantId, severity); |
|||
} |
|||
|
|||
protected async Task PublishNofiterAsync( |
|||
string name, |
|||
NotificationData data, |
|||
IEnumerable<UserIdentifier> users = null, |
|||
Guid? tenantId = null, |
|||
NotificationSeverity severity = NotificationSeverity.Info) |
|||
{ |
|||
await DistributedEventBus |
|||
.PublishAsync( |
|||
new NotificationEventData |
|||
{ |
|||
TenantId = tenantId, |
|||
Users = users?.ToList(), |
|||
Name = name, |
|||
Data = data, |
|||
CreationTime = DateTime.Now, |
|||
Severity = severity |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class LocalizableStringInfo |
|||
{ |
|||
public string ResourceName { get; } |
|||
|
|||
public string Name { get; } |
|||
|
|||
public Dictionary<object, object> Values { get; } |
|||
|
|||
public LocalizableStringInfo( |
|||
string resourceName, |
|||
string name, |
|||
Dictionary<object, object> values = null) |
|||
{ |
|||
ResourceName = resourceName; |
|||
Name = name; |
|||
Values = values; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class NotificationDataConverter |
|||
{ |
|||
public static NotificationData Convert(NotificationData notificationData) |
|||
{ |
|||
if (notificationData != null) |
|||
{ |
|||
if (notificationData.NeedLocalizer()) |
|||
{ |
|||
var title = JsonConvert.DeserializeObject<LocalizableStringInfo>(notificationData.TryGetData("title").ToString()); |
|||
var message = JsonConvert.DeserializeObject<LocalizableStringInfo>(notificationData.TryGetData("message").ToString()); |
|||
notificationData.TrySetData("title", title); |
|||
notificationData.TrySetData("message", message); |
|||
|
|||
if (notificationData.Properties.TryGetValue("description", out object description) && description != null) |
|||
{ |
|||
notificationData.TrySetData("description", JsonConvert.DeserializeObject<LocalizableStringInfo>(description.ToString())); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
notificationData = new NotificationData(); |
|||
} |
|||
return notificationData; |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +1,57 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class NotificationDefinitionContext : INotificationDefinitionContext |
|||
{ |
|||
protected Dictionary<string, NotificationDefinition> Notifications { get; } |
|||
internal Dictionary<string, NotificationGroupDefinition> Groups { get; } |
|||
|
|||
public NotificationDefinitionContext(Dictionary<string, NotificationDefinition> notifications) |
|||
public NotificationDefinitionContext() |
|||
{ |
|||
Notifications = notifications; |
|||
Groups = new Dictionary<string, NotificationGroupDefinition>(); |
|||
} |
|||
|
|||
public void Add(params NotificationDefinition[] definitions) |
|||
public NotificationGroupDefinition AddGroup( |
|||
[NotNull] string name, |
|||
ILocalizableString displayName = null, |
|||
bool allowSubscriptionToClients = true) |
|||
{ |
|||
if (definitions.IsNullOrEmpty()) |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
if (Groups.ContainsKey(name)) |
|||
{ |
|||
return; |
|||
throw new AbpException($"There is already an existing notification group with name: {name}"); |
|||
} |
|||
|
|||
return Groups[name] = new NotificationGroupDefinition(name, displayName, allowSubscriptionToClients); |
|||
} |
|||
|
|||
foreach (var definition in definitions) |
|||
public NotificationGroupDefinition GetGroupOrNull(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
if (!Groups.ContainsKey(name)) |
|||
{ |
|||
Notifications[definition.CateGory] = definition; |
|||
return null; |
|||
} |
|||
|
|||
return Groups[name]; |
|||
} |
|||
|
|||
public NotificationDefinition GetOrNull(string category) |
|||
public void RemoveGroup(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
if (!Groups.ContainsKey(name)) |
|||
{ |
|||
return Notifications.GetOrDefault(category); |
|||
throw new AbpException($"Undefined notification group: '{name}'."); |
|||
} |
|||
|
|||
Groups.Remove(name); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,38 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class NotificationEventData |
|||
public class NotificationEventData : IMultiTenant |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
public string CateGory { get; set; } |
|||
public string Name { get; set; } |
|||
public string Id { get; set; } |
|||
public NotificationData Data { get; set; } |
|||
public DateTime CreationTime { get; set; } |
|||
public NotificationLifetime Lifetime { get; set; } |
|||
public NotificationType NotificationType { get; set; } |
|||
public NotificationSeverity NotificationSeverity { get; set; } |
|||
|
|||
public NotificationSeverity Severity { get; set; } |
|||
public List<UserIdentifier> Users { get; set; } |
|||
public NotificationEventData() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public NotificationInfo ToNotificationInfo() |
|||
{ |
|||
return new NotificationInfo |
|||
{ |
|||
NotificationSeverity = NotificationSeverity, |
|||
CreationTime = CreationTime, |
|||
Data = Data, |
|||
Id = Id, |
|||
Name = Name, |
|||
CateGory = CateGory, |
|||
NotificationType = NotificationType, |
|||
Lifetime = Lifetime, |
|||
TenantId = TenantId |
|||
}; |
|||
Users = new List<UserIdentifier>(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,70 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
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; } |
|||
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>(); |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class NotificationName |
|||
{ |
|||
public string CateGory { get; } |
|||
public string Name { get; } |
|||
|
|||
public NotificationName(string cateGory, string name) |
|||
{ |
|||
Name = name; |
|||
CateGory = cateGory; |
|||
} |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public static class NotificationNameNormalizer |
|||
{ |
|||
public static NotificationName NormalizerName(string name) |
|||
{ |
|||
return new NotificationName(name, name); |
|||
} |
|||
public static NotificationName NormalizerName(string category, string name) |
|||
{ |
|||
var notifyName = string.Concat(category, ":", name); |
|||
return new NotificationName(category, notifyName); |
|||
} |
|||
} |
|||
} |
|||
@ -1,8 +0,0 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.RealTime.SignalR |
|||
{ |
|||
public class Class1 |
|||
{ |
|||
} |
|||
} |
|||
@ -1,80 +1,94 @@ |
|||
using LINGYUN.Abp.RealTime.Client; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.SignalR; |
|||
using Volo.Abp.AspNetCore.WebClientInfo; |
|||
|
|||
namespace LINGYUN.Abp.RealTime.SignalR |
|||
{ |
|||
public abstract class OnlineClientHubBase : AbpHub |
|||
public abstract class OnlineClientHubBase : AbpHub, IClient |
|||
{ |
|||
private IWebClientInfoProvider _webClientInfoProvider; |
|||
protected IWebClientInfoProvider WebClientInfoProvider => LazyGetRequiredService(ref _webClientInfoProvider); |
|||
|
|||
private IOnlineClientManager _onlineClientManager; |
|||
protected IOnlineClientManager OnlineClientManager => LazyGetRequiredService(ref _onlineClientManager); |
|||
|
|||
private IHttpContextAccessor _httpContextAccessor; |
|||
protected IHttpContextAccessor HttpContextAccessor => LazyGetRequiredService(ref _httpContextAccessor); |
|||
|
|||
public override async Task OnConnectedAsync() |
|||
{ |
|||
await base.OnConnectedAsync(); |
|||
|
|||
IOnlineClient onlineClient = CreateClientForCurrentConnection(); |
|||
Logger.LogDebug("A client is connected: " + onlineClient.ToString()); |
|||
OnlineClientManager.Add(onlineClient); |
|||
await OnClientConnectedAsync(onlineClient); |
|||
await OnConnectedAsync(onlineClient); |
|||
} |
|||
|
|||
public override async Task OnDisconnectedAsync(Exception exception) |
|||
public virtual async Task OnConnectedAsync(IOnlineClient client) |
|||
{ |
|||
await base.OnDisconnectedAsync(exception); |
|||
Logger.LogDebug("A client is disconnected: " + Context.ConnectionId); |
|||
try |
|||
Logger.LogDebug("A client is connected: " + client.ToString()); |
|||
OnlineClientManager.Add(client); |
|||
await OnClientConnectedAsync(client); |
|||
} |
|||
|
|||
public override async Task OnDisconnectedAsync(Exception exception) |
|||
{ |
|||
// 从通讯组移除
|
|||
var onlineClient = OnlineClientManager.GetByConnectionIdOrNull(Context.ConnectionId); |
|||
if(onlineClient != null) |
|||
await OnDisconnectedAsync(onlineClient); |
|||
|
|||
await base.OnDisconnectedAsync(exception); |
|||
} |
|||
|
|||
public virtual async Task OnDisconnectedAsync(IOnlineClient client) |
|||
{ |
|||
if (client != null) |
|||
{ |
|||
try |
|||
{ |
|||
Logger.LogDebug("A client is disconnected: " + client); |
|||
// 移除在线客户端
|
|||
OnlineClientManager.Remove(Context.ConnectionId); |
|||
await OnClientDisconnectedAsync(onlineClient); |
|||
} |
|||
await OnClientDisconnectedAsync(client); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning(ex.ToString(), ex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual IOnlineClient CreateClientForCurrentConnection() |
|||
{ |
|||
return new OnlineClient(Context.ConnectionId, GetClientIpAddress(), |
|||
CurrentTenant.Id, CurrentUser.Id) |
|||
return new OnlineClient( |
|||
Context.ConnectionId, |
|||
WebClientInfoProvider.ClientIpAddress, |
|||
CurrentTenant.Id, |
|||
CurrentUser.Id) |
|||
{ |
|||
ConnectTime = Clock.Now |
|||
ConnectTime = Clock.Now, |
|||
UserName = CurrentUser.UserName, |
|||
UserAccount = CurrentUser.UserName, |
|||
Roles = CurrentUser.Roles ?? new string[0], |
|||
Properties = Context.Items |
|||
}; |
|||
} |
|||
|
|||
protected virtual string GetClientIpAddress() |
|||
{ |
|||
try |
|||
protected virtual async Task OnClientConnectedAsync(IOnlineClient client) |
|||
{ |
|||
return HttpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString(); |
|||
} |
|||
catch (Exception ex) |
|||
// 角色添加进组
|
|||
foreach (var role in client.Roles) |
|||
{ |
|||
Logger.LogException(ex, LogLevel.Warning); |
|||
return null; |
|||
await Groups.AddToGroupAsync(client.ConnectionId, role); |
|||
} |
|||
} |
|||
|
|||
protected virtual Task OnClientConnectedAsync(IOnlineClient client) |
|||
protected virtual async Task OnClientDisconnectedAsync(IOnlineClient client) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual Task OnClientDisconnectedAsync(IOnlineClient client) |
|||
// 角色添加进组
|
|||
foreach (var role in client.Roles) |
|||
{ |
|||
return Task.CompletedTask; |
|||
await Groups.RemoveFromGroupAsync(client.ConnectionId, role); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,10 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.RealTime.Client |
|||
{ |
|||
public interface IClient |
|||
{ |
|||
Task OnConnectedAsync(IOnlineClient client); |
|||
Task OnDisconnectedAsync(IOnlineClient client); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public interface IUserWeChatCodeFinder |
|||
{ |
|||
Task<string> FindByUserIdAsync(Guid userId); |
|||
|
|||
Task<string> FindByUserNameAsync(string userName); |
|||
} |
|||
} |
|||
@ -1,9 +1,14 @@ |
|||
using System.Threading.Tasks; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public interface IWeChatOpenIdFinder |
|||
{ |
|||
Task<WeChatOpenId> FindAsync(string code); |
|||
|
|||
Task<WeChatOpenId> FindByUserIdAsync(Guid userId); |
|||
|
|||
Task<WeChatOpenId> FindByUserNameAsync(string userName); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class NullUserWeChatCodeFinder : IUserWeChatCodeFinder, ISingletonDependency |
|||
{ |
|||
public Task<string> FindByUserIdAsync(Guid userId) |
|||
{ |
|||
return Task.FromResult(userId.ToString()); |
|||
} |
|||
|
|||
public Task<string> FindByUserNameAsync(string userName) |
|||
{ |
|||
return Task.FromResult(userName); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +1,10 @@ |
|||
using System.Threading.Tasks; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public interface IWeChatTokenProvider |
|||
{ |
|||
Task<WeChatToken> GetTokenAsync(); |
|||
Task<WeChatToken> GetTokenAsync(CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,22 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class WeChatAuthorizationConsts |
|||
{ |
|||
/// <summary>
|
|||
/// 微信提供者标识
|
|||
/// </summary>
|
|||
public static string ProviderKey { get; set; } = "WeChat"; |
|||
/// <summary>
|
|||
/// 微信Code参数名称
|
|||
/// </summary>
|
|||
public static string WeCahtCodeKey { get; set; } = "wx-code"; |
|||
/// <summary>
|
|||
/// 微信OpenId参数名称
|
|||
/// </summary>
|
|||
public static string WeCahtOpenIdKey { get; set; } = "wx-open-id"; |
|||
/// <summary>
|
|||
/// 微信SessionKey参数名称
|
|||
/// </summary>
|
|||
public static string WeCahtSessionKey { get; set; } = "wx-session-key"; |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
// TODO: 真正的项目需要扩展Abp框架实体来关联微信
|
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(IUserWeChatCodeFinder))] |
|||
public class UserWeChatCodeFinder : IUserWeChatCodeFinder |
|||
{ |
|||
protected IdentityUserManager UserManager { get; } |
|||
|
|||
public UserWeChatCodeFinder( |
|||
IdentityUserManager userManager) |
|||
{ |
|||
UserManager = userManager; |
|||
} |
|||
|
|||
public virtual async Task<string> FindByUserIdAsync(Guid userId) |
|||
{ |
|||
var user = await UserManager.FindByIdAsync(userId.ToString()); |
|||
|
|||
var weChatCodeToken = user?.FindToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtCodeKey); |
|||
|
|||
return weChatCodeToken?.Value ?? userId.ToString(); |
|||
} |
|||
|
|||
public virtual async Task<string> FindByUserNameAsync(string userName) |
|||
{ |
|||
var user = await UserManager.FindByNameAsync(userName); |
|||
|
|||
var weChatCodeToken = user?.FindToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtCodeKey); |
|||
|
|||
return weChatCodeToken?.Value ?? userName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.MessageService.Chat |
|||
{ |
|||
public class MyFriendAddRequestDto : MyFriendOperationDto |
|||
{ |
|||
public string RemarkName { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class NotificationDto |
|||
{ |
|||
/// <summary>
|
|||
/// 通知名称
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 显示名称
|
|||
/// </summary>
|
|||
public string DisplayName { get; set; } |
|||
/// <summary>
|
|||
/// 说明
|
|||
/// </summary>
|
|||
public string Description { get; set; } |
|||
/// <summary>
|
|||
/// 存活类型
|
|||
/// </summary>
|
|||
public NotificationLifetime Lifetime { get; set; } |
|||
/// <summary>
|
|||
/// 通知类型
|
|||
/// </summary>
|
|||
public NotificationType Type { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class NotificationGroupDto |
|||
{ |
|||
public string Name { get; set; } |
|||
public string DisplayName { get; set; } |
|||
public List<NotificationDto> Notifications { get; set; } = new List<NotificationDto>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class NotificationSendDto |
|||
{ |
|||
[Required] |
|||
[StringLength(NotificationConsts.MaxNameLength)] |
|||
public string Name { get; set; } |
|||
|
|||
public NotificationData Data { get; set; } = new NotificationData(); |
|||
|
|||
public Guid? ToUserId { get; set; } |
|||
|
|||
[StringLength(128)] |
|||
public string ToUserName { get; set; } |
|||
|
|||
public NotificationSeverity Severity { get; set; } = NotificationSeverity.Info; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public interface IMyNotificationAppService : |
|||
|
|||
IReadOnlyAppService< |
|||
NotificationInfo, |
|||
long, |
|||
UserNotificationGetByPagedDto |
|||
>, |
|||
IDeleteAppService<long> |
|||
{ |
|||
Task SendNofiterAsync(NotificationSendDto input); |
|||
|
|||
Task<ListResultDto<NotificationGroupDto>> GetAssignableNotifiersAsync(); |
|||
} |
|||
} |
|||
@ -1,41 +0,0 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public interface INotificationAppService : IApplicationService |
|||
{ |
|||
/// <summary>
|
|||
/// 查询通知明细
|
|||
/// </summary>
|
|||
/// <param name="notificationGetById"></param>
|
|||
/// <returns></returns>
|
|||
Task<NotificationInfo> GetAsync(NotificationGetByIdDto notificationGetById); |
|||
/// <summary>
|
|||
/// 删除通知
|
|||
/// </summary>
|
|||
/// <param name="notificationGetById"></param>
|
|||
/// <returns></returns>
|
|||
Task DeleteAsync(NotificationGetByIdDto notificationGetById); |
|||
/// <summary>
|
|||
/// 删除用户通知
|
|||
/// </summary>
|
|||
/// <param name="notificationGetById"></param>
|
|||
/// <returns></returns>
|
|||
Task DeleteUserNotificationAsync(NotificationGetByIdDto notificationGetById); |
|||
/// <summary>
|
|||
/// 变更通知阅读状态
|
|||
/// </summary>
|
|||
/// <param name="userNotificationChangeRead"></param>
|
|||
/// <returns></returns>
|
|||
Task ChangeUserNotificationReadStateAsync(UserNotificationChangeReadStateDto userNotificationChangeRead); |
|||
/// <summary>
|
|||
/// 获取用户通知列表
|
|||
/// </summary>
|
|||
/// <param name="userNotificationGetByPaged"></param>
|
|||
/// <returns></returns>
|
|||
Task<PagedResultDto<NotificationInfo>> GetUserNotificationsAsync(UserNotificationGetByPagedDto userNotificationGetByPaged); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue