diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj
index 307851aa6..fbae27373 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj
+++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj
@@ -19,4 +19,8 @@
+
+
+
+
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs
index 97a959382..a1b4e7c4c 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs
@@ -1,4 +1,5 @@
using LINGYUN.Abp.IdentityServer.WeChatValidator;
+using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.IdentityServer;
using Volo.Abp.IdentityServer.Localization;
@@ -8,7 +9,9 @@ using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.IdentityServer
{
- [DependsOn(typeof(AbpIdentityServerDomainModule))]
+ [DependsOn(
+ typeof(AbpWeChatAuthorizationModule),
+ typeof(AbpIdentityServerDomainModule))]
public class AbpIdentityServerWeChatValidatorModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
@@ -23,7 +26,6 @@ namespace LINGYUN.Abp.IdentityServer
{
var configuration = context.Services.GetConfiguration();
- Configure(configuration.GetSection("AuthServer:WeChat"));
Configure(configuration.GetSection("WeChat:Signature"));
context.Services.AddHttpClient(WeChatValidatorConsts.WeChatValidatorClientName, options =>
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs
index c4583857e..30955d77f 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs
@@ -3,6 +3,7 @@ using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;
+using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
@@ -22,7 +23,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
public class WeChatTokenGrantValidator : IExtensionGrantValidator
{
protected ILogger Logger { get; }
- protected AbpWeChatValidatorOptions Options { get; }
+ protected AbpWeChatOptions Options { get; }
protected IHttpClientFactory HttpClientFactory{ get; }
protected IEventService EventService { get; }
protected IIdentityUserRepository UserRepository { get; }
@@ -40,7 +41,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
SignInManager signInManager,
IStringLocalizer stringLocalizer,
PhoneNumberTokenProvider phoneNumberTokenProvider,
- IOptionsSnapshot options,
+ IOptions options,
ILogger logger)
{
Logger = logger;
@@ -77,7 +78,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
return;
}
var httpClient = HttpClientFactory.CreateClient(WeChatValidatorConsts.WeChatValidatorClientName);
- var httpRequest = new WeChatTokenRequest
+ var httpRequest = new WeChatOpenIdRequest
{
Code = wechatCode,
AppId = Options.AppId,
@@ -85,19 +86,19 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
BaseUrl = httpClient.BaseAddress.AbsoluteUri
};
- var wechatTokenResponse = await httpClient.RequestWeChatCodeTokenAsync(httpRequest);
- if (wechatTokenResponse.IsError)
+ var wechatOpenIdResponse = await httpClient.RequestWeChatCodeTokenAsync(httpRequest);
+ if (wechatOpenIdResponse.IsError)
{
Logger.LogWarning("Authentication failed for token: {0}, reason: invalid token", wechatCode);
- Logger.LogWarning("WeChat auth failed, error: {0}", wechatTokenResponse.ErrorMessage);
+ Logger.LogWarning("WeChat auth failed, error: {0}", wechatOpenIdResponse.ErrorMessage);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatTokenInvalid"]);
return;
}
- var currentUser = await UserManager.FindByNameAsync(wechatTokenResponse.OpenId);
+ var currentUser = await UserManager.FindByNameAsync(wechatOpenIdResponse.OpenId);
if(currentUser == null)
{
- Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", wechatTokenResponse.OpenId);
+ Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", wechatOpenIdResponse.OpenId);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatNotRegister"]);
return;
@@ -109,9 +110,9 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
{
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString()));
}
- additionalClaims.Add(new Claim(WeChatValidatorConsts.ClaimTypes.OpenId, wechatTokenResponse.OpenId));
+ additionalClaims.Add(new Claim(WeChatValidatorConsts.ClaimTypes.OpenId, wechatOpenIdResponse.OpenId));
- await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatTokenResponse.OpenId, null));
+ await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenIdResponse.OpenId, null));
context.Result = new GrantValidationResult(sub,
WeChatValidatorConsts.AuthenticationMethods.BasedWeChatAuthentication, additionalClaims.ToArray());
}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs
index 169520000..bea6631b8 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs
@@ -7,7 +7,7 @@ namespace System.Net.Http
{
public static class HttpClientTokenRequestExtensions
{
- public static async Task RequestWeChatCodeTokenAsync(this HttpMessageInvoker client, WeChatTokenRequest request, CancellationToken cancellationToken = default)
+ public static async Task RequestWeChatCodeTokenAsync(this HttpMessageInvoker client, WeChatOpenIdRequest request, CancellationToken cancellationToken = default)
{
var getResuestUrlBuiilder = new StringBuilder();
getResuestUrlBuiilder.Append(request.BaseUrl);
@@ -24,9 +24,9 @@ namespace System.Net.Http
}
catch (Exception ex)
{
- return ProtocolResponse.FromException(ex);
+ return ProtocolResponse.FromException(ex);
}
- return await ProtocolResponse.FromHttpResponseAsync(httpResponse).ConfigureAwait(false);
+ return await ProtocolResponse.FromHttpResponseAsync(httpResponse).ConfigureAwait(false);
}
}
}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatTokenRequest.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdRequest.cs
similarity index 85%
rename from aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatTokenRequest.cs
rename to aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdRequest.cs
index 14088f187..5dddab29e 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatTokenRequest.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdRequest.cs
@@ -1,6 +1,6 @@
namespace System.Net.Http
{
- public class WeChatTokenRequest
+ public class WeChatOpenIdRequest
{
public string BaseUrl { get; set; }
public string AppId { get; set; }
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatTokenResponse.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs
similarity index 96%
rename from aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatTokenResponse.cs
rename to aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs
index 983a0bc4b..8e13e7d83 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatTokenResponse.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs
@@ -2,7 +2,7 @@
namespace System.Net.Http
{
- public class WeChatTokenResponse : ProtocolResponse
+ public class WeChatOpenIdResponse : ProtocolResponse
{
///
/// 用户唯一标识
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.csproj b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.WeApp.csproj
similarity index 59%
rename from aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.csproj
rename to aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.WeApp.csproj
index 4ed83b791..56fffe29c 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.csproj
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN.Abp.Notifications.WeChat.WeApp.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
@@ -6,6 +6,7 @@
+
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpNotificationsWeChatWeAppModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpNotificationsWeChatWeAppModule.cs
new file mode 100644
index 000000000..146da9d22
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpNotificationsWeChatWeAppModule.cs
@@ -0,0 +1,30 @@
+using LINGYUN.Abp.WeChat.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+using Polly;
+using System;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.WeChat.WeApp
+{
+ [DependsOn(
+ typeof(AbpWeChatAuthorizationModule),
+ typeof(AbpNotificationModule))]
+ public class AbpNotificationsWeChatWeAppModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Services.GetConfiguration();
+ Configure(configuration.GetSection("Notifications:WeChat:WeApp"));
+
+ // TODO:是否有必要启用重试机制?
+ context.Services.AddHttpClient(WeChatWeAppNotificationSender.SendNotificationClientName)
+ .AddTransientHttpErrorPolicy(builder =>
+ builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i))));
+
+ Configure(options =>
+ {
+ options.PublishProviders.Add();
+ });
+ }
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpWeChatWeAppNotificationOptions.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpWeChatWeAppNotificationOptions.cs
new file mode 100644
index 000000000..17f20e7be
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/AbpWeChatWeAppNotificationOptions.cs
@@ -0,0 +1,18 @@
+namespace LINGYUN.Abp.Notifications.WeChat.WeApp
+{
+ public class AbpWeChatWeAppNotificationOptions
+ {
+ ///
+ /// 默认小程序模板
+ ///
+ public string DefaultTemplateId { get; set; }
+ ///
+ /// 默认跳转小程序类型
+ ///
+ public string DefaultWeAppState { get; set; } = "developer";
+ ///
+ /// 默认小程序语言
+ ///
+ public string DefaultWeAppLanguage { get; set; } = "zh_CN";
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/IWeChatWeAppNotificationSender.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/IWeChatWeAppNotificationSender.cs
new file mode 100644
index 000000000..de5d8c500
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/IWeChatWeAppNotificationSender.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.WeChat.WeApp
+{
+ public interface IWeChatWeAppNotificationSender
+ {
+ Task SendAsync(WeChatWeAppSendNotificationData notificationData);
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs
new file mode 100644
index 000000000..837e8c7b2
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs
@@ -0,0 +1,76 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.WeChat.WeApp
+{
+ ///
+ /// 微信小程序消息推送提供者
+ ///
+ public class WeChatWeAppNotificationPublishProvider : NotificationPublishProvider
+ {
+ public override string Name => "WeChat.WeApp";
+
+ protected IWeChatWeAppNotificationSender NotificationSender { get; }
+ protected AbpWeChatWeAppNotificationOptions Options { get; }
+ public WeChatWeAppNotificationPublishProvider(
+ IServiceProvider serviceProvider,
+ IWeChatWeAppNotificationSender notificationSender,
+ IOptions options)
+ : base(serviceProvider)
+ {
+ Options = options.Value;
+ NotificationSender = notificationSender;
+ }
+
+ public override async Task PublishAsync(NotificationInfo notification, IEnumerable identifiers)
+ {
+ // step1 默认微信openid绑定的就是username,
+ // 如果不是,需要自行处理openid获取逻辑
+
+ // step2 调用微信消息推送接口
+ foreach (var identifier in identifiers)
+ {
+ await SendWeChatTemplateMessagAsync(notification, identifier);
+ }
+ }
+
+ protected virtual async Task SendWeChatTemplateMessagAsync(NotificationInfo notification, UserIdentifier identifier)
+ {
+ var templateId = GetOrDefaultTemplateId(notification.Data);
+ Logger.LogDebug($"Get wechat weapp template id: {templateId}");
+
+ var redirect = GetOrDefault(notification.Data, "RedirectPage", "");
+ Logger.LogDebug($"Get wechat weapp redirect page: {redirect}");
+
+ var weAppState = GetOrDefault(notification.Data, "WeAppState", Options.DefaultWeAppState);
+ Logger.LogDebug($"Get wechat weapp state: {weAppState}");
+
+ var weAppLang = GetOrDefault(notification.Data, "WeAppLanguage", Options.DefaultWeAppLanguage);
+ Logger.LogDebug($"Get wechat weapp language: {weAppLang}");
+
+ var weChatWeAppNotificationData = new WeChatWeAppSendNotificationData(identifier.UserName,
+ templateId, redirect, weAppState, weAppLang);
+
+ Logger.LogDebug($"Sending wechat weapp notification: {notification.Name}");
+ // 发送小程序订阅消息
+ await NotificationSender.SendAsync(weChatWeAppNotificationData);
+ }
+
+ protected string GetOrDefaultTemplateId(NotificationData data)
+ {
+ return GetOrDefault(data, "TemplateId", Options.DefaultTemplateId);
+ }
+
+ protected string GetOrDefault(NotificationData data, string key, string defaultValue)
+ {
+ if (data.Properties.TryGetValue(key, out object value))
+ {
+ return value.ToString();
+ }
+ return defaultValue;
+ }
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationSender.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationSender.cs
new file mode 100644
index 000000000..ba912aaa4
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationSender.cs
@@ -0,0 +1,93 @@
+using LINGYUN.Abp.WeChat.Authorization;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+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";
+ protected IHttpClientFactory HttpClientFactory { get; }
+ protected IJsonSerializer JsonSerializer { get; }
+ protected IWeChatTokenProvider WeChatTokenProvider { get; }
+ public WeChatWeAppNotificationSender(
+ IJsonSerializer jsonSerializer,
+ IHttpClientFactory httpClientFactory,
+ IWeChatTokenProvider weChatTokenProvider)
+ {
+ JsonSerializer = jsonSerializer;
+ HttpClientFactory = httpClientFactory;
+ WeChatTokenProvider = weChatTokenProvider;
+ }
+
+ public virtual async Task SendAsync(WeChatWeAppSendNotificationData notificationData)
+ {
+ var weChatToken = await WeChatTokenProvider.GetTokenAsync();
+ var requestParamters = new Dictionary
+ {
+ { "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);
+ var weChatSenNotificationResponse = JsonSerializer.Deserialize(responseContent);
+ weChatSenNotificationResponse.ThrowIfNotSuccess();
+ }
+ protected virtual async Task MakeRequestAndGetResultAsync(string url, WeChatWeAppSendNotificationData notificationData)
+ {
+ var client = HttpClientFactory.CreateClient(SendNotificationClientName);
+ var requestContent = new StringContent(JsonSerializer.Serialize(notificationData));
+ var requestMessage = new HttpRequestMessage(HttpMethod.Post, url)
+ {
+ Content = requestContent
+ };
+
+ var response = await client.SendAsync(requestMessage);
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new AbpException($"Baidu 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 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 void ThrowIfNotSuccess()
+ {
+ if (ErrorCode != 0)
+ {
+ throw new AbpException($"Send wechat weapp notification error:{ErrorMessage}");
+ }
+ }
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppSendNotificationData.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppSendNotificationData.cs
new file mode 100644
index 000000000..27852d822
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppSendNotificationData.cs
@@ -0,0 +1,45 @@
+#pragma warning disable IDE1006 // 禁止编译器提示
+namespace LINGYUN.Abp.Notifications.WeChat.WeApp
+{
+ public class WeChatWeAppSendNotificationData
+ {
+ ///
+ /// 接收者(用户)的 openid
+ ///
+ public string touser { get; set; }
+ ///
+ /// 所需下发的订阅模板id
+ ///
+ public string template_id { get; set; }
+ ///
+ /// 点击模板卡片后的跳转页面,仅限本小程序内的页面。
+ /// 支持带参数,(示例index?foo=bar)。
+ /// 该字段不填则模板无跳转
+ ///
+ public string page { get; set; }
+ ///
+ /// 跳转小程序类型:
+ /// developer为开发版;trial为体验版;formal为正式版;
+ /// 默认为正式版
+ ///
+ public string miniprogram_state { get; set; }
+ ///
+ /// 进入小程序查看”的语言类型,
+ /// 支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),
+ /// 默认为zh_CN
+ ///
+ public string lang { 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;
+ }
+ }
+}
+#pragma warning restore IDE1006
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeChatNotificationPublishProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeChatNotificationPublishProvider.cs
deleted file mode 100644
index f98ddfcc6..000000000
--- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeChatNotificationPublishProvider.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace LINGYUN.Abp.Notifications.WeChat
-{
- public class WeChatNotificationPublishProvider : NotificationPublishProvider
- {
- public override string Name => "WeChat";
-
- public WeChatNotificationPublishProvider(
- IServiceProvider serviceProvider)
- : base(serviceProvider)
- {
-
- }
-
- public override async Task PublishAsync(NotificationInfo notification, IEnumerable identifiers)
- {
- // step1 默认微信openid绑定的就是username,如果不是,那就根据userid去获取
-
- // step2 调用微信消息推送接口
- }
- }
-}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDispatcher.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDispatcher.cs
index 2ed1301db..5e8e3b6da 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDispatcher.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationDispatcher.cs
@@ -3,6 +3,9 @@ using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications
{
+ ///
+ /// 通知发送者接口
+ ///
public interface INotificationDispatcher
{
///
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationSubscriptionManager.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationSubscriptionManager.cs
new file mode 100644
index 000000000..f1d820598
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationSubscriptionManager.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications
+{
+ ///
+ /// 通知订阅管理器
+ ///
+ public interface INotificationSubscriptionManager
+ {
+ ///
+ /// 是否已订阅
+ ///
+ /// 租户
+ /// 用户标识
+ /// 通知名称
+ ///
+ Task IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName);
+ ///
+ /// 订阅通知
+ ///
+ /// 租户
+ /// 用户标识
+ /// 通知名称
+ ///
+ Task SubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName);
+ ///
+ /// 订阅通知
+ ///
+ /// 租户
+ /// 用户标识列表
+ /// 通知名称
+ ///
+ Task SubscribeAsync(Guid? tenantId, IEnumerable identifiers, string notificationName);
+ ///
+ /// 取消订阅
+ ///
+ /// 租户
+ /// 用户标识
+ /// 通知名称
+ ///
+ Task UnsubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName);
+ ///
+ /// 获取通知被订阅用户列表
+ ///
+ /// 租户
+ /// 通知名称
+ ///
+ Task> GetSubscriptionsAsync(Guid? tenantId, string notificationName);
+ ///
+ /// 获取用户订阅列表
+ ///
+ /// 租户
+ /// 用户标识
+ ///
+ Task> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId);
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs
index 6b5bfa6e0..36fea5853 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs
@@ -30,7 +30,14 @@ namespace LINGYUN.Abp.Notifications.Internal
NotificationSeverity notificationSeverity = NotificationSeverity.Info)
{
// 获取自定义的通知
- var defineNotification = _notificationDefinitionManager.Get(notificationName);
+ var defineNotification = _notificationDefinitionManager.GetOrNull(notificationName);
+
+ // 没有定义的通知,应该也要能发布、订阅,
+ // 比如订单之类的,是以订单编号为通知名称,这是动态的,没法自定义
+ if(defineNotification == null)
+ {
+ defineNotification = new NotificationDefinition(notificationName);
+ }
var notificationInfo = new NotificationInfo
{
@@ -75,31 +82,38 @@ namespace LINGYUN.Abp.Notifications.Internal
protected async Task PublishFromProvidersAsync(IEnumerable 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)
{
try
{
+ Logger.LogDebug($"Sending notification with provider {provider.Name}");
+
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 {0}", provider.Name);
- Logger.LogWarning("Error message:{0}", ex.Message);
+ Logger.LogWarning($"Send notification error with provider {provider.Name}");
+ Logger.LogWarning($"Error message:{ex.Message}");
- Logger.LogTrace(ex, "Send notification error with provider {0}", provider.Name);
+ Logger.LogTrace(ex, $"Send notification error with provider { provider.Name}");
}
}
}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/NotificationSubscriptionManager.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/NotificationSubscriptionManager.cs
new file mode 100644
index 000000000..508b400ef
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/NotificationSubscriptionManager.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Notifications.Internal
+{
+ internal class NotificationSubscriptionManager : INotificationSubscriptionManager, ITransientDependency
+ {
+ private readonly INotificationStore _store;
+
+ public NotificationSubscriptionManager(
+ INotificationStore store)
+ {
+ _store = store;
+ }
+
+ public virtual async Task> GetSubscriptionsAsync(Guid? tenantId, string notificationName)
+ {
+ return await _store.GetSubscriptionsAsync(tenantId, notificationName);
+ }
+
+ public virtual async Task> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId)
+ {
+ return await _store.GetUserSubscriptionsAsync(tenantId, userId);
+ }
+
+ public virtual async Task IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName)
+ {
+ return await _store.IsSubscribedAsync(tenantId, userId, notificationName);
+ }
+
+ public virtual async Task SubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName)
+ {
+ if (await IsSubscribedAsync(tenantId, identifier.UserId, notificationName))
+ {
+ return;
+ }
+ await _store.InsertUserSubscriptionAsync(tenantId, identifier, notificationName);
+ }
+
+ public virtual async Task SubscribeAsync(Guid? tenantId, IEnumerable identifiers, string notificationName)
+ {
+ foreach(var identifier in identifiers)
+ {
+ await SubscribeAsync(tenantId, identifier, notificationName);
+ }
+ }
+
+ public virtual async Task UnsubscribeAsync(Guid? tenantId, UserIdentifier identifier, string notificationName)
+ {
+ await _store.DeleteUserSubscriptionAsync(tenantId, identifier.UserId, notificationName);
+ }
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs
index 1f8a7a6a0..c2f4fda24 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs
@@ -1,4 +1,7 @@
-using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
@@ -11,6 +14,31 @@ namespace LINGYUN.Abp.Notifications
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 _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
+
+
+ protected TService LazyGetRequiredService(ref TService reference)
+ {
+ if (reference == null)
+ {
+ lock (ServiceProviderLock)
+ {
+ if (reference == null)
+ {
+ reference = ServiceProvider.GetRequiredService();
+ }
+ }
+ }
+
+ return reference;
+ }
+
protected NotificationPublishProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
diff --git a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserCreateSendWelcomeEventHandler.cs b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserCreateSendWelcomeEventHandler.cs
index 63fef383c..d676d9e38 100644
--- a/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserCreateSendWelcomeEventHandler.cs
+++ b/aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/EventBus/Local/UserCreateSendWelcomeEventHandler.cs
@@ -18,24 +18,26 @@ namespace LINGYUN.Abp.MessageService.EventBus
{
private readonly ISettingProvider _settingProvider;
private readonly IStringLocalizer _stringLocalizer;
- private readonly INotificationStore _notificationStore;
+
private readonly INotificationDispatcher _notificationDispatcher;
+ private readonly INotificationSubscriptionManager _notificationSubscriptionManager;
// 需要模拟用户令牌
// 是否有必要
// private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public UserCreateSendWelcomeEventHandler(
ISettingProvider settingProvider,
- INotificationStore notificationStore,
INotificationDispatcher notificationDispatcher,
- IStringLocalizer stringLocalizer
+ IStringLocalizer stringLocalizer,
+ INotificationSubscriptionManager notificationSubscriptionManager
//ICurrentPrincipalAccessor currentPrincipalAccessor
)
{
_settingProvider = settingProvider;
_stringLocalizer = stringLocalizer;
- _notificationStore = notificationStore;
+
_notificationDispatcher = notificationDispatcher;
+ _notificationSubscriptionManager = notificationSubscriptionManager;
//_currentPrincipalAccessor = currentPrincipalAccessor;
}
@@ -53,9 +55,13 @@ namespace LINGYUN.Abp.MessageService.EventBus
{
var userIdentifer = new UserIdentifier(eventData.Entity.Id, eventData.Entity.UserName);
// 订阅用户欢迎消息
- await _notificationStore.InsertUserSubscriptionAsync(eventData.Entity.TenantId,
+ await _notificationSubscriptionManager.SubscribeAsync(eventData.Entity.TenantId,
userIdentifer, UserNotificationNames.WelcomeToApplication);
+ // Store未检查已订阅
+ //await _notificationStore.InsertUserSubscriptionAsync(eventData.Entity.TenantId,
+ // userIdentifer, UserNotificationNames.WelcomeToApplication);
+
var userWelcomeNotifictionData = new NotificationData();
// 换成用户名称,而不是用户名
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN.Abp.WeChat.Authorization.csproj b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN.Abp.WeChat.Authorization.csproj
new file mode 100644
index 000000000..95b04ed53
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN.Abp.WeChat.Authorization.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs
new file mode 100644
index 000000000..e6bf4c160
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs
@@ -0,0 +1,25 @@
+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(AbpJsonModule), typeof(AbpCachingModule))]
+ public class AbpWeChatAuthorizationModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Services.GetConfiguration();
+ Configure(configuration.GetSection("WeChat:Auth"));
+
+ context.Services.AddHttpClient("WeChatTokenProviderClient", options =>
+ {
+ options.BaseAddress = new Uri("https://api.weixin.qq.com/cgi-bin/token");
+ }).AddTransientHttpErrorPolicy(builder =>
+ builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i))));
+ }
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpWeChatValidatorOptions.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatOptions.cs
similarity index 55%
rename from aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpWeChatValidatorOptions.cs
rename to aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatOptions.cs
index 32cf49f46..3c4174d9f 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpWeChatValidatorOptions.cs
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatOptions.cs
@@ -1,6 +1,6 @@
-namespace LINGYUN.Abp.IdentityServer
+namespace LINGYUN.Abp.WeChat.Authorization
{
- public class AbpWeChatValidatorOptions
+ public class AbpWeChatOptions
{
public string AppId { get; set; }
public string AppSecret { get; set; }
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/IWeChatTokenProvider.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/IWeChatTokenProvider.cs
new file mode 100644
index 000000000..dddf69239
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/IWeChatTokenProvider.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.WeChat.Authorization
+{
+ public interface IWeChatTokenProvider
+ {
+ Task GetTokenAsync();
+ }
+}
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatToken.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatToken.cs
new file mode 100644
index 000000000..e09902f0f
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatToken.cs
@@ -0,0 +1,26 @@
+namespace LINGYUN.Abp.WeChat.Authorization
+{
+ ///
+ /// 微信令牌
+ ///
+ public class WeChatToken
+ {
+ ///
+ /// 访问令牌
+ ///
+ public string AccessToken { get; set; }
+ ///
+ /// 过期时间,单位(s)
+ ///
+ public int ExpiresIn { get; set; }
+ public WeChatToken()
+ {
+
+ }
+ public WeChatToken(string token, int expiresIn)
+ {
+ AccessToken = token;
+ ExpiresIn = expiresIn;
+ }
+ }
+}
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenCacheItem.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenCacheItem.cs
new file mode 100644
index 000000000..e0a2d27ee
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenCacheItem.cs
@@ -0,0 +1,24 @@
+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;
+ }
+ }
+}
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenProvider.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenProvider.cs
new file mode 100644
index 000000000..3471461e3
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenProvider.cs
@@ -0,0 +1,88 @@
+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.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 Logger { get; set; }
+ protected IHttpClientFactory HttpClientFactory { get; }
+ protected IJsonSerializer JsonSerializer { get; }
+ protected IDistributedCache Cache { get; }
+ protected AbpWeChatOptions Options { get; }
+ public WeChatTokenProvider(
+ IJsonSerializer jsonSerializer,
+ IHttpClientFactory httpClientFactory,
+ IOptions options,
+ IDistributedCache cache)
+ {
+ JsonSerializer = jsonSerializer;
+ HttpClientFactory = httpClientFactory;
+
+ Cache = cache;
+ Options = options.Value;
+
+ Logger = NullLogger.Instance;
+ }
+
+ public virtual async Task GetTokenAsync()
+ {
+ return (await GetCacheItemAsync("WeChatToken", Options.AppId)).WeChatToken;
+ }
+
+ protected virtual async Task GetCacheItemAsync(string provider, string appId)
+ {
+ var cacheKey = WeChatTokenCacheItem.CalculateCacheKey(provider, appId);
+
+ Logger.LogDebug($"WeChatTokenProvider.GetCacheItemAsync: {cacheKey}");
+
+ var cacheItem = await Cache.GetAsync(cacheKey);
+
+ 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("WeChatTokenProviderClient");
+
+ var request = new WeChatTokenRequest
+ {
+ BaseUrl = client.BaseAddress.AbsoluteUri,
+ AppSecret = Options.AppSecret,
+ AppId = Options.AppId,
+ GrantType = "client_credential"
+ };
+
+ var response = await client.RequestWeChatCodeTokenAsync(request);
+ var responseContent = await response.Content.ReadAsStringAsync();
+ var weChatTokenResponse = JsonSerializer.Deserialize(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);
+
+ Logger.LogDebug($"Finished setting the cache item: {cacheKey}");
+
+ return cacheItem;
+ }
+ }
+}
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenRequest.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenRequest.cs
new file mode 100644
index 000000000..3259b378d
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenRequest.cs
@@ -0,0 +1,10 @@
+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; }
+ }
+}
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenResponse.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenResponse.cs
new file mode 100644
index 000000000..e6be63b80
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenResponse.cs
@@ -0,0 +1,41 @@
+using Newtonsoft.Json;
+using Volo.Abp;
+
+namespace LINGYUN.Abp.WeChat.Authorization
+{
+ ///
+ /// 微信访问令牌返回对象
+ ///
+ public class WeChatTokenResponse
+ {
+ ///
+ /// 错误码
+ ///
+ [JsonProperty("errcode")]
+ public int ErrorCode { get; set; }
+ ///
+ /// 错误消息
+ ///
+ [JsonProperty("errmsg")]
+ public string ErrorMessage { get; set; }
+ ///
+ /// 访问令牌
+ ///
+ [JsonProperty("access_token")]
+ public string AccessToken { get; set; }
+ ///
+ /// 过期时间,单位(s)
+ ///
+ [JsonProperty("expires_in")]
+ public int ExpiresIn { get; set; }
+
+ public WeChatToken ToWeChatToken()
+ {
+ if(ErrorCode != 0)
+ {
+ throw new AbpException(ErrorMessage);
+ }
+ return new WeChatToken(AccessToken, ExpiresIn);
+ }
+ }
+}
diff --git a/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs
new file mode 100644
index 000000000..08cb5cda3
--- /dev/null
+++ b/aspnet-core/moudles/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs
@@ -0,0 +1,26 @@
+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 RequestWeChatCodeTokenAsync(this HttpMessageInvoker client, WeChatTokenRequest request, CancellationToken cancellationToken = default)
+ {
+ var getResuestUrlBuilder = new StringBuilder();
+ getResuestUrlBuilder.Append(request.BaseUrl);
+ 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;
+ }
+ }
+}
diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs
index ab30847b9..476873529 100644
--- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs
+++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs
@@ -1,6 +1,7 @@
using LINGYUN.Abp.Notifications;
using Microsoft.AspNetCore.Mvc;
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
@@ -26,6 +27,8 @@ namespace LINGYUN.Abp.MessageService.Controllers
notificationData.Properties["datetime"] = Clock.Now;
notificationData.Properties["severity"] = notification.Severity;
+ notificationData.Properties.AddIfNotContains(notification.Data);
+
await _notificationDispatcher.DispatchAsync("TestApplicationNotofication", notificationData,
notificationSeverity: notification.Severity);
@@ -38,7 +41,9 @@ namespace LINGYUN.Abp.MessageService.Controllers
public Guid UserId { get; set; }
public string Title { get; set; }
public string Message { get; set; }
+ public Dictionary Data { get; set; } = new Dictionary();
public NotificationSeverity Severity { get; set; } = NotificationSeverity.Success;
+
}
public class TestApplicationNotificationData : NotificationData
diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj
index 1f29d704e..95c765ebd 100644
--- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj
+++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj
@@ -35,6 +35,7 @@
+
diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs
index d7f763c9f..7d4e7e8bc 100644
--- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs
+++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs
@@ -5,6 +5,7 @@ using LINGYUN.Abp.IM.SignalR;
using LINGYUN.Abp.MessageService.EntityFrameworkCore;
using LINGYUN.Abp.MessageService.MultiTenancy;
using LINGYUN.Abp.Notifications.SignalR;
+using LINGYUN.Abp.Notifications.WeChat.WeApp;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
@@ -40,6 +41,7 @@ namespace LINGYUN.Abp.MessageService
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpIMSignalRModule),
typeof(AbpNotificationsSignalRModule),
+ typeof(AbpNotificationsWeChatWeAppModule),
typeof(AbpCAPEventBusModule),
typeof(AbpAutofacModule)
)]