Browse Source

feature: Integrated WxPusher

pull/661/head
cKey 3 years ago
parent
commit
55bd8b65ac
  1. 37
      aspnet-core/LINGYUN.MicroService.Common.sln
  2. 4
      aspnet-core/modules/pushplus/LINGYUN.Abp.Notifications.PushPlus/LINGYUN.Abp.Notifications.PushPlus.csproj
  3. 15
      aspnet-core/modules/pushplus/LINGYUN.Abp.PushPlus/LINGYUN/Abp/PushPlus/AbpPushPlusModule.cs
  4. 20
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/LINGYUN.Abp.Identity.WxPusher.csproj
  5. 12
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/LINGYUN/Abp/Identity/WxPusher/AbpIdentityWxPusherModule.cs
  6. 46
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/LINGYUN/Abp/Identity/WxPusher/User/IdentityWxPusherUserStore.cs
  7. 13
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/README.md
  8. 16
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN.Abp.Notifications.WxPusher.csproj
  9. 74
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/NotificationDefinitionExtensions.cs
  10. 18
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/AbpNotificationsWxPusherModule.cs
  11. 100
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/WxPusherNotificationPublishProvider.cs
  12. 15
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/README.md
  13. 3
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/FodyWeavers.xml
  14. 30
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/FodyWeavers.xsd
  15. 31
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN.Abp.WxPusher.csproj
  16. 53
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/AbpWxPusherModule.cs
  17. 51
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Features/WxPusherFeatureDefinitionProvider.cs
  18. 28
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Features/WxPusherFeatureNames.cs
  19. 17
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Localization/Resources/en.json
  20. 17
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Localization/Resources/zh-Hans.json
  21. 8
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Localization/WxPusherResource.cs
  22. 11
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/IWxPusherMessageProvider.cs
  23. 17
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/IWxPusherMessageSender.cs
  24. 16
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/MessageContentType.cs
  25. 41
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/MessageHttpClientExtensions.cs
  26. 52
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/SendMessage.cs
  27. 38
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/SendMessageResult.cs
  28. 24
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageProvider.cs
  29. 62
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSender.cs
  30. 39
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/CreateQrcodeRequest.cs
  31. 23
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/CreateQrcodeResult.cs
  32. 35
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/GetScanQrCodeResult.cs
  33. 17
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/IWxPusherQrCodeProvider.cs
  34. 41
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/QrCodeHttpClientExtensions.cs
  35. 56
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/WxPusherQrCodeProvider.cs
  36. 9
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Security/Claims/AbpWxPusherClaimTypes.cs
  37. 30
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Settings/WxPusherSettingDefinitionProvider.cs
  38. 13
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Settings/WxPusherSettingNames.cs
  39. 9
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Token/IWxPusherTokenProvider.cs
  40. 23
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Token/WxPusherTokenProvider.cs
  41. 12
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/FlowType.cs
  42. 56
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/IWxPusherUserProvider.cs
  43. 19
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/IWxPusherUserStore.cs
  44. 22
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/NullWxPusherUserStore.cs
  45. 68
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/UserHttpClientExtensions.cs
  46. 51
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/UserProfile.cs
  47. 87
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/WxPusherUserProvider.cs
  48. 30
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherPagedResult.cs
  49. 15
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherRemoteCallException.cs
  50. 17
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherRequestProvider.cs
  51. 61
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherResult.cs
  52. 19
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/Microsoft/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
  53. 35
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/README.md
  54. 10
      aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/System/Net/Http/IHttpClientFactoryExtensions.cs
  55. 18
      aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN.Abp.WxPusher.Tests.csproj
  56. 7
      aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestBase.cs
  57. 24
      aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestModule.cs
  58. 60
      aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSenderTests.cs

37
aspnet-core/LINGYUN.MicroService.Common.sln

@ -240,15 +240,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.DistributedLock
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pushplus", "pushplus", "{0F5A2591-CE08-4184-A5F3-89F6FB3B2B10}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.PushPlus", "modules\pushplus\LINGYUN.Abp.PushPlus\LINGYUN.Abp.PushPlus.csproj", "{5515C7CA-B512-4E36-A202-49A0158A0E74}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.PushPlus", "modules\pushplus\LINGYUN.Abp.PushPlus\LINGYUN.Abp.PushPlus.csproj", "{5515C7CA-B512-4E36-A202-49A0158A0E74}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.PushPlus", "modules\pushplus\LINGYUN.Abp.Notifications.PushPlus\LINGYUN.Abp.Notifications.PushPlus.csproj", "{EBA67EAD-4958-46E3-9E0C-8186394D083F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.PushPlus", "modules\pushplus\LINGYUN.Abp.Notifications.PushPlus\LINGYUN.Abp.Notifications.PushPlus.csproj", "{EBA67EAD-4958-46E3-9E0C-8186394D083F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Emailing", "modules\common\LINGYUN.Abp.Notifications.Emailing\LINGYUN.Abp.Notifications.Emailing.csproj", "{25891EE2-3166-420F-8408-E458030C4643}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Common", "modules\common\LINGYUN.Abp.Notifications.Common\LINGYUN.Abp.Notifications.Common.csproj", "{F051C960-AA61-4283-A088-611C0B96C953}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.PushPlus.Tests", "tests\LINGYUN.Abp.PushPlus.Tests\LINGYUN.Abp.PushPlus.Tests.csproj", "{1435711B-D796-42AB-B567-0BB23F02EE08}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.PushPlus.Tests", "tests\LINGYUN.Abp.PushPlus.Tests\LINGYUN.Abp.PushPlus.Tests.csproj", "{1435711B-D796-42AB-B567-0BB23F02EE08}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wx-pusher", "wx-pusher", "{7862CE70-76EF-4228-A703-C2E2A9704D14}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WxPusher", "modules\wx-pusher\LINGYUN.Abp.WxPusher\LINGYUN.Abp.WxPusher.csproj", "{1A072FF5-1A7E-4F78-B145-1AB873AEB8FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WxPusher.Tests", "tests\LINGYUN.Abp.WxPusher.Tests\LINGYUN.Abp.WxPusher.Tests.csproj", "{88412E3D-21C8-4FF1-8EB3-84CB74094336}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.WxPusher", "modules\wx-pusher\LINGYUN.Abp.Notifications.WxPusher\LINGYUN.Abp.Notifications.WxPusher.csproj", "{F65A8835-C50F-43B0-B54C-196A92E9539F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Identity.WxPusher", "modules\wx-pusher\LINGYUN.Abp.Identity.WxPusher\LINGYUN.Abp.Identity.WxPusher.csproj", "{30FA01ED-921A-4E7D-9E83-6719538FB866}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -640,6 +650,22 @@ Global
{1435711B-D796-42AB-B567-0BB23F02EE08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1435711B-D796-42AB-B567-0BB23F02EE08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1435711B-D796-42AB-B567-0BB23F02EE08}.Release|Any CPU.Build.0 = Release|Any CPU
{1A072FF5-1A7E-4F78-B145-1AB873AEB8FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A072FF5-1A7E-4F78-B145-1AB873AEB8FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A072FF5-1A7E-4F78-B145-1AB873AEB8FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A072FF5-1A7E-4F78-B145-1AB873AEB8FF}.Release|Any CPU.Build.0 = Release|Any CPU
{88412E3D-21C8-4FF1-8EB3-84CB74094336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88412E3D-21C8-4FF1-8EB3-84CB74094336}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88412E3D-21C8-4FF1-8EB3-84CB74094336}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88412E3D-21C8-4FF1-8EB3-84CB74094336}.Release|Any CPU.Build.0 = Release|Any CPU
{F65A8835-C50F-43B0-B54C-196A92E9539F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F65A8835-C50F-43B0-B54C-196A92E9539F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F65A8835-C50F-43B0-B54C-196A92E9539F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F65A8835-C50F-43B0-B54C-196A92E9539F}.Release|Any CPU.Build.0 = Release|Any CPU
{30FA01ED-921A-4E7D-9E83-6719538FB866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30FA01ED-921A-4E7D-9E83-6719538FB866}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30FA01ED-921A-4E7D-9E83-6719538FB866}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30FA01ED-921A-4E7D-9E83-6719538FB866}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -764,6 +790,11 @@ Global
{25891EE2-3166-420F-8408-E458030C4643} = {B91F26C5-B148-4094-B5F1-71E5F945DBED}
{F051C960-AA61-4283-A088-611C0B96C953} = {B91F26C5-B148-4094-B5F1-71E5F945DBED}
{1435711B-D796-42AB-B567-0BB23F02EE08} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
{7862CE70-76EF-4228-A703-C2E2A9704D14} = {02EA4E78-5891-43BC-944F-3E52FEE032E4}
{1A072FF5-1A7E-4F78-B145-1AB873AEB8FF} = {7862CE70-76EF-4228-A703-C2E2A9704D14}
{88412E3D-21C8-4FF1-8EB3-84CB74094336} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
{F65A8835-C50F-43B0-B54C-196A92E9539F} = {7862CE70-76EF-4228-A703-C2E2A9704D14}
{30FA01ED-921A-4E7D-9E83-6719538FB866} = {7862CE70-76EF-4228-A703-C2E2A9704D14}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8}

4
aspnet-core/modules/pushplus/LINGYUN.Abp.Notifications.PushPlus/LINGYUN.Abp.Notifications.PushPlus.csproj

@ -8,10 +8,6 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Sms" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.PushPlus\LINGYUN.Abp.PushPlus.csproj" />

15
aspnet-core/modules/pushplus/LINGYUN.Abp.PushPlus/LINGYUN/Abp/PushPlus/AbpPushPlusModule.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.Features.LimitValidation;
using LINGYUN.Abp.PushPlus.Channel.Webhook;
using LINGYUN.Abp.PushPlus.Localization;
using LINGYUN.Abp.PushPlus.Message;
using LINGYUN.Abp.PushPlus.Setting;
using LINGYUN.Abp.PushPlus.Token;
@ -9,8 +10,10 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Caching;
using Volo.Abp.Json;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.PushPlus;
@ -49,5 +52,17 @@ public class AbpPushPlusModule : AbpModule
options.UnsupportedTypes.TryAdd<PushPlusResponse<PushPlusUserLimitTime>>();
options.UnsupportedTypes.TryAdd<PushPlusResponse<PushPlusUserProfile>>();
});
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpPushPlusModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<PushPlusResource>()
.AddVirtualJson("/LINGYUN/Abp/PushPlus/Localization/Resources");
});
}
}

20
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/LINGYUN.Abp.Identity.WxPusher.csproj

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

12
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/LINGYUN/Abp/Identity/WxPusher/AbpIdentityWxPusherModule.cs

@ -0,0 +1,12 @@
using LINGYUN.Abp.WxPusher;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Identity.WxPusher;
[DependsOn(
typeof(AbpWxPusherModule),
typeof(AbpIdentityDomainModule))]
public class AbpIdentityWxPusherModule : AbpModule
{
}

46
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/LINGYUN/Abp/Identity/WxPusher/User/IdentityWxPusherUserStore.cs

@ -0,0 +1,46 @@
using LINGYUN.Abp.WxPusher.Security.Claims;
using LINGYUN.Abp.WxPusher.User;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
namespace LINGYUN.Abp.Identity.WxPusher.User;
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(typeof(IWxPusherUserStore))]
public class IdentityWxPusherUserStore : IWxPusherUserStore
{
protected IdentityUserManager UserManager { get; }
public IdentityWxPusherUserStore(IdentityUserManager userManager)
{
UserManager = userManager;
}
public async virtual Task<List<int>> GetSubscribeTopicsAsync(IEnumerable<Guid> userIds, CancellationToken cancellationToken = default)
{
var topics = new List<int>();
foreach (var userId in userIds)
{
var user = await UserManager.FindByIdAsync(userId.ToString());
var userUidClaim = user?.Claims
.Where(c => c.ClaimType.Equals(AbpWxPusherClaimTypes.Uid))
.FirstOrDefault();
if (userUidClaim != null &&
int.TryParse(userUidClaim.ClaimValue, out var topic))
{
topics.Add(topic);
}
}
return topics.Distinct().ToList();
}
}

13
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Identity.WxPusher/README.md

@ -0,0 +1,13 @@
# LINGYUN.Abp.Identity.WxPusher
IWxPusherUserStore 接口的Identity模块实现, 通过用户Claims来获取关注的topic列表
## 模块引用
```csharp
[DependsOn(typeof(AbpIdentityWxPusherModule))]
public class YouProjectModule : AbpModule
{
// other
}
```

16
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN.Abp.Notifications.WxPusher.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WxPusher\LINGYUN.Abp.WxPusher.csproj" />
</ItemGroup>
</Project>

74
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/NotificationDefinitionExtensions.cs

@ -0,0 +1,74 @@
using LINGYUN.Abp.WxPusher.Messages;
using System.Collections.Generic;
namespace LINGYUN.Abp.Notifications;
public static class NotificationDefinitionExtensions
{
private const string Prefix = "wx-pusher:";
private const string ContentTypeKey = Prefix + "contentType";
private const string TopicKey = Prefix + "contentType";
/// <summary>
/// 设定消息内容类型
/// </summary>
/// <param name="notification"></param>
/// <param name="contentType"></param>
/// <returns></returns>
public static NotificationDefinition WithContentType(
this NotificationDefinition notification,
MessageContentType contentType)
{
return notification.WithProperty(ContentTypeKey, contentType);
}
/// <summary>
/// 获取消息发送通道
/// </summary>
/// <param name="notification"></param>
/// <param name="defaultChannelType"></param>
/// <returns></returns>
public static MessageContentType GetContentTypeOrDefault(
this NotificationDefinition notification,
MessageContentType defaultContentType = MessageContentType.Text)
{
if (notification.Properties.TryGetValue(ContentTypeKey, out var defineContentType) == true &&
defineContentType is MessageContentType contentType)
{
return contentType;
}
return defaultContentType;
}
/// <summary>
/// 消息主题(Topic)
/// see: https://wxpusher.dingliqc.com/docs/#/?id=%e5%90%8d%e8%af%8d%e8%a7%a3%e9%87%8a
/// </summary>
/// <param name="notification">群组编码</param>
/// <param name="topics"></param>
/// <returns>
/// <see cref="NotificationDefinition"/>
/// </returns>
public static NotificationDefinition WithTopics(
this NotificationDefinition notification,
List<int> topics)
{
return notification.WithProperty(TopicKey, topics);
}
/// <summary>
/// 获取消息群发群组编码
/// </summary>
/// <param name="notification"></param>
/// <returns>
/// 通知定义的群组编码列表
/// </returns>
public static List<int> GetTopics(
this NotificationDefinition notification)
{
if (notification.Properties.TryGetValue(TopicKey, out var topicsDefine) == true &&
topicsDefine is List<int> topics)
{
return topics;
}
return new List<int>();
}
}

18
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/AbpNotificationsWxPusherModule.cs

@ -0,0 +1,18 @@
using LINGYUN.Abp.WxPusher;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Notifications.WxPusher;
[DependsOn(
typeof(AbpNotificationModule),
typeof(AbpWxPusherModule))]
public class AbpNotificationsPushPlusModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpNotificationOptions>(options =>
{
options.PublishProviders.Add<WxPusherNotificationPublishProvider>();
});
}
}

100
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/LINGYUN/Abp/Notifications/WxPusher/WxPusherNotificationPublishProvider.cs

@ -0,0 +1,100 @@
using LINGYUN.Abp.RealTime.Localization;
using LINGYUN.Abp.WxPusher.Messages;
using LINGYUN.Abp.WxPusher.User;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Notifications.WxPusher;
public class WxPusherNotificationPublishProvider : NotificationPublishProvider
{
public const string ProviderName = "WxPusher";
public override string Name => ProviderName;
protected IWxPusherUserStore WxPusherUserStore { get; }
protected IWxPusherMessageSender WxPusherMessageSender { get; }
protected IStringLocalizerFactory LocalizerFactory { get; }
protected AbpLocalizationOptions LocalizationOptions { get; }
protected INotificationDefinitionManager NotificationDefinitionManager { get; }
public WxPusherNotificationPublishProvider(
IWxPusherUserStore wxPusherUserStore,
IWxPusherMessageSender wxPusherMessageSender,
IStringLocalizerFactory localizerFactory,
IOptions<AbpLocalizationOptions> localizationOptions,
INotificationDefinitionManager notificationDefinitionManager)
{
WxPusherUserStore = wxPusherUserStore;
WxPusherMessageSender = wxPusherMessageSender;
LocalizerFactory = localizerFactory;
LocalizationOptions = localizationOptions.Value;
NotificationDefinitionManager = notificationDefinitionManager;
}
protected async override Task PublishAsync(
NotificationInfo notification,
IEnumerable<UserIdentifier> identifiers,
CancellationToken cancellationToken = default)
{
var topics = await WxPusherUserStore
.GetSubscribeTopicsAsync(
identifiers.Select(x => x.UserId),
cancellationToken);
var notificationDefine = NotificationDefinitionManager.GetOrNull(notification.Name);
var topicDefine = notificationDefine?.GetTopics();
if (topicDefine.Any())
{
topics = topicDefine;
}
var contentType = notificationDefine?.GetContentTypeOrDefault(MessageContentType.Text)
?? MessageContentType.Text;
if (!notification.Data.NeedLocalizer())
{
var title = notification.Data.TryGetData("title").ToString();
var message = notification.Data.TryGetData("message").ToString();
await WxPusherMessageSender.SendAsync(
content: message,
summary: title,
contentType: contentType,
topicIds: topics,
cancellationToken: cancellationToken);
}
else
{
var titleInfo = notification.Data.TryGetData("title").As<LocalizableStringInfo>();
var titleResource = GetResource(titleInfo.ResourceName);
var title = LocalizerFactory.Create(titleResource.ResourceType)[titleInfo.Name, titleInfo.Values].Value;
var messageInfo = notification.Data.TryGetData("message").As<LocalizableStringInfo>();
var messageResource = GetResource(messageInfo.ResourceName);
var message = LocalizerFactory.Create(messageResource.ResourceType)[messageInfo.Name, messageInfo.Values].Value;
await WxPusherMessageSender.SendAsync(
content: message,
summary: title,
contentType: contentType,
topicIds: topics,
cancellationToken: cancellationToken);
}
}
private LocalizationResource GetResource(string resourceName)
{
return LocalizationOptions.Resources.Values
.First(x => x.ResourceName.Equals(resourceName));
}
}

15
aspnet-core/modules/wx-pusher/LINGYUN.Abp.Notifications.WxPusher/README.md

@ -0,0 +1,15 @@
# LINGYUN.Abp.Notifications.WxPusher
通知模块的WxPusher实现
使应用可通过WxPusher发布实时通知
## 模块引用
```csharp
[DependsOn(typeof(AbpNotificationsWxPusherModule))]
public class YouProjectModule : AbpModule
{
// other
}
```

3
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/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/modules/wx-pusher/LINGYUN.Abp.WxPusher/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>

31
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN.Abp.WxPusher.csproj

@ -0,0 +1,31 @@
<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\WxPusher\Localization\Resources\en.json" />
<None Remove="LINGYUN\Abp\WxPusher\Localization\Resources\zh-Hans.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\WxPusher\Localization\Resources\en.json" />
<EmbeddedResource Include="LINGYUN\Abp\WxPusher\Localization\Resources\zh-Hans.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\LINGYUN.Abp.Features.LimitValidation\LINGYUN.Abp.Features.LimitValidation.csproj" />
</ItemGroup>
</Project>

53
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/AbpWxPusherModule.cs

@ -0,0 +1,53 @@
using LINGYUN.Abp.Features.LimitValidation;
using LINGYUN.Abp.WxPusher.Localization;
using LINGYUN.Abp.WxPusher.Messages;
using LINGYUN.Abp.WxPusher.QrCode;
using LINGYUN.Abp.WxPusher.User;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Collections.Generic;
using Volo.Abp.Caching;
using Volo.Abp.Json;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.WxPusher;
[DependsOn(
typeof(AbpJsonModule),
typeof(AbpSettingsModule),
typeof(AbpCachingModule),
typeof(AbpFeaturesLimitValidationModule))]
public class AbpWxPusherModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddWxPusherClient();
context.Services.TryAddSingleton<IWxPusherUserStore>(NullWxPusherUserStore.Instance);
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.UnsupportedTypes.TryAdd<WxPusherResult<int>>();
options.UnsupportedTypes.TryAdd<WxPusherResult<string>>();
options.UnsupportedTypes.TryAdd<WxPusherResult<CreateQrcodeResult>>();
options.UnsupportedTypes.TryAdd<WxPusherResult<GetScanQrCodeResult>>();
options.UnsupportedTypes.TryAdd<WxPusherResult<List<SendMessageResult>>>();
options.UnsupportedTypes.TryAdd<WxPusherResult<WxPusherPagedResult<UserProfile>>>();
});
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpWxPusherModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<WxPusherResource>()
.AddVirtualJson("/LINGYUN/Abp/WxPusher/Localization/Resources");
});
}
}

51
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Features/WxPusherFeatureDefinitionProvider.cs

@ -0,0 +1,51 @@
using LINGYUN.Abp.WxPusher.Localization;
using Volo.Abp.Features;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace LINGYUN.Abp.WxPusher.Features;
public class WxPusherFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var group = context.AddGroup(
name: WxPusherFeatureNames.GroupName,
displayName: L("Features:WxPusher"));
group.AddFeature(
name: WxPusherFeatureNames.Enable,
defaultValue: "true",
displayName: L("Features:WxPusherEnable"),
description: L("Features:WxPusherEnableDesc"),
valueType: new ToggleStringValueType(new BooleanValueValidator()));
var message = group.AddFeature(
name: WxPusherFeatureNames.Message.GroupName,
displayName: L("Features:Message"),
description: L("Features:Message"));
message.CreateChild(
name: WxPusherFeatureNames.Message.Enable,
defaultValue: "true",
displayName: L("Features:MessageEnable"),
description: L("Features:MessageEnableDesc"),
valueType: new ToggleStringValueType(new BooleanValueValidator()));
message.CreateChild(
name: WxPusherFeatureNames.Message.SendLimit,
defaultValue: "500",
displayName: L("Features:Message.SendLimit"),
description: L("Features:Message.SendLimitDesc"),
valueType: new FreeTextStringValueType(new NumericValueValidator(1, 500)));
message.CreateChild(
name: WxPusherFeatureNames.Message.SendLimitInterval,
defaultValue: "1",
displayName: L("Features:Message.SendLimitInterval"),
description: L("Features:Message.SendLimitIntervalDesc"),
valueType: new FreeTextStringValueType(new NumericValueValidator(1, 1)));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<WxPusherResource>(name);
}
}

28
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Features/WxPusherFeatureNames.cs

@ -0,0 +1,28 @@
namespace LINGYUN.Abp.WxPusher.Features;
public static class WxPusherFeatureNames
{
public const string GroupName = "WxPusher";
/// <summary>
/// 启用WxPusher
/// </summary>
public const string Enable = GroupName + ".Enable";
public static class Message
{
public const string GroupName = WxPusherFeatureNames.GroupName + ".Message";
/// <summary>
/// 启用消息推送
/// </summary>
public const string Enable = GroupName + ".Enable";
/// <summary>
/// 发送次数上限
/// </summary>
public const string SendLimit = GroupName + ".SendLimit";
/// <summary>
/// 发送次数上限时长
/// </summary>
public const string SendLimitInterval = GroupName + ".SendLimitInterval";
}
}

17
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Localization/Resources/en.json

@ -0,0 +1,17 @@
{
"culture": "en",
"texts": {
"Settings:Security.AppToken": "App Token",
"Settings:Security.AppTokenDesc": "If you have APP_TOKEN, you can send messages to users of the corresponding application. Keep it confidential.",
"Features:WxPusher": "WxPusher Wechat push service",
"Features:WxPusherEnable": "Enable WxPusher",
"Features:WxPusherEnableDesc": "Enable to enable the application to have WxPusher capabilities.",
"Features:Message": "WxPusher Wechat message push",
"Features:MessageEnable": "Enable WxPusher Wechat Message Push",
"Features:MessageEnableDesc": "Enable so that apps will have the ability to be pushed to wechat via WxPusher.",
"Features:Message.SendLimit": "Amount of wechat message push",
"Features:Message.SendLimitDesc": "Set to limit the amount of wechat message push.",
"Features:Message.SendLimitInterval": "Wechat message limit interval",
"Features:Message.SendLimitIntervalDesc": "Set the wechat message limit period (time scale: days). A single wechat user (UID) can receive a maximum of 500 messages per day. Please arrange the sending frequency reasonably."
}
}

17
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Localization/Resources/zh-Hans.json

@ -0,0 +1,17 @@
{
"culture": "zh-Hans",
"texts": {
"Settings:Security.AppToken": "应用的身份标志",
"Settings:Security.AppTokenDesc": "拥有APP_TOKEN,就可以给对应的应用的用户发送消息, 请严格保密.",
"Features:WxPusher": "WxPusher微信推送服务",
"Features:WxPusherEnable": "启用WxPusher",
"Features:WxPusherEnableDesc": "启用以使应用拥有WxPusher的能力.",
"Features:Message": "WxPusher微信消息推送",
"Features:MessageEnable": "启用WxPusher微信消息推送",
"Features:MessageEnableDesc": "启用以使应用将拥有通过WxPusher推送到微信的能力.",
"Features:Message.SendLimit": "微信消息推送量",
"Features:Message.SendLimitDesc": "设置以限制微信消息推送量.",
"Features:Message.SendLimitInterval": "微信消息限制周期",
"Features:Message.SendLimitIntervalDesc": "设置微信消息限制周期(时间刻度: 天).单个微信用户(uid),每天最多接收500条消息,请合理安排发送频率."
}
}

8
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Localization/WxPusherResource.cs

@ -0,0 +1,8 @@
using Volo.Abp.Localization;
namespace LINGYUN.Abp.WxPusher.Localization;
[LocalizationResourceName("WxPusher")]
public class WxPusherResource
{
}

11
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/IWxPusherMessageProvider.cs

@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.Messages;
public interface IWxPusherMessageProvider
{
Task<WxPusherResult<int>> QueryMessageAsync(
int messageId,
CancellationToken cancellationToken = default);
}

17
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/IWxPusherMessageSender.cs

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.Messages;
public interface IWxPusherMessageSender
{
Task<List<SendMessageResult>> SendAsync(
string content,
string summary = "",
MessageContentType contentType = MessageContentType.Text,
List<int> topicIds = null,
List<string> uids = null,
string url = "",
CancellationToken cancellationToken = default);
}

16
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/MessageContentType.cs

@ -0,0 +1,16 @@
namespace LINGYUN.Abp.WxPusher.Messages;
public enum MessageContentType
{
/// <summary>
/// 文字
/// </summary>
Text = 1,
/// <summary>
/// html(只发送body标签内部的数据即可,不包括body标签
/// </summary>
Html = 2,
/// <summary>
/// markdown
/// </summary>
Markdown = 3
}

41
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/MessageHttpClientExtensions.cs

