85 changed files with 1205 additions and 997 deletions
@ -0,0 +1,29 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<Description>阿里云SDK基础框架</Description> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINYUN\Abp\Aliyun\Localization\Resources\en.json" /> |
|||
<None Remove="LINYUN\Abp\Aliyun\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINYUN\Abp\Aliyun\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINYUN\Abp\Aliyun\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="aliyun-net-sdk-core" Version="1.5.8" /> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="3.3.0" /> |
|||
<PackageReference Include="Volo.Abp.Json" Version="3.3.0" /> |
|||
<PackageReference Include="Volo.Abp.Settings" Version="3.3.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,29 @@ |
|||
using LINYUN.Abp.Aliyun.Localization; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Aliyun |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpJsonModule), |
|||
typeof(AbpLocalizationModule))] |
|||
public class AbpAliyunCloudModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAliyunCloudModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<AliyunResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/Aliyun/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINYUN.Abp.Aliyun.Localization |
|||
{ |
|||
[LocalizationResourceName("Aliyun")] |
|||
public class AliyunResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
namespace LINYUN.Abp.Aliyun.Settings |
|||
{ |
|||
public static class AliyunSettingNames |
|||
{ |
|||
public const string Prefix = "Abp.Aliyun"; |
|||
|
|||
/// <summary>
|
|||
/// 认证方式
|
|||
/// </summary>
|
|||
public class Authorization |
|||
{ |
|||
public const string Prefix = AliyunSettingNames.Prefix + ".Authorization"; |
|||
|
|||
public const string AccessKeyId = Prefix + ".AccessKeyId"; |
|||
|
|||
public const string AccessKeySecret = Prefix + ".AccessKeySecret"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<Description>腾讯云SDK基础框架</Description> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINYUN\Abp\Tencent\Localization\Resources\en.json" /> |
|||
<None Remove="LINYUN\Abp\Tencent\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINYUN\Abp\Tencent\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINYUN\Abp\Tencent\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="3.3.0" /> |
|||
<PackageReference Include="Volo.Abp.Json" Version="3.3.0" /> |
|||
<PackageReference Include="Volo.Abp.Settings" Version="3.3.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,29 @@ |
|||
using LINYUN.Abp.Tencent.Localization; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Tencent |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpJsonModule), |
|||
typeof(AbpLocalizationModule))] |
|||
public class AbpTencentCloudModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpTencentCloudModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<TencentResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/Tencent/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINYUN.Abp.Tencent.Localization |
|||
{ |
|||
[LocalizationResourceName("Tencent")] |
|||
public class TencentResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
namespace LINGYUN.Abp.IM.SignalR |
|||
{ |
|||
public class AbpIMSignalROptions |
|||
{ |
|||
/// <summary>
|
|||
/// 自定义的客户端接收消息方法名称
|
|||
/// </summary>
|
|||
public string GetChatMessageMethod { get; set; } |
|||
/// <summary>
|
|||
/// 用户上线接收方法名称
|
|||
/// </summary>
|
|||
public string UserOnlineMethod { get; set; } |
|||
public AbpIMSignalROptions() |
|||
{ |
|||
GetChatMessageMethod = "getChatMessage"; |
|||
UserOnlineMethod = "onUserOnlined"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace LINGYUN.Abp.Notifications.SignalR |
|||
{ |
|||
public class AbpNotificationsSignalROptions |
|||
{ |
|||
/// <summary>
|
|||
/// 自定义的客户端订阅通知方法名称
|
|||
/// </summary>
|
|||
public string MethodName { get; set; } |
|||
|
|||
public AbpNotificationsSignalROptions() |
|||
{ |
|||
MethodName = "getNotification"; |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Sms; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpNotificationModule), |
|||
typeof(AbpSmsModule))] |
|||
public class AbpNotificationsSmsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpNotificationOptions>(options => |
|||
{ |
|||
options.PublishProviders.Add<SmsNotificationPublishProvider>(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Sms; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpNotificationModule), |
|||
typeof(AbpSmsModule))] |
|||
public class AbpNotificationsSmsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpNotificationsSmsOptions>(options => |
|||
{ |
|||
context.Services.ExecutePreConfiguredActions(options); |
|||
}); |
|||
|
|||
Configure<AbpNotificationOptions>(options => |
|||
{ |
|||
options.PublishProviders.Add<SmsNotificationPublishProvider>(); |
|||
|
|||
var smsOptions = context.Services.ExecutePreConfiguredActions<AbpNotificationsSmsOptions>(); |
|||
options.NotificationDataMappings |
|||
.MappingDefault( |
|||
SmsNotificationPublishProvider.ProviderName, |
|||
data => NotificationData.ToStandardData(smsOptions.TemplateParamsPrefix, data)); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
public class NotificationSmsOptions |
|||
public class AbpNotificationsSmsOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 短信模板变量前缀
|
|||
@ -0,0 +1,21 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
/// <summary>
|
|||
/// 短信通知发送接口
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// 重写实现自定义的短信消息处理
|
|||
/// </remarks>
|
|||
public interface ISmsNotificationSender |
|||
{ |
|||
/// <summary>
|
|||
/// 发送通知
|
|||
/// </summary>
|
|||
/// <param name="notification">通知数据</param>
|
|||
/// <param name="phoneNumbers">手机号列表,多个手机号通过,分隔</param>
|
|||
/// <returns></returns>
|
|||
Task SendAsync(NotificationInfo notification, string phoneNumbers); |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Sms; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
/// <summary>
|
|||
/// 短信通知的默认实现者
|
|||
/// </summary>
|
|||
public class SmsNotificationSender : ISmsNotificationSender, ITransientDependency |
|||
{ |
|||
public ILogger Logger { protected get; set; } |
|||
protected ISmsSender SmsSender { get; } |
|||
|
|||
public SmsNotificationSender(ISmsSender smsSender) |
|||
{ |
|||
SmsSender = smsSender; |
|||
|
|||
Logger = NullLogger<SmsNotificationSender>.Instance; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 发送通知
|
|||
/// </summary>
|
|||
/// <param name="notification"></param>
|
|||
/// <param name="phoneNumbers"></param>
|
|||
/// <returns></returns>
|
|||
public virtual async Task SendAsync(NotificationInfo notification, string phoneNumbers) |
|||
{ |
|||
var templateCode = notification.Data.TryGetData("TemplateCode"); |
|||
if (templateCode == null) |
|||
{ |
|||
Logger.LogWarning("sms template code is empty, can not send sms message!"); |
|||
return; |
|||
} |
|||
var message = new SmsMessage(phoneNumbers, "SmsNotification"); |
|||
|
|||
// TODO: 后期增强功能,增加短信模板、通知模板功能
|
|||
message.Properties.Add("TemplateCode", templateCode); |
|||
message.Properties.Add("SignName", notification.Data.TryGetData("SignName")); |
|||
|
|||
await SmsSender.SendAsync(message); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
# LINGYUN.Abp.Notifications.Sms |
|||
|
|||
通知发布提供程序的短信实现 |
|||
|
|||
大部分重写的模块都和官方模块名称保持一致,通过命名空间区分,主要是只改写了一小部分或者增加额外的功能 |
|||
如果大部分模块代码都重写,或者完全就是扩展模块,才会定义自己的名字 |
|||
|
|||
#### 注意 |
|||
|
|||
自定义的发送方法可以通过实现 ##ISmsNotificationSender## 接口或重写 ##SmsNotificationSender## 即可 |
|||
|
|||
内置了通知数据 NotificationDataMappings 方法 |
|||
可通过 NotificationDataMappings.MappingAll(SmsNotificationPublishProvider.ProviderName, Func<NotificationData, NotificationData> func) 来自定义规则 |
|||
|
|||
|
|||
## 配置使用 |
|||
|
|||
* 此配置项将在下一个短信相关大版本移除 |
|||
|
|||
```json |
|||
|
|||
{ |
|||
"Notifications": { |
|||
"Sms": { |
|||
"TemplateParamsPrefix": "短信模板变量前缀" |
|||
} |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpNotificationsSmsModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,11 +1,7 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<Description>通知接口的微信小程序发布者实现</Description> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
@ -0,0 +1,30 @@ |
|||
using LINGYUN.Abp.WeChat.MiniProgram; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.MiniProgram |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpWeChatMiniProgramModule), |
|||
typeof(AbpNotificationModule))] |
|||
public class AbpNotificationsWeChatMiniProgramModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpNotificationsWeChatMiniProgramOptions>(options => |
|||
{ |
|||
context.Services.ExecutePreConfiguredActions(options); |
|||
}); |
|||
|
|||
Configure<AbpNotificationOptions>(options => |
|||
{ |
|||
options.PublishProviders.Add<WeChatMiniProgramNotificationPublishProvider>(); |
|||
|
|||
var wechatOptions = context.Services.ExecutePreConfiguredActions<AbpNotificationsWeChatMiniProgramOptions>(); |
|||
options.NotificationDataMappings |
|||
.MappingDefault(WeChatMiniProgramNotificationPublishProvider.ProviderName, |
|||
data => NotificationData.ToStandardData(wechatOptions.DefaultMsgPrefix, data)); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
namespace LINGYUN.Abp.Notifications.WeChat.MiniProgram |
|||
{ |
|||
/// <summary>
|
|||
/// TODO: 后期改进,配置项集成到 <see cref="LINGYUN.Abp.WeChat.MiniProgram.AbpWeChatMiniProgramOptions"/>
|
|||
/// </summary>
|
|||
public class AbpNotificationsWeChatMiniProgramOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 默认消息头部标记
|
|||
/// </summary>
|
|||
public string DefaultMsgPrefix { get; set; } = "[wmp]"; |
|||
/// <summary>
|
|||
/// 默认小程序模板
|
|||
/// </summary>
|
|||
public string DefaultTemplateId { get; set; } |
|||
/// <summary>
|
|||
/// 默认跳转小程序类型
|
|||
/// </summary>
|
|||
public string DefaultState { get; set; } = "developer"; |
|||
/// <summary>
|
|||
/// 默认小程序语言
|
|||
/// </summary>
|
|||
public string DefaultLanguage { get; set; } = "zh_CN"; |
|||
} |
|||
} |
|||
20
aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/Features/WeChatWeAppFeatureDefinitionProvider.cs → aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/Features/WeChatMiniProgramFeatureDefinitionProvider.cs
20
aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/Features/WeChatWeAppFeatureDefinitionProvider.cs → aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/Features/WeChatMiniProgramFeatureDefinitionProvider.cs
@ -1,10 +1,10 @@ |
|||
using LINGYUN.Abp.WeChat.Features; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp.Features |
|||
namespace LINGYUN.Abp.Notifications.WeChat.MiniProgram.Features |
|||
{ |
|||
public static class WeChatWeAppFeatures |
|||
public static class WeChatMiniProgramFeatures |
|||
{ |
|||
public const string GroupName = WeChatFeatures.GroupName + ".WeApp"; |
|||
public const string GroupName = WeChatFeatures.GroupName + ".MiniProgram"; |
|||
|
|||
public static class Notifications |
|||
{ |
|||
27
aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs → aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/WeChatMiniProgramNotificationPublishProvider.cs
27
aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs → aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat.MiniProgram/LINGYUN/Abp/Notifications/WeChat/MiniProgram/WeChatMiniProgramNotificationPublishProvider.cs
@ -0,0 +1,40 @@ |
|||
# LINGYUN.Abp.Notifications.WeChat.MiniProgram |
|||
|
|||
通知发布提供程序的微信小程序实现 |
|||
|
|||
大部分重写的模块都和官方模块名称保持一致,通过命名空间区分,主要是只改写了一小部分或者增加额外的功能 |
|||
如果大部分模块代码都重写,或者完全就是扩展模块,才会定义自己的名字 |
|||
|
|||
#### 注意 |
|||
|
|||
内置了通知数据 NotificationDataMappings 方法 |
|||
可通过 NotificationDataMappings.MappingAll(WeChatMiniProgramNotificationPublishProvider.ProviderName, Func<NotificationData, NotificationData> func) 来自定义规则 |
|||
|
|||
## 配置使用 |
|||
|
|||
* 此配置项将在下一个微信相关大版本移除,合并到 LINGYUN.Abp.WeChat.MiniProgram.AbpWeChatMiniProgramOptions |
|||
|
|||
```json |
|||
|
|||
{ |
|||
"Notifications": { |
|||
"WeChat": { |
|||
"MiniProgram": { |
|||
"DefaultMsgPrefix": "默认消息头部标记", |
|||
"DefaultTemplateId": "默认小程序模板", |
|||
"DefaultState": "默认跳转小程序类型", |
|||
"DefaultLanguage": "默认小程序语言" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpNotificationsWeChatMiniProgramModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
using LINGYUN.Abp.WeChat.MiniProgram; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpWeChatMiniProgramModule), |
|||
typeof(AbpNotificationModule))] |
|||
public class AbpNotificationsWeChatWeAppModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpWeChatWeAppNotificationOptions>(configuration.GetSection("Notifications:WeChat:WeApp")); |
|||
|
|||
Configure<AbpNotificationOptions>(options => |
|||
{ |
|||
options.PublishProviders.Add<WeChatWeAppNotificationPublishProvider>(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp |
|||
{ |
|||
public class AbpWeChatWeAppNotificationOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 默认消息头部标记
|
|||
/// </summary>
|
|||
public string DefaultMsgPrefix { get; set; } = "[wx]"; |
|||
/// <summary>
|
|||
/// 默认小程序模板
|
|||
/// </summary>
|
|||
public string DefaultTemplateId { get; set; } |
|||
/// <summary>
|
|||
/// 默认跳转小程序类型
|
|||
/// </summary>
|
|||
public string DefaultWeAppState { get; set; } = "developer"; |
|||
/// <summary>
|
|||
/// 默认小程序语言
|
|||
/// </summary>
|
|||
public string DefaultWeAppLanguage { get; set; } = "zh_CN"; |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp |
|||
{ |
|||
public interface IWeChatWeAppNotificationSender |
|||
{ |
|||
Task SendAsync(WeChatWeAppSendNotificationData notificationData, CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -1,123 +0,0 @@ |
|||
using LINGYUN.Abp.Features.LimitValidation; |
|||
using LINGYUN.Abp.Notifications.WeChat.WeApp.Features; |
|||
using LINGYUN.Abp.WeChat.MiniProgram; |
|||
using LINGYUN.Abp.WeChat.Token; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp |
|||
{ |
|||
public class WeChatWeAppNotificationSender : IWeChatWeAppNotificationSender, ITransientDependency |
|||
{ |
|||
public const string SendNotificationClientName = "WeChatWeAppSendNotificationClient"; |
|||
public ILogger<WeChatWeAppNotificationSender> Logger { get; set; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected AbpWeChatMiniProgramOptions MiniProgramOptions { get; } |
|||
protected IWeChatTokenProvider WeChatTokenProvider { get; } |
|||
public WeChatWeAppNotificationSender( |
|||
IJsonSerializer jsonSerializer, |
|||
IHttpClientFactory httpClientFactory, |
|||
IWeChatTokenProvider weChatTokenProvider, |
|||
IOptions<AbpWeChatMiniProgramOptions> miniProgramOptions) |
|||
{ |
|||
JsonSerializer = jsonSerializer; |
|||
HttpClientFactory = httpClientFactory; |
|||
WeChatTokenProvider = weChatTokenProvider; |
|||
MiniProgramOptions = miniProgramOptions.Value; |
|||
|
|||
Logger = NullLogger<WeChatWeAppNotificationSender>.Instance; |
|||
} |
|||
|
|||
[RequiresLimitFeature( // 检查消息发布功能限制
|
|||
WeChatWeAppFeatures.Notifications.PublishLimit, |
|||
WeChatWeAppFeatures.Notifications.PublishLimitInterval, |
|||
LimitPolicy.Month, |
|||
WeChatWeAppFeatures.Notifications.DefaultPublishLimit, |
|||
WeChatWeAppFeatures.Notifications.DefaultPublishLimitInterval |
|||
)] |
|||
public virtual async Task SendAsync(WeChatWeAppSendNotificationData notificationData, CancellationToken cancellationToken = default) |
|||
{ |
|||
var weChatToken = await WeChatTokenProvider.GetTokenAsync(MiniProgramOptions.AppId, MiniProgramOptions.AppSecret, cancellationToken); |
|||
var requestParamters = new Dictionary<string, string> |
|||
{ |
|||
{ "access_token", weChatToken.AccessToken } |
|||
}; |
|||
var weChatSendNotificationUrl = "https://api.weixin.qq.com"; |
|||
var weChatSendNotificationPath = "/cgi-bin/message/subscribe/send"; |
|||
var requestUrl = BuildRequestUrl(weChatSendNotificationUrl, weChatSendNotificationPath, requestParamters); |
|||
var responseContent = await MakeRequestAndGetResultAsync(requestUrl, notificationData, cancellationToken); |
|||
var weChatSenNotificationResponse = JsonSerializer.Deserialize<WeChatSendNotificationResponse>(responseContent); |
|||
|
|||
if (!weChatSenNotificationResponse.IsSuccessed) |
|||
{ |
|||
Logger.LogWarning("Send wechat we app subscribe message failed"); |
|||
Logger.LogWarning($"Error code: {weChatSenNotificationResponse.ErrorCode}, message: {weChatSenNotificationResponse.ErrorMessage}"); |
|||
} |
|||
// 失败是否抛出异常
|
|||
// weChatSenNotificationResponse.ThrowIfNotSuccess();
|
|||
} |
|||
protected virtual async Task<string> MakeRequestAndGetResultAsync(string url, WeChatWeAppSendNotificationData notificationData, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SendNotificationClientName); |
|||
var sendDataContent = JsonSerializer.Serialize(notificationData); |
|||
var requestContent = new StringContent(sendDataContent); |
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Post, url) |
|||
{ |
|||
Content = requestContent |
|||
}; |
|||
|
|||
var response = await client.SendAsync(requestMessage, cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException($"WeChat send subscribe message http request service returns error! HttpStatusCode: {response.StatusCode}, ReasonPhrase: {response.ReasonPhrase}"); |
|||
} |
|||
var resultContent = await response.Content.ReadAsStringAsync(); |
|||
|
|||
return resultContent; |
|||
} |
|||
|
|||
protected virtual string BuildRequestUrl(string uri, string path, IDictionary<string, string> paramters) |
|||
{ |
|||
var requestUrlBuilder = new StringBuilder(128); |
|||
requestUrlBuilder.Append(uri); |
|||
requestUrlBuilder.Append(path).Append("?"); |
|||
foreach (var paramter in paramters) |
|||
{ |
|||
requestUrlBuilder.AppendFormat("{0}={1}", paramter.Key, paramter.Value); |
|||
requestUrlBuilder.Append("&"); |
|||
} |
|||
requestUrlBuilder.Remove(requestUrlBuilder.Length - 1, 1); |
|||
return requestUrlBuilder.ToString(); |
|||
} |
|||
} |
|||
|
|||
public class WeChatSendNotificationResponse |
|||
{ |
|||
[JsonProperty("errcode")] |
|||
public int ErrorCode { get; set; } |
|||
|
|||
[JsonProperty("errmsg")] |
|||
public string ErrorMessage { get; set; } |
|||
|
|||
public bool IsSuccessed => ErrorCode == 0; |
|||
|
|||
public void ThrowIfNotSuccess() |
|||
{ |
|||
if (ErrorCode != 0) |
|||
{ |
|||
throw new AbpException($"Send wechat weapp notification error:{ErrorMessage}"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,103 +0,0 @@ |
|||
#pragma warning disable IDE1006 // 禁止编译器提示
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.WeApp |
|||
{ |
|||
public class WeChatWeAppSendNotificationData |
|||
{ |
|||
/// <summary>
|
|||
/// 接收者(用户)的 openid
|
|||
/// </summary>
|
|||
public string touser { get; set; } |
|||
/// <summary>
|
|||
/// 所需下发的订阅模板id
|
|||
/// </summary>
|
|||
public string template_id { get; set; } |
|||
/// <summary>
|
|||
/// 点击模板卡片后的跳转页面,仅限本小程序内的页面。
|
|||
/// 支持带参数,(示例index?foo=bar)。
|
|||
/// 该字段不填则模板无跳转
|
|||
/// </summary>
|
|||
public string page { get; set; } |
|||
/// <summary>
|
|||
/// 跳转小程序类型:
|
|||
/// developer为开发版;trial为体验版;formal为正式版;
|
|||
/// 默认为正式版
|
|||
/// </summary>
|
|||
public string miniprogram_state { get; set; } |
|||
/// <summary>
|
|||
/// 进入小程序查看”的语言类型,
|
|||
/// 支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),
|
|||
/// 默认为zh_CN
|
|||
/// </summary>
|
|||
public string lang { get; set; } |
|||
/// <summary>
|
|||
/// 模板内容,
|
|||
/// 格式形如 { "key1": { "value": any }, "key2": { "value": any } }
|
|||
/// </summary>
|
|||
public Dictionary<string, WeChatNotificationData> data { get; set; } |
|||
|
|||
public WeChatWeAppSendNotificationData() { } |
|||
public WeChatWeAppSendNotificationData(string openId, string templateId, string redirectPage = "", |
|||
string state = "formal", string miniLang = "zh_CN") |
|||
{ |
|||
touser = openId; |
|||
template_id = templateId; |
|||
page = redirectPage; |
|||
miniprogram_state = state; |
|||
lang = miniLang; |
|||
|
|||
data = new Dictionary<string, WeChatNotificationData>(); |
|||
} |
|||
/// <summary>
|
|||
/// 写入标准数据
|
|||
/// </summary>
|
|||
/// <param name="writeData"></param>
|
|||
/// <returns></returns>
|
|||
public WeChatWeAppSendNotificationData WriteStandardData(NotificationData writeData) |
|||
{ |
|||
foreach (var kv in writeData.Properties) |
|||
{ |
|||
if (!data.ContainsKey(kv.Key)) |
|||
{ |
|||
data.Add(kv.Key, new WeChatNotificationData(kv.Value)); |
|||
} |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
public WeChatWeAppSendNotificationData WriteData(string prefix, string key, object value) |
|||
{ |
|||
// 只截取符合标记的数据
|
|||
if (key.StartsWith(prefix)) |
|||
{ |
|||
key = key.Replace(prefix, ""); |
|||
if (!data.ContainsKey(key)) |
|||
{ |
|||
data.Add(key, new WeChatNotificationData(value)); |
|||
} |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
public WeChatWeAppSendNotificationData WriteData(string prefix, IDictionary<string, object> setData) |
|||
{ |
|||
foreach(var kv in setData) |
|||
{ |
|||
WriteData(prefix, kv.Key, kv.Value); |
|||
} |
|||
return this; |
|||
} |
|||
} |
|||
|
|||
public class WeChatNotificationData |
|||
{ |
|||
public object Value { get; } |
|||
|
|||
public WeChatNotificationData(object value) |
|||
{ |
|||
Value = value; |
|||
} |
|||
} |
|||
} |
|||
#pragma warning restore IDE1006
|
|||
@ -1,24 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="3.1.8" /> |
|||
<PackageReference Include="Volo.Abp.Caching" Version="3.3.0" /> |
|||
<PackageReference Include="Volo.Abp.Json" Version="3.3.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\wechat\LINGYUN.Abp.WeChat\LINGYUN.Abp.WeChat.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="LINGYUN\Abp\WeChat\Authorization\OpenId\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,72 +0,0 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
/// <summary>
|
|||
/// 与微信认证相关的静态(可变)常量
|
|||
/// </summary>
|
|||
public static class AbpWeChatAuthorizationConsts |
|||
{ |
|||
/// <summary>
|
|||
/// 微信授权名称
|
|||
/// </summary>
|
|||
public const string AuthenticationScheme = "WeChat"; |
|||
/// <summary>
|
|||
/// 微信授权显示名称
|
|||
/// </summary>
|
|||
public static string DisplayName = "WeChat"; |
|||
/// <summary>
|
|||
/// 微信个人信息标识
|
|||
/// </summary>
|
|||
public static string ProfileKey { get; set; } = "wechat.profile"; |
|||
/// <summary>
|
|||
/// 微信提供者标识
|
|||
/// </summary>
|
|||
public static string ProviderKey { get; set; } = AuthenticationScheme; |
|||
/// <summary>
|
|||
/// 回调地址
|
|||
/// </summary>
|
|||
public static string CallbackPath { get; set; } = "/signin-wechat"; |
|||
|
|||
/// <summary>
|
|||
/// 微信客户端外的网页登录
|
|||
/// </summary>
|
|||
public const string QrConnectEndpoint = "https://open.weixin.qq.com/connect/qrconnect"; |
|||
|
|||
/// <summary>
|
|||
/// 微信客户端内的网页登录
|
|||
/// </summary>
|
|||
public const string AuthorizationEndpoint = "https://open.weixin.qq.com/connect/oauth2/authorize"; |
|||
|
|||
/// <summary>
|
|||
/// 用户允许授权后通过返回的code换取access_token地址
|
|||
/// </summary>
|
|||
public const string TokenEndpoint = "https://api.weixin.qq.com/sns/oauth2/access_token"; |
|||
|
|||
/// <summary>
|
|||
/// 使用access_token获取用户个人信息地址
|
|||
/// </summary>
|
|||
public const string UserInformationEndpoint = "https://api.weixin.qq.com/sns/userinfo"; |
|||
/// <summary>
|
|||
/// 弹出授权页面,可通过openid拿到昵称、性别、所在地。
|
|||
/// 并且, 即使在未关注的情况下,只要用户授权,也能获取其信息
|
|||
/// <br />
|
|||
/// <br />
|
|||
/// 详询: <see cref="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html"/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// 以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。
|
|||
/// 但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息
|
|||
/// </remarks>
|
|||
public const string UserInfoScope = "snsapi_userinfo"; |
|||
/// <summary>
|
|||
/// 不弹出授权页面,直接跳转,只能获取用户openid
|
|||
/// <br />
|
|||
/// <br />
|
|||
/// 详询: <see cref="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html"/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// 以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。
|
|||
/// 用户感知的就是直接进入了回调页(往往是业务页面)
|
|||
/// </remarks>
|
|||
public const string LoginScope = "snsapi_login"; |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Polly; |
|||
using System; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpWeChatModule), |
|||
typeof(AbpJsonModule), |
|||
typeof(AbpCachingModule))] |
|||
public class AbpWeChatAuthorizationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpWeChatAuthorizationOptions>(configuration.GetSection("WeChat:Auth")); |
|||
|
|||
context.Services.AddHttpClient("WeChatRequestClient", options => |
|||
{ |
|||
options.BaseAddress = new Uri("https://api.weixin.qq.com"); |
|||
}).AddTransientHttpErrorPolicy(builder => |
|||
builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)))); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class AbpWeChatAuthorizationOptions |
|||
{ |
|||
public string AppId { get; set; } |
|||
|
|||
public string AppSecret { get; set; } |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
using LINGYUN.Abp.WeChat.Authorization.Settings; |
|||
using Microsoft.Extensions.Options; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Options; |
|||
using Volo.Abp.Settings; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class AbpWeChatAuthorizationOptionsFactory : AbpOptionsFactory<AbpWeChatAuthorizationOptions> |
|||
{ |
|||
protected ISettingProvider SettingProvider { get; } |
|||
|
|||
public AbpWeChatAuthorizationOptionsFactory( |
|||
ISettingProvider settingProvider, |
|||
IEnumerable<IConfigureOptions<AbpWeChatAuthorizationOptions>> setups, |
|||
IEnumerable<IPostConfigureOptions<AbpWeChatAuthorizationOptions>> postConfigures) |
|||
: base(setups, postConfigures) |
|||
{ |
|||
SettingProvider = settingProvider; |
|||
} |
|||
|
|||
public override AbpWeChatAuthorizationOptions Create(string name) |
|||
{ |
|||
var options = base.Create(name); |
|||
|
|||
OverrideOptions(options); |
|||
|
|||
return options; |
|||
} |
|||
|
|||
protected virtual void OverrideOptions(AbpWeChatAuthorizationOptions options) |
|||
{ |
|||
AsyncHelper.RunSync(() => OverrideOptionsAsync(options)); |
|||
} |
|||
|
|||
protected virtual async Task OverrideOptionsAsync(AbpWeChatAuthorizationOptions options) |
|||
{ |
|||
var appId = await SettingProvider.GetOrNullAsync(WeChatAuthorizationSettingNames.AppId); |
|||
var appSecret = await SettingProvider.GetOrNullAsync(WeChatAuthorizationSettingNames.AppSecret); |
|||
|
|||
options.AppId = appId ?? options.AppId; |
|||
options.AppSecret = appSecret ?? options.AppSecret; |
|||
} |
|||
} |
|||
} |
|||
@ -1,48 +0,0 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
/// <summary>
|
|||
/// 微信认证身份类型,可以像 <see cref="Volo.Abp.Security.Claims.AbpClaimTypes"/> 自行配置
|
|||
/// <br />
|
|||
/// See: <see cref="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html"/>
|
|||
/// </summary>
|
|||
public class AbpWeChatClaimTypes |
|||
{ |
|||
/// <summary>
|
|||
/// 用户的唯一标识
|
|||
/// </summary>
|
|||
public static string OpenId { get; set; } = "wx-openid"; // 可变更
|
|||
/// <summary>
|
|||
/// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
|
|||
/// </summary>
|
|||
public static string UnionId { get; set; } = "wx-unionid"; //可变更
|
|||
/// <summary>
|
|||
/// 用户昵称
|
|||
/// </summary>
|
|||
public static string NickName { get; set; } = "nickname"; |
|||
/// <summary>
|
|||
/// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
|
|||
/// </summary>
|
|||
public static string Sex { get; set; } = "sex"; |
|||
/// <summary>
|
|||
/// 国家,如中国为CN
|
|||
/// </summary>
|
|||
public static string Country { get; set; } = "country"; |
|||
/// <summary>
|
|||
/// 用户个人资料填写的省份
|
|||
/// </summary>
|
|||
public static string Province { get; set; } = "province"; |
|||
/// <summary>
|
|||
/// 普通用户个人资料填写的城市
|
|||
/// </summary>
|
|||
public static string City { get; set; } = "city"; |
|||
/// <summary>
|
|||
/// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。
|
|||
/// 若用户更换头像,原有头像URL将失效。
|
|||
/// </summary>
|
|||
public static string AvatarUrl { get; set; } = "avatar"; |
|||
/// <summary>
|
|||
/// 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
|
|||
/// </summary>
|
|||
public static string Privilege { get; set; } = "privilege"; |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
using LINGYUN.Abp.WeChat.Settings; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization.Settings |
|||
{ |
|||
public class WeChatAuthorizationSettingNames |
|||
{ |
|||
private const string Prefix = WeChatSettingNames.Prefix + ".Authorization"; |
|||
public static string AppId = Prefix + "." + nameof(AbpWeChatAuthorizationOptions.AppId); |
|||
public static string AppSecret = Prefix + "." + nameof(AbpWeChatAuthorizationOptions.AppSecret); |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
using LINGYUN.Abp.WeChat.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization.Settings |
|||
{ |
|||
public class WeChatAuthorizationSettingProvider : SettingDefinitionProvider |
|||
{ |
|||
public override void Define(ISettingDefinitionContext context) |
|||
{ |
|||
context.Add( |
|||
new SettingDefinition( |
|||
WeChatAuthorizationSettingNames.AppId, "", |
|||
L("DisplayName:WeChat.Auth.AppId"), |
|||
L("Description:WeChat.Auth.AppId"), |
|||
isVisibleToClients: true, |
|||
isEncrypted: true), |
|||
new SettingDefinition( |
|||
WeChatAuthorizationSettingNames.AppSecret, "", |
|||
L("DisplayName:WeChat.Auth.AppSecret"), |
|||
L("Description:WeChat.Auth.AppSecret"), |
|||
isVisibleToClients: true, |
|||
isEncrypted: true) |
|||
); |
|||
} |
|||
|
|||
protected ILocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<WeChatResource>(name); |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public interface IWeChatTokenProvider |
|||
{ |
|||
Task<WeChatToken> GetTokenAsync(CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
/// <summary>
|
|||
/// 微信令牌
|
|||
/// </summary>
|
|||
public class WeChatToken |
|||
{ |
|||
/// <summary>
|
|||
/// 访问令牌
|
|||
/// </summary>
|
|||
public string AccessToken { get; set; } |
|||
/// <summary>
|
|||
/// 过期时间,单位(s)
|
|||
/// </summary>
|
|||
public int ExpiresIn { get; set; } |
|||
public WeChatToken() |
|||
{ |
|||
|
|||
} |
|||
public WeChatToken(string token, int expiresIn) |
|||
{ |
|||
AccessToken = token; |
|||
ExpiresIn = expiresIn; |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class WeChatTokenCacheItem |
|||
{ |
|||
public string AppId { get; set; } |
|||
|
|||
public WeChatToken WeChatToken { get; set; } |
|||
public WeChatTokenCacheItem() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public WeChatTokenCacheItem(string appId, WeChatToken weChatToken) |
|||
{ |
|||
AppId = appId; |
|||
WeChatToken = weChatToken; |
|||
} |
|||
|
|||
public static string CalculateCacheKey(string provider, string appId) |
|||
{ |
|||
return "p:" + provider + ",o:" + appId; |
|||
} |
|||
} |
|||
} |
|||
@ -1,89 +0,0 @@ |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class WeChatTokenProvider : IWeChatTokenProvider, ISingletonDependency |
|||
{ |
|||
public ILogger<WeChatTokenProvider> Logger { get; set; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IDistributedCache<WeChatTokenCacheItem> Cache { get; } |
|||
protected AbpWeChatAuthorizationOptions Options { get; } |
|||
public WeChatTokenProvider( |
|||
IJsonSerializer jsonSerializer, |
|||
IHttpClientFactory httpClientFactory, |
|||
IOptions<AbpWeChatAuthorizationOptions> options, |
|||
IDistributedCache<WeChatTokenCacheItem> cache) |
|||
{ |
|||
JsonSerializer = jsonSerializer; |
|||
HttpClientFactory = httpClientFactory; |
|||
|
|||
Cache = cache; |
|||
Options = options.Value; |
|||
|
|||
Logger = NullLogger<WeChatTokenProvider>.Instance; |
|||
} |
|||
|
|||
public virtual async Task<WeChatToken> GetTokenAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
return (await GetCacheItemAsync("WeChatToken", Options.AppId, cancellationToken)).WeChatToken; |
|||
} |
|||
|
|||
protected virtual async Task<WeChatTokenCacheItem> GetCacheItemAsync(string provider, string appId, CancellationToken cancellationToken = default) |
|||
{ |
|||
var cacheKey = WeChatTokenCacheItem.CalculateCacheKey(provider, appId); |
|||
|
|||
Logger.LogDebug($"WeChatTokenProvider.GetCacheItemAsync: {cacheKey}"); |
|||
|
|||
var cacheItem = await Cache.GetAsync(cacheKey, token: cancellationToken); |
|||
|
|||
if (cacheItem != null) |
|||
{ |
|||
Logger.LogDebug($"Found in the cache: {cacheKey}"); |
|||
return cacheItem; |
|||
} |
|||
|
|||
Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}"); |
|||
|
|||
var client = HttpClientFactory.CreateClient("WeChatRequestClient"); |
|||
|
|||
var request = new WeChatTokenRequest |
|||
{ |
|||
BaseUrl = client.BaseAddress.AbsoluteUri, |
|||
AppSecret = Options.AppSecret, |
|||
AppId = Options.AppId, |
|||
GrantType = "client_credential" |
|||
}; |
|||
|
|||
var response = await client.RequestWeChatCodeTokenAsync(request, cancellationToken); |
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var weChatTokenResponse = JsonSerializer.Deserialize<WeChatTokenResponse>(responseContent); |
|||
var weChatToken = weChatTokenResponse.ToWeChatToken(); |
|||
cacheItem = new WeChatTokenCacheItem(appId, weChatToken); |
|||
|
|||
Logger.LogDebug($"Setting the cache item: {cacheKey}"); |
|||
|
|||
var cacheOptions = new DistributedCacheEntryOptions |
|||
{ |
|||
// 设置绝对过期时间为Token有效期剩余的二分钟
|
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(weChatToken.ExpiresIn - 120) |
|||
}; |
|||
|
|||
await Cache.SetAsync(cacheKey, cacheItem, cacheOptions, token: cancellationToken); |
|||
|
|||
Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); |
|||
|
|||
return cacheItem; |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
public class WeChatTokenRequest |
|||
{ |
|||
public string BaseUrl { get; set; } |
|||
public string GrantType { get; set; } |
|||
public string AppId { get; set; } |
|||
public string AppSecret { get; set; } |
|||
} |
|||
} |
|||
@ -1,41 +0,0 @@ |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.WeChat.Authorization |
|||
{ |
|||
/// <summary>
|
|||
/// 微信访问令牌返回对象
|
|||
/// </summary>
|
|||
public class WeChatTokenResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 错误码
|
|||
/// </summary>
|
|||
[JsonProperty("errcode")] |
|||
public int ErrorCode { get; set; } |
|||
/// <summary>
|
|||
/// 错误消息
|
|||
/// </summary>
|
|||
[JsonProperty("errmsg")] |
|||
public string ErrorMessage { get; set; } |
|||
/// <summary>
|
|||
/// 访问令牌
|
|||
/// </summary>
|
|||
[JsonProperty("access_token")] |
|||
public string AccessToken { get; set; } |
|||
/// <summary>
|
|||
/// 过期时间,单位(s)
|
|||
/// </summary>
|
|||
[JsonProperty("expires_in")] |
|||
public int ExpiresIn { get; set; } |
|||
|
|||
public WeChatToken ToWeChatToken() |
|||
{ |
|||
if(ErrorCode != 0) |
|||
{ |
|||
throw new AbpException(ErrorMessage); |
|||
} |
|||
return new WeChatToken(AccessToken, ExpiresIn); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
# LINGYUN.Abp.WeChat.Authorization |
|||
|
|||
废弃模块,模块层次不清晰,微信有多端平台,不同平台授权规则不一致 |
|||
|
|||
#### 注意 |
|||
|
|||
|
|||
|
|||
## 配置使用 |
|||
|
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpWeChatAuthorizationModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
using LINGYUN.Abp.WeChat.Authorization; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace System.Net.Http |
|||
{ |
|||
public static class HttpClientWeChatTokenRequestExtensions |
|||
{ |
|||
public static async Task<HttpResponseMessage> RequestWeChatCodeTokenAsync(this HttpMessageInvoker client, WeChatTokenRequest request, CancellationToken cancellationToken = default) |
|||
{ |
|||
var getResuestUrlBuilder = new StringBuilder(); |
|||
getResuestUrlBuilder.Append(request.BaseUrl); |
|||
getResuestUrlBuilder.Append("cgi-bin/token"); |
|||
getResuestUrlBuilder.Append("?grant_type=client_credential"); |
|||
getResuestUrlBuilder.AppendFormat("&appid={0}", request.AppId); |
|||
getResuestUrlBuilder.AppendFormat("&secret={0}", request.AppSecret); |
|||
|
|||
var getRequest = new HttpRequestMessage(HttpMethod.Get, getResuestUrlBuilder.ToString()); |
|||
HttpResponseMessage httpResponse; |
|||
|
|||
httpResponse = await client.SendAsync(getRequest, cancellationToken).ConfigureAwait(false); |
|||
|
|||
return httpResponse; |
|||
} |
|||
|
|||
public static async Task<HttpResponseMessage> RequestWeChatOpenIdAsync(this HttpMessageInvoker client, WeChatOpenIdRequest request, CancellationToken cancellationToken = default) |
|||
{ |
|||
var getResuestUrlBuiilder = new StringBuilder(); |
|||
getResuestUrlBuiilder.Append(request.BaseUrl); |
|||
getResuestUrlBuiilder.Append("sns/jscode2session"); |
|||
getResuestUrlBuiilder.AppendFormat("?appid={0}", request.AppId); |
|||
getResuestUrlBuiilder.AppendFormat("&secret={0}", request.Secret); |
|||
getResuestUrlBuiilder.AppendFormat("&js_code={0}", request.Code); |
|||
getResuestUrlBuiilder.Append("&grant_type=authorization_code"); |
|||
|
|||
var getRequest = new HttpRequestMessage(HttpMethod.Get, getResuestUrlBuiilder.ToString()); |
|||
HttpResponseMessage httpResponse; |
|||
|
|||
httpResponse = await client.SendAsync(getRequest, cancellationToken).ConfigureAwait(false); |
|||
|
|||
return httpResponse; |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
using LINGYUN.Abp.WeChat.Authorization; |
|||
|
|||
namespace Volo.Abp.Users |
|||
{ |
|||
public static class CurrentUserExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 获取用户微信id,如果不存在返回空值
|
|||
/// </summary>
|
|||
/// <param name="currentUser"></param>
|
|||
/// <returns></returns>
|
|||
public static string FindWeChatOpenId(this ICurrentUser currentUser) |
|||
{ |
|||
var weChatClaim = currentUser.FindClaim(AbpWeChatClaimTypes.OpenId); |
|||
if (weChatClaim == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return weChatClaim.Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取微信用户主体id,如果不存在返回空值
|
|||
/// </summary>
|
|||
/// <param name="currentUser"></param>
|
|||
/// <returns></returns>
|
|||
public static string FindWeChatUnionId(this ICurrentUser currentUser) |
|||
{ |
|||
var weChatClaim = currentUser.FindClaim(AbpWeChatClaimTypes.UnionId); |
|||
if (weChatClaim == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return weChatClaim.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> |
|||
<PackageReference Include="NSubstitute" Version="4.2.1" /> |
|||
<PackageReference Include="Shouldly" Version="3.0.2" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\common\LINGYUN.Abp.Notifications.Sms\LINGYUN.Abp.Notifications.Sms.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Notifications.Tests\LINGYUN.Abp.Notifications.Tests.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
public class AbpNotificationsSmsTestsBase : AbpTestsBase<AbpNotificationsSmsTestsModule> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpNotificationsSmsModule), |
|||
typeof(AbpNotificationsTestsModule), |
|||
typeof(AbpTestsBaseModule))] |
|||
public class AbpNotificationsSmsTestsModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// 改变默认数据前缀方法
|
|||
PreConfigure<AbpNotificationsSmsOptions>(options => |
|||
{ |
|||
options.TemplateParamsPrefix = "[sms-override]"; |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// 自定义数据处理方法
|
|||
Configure<AbpNotificationOptions>(options => |
|||
{ |
|||
// 这条通知返回标准化的通知
|
|||
options.NotificationDataMappings |
|||
.Mapping( |
|||
SmsNotificationPublishProvider.ProviderName, |
|||
NotificationsTestsNames.Test2, |
|||
data => NotificationData.ToStandardData(data)); |
|||
|
|||
// 这条通知不做任何处理
|
|||
options.NotificationDataMappings |
|||
.Mapping( |
|||
SmsNotificationPublishProvider.ProviderName, |
|||
NotificationsTestsNames.Test3, |
|||
data => data); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using System; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Sms |
|||
{ |
|||
public class SmsNotificationDataMapping_Tests : AbpNotificationsSmsTestsBase |
|||
{ |
|||
private readonly NotificationData _notificationData; |
|||
protected AbpNotificationOptions NotificationOptions { get; } |
|||
protected AbpNotificationsSmsOptions NotificationSmsOptions { get; } |
|||
public SmsNotificationDataMapping_Tests() |
|||
{ |
|||
NotificationOptions = GetRequiredService<IOptions<AbpNotificationOptions>>().Value; |
|||
NotificationSmsOptions = GetRequiredService<IOptions<AbpNotificationsSmsOptions>>().Value; |
|||
|
|||
_notificationData = new NotificationData(); |
|||
InitNotificationData(_notificationData); |
|||
} |
|||
|
|||
private void InitNotificationData(NotificationData data) |
|||
{ |
|||
data.WriteStandardData("title", "message", DateTime.Now, "formUser", "description"); |
|||
data.WriteStandardData(NotificationSmsOptions.TemplateParamsPrefix, "phoneNumber", "13800138000"); |
|||
data.WriteStandardData(NotificationSmsOptions.TemplateParamsPrefix, "template", "SM_202011250901"); |
|||
data.TrySetData("otherDataKey", "otherDataValue"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Mapping_Sms_Notification_Data_Test() |
|||
{ |
|||
var mappingSmsItem = NotificationOptions |
|||
.NotificationDataMappings |
|||
.GetMapItemOrDefault(SmsNotificationPublishProvider.ProviderName, NotificationsTestsNames.Test1); |
|||
|
|||
mappingSmsItem.ShouldNotBeNull(); |
|||
|
|||
var mappingSmsData = mappingSmsItem.MappingFunc(_notificationData); |
|||
mappingSmsData.TryGetData("phoneNumber").ShouldNotBeNull(); |
|||
mappingSmsData.TryGetData("phoneNumber").ToString().ShouldBe("13800138000"); |
|||
|
|||
mappingSmsData.TryGetData("template").ShouldNotBeNull(); |
|||
mappingSmsData.TryGetData("template").ToString().ShouldBe("SM_202011250901"); |
|||
|
|||
// 按照预定义规则,这条数据被丢弃
|
|||
mappingSmsData.TryGetData("otherDataKey").ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Mapping_Standard_Notification_Data_Test() |
|||
{ |
|||
var mappingStandardItem = NotificationOptions |
|||
.NotificationDataMappings |
|||
.GetMapItemOrDefault(SmsNotificationPublishProvider.ProviderName, NotificationsTestsNames.Test2); |
|||
|
|||
var mappingStandardData = mappingStandardItem.MappingFunc(_notificationData); |
|||
|
|||
// 按照自定义规则,其他数据被丢弃
|
|||
mappingStandardData.TryGetData("phoneNumber").ShouldBeNull(); |
|||
mappingStandardData.TryGetData("template").ShouldBeNull(); |
|||
mappingStandardData.TryGetData("otherDataKey").ShouldBeNull(); |
|||
mappingStandardData.Properties.Count.ShouldBe(6); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Mapping_Origin_Notification_Data_Test() |
|||
{ |
|||
var mappingOriginItem = NotificationOptions |
|||
.NotificationDataMappings |
|||
.GetMapItemOrDefault(SmsNotificationPublishProvider.ProviderName, NotificationsTestsNames.Test3); |
|||
|
|||
var mappingOriginData = mappingOriginItem.MappingFunc(_notificationData); |
|||
|
|||
// 按照自定义规则,所有数据被保留
|
|||
mappingOriginData.TryGetData(NotificationSmsOptions.TemplateParamsPrefix + "phoneNumber").ShouldNotBeNull(); |
|||
mappingOriginData.TryGetData(NotificationSmsOptions.TemplateParamsPrefix + "template").ShouldNotBeNull(); |
|||
mappingOriginData.TryGetData("otherDataKey").ShouldNotBeNull(); |
|||
mappingOriginData.Properties.Count.ShouldBe(9); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> |
|||
<PackageReference Include="NSubstitute" Version="4.2.1" /> |
|||
<PackageReference Include="Shouldly" Version="3.0.2" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\common\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class AbpNotificationsTestsBase : AbpTestsBase<AbpNotificationsTestsModule> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpNotificationModule), |
|||
typeof(AbpTestsBaseModule))] |
|||
public class AbpNotificationsTestsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class NotificationsTestsDefinitionProvider : NotificationDefinitionProvider |
|||
{ |
|||
public override void Define(INotificationDefinitionContext context) |
|||
{ |
|||
var group = context.AddGroup(NotificationsTestsNames.GroupName); |
|||
|
|||
group.AddNotification(NotificationsTestsNames.Test1, |
|||
notificationType: NotificationType.Application, |
|||
lifetime: NotificationLifetime.OnlyOne); |
|||
|
|||
group.AddNotification(NotificationsTestsNames.Test2, |
|||
notificationType: NotificationType.Application, |
|||
lifetime: NotificationLifetime.Persistent); |
|||
|
|||
group.AddNotification(NotificationsTestsNames.Test3, |
|||
notificationType: NotificationType.User, |
|||
lifetime: NotificationLifetime.OnlyOne); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using Shouldly; |
|||
using Volo.Abp.Localization; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class NotificationsTestsDefinitionProvider_Tests : AbpNotificationsTestsBase |
|||
{ |
|||
protected INotificationDefinitionManager NotificationDefinitionManager { get; } |
|||
|
|||
public NotificationsTestsDefinitionProvider_Tests() |
|||
{ |
|||
NotificationDefinitionManager = GetRequiredService<INotificationDefinitionManager>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetGroups_Test() |
|||
{ |
|||
var groups = NotificationDefinitionManager.GetGroups(); |
|||
groups.Count.ShouldBe(1); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetAll_Test() |
|||
{ |
|||
var notifications = NotificationDefinitionManager.GetAll(); |
|||
notifications.Count.ShouldBe(3); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetOrNull_Test() |
|||
{ |
|||
NotificationDefinitionManager.GetOrNull(NotificationsTestsNames.Test2).ShouldNotBeNull(); |
|||
NotificationDefinitionManager.GetOrNull(NotificationsTestsNames.Test3).ShouldNotBeNull(); |
|||
NotificationDefinitionManager.GetOrNull("NullOfNotification").ShouldBeNull(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(NotificationsTestsNames.Test1)] |
|||
public void Get_Test(string name) |
|||
{ |
|||
var notification = NotificationDefinitionManager.Get(name); |
|||
notification.Name.ShouldBe(name); |
|||
notification.DisplayName.ShouldBeOfType<FixedLocalizableString>(); |
|||
notification.Description.ShouldBeNull(); |
|||
notification.AllowSubscriptionToClients.ShouldBeFalse(); |
|||
notification.NotificationLifetime.ShouldBe(NotificationLifetime.OnlyOne); |
|||
notification.NotificationType.ShouldBe(NotificationType.Application); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public static class NotificationsTestsNames |
|||
{ |
|||
public const string GroupName = "Abp.Notifications"; |
|||
|
|||
public const string Test1 = GroupName + ".Test1"; |
|||
|
|||
public const string Test2 = GroupName + ".Test2"; |
|||
|
|||
public const string Test3 = GroupName + ".Test3"; |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> |
|||
<PackageReference Include="NSubstitute" Version="4.2.1" /> |
|||
<PackageReference Include="Shouldly" Version="3.0.2" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\wechat\LINGYUN.Abp.Notifications.WeChat.MiniProgram\LINGYUN.Abp.Notifications.WeChat.MiniProgram.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Notifications.Tests\LINGYUN.Abp.Notifications.Tests.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.MiniProgram |
|||
{ |
|||
public class AbpNotificationsWeChatMiniProgramTestsBase : AbpTestsBase<AbpNotificationsWeChatMiniProgramTestsModule> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.MiniProgram |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpNotificationsWeChatMiniProgramModule), |
|||
typeof(AbpNotificationsTestsModule), |
|||
typeof(AbpTestsBaseModule))] |
|||
public class AbpNotificationsWeChatMiniProgramTestsModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpNotificationsWeChatMiniProgramOptions>(options => |
|||
{ |
|||
options.DefaultMsgPrefix = "[wmp-override]"; |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// 自定义数据处理方法
|
|||
Configure<AbpNotificationOptions>(options => |
|||
{ |
|||
// 这条通知返回标准化的通知
|
|||
options.NotificationDataMappings |
|||
.Mapping( |
|||
WeChatMiniProgramNotificationPublishProvider.ProviderName, |
|||
NotificationsTestsNames.Test2, |
|||
data => NotificationData.ToStandardData(data)); |
|||
|
|||
// 这条通知不做任何处理
|
|||
options.NotificationDataMappings |
|||
.Mapping( |
|||
WeChatMiniProgramNotificationPublishProvider.ProviderName, |
|||
NotificationsTestsNames.Test3, |
|||
data => data); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using System; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.WeChat.MiniProgram |
|||
{ |
|||
public class WeChatMiniProgramNotificationDataMapping_Tests : AbpNotificationsWeChatMiniProgramTestsBase |
|||
{ |
|||
private readonly NotificationData _notificationData; |
|||
protected AbpNotificationOptions NotificationOptions { get; } |
|||
protected AbpNotificationsWeChatMiniProgramOptions NotificationWeChatMiniProgramOptions { get; } |
|||
public WeChatMiniProgramNotificationDataMapping_Tests() |
|||
{ |
|||
NotificationOptions = GetRequiredService<IOptions<AbpNotificationOptions>>().Value; |
|||
NotificationWeChatMiniProgramOptions = GetRequiredService<IOptions<AbpNotificationsWeChatMiniProgramOptions>>().Value; |
|||
|
|||
_notificationData = new NotificationData(); |
|||
InitNotificationData(_notificationData); |
|||
} |
|||
|
|||
private void InitNotificationData(NotificationData data) |
|||
{ |
|||
data.WriteStandardData("title", "message", DateTime.Now, "formUser", "description"); |
|||
data.WriteStandardData(NotificationWeChatMiniProgramOptions.DefaultMsgPrefix, "openid", "TEST"); |
|||
data.TrySetData("otherDataKey", "otherDataValue"); |
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public void Mapping_WeChatMiniProgram_Notification_Data_Test() |
|||
{ |
|||
var mappingOpenIdItem = NotificationOptions |
|||
.NotificationDataMappings |
|||
.GetMapItemOrDefault(WeChatMiniProgramNotificationPublishProvider.ProviderName, NotificationsTestsNames.Test1); |
|||
|
|||
mappingOpenIdItem.ShouldNotBeNull(); |
|||
|
|||
var mappingOpenIdData = mappingOpenIdItem.MappingFunc(_notificationData); |
|||
mappingOpenIdData.TryGetData("openid").ShouldNotBeNull(); |
|||
mappingOpenIdData.TryGetData("openid").ToString().ShouldBe("TEST"); |
|||
|
|||
// 按照预定义规则,这条数据被丢弃
|
|||
mappingOpenIdData.TryGetData("otherDataKey").ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Mapping_Standard_Notification_Data_Test() |
|||
{ |
|||
var mappingStandardItem = NotificationOptions |
|||
.NotificationDataMappings |
|||
.GetMapItemOrDefault(WeChatMiniProgramNotificationPublishProvider.ProviderName, NotificationsTestsNames.Test2); |
|||
|
|||
var mappingStandardData = mappingStandardItem.MappingFunc(_notificationData); |
|||
|
|||
// 按照自定义规则,其他数据被丢弃
|
|||
mappingStandardData.TryGetData("openid").ShouldBeNull(); |
|||
mappingStandardData.TryGetData("otherDataKey").ShouldBeNull(); |
|||
mappingStandardData.Properties.Count.ShouldBe(6); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Mapping_Origin_Notification_Data_Test() |
|||
{ |
|||
var mappingOriginItem = NotificationOptions |
|||
.NotificationDataMappings |
|||
.GetMapItemOrDefault(WeChatMiniProgramNotificationPublishProvider.ProviderName, NotificationsTestsNames.Test3); |
|||
|
|||
var mappingOriginData = mappingOriginItem.MappingFunc(_notificationData); |
|||
|
|||
// 按照自定义规则,所有数据被保留
|
|||
mappingOriginData.TryGetData(NotificationWeChatMiniProgramOptions.DefaultMsgPrefix + "openid").ShouldNotBeNull(); |
|||
mappingOriginData.TryGetData("otherDataKey").ShouldNotBeNull(); |
|||
mappingOriginData.Properties.Count.ShouldBe(8); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue