Browse Source

refactor(wechat): refactor the wechat module

pull/905/head
colin 2 years ago
parent
commit
17b3f45f27
  1. 35
      aspnet-core/LINGYUN.MicroService.SingleProject.sln
  2. 2
      aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.QQ/LINGYUN/Abp/Authentication/QQ/AbpAuthenticationQQConsts.cs
  3. 2
      aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/LINGYUN/Abp/Authentication/WeChat/AbpAuthenticationWeChatConsts.cs
  4. 162
      aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs
  5. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xml
  6. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xsd
  7. 21
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN.Abp.WeChat.Common.csproj
  8. 33
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatCommonModule.cs
  9. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/AbpWeChatException.cs
  10. 37
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/AbpWeChatCryptoException.cs
  11. 29
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/IWeChatCryptoService.cs
  12. 28
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoDecryptData.cs
  13. 11
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEchoData.cs
  14. 19
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/Models/WeChatCryptoEncryptData.cs
  15. 94
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Crypto/WeChatCryptoService.cs
  16. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/en.json
  17. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/Resources/zh-Hans.json
  18. 8
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Localization/WeChatCommonResource.cs
  19. 12
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/AbpWeChatMessageResolveOptions.cs
  20. 40
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/AbpWeChatMessageHandleOptions.cs
  21. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IEventHandleContributor.cs
  22. 8
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandleContributor.cs
  23. 10
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/IMessageHandler.cs
  24. 14
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandleContext.cs
  25. 54
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/Handlers/MessageHandler.cs
  26. 11
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContext.cs
  27. 20
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContextExtensions.cs
  28. 9
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolveContributor.cs
  29. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/IMessageResolver.cs
  30. 27
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContext.cs
  31. 9
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveContributorBase.cs
  32. 34
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveData.cs
  33. 17
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolveResult.cs
  34. 88
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/MessageResolver.cs
  35. 14
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatEventMessage.cs
  36. 14
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatGeneralMessage.cs
  37. 44
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessage.cs
  38. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Messages/WeChatMessageEto.cs
  39. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Security/Claims/AbpWeChatClaimTypes.cs
  40. 106
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/Cryptography.cs
  41. 96
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/Utils/WXBizMsgCrypt.cs
  42. 5
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN/Abp/WeChat/Common/WeChatCommonErrorCodes.cs
  43. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/README.md
  44. 10
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatObjectSerializeExtensions.cs
  45. 66
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/System/WeChatXmlDataSerializeExtensions.cs
  46. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xml
  47. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xsd
  48. 15
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj
  49. 10
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationContractsModule.cs
  50. 8
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialRemoteServiceConsts.cs
  51. 5
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/Dto/ParametricQrCodeGenerateInput.cs
  52. 9
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeAppService.cs
  53. 10
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageHandleInput.cs
  54. 12
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/Dto/MessageValidationInput.cs
  55. 28
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Message/IWeChatMessageAppService.cs
  56. 25
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN/Abp/WeChat/Official/Models/WeChatMessage.cs
  57. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xml
  58. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xsd
  59. 20
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN.Abp.WeChat.Official.Application.csproj
  60. 13
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialApplicationModule.cs
  61. 24
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeAppService.cs
  62. 85
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageAppService.cs
  63. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xml
  64. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xsd
  65. 19
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN.Abp.WeChat.Official.HttpApi.csproj
  66. 19
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialHttpApiModule.cs
  67. 28
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeController.cs
  68. 39
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN/Abp/WeChat/Official/Message/WeChatMessageController.cs
  69. 57
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs
  70. 7
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptions.cs
  71. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs
  72. 6
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Enums/SceneEnum.cs
  73. 29
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/IParametricQrCodeGenerator.cs
  74. 97
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/CreateTicketModel.cs
  75. 22
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/EnumScene.cs
  76. 5
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/Scene.cs
  77. 22
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/StringScene.cs
  78. 25
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModel.cs
  79. 26
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/Models/TicketModelCacheItem.cs
  80. 87
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Account/ParametricQrCodeGenerator.cs
  81. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Localization/Resources/zh-Hans.json
  82. 25
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/AbpWeChatOfficialMessageResolveOptions.cs
  83. 23
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/TextMessageReplyContributor.cs
  84. 23
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/UserSubscribeEventContributor.cs
  85. 54
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialEventEventHandler.cs
  86. 54
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Handlers/WeChatOfficialMessageEventHandler.cs
  87. 22
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/CustomMenuEvent.cs
  88. 36
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/GeoLocationMessage.cs
  89. 31
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/LinkMessage.cs
  90. 21
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/MenuClickJumpLinkEvent.cs
  91. 26
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ParametricQrCodeEvent.cs
  92. 26
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/PictureMessage.cs
  93. 31
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/ReportingGeoLocationEvent.cs
  94. 21
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/TextMessage.cs
  95. 15
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserSubscribeEvent.cs
  96. 15
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/UserUnSubscribeEvent.cs
  97. 26
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VideoMessage.cs
  98. 35
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/Models/VoiceMessage.cs
  99. 19
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventMessageEto.cs
  100. 29
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/Messages/WeChatOfficialEventResolveContributor.cs

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

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

2
aspnet-core/framework/authentication/LINGYUN.Abp.Authentication.WeChat/LINGYUN/Abp/Authentication/WeChat/AbpAuthenticationWeChatConsts.cs

@ -20,7 +20,7 @@ public static class AbpAuthenticationWeChatConsts
/// <summary>
/// 回调地址
/// </summary>
public static string CallbackPath { get; set; } = "/signin-callback";
public static string CallbackPath { get; set; } = "/signin-wechat";
/// <summary>
/// 微信客户端外的网页登录

162
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
/// </summary>
public class WeChatOfficialOAuthHandler : OAuthHandler<WeChatOfficialOAuthOptions>
{
protected IDistributedCache<WeChatOfficialStateCacheItem> Cache { get; }
protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; }
public WeChatOfficialOAuthHandler(
IDistributedCache<WeChatOfficialStateCacheItem> cache,
IOptionsMonitor<WeChatOfficialOAuthOptions> 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();
}
/// <summary>
/// 第一步:构建用户授权地址
/// </summary>
protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
{
var isWeChatBrewserRequest = IsWeChatBrowser();
protected override async Task<AuthenticationTicket> 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<string, string>
{
{ "appid", Options.ClientId },
{ "redirect_uri", redirectUri },
{ "response_type", "code" },
{ "scope", scope },
{ "state", Guid.NewGuid().ToString("N") },
};
return $"{QueryHelpers.AddQueryString(endPoint, parameters)}#wechat_redirect";
}
/// <summary>
/// 第二步:code换取access_token
/// </summary>
protected async override Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
{
var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
var parameters = new Dictionary<string, string>()
{
["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);
}
/// <summary>
/// code换取access_token
/// </summary>
protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
/// 第三步:构建用户票据
/// </summary>
/// <param name="identity"></param>
/// <param name="properties"></param>
/// <param name="tokens"></param>
/// <returns></returns>
/// <exception cref="HttpRequestException"></exception>
protected async override Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{
var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, new Dictionary<string, string>()
var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
{
["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);
}
/// <summary>
/// 构建用户授权地址
/// </summary>
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<string, string>
{
["appid"] = Options.ClientId,
["redirect_uri"] = redirectUri,
["response_type"] = "code"
});
challengeUrl += $"&scope={scope}&state={state.ToMd5()}";
return challengeUrl;
public override Task<bool> HandleRequestAsync()
{
return base.HandleRequestAsync();
}
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
protected async override Task<HandleRequestResult> 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)
{

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xml

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

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

21
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Common/LINGYUN.Abp.WeChat.Common.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\WeChat\Common\Localization\Resources\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\WeChat\Common\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
</ItemGroup>
</Project>

33
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<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpWeChatCommonModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<WeChatCommonResource>("zh-Hans")
.AddVirtualJson("/LINGYUN/Abp/WeChat/Common/Localization/Resources");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace(WeChatCommonErrorCodes.Namespace, typeof(WeChatCommonResource));
});
}
}

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatException.cs → 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
{

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

29
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
{
/// <summary>
/// 校验
/// </summary>
/// <param name="data"></param>
/// <param name="sReplyEchoStr"></param>
/// <returns></returns>
string Validation(WeChatCryptoEchoData data);
/// <summary>
/// 解密
/// </summary>
/// <param name="data"></param>
/// <param name="sMsg"></param>
/// <returns></returns>
string Decrypt(WeChatCryptoDecryptData data);
/// <summary>
/// 加密
/// </summary>
/// <param name="data"></param>
/// <param name="sEncryptMsg"></param>
/// <returns></returns>
string Encrypt(WeChatCryptoEncryptData data);
}
}

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

11
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Models/WeChatWorkCryptoData.cs → 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;

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

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

16
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失败"
}
}

16
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失败"
}
}

8
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
{
}

12
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<IMessageResolveContributor> MessageResolvers { get; }
public AbpWeChatMessageResolveOptions()
{
MessageResolvers = new List<IMessageResolveContributor>();
}
}

40
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<Type, IList<Type>> EventHandlers { get; }
internal IDictionary<Type, IList<Type>> MessageHandlers { get; }
public AbpWeChatMessageHandleOptions()
{
EventHandlers = new Dictionary<Type, IList<Type>>();
MessageHandlers = new Dictionary<Type, IList<Type>>();
}
public void MapEvent<TEvent, TEventHandler>()
where TEvent : WeChatEventMessage
where TEventHandler : IEventHandleContributor<TEvent>
{
var eventType = typeof(TEvent);
if (!EventHandlers.ContainsKey(eventType))
{
EventHandlers.Add(eventType, new List<Type>());
}
EventHandlers[eventType].AddIfNotContains(typeof(TEventHandler));
}
public void MapMessage<TMessage, TMessageHandler>()
where TMessage : WeChatGeneralMessage
where TMessageHandler : IMessageHandleContributor<TMessage>
{
var eventType = typeof(TMessage);
if (!MessageHandlers.ContainsKey(eventType))
{
MessageHandlers.Add(eventType, new List<Type>());
}
MessageHandlers[eventType].AddIfNotContains(typeof(TMessageHandler));
}
}

7
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<TMessage> where TMessage : WeChatEventMessage
{
Task HandleAsync(MessageHandleContext<TMessage> context);
}

8
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<TMessage> where TMessage : WeChatGeneralMessage
{
Task HandleAsync(MessageHandleContext<TMessage> context);
}

10
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>(TMessage data) where TMessage : WeChatEventMessage;
Task HandleMessageAsync<TMessage>(TMessage data) where TMessage : WeChatGeneralMessage;
}

14
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<TMessage> where TMessage : WeChatMessage
{
public TMessage Message { get; }
public IServiceProvider ServiceProvider { get; }
public MessageHandleContext(TMessage message, IServiceProvider serviceProvider)
{
Message = message;
ServiceProvider = serviceProvider;
}
}

54
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<AbpWeChatMessageHandleOptions> handleOptions)
{
_serviceScopeFactory = serviceScopeFactory;
_handleOptions = handleOptions.Value;
}
public async virtual Task HandleEventAsync<TMessage>(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<TMessage> handler)
{
var context = new MessageHandleContext<TMessage>(data, scope.ServiceProvider);
await handler.HandleAsync(context);
}
}
}
}
public async virtual Task HandleMessageAsync<TMessage>(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<TMessage> handler)
{
var context = new MessageHandleContext<TMessage>(data, scope.ServiceProvider);
await handler.HandleAsync(context);
}
}
}
}
}

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

20
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<T>(this IMessageResolveContext context) where T : WeChatMessage
{
return context.Origin.DeserializeWeChatMessage<T>();
}
}

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

7
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<MessageResolveResult> ResolveMessageAsync(MessageResolveData messageData);
}

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

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

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

17
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<string> AppliedResolvers { get; }
public MessageResolveResult(string input)
{
Input = input;
AppliedResolvers = new List<string>();
}
}

88
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<AbpWeChatMessageResolveOptions> options,
IWeChatCryptoService cryptoService,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_cryptoService = cryptoService;
_options = options.Value;
}
/// <summary>
/// 解析微信服务器推送消息/事件
/// </summary>
/// <param name="messageData"></param>
/// <returns></returns>
public async virtual Task<MessageResolveResult> ResolveMessageAsync(MessageResolveData messageData)
{
var result = new MessageResolveResult(messageData.Data);
using (var serviceScope = _serviceProvider.CreateScope())
{
/*
* <xml>
* <ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUserName]]></FromUserName>
<CreateTime>1699433172</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[event]]></Event>
<EventKey><![CDATA[eventKey]]></EventKey>
<Ticket><![CDATA[gQH97zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyTVoyOTBoLVJka0YxazFiYnhCMXcAAgTFSktlAwQ8AAAA]]></Ticket>
</xml>
*/
var xmlDocument = XDocument.Parse(messageData.Data);
var encryptData = xmlDocument.Root.Element("Encrypt")?.Value;
if (!encryptData.IsNullOrWhiteSpace())
{
/*
* <xml>
* <ToUserName><![CDATA[toUser]]></ToUserName>
<Encrypt><![CDATA[msg_encrypt]]></Encrypt>
</xml>
*/
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;
}
}

14
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;
/// <summary>
/// 微信事件消息
/// </summary>
public abstract class WeChatEventMessage : WeChatMessage
{
/// <summary>
/// 事件类型
/// </summary>
[XmlElement("Event")]
public string Event { get; set; }
}

14
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;
/// <summary>
/// 微信普通消息
/// </summary>
public abstract class WeChatGeneralMessage : WeChatMessage
{
/// <summary>
/// 消息id,64位整型
/// </summary>
[XmlElement("MsgId")]
public long MsgId { get; set; }
}

44
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;
/// <summary>
/// 微信消息
/// </summary>
[Serializable]
[XmlRoot("xml")]
public abstract class WeChatMessage
{
/// <summary>
/// 开发者微信号
/// </summary>
[XmlElement("ToUserName")]
public string ToUserName { get; set; }
/// <summary>
/// 发送方账号(一个OpenID)
/// </summary>
[XmlElement("FromUserName")]
public string FromUserName { get; set; }
/// <summary>
/// 消息创建时间 (整型)
/// </summary>
[XmlElement("CreateTime")]
public int CreateTime { get; set; }
/// <summary>
/// 消息类型,event
/// </summary>
[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();
}
}

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

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Security/Claims/AbpWeChatClaimTypes.cs → 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
{
/// <summary>
/// 微信认证身份类型,可以像 <see cref="Volo.Abp.Security.Claims.AbpClaimTypes"/> 自行配置

106
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/Cryptography.cs → 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;
}
/// <summary>
@ -32,62 +32,62 @@ namespace LINGYUN.Abp.WeChat.Work.Utils
/// <param name="EncodingAESKey"></param>
/// <returns></returns>
///
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;
}

96
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Utils/WXBizMsgCrypt.cs → 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 = "<Encrypt><![CDATA[";
string EncryptLabelTail = "]]></Encrypt>";
string MsgSigLabelHead = "<MsgSignature><![CDATA[";
string MsgSigLabelTail = "]]></MsgSignature>";
string TimeStampLabelHead = "<TimeStamp><![CDATA[";
string TimeStampLabelTail = "]]></TimeStamp>";
string NonceLabelHead = "<Nonce><![CDATA[";
string NonceLabelTail = "]]></Nonce>";
var EncryptLabelHead = "<Encrypt><![CDATA[";
var EncryptLabelTail = "]]></Encrypt>";
var MsgSigLabelHead = "<MsgSignature><![CDATA[";
var MsgSigLabelTail = "]]></MsgSignature>";
var TimeStampLabelHead = "<TimeStamp><![CDATA[";
var TimeStampLabelTail = "]]></TimeStamp>";
var NonceLabelHead = "<Nonce><![CDATA[";
var NonceLabelTail = "]]></Nonce>";
sEncryptMsg = sEncryptMsg + "<xml>" + 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();
}

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

30
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
{
}
```
### 更新日志

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

66
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<T>(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;
}
}

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xml

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

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

15
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application.Contracts/LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
</Project>

10
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
{
}

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

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

9
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<IRemoteStreamContent> GenerateAsync(ParametricQrCodeGenerateInput input);
}

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

12
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
{
/// <summary>
/// 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、receiveid四个字段,其中msg即为消息内容明文
/// </summary>
[JsonPropertyName("echostr")]
public string EchoStr { get; set; }
}

28
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;
/// <summary>
/// 微信消息接口
/// </summary>
public interface IWeChatMessageAppService : IApplicationService
{
/// <summary>
/// 校验微信消息
/// </summary>
/// <remarks>
/// 参考文档:<see cref="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html"/>
/// </remarks>
/// <param name="input"></param>
/// <returns></returns>
Task<string> Handle(MessageValidationInput input);
/// <summary>
/// 处理微信消息
/// </summary>
/// <remarks>
/// 参考文档:<see cref="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html"/>
/// </remarks>
/// <param name="input"></param>
/// <returns></returns>
Task<string> Handle(MessageHandleInput input);
}

25
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
{
/// <summary>
/// 微信加密签名,
/// signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数
/// </summary>
/// <remarks>
/// 签名计算方法参考: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Before_Develop/Message_encryption_and_decryption.html
/// </remarks>
[JsonPropertyName("signature")]
public string Signature { get; set; }
/// <summary>
/// 时间戳。与nonce结合使用,用于防止请求重放攻击。
/// </summary>
[JsonPropertyName("timestamp")]
public int TimeStamp { get; set; }
/// <summary>
/// 随机数。与timestamp结合使用,用于防止请求重放攻击。
/// </summary>
[JsonPropertyName("nonce")]
public string Nonce { get; set; }
}

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xml

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

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

20
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.Application/LINGYUN.Abp.WeChat.Official.Application.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WeChat.Official\LINGYUN.Abp.WeChat.Official.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WeChat.Official.Application.Contracts\LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj" />
</ItemGroup>
</Project>

13
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
{
}

24
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<IRemoteStreamContent> 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);
}
}

85
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<string> 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<string> 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";
}
}

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xml

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

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

19
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official.HttpApi/LINGYUN.Abp.WeChat.Official.HttpApi.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WeChat.Official.Application.Contracts\LINGYUN.Abp.WeChat.Official.Application.Contracts.csproj" />
</ItemGroup>
</Project>

19
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<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpWeChatOfficialHttpApiModule).Assembly);
});
}
}

28
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<IRemoteStreamContent> GenerateAsync(ParametricQrCodeGenerateInput input)
{
return _service.GenerateAsync(input);
}
}

39
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<string> Handle([FromQuery] MessageValidationInput input)
{
return _service.Handle(input);
}
[HttpPost]
public async virtual Task<string> 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);
}
}

57
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<AbpWeChatOfficialMessageResolveOptions>(options =>
{
options.MapEvent("subscribe", context =>
{
return context.HasMessageKey("Ticket")
// 用户未关注公众号时, 扫描带参数二维码后进行关注的事件
? context.GetWeChatMessage<ParametricQrCodeEvent>()
// 用户关注/取消关注
: context.GetWeChatMessage<UserSubscribeEvent>();
});
options.MapEvent("unsubscribe", context => context.GetWeChatMessage<UserUnSubscribeEvent>());
options.MapEvent("LOCATION", context => context.GetWeChatMessage<ReportingGeoLocationEvent>());
options.MapEvent("CLICK", context => context.GetWeChatMessage<CustomMenuEvent>());
options.MapEvent("VIEW", context => context.GetWeChatMessage<MenuClickJumpLinkEvent>());
options.MapEvent("SCAN", context => context.GetWeChatMessage<ParametricQrCodeEvent>());
options.MapMessage("text", context => context.GetWeChatMessage<TextMessage>());
options.MapMessage("image", context => context.GetWeChatMessage<PictureMessage>());
options.MapMessage("voice", context => context.GetWeChatMessage<VoiceMessage>());
options.MapMessage("video", context => context.GetWeChatMessage<VideoMessage>());
options.MapMessage("shortvideo", context => context.GetWeChatMessage<VideoMessage>());
options.MapMessage("location", context => context.GetWeChatMessage<GeoLocationMessage>());
options.MapMessage("link", context => context.GetWeChatMessage<LinkMessage>());
});
Configure<AbpWeChatMessageResolveOptions>(options =>
{
// 事件处理器
options.MessageResolvers.AddIfNotContains(new WeChatOfficialEventResolveContributor());
// 消息处理器
options.MessageResolvers.AddIfNotContains(new WeChatOfficialMessageResolveContributor());
});
Configure<AbpWeChatMessageHandleOptions>(options =>
{
// 回复文本消息
options.MapMessage<TextMessage, TextMessageReplyContributor>();
// 处理关注事件
options.MapEvent<UserSubscribeEvent, UserSubscribeEventContributor>();
});
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpWeChatOfficialModule>();
@ -25,6 +74,12 @@ namespace LINGYUN.Abp.WeChat.Official
});
context.Services.AddAbpDynamicOptions<AbpWeChatOfficialOptions, AbpWeChatOfficialOptionsManager>();
context.Services.AddHttpClient(AbpWeChatOfficialConsts.HttpClient,
options =>
{
options.BaseAddress = new Uri("https://mp.weixin.qq.com");
});
}
}
}

7
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptions.cs

@ -2,6 +2,13 @@
{
public class AbpWeChatOfficialOptions
{
/// <summary>
/// 是否沙盒测试
/// </summary>
/// <remarks>
/// Tips: 当使用测试号时,消息为明文传输,调试时需要启用
/// </remarks>
public bool IsSandBox { get; set; }
/// <summary>
/// 公众号服务器消息Url
/// </summary>

4
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;

6
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,
}

29
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;
/// <summary>
/// 生成带参数的二维码接口
/// </summary>
/// <remarks>
/// 详情见: https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
/// </remarks>
public interface IParametricQrCodeGenerator
{
/// <summary>
/// 创建二维码ticket
/// </summary>
/// <param name="model"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<TicketModel> CreateTicketAsync(CreateTicketModel model, CancellationToken cancellationToken = default);
/// <summary>
/// 通过ticket换取二维码
/// </summary>
/// <param name="ticket"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<Stream> ShowQrCodeAsync(string ticket, CancellationToken cancellationToken = default);
}

97
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
{
/// <summary>
/// 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。
/// </summary>
[JsonProperty("expire_seconds")]
[JsonPropertyName("expire_seconds")]
public int? ExpireSeconds { get; set; }
/// <summary>
/// 二维码类型
/// </summary>
/// <remarks>
/// <list type="table">
/// <item>QR_SCENE为临时的整型参数值</item>
/// <item>QR_STR_SCENE为临时的字符串参数值</item>
/// <item>QR_LIMIT_SCENE为永久的整型参数值</item>
/// <item>QR_LIMIT_STR_SCENE为永久的字符串参数值</item>
/// </list>
/// </remarks>
[JsonProperty("action_name")]
[JsonPropertyName("action_name")]
public string ActionName { get; private set; }
/// <summary>
/// 二维码详细信息
/// </summary>
[JsonProperty("action_info")]
[JsonPropertyName("action_info")]
public Scene SceneInfo { get; private set; }
private CreateTicketModel()
{
}
/// <summary>
/// 通过场景值名称创建临时二维码ticket
/// </summary>
/// <param name="sceneStr">场景值名称</param>
/// <param name="expireSeconds">二维码有效时间</param>
/// <returns></returns>
public static CreateTicketModel StringScene(
string sceneStr,
int expireSeconds = 60)
{
return new CreateTicketModel
{
ExpireSeconds = expireSeconds,
ActionName = "QR_STR_SCENE",
SceneInfo = new StringScene(sceneStr),
};
}
/// <summary>
/// 通过场景值名称创建永久二维码ticket
/// </summary>
/// <param name="sceneStr">场景值名称</param>
/// <returns></returns>
public static CreateTicketModel LimitStringScene(string sceneStr)
{
return new CreateTicketModel
{
ActionName = "QR_LIMIT_STR_SCENE",
SceneInfo = new StringScene(sceneStr),
};
}
/// <summary>
/// 通过场景值标识创建二维码ticket
/// </summary>
/// <param name="sceneId">场景值标识</param>
/// <param name="expireSeconds">二维码有效时间</param>
/// <returns></returns>
public static CreateTicketModel EnumScene(
int sceneId,
int expireSeconds = 60)
{
return new CreateTicketModel
{
ExpireSeconds = expireSeconds,
ActionName = "QR_SCENE",
SceneInfo = new EnumScene(sceneId),
};
}
/// <summary>
/// 通过场景值标识创建永久二维码ticket
/// </summary>
/// <param name="sceneId">场景值标识</param>
/// <returns></returns>
public static CreateTicketModel LimitEnumScene(int sceneId)
{
return new CreateTicketModel
{
ActionName = "QR_LIMIT_SCENE",
SceneInfo = new EnumScene(sceneId),
};
}
}

22
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
{
/// <summary>
/// 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)
/// </summary>
[JsonProperty("scene_id")]
[JsonPropertyName("scene_id")]
public int SceneId { get; }
public EnumScene(int sceneId)
{
SceneId = sceneId;
}
public override string GetKey()
{
return SceneId.ToString();
}
}

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

22
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
{
/// <summary>
/// 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64
/// </summary>
[JsonProperty("scene_str")]
[JsonPropertyName("scene_str")]
public string SceneStr { get; }
public StringScene(string sceneStr)
{
SceneStr = sceneStr;
}
public override string GetKey()
{
return SceneStr;
}
}

25
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
{
/// <summary>
/// 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码
/// </summary>
[JsonProperty("ticket")]
[JsonPropertyName("ticket")]
public string Ticket { get; set; }
/// <summary>
/// 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)。
/// </summary>
[JsonProperty("expire_seconds")]
[JsonPropertyName("expire_seconds")]
public int ExpireSeconds { get; set; }
/// <summary>
/// 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
/// </summary>
[JsonProperty("url")]
[JsonPropertyName("url")]
public string Url { get; set; }
}

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

87
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<TicketModelCacheItem> TicketModelCache { get; }
public ParametricQrCodeGenerator(
IHttpClientFactory httpClientFactory,
IWeChatTokenProvider weChatTokenProvider,
AbpWeChatOfficialOptionsFactory officialOptionsFactory,
IDistributedCache<TicketModelCacheItem> ticketModelCache)
{
TicketModelCache = ticketModelCache;
HttpClientFactory = httpClientFactory;
WeChatTokenProvider = weChatTokenProvider;
OfficialOptionsFactory = officialOptionsFactory;
}
public async virtual Task<TicketModel> 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<Stream> 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<TicketModelCacheItem> 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<TicketModel>(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;
}
}

2
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",

25
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<string, Func<IMessageResolveContext, WeChatEventMessage>> EventMaps { get; }
public IDictionary<string, Func<IMessageResolveContext, WeChatGeneralMessage>> MessageMaps { get; }
public AbpWeChatOfficialMessageResolveOptions()
{
EventMaps = new Dictionary<string, Func<IMessageResolveContext, WeChatEventMessage>>();
MessageMaps = new Dictionary<string, Func<IMessageResolveContext, WeChatGeneralMessage>>();
}
public void MapEvent(string eventName, Func<IMessageResolveContext, WeChatEventMessage> mapFunc)
{
EventMaps[eventName] = mapFunc;
}
public void MapMessage(string messageType, Func<IMessageResolveContext, WeChatGeneralMessage> mapFunc)
{
MessageMaps[messageType] = mapFunc;
}
}

23
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;
/// <summary>
/// 文本消息客服回复
/// </summary>
public class TextMessageReplyContributor : IMessageHandleContributor<TextMessage>
{
public async virtual Task HandleAsync(MessageHandleContext<TextMessage> context)
{
var messageSender = context.ServiceProvider.GetRequiredService<IServiceCenterMessageSender>();
await messageSender.SendAsync(
new Services.Models.TextMessageModel(
context.Message.FromUserName,
new Services.Models.TextMessage(
context.Message.Content)));
}
}

23
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;
/// <summary>
/// 用户关注回复消息
/// </summary>
public class UserSubscribeEventContributor : IEventHandleContributor<UserSubscribeEvent>
{
public async virtual Task HandleAsync(MessageHandleContext<UserSubscribeEvent> context)
{
var messageSender = context.ServiceProvider.GetRequiredService<IServiceCenterMessageSender>();
await messageSender.SendAsync(
new Services.Models.TextMessageModel(
context.Message.FromUserName,
new Services.Models.TextMessage(
"感谢您的关注, 点击菜单了解更多.")));
}
}

54
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<WeChatOfficialEventMessageEto<CustomMenuEvent>>,
IDistributedEventHandler<WeChatOfficialEventMessageEto<UserSubscribeEvent>>,
IDistributedEventHandler<WeChatOfficialEventMessageEto<UserUnSubscribeEvent>>,
IDistributedEventHandler<WeChatOfficialEventMessageEto<ParametricQrCodeEvent>>,
IDistributedEventHandler<WeChatOfficialEventMessageEto<MenuClickJumpLinkEvent>>,
IDistributedEventHandler<WeChatOfficialEventMessageEto<ReportingGeoLocationEvent>>,
ITransientDependency
{
private readonly IMessageHandler _messageHandler;
public WeChatOfficialEventEventHandler(IMessageHandler messageHandler)
{
_messageHandler = messageHandler;
}
public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto<CustomMenuEvent> eventData)
{
await _messageHandler.HandleEventAsync(eventData.Event);
}
public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto<UserSubscribeEvent> eventData)
{
await _messageHandler.HandleEventAsync(eventData.Event);
}
public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto<UserUnSubscribeEvent> eventData)
{
await _messageHandler.HandleEventAsync(eventData.Event);
}
public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto<ParametricQrCodeEvent> eventData)
{
await _messageHandler.HandleEventAsync(eventData.Event);
}
public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto<MenuClickJumpLinkEvent> eventData)
{
await _messageHandler.HandleEventAsync(eventData.Event);
}
public async virtual Task HandleEventAsync(WeChatOfficialEventMessageEto<ReportingGeoLocationEvent> eventData)
{
await _messageHandler.HandleEventAsync(eventData.Event);
}
}

54
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<WeChatOfficialGeneralMessageEto<TextMessage>>,
IDistributedEventHandler<WeChatOfficialGeneralMessageEto<LinkMessage>>,
IDistributedEventHandler<WeChatOfficialGeneralMessageEto<VoiceMessage>>,
IDistributedEventHandler<WeChatOfficialGeneralMessageEto<VideoMessage>>,
IDistributedEventHandler<WeChatOfficialGeneralMessageEto<PictureMessage>>,
IDistributedEventHandler<WeChatOfficialGeneralMessageEto<GeoLocationMessage>>,
ITransientDependency
{
private readonly IMessageHandler _messageHandler;
public WeChatOfficialMessageEventHandler(IMessageHandler messageHandler)
{
_messageHandler = messageHandler;
}
public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto<TextMessage> eventData)
{
await _messageHandler.HandleMessageAsync(eventData.Message);
}
public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto<LinkMessage> eventData)
{
await _messageHandler.HandleMessageAsync(eventData.Message);
}
public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto<VoiceMessage> eventData)
{
await _messageHandler.HandleMessageAsync(eventData.Message);
}
public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto<VideoMessage> eventData)
{
await _messageHandler.HandleMessageAsync(eventData.Message);
}
public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto<PictureMessage> eventData)
{
await _messageHandler.HandleMessageAsync(eventData.Message);
}
public async virtual Task HandleEventAsync(WeChatOfficialGeneralMessageEto<GeoLocationMessage> eventData)
{
await _messageHandler.HandleMessageAsync(eventData.Message);
}
}

22
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;
/// <summary>
/// 自定义菜单事件
/// </summary>
[EventName("custom_menu")]
public class CustomMenuEvent : WeChatEventMessage
{
/// <summary>
/// 事件KEY值
/// </summary>
[XmlElement("EventKey")]
public string EventKey { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialEventMessageEto<CustomMenuEvent>(this);
}
}

36
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;
/// <summary>
/// 地理位置消息
/// </summary>
[EventName("geo_location")]
public class GeoLocationMessage : WeChatOfficialGeneralMessage
{
/// <summary>
/// 地理位置纬度
/// </summary>
[XmlElement("Location_X")]
public double Latitude { get; set; }
/// <summary>
/// 地理位置经度
/// </summary>
[XmlElement("Location_Y")]
public double Longitude { get; set; }
/// <summary>
/// 地图缩放大小
/// </summary>
[XmlElement("Scale")]
public double Scale { get; set; }
/// <summary>
/// 地理位置信息
/// </summary>
[XmlElement("Label")]
public string Label { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialGeneralMessageEto<GeoLocationMessage>(this);
}
}

31
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;
/// <summary>
/// 链接消息
/// </summary>
[EventName("link")]
public class LinkMessage : WeChatOfficialGeneralMessage
{
/// <summary>
/// 消息标题
/// </summary>
[XmlElement("Title")]
public string Title { get; set; }
/// <summary>
/// 消息描述
/// </summary>
[XmlElement("Description")]
public string Description { get; set; }
/// <summary>
/// 消息链接
/// </summary>
[XmlElement("Url")]
public string Url { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialGeneralMessageEto<LinkMessage>(this);
}
}

21
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;
/// <summary>
/// 点击菜单跳转链接时的事件推送
/// </summary>
[EventName("menu_click_jump_link")]
public class MenuClickJumpLinkEvent : WeChatEventMessage
{
/// <summary>
/// 事件KEY值
/// </summary>
[XmlElement("EventKey")]
public string EventKey { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialEventMessageEto<MenuClickJumpLinkEvent>(this);
}
}

26
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;
/// <summary>
/// 扫描带参数二维码事件
/// </summary>
[EventName("parametric_qr_code")]
public class ParametricQrCodeEvent : WeChatEventMessage
{
/// <summary>
/// 事件KEY值
/// </summary>
[XmlElement("EventKey")]
public string EventKey { get; set; }
/// <summary>
/// 二维码的ticket,可用来换取二维码图片
/// </summary>
[XmlElement("Ticket")]
public string Ticket { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialEventMessageEto<ParametricQrCodeEvent>(this);
}
}

26
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;
/// <summary>
/// 图片消息
/// </summary>
[EventName("picture")]
public class PictureMessage : WeChatOfficialGeneralMessage
{
/// <summary>
/// 图片链接(由系统生成)
/// </summary>
[XmlElement("PicUrl")]
public string PicUrl { get; set; }
/// <summary>
/// 图片消息媒体id,可以调用获取临时素材接口拉取数据。
/// </summary>
[XmlElement("MediaId")]
public string MediaId { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialGeneralMessageEto<PictureMessage>(this);
}
}

31
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;
/// <summary>
/// 上报地理位置事件
/// </summary>
[EventName("reporting_geo_location")]
public class ReportingGeoLocationEvent : WeChatEventMessage
{
/// <summary>
/// 地理位置纬度
/// </summary>
[XmlElement("Latitude")]
public double Latitude { get; set; }
/// <summary>
/// 地理位置经度
/// </summary>
[XmlElement("Longitude")]
public double Longitude { get; set; }
/// <summary>
/// 地理位置精度
/// </summary>
[XmlElement("Precision")]
public double Precision { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialEventMessageEto<ReportingGeoLocationEvent>(this);
}
}

21
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;
/// <summary>
/// 文本消息
/// </summary>
[EventName("text")]
public class TextMessage : WeChatOfficialGeneralMessage
{
/// <summary>
/// 文本消息内容
/// </summary>
[XmlElement("Content")]
public string Content { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialGeneralMessageEto<TextMessage>(this);
}
}

15
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;
/// <summary>
/// 用户关注事件
/// </summary>
[EventName("user_subscribe")]
public class UserSubscribeEvent : WeChatEventMessage
{
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialEventMessageEto<UserSubscribeEvent>(this);
}
}

15
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;
/// <summary>
/// 用户取消关注事件
/// </summary>
[EventName("user_un_subscribe")]
public class UserUnSubscribeEvent : WeChatEventMessage
{
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialEventMessageEto<UserUnSubscribeEvent>(this);
}
}

26
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;
/// <summary>
/// 视频消息
/// </summary>
[EventName("video")]
public class VideoMessage : WeChatOfficialGeneralMessage
{
/// <summary>
/// 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
/// </summary>
[XmlElement("ThumbMediaId")]
public string ThumbMediaId { get; set; }
/// <summary>
/// 视频消息媒体id,可以调用获取临时素材接口拉取数据。
/// </summary>
[XmlElement("MediaId")]
public string MediaId { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialGeneralMessageEto<VideoMessage>(this);
}
}

35
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;
/// <summary>
/// 语音消息
/// </summary>
[EventName("voice")]
public class VoiceMessage : WeChatOfficialGeneralMessage
{
/// <summary>
/// 语音格式,如amr,speex等
/// </summary>
[XmlElement("Format")]
public string Format { get; set; }
/// <summary>
/// 语音识别结果,UTF8编码
/// </summary>
/// <remarks>
/// 开通语音识别后,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增加一个Recognition字段(
/// 注:由于客户端缓存,开发者开启或者关闭语音识别功能,对新关注者立刻生效,对已关注用户需要24小时生效。开发者可以重新关注此账号进行测试)。
/// </remarks>
[XmlElement("Recognition")]
public string Recognition { get; set; }
/// <summary>
/// 语音消息媒体id,可以调用获取临时素材接口拉取该媒体
/// </summary>
[XmlElement("MediaId")]
public string MediaId { get; set; }
public override WeChatMessageEto ToEto()
{
return new WeChatOfficialGeneralMessageEto<VoiceMessage>(this);
}
}

19
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<TEvent> : WeChatMessageEto
where TEvent : WeChatEventMessage
{
public TEvent Event { get; set; }
public WeChatOfficialEventMessageEto()
{
}
public WeChatOfficialEventMessageEto(TEvent @event)
{
Event = @event;
}
}

29
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;
/// <summary>
/// 微信公众号事件处理器
/// </summary>
public class WeChatOfficialEventResolveContributor : MessageResolveContributorBase
{
public override string Name => "WeChat.Official.Event";
public override Task ResolveAsync(IMessageResolveContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpWeChatOfficialMessageResolveOptions>>().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;
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save