@ -0,0 +1,41 @@
using Newtonsoft.Json;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.Messages;
internal static class MessageHttpClientExtensions
{
public async static Task<string> SendMessageAsync(
this HttpClient httpClient,
SendMessage sendMessage,
CancellationToken cancellationToken = default)
{
var requestMessage = new HttpRequestMessage(
HttpMethod.Post,
"/api/send/message");
var requestBody = JsonConvert.SerializeObject(sendMessage);
requestMessage.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
public async static Task<string> QueryMessageAsync(
this HttpClient httpClient,
int messageId,
CancellationToken cancellationToken = default)
{
var requestMessage = new HttpRequestMessage(
HttpMethod.Get,
$"/api/send/query/{messageId}");
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
}

52
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/SendMessage.cs

@ -0,0 +1,52 @@
using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Volo.Abp;
namespace LINGYUN.Abp.WxPusher.Messages;
[Serializable]
public class SendMessage
{
[JsonProperty("appToken")]
public string AppToken { get; }
[JsonProperty("content")]
public string Content { get; }
[JsonProperty("summary")]
public string Summary { get; set; }
[JsonProperty("contentType")]
public MessageContentType ContentType { get; }
[JsonProperty("topicIds")]
public List<int> TopicIds { get; }
[JsonProperty("uids")]
public List<string> Uids { get; }
[JsonProperty("url")]
public string Url { get; }
public SendMessage(
[NotNull] string appToken,
[NotNull] string content,
string summary = "",
MessageContentType contentType = MessageContentType.Text,
string url = "")
{
Check.NotNullOrWhiteSpace(appToken, nameof(appToken));
Check.NotNullOrWhiteSpace(content, nameof(content));
Check.Length(summary, nameof(summary), 100);
AppToken = appToken;
Content = content;
Summary = summary;
ContentType = contentType;
Url = url;
TopicIds = new List<int>();
Uids = new List<string>();
}
}

38
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/SendMessageResult.cs

@ -0,0 +1,38 @@
using Newtonsoft.Json;
using System;
namespace LINGYUN.Abp.WxPusher.Messages;
[Serializable]
public class SendMessageResult
{
/// <summary>
/// 状态码
/// </summary>
[JsonProperty("code")]
public int Code { get; set; }
/// <summary>
/// 消息标识
/// </summary>
[JsonProperty("messageId")]
public long MessageId { get; set; }
/// <summary>
/// 状态
/// </summary>
[JsonProperty("status")]
public string Status { get; set; }
/// <summary>
/// 用户标识
/// </summary>
[JsonProperty("uid")]
public string Uid { get; set; }
/// <summary>
/// 群组标识
/// </summary>
[JsonProperty("topicId")]
public string TopicId { get; set; }
/// <summary>
/// 是否调用成功
/// </summary>
public bool IsSuccessed => Code == 1000;
}

24
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageProvider.cs

@ -0,0 +1,24 @@
using LINGYUN.Abp.WxPusher.Features;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Features;
namespace LINGYUN.Abp.WxPusher.Messages;
[RequiresFeature(WxPusherFeatureNames.Enable)]
public class WxPusherMessageProvider : WxPusherRequestProvider, IWxPusherMessageProvider
{
public async virtual Task<WxPusherResult<int>> QueryMessageAsync(
int messageId,
CancellationToken cancellationToken = default)
{
var client = HttpClientFactory.GetPushPlusClient();
var content = await client.QueryMessageAsync(
messageId,
cancellationToken);
return JsonSerializer.Deserialize<WxPusherResult<int>>(content);
}
}

62
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSender.cs

@ -0,0 +1,62 @@
using LINGYUN.Abp.Features.LimitValidation;
using LINGYUN.Abp.WxPusher.Features;
using LINGYUN.Abp.WxPusher.Token;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Features;
namespace LINGYUN.Abp.WxPusher.Messages;
[RequiresFeature(WxPusherFeatureNames.Enable)]
public class WxPusherMessageSender : WxPusherRequestProvider, IWxPusherMessageSender
{
protected IWxPusherTokenProvider WxPusherTokenProvider { get; }
public WxPusherMessageSender(IWxPusherTokenProvider wxPusherTokenProvider)
{
WxPusherTokenProvider = wxPusherTokenProvider;
}
[RequiresFeature(WxPusherFeatureNames.Message.Enable)]
[RequiresLimitFeature(
WxPusherFeatureNames.Message.SendLimit,
WxPusherFeatureNames.Message.SendLimitInterval,
LimitPolicy.Days)]
public async virtual Task<List<SendMessageResult>> SendAsync(
string content,
string summary = "",
MessageContentType contentType = MessageContentType.Text,
List<int> topicIds = null,
List<string> uids = null,
string url = "",
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.GetPushPlusClient();
var sendMessage = new SendMessage(
token,
content,
summary,
contentType,
url);
if (topicIds != null)
{
sendMessage.TopicIds.AddIfNotContains(topicIds);
}
if (uids != null)
{
sendMessage.Uids.AddIfNotContains(uids);
}
var resultContent = await client.SendMessageAsync(
sendMessage,
cancellationToken);
var response = JsonSerializer
.Deserialize<WxPusherResult<List<SendMessageResult>>>(resultContent);
return response.GetData();
}
}

39
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/CreateQrcodeRequest.cs

@ -0,0 +1,39 @@
using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using Volo.Abp;
namespace LINGYUN.Abp.WxPusher.QrCode;
[Serializable]
public class CreateQrcodeRequest
{
/// <summary>
/// 应用的标志
/// </summary>
[JsonProperty("appToken")]
public string AppToken { get; }
/// <summary>
/// 二维码携带的参数,最长64位
/// </summary>
[JsonProperty("extra")]
public string Extra { get; }
/// <summary>
/// 二维码有效时间,s为单位,最大30天。
/// </summary>
[JsonProperty("validTime")]
public int ValidTime { get; }
public CreateQrcodeRequest(
[NotNull] string appToken,
[NotNull] string extra,
int validTime = 1800)
{
Check.NotNullOrWhiteSpace(appToken, nameof(appToken));
Check.NotNullOrWhiteSpace(extra, nameof(extra), 64);
AppToken = appToken;
Extra = extra;
ValidTime = validTime;
}
}

23
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/CreateQrcodeResult.cs

@ -0,0 +1,23 @@
using Newtonsoft.Json;
using System;
namespace LINGYUN.Abp.WxPusher.QrCode;
[Serializable]
public class CreateQrcodeResult
{
[JsonProperty("expires")]
public long Expires { get; set; }
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("shortUrl")]
public string ShortUrl { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("extra")]
public string Extra { get; set; }
}

35
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/GetScanQrCodeResult.cs

@ -0,0 +1,35 @@
using Newtonsoft.Json;
using System;
namespace LINGYUN.Abp.WxPusher.QrCode;
[Serializable]
public class GetScanQrCodeResult
{
[JsonProperty("appId")]
public int AppId { get; set; }
[JsonProperty("appKey")]
public string AppKey { get; set; }
[JsonProperty("appName")]
public string AppName { get; set; }
[JsonProperty("extra")]
public string Extra { get; set; }
[JsonProperty("source")]
public string Source { get; set; }
[JsonProperty("time")]
public long Time { get; set; }
[JsonProperty("uid")]
public string Uid { get; set; }
[JsonProperty("userHeadImg")]
public string UserHeadImg { get; set; }
[JsonProperty("userName")]
public string UserName { get; set; }
}

17
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/IWxPusherQrCodeProvider.cs

@ -0,0 +1,17 @@
using JetBrains.Annotations;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.QrCode;
public interface IWxPusherQrCodeProvider
{
Task<CreateQrcodeResult> CreateQrcodeAsync(
[NotNull] string extra,
int validTime = 1800,
CancellationToken cancellationToken = default);
Task<GetScanQrCodeResult> GetScanQrCodeAsync(
[NotNull] string code,
CancellationToken cancellationToken = default);
}

41
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/QrCodeHttpClientExtensions.cs

@ -0,0 +1,41 @@
using Newtonsoft.Json;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.QrCode;
internal static class QrCodeHttpClientExtensions
{
public async static Task<string> CreateQrcodeAsync(
this HttpClient httpClient,
CreateQrcodeRequest qrcodeRequest,
CancellationToken cancellationToken = default)
{
var requestMessage = new HttpRequestMessage(
HttpMethod.Post,
"/api/fun/create/qrcode");
var requestBody = JsonConvert.SerializeObject(qrcodeRequest);
requestMessage.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
public async static Task<string> GetScanQrCodeUidAsync(
this HttpClient httpClient,
string code,
CancellationToken cancellationToken = default)
{
var requestMessage = new HttpRequestMessage(
HttpMethod.Get,
$"/api/fun/scan-qrcode-uid?code={code}");
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
}

56
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/WxPusherQrCodeProvider.cs

@ -0,0 +1,56 @@
using JetBrains.Annotations;
using LINGYUN.Abp.WxPusher.Features;
using LINGYUN.Abp.WxPusher.Token;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Features;
namespace LINGYUN.Abp.WxPusher.QrCode;
[RequiresFeature(WxPusherFeatureNames.Enable)]
public class WxPusherQrCodeProvider : WxPusherRequestProvider, IWxPusherQrCodeProvider
{
protected IWxPusherTokenProvider WxPusherTokenProvider { get; }
public WxPusherQrCodeProvider(IWxPusherTokenProvider wxPusherTokenProvider)
{
WxPusherTokenProvider = wxPusherTokenProvider;
}
public async virtual Task<CreateQrcodeResult> CreateQrcodeAsync(
[NotNull] string extra,
int validTime = 1800,
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.GetPushPlusClient();
var request = new CreateQrcodeRequest(token, extra, validTime);
var content = await client.CreateQrcodeAsync(
request,
cancellationToken);
var response = JsonSerializer.Deserialize<WxPusherResult<CreateQrcodeResult>>(content);
return response.GetData();
}
public async virtual Task<GetScanQrCodeResult> GetScanQrCodeAsync(
[NotNull] string code,
CancellationToken cancellationToken = default)
{
Check.NotNullOrWhiteSpace(code, nameof(code));
var client = HttpClientFactory.GetPushPlusClient();
var content = await client.GetScanQrCodeUidAsync(
code,
cancellationToken);
var response = JsonSerializer.Deserialize<WxPusherResult<GetScanQrCodeResult>>(content);
return response.GetData();
}
}

9
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Security/Claims/AbpWxPusherClaimTypes.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.WxPusher.Security.Claims;
public static class AbpWxPusherClaimTypes
{
/// <summary>
/// 用户的唯一标识
/// </summary>
public static string Uid { get; set; } = "wx-pusher-uid";
}

30
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Settings/WxPusherSettingDefinitionProvider.cs

@ -0,0 +1,30 @@
using LINGYUN.Abp.WxPusher.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Settings;
namespace LINGYUN.Abp.WxPusher.Settings;
public class WxPusherSettingDefinitionProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
context.Add(new[]
{
new SettingDefinition(
name: WxPusherSettingNames.Security.AppToken,
displayName: L("Settings:Security.AppToken"),
description: L("Settings:Security.AppTokenDesc"),
isEncrypted: true)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
});
}
public static ILocalizableString L(string name)
{
return LocalizableString.Create<WxPusherResource>(name);
}
}

13
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Settings/WxPusherSettingNames.cs

@ -0,0 +1,13 @@
namespace LINGYUN.Abp.WxPusher.Settings;
public static class WxPusherSettingNames
{
public const string Prefix = "WxPusher";
public static class Security
{
public const string Prefix = WxPusherSettingNames.Prefix + ".Security";
public const string AppToken = Prefix + ".AppToken";
}
}

9
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Token/IWxPusherTokenProvider.cs

@ -0,0 +1,9 @@
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.Token;
public interface IWxPusherTokenProvider
{
Task<string> GetTokenAsync(CancellationToken cancellationToken = default);
}

23
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Token/WxPusherTokenProvider.cs

@ -0,0 +1,23 @@
using LINGYUN.Abp.WxPusher.Settings;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Settings;
namespace LINGYUN.Abp.WxPusher.Token;
public class WxPusherTokenProvider : IWxPusherTokenProvider, ITransientDependency
{
protected ISettingProvider SettingProvider { get; }
public WxPusherTokenProvider(ISettingProvider settingProvider)
{
SettingProvider = settingProvider;
}
public async virtual Task<string> GetTokenAsync(CancellationToken cancellationToken = default)
{
return await SettingProvider.GetOrNullAsync(
WxPusherSettingNames.Security.AppToken);
}
}

12
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/FlowType.cs

@ -0,0 +1,12 @@
namespace LINGYUN.Abp.WxPusher.User;
public enum FlowType
{
/// <summary>
/// 关注应用
/// </summary>
App = 0,
/// <summary>
/// 关注topic
/// </summary>
Topic = 1,
}

56
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/IWxPusherUserProvider.cs

@ -0,0 +1,56 @@
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.User;
/// <summary>
/// 用户接口
/// </summary>
public interface IWxPusherUserProvider
{
/// <summary>
/// 查询App的关注用户V2
/// </summary>
/// <remarks>
/// 获取到所有关注应用/主题的微信用户用户信息。需要注意,一个微信用户,如果同时关注应用,主题,甚至关注多个主题,会返回多条记录。
/// </remarks>
/// <param name="page">请求数据的页码</param>
/// <param name="pageSize">分页大小,不能超过100</param>
/// <param name="uid">用户的uid,可选,如果不传就是查询所有用户,传uid就是查某个用户的信息。</param>
/// <param name="isBlock">查询拉黑用户,可选,不传查询所有用户,true查询拉黑用户,false查询没有拉黑的用户</param>
/// <param name="type">关注的类型,可选,不传查询所有用户,0是应用,1是主题</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<WxPusherPagedResult<UserProfile>> GetUserListAsync(
int page = 1,
int pageSize = 10,
string uid = null,
bool? isBlock = null,
FlowType? type = null,
CancellationToken cancellationToken = default);
/// <summary>
/// 删除用户
/// </summary>
/// <remarks>
/// 你可以删除用户对应用、主题的关注,删除以后,用户可以重新关注,如想让用户再次关注,可以调用拉黑接口,对用户拉黑。
/// </remarks>
/// <param name="id">用户id,通过用户查询接口可以获取</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<bool> DeleteUserAsync(
int id,
CancellationToken cancellationToken = default);
/// <summary>
/// 拉黑用户
/// </summary>
/// <remarks>
/// 拉黑以后不能再发送消息,用户也不能再次关注,除非你取消对他的拉黑。调用拉黑接口,不用再调用删除接口。
/// </remarks>
/// <param name="id">用户id,通过用户查询接口可以获取</param>
/// <param name="reject">是否拉黑,true表示拉黑,false表示取消拉黑</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<bool> RejectUserAsync(
int id,
bool reject,
CancellationToken cancellationToken = default);
}

