diff --git a/aspnet-core/LINGYUN.MicroService.SingleProject.sln b/aspnet-core/LINGYUN.MicroService.SingleProject.sln index 24066a7de..93e856774 100644 --- a/aspnet-core/LINGYUN.MicroService.SingleProject.sln +++ b/aspnet-core/LINGYUN.MicroService.SingleProject.sln @@ -496,6 +496,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.Wrap EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.OpenIddict.AspNetCore", "modules\openIddict\LINGYUN.Abp.OpenIddict.AspNetCore\LINGYUN.Abp.OpenIddict.AspNetCore.csproj", "{6026DAE3-F2AD-4F6B-99EF-EEAAA5873861}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Official.Application.Contracts", "framework\wechat\LINGYUN.Abp.WeChat.Official.Application.Contracts\LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj", "{B7F866FF-8C8F-4C7D-9084-E0C206F1F26F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Official.Application", "framework\wechat\LINGYUN.Abp.WeChat.Official.Application\LINGYUN.Abp.WeChat.Official.Application.csproj", "{E957DB2E-589D-4310-9576-92F108A67CE7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Official.HttpApi", "framework\wechat\LINGYUN.Abp.WeChat.Official.HttpApi\LINGYUN.Abp.WeChat.Official.HttpApi.csproj", "{4DEFD1AB-C8CD-4240-B4C7-1C8EA4286B2A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Common", "framework\wechat\LINGYUN.Abp.WeChat.Common\LINGYUN.Abp.WeChat.Common.csproj", "{C4690A20-8628-4A39-8E71-2D09800D0E72}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Work.Common", "framework\wechat\LINGYUN.Abp.WeChat.Work.Common\LINGYUN.Abp.WeChat.Work.Common.csproj", "{8233A44F-4DFC-4701-9C04-834FD3C97060}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1298,6 +1308,26 @@ Global {6026DAE3-F2AD-4F6B-99EF-EEAAA5873861}.Debug|Any CPU.Build.0 = Debug|Any CPU {6026DAE3-F2AD-4F6B-99EF-EEAAA5873861}.Release|Any CPU.ActiveCfg = Release|Any CPU {6026DAE3-F2AD-4F6B-99EF-EEAAA5873861}.Release|Any CPU.Build.0 = Release|Any CPU + {B7F866FF-8C8F-4C7D-9084-E0C206F1F26F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7F866FF-8C8F-4C7D-9084-E0C206F1F26F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7F866FF-8C8F-4C7D-9084-E0C206F1F26F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7F866FF-8C8F-4C7D-9084-E0C206F1F26F}.Release|Any CPU.Build.0 = Release|Any CPU + {E957DB2E-589D-4310-9576-92F108A67CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E957DB2E-589D-4310-9576-92F108A67CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E957DB2E-589D-4310-9576-92F108A67CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E957DB2E-589D-4310-9576-92F108A67CE7}.Release|Any CPU.Build.0 = Release|Any CPU + {4DEFD1AB-C8CD-4240-B4C7-1C8EA4286B2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DEFD1AB-C8CD-4240-B4C7-1C8EA4286B2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DEFD1AB-C8CD-4240-B4C7-1C8EA4286B2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DEFD1AB-C8CD-4240-B4C7-1C8EA4286B2A}.Release|Any CPU.Build.0 = Release|Any CPU + {C4690A20-8628-4A39-8E71-2D09800D0E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4690A20-8628-4A39-8E71-2D09800D0E72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4690A20-8628-4A39-8E71-2D09800D0E72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4690A20-8628-4A39-8E71-2D09800D0E72}.Release|Any CPU.Build.0 = Release|Any CPU + {8233A44F-4DFC-4701-9C04-834FD3C97060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8233A44F-4DFC-4701-9C04-834FD3C97060}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8233A44F-4DFC-4701-9C04-834FD3C97060}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8233A44F-4DFC-4701-9C04-834FD3C97060}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1541,6 +1571,11 @@ Global {0D34162C-0CE3-4D7B-B19A-4786C616D0B3} = {5531E2F2-2FC2-45A0-93C7-7FC6F6A4F0AD} {FDBA1B4A-CC5D-4710-AB8C-FA5A91B91BDE} = {40A9F0DB-66AA-42A8-8670-9DD6DA992103} {6026DAE3-F2AD-4F6B-99EF-EEAAA5873861} = {7C714185-D3D9-4D94-B5CB-D857A0091F04} + {B7F866FF-8C8F-4C7D-9084-E0C206F1F26F} = {91867618-0D86-4410-91C6-B1166A9ACDF9} + {E957DB2E-589D-4310-9576-92F108A67CE7} = {91867618-0D86-4410-91C6-B1166A9ACDF9} + {4DEFD1AB-C8CD-4240-B4C7-1C8EA4286B2A} = {91867618-0D86-4410-91C6-B1166A9ACDF9} + {C4690A20-8628-4A39-8E71-2D09800D0E72} = {91867618-0D86-4410-91C6-B1166A9ACDF9} + {8233A44F-4DFC-4701-9C04-834FD3C97060} = {91867618-0D86-4410-91C6-B1166A9ACDF9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {711A43C0-A2F8-4E5C-9B9F-F2551E4B3FF1} diff --git a/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.QQ/LINGYUN/Abp/Authentication/QQ/AbpAuthenticationQQConsts.cs b/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.QQ/LINGYUN/Abp/Authentication/QQ/AbpAuthenticationQQConsts.cs index cc7ee8c6f..1a002c916 100644 --- a/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.QQ/LINGYUN/Abp/Authentication/QQ/AbpAuthenticationQQConsts.cs +++ b/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.QQ/LINGYUN/Abp/Authentication/QQ/AbpAuthenticationQQConsts.cs @@ -4,5 +4,5 @@ public static class AbpAuthenticationQQConsts { public static string AuthenticationScheme { get; set; } = "QQ Connect"; public static string DisplayName { get; set; } = "QQ Connect"; - public static string CallbackPath { get; set; } = "/signin-callback"; + public static string CallbackPath { get; set; } = "/signin-qq"; } diff --git a/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/LINGYUN/Abp/Authentication/WeChat/AbpAuthenticationWeChatConsts.cs b/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/LINGYUN/Abp/Authentication/WeChat/AbpAuthenticationWeChatConsts.cs index 481315312..038924adf 100644 --- a/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/LINGYUN/Abp/Authentication/WeChat/AbpAuthenticationWeChatConsts.cs +++ b/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/LINGYUN/Abp/Authentication/WeChat/AbpAuthenticationWeChatConsts.cs @@ -20,7 +20,7 @@ public static class AbpAuthenticationWeChatConsts /// /// 回调地址 /// - public static string CallbackPath { get; set; } = "/signin-callback"; + public static string CallbackPath { get; set; } = "/signin-wechat"; /// /// 微信客户端外的网页登录 diff --git a/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs b/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs index a0786347f..faa5f0b43 100644 --- a/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs +++ b/aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs @@ -2,7 +2,6 @@ using LINGYUN.Abp.WeChat.Official; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -16,7 +15,6 @@ using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading.Tasks; -using Volo.Abp.Caching; namespace Microsoft.AspNetCore.Authentication.WeChat.Official { @@ -25,10 +23,8 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official /// public class WeChatOfficialOAuthHandler : OAuthHandler { - protected IDistributedCache Cache { get; } protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; } public WeChatOfficialOAuthHandler( - IDistributedCache cache, IOptionsMonitor options, AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory, ILoggerFactory logger, @@ -36,7 +32,6 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official ISystemClock clock) : base(options, logger, encoder, clock) { - Cache = cache; WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; } @@ -50,154 +45,137 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official await base.InitializeHandlerAsync(); } + /// + /// 第一步:构建用户授权地址 + /// + protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri) + { + var isWeChatBrewserRequest = IsWeChatBrowser(); - protected override async Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) + var scope = isWeChatBrewserRequest + ? AbpAuthenticationWeChatConsts.UserInfoScope + : AbpAuthenticationWeChatConsts.LoginScope; + + var endPoint = isWeChatBrewserRequest + ? Options.AuthorizationEndpoint + : AbpAuthenticationWeChatConsts.QrConnectEndpoint; + + redirectUri += $"?protected={Options.StateDataFormat.Protect(properties)}"; + + var parameters = new Dictionary + { + { "appid", Options.ClientId }, + { "redirect_uri", redirectUri }, + { "response_type", "code" }, + { "scope", scope }, + { "state", Guid.NewGuid().ToString("N") }, + }; + + return $"{QueryHelpers.AddQueryString(endPoint, parameters)}#wechat_redirect"; + } + + /// + /// 第二步:code换取access_token + /// + protected async override Task ExchangeCodeAsync(OAuthCodeExchangeContext context) { - var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary + var parameters = new Dictionary() { - ["access_token"] = tokens.AccessToken, - ["openid"] = tokens.Response.GetRootString("openid") - }); + { "appid", Options.ClientId }, + { "secret", Options.ClientSecret }, + { "code", context.Code }, + { "grant_type", "authorization_code" }, + }; + + var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, parameters); var response = await Backchannel.GetAsync(address); if (!response.IsSuccessStatusCode) { - Logger.LogError("An error occurred while retrieving the user profile: the remote server " + + Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); - throw new HttpRequestException("An error occurred while retrieving user information."); + return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token.")); } var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); if (!string.IsNullOrEmpty(payload.GetRootString("errcode"))) { - Logger.LogError("An error occurred while retrieving the user profile: the remote server " + + Logger.LogError("An error occurred while retrieving an access token: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); - throw new HttpRequestException("An error occurred while retrieving user information."); + return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token.")); } - - var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); - context.RunClaimActions(); - - await Events.CreatingTicket(context); - - // TODO: 此处通过唯一的 CorrelationId, 将 properties生成的State缓存删除 - var state = Request.Query["state"]; - - var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state.ToString().ToMd5(), null); - await Cache.RemoveAsync(stateCacheKey, token: Context.RequestAborted); - - return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); + return OAuthTokenResponse.Success(payload); } /// - /// code换取access_token - /// - protected override async Task ExchangeCodeAsync(OAuthCodeExchangeContext context) + /// 第三步:构建用户票据 + /// + /// + /// + /// + /// + /// + protected async override Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) { - var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, new Dictionary() + var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary { - ["appid"] = Options.ClientId, - ["secret"] = Options.ClientSecret, - ["code"] = context.Code, - ["grant_type"] = "authorization_code" + ["access_token"] = tokens.AccessToken, + ["openid"] = tokens.Response.GetRootString("openid") }); var response = await Backchannel.GetAsync(address); if (!response.IsSuccessStatusCode) { - Logger.LogError("An error occurred while retrieving an access token: the remote server " + + Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); - return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token.")); + throw new HttpRequestException("An error occurred while retrieving user information."); } var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); if (!string.IsNullOrEmpty(payload.GetRootString("errcode"))) { - Logger.LogError("An error occurred while retrieving an access token: the remote server " + + Logger.LogError("An error occurred while retrieving the user profile: the remote server " + "returned a {Status} response with the following payload: {Headers} {Body}.", /* Status: */ response.StatusCode, /* Headers: */ response.Headers.ToString(), /* Body: */ await response.Content.ReadAsStringAsync()); - return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token.")); + throw new HttpRequestException("An error occurred while retrieving user information."); } - return OAuthTokenResponse.Success(payload); - } - - protected override async Task HandleChallengeAsync(AuthenticationProperties properties) - { - await base.HandleChallengeAsync(properties); - // TODO: 此处已经生成唯一的 CorrelationId, 可以借此将 properties生成State之后再进行缓存 - // 注: 默认的State对于微信来说太长(微信只支持128位长度的State),因此巧妙的利用CorrelationId的MD5值来替代State - // MD5转换防止直接通过CorrelationId干些别的事情... - var state = properties.Items[".xsrf"]; + var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); + context.RunClaimActions(); - var stateToken = Options.StateDataFormat.Protect(properties); - var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state.ToMd5(), null); + await Events.CreatingTicket(context); - await Cache - .SetAsync( - stateCacheKey, - new WeChatOfficialStateCacheItem(stateToken), - new DistributedCacheEntryOptions - { - AbsoluteExpiration = Clock.UtcNow.AddMinutes(2) // TODO: 设定2分钟过期? - }, - token: Context.RequestAborted); + return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); } - /// - /// 构建用户授权地址 - /// - protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri) - { - var state = properties.Items[".xsrf"]; - - var isWeChatBrewserRequest = IsWeChatBrowser(); - - var scope = isWeChatBrewserRequest - ? AbpAuthenticationWeChatConsts.UserInfoScope - : FormatScope(); - var endPoint = isWeChatBrewserRequest - ? Options.AuthorizationEndpoint - : AbpAuthenticationWeChatConsts.QrConnectEndpoint; - - var challengeUrl = QueryHelpers.AddQueryString(endPoint, new Dictionary - { - ["appid"] = Options.ClientId, - ["redirect_uri"] = redirectUri, - ["response_type"] = "code" - }); - - challengeUrl += $"&scope={scope}&state={state.ToMd5()}"; - - return challengeUrl; + public override Task HandleRequestAsync() + { + return base.HandleRequestAsync(); } - protected override async Task HandleRemoteAuthenticateAsync() + protected async override Task HandleRemoteAuthenticateAsync() { var query = Request.Query; // TODO: 此处借用唯一的 CorrelationId, 将 properties生成的State缓存取出,进行解密 - var state = query["state"]; - - var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state.ToString().ToMd5(), null); - var stateCacheItem = await Cache.GetAsync(stateCacheKey, token: Context.RequestAborted); + var state = query["protected"]; - var properties = Options.StateDataFormat.Unprotect(stateCacheItem.State); + var properties = Options.StateDataFormat.Unprotect(state); if (properties == null) { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xml b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xsd b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN.Abp.WeChat.Common.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN.Abp.WeChat.Common.csproj new file mode 100644 index 000000000..f2794ccd9 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN.Abp.WeChat.Common.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatCommonModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatCommonModule.cs new file mode 100644 index 000000000..a87210d31 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatCommonModule.cs @@ -0,0 +1,33 @@ +using LINGYUN.Abp.WeChat.Common.Localization; +using Volo.Abp.EventBus; +using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace LINGYUN.Abp.WeChat.Common; + +[DependsOn( + typeof(AbpEventBusModule))] +public class AbpWeChatCommonModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add("zh-Hans") + .AddVirtualJson("/LINGYUN/Abp/WeChat/Common/Localization/Resources"); + }); + + Configure(options => + { + options.MapCodeNamespace(WeChatCommonErrorCodes.Namespace, typeof(WeChatCommonResource)); + }); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatException.cs similarity index 93% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatException.cs index 2ef28d3f9..176bb465b 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatException.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using Volo.Abp; -namespace LINGYUN.Abp.WeChat; +namespace LINGYUN.Abp.WeChat.Common; public class AbpWeChatException : BusinessException { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/AbpWeChatCryptoException.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/AbpWeChatCryptoException.cs new file mode 100644 index 000000000..c6e1fe958 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/AbpWeChatCryptoException.cs @@ -0,0 +1,37 @@ +using LINGYUN.Abp.WeChat.Common; +using System; +using System.Runtime.Serialization; + +namespace LINGYUN.Abp.WeChat.Common.Crypto; +public class AbpWeChatCryptoException : AbpWeChatException +{ + public AbpWeChatCryptoException() + { + } + + public AbpWeChatCryptoException( + SerializationInfo serializationInfo, + StreamingContext context) : base(serializationInfo, context) + { + } + + public AbpWeChatCryptoException( + string appId, + string message = null, + string details = null, + Exception innerException = null) + : this(appId, "WeChat:100400", message, details, innerException) + { + } + + public AbpWeChatCryptoException( + string appId, + string code = null, + string message = null, + string details = null, + Exception innerException = null) + : base(code, message, details, innerException) + { + WithData("AppId", appId); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/IWeChatCryptoService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/IWeChatCryptoService.cs new file mode 100644 index 000000000..a8beb523f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/IWeChatCryptoService.cs @@ -0,0 +1,29 @@ +using LINGYUN.Abp.WeChat.Common.Crypto.Models; + +namespace LINGYUN.Abp.WeChat.Common.Crypto +{ + public interface IWeChatCryptoService + { + /// + /// 校验 + /// + /// + /// + /// + string Validation(WeChatCryptoEchoData data); + /// + /// 解密 + /// + /// + /// + /// + string Decrypt(WeChatCryptoDecryptData data); + /// + /// 加密 + /// + /// + /// + /// + string Encrypt(WeChatCryptoEncryptData data); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoDecryptData.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoDecryptData.cs new file mode 100644 index 000000000..5e5ad65aa --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoDecryptData.cs @@ -0,0 +1,28 @@ +namespace LINGYUN.Abp.WeChat.Common.Crypto.Models; +public class WeChatCryptoDecryptData +{ + public string MsgSignature { get; } + public string ReceiveId { get; } + public string Token { get; } + public string EncodingAESKey { get; } + public string TimeStamp { get; } + public string Nonce { get; } + public string PostData { get; } + public WeChatCryptoDecryptData( + string postData, + string receiveId, + string token, + string encodingAESKey, + string msgSignature, + string timeStamp, + string nonce) + { + PostData = postData; + ReceiveId = receiveId; + Token = token; + EncodingAESKey = encodingAESKey; + MsgSignature = msgSignature; + TimeStamp = timeStamp; + Nonce = nonce; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoData.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEchoData.cs similarity index 73% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoData.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEchoData.cs index bdaab08c1..6884944f0 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoData.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEchoData.cs @@ -1,13 +1,15 @@ -namespace LINGYUN.Abp.WeChat.Work.Security.Models; -public class WeChatWorkCryptoData +namespace LINGYUN.Abp.WeChat.Common.Crypto.Models; +public class WeChatCryptoEchoData { + public string EchoStr { get; } + public string MsgSignature { get; } public string ReceiveId { get; } public string Token { get; } public string EncodingAESKey { get; } - public string MsgSignature { get; } public string TimeStamp { get; } public string Nonce { get; } - public WeChatWorkCryptoData( + public WeChatCryptoEchoData( + string echoStr, string receiveId, string token, string encodingAESKey, @@ -15,6 +17,7 @@ public class WeChatWorkCryptoData string timeStamp, string nonce) { + EchoStr = echoStr; ReceiveId = receiveId; Token = token; EncodingAESKey = encodingAESKey; diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEncryptData.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEncryptData.cs new file mode 100644 index 000000000..edb8e4404 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEncryptData.cs @@ -0,0 +1,19 @@ +namespace LINGYUN.Abp.WeChat.Common.Crypto.Models; +public class WeChatCryptoEncryptData +{ + public string Data { get; } + public string ReceiveId { get; } + public string Token { get; } + public string EncodingAESKey { get; } + public WeChatCryptoEncryptData( + string data, + string receiveId, + string token, + string encodingAESKey) + { + Data = data; + ReceiveId = receiveId; + Token = token; + EncodingAESKey = encodingAESKey; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/WeChatCryptoService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/WeChatCryptoService.cs new file mode 100644 index 000000000..aaef074ae --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/WeChatCryptoService.cs @@ -0,0 +1,94 @@ +using LINGYUN.Abp.WeChat.Common.Crypto.Models; +using LINGYUN.Abp.WeChat.Common.Utils; +using System; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Common.Crypto +{ + public class WeChatCryptoService : IWeChatCryptoService, ITransientDependency + { + public string Decrypt(WeChatCryptoDecryptData data) + { + var crypto = new WXBizMsgCrypt( + data.Token, + data.EncodingAESKey, + data.ReceiveId); + + var retMsg = ""; + var ret = crypto.DecryptMsg( + data.MsgSignature, + data.TimeStamp, + data.Nonce, + data.PostData, + ref retMsg); + + if (ret != 0) + { + throw new AbpWeChatCryptoException(data.ReceiveId, code: $"WeChat:{ret}"); + } + + return retMsg; + } + + public string Encrypt(WeChatCryptoEncryptData data) + { + var crypto = new WXBizMsgCrypt( + data.Token, + data.EncodingAESKey, + data.ReceiveId); + + var sinature = ""; + var timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString(); + var nonce = DateTimeOffset.Now.Ticks.ToString("x"); + var sinatureRet = WXBizMsgCrypt.GenarateSinature( + data.Token, + timestamp, + nonce, + data.Data, + ref sinature); + if (sinatureRet != 0) + { + throw new AbpWeChatCryptoException(data.ReceiveId, code: $"WeChat:{sinatureRet}"); + } + + var retMsg = ""; + + var ret = crypto.EncryptMsg( + sinature, + timestamp, + nonce, + ref retMsg); + + if (ret != 0) + { + throw new AbpWeChatCryptoException(data.ReceiveId, code: $"WeChat:{ret}"); + } + + return retMsg; + } + + public string Validation(WeChatCryptoEchoData data) + { + var crypto = new WXBizMsgCrypt( + data.Token, + data.EncodingAESKey, + data.ReceiveId); + + var retMsg = ""; + + var ret = crypto.VerifyURL( + data.MsgSignature, + data.TimeStamp, + data.Nonce, + data.EchoStr, + ref retMsg); + + if (ret != 0) + { + throw new AbpWeChatCryptoException(data.ReceiveId, code: $"WeChat:{ret}"); + } + + return retMsg; + } + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/en.json b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/en.json new file mode 100644 index 000000000..5303f0e8e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/en.json @@ -0,0 +1,16 @@ +{ + "culture": "en", + "texts": { + "WeChat:-40001": "签名验证错误", + "WeChat:-40002": "xml/json解析失败", + "WeChat:-40003": "sha加密生成签名失败", + "WeChat:-40004": "AESKey 非法", + "WeChat:-40005": "AppId 校验错误", + "WeChat:-40006": "AES 加密失败", + "WeChat:-40007": "AES 解密失败", + "WeChat:-40008": "解密后得到的buffer非法", + "WeChat:-40009": "base64加密失败", + "WeChat:-40010": "base64解密失败", + "WeChat:-40011": "生成xml/json失败" + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/zh-Hans.json b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..42683227d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/zh-Hans.json @@ -0,0 +1,16 @@ +{ + "culture": "zh-Hans", + "texts": { + "WeChat:-40001": "签名验证错误", + "WeChat:-40002": "xml/json解析失败", + "WeChat:-40003": "sha加密生成签名失败", + "WeChat:-40004": "AESKey 非法", + "WeChat:-40005": "AppId 校验错误", + "WeChat:-40006": "AES 加密失败", + "WeChat:-40007": "AES 解密失败", + "WeChat:-40008": "解密后得到的buffer非法", + "WeChat:-40009": "base64加密失败", + "WeChat:-40010": "base64解密失败", + "WeChat:-40011": "生成xml/json失败" + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/WeChatCommonResource.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/WeChatCommonResource.cs new file mode 100644 index 000000000..9b8efa625 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/WeChatCommonResource.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.WeChat.Common.Localization; + +[LocalizationResourceName("WeChatCommon")] +public class WeChatCommonResource +{ +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs new file mode 100644 index 000000000..91af5f200 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public class AbpWeChatMessageResolveOptions +{ + public List MessageResolvers { get; } + + public AbpWeChatMessageResolveOptions() + { + MessageResolvers = new List(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/AbpWeChatMessageHandleOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/AbpWeChatMessageHandleOptions.cs new file mode 100644 index 000000000..fcab3676b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/AbpWeChatMessageHandleOptions.cs @@ -0,0 +1,40 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.WeChat.Common.Messages.Handlers; +public class AbpWeChatMessageHandleOptions +{ + internal IDictionary> EventHandlers { get; } + internal IDictionary> MessageHandlers { get; } + + public AbpWeChatMessageHandleOptions() + { + EventHandlers = new Dictionary>(); + MessageHandlers = new Dictionary>(); + } + + public void MapEvent() + where TEvent : WeChatEventMessage + where TEventHandler : IEventHandleContributor + { + var eventType = typeof(TEvent); + if (!EventHandlers.ContainsKey(eventType)) + { + EventHandlers.Add(eventType, new List()); + } + EventHandlers[eventType].AddIfNotContains(typeof(TEventHandler)); + } + + public void MapMessage() + where TMessage : WeChatGeneralMessage + where TMessageHandler : IMessageHandleContributor + { + var eventType = typeof(TMessage); + if (!MessageHandlers.ContainsKey(eventType)) + { + MessageHandlers.Add(eventType, new List()); + } + MessageHandlers[eventType].AddIfNotContains(typeof(TMessageHandler)); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IEventHandleContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IEventHandleContributor.cs new file mode 100644 index 000000000..ed96a3617 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IEventHandleContributor.cs @@ -0,0 +1,7 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Common.Messages.Handlers; +public interface IEventHandleContributor where TMessage : WeChatEventMessage +{ + Task HandleAsync(MessageHandleContext context); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandleContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandleContributor.cs new file mode 100644 index 000000000..eb1b1bcf3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandleContributor.cs @@ -0,0 +1,8 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Common.Messages.Handlers; +public interface IMessageHandleContributor where TMessage : WeChatGeneralMessage +{ + Task HandleAsync(MessageHandleContext context); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandler.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandler.cs new file mode 100644 index 000000000..c2f52ba5a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandler.cs @@ -0,0 +1,10 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Common.Messages.Handlers; +public interface IMessageHandler +{ + Task HandleEventAsync(TMessage data) where TMessage : WeChatEventMessage; + + Task HandleMessageAsync(TMessage data) where TMessage : WeChatGeneralMessage; +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandleContext.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandleContext.cs new file mode 100644 index 000000000..e4ef78838 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandleContext.cs @@ -0,0 +1,14 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System; + +namespace LINGYUN.Abp.WeChat.Common.Messages.Handlers; +public class MessageHandleContext where TMessage : WeChatMessage +{ + public TMessage Message { get; } + public IServiceProvider ServiceProvider { get; } + public MessageHandleContext(TMessage message, IServiceProvider serviceProvider) + { + Message = message; + ServiceProvider = serviceProvider; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandler.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandler.cs new file mode 100644 index 000000000..97b2c4caa --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandler.cs @@ -0,0 +1,54 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Common.Messages.Handlers; +public class MessageHandler : IMessageHandler, ITransientDependency +{ + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly AbpWeChatMessageHandleOptions _handleOptions; + + public MessageHandler( + IServiceScopeFactory serviceScopeFactory, + IOptions handleOptions) + { + _serviceScopeFactory = serviceScopeFactory; + _handleOptions = handleOptions.Value; + } + + public async virtual Task HandleEventAsync(TMessage data) where TMessage : WeChatEventMessage + { + if (_handleOptions.EventHandlers.TryGetValue(data.GetType(), out var handleTypes)) + { + using var scope = _serviceScopeFactory.CreateScope(); + foreach (var handleType in handleTypes) + { + var handlerService = ActivatorUtilities.CreateInstance(scope.ServiceProvider, handleType); + if (handlerService is IEventHandleContributor handler) + { + var context = new MessageHandleContext(data, scope.ServiceProvider); + await handler.HandleAsync(context); + } + } + } + } + + public async virtual Task HandleMessageAsync(TMessage data) where TMessage : WeChatGeneralMessage + { + if (_handleOptions.MessageHandlers.TryGetValue(data.GetType(), out var handleTypes)) + { + using var scope = _serviceScopeFactory.CreateScope(); + foreach (var handleType in handleTypes) + { + var handlerService = ActivatorUtilities.CreateInstance(scope.ServiceProvider, handleType); + if (handlerService is IMessageHandleContributor handler) + { + var context = new MessageHandleContext(data, scope.ServiceProvider); + await handler.HandleAsync(context); + } + } + } + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContext.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContext.cs new file mode 100644 index 000000000..f547f40b2 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContext.cs @@ -0,0 +1,11 @@ +using System.Xml.Linq; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public interface IMessageResolveContext : IServiceProviderAccessor +{ + string Origin { get; } + XDocument MessageData { get; } + bool Handled { get; set; } + WeChatMessage Message { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContextExtensions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContextExtensions.cs new file mode 100644 index 000000000..3cc22d533 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContextExtensions.cs @@ -0,0 +1,20 @@ +using System; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public static class IMessageResolveContextExtensions +{ + public static bool HasMessageKey(this IMessageResolveContext context, string key) + { + return context.MessageData.Root.Element(key) != null; + } + + public static string GetMessageData(this IMessageResolveContext context, string key) + { + return context.MessageData.Root.Element(key)?.Value; + } + + public static T GetWeChatMessage(this IMessageResolveContext context) where T : WeChatMessage + { + return context.Origin.DeserializeWeChatMessage(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs new file mode 100644 index 000000000..78bb587b6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public interface IMessageResolveContributor +{ + string Name { get; } + + Task ResolveAsync(IMessageResolveContext context); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs new file mode 100644 index 000000000..c708a2aa1 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs @@ -0,0 +1,7 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public interface IMessageResolver +{ + Task ResolveMessageAsync(MessageResolveData messageData); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContext.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContext.cs new file mode 100644 index 000000000..3e042f18b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Xml.Linq; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public class MessageResolveContext : IMessageResolveContext +{ + public IServiceProvider ServiceProvider { get; } + public string Origin { get; } + public XDocument MessageData { get; } + public bool Handled { get; set; } + public WeChatMessage Message { get; set; } + + public bool HasResolvedMessage() + { + return Handled || Message != null; + } + + public MessageResolveContext( + string origin, + XDocument messageData, + IServiceProvider serviceProvider) + { + Origin = origin; + MessageData = messageData; + ServiceProvider = serviceProvider; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs new file mode 100644 index 000000000..088b67e21 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public abstract class MessageResolveContributorBase : IMessageResolveContributor +{ + public abstract string Name { get; } + + public abstract Task ResolveAsync(IMessageResolveContext context); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveData.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveData.cs new file mode 100644 index 000000000..0d353bf79 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveData.cs @@ -0,0 +1,34 @@ +namespace LINGYUN.Abp.WeChat.Common.Messages; +public class MessageResolveData +{ + public string AppId { get; set; } + + public string Token { get; set; } + + public string EncodingAESKey { get; set; } + + public string Signature { get; set; } + + public int TimeStamp { get; set; } + + public string Nonce { get; set; } + + public string Data { get; set; } + public MessageResolveData( + string appId, + string token, + string encodingAESKey, + string signature, + int timeStamp, + string nonce, + string data) + { + AppId = appId; + Token = token; + EncodingAESKey = encodingAESKey; + Signature = signature; + TimeStamp = timeStamp; + Nonce = nonce; + Data = data; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveResult.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveResult.cs new file mode 100644 index 000000000..d7259a7d9 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveResult.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public class MessageResolveResult +{ + public string Input { get; internal set; } + + public WeChatMessage Message { get; set; } + + public List AppliedResolvers { get; } + + public MessageResolveResult(string input) + { + Input = input; + AppliedResolvers = new List(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs new file mode 100644 index 000000000..2cbdc49c6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs @@ -0,0 +1,88 @@ +using LINGYUN.Abp.WeChat.Common.Crypto; +using LINGYUN.Abp.WeChat.Common.Crypto.Models; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using System.Xml.Linq; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +public class MessageResolver : IMessageResolver, ITransientDependency +{ + private readonly IWeChatCryptoService _cryptoService; + private readonly IServiceProvider _serviceProvider; + private readonly AbpWeChatMessageResolveOptions _options; + + public MessageResolver( + IOptions options, + IWeChatCryptoService cryptoService, + IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _cryptoService = cryptoService; + _options = options.Value; + } + /// + /// 解析微信服务器推送消息/事件 + /// + /// + /// + public async virtual Task ResolveMessageAsync(MessageResolveData messageData) + { + var result = new MessageResolveResult(messageData.Data); + using (var serviceScope = _serviceProvider.CreateScope()) + { + /* 明文数据格式 + * + * + + 1699433172 + + + + + + */ + var xmlDocument = XDocument.Parse(messageData.Data); + var encryptData = xmlDocument.Root.Element("Encrypt")?.Value; + if (!encryptData.IsNullOrWhiteSpace()) + { + /* 加密数据格式 + * + * + + + */ + var cryptoDecryptData = new WeChatCryptoDecryptData( + encryptData, + messageData.AppId, + messageData.Token, + messageData.EncodingAESKey, + messageData.Signature, + messageData.TimeStamp.ToString(), + messageData.Nonce); + // 经过解密函数得到如上真实数据 + var decryptMessage = _cryptoService.Decrypt(cryptoDecryptData); + xmlDocument = XDocument.Parse(decryptMessage); + result.Input = decryptMessage; + } + + var context = new MessageResolveContext(result.Input, xmlDocument, serviceScope.ServiceProvider); + + foreach (var messageResolver in _options.MessageResolvers) + { + await messageResolver.ResolveAsync(context); + + result.AppliedResolvers.Add(messageResolver.Name); + + if (context.HasResolvedMessage()) + { + result.Message = context.Message; + break; + } + } + } + return result; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatEventMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatEventMessage.cs new file mode 100644 index 000000000..7d725a604 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatEventMessage.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +/// +/// 微信事件消息 +/// +public abstract class WeChatEventMessage : WeChatMessage +{ + /// + /// 事件类型 + /// + [XmlElement("Event")] + public string Event { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatGeneralMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatGeneralMessage.cs new file mode 100644 index 000000000..c40c9eba7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatGeneralMessage.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +/// +/// 微信普通消息 +/// +public abstract class WeChatGeneralMessage : WeChatMessage +{ + /// + /// 消息id,64位整型 + /// + [XmlElement("MsgId")] + public long MsgId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessage.cs new file mode 100644 index 000000000..558ed61d8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessage.cs @@ -0,0 +1,44 @@ +using System; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Common.Messages; +/// +/// 微信消息 +/// +[Serializable] +[XmlRoot("xml")] +public abstract class WeChatMessage +{ + /// + /// 开发者微信号 + /// + [XmlElement("ToUserName")] + public string ToUserName { get; set; } + /// + /// 发送方账号(一个OpenID) + /// + [XmlElement("FromUserName")] + public string FromUserName { get; set; } + /// + /// 消息创建时间 (整型) + /// + [XmlElement("CreateTime")] + public int CreateTime { get; set; } + /// + /// 消息类型,event + /// + [XmlElement("MsgType")] + public string MsgType { get; set; } + + public abstract WeChatMessageEto ToEto(); + + public virtual string SerializeToJson() + { + return WeChatObjectSerializeExtensions.SerializeToJson(this); + } + + public virtual string SerializeToXml() + { + return this.SerializeWeChatMessage(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessageEto.cs new file mode 100644 index 000000000..c5612ab91 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessageEto.cs @@ -0,0 +1,7 @@ +using Volo.Abp.Domain.Entities.Events.Distributed; + +namespace LINGYUN.Abp.WeChat.Common.Messages; + +public abstract class WeChatMessageEto : EtoBase +{ +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Security/Claims/AbpWeChatClaimTypes.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Security/Claims/AbpWeChatClaimTypes.cs similarity index 97% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Security/Claims/AbpWeChatClaimTypes.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Security/Claims/AbpWeChatClaimTypes.cs index 5f1181da3..4407215ee 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Security/Claims/AbpWeChatClaimTypes.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Security/Claims/AbpWeChatClaimTypes.cs @@ -1,4 +1,4 @@ -namespace LINGYUN.Abp.WeChat.Security.Claims +namespace LINGYUN.Abp.WeChat.Common.Security.Claims { /// /// 微信认证身份类型,可以像 自行配置 diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/Cryptography.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/Cryptography.cs similarity index 64% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/Cryptography.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/Cryptography.cs index 35f014b7f..7ef1e2cd7 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/Cryptography.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/Cryptography.cs @@ -6,23 +6,23 @@ using System.Security.Cryptography; using System.IO; using System.Net; -namespace LINGYUN.Abp.WeChat.Work.Utils +namespace LINGYUN.Abp.WeChat.Common.Utils { - internal class Cryptography + public class Cryptography { - public static UInt32 HostToNetworkOrder(UInt32 inval) + public static uint HostToNetworkOrder(uint inval) { - UInt32 outval = 0; - for (int i = 0; i < 4; i++) - outval = (outval << 8) + ((inval >> (i * 8)) & 255); + uint outval = 0; + for (var i = 0; i < 4; i++) + outval = (outval << 8) + (inval >> i * 8 & 255); return outval; } - public static Int32 HostToNetworkOrder(Int32 inval) + public static int HostToNetworkOrder(int inval) { - Int32 outval = 0; - for (int i = 0; i < 4; i++) - outval = (outval << 8) + ((inval >> (i * 8)) & 255); + var outval = 0; + for (var i = 0; i < 4; i++) + outval = (outval << 8) + (inval >> i * 8 & 255); return outval; } /// @@ -32,62 +32,62 @@ namespace LINGYUN.Abp.WeChat.Work.Utils /// /// /// - public static string AES_decrypt(String Input, string EncodingAESKey, ref string corpid) + public static string AES_decrypt(string Input, string EncodingAESKey, ref string corpid) { byte[] Key; Key = Convert.FromBase64String(EncodingAESKey + "="); - byte[] Iv = new byte[16]; + var Iv = new byte[16]; Array.Copy(Key, Iv, 16); - byte[] btmpMsg = AES_decrypt(Input, Iv, Key); + var btmpMsg = AES_decrypt(Input, Iv, Key); - int len = BitConverter.ToInt32(btmpMsg, 16); + var len = BitConverter.ToInt32(btmpMsg, 16); len = IPAddress.NetworkToHostOrder(len); - byte[] bMsg = new byte[len]; - byte[] bCorpid = new byte[btmpMsg.Length - 20 - len]; + var bMsg = new byte[len]; + var bCorpid = new byte[btmpMsg.Length - 20 - len]; Array.Copy(btmpMsg, 20, bMsg, 0, len); - Array.Copy(btmpMsg, 20+len , bCorpid, 0, btmpMsg.Length - 20 - len); - string oriMsg = Encoding.UTF8.GetString(bMsg); + Array.Copy(btmpMsg, 20 + len, bCorpid, 0, btmpMsg.Length - 20 - len); + var oriMsg = Encoding.UTF8.GetString(bMsg); corpid = Encoding.UTF8.GetString(bCorpid); - + return oriMsg; } - public static String AES_encrypt(String Input, string EncodingAESKey, string corpid) + public static string AES_encrypt(string Input, string EncodingAESKey, string corpid) { byte[] Key; Key = Convert.FromBase64String(EncodingAESKey + "="); - byte[] Iv = new byte[16]; + var Iv = new byte[16]; Array.Copy(Key, Iv, 16); - string Randcode = CreateRandCode(16); - byte[] bRand = Encoding.UTF8.GetBytes(Randcode); - byte[] bCorpid = Encoding.UTF8.GetBytes(corpid); - byte[] btmpMsg = Encoding.UTF8.GetBytes(Input); - byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length)); - byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bCorpid.Length + btmpMsg.Length]; + var Randcode = CreateRandCode(16); + var bRand = Encoding.UTF8.GetBytes(Randcode); + var bCorpid = Encoding.UTF8.GetBytes(corpid); + var btmpMsg = Encoding.UTF8.GetBytes(Input); + var bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length)); + var bMsg = new byte[bRand.Length + bMsgLen.Length + bCorpid.Length + btmpMsg.Length]; Array.Copy(bRand, bMsg, bRand.Length); Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length); Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length); Array.Copy(bCorpid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bCorpid.Length); - + return AES_encrypt(bMsg, Iv, Key); } private static string CreateRandCode(int codeLen) { - string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z"; + var codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z"; if (codeLen == 0) { codeLen = 16; } - string[] arr = codeSerial.Split(','); - string code = ""; - int randValue = -1; - Random rand = new Random(unchecked((int)DateTime.Now.Ticks)); - for (int i = 0; i < codeLen; i++) + var arr = codeSerial.Split(','); + var code = ""; + var randValue = -1; + var rand = new Random(unchecked((int)DateTime.Now.Ticks)); + for (var i = 0; i < codeLen; i++) { randValue = rand.Next(0, arr.Length - 1); code += arr[randValue]; @@ -95,7 +95,7 @@ namespace LINGYUN.Abp.WeChat.Work.Utils return code; } - private static String AES_encrypt(String Input, byte[] Iv, byte[] Key) + private static string AES_encrypt(string Input, byte[] Iv, byte[] Key) { var aes = new RijndaelManaged(); //秘钥的大小,以位为单位 @@ -114,16 +114,16 @@ namespace LINGYUN.Abp.WeChat.Work.Utils { using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) { - byte[] xXml = Encoding.UTF8.GetBytes(Input); + var xXml = Encoding.UTF8.GetBytes(Input); cs.Write(xXml, 0, xXml.Length); } xBuff = ms.ToArray(); } - String Output = Convert.ToBase64String(xBuff); + var Output = Convert.ToBase64String(xBuff); return Output; } - private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key) + private static string AES_encrypt(byte[] Input, byte[] Iv, byte[] Key) { var aes = new RijndaelManaged(); //秘钥的大小,以位为单位 @@ -140,9 +140,9 @@ namespace LINGYUN.Abp.WeChat.Work.Utils byte[] xBuff = null; #region 自己进行PKCS7补位,用系统自己带的不行 - byte[] msg = new byte[Input.Length + 32 - Input.Length % 32]; + var msg = new byte[Input.Length + 32 - Input.Length % 32]; Array.Copy(Input, msg, Input.Length); - byte[] pad = KCS7Encoder(Input.Length); + var pad = KCS7Encoder(Input.Length); Array.Copy(pad, 0, msg, Input.Length, pad.Length); #endregion @@ -160,23 +160,23 @@ namespace LINGYUN.Abp.WeChat.Work.Utils xBuff = ms.ToArray(); } - String Output = Convert.ToBase64String(xBuff); + var Output = Convert.ToBase64String(xBuff); return Output; } private static byte[] KCS7Encoder(int text_length) { - int block_size = 32; + var block_size = 32; // 计算需要填充的位数 - int amount_to_pad = block_size - (text_length % block_size); + var amount_to_pad = block_size - text_length % block_size; if (amount_to_pad == 0) { amount_to_pad = block_size; } // 获得补位所用的字符 - char pad_chr = chr(amount_to_pad); - string tmp = ""; - for (int index = 0; index < amount_to_pad; index++) + var pad_chr = chr(amount_to_pad); + var tmp = ""; + for (var index = 0; index < amount_to_pad; index++) { tmp += pad_chr; } @@ -191,12 +191,12 @@ namespace LINGYUN.Abp.WeChat.Work.Utils static char chr(int a) { - byte target = (byte)(a & 0xFF); + var target = (byte)(a & 0xFF); return (char)target; } - private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key) + private static byte[] AES_decrypt(string Input, byte[] Iv, byte[] Key) { - RijndaelManaged aes = new RijndaelManaged(); + var aes = new RijndaelManaged(); aes.KeySize = 256; aes.BlockSize = 128; aes.Mode = CipherMode.CBC; @@ -209,8 +209,8 @@ namespace LINGYUN.Abp.WeChat.Work.Utils { using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)) { - byte[] xXml = Convert.FromBase64String(Input); - byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32]; + var xXml = Convert.FromBase64String(Input); + var msg = new byte[xXml.Length + 32 - xXml.Length % 32]; Array.Copy(xXml, msg, xXml.Length); cs.Write(xXml, 0, xXml.Length); } @@ -220,12 +220,12 @@ namespace LINGYUN.Abp.WeChat.Work.Utils } private static byte[] decode2(byte[] decrypted) { - int pad = (int)decrypted[decrypted.Length - 1]; + var pad = (int)decrypted[decrypted.Length - 1]; if (pad < 1 || pad > 32) { pad = 0; } - byte[] res = new byte[decrypted.Length - pad]; + var res = new byte[decrypted.Length - pad]; Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad); return res; } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/WXBizMsgCrypt.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/WXBizMsgCrypt.cs similarity index 79% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/WXBizMsgCrypt.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/WXBizMsgCrypt.cs index 7d34d817c..7a2068f72 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/WXBizMsgCrypt.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/WXBizMsgCrypt.cs @@ -16,9 +16,9 @@ using System.Security.Cryptography; //-40008 : 解密后得到的buffer非法 //-40009 : base64加密异常 //-40010 : base64解密异常 -namespace LINGYUN.Abp.WeChat.Work.Utils +namespace LINGYUN.Abp.WeChat.Common.Utils { - internal class WXBizMsgCrypt + public class WXBizMsgCrypt { string m_sToken; string m_sEncodingAESKey; @@ -39,9 +39,9 @@ namespace LINGYUN.Abp.WeChat.Work.Utils }; //构造函数 - // @param sToken: 企业微信后台,开发者设置的Token - // @param sEncodingAESKey: 企业微信后台,开发者设置的EncodingAESKey - // @param sReceiveId: 不同场景含义不同,详见文档说明 + // @param sToken: 企业微信后台,开发者设置的Token + // @param sEncodingAESKey: 企业微信后台,开发者设置的EncodingAESKey + // @param sReceiveId: 不同场景含义不同,详见文档说明 public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sReceiveId) { m_sToken = sToken; @@ -58,18 +58,18 @@ namespace LINGYUN.Abp.WeChat.Work.Utils // @return:成功0,失败返回对应的错误码 public int VerifyURL(string sMsgSignature, string sTimeStamp, string sNonce, string sEchoStr, ref string sReplyEchoStr) { - int ret = 0; - if (m_sEncodingAESKey.Length!=43) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; - } + var ret = 0; + if (m_sEncodingAESKey.Length != 43) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; + } ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEchoStr, sMsgSignature); if (0 != ret) { return ret; } sReplyEchoStr = ""; - string cpid = ""; + var cpid = ""; try { sReplyEchoStr = Cryptography.AES_decrypt(sEchoStr, m_sEncodingAESKey, ref cpid); //m_sReceiveId); @@ -96,11 +96,11 @@ namespace LINGYUN.Abp.WeChat.Work.Utils // @return: 成功0,失败返回对应的错误码 public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg) { - if (m_sEncodingAESKey.Length!=43) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; - } - XmlDocument doc = new XmlDocument(); + if (m_sEncodingAESKey.Length != 43) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; + } + var doc = new XmlDocument(); XmlNode root; string sEncryptMsg; try @@ -114,12 +114,12 @@ namespace LINGYUN.Abp.WeChat.Work.Utils return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error; } //verify signature - int ret = 0; + var ret = 0; ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature); if (ret != 0) return ret; //decrypt - string cpid = ""; + var cpid = ""; try { sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid); @@ -148,11 +148,11 @@ namespace LINGYUN.Abp.WeChat.Work.Utils // return:成功0,失败返回对应的错误码 public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg) { - if (m_sEncodingAESKey.Length!=43) - { - return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; - } - string raw = ""; + if (m_sEncodingAESKey.Length != 43) + { + return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; + } + var raw = ""; try { raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sReceiveId); @@ -161,21 +161,21 @@ namespace LINGYUN.Abp.WeChat.Work.Utils { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error; } - string MsgSigature = ""; - int ret = 0; + var MsgSigature = ""; + var ret = 0; ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature); if (0 != ret) return ret; sEncryptMsg = ""; - string EncryptLabelHead = ""; - string MsgSigLabelHead = ""; - string TimeStampLabelHead = ""; - string NonceLabelHead = ""; + var EncryptLabelHead = ""; + var MsgSigLabelHead = ""; + var TimeStampLabelHead = ""; + var NonceLabelHead = ""; sEncryptMsg = sEncryptMsg + "" + EncryptLabelHead + raw + EncryptLabelTail; sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail; sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail; @@ -184,15 +184,15 @@ namespace LINGYUN.Abp.WeChat.Work.Utils return 0; } - public class DictionarySort : System.Collections.IComparer + public class DictionarySort : IComparer { public int Compare(object oLeft, object oRight) { - string sLeft = oLeft as string; - string sRight = oRight as string; - int iLeftLength = sLeft.Length; - int iRightLength = sRight.Length; - int index = 0; + var sLeft = oLeft as string; + var sRight = oRight as string; + var iLeftLength = sLeft.Length; + var iRightLength = sRight.Length; + var index = 0; while (index < iLeftLength && index < iRightLength) { if (sLeft[index] < sRight[index]) @@ -209,8 +209,8 @@ namespace LINGYUN.Abp.WeChat.Work.Utils //Verify Signature private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture) { - string hash = ""; - int ret = 0; + var hash = ""; + var ret = 0; ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash); if (ret != 0) return ret; @@ -222,29 +222,29 @@ namespace LINGYUN.Abp.WeChat.Work.Utils } } - public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature) + public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, ref string sMsgSignature) { - ArrayList AL = new ArrayList(); + var AL = new ArrayList(); AL.Add(sToken); AL.Add(sTimeStamp); AL.Add(sNonce); AL.Add(sMsgEncrypt); AL.Sort(new DictionarySort()); - string raw = ""; - for (int i = 0; i < AL.Count; ++i) + var raw = ""; + for (var i = 0; i < AL.Count; ++i) { raw += AL[i]; } SHA1 sha; ASCIIEncoding enc; - string hash = ""; + var hash = ""; try { sha = new SHA1CryptoServiceProvider(); enc = new ASCIIEncoding(); - byte[] dataToHash = enc.GetBytes(raw); - byte[] dataHashed = sha.ComputeHash(dataToHash); + var dataToHash = enc.GetBytes(raw); + var dataHashed = sha.ComputeHash(dataToHash); hash = BitConverter.ToString(dataHashed).Replace("-", ""); hash = hash.ToLower(); } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/WeChatCommonErrorCodes.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/WeChatCommonErrorCodes.cs new file mode 100644 index 000000000..4fa43dab3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/WeChatCommonErrorCodes.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.WeChat.Common; +public static class WeChatCommonErrorCodes +{ + public const string Namespace = "WeChatCommon"; +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/README.md b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/README.md new file mode 100644 index 000000000..d16a17a72 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/README.md @@ -0,0 +1,30 @@ +# LINGYUN.Abp.WeChat.Common + +## 模块说明 + +由于微信体系众多产品部分功能有共同点, 抽象一个通用模块, 实现一些通用的接口. + +### 基础模块 + +### 高阶模块 + +### 权限定义 + +### 功能定义 + +### 配置定义 + +### 如何使用 + + +```csharp + + [DependsOn( + typeof(AbpWeChatCommonModule))] + public class YouProjectModule : AbpModule + { + } + +``` + +### 更新日志 diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatObjectSerializeExtensions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatObjectSerializeExtensions.cs new file mode 100644 index 000000000..450e21310 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatObjectSerializeExtensions.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace System; +internal static class WeChatObjectSerializeExtensions +{ + public static string SerializeToJson(this object @object) + { + return JsonConvert.SerializeObject(@object); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatXmlDataSerializeExtensions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatXmlDataSerializeExtensions.cs new file mode 100644 index 000000000..be11aeb99 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatXmlDataSerializeExtensions.cs @@ -0,0 +1,66 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Collections; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; + +namespace System; +public static class WeChatXmlDataSerializeExtensions +{ + private readonly static Hashtable _xmlSerializers = new(); + private readonly static XmlRootAttribute _xmlRoot = new("xml"); + + private static XmlSerializer GetTypedSerializer(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var skey = type.AssemblyQualifiedName ?? type.GetHashCode().ToString(); + if (_xmlSerializers[skey] is not XmlSerializer xmlSerializer) + { + xmlSerializer = new XmlSerializer(type, _xmlRoot); + _xmlSerializers[skey] = xmlSerializer; + } + + return xmlSerializer; + } + + public static T DeserializeWeChatMessage(this string xml, XmlDeserializationEvents? events = null) where T : WeChatMessage + { + var objectType = typeof(T); + using var stringReader = new StringReader(xml); + using var xmlReader = XmlReader.Create(stringReader); + var serializer = GetTypedSerializer(objectType); + var usingEvents = events ?? new XmlDeserializationEvents(); + return (T)serializer.Deserialize(xmlReader, usingEvents); + } + + public static string SerializeWeChatMessage(this WeChatMessage message) + { + var objectType = message.GetType(); + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + Indent = false, + OmitXmlDeclaration = true, + WriteEndDocumentOnClose = false, + NamespaceHandling = NamespaceHandling.OmitDuplicates + }; + using var stream = new MemoryStream(); + using var writer = XmlWriter.Create(stream, settings); + var serializer = GetTypedSerializer(objectType); + var ns = new XmlSerializerNamespaces(); + ns.Add(string.Empty, string.Empty); + serializer.Serialize(writer, message, ns); + writer.Flush(); + var xml = Encoding.UTF8.GetString(stream.ToArray()); + xml = Regex.Replace(xml, "\\s*<\\w+ ([a-zA-Z0-9]+):nil=\"true\"[^>]*/>", string.Empty, RegexOptions.IgnoreCase); + xml = Regex.Replace(xml, "<\\?xml[^>]*\\?>", string.Empty, RegexOptions.IgnoreCase); + + return xml; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xml b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xsd b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj new file mode 100644 index 000000000..20daae9cc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj @@ -0,0 +1,15 @@ + + + + + + + netstandard2.0 + + + + + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationContractsModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationContractsModule.cs new file mode 100644 index 000000000..7734f4a3f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationContractsModule.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Application; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WeChat.Official; + +[DependsOn( + typeof(AbpDddApplicationContractsModule))] +public class AbpWeChatOfficialApplicationContractsModule : AbpModule +{ +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialRemoteServiceConsts.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialRemoteServiceConsts.cs new file mode 100644 index 000000000..faec482a5 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialRemoteServiceConsts.cs @@ -0,0 +1,8 @@ +namespace LINGYUN.Abp.WeChat.Official; + +public class AbpWeChatOfficialRemoteServiceConsts +{ + public const string RemoteServiceName = "AbpWeChatOfficial"; + + public const string ModuleName = "wechat-official"; +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/Dto/ParametricQrCodeGenerateInput.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/Dto/ParametricQrCodeGenerateInput.cs new file mode 100644 index 000000000..e1b0dc2b8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/Dto/ParametricQrCodeGenerateInput.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.WeChat.Official.Account; +public class ParametricQrCodeGenerateInput +{ + public int SceneEnum { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeAppService.cs new file mode 100644 index 000000000..3961663ed --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeAppService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Content; + +namespace LINGYUN.Abp.WeChat.Official.Account; +public interface IParametricQrCodeAppService : IApplicationService +{ + Task GenerateAsync(ParametricQrCodeGenerateInput input); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageHandleInput.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageHandleInput.cs new file mode 100644 index 000000000..317005143 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageHandleInput.cs @@ -0,0 +1,10 @@ +using LINGYUN.Abp.WeChat.Official.Models; +using System; + +namespace LINGYUN.Abp.WeChat.Official.Message; + +[Serializable] +public class MessageHandleInput : WeChatMessage +{ + public string Data { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageValidationInput.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageValidationInput.cs new file mode 100644 index 000000000..0929bf76b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageValidationInput.cs @@ -0,0 +1,12 @@ +using LINGYUN.Abp.WeChat.Official.Models; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Message; +public class MessageValidationInput : WeChatMessage +{ + /// + /// 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、receiveid四个字段,其中msg即为消息内容明文 + /// + [JsonPropertyName("echostr")] + public string EchoStr { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/IWeChatMessageAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/IWeChatMessageAppService.cs new file mode 100644 index 000000000..1e70831d0 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/IWeChatMessageAppService.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.WeChat.Official.Message; +/// +/// 微信消息接口 +/// +public interface IWeChatMessageAppService : IApplicationService +{ + /// + /// 校验微信消息 + /// + /// + /// 参考文档: + /// + /// + /// + Task Handle(MessageValidationInput input); + /// + /// 处理微信消息 + /// + /// + /// 参考文档: + /// + /// + /// + Task Handle(MessageHandleInput input); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Models/WeChatMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Models/WeChatMessage.cs new file mode 100644 index 000000000..150abf143 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Models/WeChatMessage.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Models; +public class WeChatMessage +{ + /// + /// 微信加密签名, + /// signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数 + /// + /// + /// 签名计算方法参考: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Before_Develop/Message_encryption_and_decryption.html + /// + [JsonPropertyName("signature")] + public string Signature { get; set; } + /// + /// 时间戳。与nonce结合使用,用于防止请求重放攻击。 + /// + [JsonPropertyName("timestamp")] + public int TimeStamp { get; set; } + /// + /// 随机数。与timestamp结合使用,用于防止请求重放攻击。 + /// + [JsonPropertyName("nonce")] + public string Nonce { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xml b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xsd b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN.Abp.WeChat.Official.Application.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN.Abp.WeChat.Official.Application.csproj new file mode 100644 index 000000000..72e0f91c5 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN.Abp.WeChat.Official.Application.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationModule.cs new file mode 100644 index 000000000..458022a16 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Application; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WeChat.Official; + +[DependsOn( + typeof(AbpWeChatOfficialApplicationContractsModule), + typeof(AbpWeChatOfficialModule), + typeof(AbpDddApplicationModule))] +public class AbpWeChatOfficialApplicationModule : AbpModule +{ + +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeAppService.cs new file mode 100644 index 000000000..7d6720ae7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeAppService.cs @@ -0,0 +1,24 @@ +using LINGYUN.Abp.WeChat.Official.Account.Models; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Content; + +namespace LINGYUN.Abp.WeChat.Official.Account; +public class ParametricQrCodeAppService : ApplicationService, IParametricQrCodeAppService +{ + private readonly IParametricQrCodeGenerator _qrCodeGenerator; + + public ParametricQrCodeAppService(IParametricQrCodeGenerator qrCodeGenerator) + { + _qrCodeGenerator = qrCodeGenerator; + } + + public async virtual Task GenerateAsync(ParametricQrCodeGenerateInput input) + { + var createTicketModel = CreateTicketModel.EnumScene(input.SceneEnum); + var ticketModel = await _qrCodeGenerator.CreateTicketAsync(createTicketModel); + var stream = await _qrCodeGenerator.ShowQrCodeAsync(ticketModel.Ticket); + + return new RemoteStreamContent(stream); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs new file mode 100644 index 000000000..bc4096c6b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs @@ -0,0 +1,85 @@ +using LINGYUN.Abp.WeChat.Common.Crypto; +using LINGYUN.Abp.WeChat.Common.Crypto.Models; +using LINGYUN.Abp.WeChat.Common.Messages; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.EventBus.Distributed; + +namespace LINGYUN.Abp.WeChat.Official.Message; +public class WeChatMessageAppService : ApplicationService, IWeChatMessageAppService +{ + private readonly IWeChatCryptoService _cryptoService; + private readonly AbpWeChatOfficialOptionsFactory _optionsFactory; + private readonly IDistributedEventBus _distributedEventBus; + private readonly IMessageResolver _messageResolver; + public WeChatMessageAppService( + IMessageResolver messageResolver, + IWeChatCryptoService cryptoService, + IDistributedEventBus distributedEventBus, + AbpWeChatOfficialOptionsFactory optionsFactory) + { + _cryptoService = cryptoService; + _optionsFactory = optionsFactory; + _messageResolver = messageResolver; + _distributedEventBus = distributedEventBus; + } + + public async virtual Task Handle(MessageValidationInput input) + { + var options = await _optionsFactory.CreateAsync(); + + Check.NotNull(options, nameof(options)); + + // 沙盒测试时,无需验证消息 + if (options.IsSandBox) + { + return input.EchoStr; + } + + var echoData = new WeChatCryptoEchoData( + input.EchoStr, + options.AppId, + options.Token, + options.EncodingAESKey, + input.Signature, + input.TimeStamp.ToString(), + input.Nonce); + + var echoStr = _cryptoService.Validation(echoData); + + return echoStr; + } + + public async virtual Task Handle(MessageHandleInput input) + { + var options = await _optionsFactory.CreateAsync(); + + Check.NotNull(options, nameof(options)); + + var messageData = new MessageResolveData( + options.AppId, + options.Token, + options.EncodingAESKey, + input.Signature, + input.TimeStamp, + input.Nonce, + input.Data); + + var result = await _messageResolver.ResolveMessageAsync(messageData); + if (result.Message == null) + { + Logger.LogWarning(input.Data); + Logger.LogWarning("解析消息失败, 无法处理微信消息."); + } + else + { + Logger.LogInformation(result.Message.SerializeToXml()); + var eto = result.Message.ToEto(); + await _distributedEventBus.PublishAsync(eto.GetType(), eto); + } + // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html + return "success"; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xml b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xsd b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN.Abp.WeChat.Official.HttpApi.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN.Abp.WeChat.Official.HttpApi.csproj new file mode 100644 index 000000000..44178a95d --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN.Abp.WeChat.Official.HttpApi.csproj @@ -0,0 +1,19 @@ + + + + + + + net7.0 + + + + + + + + + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialHttpApiModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialHttpApiModule.cs new file mode 100644 index 000000000..cf3ea0ab8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialHttpApiModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WeChat.Official; + +[DependsOn( + typeof(AbpWeChatOfficialApplicationContractsModule), + typeof(AbpAspNetCoreMvcModule))] +public class AbpWeChatOfficialHttpApiModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpWeChatOfficialHttpApiModule).Assembly); + }); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeController.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeController.cs new file mode 100644 index 000000000..e1062aae4 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Content; + +namespace LINGYUN.Abp.WeChat.Official.Account; + +[Controller] +[RemoteService(Name = AbpWeChatOfficialRemoteServiceConsts.RemoteServiceName)] +[Area(AbpWeChatOfficialRemoteServiceConsts.ModuleName)] +[Route("api/wechat/official/account/parametric-qrcode")] +public class ParametricQrCodeController : AbpControllerBase, IParametricQrCodeAppService +{ + private readonly IParametricQrCodeAppService _service; + + public ParametricQrCodeController(IParametricQrCodeAppService service) + { + _service = service; + } + + [HttpPost] + [Route("generate")] + public virtual Task GenerateAsync(ParametricQrCodeGenerateInput input) + { + return _service.GenerateAsync(input); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageController.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageController.cs new file mode 100644 index 000000000..1d15902cd --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageController.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; + +namespace LINGYUN.Abp.WeChat.Official.Message; + +[Controller] +[RemoteService(Name = AbpWeChatOfficialRemoteServiceConsts.RemoteServiceName)] +[Area(AbpWeChatOfficialRemoteServiceConsts.ModuleName)] +[Route("api/wechat/official/messages")] +public class WeChatMessageController : AbpControllerBase, IWeChatMessageAppService +{ + private readonly IWeChatMessageAppService _service; + + public WeChatMessageController(IWeChatMessageAppService service) + { + _service = service; + } + + [HttpGet] + public virtual Task Handle([FromQuery] MessageValidationInput input) + { + return _service.Handle(input); + } + + [HttpPost] + public async virtual Task Handle([FromQuery] MessageHandleInput input) + { + using var reader = new StreamReader(Request.Body, Encoding.UTF8); + var content = await reader.ReadToEndAsync(); + + input.Data = content; + + return await _service.Handle(input); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs index 4ce2cfbab..66a074f75 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs @@ -1,5 +1,12 @@ -using LINGYUN.Abp.WeChat.Localization; +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Common.Messages.Handlers; +using LINGYUN.Abp.WeChat.Localization; +using LINGYUN.Abp.WeChat.Official.Messages; +using LINGYUN.Abp.WeChat.Official.Messages.Handlers; +using LINGYUN.Abp.WeChat.Official.Messages.Models; using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -12,6 +19,48 @@ namespace LINGYUN.Abp.WeChat.Official { public override void ConfigureServices(ServiceConfigurationContext context) { + Configure(options => + { + options.MapEvent("subscribe", context => + { + return context.HasMessageKey("Ticket") + // 用户未关注公众号时, 扫描带参数二维码后进行关注的事件 + ? context.GetWeChatMessage() + // 用户关注/取消关注 + : context.GetWeChatMessage(); + }); + options.MapEvent("unsubscribe", context => context.GetWeChatMessage()); + options.MapEvent("LOCATION", context => context.GetWeChatMessage()); + options.MapEvent("CLICK", context => context.GetWeChatMessage()); + options.MapEvent("VIEW", context => context.GetWeChatMessage()); + options.MapEvent("SCAN", context => context.GetWeChatMessage()); + + options.MapMessage("text", context => context.GetWeChatMessage()); + options.MapMessage("image", context => context.GetWeChatMessage()); + options.MapMessage("voice", context => context.GetWeChatMessage()); + options.MapMessage("video", context => context.GetWeChatMessage()); + options.MapMessage("shortvideo", context => context.GetWeChatMessage()); + options.MapMessage("location", context => context.GetWeChatMessage()); + options.MapMessage("link", context => context.GetWeChatMessage()); + }); + + Configure(options => + { + // 事件处理器 + options.MessageResolvers.AddIfNotContains(new WeChatOfficialEventResolveContributor()); + // 消息处理器 + options.MessageResolvers.AddIfNotContains(new WeChatOfficialMessageResolveContributor()); + }); + + Configure(options => + { + // 回复文本消息 + options.MapMessage(); + + // 处理关注事件 + options.MapEvent(); + }); + Configure(options => { options.FileSets.AddEmbedded(); @@ -25,6 +74,12 @@ namespace LINGYUN.Abp.WeChat.Official }); context.Services.AddAbpDynamicOptions(); + + context.Services.AddHttpClient(AbpWeChatOfficialConsts.HttpClient, + options => + { + options.BaseAddress = new Uri("https://mp.weixin.qq.com"); + }); } } } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptions.cs index 628cd7733..5e9a88cf0 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptions.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptions.cs @@ -2,6 +2,13 @@ { public class AbpWeChatOfficialOptions { + /// + /// 是否沙盒测试 + /// + /// + /// Tips: 当使用测试号时,消息为明文传输,调试时需要启用 + /// + public bool IsSandBox { get; set; } /// /// 公众号服务器消息Url /// diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs index 5dd42e928..fb8197f0f 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs @@ -17,14 +17,16 @@ namespace LINGYUN.Abp.WeChat.Official SettingProvider = settingProvider; } - protected override async Task OverrideOptionsAsync(string name, AbpWeChatOfficialOptions options) + protected async override Task OverrideOptionsAsync(string name, AbpWeChatOfficialOptions options) { + var isSandBox = await SettingProvider.IsTrueAsync(WeChatOfficialSettingNames.IsSandBox); var appId = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppId); var appSecret = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppSecret); var url = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.Url); var token = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.Token); var aesKey = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.EncodingAESKey); + options.IsSandBox = isSandBox; options.AppId = appId ?? options.AppId; options.AppSecret = appSecret ?? options.AppSecret; options.Url = url ?? options.Url; diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Enums/SceneEnum.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Enums/SceneEnum.cs new file mode 100644 index 000000000..580bd7ecd --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Enums/SceneEnum.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.WeChat.Official.Account.Enums; +public enum SceneEnum +{ + Login = 0, + Binding = 1, +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeGenerator.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeGenerator.cs new file mode 100644 index 000000000..322918b92 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeGenerator.cs @@ -0,0 +1,29 @@ +using LINGYUN.Abp.WeChat.Official.Account.Models; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Official.Account; +/// +/// 生成带参数的二维码接口 +/// +/// +/// 详情见: https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html +/// +public interface IParametricQrCodeGenerator +{ + /// + /// 创建二维码ticket + /// + /// + /// + /// + Task CreateTicketAsync(CreateTicketModel model, CancellationToken cancellationToken = default); + /// + /// 通过ticket换取二维码 + /// + /// + /// + /// + Task ShowQrCodeAsync(string ticket, CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/CreateTicketModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/CreateTicketModel.cs new file mode 100644 index 000000000..b5b5e3535 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/CreateTicketModel.cs @@ -0,0 +1,97 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Account.Models; +public class CreateTicketModel : WeChatRequest +{ + /// + /// 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。 + /// + [JsonProperty("expire_seconds")] + [JsonPropertyName("expire_seconds")] + public int? ExpireSeconds { get; set; } + /// + /// 二维码类型 + /// + /// + /// + /// QR_SCENE为临时的整型参数值 + /// QR_STR_SCENE为临时的字符串参数值 + /// QR_LIMIT_SCENE为永久的整型参数值 + /// QR_LIMIT_STR_SCENE为永久的字符串参数值 + /// + /// + [JsonProperty("action_name")] + [JsonPropertyName("action_name")] + public string ActionName { get; private set; } + /// + /// 二维码详细信息 + /// + [JsonProperty("action_info")] + [JsonPropertyName("action_info")] + public Scene SceneInfo { get; private set; } + private CreateTicketModel() + { + + } + /// + /// 通过场景值名称创建临时二维码ticket + /// + /// 场景值名称 + /// 二维码有效时间 + /// + public static CreateTicketModel StringScene( + string sceneStr, + int expireSeconds = 60) + { + return new CreateTicketModel + { + ExpireSeconds = expireSeconds, + ActionName = "QR_STR_SCENE", + SceneInfo = new StringScene(sceneStr), + }; + } + /// + /// 通过场景值名称创建永久二维码ticket + /// + /// 场景值名称 + /// + public static CreateTicketModel LimitStringScene(string sceneStr) + { + return new CreateTicketModel + { + ActionName = "QR_LIMIT_STR_SCENE", + SceneInfo = new StringScene(sceneStr), + }; + } + /// + /// 通过场景值标识创建二维码ticket + /// + /// 场景值标识 + /// 二维码有效时间 + /// + public static CreateTicketModel EnumScene( + int sceneId, + int expireSeconds = 60) + { + return new CreateTicketModel + { + ExpireSeconds = expireSeconds, + ActionName = "QR_SCENE", + SceneInfo = new EnumScene(sceneId), + }; + } + /// + /// 通过场景值标识创建永久二维码ticket + /// + /// 场景值标识 + /// + public static CreateTicketModel LimitEnumScene(int sceneId) + { + return new CreateTicketModel + { + ActionName = "QR_LIMIT_SCENE", + SceneInfo = new EnumScene(sceneId), + }; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/EnumScene.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/EnumScene.cs new file mode 100644 index 000000000..de339816f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/EnumScene.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Account.Models; +public class EnumScene : Scene +{ + /// + /// 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000) + /// + [JsonProperty("scene_id")] + [JsonPropertyName("scene_id")] + public int SceneId { get; } + public EnumScene(int sceneId) + { + SceneId = sceneId; + } + + public override string GetKey() + { + return SceneId.ToString(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/Scene.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/Scene.cs new file mode 100644 index 000000000..46202d0df --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/Scene.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.WeChat.Official.Account.Models; +public abstract class Scene +{ + public abstract string GetKey(); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/StringScene.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/StringScene.cs new file mode 100644 index 000000000..e5621d75e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/StringScene.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Account.Models; +public class StringScene : Scene +{ + /// + /// 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64 + /// + [JsonProperty("scene_str")] + [JsonPropertyName("scene_str")] + public string SceneStr { get; } + public StringScene(string sceneStr) + { + SceneStr = sceneStr; + } + + public override string GetKey() + { + return SceneStr; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModel.cs new file mode 100644 index 000000000..013c15a25 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModel.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Account.Models; +public class TicketModel +{ + /// + /// 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码 + /// + [JsonProperty("ticket")] + [JsonPropertyName("ticket")] + public string Ticket { get; set; } + /// + /// 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)。 + /// + [JsonProperty("expire_seconds")] + [JsonPropertyName("expire_seconds")] + public int ExpireSeconds { get; set; } + /// + /// 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片 + /// + [JsonProperty("url")] + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModelCacheItem.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModelCacheItem.cs new file mode 100644 index 000000000..218ee5569 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModelCacheItem.cs @@ -0,0 +1,26 @@ +namespace LINGYUN.Abp.WeChat.Official.Account.Models; +public class TicketModelCacheItem +{ + public string Ticket { get; set; } + + public int ExpireSeconds { get; set; } + + public string Url { get; set; } + + public TicketModelCacheItem() + { + + } + + public TicketModelCacheItem(string ticket, int expireSeconds, string url) + { + Ticket = ticket; + ExpireSeconds = expireSeconds; + Url = url; + } + + public static string CalculateCacheKey(string action, string scene) + { + return "a:" + action + ";s:" + scene; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeGenerator.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeGenerator.cs new file mode 100644 index 000000000..4027c388b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeGenerator.cs @@ -0,0 +1,87 @@ +using LINGYUN.Abp.WeChat.Official.Account.Models; +using LINGYUN.Abp.WeChat.Token; +using Microsoft.Extensions.Caching.Distributed; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Official.Account; +public class ParametricQrCodeGenerator : IParametricQrCodeGenerator, ITransientDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected AbpWeChatOfficialOptionsFactory OfficialOptionsFactory { get; } + protected IWeChatTokenProvider WeChatTokenProvider { get; } + protected IDistributedCache TicketModelCache { get; } + public ParametricQrCodeGenerator( + IHttpClientFactory httpClientFactory, + IWeChatTokenProvider weChatTokenProvider, + AbpWeChatOfficialOptionsFactory officialOptionsFactory, + IDistributedCache ticketModelCache) + { + TicketModelCache = ticketModelCache; + HttpClientFactory = httpClientFactory; + WeChatTokenProvider = weChatTokenProvider; + OfficialOptionsFactory = officialOptionsFactory; + } + + public async virtual Task CreateTicketAsync(CreateTicketModel model, CancellationToken cancellationToken = default) + { + var cacheItem = await GetOrCreateTicketModelCacheItem(model, cancellationToken); + + return new TicketModel + { + ExpireSeconds = cacheItem.ExpireSeconds, + Ticket = cacheItem.Ticket, + Url = cacheItem.Url, + }; + } + + public async virtual Task ShowQrCodeAsync(string ticket, CancellationToken cancellationToken = default) + { + var client = HttpClientFactory.CreateClient(AbpWeChatOfficialConsts.HttpClient); + var response = await client.GetAsync($"/cgi-bin/showqrcode?ticket={ticket}", cancellationToken); + response.ThrowNotSuccessStatusCode(); + + return await response.Content.ReadAsStreamAsync(); + } + + protected async virtual Task GetOrCreateTicketModelCacheItem(CreateTicketModel model, CancellationToken cancellationToken = default) + { + var cacheKey = TicketModelCacheItem.CalculateCacheKey(model.ActionName, model.SceneInfo.GetKey()); + var cacheItem = await TicketModelCache.GetAsync(cacheKey, token: cancellationToken); + if (cacheItem != null) + { + return cacheItem; + } + + var options = await OfficialOptionsFactory.CreateAsync(); + + var token = await WeChatTokenProvider.GetTokenAsync(options.AppId, options.AppSecret, cancellationToken); + + var client = HttpClientFactory.CreateClient(AbpWeChatGlobalConsts.HttpClient); + var response = await client.PostAsync( + $"/cgi-bin/qrcode/create?access_token={token.AccessToken}", + new StringContent(model.SerializeToJson())); + response.ThrowNotSuccessStatusCode(); + + var responseContent = await response.Content.ReadAsStringAsync(); + var ticketModel = JsonConvert.DeserializeObject(responseContent); + + cacheItem = new TicketModelCacheItem(ticketModel.Ticket, ticketModel.ExpireSeconds, ticketModel.Url); + + var cacheOptions = new DistributedCacheEntryOptions + { + // 设置绝对过期时间为Token有效期剩余的二分钟 + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(ticketModel.ExpireSeconds) + }; + + await TicketModelCache.SetAsync(cacheKey, cacheItem, cacheOptions, token: cancellationToken); + + return cacheItem; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Localization/Resources/zh-Hans.json b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Localization/Resources/zh-Hans.json index e133623c2..0ab6d3ed9 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Localization/Resources/zh-Hans.json +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Localization/Resources/zh-Hans.json @@ -3,6 +3,8 @@ "texts": { "DisplayName:WeChat.Official": "微信公众号", "Description:WeChat.Official": "微信公众号", + "DisplayName:WeChat.Official.IsSandBox": "是否沙盒测试", + "Description:WeChat.Official.IsSandBox": "在使用微信公众号能力前,可通过申请测试号熟悉调用,详情见:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Requesting_an_API_Test_Account.html", "DisplayName:WeChat.Official.AppId": "公众号AppId", "Description:WeChat.Official.AppId": "微信公众号AppId,详情见:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html", "DisplayName:WeChat.Official.AppSecret": "公众号AppSecret", diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs new file mode 100644 index 000000000..d145468e7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs @@ -0,0 +1,25 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.WeChat.Official.Messages; +public class AbpWeChatOfficialMessageResolveOptions +{ + public IDictionary> EventMaps { get; } + public IDictionary> MessageMaps { get; } + public AbpWeChatOfficialMessageResolveOptions() + { + EventMaps = new Dictionary>(); + MessageMaps = new Dictionary>(); + } + + public void MapEvent(string eventName, Func mapFunc) + { + EventMaps[eventName] = mapFunc; + } + + public void MapMessage(string messageType, Func mapFunc) + { + MessageMaps[messageType] = mapFunc; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/TextMessageReplyContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/TextMessageReplyContributor.cs new file mode 100644 index 000000000..9d3bff545 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/TextMessageReplyContributor.cs @@ -0,0 +1,23 @@ +using LINGYUN.Abp.WeChat.Common.Messages.Handlers; +using LINGYUN.Abp.WeChat.Official.Messages.Models; +using LINGYUN.Abp.WeChat.Official.Services; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Handlers; +/// +/// 文本消息客服回复 +/// +public class TextMessageReplyContributor : IMessageHandleContributor +{ + public async virtual Task HandleAsync(MessageHandleContext context) + { + var messageSender = context.ServiceProvider.GetRequiredService(); + + await messageSender.SendAsync( + new Services.Models.TextMessageModel( + context.Message.FromUserName, + new Services.Models.TextMessage( + context.Message.Content))); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/UserSubscribeEventContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/UserSubscribeEventContributor.cs new file mode 100644 index 000000000..216a050e8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/UserSubscribeEventContributor.cs @@ -0,0 +1,23 @@ +using LINGYUN.Abp.WeChat.Common.Messages.Handlers; +using LINGYUN.Abp.WeChat.Official.Messages.Models; +using LINGYUN.Abp.WeChat.Official.Services; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Handlers; +/// +/// 用户关注回复消息 +/// +public class UserSubscribeEventContributor : IEventHandleContributor +{ + public async virtual Task HandleAsync(MessageHandleContext context) + { + var messageSender = context.ServiceProvider.GetRequiredService(); + + await messageSender.SendAsync( + new Services.Models.TextMessageModel( + context.Message.FromUserName, + new Services.Models.TextMessage( + "感谢您的关注, 点击菜单了解更多."))); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialEventEventHandler.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialEventEventHandler.cs new file mode 100644 index 000000000..65fdf013f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialEventEventHandler.cs @@ -0,0 +1,54 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Common.Messages.Handlers; +using LINGYUN.Abp.WeChat.Official.Messages.Models; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Handlers; +public class WeChatOfficialEventEventHandler : + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + ITransientDependency +{ + private readonly IMessageHandler _messageHandler; + + public WeChatOfficialEventEventHandler(IMessageHandler messageHandler) + { + _messageHandler = messageHandler; + } + + public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto eventData) + { + await _messageHandler.HandleEventAsync(eventData.Event); + } + + public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto eventData) + { + await _messageHandler.HandleEventAsync(eventData.Event); + } + + public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto eventData) + { + await _messageHandler.HandleEventAsync(eventData.Event); + } + + public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto eventData) + { + await _messageHandler.HandleEventAsync(eventData.Event); + } + + public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto eventData) + { + await _messageHandler.HandleEventAsync(eventData.Event); + } + + public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto eventData) + { + await _messageHandler.HandleEventAsync(eventData.Event); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialMessageEventHandler.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialMessageEventHandler.cs new file mode 100644 index 000000000..674866605 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialMessageEventHandler.cs @@ -0,0 +1,54 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Common.Messages.Handlers; +using LINGYUN.Abp.WeChat.Official.Messages.Models; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Handlers; +public class WeChatOfficialMessageEventHandler : + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + IDistributedEventHandler>, + ITransientDependency +{ + private readonly IMessageHandler _messageHandler; + + public WeChatOfficialMessageEventHandler(IMessageHandler messageHandler) + { + _messageHandler = messageHandler; + } + + public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto eventData) + { + await _messageHandler.HandleMessageAsync(eventData.Message); + } + + public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto eventData) + { + await _messageHandler.HandleMessageAsync(eventData.Message); + } + + public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto eventData) + { + await _messageHandler.HandleMessageAsync(eventData.Message); + } + + public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto eventData) + { + await _messageHandler.HandleMessageAsync(eventData.Message); + } + + public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto eventData) + { + await _messageHandler.HandleMessageAsync(eventData.Message); + } + + public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto eventData) + { + await _messageHandler.HandleMessageAsync(eventData.Message); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/CustomMenuEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/CustomMenuEvent.cs new file mode 100644 index 000000000..773e2ffcf --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/CustomMenuEvent.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 自定义菜单事件 +/// +[EventName("custom_menu")] +public class CustomMenuEvent : WeChatEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/GeoLocationMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/GeoLocationMessage.cs new file mode 100644 index 000000000..1179993da --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/GeoLocationMessage.cs @@ -0,0 +1,36 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 地理位置消息 +/// +[EventName("geo_location")] +public class GeoLocationMessage : WeChatOfficialGeneralMessage +{ + /// + /// 地理位置纬度 + /// + [XmlElement("Location_X")] + public double Latitude { get; set; } + /// + /// 地理位置经度 + /// + [XmlElement("Location_Y")] + public double Longitude { get; set; } + /// + /// 地图缩放大小 + /// + [XmlElement("Scale")] + public double Scale { get; set; } + /// + /// 地理位置信息 + /// + [XmlElement("Label")] + public string Label { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/LinkMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/LinkMessage.cs new file mode 100644 index 000000000..1c4423295 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/LinkMessage.cs @@ -0,0 +1,31 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 链接消息 +/// +[EventName("link")] +public class LinkMessage : WeChatOfficialGeneralMessage +{ + /// + /// 消息标题 + /// + [XmlElement("Title")] + public string Title { get; set; } + /// + /// 消息描述 + /// + [XmlElement("Description")] + public string Description { get; set; } + /// + /// 消息链接 + /// + [XmlElement("Url")] + public string Url { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/MenuClickJumpLinkEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/MenuClickJumpLinkEvent.cs new file mode 100644 index 000000000..487e4fb65 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/MenuClickJumpLinkEvent.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 点击菜单跳转链接时的事件推送 +/// +[EventName("menu_click_jump_link")] +public class MenuClickJumpLinkEvent : WeChatEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ParametricQrCodeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ParametricQrCodeEvent.cs new file mode 100644 index 000000000..696ed977c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ParametricQrCodeEvent.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 扫描带参数二维码事件 +/// +[EventName("parametric_qr_code")] +public class ParametricQrCodeEvent : WeChatEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + /// + /// 二维码的ticket,可用来换取二维码图片 + /// + [XmlElement("Ticket")] + public string Ticket { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/PictureMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/PictureMessage.cs new file mode 100644 index 000000000..13a06cde9 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/PictureMessage.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 图片消息 +/// +[EventName("picture")] +public class PictureMessage : WeChatOfficialGeneralMessage +{ + /// + /// 图片链接(由系统生成) + /// + [XmlElement("PicUrl")] + public string PicUrl { get; set; } + /// + /// 图片消息媒体id,可以调用获取临时素材接口拉取数据。 + /// + [XmlElement("MediaId")] + public string MediaId { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ReportingGeoLocationEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ReportingGeoLocationEvent.cs new file mode 100644 index 000000000..9d22fcae9 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ReportingGeoLocationEvent.cs @@ -0,0 +1,31 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 上报地理位置事件 +/// +[EventName("reporting_geo_location")] +public class ReportingGeoLocationEvent : WeChatEventMessage +{ + /// + /// 地理位置纬度 + /// + [XmlElement("Latitude")] + public double Latitude { get; set; } + /// + /// 地理位置经度 + /// + [XmlElement("Longitude")] + public double Longitude { get; set; } + /// + /// 地理位置精度 + /// + [XmlElement("Precision")] + public double Precision { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/TextMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/TextMessage.cs new file mode 100644 index 000000000..f9e495433 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/TextMessage.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 文本消息 +/// +[EventName("text")] +public class TextMessage : WeChatOfficialGeneralMessage +{ + /// + /// 文本消息内容 + /// + [XmlElement("Content")] + public string Content { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserSubscribeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserSubscribeEvent.cs new file mode 100644 index 000000000..8dee66f71 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserSubscribeEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 用户关注事件 +/// +[EventName("user_subscribe")] +public class UserSubscribeEvent : WeChatEventMessage +{ + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserUnSubscribeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserUnSubscribeEvent.cs new file mode 100644 index 000000000..00b6d4b6e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserUnSubscribeEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 用户取消关注事件 +/// +[EventName("user_un_subscribe")] +public class UserUnSubscribeEvent : WeChatEventMessage +{ + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VideoMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VideoMessage.cs new file mode 100644 index 000000000..bfd211376 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VideoMessage.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 视频消息 +/// +[EventName("video")] +public class VideoMessage : WeChatOfficialGeneralMessage +{ + /// + /// 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。 + /// + [XmlElement("ThumbMediaId")] + public string ThumbMediaId { get; set; } + /// + /// 视频消息媒体id,可以调用获取临时素材接口拉取数据。 + /// + [XmlElement("MediaId")] + public string MediaId { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VoiceMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VoiceMessage.cs new file mode 100644 index 000000000..2f8990c53 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VoiceMessage.cs @@ -0,0 +1,35 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages.Models; +/// +/// 语音消息 +/// +[EventName("voice")] +public class VoiceMessage : WeChatOfficialGeneralMessage +{ + /// + /// 语音格式,如amr,speex等 + /// + [XmlElement("Format")] + public string Format { get; set; } + /// + /// 语音识别结果,UTF8编码 + /// + /// + /// 开通语音识别后,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增加一个Recognition字段( + /// 注:由于客户端缓存,开发者开启或者关闭语音识别功能,对新关注者立刻生效,对已关注用户需要24小时生效。开发者可以重新关注此账号进行测试)。 + /// + [XmlElement("Recognition")] + public string Recognition { get; set; } + /// + /// 语音消息媒体id,可以调用获取临时素材接口拉取该媒体 + /// + [XmlElement("MediaId")] + public string MediaId { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatOfficialGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs new file mode 100644 index 000000000..b63df0c34 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages; + +[GenericEventName(Prefix = "wechat.official.events")] +public class WeChatOfficialEventMessageEto : WeChatMessageEto + where TEvent : WeChatEventMessage +{ + public TEvent Event { get; set; } + public WeChatOfficialEventMessageEto() + { + + } + public WeChatOfficialEventMessageEto(TEvent @event) + { + Event = @event; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs new file mode 100644 index 000000000..d6052e7c3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs @@ -0,0 +1,29 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Official.Messages; +/// +/// 微信公众号事件处理器 +/// +public class WeChatOfficialEventResolveContributor : MessageResolveContributorBase +{ + public override string Name => "WeChat.Official.Event"; + + public override Task ResolveAsync(IMessageResolveContext context) + { + var options = context.ServiceProvider.GetRequiredService>().Value; + var messageType = context.GetMessageData("MsgType"); + var eventName = context.GetMessageData("Event"); + if ("event".Equals(messageType, StringComparison.InvariantCultureIgnoreCase) && + !eventName.IsNullOrWhiteSpace() && + options.EventMaps.TryGetValue(eventName, out var eventFactory)) + { + context.Message = eventFactory(context); + context.Handled = true; + } + return Task.CompletedTask; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessage.cs new file mode 100644 index 000000000..0979fa034 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessage.cs @@ -0,0 +1,17 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Messages; +public abstract class WeChatOfficialGeneralMessage : WeChatGeneralMessage +{ + /// + /// 消息的数据ID(消息如果来自文章时才有) + /// + [XmlElement("MsgDataId")] + public string MsgDataId { get; set; } + /// + /// 多图文时第几篇文章,从1开始(消息如果来自文章时才有) + /// + [XmlElement("Idx")] + public string Idx { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs new file mode 100644 index 000000000..a5129d268 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialGeneralMessageEto.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Official.Messages; + +[GenericEventName(Prefix = "wechat.official.messages")] +public class WeChatOfficialGeneralMessageEto : WeChatMessageEto + where TMessage : WeChatOfficialGeneralMessage +{ + public TMessage Message { get; set; } + public WeChatOfficialGeneralMessageEto() + { + + } + public WeChatOfficialGeneralMessageEto(TMessage message) + { + Message = message; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs new file mode 100644 index 000000000..8a8408523 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialMessageResolveContributor.cs @@ -0,0 +1,25 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Official.Messages; +/// +/// 微信公众号消息处理器 +/// +public class WeChatOfficialMessageResolveContributor : MessageResolveContributorBase +{ + public override string Name => "WeChat.Official.Message"; + + public override Task ResolveAsync(IMessageResolveContext context) + { + var options = context.ServiceProvider.GetRequiredService>().Value; + var messageType = context.GetMessageData("MsgType"); + if (options.MessageMaps.TryGetValue(messageType, out var messageFactory)) + { + context.Message = messageFactory(context); + context.Handled = true; + } + return Task.CompletedTask; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/IServiceCenterMessageSender.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/IServiceCenterMessageSender.cs new file mode 100644 index 000000000..a32ee7938 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/IServiceCenterMessageSender.cs @@ -0,0 +1,12 @@ +using LINGYUN.Abp.WeChat.Official.Services.Models; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Official.Services; +/// +/// 客服中心消息接口 +/// +public interface IServiceCenterMessageSender +{ + Task SendAsync(MessageModel message, CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/Models/MessageModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/Models/MessageModel.cs new file mode 100644 index 000000000..e6f768c21 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/Models/MessageModel.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Services.Models; +public abstract class MessageModel : WeChatRequest +{ + /// + /// 消息类型 + /// + [JsonProperty("msgtype")] + [JsonPropertyName("msgtype")] + public string MsgType { get; } + protected MessageModel(string msgType) + { + MsgType = msgType; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/Models/TextMessageModel.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/Models/TextMessageModel.cs new file mode 100644 index 000000000..2bac29137 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/Models/TextMessageModel.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using System; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat.Official.Services.Models; +/// +/// 发送文本消息 +/// +[Serializable] +public class TextMessageModel : MessageModel +{ + /// + /// 接收消息用户 + /// + [JsonProperty("touser")] + [JsonPropertyName("touser")] + public string ToUser { get; } + /// + /// 消息内容 + /// + [JsonProperty("text")] + [JsonPropertyName("text")] + public TextMessage Text { get; } + public TextMessageModel(string toUser, TextMessage text) + : base("text") + { + ToUser = toUser; + Text = text; + } +} + +public class TextMessage +{ + /// + /// 内容文本 + /// + [JsonProperty("content")] + [JsonPropertyName("content")] + public string Content { get; set; } + public TextMessage(string content) + { + Content = content; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/ServiceCenterMessageSender.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/ServiceCenterMessageSender.cs new file mode 100644 index 000000000..546e28e2b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Services/ServiceCenterMessageSender.cs @@ -0,0 +1,34 @@ +using LINGYUN.Abp.WeChat.Official.Services.Models; +using LINGYUN.Abp.WeChat.Token; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.WeChat.Official.Services; +public class ServiceCenterMessageSender : IServiceCenterMessageSender, ITransientDependency +{ + protected IHttpClientFactory HttpClientFactory { get; } + protected AbpWeChatOfficialOptionsFactory OfficialOptionsFactory { get; } + protected IWeChatTokenProvider WeChatTokenProvider { get; } + public ServiceCenterMessageSender( + IHttpClientFactory httpClientFactory, + IWeChatTokenProvider weChatTokenProvider, + AbpWeChatOfficialOptionsFactory officialOptionsFactory) + { + HttpClientFactory = httpClientFactory; + WeChatTokenProvider = weChatTokenProvider; + OfficialOptionsFactory = officialOptionsFactory; + } + + public async virtual Task SendAsync(MessageModel message, CancellationToken cancellationToken = default) + { + var options = await OfficialOptionsFactory.CreateAsync(); + var token = await WeChatTokenProvider.GetTokenAsync(options.AppId, options.AppSecret, cancellationToken); + var client = HttpClientFactory.CreateClient(AbpWeChatGlobalConsts.HttpClient); + await client.PostAsync( + $"cgi-bin/message/custom/send?access_token={token.AccessToken}", + new StringContent(message.SerializeToJson()), + cancellationToken); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingDefinitionProvider.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingDefinitionProvider.cs index 9c803963e..f3432169c 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingDefinitionProvider.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingDefinitionProvider.cs @@ -9,6 +9,18 @@ namespace LINGYUN.Abp.WeChat.Official.Settings public override void Define(ISettingDefinitionContext context) { context.Add( + new SettingDefinition( + WeChatOfficialSettingNames.IsSandBox, + "false", + L("DisplayName:WeChat.Official.IsSandBox"), + L("Description:WeChat.Official.IsSandBox"), + isVisibleToClients: false, + isEncrypted: false) + .WithProviders( + DefaultValueSettingValueProvider.ProviderName, + ConfigurationSettingValueProvider.ProviderName, + GlobalSettingValueProvider.ProviderName, + TenantSettingValueProvider.ProviderName), new SettingDefinition( WeChatOfficialSettingNames.AppId, "", L("DisplayName:WeChat.Official.AppId"), diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingNames.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingNames.cs index 9c20f0481..9b179e1c4 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingNames.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Settings/WeChatOfficialSettingNames.cs @@ -6,6 +6,7 @@ namespace LINGYUN.Abp.WeChat.Official.Settings { private const string Prefix = WeChatSettingNames.Prefix + ".Official"; + public static string IsSandBox = Prefix + "." + nameof(AbpWeChatOfficialOptions.IsSandBox); public static string AppId = Prefix + "." + nameof(AbpWeChatOfficialOptions.AppId); public static string AppSecret = Prefix + "." + nameof(AbpWeChatOfficialOptions.AppSecret); public static string Url = Prefix + "." + nameof(AbpWeChatOfficialOptions.Url); diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.SettingManagement/LINGYUN/Abp/WeChat/SettingManagement/WeChatSettingAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.SettingManagement/LINGYUN/Abp/WeChat/SettingManagement/WeChatSettingAppService.cs index 0d91c5844..1616fcd76 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.SettingManagement/LINGYUN/Abp/WeChat/SettingManagement/WeChatSettingAppService.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.SettingManagement/LINGYUN/Abp/WeChat/SettingManagement/WeChatSettingAppService.cs @@ -59,6 +59,12 @@ namespace LINGYUN.Abp.WeChat.SettingManagement #region 公众号 var officialSetting = wechatSettingGroup.AddSetting(L["DisplayName:WeChat.Official"], L["Description:WeChat.Official"]); + officialSetting.AddDetail( + await SettingDefinitionManager.GetAsync(WeChatOfficialSettingNames.IsSandBox), + StringLocalizerFactory, + await SettingManager.GetOrNullAsync(WeChatOfficialSettingNames.IsSandBox, providerName, providerKey), + ValueType.Boolean, + providerName); officialSetting.AddDetail( await SettingDefinitionManager.GetAsync(WeChatOfficialSettingNames.AppId), StringLocalizerFactory, diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs index e1aeed744..b4552c38a 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs @@ -1,22 +1,30 @@ -using LINGYUN.Abp.WeChat.Work.Security; -using LINGYUN.Abp.WeChat.Work.Security.Models; +using LINGYUN.Abp.WeChat.Common.Crypto; +using LINGYUN.Abp.WeChat.Common.Crypto.Models; +using LINGYUN.Abp.WeChat.Common.Messages; using LINGYUN.Abp.WeChat.Work.Settings; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Application.Services; +using Volo.Abp.EventBus.Distributed; namespace LINGYUN.Abp.WeChat.Work.Message; public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessageAppService { - private readonly IWeChatWorkCryptoService _cryptoService; + private readonly IWeChatCryptoService _cryptoService; private readonly WeChatWorkOptions _options; - + private readonly IDistributedEventBus _distributedEventBus; + private readonly IMessageResolver _messageResolver; public WeChatWorkMessageAppService( - IWeChatWorkCryptoService cryptoService, + IMessageResolver messageResolver, + IWeChatCryptoService cryptoService, + IDistributedEventBus distributedEventBus, IOptionsMonitor options) { _cryptoService = cryptoService; + _messageResolver = messageResolver; + _distributedEventBus = distributedEventBus; _options = options.CurrentValue; } @@ -27,7 +35,7 @@ public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessag var applicationConfiguration = _options.Applications.GetConfiguration(agentId); var cryptoConfiguration = applicationConfiguration.GetCryptoConfiguration("Message"); - var echoData = new WeChatWorkCryptoEchoData( + var echoData = new WeChatCryptoEchoData( input.EchoStr, corpId, cryptoConfiguration.Token, @@ -48,16 +56,29 @@ public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessag var applicationConfiguration = _options.Applications.GetConfiguration(agentId); var cryptoConfiguration = applicationConfiguration.GetCryptoConfiguration("Message"); - var decryptData = new WeChatWorkCryptoDecryptData( - input.Data, + + var messageData = new MessageResolveData( corpId, cryptoConfiguration.Token, cryptoConfiguration.EncodingAESKey, input.Msg_Signature, - input.TimeStamp.ToString(), - input.Nonce); + input.TimeStamp, + input.Nonce, + input.Data); - var msg = _cryptoService.Decrypt(decryptData); - return msg; + var result = await _messageResolver.ResolveMessageAsync(messageData); + if (result.Message == null) + { + Logger.LogWarning(input.Data); + Logger.LogWarning("解析消息失败, 无法处理企业微信消息."); + } + else + { + Logger.LogInformation(result.Message.SerializeToXml()); + var eto = result.Message.ToEto(); + await _distributedEventBus.PublishAsync(eto.GetType(), eto); + } + // https://developer.work.weixin.qq.com/document/path/90238 + return "success"; } } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj new file mode 100644 index 000000000..4b3e4eb03 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN.Abp.WeChat.Work.Common.csproj @@ -0,0 +1,15 @@ + + + + + + + netstandard2.0 + + + + + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs new file mode 100644 index 000000000..e0bea9a15 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/AbpWeChatWorkCommonModule.cs @@ -0,0 +1,62 @@ +using LINGYUN.Abp.WeChat.Common; +using LINGYUN.Abp.WeChat.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages; +using LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +using System; +using System.Xml.Serialization; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.WeChat.Work.Common; + +[DependsOn( + typeof(AbpWeChatCommonModule))] +public class AbpWeChatWorkCommonModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.MapEvent("click", context => context.GetWeChatMessage()); + options.MapEvent("view", context => context.GetWeChatMessage()); + options.MapEvent("scancode_push", context => context.GetWeChatMessage()); + options.MapEvent("scancode_waitmsg", context => context.GetWeChatMessage()); + options.MapEvent("pic_sysphoto", context => context.GetWeChatMessage()); + options.MapEvent("pic_photo_or_album", context => context.GetWeChatMessage()); + options.MapEvent("pic_weixin", context => context.GetWeChatMessage()); + options.MapEvent("subscribe", context => context.GetWeChatMessage()); + options.MapEvent("unsubscribe", context => context.GetWeChatMessage()); + options.MapEvent("enter_agent", context => context.GetWeChatMessage()); + options.MapEvent("LOCATION", context => context.GetWeChatMessage()); + options.MapEvent("batch_job_result", context => context.GetWeChatMessage()); + options.MapEvent("change_contact", context => + { + static UserChangeEvent CreateUserChangeEvent(string originXml) where TEvent : UserChangeEvent + { + var events = new XmlDeserializationEvents(); + return originXml.DeserializeWeChatMessage(events); + } + + var changeType = context.GetMessageData("ChangeType"); + return changeType switch + { + "create_user" => CreateUserChangeEvent(context.Origin), + "update_user" => CreateUserChangeEvent(context.Origin), + "delete_user" => context.GetWeChatMessage(), + "create_party" => context.GetWeChatMessage(), + "update_party" => context.GetWeChatMessage(), + "delete_party" => context.GetWeChatMessage(), + "update_tag" => context.GetWeChatMessage(), + _ => throw new AbpWeChatException($"Contact change event {changeType} is not mounted!"), + }; + }); + + options.MapMessage("text", context => context.GetWeChatMessage()); + options.MapMessage("image", context => context.GetWeChatMessage()); + options.MapMessage("voice", context => context.GetWeChatMessage()); + options.MapMessage("video", context => context.GetWeChatMessage()); + options.MapMessage("shortvideo", context => context.GetWeChatMessage()); + options.MapMessage("location", context => context.GetWeChatMessage()); + options.MapMessage("link", context => context.GetWeChatMessage()); + }); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs new file mode 100644 index 000000000..e994957b3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/AbpWeChatWorkMessageResolveOptions.cs @@ -0,0 +1,25 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages; +public class AbpWeChatWorkMessageResolveOptions +{ + public IDictionary> EventMaps { get; } + public IDictionary> MessageMaps { get; } + public AbpWeChatWorkMessageResolveOptions() + { + EventMaps = new Dictionary>(); + MessageMaps = new Dictionary>(); + } + + public void MapEvent(string eventName, Func mapFunc) + { + EventMaps[eventName] = mapFunc; + } + + public void MapMessage(string messageType, Func mapFunc) + { + MessageMaps[messageType] = mapFunc; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs new file mode 100644 index 000000000..504f8ec3c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/BatchJobResultEvent.cs @@ -0,0 +1,50 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 进入应用事件 +/// +[EventName("batch_job_result")] +public class BatchJobResultEvent : WeChatWorkEventMessage +{ + /// + /// 异步任务信息 + /// + [XmlElement("BatchJob")] + public BatchJobResult BatchJob { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} + +public class BatchJobResult +{ + /// + /// 异步任务id,最大长度为64字符 + /// + [XmlElement("JobId")] + public string JobId { get; set; } + /// + /// 操作类型,字符串, + /// 目前分别有: + /// sync_user(增量更新成员)、 + /// replace_user(全量覆盖成员)、 + /// invite_user(邀请成员关注)、 + /// replace_party(全量覆盖部门) + /// + [XmlElement("JobType")] + public string JobType { get; set; } + /// + /// 返回码 + /// + [XmlElement("ErrCode")] + public int ErrCode { get; set; } + /// + /// 返回码 + /// + [XmlElement("ErrMsg")] + public string ErrMsg { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CreateDepartmentEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CreateDepartmentEvent.cs new file mode 100644 index 000000000..ae79490d6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CreateDepartmentEvent.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 新增部门事件 +/// +[EventName("create_party")] +public class CreateDepartmentEvent : DepartmentUpdateEvent +{ + /// + /// 部门排序 + /// + [XmlElement("Order", IsNullable = true)] + public int? Order { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CreateUserEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CreateUserEvent.cs new file mode 100644 index 000000000..22320382e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CreateUserEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 新增成员事件 +/// +[EventName("create_user")] +public class CreateUserEvent : UserChangeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CustomMenuEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CustomMenuEvent.cs new file mode 100644 index 000000000..fb9aa9b82 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/CustomMenuEvent.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 自定义菜单事件 +/// +[EventName("custom_menu")] +public class CustomMenuEvent : WeChatWorkEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DeleteDepartmentEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DeleteDepartmentEvent.cs new file mode 100644 index 000000000..6827d82db --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DeleteDepartmentEvent.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 删除部门事件 +/// +[EventName("delete_party")] +public class DeleteDepartmentEvent : WeChatWorkEventMessage +{ + /// + /// 部门Id + /// + [XmlElement("Id")] + public string Id { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DeleteUserEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DeleteUserEvent.cs new file mode 100644 index 000000000..3d98b97fe --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DeleteUserEvent.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 删除成员事件 +/// +[EventName("delete_user")] +public class DeleteUserEvent : WeChatWorkEventMessage +{ + /// + /// 变更信息的成员UserID + /// + [XmlElement("UserID")] + public string UserId { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DepartmentUpdateEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DepartmentUpdateEvent.cs new file mode 100644 index 000000000..628dca80c --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/DepartmentUpdateEvent.cs @@ -0,0 +1,24 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 部门变更事件 +/// +public abstract class DepartmentUpdateEvent : WeChatWorkEventMessage +{ + /// + /// 部门Id + /// + [XmlElement("Id")] + public int Id { get; set; } + /// + /// 部门名称 + /// + [XmlElement("Name")] + public int Name { get; set; } + /// + /// 父部门id + /// + [XmlElement("ParentId", IsNullable = true)] + public int? ParentId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/EnterAgentEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/EnterAgentEvent.cs new file mode 100644 index 000000000..6902e593a --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/EnterAgentEvent.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 进入应用事件 +/// +[EventName("enter_agent")] +public class EnterAgentEvent : WeChatWorkEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/GeoLocationMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/GeoLocationMessage.cs new file mode 100644 index 000000000..8f19d4517 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/GeoLocationMessage.cs @@ -0,0 +1,41 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 地理位置消息 +/// +[EventName("geo_location")] +public class GeoLocationMessage : WeChatWorkGeneralMessage +{ + /// + /// 地理位置纬度 + /// + [XmlElement("Location_X")] + public double Latitude { get; set; } + /// + /// 地理位置经度 + /// + [XmlElement("Location_Y")] + public double Longitude { get; set; } + /// + /// 地图缩放大小 + /// + [XmlElement("Scale")] + public double Scale { get; set; } + /// + /// 地理位置信息 + /// + [XmlElement("Label")] + public string Label { get; set; } + /// + /// app类型,在企业微信固定返回wxwork,在微信不返回该字段 + /// + [XmlElement("AppType")] + public string AppType { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/LinkMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/LinkMessage.cs new file mode 100644 index 000000000..d0af61f62 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/LinkMessage.cs @@ -0,0 +1,31 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 链接消息 +/// +[EventName("link")] +public class LinkMessage : WeChatWorkGeneralMessage +{ + /// + /// 消息标题 + /// + [XmlElement("Title")] + public string Title { get; set; } + /// + /// 消息描述 + /// + [XmlElement("Description")] + public string Description { get; set; } + /// + /// 消息链接 + /// + [XmlElement("Url")] + public string Url { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/MemberExtendAttribute.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/MemberExtendAttribute.cs new file mode 100644 index 000000000..087516069 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/MemberExtendAttribute.cs @@ -0,0 +1,50 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +[XmlRoot("Item")] +public class MemberExtendAttribute +{ + /// + /// 扩展属性类型: 0-本文 1-网页 + /// + [XmlElement("Type")] + public byte Type { get; set; } + /// + /// 扩展属性类型: 0-本文 1-网页 + /// + public MemberExtend Extend { get; set; } +} + +public abstract class MemberExtend +{ +} + +/// +/// 文本属性类型,扩展属性类型为0时填写 +/// +[XmlRoot("Text")] +public class MemberTextExtend : MemberExtend +{ + /// + /// 文本属性内容 + /// + [XmlElement("Value")] + public string Value { get; set;} +} +/// +/// 网页类型属性,扩展属性类型为1时填写 +/// +[XmlRoot("Web")] +public class MemberWebExtend : MemberExtend +{ + /// + /// 网页的展示标题 + /// + [XmlElement("Title")] + public string Title { get; set; } + /// + /// 网页的url + /// + [XmlElement("Url")] + public string Url { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/MenuClickJumpLinkEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/MenuClickJumpLinkEvent.cs new file mode 100644 index 000000000..310feb627 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/MenuClickJumpLinkEvent.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 点击菜单跳转链接时的事件推送 +/// +[EventName("menu_click_jump_link")] +public class MenuClickJumpLinkEvent : WeChatWorkEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureAlbumPushEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureAlbumPushEvent.cs new file mode 100644 index 000000000..6ac952379 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureAlbumPushEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 弹出拍照或者相册发图的事件推送 +/// +[EventName("pic_photo_or_album")] +public class PictureAlbumPushEvent : PicturePushEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureMessage.cs new file mode 100644 index 000000000..dedcc371b --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureMessage.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 图片消息 +/// +[EventName("picture")] +public class PictureMessage : WeChatWorkGeneralMessage +{ + /// + /// 图片链接(由系统生成) + /// + [XmlElement("PicUrl")] + public string PicUrl { get; set; } + /// + /// 图片消息媒体id,可以调用获取临时素材接口拉取数据。 + /// + [XmlElement("MediaId")] + public string MediaId { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PicturePushEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PicturePushEvent.cs new file mode 100644 index 000000000..836e59a86 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PicturePushEvent.cs @@ -0,0 +1,43 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 弹出系统发图的事件推送 +/// +public abstract class PicturePushEvent : WeChatWorkEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + /// + /// 事件KEY值 + /// + [XmlElement("SendPicsInfo")] + public PictureInfo SendPicsInfo { get; set; } +} + +public class PictureInfo +{ + /// + /// 发送的图片数量 + /// + [XmlElement("Count")] + public int Count { get; set; } + /// + /// 发送的图片数量 + /// + [XmlArrayItem("Item")] + public Picture Picture { get; set; } +} + +[XmlRoot("PicList")] +public class Picture +{ + /// + /// 图片的MD5值,开发者若需要,可用于验证接收到图片 + /// + [XmlElement("PicMd5Sum")] + public string PicMd5Sum { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureSystemPushEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureSystemPushEvent.cs new file mode 100644 index 000000000..7106ef6e8 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureSystemPushEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 弹出系统拍照发图的事件推送 +/// +[EventName("pic_sysphoto")] +public class PictureSystemPushEvent : PicturePushEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureWeixinPushEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureWeixinPushEvent.cs new file mode 100644 index 000000000..cb2fd3884 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/PictureWeixinPushEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 弹出微信相册发图器的事件推送 +/// +[EventName("pic_weixin")] +public class PictureWeixinPushEvent : PicturePushEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ReportingGeoLocationEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ReportingGeoLocationEvent.cs new file mode 100644 index 000000000..e61abcf0f --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ReportingGeoLocationEvent.cs @@ -0,0 +1,36 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 上报地理位置事件 +/// +[EventName("reporting_geo_location")] +public class ReportingGeoLocationEvent : WeChatWorkEventMessage +{ + /// + /// 地理位置纬度 + /// + [XmlElement("Latitude")] + public double Latitude { get; set; } + /// + /// 地理位置经度 + /// + [XmlElement("Longitude")] + public double Longitude { get; set; } + /// + /// 地理位置精度 + /// + [XmlElement("Precision")] + public double Precision { get; set; } + /// + /// app类型,在企业微信固定返回wxwork,在微信不返回该字段 + /// + [XmlElement("AppType")] + public string AppType { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodeEvent.cs new file mode 100644 index 000000000..131b36cad --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodeEvent.cs @@ -0,0 +1,33 @@ +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 用户扫码事件 +/// +public abstract class ScanCodeEvent : WeChatWorkEventMessage +{ + /// + /// 事件KEY值 + /// + [XmlElement("EventKey")] + public string EventKey { get; set; } + /// + /// 扫描信息 + /// + [XmlElement("ScanCodeInfo")] + public ScanCodeInfo ScanCodeInfo { get; set; } +} + +public class ScanCodeInfo +{ + /// + /// 扫描类型,一般是qrcode + /// + [XmlElement("ScanType")] + public string ScanType { get; set; } + /// + /// 扫描结果,即二维码对应的字符串信息 + /// + [XmlElement("ScanResult")] + public string ScanResult { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodePushEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodePushEvent.cs new file mode 100644 index 000000000..9f89f0617 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodePushEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 自定义菜单事件 +/// +[EventName("scancode_push")] +public class ScanCodePushEvent : ScanCodeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodeWaitMsgEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodeWaitMsgEvent.cs new file mode 100644 index 000000000..e0cf6d723 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/ScanCodeWaitMsgEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 自定义菜单事件 +/// +[EventName("scancode_waitmsg")] +public class ScanCodeWaitMsgEvent : ScanCodeEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/TextMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/TextMessage.cs new file mode 100644 index 000000000..ea1c3ac46 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/TextMessage.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 文本消息 +/// +[EventName("text")] +public class TextMessage : WeChatWorkGeneralMessage +{ + /// + /// 文本消息内容 + /// + [XmlElement("Content")] + public string Content { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UpdateDepartmentEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UpdateDepartmentEvent.cs new file mode 100644 index 000000000..9ff67015e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UpdateDepartmentEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 更新部门事件 +/// +[EventName("update_party")] +public class UpdateDepartmentEvent : DepartmentUpdateEvent +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UpdateUserEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UpdateUserEvent.cs new file mode 100644 index 000000000..b19136b47 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UpdateUserEvent.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 更新成员事件 +/// +[EventName("update_user")] +public class UpdateUserEvent : UserChangeEvent +{ + /// + /// 新的UserID,变更时推送(userid由系统生成时可更改一次) + /// + [XmlElement("NewUserID")] + public string NewUserId { get; set; } + + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserChangeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserChangeEvent.cs new file mode 100644 index 000000000..48c2d95be --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserChangeEvent.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 成员变更事件 +/// +public abstract class UserChangeEvent : WeChatWorkEventMessage +{ + /// + /// 改变类型 + /// + [XmlElement("ChangeType")] + public string ChangeType { get; set; } + /// + /// 成员UserID + /// + [XmlElement("UserID")] + public string UserId { get; set; } + /// + /// 成员名称;代开发自建应用需要管理员授权才返回 + /// + [XmlElement("Name")] + public string Name { get; set; } + /// + /// 成员部门列表,仅返回该应用有查看权限的部门id + /// + [XmlElement("Department")] + public string Department { get; set; } + /// + /// 主部门 + /// + [XmlElement("MainDepartment")] + public string MainDepartment { get; set; } + /// + /// 表示所在部门是否为部门负责人, + /// 0-否, + /// 1-是, + /// 顺序与Department字段的部门逐一对应。 + /// 第三方通讯录应用或者授权了“组织架构信息-应用可获取企业的部门组织架构信息-部门负责人”权限的第三方应用和代开发应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("IsLeaderInDept")] + public string IsLeaderInDept { get; set; } + /// + /// 直属上级UserID,最多1个。 + /// 第三方通讯录应用或者授权了“组织架构信息-应用可获取可见范围内成员组织架构信息-直属上级”权限的第三方应用和代开发应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("DirectLeader")] + public string DirectLeader { get; set; } + /// + /// 职位信息。 + /// 长度为0~64个字节;代开发自建应用需要管理员授权才返回。 + /// 上游共享的应用不返回该字段 + /// + [XmlElement("Position")] + public string Position { get; set; } + /// + /// 手机号码,代开发自建应用需要管理员授权且成员oauth2授权获取; + /// 第三方仅通讯录应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用也不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("Mobile")] + public string Mobile { get; set; } + /// + /// 性别。 + /// 0表示未定义, + /// 1表示男性, + /// 2表示女性。 + /// 代开发自建应用需要管理员授权且成员oauth2授权获取; + /// 第三方仅通讯录应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用也不可获取; + /// 上游企业不可获取下游企业成员该字段。 + /// 注:不可获取指返回值0 + /// + [XmlElement("Gender")] + public byte Gender { get; set; } + /// + /// 邮箱, + /// 代开发自建应用需要管理员授权且成员oauth2授权获取; + /// 第三方仅通讯录应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用也不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("Email")] + public string Email { get; set; } + /// + /// 企业邮箱, + /// 代开发自建应用需要管理员授权且成员oauth2授权获取; + /// 第三方仅通讯录应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用也不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("BizMail")] + public string BizMail { get; set; } + /// + /// 激活状态: + /// 1=已激活 + /// 2=已禁用 + /// 4=未激活 已激活代表已激活企业微信或已关注微信插件(原企业号) + /// 5=成员退出 + /// + [XmlElement("Status")] + public byte Status { get; set; } + /// + /// 头像url。 + /// 注:如果要获取小图将url最后的”/0”改成”/100”即可。 + /// 代开发自建应用需要管理员授权且成员oauth2授权获取; + /// 第三方仅通讯录应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用也不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("Avatar")] + public string Avatar { get; set; } + /// + /// 成员别名。 + /// 上游共享的应用不返回该字段 + /// + [XmlElement("Alias")] + public string Alias { get; set; } + /// + /// 座机; + /// 代开发自建应用需要管理员授权才返回。 + /// 上游共享的应用不返回该字段 + /// + [XmlElement("Telephone")] + public string Telephone { get; set; } + /// + /// 地址。 + /// 代开发自建应用需要管理员授权且成员oauth2授权获取; + /// 第三方仅通讯录应用可获取; + /// 对于非第三方创建的成员,第三方通讯录应用也不可获取; + /// 上游企业不可获取下游企业成员该字段 + /// + [XmlElement("Address")] + public string Address { get; set; } + /// + /// 扩展属性; + /// 代开发自建应用需要管理员授权才返回。 + /// 上游共享的应用不返回该字段 + /// + [XmlArray("ExtAttr")] + public List Extend { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserSubscribeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserSubscribeEvent.cs new file mode 100644 index 000000000..e08ce7da5 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserSubscribeEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 用户关注事件 +/// +[EventName("user_subscribe")] +public class UserSubscribeEvent : WeChatWorkEventMessage +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserTagChangeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserTagChangeEvent.cs new file mode 100644 index 000000000..5e770c7e6 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserTagChangeEvent.cs @@ -0,0 +1,41 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 标签成员变更事件 +/// +[EventName("update_tag")] +public class UserTagChangeEvent : WeChatWorkEventMessage +{ + /// + /// 标签Id + /// + [XmlElement("TagId")] + public string TagId { get; set; } + /// + /// 标签中新增的成员userid列表,用逗号分隔 + /// + [XmlElement("AddUserItems", IsNullable = true)] + public string AddUserItems { get; set; } + /// + /// 标签中删除的成员userid列表,用逗号分隔 + /// + [XmlElement("DelUserItems", IsNullable = true)] + public string DelUserItems { get; set; } + /// + /// 标签中新增的部门id列表,用逗号分隔 + /// + [XmlElement("AddPartyItems", IsNullable = true)] + public string AddPartyItems { get; set; } + /// + /// 标签中删除的部门id列表,用逗号分隔 + /// + [XmlElement("DelPartyItems", IsNullable = true)] + public string DelPartyItems { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserUnSubscribeEvent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserUnSubscribeEvent.cs new file mode 100644 index 000000000..be1116314 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/UserUnSubscribeEvent.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 用户取消关注事件 +/// +[EventName("user_un_subscribe")] +public class UserUnSubscribeEvent : WeChatWorkEventMessage +{ + public override WeChatMessageEto ToEto() + { + return new WeChatWorkEventMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/VideoMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/VideoMessage.cs new file mode 100644 index 000000000..729591b92 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/VideoMessage.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 视频消息 +/// +[EventName("video")] +public class VideoMessage : WeChatWorkGeneralMessage +{ + /// + /// 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。 + /// + [XmlElement("ThumbMediaId")] + public string ThumbMediaId { get; set; } + /// + /// 视频消息媒体id,可以调用获取临时素材接口拉取数据。 + /// + [XmlElement("MediaId")] + public string MediaId { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/VoiceMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/VoiceMessage.cs new file mode 100644 index 000000000..ac4233ce3 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/Models/VoiceMessage.cs @@ -0,0 +1,26 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages.Models; +/// +/// 语音消息 +/// +[EventName("voice")] +public class VoiceMessage : WeChatWorkGeneralMessage +{ + /// + /// 语音格式,如amr,speex等 + /// + [XmlElement("Format")] + public string Format { get; set; } + /// + /// 语音消息媒体id,可以调用获取临时素材接口拉取该媒体 + /// + [XmlElement("MediaId")] + public string MediaId { get; set; } + public override WeChatMessageEto ToEto() + { + return new WeChatWorkGeneralMessageEto(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessage.cs new file mode 100644 index 000000000..8760cc30e --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessage.cs @@ -0,0 +1,20 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages; +/// +/// 企业微信事件消息 +/// +public abstract class WeChatWorkEventMessage : WeChatMessage +{ + /// + /// 事件类型 + /// + [XmlElement("Event")] + public string Event { get; set; } + /// + /// 企业应用的id,整型。可在应用的设置页面查看 + /// + [XmlElement("AgentID")] + public int AgentId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs new file mode 100644 index 000000000..3d9d67723 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkEventMessageEto.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages; + +[GenericEventName(Prefix = "wechat.work.events")] +public class WeChatWorkEventMessageEto : WeChatMessageEto + where TEvent : WeChatWorkEventMessage +{ + public TEvent Event { get; set; } + public WeChatWorkEventMessageEto() + { + + } + public WeChatWorkEventMessageEto(TEvent @event) + { + Event = @event; + } +} \ No newline at end of file diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessage.cs new file mode 100644 index 000000000..6833cf564 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessage.cs @@ -0,0 +1,15 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using System.Xml.Serialization; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages; +/// +/// 企业微信普通消息 +/// +public abstract class WeChatWorkGeneralMessage : WeChatGeneralMessage +{ + /// + /// 企业应用的id,整型。可在应用的设置页面查看 + /// + [XmlElement("AgentID")] + public int AgentId { get; set; } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs new file mode 100644 index 000000000..734642916 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Common/LINGYUN/Abp/WeChat/Work/Common/Messages/WeChatWorkGeneralMessageEto.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.WeChat.Common.Messages; +using Volo.Abp.EventBus; + +namespace LINGYUN.Abp.WeChat.Work.Common.Messages; + +[GenericEventName(Prefix = "wechat.work.messages")] +public class WeChatWorkGeneralMessageEto : WeChatMessageEto + where TMessage : WeChatWorkGeneralMessage +{ + public TMessage Message { get; set; } + public WeChatWorkGeneralMessageEto() + { + + } + public WeChatWorkGeneralMessageEto(TMessage message) + { + Message = message; + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN.Abp.WeChat.Work.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN.Abp.WeChat.Work.csproj index 42ec2af81..f1a173153 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN.Abp.WeChat.Work.csproj +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN.Abp.WeChat.Work.csproj @@ -23,6 +23,7 @@ + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkModule.cs index fe43837a0..2ba94b9ee 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkModule.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkModule.cs @@ -1,4 +1,6 @@ using LINGYUN.Abp.Features.LimitValidation; +using LINGYUN.Abp.WeChat.Common; +using LINGYUN.Abp.WeChat.Common.Localization; using LINGYUN.Abp.WeChat.Work.Localization; using Microsoft.Extensions.DependencyInjection; using System; @@ -16,7 +18,8 @@ namespace LINGYUN.Abp.WeChat.Work; typeof(AbpCachingModule), typeof(AbpExceptionHandlingModule), typeof(AbpFeaturesLimitValidationModule), - typeof(AbpSettingsModule))] + typeof(AbpSettingsModule), + typeof(AbpWeChatCommonModule))] public class AbpWeChatWorkModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) @@ -33,6 +36,7 @@ public class AbpWeChatWorkModule : AbpModule { options.Resources .Add("zh-Hans") + .AddBaseTypes(typeof(WeChatCommonResource)) .AddVirtualJson("/LINGYUN/Abp/WeChat/Work/Localization/Resources"); }); diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageManager.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageManager.cs similarity index 94% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageManager.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageManager.cs index 74598baa9..e85edb5e6 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageManager.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageManager.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -namespace LINGYUN.Abp.WeChat.Work.Message; +namespace LINGYUN.Abp.WeChat.Work.Messages; /// /// 企业微信消息管理器 /// diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageSender.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageSender.cs similarity index 96% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageSender.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageSender.cs index aedc69f81..db393d368 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageSender.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageSender.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; -namespace LINGYUN.Abp.WeChat.Work.Message; +namespace LINGYUN.Abp.WeChat.Work.Messages; /// /// 消息发送接口 /// diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MarkdownMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MarkdownMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MarkdownMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MarkdownMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MediaMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MediaMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MediaMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MediaMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MiniProgramMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MiniProgramMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MiniProgramMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MiniProgramMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MpNewMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MpNewMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/MpNewMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/MpNewMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/NewMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/NewMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/NewMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/NewMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/TextCardMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/TextCardMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/TextCardMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/TextCardMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/TextMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/TextMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/TextMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/TextMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/VideoMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/VideoMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/VideoMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/VideoMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatFileMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatFileMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatFileMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatFileMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatImageMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatImageMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatImageMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatImageMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatMarkdownMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatMarkdownMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatMarkdownMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatMarkdownMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatMpNewMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatMpNewMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatMpNewMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatMpNewMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatNewMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatNewMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatNewMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatNewMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatTextCardMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatTextCardMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatTextCardMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatTextCardMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatTextMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatTextMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatTextMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatTextMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatVideoMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatVideoMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatVideoMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatVideoMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatVoiceMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatVoiceMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkAppChatVoiceMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkAppChatVoiceMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkFileMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkFileMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkFileMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkFileMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkImageMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkImageMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkImageMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkImageMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkMarkdownMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkMarkdownMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkMarkdownMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkMarkdownMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkMiniProgramMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkMiniProgramMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkMiniProgramMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkMiniProgramMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkMpNewMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkMpNewMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkMpNewMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkMpNewMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkNewMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkNewMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkNewMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkNewMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkTextCardMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkTextCardMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkTextCardMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkTextCardMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkTextMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkTextMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkTextMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkTextMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkVideoMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkVideoMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkVideoMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkVideoMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkVoiceMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkVoiceMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Models/WeChatWorkVoiceMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Models/WeChatWorkVoiceMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Request/WeChatWorkMessageReCallRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Request/WeChatWorkMessageReCallRequest.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Request/WeChatWorkMessageReCallRequest.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Request/WeChatWorkMessageReCallRequest.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Request/WeChatWorkMessageRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Request/WeChatWorkMessageRequest.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Request/WeChatWorkMessageRequest.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Request/WeChatWorkMessageRequest.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Response/WeChatWorkMessageResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Response/WeChatWorkMessageResponse.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Response/WeChatWorkMessageResponse.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Response/WeChatWorkMessageResponse.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCard.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCard.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCard.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCard.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardAction.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardAction.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardAction.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardAction.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardActionMenu.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardActionMenu.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardActionMenu.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardActionMenu.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardCardAction.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardCardAction.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardCardAction.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardCardAction.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardEmphasisContent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardEmphasisContent.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardEmphasisContent.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardEmphasisContent.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardHorizontalContent.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardHorizontalContent.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardHorizontalContent.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardHorizontalContent.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardJump.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardJump.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardJump.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardJump.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardMainTitle.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardMainTitle.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardMainTitle.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardMainTitle.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardQuoteArea.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardQuoteArea.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardQuoteArea.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardQuoteArea.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardSource.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardSource.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TemplateCardSource.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TemplateCardSource.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TextTemplateCard.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TextTemplateCard.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/TextTemplateCard.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/TextTemplateCard.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/WeChatWorkTemplateCardMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/WeChatWorkTemplateCardMessage.cs similarity index 100% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/Templates/WeChatWorkTemplateCardMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/Templates/WeChatWorkTemplateCardMessage.cs diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkAppChatMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkAppChatMessage.cs similarity index 96% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkAppChatMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkAppChatMessage.cs index b88c0750b..d842baee4 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkAppChatMessage.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkAppChatMessage.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json; using System.Text.Json.Serialization; -namespace LINGYUN.Abp.WeChat.Work.Message; +namespace LINGYUN.Abp.WeChat.Work.Messages; /// /// 企业微信群聊消息 /// diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessage.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessage.cs similarity index 97% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessage.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessage.cs index b3c4550e0..9fce54397 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessage.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessage.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json; using System.Text.Json.Serialization; -namespace LINGYUN.Abp.WeChat.Work.Message; +namespace LINGYUN.Abp.WeChat.Work.Messages; /// /// 企业微信消息 /// diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageManager.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageManager.cs similarity index 97% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageManager.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageManager.cs index 1a8fdbb2e..e7d46b47e 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageManager.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageManager.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -namespace LINGYUN.Abp.WeChat.Work.Message; +namespace LINGYUN.Abp.WeChat.Work.Messages; public class WeChatWorkMessageManager : IWeChatWorkMessageManager, ISingletonDependency { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageSender.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageSender.cs similarity index 98% rename from aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageSender.cs rename to aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageSender.cs index c08727721..8867d9744 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageSender.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageSender.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Features; -namespace LINGYUN.Abp.WeChat.Work.Message; +namespace LINGYUN.Abp.WeChat.Work.Messages; [RequiresFeature(WeChatWorkFeatureNames.Enable)] public class WeChatWorkMessageSender : IWeChatWorkMessageSender, ISingletonDependency diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/AbpWeChatWorkCryptoException.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/AbpWeChatWorkCryptoException.cs deleted file mode 100644 index 51e99a5c4..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/AbpWeChatWorkCryptoException.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace LINGYUN.Abp.WeChat.Work.Security; -public class AbpWeChatWorkCryptoException : AbpWeChatWorkException -{ - public AbpWeChatWorkCryptoException() - { - } - - public AbpWeChatWorkCryptoException( - SerializationInfo serializationInfo, - StreamingContext context) : base(serializationInfo, context) - { - } - - public AbpWeChatWorkCryptoException( - string agentId, - string message = null, - string details = null, - Exception innerException = null) - : this(agentId, "WeChatWork:100400", message, details, innerException) - { - } - - public AbpWeChatWorkCryptoException( - string agentId, - string code = null, - string message = null, - string details = null, - Exception innerException = null) - : base(code, message, details, innerException) - { - WithData("AgentId", agentId); - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkCryptoService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkCryptoService.cs deleted file mode 100644 index 918652a63..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/IWeChatWorkCryptoService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LINGYUN.Abp.WeChat.Work.Security.Models; - -namespace LINGYUN.Abp.WeChat.Work.Security; -/// -/// 企业微信加解密接口 -/// -public interface IWeChatWorkCryptoService -{ - /// - /// 校验 - /// - /// - /// - /// - string Validation(WeChatWorkCryptoEchoData data); - /// - /// 解密 - /// - /// - /// - /// - string Decrypt(WeChatWorkCryptoDecryptData data); - /// - /// 加密 - /// - /// - /// - /// - string Encrypt(WeChatWorkCryptoData data); -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoDecryptData.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoDecryptData.cs deleted file mode 100644 index 95b67da67..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoDecryptData.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace LINGYUN.Abp.WeChat.Work.Security.Models; -public class WeChatWorkCryptoDecryptData : WeChatWorkCryptoData -{ - public string PostData { get; } - public WeChatWorkCryptoDecryptData( - string postData, - string receiveId, - string token, - string encodingAESKey, - string msgSignature, - string timeStamp, - string nonce) : base(receiveId, token, encodingAESKey, msgSignature, timeStamp, nonce) - { - PostData = postData; - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoEchoData.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoEchoData.cs deleted file mode 100644 index e9a32bd86..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoEchoData.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace LINGYUN.Abp.WeChat.Work.Security.Models; -public class WeChatWorkCryptoEchoData : WeChatWorkCryptoData -{ - public string EchoStr { get; } - public WeChatWorkCryptoEchoData( - string echoStr, - string receiveId, - string token, - string encodingAESKey, - string msgSignature, - string timeStamp, - string nonce) : base(receiveId, token, encodingAESKey, msgSignature, timeStamp, nonce) - { - EchoStr = echoStr; - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoService.cs deleted file mode 100644 index 914a11217..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoService.cs +++ /dev/null @@ -1,78 +0,0 @@ -using LINGYUN.Abp.WeChat.Work.Security.Models; -using LINGYUN.Abp.WeChat.Work.Utils; -using Volo.Abp.DependencyInjection; - -namespace LINGYUN.Abp.WeChat.Work.Security; - -public class WeChatWorkCryptoService : IWeChatWorkCryptoService, ISingletonDependency -{ - public string Decrypt(WeChatWorkCryptoDecryptData data) - { - var crypto = new WXBizMsgCrypt( - data.Token, - data.EncodingAESKey, - data.ReceiveId); - - var retMsg = ""; - var ret = crypto.DecryptMsg( - data.MsgSignature, - data.TimeStamp, - data.Nonce, - data.PostData, - ref retMsg); - - if (ret != 0) - { - throw new AbpWeChatWorkCryptoException(data.ReceiveId, code: $"WeChatWork:{ret}"); - } - - return retMsg; - } - - public string Encrypt(WeChatWorkCryptoData data) - { - var crypto = new WXBizMsgCrypt( - data.Token, - data.EncodingAESKey, - data.ReceiveId); - - var retMsg = ""; - - var ret = crypto.EncryptMsg( - data.MsgSignature, - data.TimeStamp, - data.Nonce, - ref retMsg); - - if (ret != 0) - { - throw new AbpWeChatWorkCryptoException(data.ReceiveId, code: $"WeChatWork:{ret}"); - } - - return retMsg; - } - - public string Validation(WeChatWorkCryptoEchoData data) - { - var crypto = new WXBizMsgCrypt( - data.Token, - data.EncodingAESKey, - data.ReceiveId); - - var retMsg = ""; - - var ret = crypto.VerifyURL( - data.MsgSignature, - data.TimeStamp, - data.Nonce, - data.EchoStr, - ref retMsg); - - if (ret != 0) - { - throw new AbpWeChatWorkCryptoException(data.ReceiveId, code: $"WeChatWork:{ret}"); - } - - return retMsg; - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Message.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Message.cs index b7396e1a0..56a30109b 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Message.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/System/Net/Http/HttpClientWeChatWorkRequestExtensions.Message.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.WeChat.Work.Message; +using LINGYUN.Abp.WeChat.Work.Messages; using LINGYUN.Abp.WeChat.Work.Message.Request; using System.Text; using System.Threading; diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN.Abp.WeChat.csproj b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN.Abp.WeChat.csproj index 696105297..6c4f0eb30 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN.Abp.WeChat.csproj +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN.Abp.WeChat.csproj @@ -23,4 +23,8 @@ + + + + diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatModule.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatModule.cs index 6d5858464..5245c8a4b 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatModule.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatModule.cs @@ -1,40 +1,49 @@ -using LINGYUN.Abp.WeChat.Localization; +using LINGYUN.Abp.WeChat.Common; +using LINGYUN.Abp.WeChat.Common.Localization; +using LINGYUN.Abp.WeChat.Localization; using Microsoft.Extensions.DependencyInjection; using System; using Volo.Abp.Caching; using Volo.Abp.Features; using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; using Volo.Abp.Settings; using Volo.Abp.VirtualFileSystem; -namespace LINGYUN.Abp.WeChat +namespace LINGYUN.Abp.WeChat; + +[DependsOn( + typeof(AbpCachingModule), + typeof(AbpFeaturesModule), + typeof(AbpSettingsModule), + typeof(AbpWeChatCommonModule))] +public class AbpWeChatModule : AbpModule { - [DependsOn( - typeof(AbpCachingModule), - typeof(AbpFeaturesModule), - typeof(AbpSettingsModule))] - public class AbpWeChatModule : AbpModule + public override void ConfigureServices(ServiceConfigurationContext context) { - public override void ConfigureServices(ServiceConfigurationContext context) + Configure(options => { - Configure(options => - { - options.FileSets.AddEmbedded(); - }); + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add("zh-Hans") + .AddBaseTypes(typeof(WeChatCommonResource)) + .AddVirtualJson("/LINGYUN/Abp/WeChat/Localization/Resources"); + }); + + Configure(options => + { + options.MapCodeNamespace(WeChatErrorCodes.Namespace, typeof(WeChatResource)); + }); - Configure(options => + context.Services.AddHttpClient(AbpWeChatGlobalConsts.HttpClient, + options => { - options.Resources - .Add("zh-Hans") - .AddVirtualJson("/LINGYUN/Abp/WeChat/Localization/Resources"); + options.BaseAddress = new Uri("https://api.weixin.qq.com"); }); - - context.Services.AddHttpClient(AbpWeChatGlobalConsts.HttpClient, - options => - { - options.BaseAddress = new Uri("https://api.weixin.qq.com"); - }); - } } } diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Crypto/IWeChatCryptoService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Crypto/IWeChatCryptoService.cs deleted file mode 100644 index da38022a5..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Crypto/IWeChatCryptoService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace LINGYUN.Abp.WeChat.Crypto -{ - public interface IWeChatCryptoService - { - string Decrypt(string encryptedData, string iv, string sessionKey); - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Crypto/WeChatCryptoService.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Crypto/WeChatCryptoService.cs deleted file mode 100644 index 7bb98265e..000000000 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Crypto/WeChatCryptoService.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Text; -using Volo.Abp.DependencyInjection; - -namespace LINGYUN.Abp.WeChat.Crypto -{ - public class WeChatCryptoService : IWeChatCryptoService, ITransientDependency - { - public virtual string Decrypt(string encryptedData, string iv, string sessionKey) - { - using var aes = new AesCryptoServiceProvider(); - aes.Mode = CipherMode.CBC; - aes.BlockSize = 128; - // 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。 - aes.Padding = PaddingMode.PKCS7; - - //格式化待处理字符串 - // 对称解密的目标密文为 Base64_Decode(encryptedData)。 - byte[] byte_encryptedData = Convert.FromBase64String(encryptedData); - // 对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。 - byte[] byte_iv = Convert.FromBase64String(iv); - // 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。 - byte[] byte_sessionKey = Convert.FromBase64String(sessionKey); - - //根据设置好的数据生成解密器实例 - using var transform = aes.CreateDecryptor(byte_iv, byte_sessionKey); - //解密 - byte[] final = transform.TransformFinalBlock(byte_encryptedData, 0, byte_encryptedData.Length); - - //生成结果 - string result = Encoding.UTF8.GetString(final); - return result; - } - } -} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs index 81261ec9f..e2760383d 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdResponse.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using LINGYUN.Abp.WeChat.Common; +using Newtonsoft.Json; using System; namespace LINGYUN.Abp.WeChat.OpenId diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs index 933534984..166b9d518 100644 --- a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenResponse.cs @@ -1,5 +1,5 @@ -using Newtonsoft.Json; -using Volo.Abp; +using LINGYUN.Abp.WeChat.Common; +using Newtonsoft.Json; namespace LINGYUN.Abp.WeChat.Token { diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatErrorCodes.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatErrorCodes.cs new file mode 100644 index 000000000..b10cfd204 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatErrorCodes.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.WeChat; +public static class WeChatErrorCodes +{ + public const string Namespace = "WeChat"; +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatRequest.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatRequest.cs new file mode 100644 index 000000000..b80ab03d7 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatRequest.cs @@ -0,0 +1,12 @@ +using System; + +namespace LINGYUN.Abp.WeChat; + +[Serializable] +public abstract class WeChatRequest +{ + public virtual string SerializeToJson() + { + return WeChatObjectSerializeExtensions.SerializeToJson(this); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatResponse.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatResponse.cs new file mode 100644 index 000000000..3301c9e09 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/WeChatResponse.cs @@ -0,0 +1,35 @@ +using LINGYUN.Abp.WeChat.Common; +using Newtonsoft.Json; +using System; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.WeChat; +/// +/// 微信请求响应 +/// +[Serializable] +public class WeChatResponse +{ + /// + /// 错误码 + /// + [JsonProperty("errcode")] + [JsonPropertyName("errcode")] + public int ErrorCode { get; set; } + /// + /// 错误消息 + /// + [JsonProperty("errmsg")] + [JsonPropertyName("errmsg")] + public string ErrorMessage { get; set; } + + public bool IsSuccessed => ErrorCode == 0; + + public void ThrowIfNotSuccess() + { + if (ErrorCode != 0) + { + throw new AbpWeChatException($"WeChat:{ErrorCode}", $"Wechat work request error:{ErrorMessage}"); + } + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/Net/Http/HttpResponseDeserializeExtensions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/Net/Http/HttpResponseDeserializeExtensions.cs new file mode 100644 index 000000000..53ae5baf0 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/Net/Http/HttpResponseDeserializeExtensions.cs @@ -0,0 +1,23 @@ +using LINGYUN.Abp.WeChat; +using LINGYUN.Abp.WeChat.Common; +using System.Threading.Tasks; + +namespace System.Net.Http; +public static class HttpResponseDeserializeExtensions +{ + public static void ThrowNotSuccessStatusCode(this HttpResponseMessage response) + { + if (!response.IsSuccessStatusCode) + { + throw new AbpWeChatException($"Wechat request error: {response.StatusCode} - {response.ReasonPhrase}"); + } + } + + public async static Task DeserializeObjectAsync(this HttpResponseMessage response) where T : WeChatResponse + { + response.ThrowNotSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync(); + + return responseContent.DeserializeObject(); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/WeChatObjectSerializeExtensions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/WeChatObjectSerializeExtensions.cs new file mode 100644 index 000000000..450e21310 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/WeChatObjectSerializeExtensions.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace System; +internal static class WeChatObjectSerializeExtensions +{ + public static string SerializeToJson(this object @object) + { + return JsonConvert.SerializeObject(@object); + } +} diff --git a/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/WeChatResponseDeserializeExtensions.cs b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/WeChatResponseDeserializeExtensions.cs new file mode 100644 index 000000000..6d8abda93 --- /dev/null +++ b/aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/System/WeChatResponseDeserializeExtensions.cs @@ -0,0 +1,11 @@ +using LINGYUN.Abp.WeChat; +using Newtonsoft.Json; + +namespace System; +public static class WeChatResponseDeserializeExtensions +{ + public static T DeserializeObject(this string responseContent) where T : WeChatResponse + { + return JsonConvert.DeserializeObject(responseContent); + } +} diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj index baceecefa..b4c65a86e 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj +++ b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj @@ -92,6 +92,8 @@ + + diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs index 71779aad2..d74fd53e9 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs @@ -337,8 +337,9 @@ public partial class MicroServiceApplicationsSingleModule Configure(options => { options.NodeName = ApplicationName; - options.JobCleanEnabled = true; - options.JobFetchEnabled = true; + options.JobCleanEnabled = false; + options.JobFetchEnabled = false; + options.JobCheckEnabled = false; }); } @@ -671,6 +672,7 @@ public partial class MicroServiceApplicationsSingleModule options.IsEnabled = true; options.IgnoreNamespaces.Add("Elsa"); options.IgnoreNamespaces.Add("LINGYUN.Abp.OssManagement"); + options.IgnoreNamespaces.Add("LINGYUN.Abp.WeChat"); }); } diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs index 3dc099a12..03ee3168a 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs @@ -240,6 +240,8 @@ namespace LY.MicroService.Applications.Single; typeof(AbpNotificationsWeChatMiniProgramModule), typeof(AbpWeChatMiniProgramModule), typeof(AbpWeChatOfficialModule), + typeof(AbpWeChatOfficialApplicationModule), + typeof(AbpWeChatOfficialHttpApiModule), typeof(AbpWeChatWorkModule), typeof(AbpWeChatWorkApplicationModule), typeof(AbpWeChatWorkHttpApiModule), diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/Properties/launchSettings.json b/aspnet-core/services/LY.MicroService.Applications.Single/Properties/launchSettings.json index b95142d78..b9fee327d 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/Properties/launchSettings.json +++ b/aspnet-core/services/LY.MicroService.Applications.Single/Properties/launchSettings.json @@ -12,7 +12,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": false, - "applicationUrl": "http://127.0.0.1:30001", + "applicationUrl": "http://0.0.0.0:30001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Production" }