19
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/IWxPusherUserStore.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.User;
public interface IWxPusherUserStore
{
/// <summary>
/// 获取用户订阅的topic列表
/// </summary>
/// <param name="userIds">用户标识列表</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<List<int>> GetSubscribeTopicsAsync(
IEnumerable<Guid> userIds,
CancellationToken cancellationToken = default);
}

22
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/NullWxPusherUserStore.cs

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.User;
public sealed class NullWxPusherUserStore : IWxPusherUserStore
{
public readonly static IWxPusherUserStore Instance = new NullWxPusherUserStore();
private NullWxPusherUserStore()
{
}
public Task<List<int>> GetSubscribeTopicsAsync(
IEnumerable<Guid> userIds,
CancellationToken cancellationToken = default)
{
return Task.FromResult(new List<int>());
}
}

68
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/UserHttpClientExtensions.cs

@ -0,0 +1,68 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WxPusher.User;
internal static class UserHttpClientExtensions
{
public async static Task<string> GetUserListAsync(
this HttpClient httpClient,
string appToken,
int page = 1,
int pageSize = 10,
string uid = null,
bool? isBlock = null,
FlowType? type = null,
CancellationToken cancellationToken = default)
{
var requestUrl = "/api/fun/wxuser/v2?appToken=$appToken&page=$page&pageSize=$pageSize&uid=$uid&isBlock=$isBlock&type=$type";
requestUrl = requestUrl
.Replace("$appToken", appToken)
.Replace("$page", page.ToString())
.Replace("$pageSize", pageSize.ToString())
.Replace("$uid", uid ?? "")
.Replace("$isBlock", isBlock?.ToString() ?? "")
.Replace("$type", type.HasValue ? ((int)type).ToString() : "");
var requestMessage = new HttpRequestMessage(
HttpMethod.Get,
requestUrl);
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
public async static Task<string> DeleteUserAsync(
this HttpClient httpClient,
string appToken,
int id,
CancellationToken cancellationToken = default)
{
var requestMessage = new HttpRequestMessage(
HttpMethod.Delete,
$"/api/fun/remove?appToken={appToken}&id={id}");
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
public async static Task<string> RejectUserAsync(
this HttpClient httpClient,
string appToken,
int id,
bool reject,
CancellationToken cancellationToken = default)
{
var requestMessage = new HttpRequestMessage(
HttpMethod.Put,
$"/api/fun/reject?appToken={appToken}&id={id}&reject={reject}");
var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
return await httpResponse.Content.ReadAsStringAsync();
}
}

51
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/UserProfile.cs

@ -0,0 +1,51 @@
using Newtonsoft.Json;
using System;
namespace LINGYUN.Abp.WxPusher.User;
[Serializable]
public class UserProfile
{
/// <summary>
/// 用户uid
/// </summary>
[JsonProperty("uid")]
public string Uid { get; set; }
/// <summary>
/// 新用户微信不再返回 ,强制返回空
/// </summary>
[JsonProperty("headImg")]
public string HeadImg { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[JsonProperty("createTime")]
public long CreateTime { get; set; }
/// <summary>
/// 新用户微信不再返回 ,强制返回空
/// </summary>
[JsonProperty("nickName")]
public string NickName { get; set; }
/// <summary>
/// 是否拉黑
/// </summary>
[JsonProperty("reject")]
public bool Reject { get; set; }
/// <summary>
/// 如果调用删除或者拉黑接口,需要这个id
/// </summary>
[JsonProperty("id")]
public int Id { get; set; }
/// <summary>
/// 关注类型,
/// 0:关注应用,
/// 1:关注topic
/// </summary>
[JsonProperty("type")]
public FlowType Type { get; set; }
/// <summary>
/// 关注的应用或者主题名字
/// </summary>
[JsonProperty("target")]
public string Target { get; set; }
}

87
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/WxPusherUserProvider.cs

@ -0,0 +1,87 @@
using LINGYUN.Abp.WxPusher.Features;
using LINGYUN.Abp.WxPusher.Token;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Features;
namespace LINGYUN.Abp.WxPusher.User;
[RequiresFeature(WxPusherFeatureNames.Enable)]
public class WxPusherUserProvider : WxPusherRequestProvider, IWxPusherUserProvider
{
protected IWxPusherTokenProvider WxPusherTokenProvider { get; }
public WxPusherUserProvider(IWxPusherTokenProvider wxPusherTokenProvider)
{
WxPusherTokenProvider = wxPusherTokenProvider;
}
public async virtual Task<bool> DeleteUserAsync(
int id,
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.GetPushPlusClient();
var content = await client.DeleteUserAsync(
token,
id,
cancellationToken);
var response = JsonSerializer.Deserialize<WxPusherResult<string>>(content);
return response.Success;
}
public async virtual Task<WxPusherPagedResult<UserProfile>> GetUserListAsync(
int page = 1,
int pageSize = 10,
string uid = null,
bool? isBlock = null,
FlowType? type = null,
CancellationToken cancellationToken = default)
{
if (pageSize > 100)
{
throw new ArgumentException("pageSize must be equal to or lower than 100!", nameof(pageSize));
}
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.GetPushPlusClient();
var content = await client.GetUserListAsync(
token,
page,
pageSize,
uid,
isBlock,
type,
cancellationToken);
var response = JsonSerializer
.Deserialize<WxPusherResult<WxPusherPagedResult<UserProfile>>>(content);
return response.GetData();
}
public async virtual Task<bool> RejectUserAsync(
int id,
bool reject,
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.GetPushPlusClient();
var content = await client.RejectUserAsync(
token,
id,
reject,
cancellationToken);
var response = JsonSerializer.Deserialize<WxPusherResult<string>>(content);
return response.Success;
}
}

30
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherPagedResult.cs

@ -0,0 +1,30 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.WxPusher;
[Serializable]
public class WxPusherPagedResult<T>
{
/// <summary>
/// 总数
/// </summary>
[JsonProperty("total")]
public int Total { get; set; }
/// <summary>
/// 当前页码
/// </summary>
[JsonProperty("page")]
public int Page { get; set; }
/// <summary>
/// 页码大小
/// </summary>
[JsonProperty("pageSize")]
public int PageSize { get; set; }
/// <summary>
/// 记录列表
/// </summary>
[JsonProperty("records")]
public List<T> Records { get; set; } = new List<T>();
}

15
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherRemoteCallException.cs

@ -0,0 +1,15 @@
using Volo.Abp;
using Volo.Abp.ExceptionHandling;
namespace LINGYUN.Abp.WxPusher;
public class WxPusherRemoteCallException : AbpException, IHasErrorCode
{
public string Code { get; }
public WxPusherRemoteCallException(string code, string message)
: base($"The WxPusher api returns an error: {code} - {message}")
{
Code = code;
}
}

17
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherRequestProvider.cs

@ -0,0 +1,17 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.Net.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace LINGYUN.Abp.WxPusher;
public abstract class WxPusherRequestProvider : ITransientDependency
{
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
protected ILoggerFactory LoggerFactory => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
protected ILogger Logger => LazyServiceProvider.LazyGetService<ILogger>(provider => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance);
protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService<IJsonSerializer>();
protected IHttpClientFactory HttpClientFactory => LazyServiceProvider.LazyGetRequiredService<IHttpClientFactory>();
}

61
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/WxPusherResult.cs

@ -0,0 +1,61 @@
using Newtonsoft.Json;
using System;
namespace LINGYUN.Abp.WxPusher;
[Serializable]
public class WxPusherResult<T>
{
/// <summary>
/// 状态码
/// </summary>
[JsonProperty("code")]
public int Code { get; set; }
/// <summary>
/// 错误消息
/// </summary>
[JsonProperty("msg")]
public string Message { get; set; }
/// <summary>
/// 返回数据
/// </summary>
[JsonProperty("data")]
public T Data { get; set; }
/// <summary>
/// 是否调用成功
/// </summary>
[JsonProperty("success")]
public bool Success { get; set; }
public WxPusherResult()
{
}
public WxPusherResult(int code, string message)
{
Code = code;
Message = message;
}
public WxPusherResult(int code, string message, T data)
{
Code = code;
Message = message;
Data = data;
}
public T GetData()
{
ThrowOfFailed();
return Data;
}
public void ThrowOfFailed()
{
if (!Success)
{
throw new WxPusherRemoteCallException(Code.ToString(), Message);
}
}
}

19
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/Microsoft/Extensions/DependencyInjection/IServiceCollectionExtensions.cs

@ -0,0 +1,19 @@
using System;
namespace Microsoft.Extensions.DependencyInjection;
internal static class IServiceCollectionExtensions
{
public static IServiceCollection AddWxPusherClient(
this IServiceCollection services)
{
services.AddHttpClient(
"_Abp_WxPusher_Client",
(httpClient) =>
{
httpClient.BaseAddress = new Uri("https://wxpusher.zjiecode.com");
});
return services;
}
}

35
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/README.md

@ -0,0 +1,35 @@
# LINGYUN.Abp.WxPusher
集成WxPusher
实现WxPusher相关Api文档,拥有WxPusher开放能力
详情见WxPusher文档: https://wxpusher.dingliqc.com/docs/#/
## 模块引用
```csharp
[DependsOn(typeof(AbpWxPusherModule))]
public class YouProjectModule : AbpModule
{
// other
}
```
## 用户订阅
实现 [IWxPusherUserStore](./LINGYUN/Abp/WxPusher/User/IWxPusherUserStore) 接口获取用户订阅列表
## Features
* WxPusher WxPusher特性分组
* WxPusher.Enable 全局启用WxPusher
* WxPusher.Message.Enable 全局启用WxPusher消息通道
* WxPusher.Message WxPusher消息推送
* WxPusher.Message.Enable 启用WxPusher消息推送
* WxPusher.Message.SendLimit WxPusher消息推送限制次数
* WxPusher.Message.SendLimitInterval WxPusher消息推送限制周期(天)
## Settings
* WxPusher.Security.AppToken 应用的身份标志,拥有APP_TOKEN,就可以给对应的应用的用户发送消息, 请严格保密.

10
aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/System/Net/Http/IHttpClientFactoryExtensions.cs

@ -0,0 +1,10 @@
namespace System.Net.Http;
internal static class IHttpClientFactoryExtensions
{
public static HttpClient GetPushPlusClient(
this IHttpClientFactory httpClientFactory)
{
return httpClientFactory.CreateClient("_Abp_WxPusher_Client");
}
}

18
aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN.Abp.WxPusher.Tests.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\modules\wx-pusher\LINGYUN.Abp.WxPusher\LINGYUN.Abp.WxPusher.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" />
</ItemGroup>
</Project>

7
aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestBase.cs

@ -0,0 +1,7 @@
using LINGYUN.Abp.Tests;
namespace LINGYUN.Abp.WxPusher;
public class AbpWxPusherTestBase : AbpTestsBase<AbpWxPusherTestModule>
{
}

24
aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestModule.cs

@ -0,0 +1,24 @@
using LINGYUN.Abp.Tests;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WxPusher;
[DependsOn(
typeof(AbpWxPusherModule),
typeof(AbpTestsBaseModule))]
public class AbpWxPusherTestModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configurationOptions = new AbpConfigurationBuilderOptions
{
BasePath = @"D:\Projects\Development\Abp\WxPusher",
EnvironmentName = "Test"
};
var configuration = ConfigurationHelper.BuildConfiguration(configurationOptions);
context.Services.ReplaceConfiguration(configuration);
}
}

60
aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSenderTests.cs

@ -0,0 +1,60 @@
using Shouldly;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace LINGYUN.Abp.WxPusher.Messages;
public class WxPusherMessageSenderTests : AbpWxPusherTestBase
{
protected IWxPusherMessageSender WxPusherMessageSender { get; }
public WxPusherMessageSenderTests()
{
WxPusherMessageSender = GetRequiredService<IWxPusherMessageSender>();
}
[Theory]
[InlineData("Content from the Xunit unit test. \r\n Click the link at the top to redirect baidu site.")]
public async virtual Task Send_Text_Test(string content)
{
var result = await WxPusherMessageSender
.SendAsync(
content,
contentType: MessageContentType.Text,
topicIds: new List<int> { 7182 },
url: "https://www.baidu.com/");
result.ShouldNotBeNull();
result.Count.ShouldBeGreaterThanOrEqualTo(1);
}
[Theory]
[InlineData("<h>Content from the Xunit unit test.</h> <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>")]
public async virtual Task Send_Html_Test(string content)
{
var result = await WxPusherMessageSender
.SendAsync(
content,
contentType: MessageContentType.Html,
topicIds: new List<int> { 7182 },
url: "https://www.baidu.com/");
result.ShouldNotBeNull();
result.Count.ShouldBeGreaterThanOrEqualTo(1);
}
[Theory]
[InlineData("**Content from the Xunit unit test.** <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>")]
public async virtual Task Send_Markdown_Test(string content)
{
var result = await WxPusherMessageSender
.SendAsync(
content,
contentType: MessageContentType.Markdown,
topicIds: new List<int> { 7182 },
url: "https://www.baidu.com/");
result.ShouldNotBeNull();
result.Count.ShouldBeGreaterThanOrEqualTo(1);
}
}
Loading…
Cancel
Save