diff --git a/aspnet-core/LINGYUN.MicroService.Common.sln b/aspnet-core/LINGYUN.MicroService.Common.sln
index e43cceb25..1992b26c3 100644
--- a/aspnet-core/LINGYUN.MicroService.Common.sln
+++ b/aspnet-core/LINGYUN.MicroService.Common.sln
@@ -272,6 +272,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Http.Client.Wrapper", "modules\common\LINGYUN.Abp.Http.Client.Wrapper\LINGYUN.Abp.Http.Client.Wrapper.csproj", "{2C5C57FB-70F5-4C20-92FC-39ACD260CEE3}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tui-juhe", "tui-juhe", "{A1C75C3E-67D1-4BCE-89BF-44A735909BD5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.TuiJuhe", "modules\tui-juhe\LINGYUN.Abp.TuiJuhe\LINGYUN.Abp.TuiJuhe.csproj", "{60143C9C-E539-498A-9DBE-B95E6A8301A5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.TuiJuhe", "modules\tui-juhe\LINGYUN.Abp.Notifications.TuiJuhe\LINGYUN.Abp.Notifications.TuiJuhe.csproj", "{C55D9BA0-4AFD-4D18-958B-B53FD3F2FF4F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.TuiJuhe.Tests", "tests\LINGYUN.Abp.Notifications.TuiJuhe.Tests\LINGYUN.Abp.Notifications.TuiJuhe.Tests.csproj", "{641DF0BA-0E79-441D-B328-FDA288A35CDE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.TuiJuhe.Tests", "tests\LINGYUN.Abp.TuiJuhe.Tests\LINGYUN.Abp.TuiJuhe.Tests.csproj", "{2020EA8D-F276-498D-92D1-94D1E25F8E1A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.TuiJuhe.SettingManagement", "modules\tui-juhe\LINGYUN.Abp.TuiJuhe.SettingManagement\LINGYUN.Abp.TuiJuhe.SettingManagement.csproj", "{2A3DF8B2-4760-41C4-9337-44FEE29B17CD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -702,6 +714,26 @@ Global
{2C5C57FB-70F5-4C20-92FC-39ACD260CEE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C5C57FB-70F5-4C20-92FC-39ACD260CEE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C5C57FB-70F5-4C20-92FC-39ACD260CEE3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {60143C9C-E539-498A-9DBE-B95E6A8301A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {60143C9C-E539-498A-9DBE-B95E6A8301A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {60143C9C-E539-498A-9DBE-B95E6A8301A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {60143C9C-E539-498A-9DBE-B95E6A8301A5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C55D9BA0-4AFD-4D18-958B-B53FD3F2FF4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C55D9BA0-4AFD-4D18-958B-B53FD3F2FF4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C55D9BA0-4AFD-4D18-958B-B53FD3F2FF4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C55D9BA0-4AFD-4D18-958B-B53FD3F2FF4F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {641DF0BA-0E79-441D-B328-FDA288A35CDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {641DF0BA-0E79-441D-B328-FDA288A35CDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {641DF0BA-0E79-441D-B328-FDA288A35CDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {641DF0BA-0E79-441D-B328-FDA288A35CDE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2020EA8D-F276-498D-92D1-94D1E25F8E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2020EA8D-F276-498D-92D1-94D1E25F8E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2020EA8D-F276-498D-92D1-94D1E25F8E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2020EA8D-F276-498D-92D1-94D1E25F8E1A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2A3DF8B2-4760-41C4-9337-44FEE29B17CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A3DF8B2-4760-41C4-9337-44FEE29B17CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A3DF8B2-4760-41C4-9337-44FEE29B17CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A3DF8B2-4760-41C4-9337-44FEE29B17CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -837,6 +869,12 @@ Global
{B78E53AC-6BB8-402D-90CF-BEF1BD9558EB} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
{AA039B4E-CE12-43AC-8340-3D52463E971E} = {B91F26C5-B148-4094-B5F1-71E5F945DBED}
{2C5C57FB-70F5-4C20-92FC-39ACD260CEE3} = {086BE5BE-8594-4DA7-8819-935FEF76DABD}
+ {A1C75C3E-67D1-4BCE-89BF-44A735909BD5} = {02EA4E78-5891-43BC-944F-3E52FEE032E4}
+ {60143C9C-E539-498A-9DBE-B95E6A8301A5} = {A1C75C3E-67D1-4BCE-89BF-44A735909BD5}
+ {C55D9BA0-4AFD-4D18-958B-B53FD3F2FF4F} = {A1C75C3E-67D1-4BCE-89BF-44A735909BD5}
+ {641DF0BA-0E79-441D-B328-FDA288A35CDE} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
+ {2020EA8D-F276-498D-92D1-94D1E25F8E1A} = {B86C21A4-73B7-471E-B73A-B4B905EC9435}
+ {2A3DF8B2-4760-41C4-9337-44FEE29B17CD} = {A1C75C3E-67D1-4BCE-89BF-44A735909BD5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/FodyWeavers.xml b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/FodyWeavers.xsd b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN.Abp.Notifications.TuiJuhe.csproj b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN.Abp.Notifications.TuiJuhe.csproj
new file mode 100644
index 000000000..ba203b119
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN.Abp.Notifications.TuiJuhe.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/NotificationDefinitionExtensions.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/NotificationDefinitionExtensions.cs
new file mode 100644
index 000000000..d65df5d71
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/NotificationDefinitionExtensions.cs
@@ -0,0 +1,57 @@
+using LINGYUN.Abp.TuiJuhe.Messages;
+
+namespace LINGYUN.Abp.Notifications;
+
+public static class NotificationDefinitionExtensions
+{
+ private const string Prefix = "tui-juhe:";
+ private const string ServiceIdKey = Prefix + "serviceId";
+
+ ///
+ /// 获取消息内容类型
+ ///
+ ///
+ ///
+ ///
+ public static MessageContentType GetContentTypeOrDefault(
+ this NotificationDefinition notification,
+ MessageContentType defaultContentType = MessageContentType.Text)
+ {
+ return notification.ContentType switch
+ {
+ NotificationContentType.Text => MessageContentType.Text,
+ NotificationContentType.Html => MessageContentType.Html,
+ NotificationContentType.Markdown => MessageContentType.Markdown,
+ NotificationContentType.Json => MessageContentType.Text,
+ _ => defaultContentType,
+ };
+ }
+ ///
+ /// 指定服务编号
+ ///
+ /// 群组编码
+ /// 服务编号,在服务创建后自动生成ServiceID,是服务的唯一标识,可在每个服务的详情页中查看
+ ///
+ ///
+ ///
+ public static NotificationDefinition WithServiceId(
+ this NotificationDefinition notification,
+ string serviceId)
+ {
+ return notification.WithProperty(ServiceIdKey, serviceId);
+ }
+ ///
+ /// 获取服务编号
+ ///
+ ///
+ public static string GetServiceIdOrNull(
+ this NotificationDefinition notification)
+ {
+ if (notification.Properties.TryGetValue(ServiceIdKey, out var serviceIdDefine))
+ {
+ return serviceIdDefine.ToString();
+ }
+
+ return null;
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheModule.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheModule.cs
new file mode 100644
index 000000000..95ed92c76
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheModule.cs
@@ -0,0 +1,18 @@
+using LINGYUN.Abp.TuiJuhe;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.TuiJuhe;
+
+[DependsOn(
+ typeof(AbpNotificationsModule),
+ typeof(AbpTuiJuheModule))]
+public class AbpNotificationsTuiJuheModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.PublishProviders.Add();
+ });
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/TuiJuhe/TuiJuheNotificationPublishProvider.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/TuiJuhe/TuiJuheNotificationPublishProvider.cs
new file mode 100644
index 000000000..d96df5047
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/LINGYUN/Abp/Notifications/TuiJuhe/TuiJuheNotificationPublishProvider.cs
@@ -0,0 +1,115 @@
+using LINGYUN.Abp.RealTime.Localization;
+using LINGYUN.Abp.TuiJuhe.Features;
+using LINGYUN.Abp.TuiJuhe.Messages;
+using Microsoft.Extensions.Localization;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Features;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Notifications.TuiJuhe;
+
+public class TuiJuheNotificationPublishProvider : NotificationPublishProvider
+{
+ public const string ProviderName = "TuiJuhe";
+
+ public override string Name => ProviderName;
+
+ protected IFeatureChecker FeatureChecker { get; }
+
+ protected ITuiJuheMessageSender TuiJuheMessageSender { get; }
+
+ protected IStringLocalizerFactory LocalizerFactory { get; }
+
+ protected AbpLocalizationOptions LocalizationOptions { get; }
+
+ protected INotificationDefinitionManager NotificationDefinitionManager { get; }
+
+ public TuiJuheNotificationPublishProvider(
+ IFeatureChecker featureChecker,
+ ITuiJuheMessageSender tuiJuheMessageSender,
+ IStringLocalizerFactory localizerFactory,
+ IOptions localizationOptions,
+ INotificationDefinitionManager notificationDefinitionManager)
+ {
+ FeatureChecker = featureChecker;
+ TuiJuheMessageSender = tuiJuheMessageSender;
+ LocalizerFactory = localizerFactory;
+ LocalizationOptions = localizationOptions.Value;
+ NotificationDefinitionManager = notificationDefinitionManager;
+ }
+
+ protected async override Task CanPublishAsync(NotificationInfo notification, CancellationToken cancellationToken = default)
+ {
+ if (!await FeatureChecker.IsEnabledAsync(TuiJuheFeatureNames.Message.Enable))
+ {
+ Logger.LogWarning(
+ "{0} cannot push messages because the feature {1} is not enabled",
+ Name,
+ TuiJuheFeatureNames.Message.Enable);
+ return false;
+ }
+ return true;
+ }
+
+ protected async override Task PublishAsync(
+ NotificationInfo notification,
+ IEnumerable identifiers,
+ CancellationToken cancellationToken = default)
+ {
+ var notificationDefine = await NotificationDefinitionManager.GetOrNullAsync(notification.Name);
+ var contentType = notificationDefine?.GetContentTypeOrDefault(MessageContentType.Text)
+ ?? MessageContentType.Text;
+ var serviceId = notificationDefine?.GetServiceIdOrNull();
+
+ if (serviceId.IsNullOrWhiteSpace())
+ {
+ Logger.LogWarning(
+ "{0} cannot push messages because the notification {1} service id is not specified",
+ Name,
+ notification.Name);
+ return;
+ }
+
+ if (!notification.Data.NeedLocalizer())
+ {
+ var title = notification.Data.TryGetData("title").ToString();
+ var message = notification.Data.TryGetData("message").ToString();
+
+ await TuiJuheMessageSender.SendAsync(
+ title: title,
+ content: message,
+ serviceId: serviceId,
+ contentType: contentType,
+ cancellationToken: cancellationToken);
+ }
+ else
+ {
+ var titleInfo = notification.Data.TryGetData("title").As();
+ var titleResource = GetResource(titleInfo.ResourceName);
+ var title = LocalizerFactory.Create(titleResource.ResourceType)[titleInfo.Name, titleInfo.Values].Value;
+
+ var messageInfo = notification.Data.TryGetData("message").As();
+ var messageResource = GetResource(messageInfo.ResourceName);
+ var message = LocalizerFactory.Create(messageResource.ResourceType)[messageInfo.Name, messageInfo.Values].Value;
+
+ await TuiJuheMessageSender.SendAsync(
+ title: title,
+ content: message,
+ serviceId: serviceId,
+ contentType: contentType,
+ cancellationToken: cancellationToken);
+ }
+ }
+
+ private LocalizationResource GetResource(string resourceName)
+ {
+ return LocalizationOptions.Resources.Values
+ .First(x => x.ResourceName.Equals(resourceName));
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/README.md b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/README.md
new file mode 100644
index 000000000..93b5c8c8c
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.Notifications.TuiJuhe/README.md
@@ -0,0 +1,15 @@
+# LINGYUN.Abp.Notifications.TuiJuhe
+
+通知模块的TuiJuhe实现
+
+使应用可通过TuiJuhe发布实时通知
+
+## 模块引用
+
+```csharp
+[DependsOn(typeof(AbpNotificationsTuiJuheModule))]
+public class YouProjectModule : AbpModule
+{
+ // other
+}
+```
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/FodyWeavers.xml b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/FodyWeavers.xsd b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN.Abp.TuiJuhe.SettingManagement.csproj b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN.Abp.TuiJuhe.SettingManagement.csproj
new file mode 100644
index 000000000..d15f3f3f2
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN.Abp.TuiJuhe.SettingManagement.csproj
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/AbpTuiJuheSettingManagementModule.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/AbpTuiJuheSettingManagementModule.cs
new file mode 100644
index 000000000..fcaa2eef4
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/AbpTuiJuheSettingManagementModule.cs
@@ -0,0 +1,48 @@
+using LINGYUN.Abp.TuiJuhe.Localization;
+using Localization.Resources.AbpUi;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.TuiJuhe.SettingManagement
+{
+ [DependsOn(
+ typeof(AbpTuiJuheModule),
+ typeof(AbpAspNetCoreMvcModule))]
+ public class AbpWxPusherSettingManagementModule : AbpModule
+ {
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpWxPusherSettingManagementModule).Assembly);
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources");
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddBaseTypes(
+ typeof(AbpUiResource)
+ );
+ });
+ }
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/ITuiJuheSettingAppService.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/ITuiJuheSettingAppService.cs
new file mode 100644
index 000000000..bdc6e7659
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/ITuiJuheSettingAppService.cs
@@ -0,0 +1,8 @@
+using LINGYUN.Abp.SettingManagement;
+
+namespace LINGYUN.Abp.TuiJuhe.SettingManagement
+{
+ public interface ITuiJuheSettingAppService : IReadonlySettingAppService
+ {
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources/en.json b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources/en.json
new file mode 100644
index 000000000..4a7d52470
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources/en.json
@@ -0,0 +1,7 @@
+{
+ "culture": "en",
+ "texts": {
+ "Permission:TuiJuhe": "TuiJuhe",
+ "Permission:ManageSetting": "Manage Setting"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..505023415
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources/zh-Hans.json
@@ -0,0 +1,7 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Permission:TuiJuhe": "聚合云推",
+ "Permission:ManageSetting": "管理设置"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingAppService.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingAppService.cs
new file mode 100644
index 000000000..8e0061804
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingAppService.cs
@@ -0,0 +1,61 @@
+using LINGYUN.Abp.SettingManagement;
+using LINGYUN.Abp.TuiJuhe.Localization;
+using LINGYUN.Abp.TuiJuhe.Settings;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.SettingManagement;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.TuiJuhe.SettingManagement
+{
+ public class TuiJuheSettingAppService : ApplicationService, ITuiJuheSettingAppService
+ {
+ protected ISettingManager SettingManager { get; }
+ protected IPermissionChecker PermissionChecker { get; }
+ protected ISettingDefinitionManager SettingDefinitionManager { get; }
+
+ public TuiJuheSettingAppService(
+ ISettingManager settingManager,
+ IPermissionChecker permissionChecker,
+ ISettingDefinitionManager settingDefinitionManager)
+ {
+ SettingManager = settingManager;
+ PermissionChecker = permissionChecker;
+ SettingDefinitionManager = settingDefinitionManager;
+ LocalizationResource = typeof(TuiJuheResource);
+ }
+
+ public async virtual Task GetAllForCurrentTenantAsync()
+ {
+ return await GetAllForProviderAsync(TenantSettingValueProvider.ProviderName, CurrentTenant.GetId().ToString());
+ }
+
+ public async virtual Task GetAllForGlobalAsync()
+ {
+ return await GetAllForProviderAsync(GlobalSettingValueProvider.ProviderName, null);
+ }
+
+ protected async virtual Task GetAllForProviderAsync(string providerName, string providerKey)
+ {
+ var settingGroups = new SettingGroupResult();
+ var wxPusherSettingGroup = new SettingGroupDto(L["DisplayName:TuiJuhe"], L["Description:TuiJuhe"]);
+
+ if (await PermissionChecker.IsGrantedAsync(TuiJuheSettingPermissionNames.ManageSetting))
+ {
+ var securitySetting = wxPusherSettingGroup.AddSetting(L["Security"], L["Security"]);
+ securitySetting.AddDetail(
+ SettingDefinitionManager.Get(TuiJuheSettingNames.Security.Token),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(TuiJuheSettingNames.Security.Token, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ }
+
+ settingGroups.AddGroup(wxPusherSettingGroup);
+
+ return settingGroups;
+ }
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingController.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingController.cs
new file mode 100644
index 000000000..4521554d3
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingController.cs
@@ -0,0 +1,36 @@
+using LINGYUN.Abp.SettingManagement;
+using Microsoft.AspNetCore.Mvc;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.AspNetCore.Mvc;
+
+namespace LINGYUN.Abp.TuiJuhe.SettingManagement
+{
+ [RemoteService(Name = AbpSettingManagementRemoteServiceConsts.RemoteServiceName)]
+ [Area(AbpSettingManagementRemoteServiceConsts.ModuleName)]
+ [Route($"api/{AbpSettingManagementRemoteServiceConsts.ModuleName}/tui-juhe")]
+ public class TuiJuheSettingController : AbpController, ITuiJuheSettingAppService
+ {
+ protected ITuiJuheSettingAppService Service { get; }
+
+ public TuiJuheSettingController(
+ ITuiJuheSettingAppService service)
+ {
+ Service = service;
+ }
+
+ [HttpGet]
+ [Route("by-current-tenant")]
+ public async virtual Task GetAllForCurrentTenantAsync()
+ {
+ return await Service.GetAllForCurrentTenantAsync();
+ }
+
+ [HttpGet]
+ [Route("by-global")]
+ public async virtual Task GetAllForGlobalAsync()
+ {
+ return await Service.GetAllForGlobalAsync();
+ }
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingPermissionDefinitionProvider.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingPermissionDefinitionProvider.cs
new file mode 100644
index 000000000..68e7977e3
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingPermissionDefinitionProvider.cs
@@ -0,0 +1,24 @@
+using LINGYUN.Abp.TuiJuhe.Localization;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.TuiJuhe.SettingManagement
+{
+ public class WxPusherSettingPermissionDefinitionProvider : PermissionDefinitionProvider
+ {
+ public override void Define(IPermissionDefinitionContext context)
+ {
+ var tuiJuheGroup = context.AddGroup(
+ TuiJuheSettingPermissionNames.GroupName,
+ L("Permission:TuiJuhe"));
+
+ tuiJuheGroup.AddPermission(
+ TuiJuheSettingPermissionNames.ManageSetting, L("Permission:ManageSetting"));
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingPermissionNames.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingPermissionNames.cs
new file mode 100644
index 000000000..ad5fd02c3
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe.SettingManagement/LINGYUN/Abp/TuiJuhe/SettingManagement/TuiJuheSettingPermissionNames.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.TuiJuhe.SettingManagement
+{
+ public class TuiJuheSettingPermissionNames
+ {
+ public const string GroupName = "Abp.TuiJuhe";
+
+ public const string ManageSetting = GroupName + ".ManageSetting";
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/FodyWeavers.xml b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/FodyWeavers.xsd b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN.Abp.TuiJuhe.csproj b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN.Abp.TuiJuhe.csproj
new file mode 100644
index 000000000..9e0759210
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN.Abp.TuiJuhe.csproj
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/AbpTuiJuheModule.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/AbpTuiJuheModule.cs
new file mode 100644
index 000000000..491f117ad
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/AbpTuiJuheModule.cs
@@ -0,0 +1,42 @@
+using LINGYUN.Abp.Features.LimitValidation;
+using LINGYUN.Abp.TuiJuhe.Localization;
+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.TuiJuhe;
+
+[DependsOn(
+ typeof(AbpJsonModule),
+ typeof(AbpSettingsModule),
+ typeof(AbpCachingModule),
+ typeof(AbpFeaturesLimitValidationModule))]
+public class AbpTuiJuheModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddTuiJuheClient();
+
+ Configure(options =>
+ {
+
+ });
+
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add()
+ .AddVirtualJson("/LINGYUN/Abp/TuiJuhe/Localization/Resources");
+ });
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Features/TuiJuheFeatureDefinitionProvider.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Features/TuiJuheFeatureDefinitionProvider.cs
new file mode 100644
index 000000000..4a2ae74a6
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Features/TuiJuheFeatureDefinitionProvider.cs
@@ -0,0 +1,51 @@
+using LINGYUN.Abp.TuiJuhe.Localization;
+using Volo.Abp.Features;
+using Volo.Abp.Localization;
+using Volo.Abp.Validation.StringValues;
+
+namespace LINGYUN.Abp.TuiJuhe.Features;
+
+public class TuiJuheFeatureDefinitionProvider : FeatureDefinitionProvider
+{
+ public override void Define(IFeatureDefinitionContext context)
+ {
+ var group = context.AddGroup(
+ name: TuiJuheFeatureNames.GroupName,
+ displayName: L("Features:TuiJuhe"));
+ group.AddFeature(
+ name: TuiJuheFeatureNames.Enable,
+ defaultValue: "false",
+ displayName: L("Features:TuiJuheEnable"),
+ description: L("Features:TuiJuheEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+
+ var message = group.AddFeature(
+ name: TuiJuheFeatureNames.Message.GroupName,
+ displayName: L("Features:Message"),
+ description: L("Features:Message"));
+
+ message.CreateChild(
+ name: TuiJuheFeatureNames.Message.Enable,
+ defaultValue: "false",
+ displayName: L("Features:MessageEnable"),
+ description: L("Features:MessageEnableDesc"),
+ valueType: new ToggleStringValueType(new BooleanValueValidator()));
+ message.CreateChild(
+ name: TuiJuheFeatureNames.Message.SendLimit,
+ defaultValue: "50",
+ displayName: L("Features:Message.SendLimit"),
+ description: L("Features:Message.SendLimitDesc"),
+ valueType: new FreeTextStringValueType(new NumericValueValidator(1, 50)));
+ message.CreateChild(
+ name: TuiJuheFeatureNames.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(name);
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Features/TuiJuheFeatureNames.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Features/TuiJuheFeatureNames.cs
new file mode 100644
index 000000000..e8059cec3
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Features/TuiJuheFeatureNames.cs
@@ -0,0 +1,28 @@
+namespace LINGYUN.Abp.TuiJuhe.Features;
+
+public static class TuiJuheFeatureNames
+{
+ public const string GroupName = "TuiJuhe";
+
+ ///
+ /// 启用TuiJuhe
+ ///
+ public const string Enable = GroupName + ".Enable";
+
+ public static class Message
+ {
+ public const string GroupName = TuiJuheFeatureNames.GroupName + ".Message";
+ ///
+ /// 启用消息推送
+ ///
+ public const string Enable = GroupName + ".Enable";
+ ///
+ /// 发送次数上限
+ ///
+ public const string SendLimit = GroupName + ".SendLimit";
+ ///
+ /// 发送次数上限时长
+ ///
+ public const string SendLimitInterval = GroupName + ".SendLimitInterval";
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/Resources/en.json b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/Resources/en.json
new file mode 100644
index 000000000..a853ce480
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/Resources/en.json
@@ -0,0 +1,20 @@
+{
+ "culture": "en",
+ "texts": {
+ "DisplayName:TuiJuhe": "TuiJuhe",
+ "Description:TuiJuhe": "TuiJuhe",
+ "Settings:Security.Token": "Token",
+ "Settings:Security.TokenDesc": "The user token, (https://tui.juhe.cn/setup) in the center of the personal page views.",
+ "Features:TuiJuhe": "TuiJuhe push service",
+ "Features:TuiJuheEnable": "Enable TuiJuhe",
+ "Features:TuiJuheEnableDesc": "Enable to enable the application to have TuiJuhe capabilities.",
+ "Features:Message": "TuiJuhe message push",
+ "Features:MessageEnable": "Enable TuiJuhe Message Push",
+ "Features:MessageEnableDesc": "Enable so that apps will have the ability to be pushed notification via TuiJuhe.",
+ "Features:Message.SendLimit": "Amount of TuiJuhe push",
+ "Features:Message.SendLimitDesc": "Set to limit the amount of TuiJuhe message push.",
+ "Features:Message.SendLimitInterval": "TuiJuhe message limit interval",
+ "Features:Message.SendLimitIntervalDesc": "Set the TuiJuhe message limit period (time scale: hours). Message sending frequency (request limit) : The rate does not exceed 50 times per unit hour.",
+ "Security": "Security"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/Resources/zh-Hans.json b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..3964376cc
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/Resources/zh-Hans.json
@@ -0,0 +1,20 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "DisplayName:TuiJuhe": "聚合云推",
+ "Description:TuiJuhe": "聚合云推(TuiJuhe)",
+ "Settings:Security.Token": "用户令牌",
+ "Settings:Security.TokenDesc": "用户令牌,在个人中心(https://tui.juhe.cn/setup)页面查看获取.",
+ "Features:TuiJuhe": "聚合云推服务",
+ "Features:TuiJuheEnable": "启用聚合云推",
+ "Features:TuiJuheEnableDesc": "启用以使应用拥有聚合云推的能力.",
+ "Features:Message": "聚合云推消息推送",
+ "Features:MessageEnable": "启用聚合云推消息推送",
+ "Features:MessageEnableDesc": "启用以使应用将拥有通过聚合云推推送到实时消息的能力.",
+ "Features:Message.SendLimit": "聚合云推推送量",
+ "Features:Message.SendLimitDesc": "设置以限制聚合云推推送量.",
+ "Features:Message.SendLimitInterval": "聚合云推消息限制周期",
+ "Features:Message.SendLimitIntervalDesc": "设置聚合云推消息限制周期(时间刻度: 时).消息发送频率(请求限制): 单位小时内不超过50次率.",
+ "Security": "安全选项"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/TuiJuheResource.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/TuiJuheResource.cs
new file mode 100644
index 000000000..c0b126ce1
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Localization/TuiJuheResource.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.TuiJuhe.Localization;
+
+[LocalizationResourceName("TuiJuhe")]
+public class TuiJuheResource
+{
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/ITuiJuheMessageSender.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/ITuiJuheMessageSender.cs
new file mode 100644
index 000000000..c202993c4
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/ITuiJuheMessageSender.cs
@@ -0,0 +1,15 @@
+using JetBrains.Annotations;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.TuiJuhe.Messages;
+
+public interface ITuiJuheMessageSender
+{
+ Task> SendAsync(
+ [NotNull] string title,
+ [NotNull] string content,
+ [NotNull] string serviceId,
+ MessageContentType contentType = MessageContentType.Text,
+ CancellationToken cancellationToken = default);
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/MessageContentType.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/MessageContentType.cs
new file mode 100644
index 000000000..bffc32d7e
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/MessageContentType.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.TuiJuhe.Messages;
+
+public enum MessageContentType
+{
+ Text = 0,
+ Html = 1,
+ Markdown = 2,
+ Json = 3
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/MessageHttpClientExtensions.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/MessageHttpClientExtensions.cs
new file mode 100644
index 000000000..d443b2031
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/MessageHttpClientExtensions.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.TuiJuhe.Messages;
+
+internal static class MessageHttpClientExtensions
+{
+ public async static Task SendMessageAsync(
+ this HttpClient httpClient,
+ string token,
+ string title,
+ string content,
+ string serviceId,
+ string docType = "txt",
+ CancellationToken cancellationToken = default)
+ {
+ var requestMessage = new HttpRequestMessage(
+ HttpMethod.Post,
+ "/api/plus/pushApi");
+
+ var requestData = new Dictionary
+ {
+ { "token", token },
+ { "title", title },
+ { "content", content },
+ { "service_id", serviceId },
+ { "doc_type", docType ?? "txt" },
+ };
+ var formData = new FormUrlEncodedContent(requestData);
+ requestMessage.Content = formData;
+
+ var httpResponse = await httpClient.SendAsync(requestMessage, cancellationToken);
+
+ return await httpResponse.Content.ReadAsStringAsync();
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/TuiJuheMessageSender.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/TuiJuheMessageSender.cs
new file mode 100644
index 000000000..322a830c1
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Messages/TuiJuheMessageSender.cs
@@ -0,0 +1,76 @@
+using JetBrains.Annotations;
+using LINGYUN.Abp.Features.LimitValidation;
+using LINGYUN.Abp.TuiJuhe.Features;
+using LINGYUN.Abp.TuiJuhe.Token;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Features;
+using Volo.Abp.Json;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.TuiJuhe.Messages;
+
+[RequiresFeature(TuiJuheFeatureNames.Enable)]
+public class TuiJuheMessageSender : ITuiJuheMessageSender, ITransientDependency
+{
+ protected IJsonSerializer JsonSerializer { get; }
+ protected ISettingProvider SettingProvider { get; }
+ protected IHttpClientFactory HttpClientFactory { get; }
+ protected ITuiJuheTokenProvider TokenProvider { get; }
+
+ public TuiJuheMessageSender(
+ IJsonSerializer jsonSerializer,
+ ISettingProvider settingProvider,
+ IHttpClientFactory httpClientFactory,
+ ITuiJuheTokenProvider tokenProvider)
+ {
+ JsonSerializer = jsonSerializer;
+ SettingProvider = settingProvider;
+ HttpClientFactory = httpClientFactory;
+ TokenProvider = tokenProvider;
+ }
+
+ // 消息发送频率(请求限制):单位小时内不超过50次
+ [RequiresFeature(TuiJuheFeatureNames.Message.Enable)]
+ [RequiresLimitFeature(
+ TuiJuheFeatureNames.Message.SendLimit,
+ TuiJuheFeatureNames.Message.SendLimitInterval,
+ LimitPolicy.Hours)]
+ public async virtual Task> SendAsync(
+ [NotNull] string title,
+ [NotNull] string content,
+ [NotNull] string serviceId,
+ MessageContentType contentType = MessageContentType.Text,
+ CancellationToken cancellationToken = default)
+ {
+ var token = await TokenProvider.GetTokenAsync(cancellationToken);
+ var client = HttpClientFactory.GetTuiJuheClient();
+
+ var resultContent = await client.SendMessageAsync(
+ token,
+ title,
+ content,
+ serviceId,
+ GetDocType(contentType),
+ cancellationToken);
+
+ var response = JsonSerializer
+ .Deserialize>(resultContent);
+
+ return response;
+ }
+
+ private static string GetDocType(MessageContentType contentType)
+ {
+ return contentType switch
+ {
+ MessageContentType.Html => "html",
+ MessageContentType.Text => "txt",
+ MessageContentType.Json => "json",
+ MessageContentType.Markdown => "markdown",
+ _ => "html",
+ };
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Settings/TuiJuheSettingDefinitionProvider.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Settings/TuiJuheSettingDefinitionProvider.cs
new file mode 100644
index 000000000..a35623511
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Settings/TuiJuheSettingDefinitionProvider.cs
@@ -0,0 +1,30 @@
+using LINGYUN.Abp.TuiJuhe.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.TuiJuhe.Settings;
+
+public class TuiJuheSettingDefinitionProvider : SettingDefinitionProvider
+{
+ public override void Define(ISettingDefinitionContext context)
+ {
+ context.Add(new[]
+ {
+ new SettingDefinition(
+ name: TuiJuheSettingNames.Security.Token,
+ displayName: L("Settings:Security.Token"),
+ description: L("Settings:Security.TokenDesc"),
+ isEncrypted: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ });
+ }
+
+ public static ILocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Settings/TuiJuheSettingNames.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Settings/TuiJuheSettingNames.cs
new file mode 100644
index 000000000..9c080c05f
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Settings/TuiJuheSettingNames.cs
@@ -0,0 +1,13 @@
+namespace LINGYUN.Abp.TuiJuhe.Settings;
+
+public static class TuiJuheSettingNames
+{
+ public const string Prefix = "TuiJuhe";
+
+ public static class Security
+ {
+ public const string Prefix = TuiJuheSettingNames.Prefix + ".Security";
+
+ public const string Token = Prefix + ".Token";
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Token/ITuiJuheTokenProvider.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Token/ITuiJuheTokenProvider.cs
new file mode 100644
index 000000000..4aa82aceb
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Token/ITuiJuheTokenProvider.cs
@@ -0,0 +1,9 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.TuiJuhe.Token;
+
+public interface ITuiJuheTokenProvider
+{
+ Task GetTokenAsync(CancellationToken cancellationToken = default);
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Token/TuiJuheTokenProvider.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Token/TuiJuheTokenProvider.cs
new file mode 100644
index 000000000..289b8ff43
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/Token/TuiJuheTokenProvider.cs
@@ -0,0 +1,23 @@
+using LINGYUN.Abp.TuiJuhe.Settings;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.TuiJuhe.Token;
+
+public class TuiJuheTokenProvider : ITuiJuheTokenProvider, ITransientDependency
+{
+ protected ISettingProvider SettingProvider { get; }
+
+ public TuiJuheTokenProvider(ISettingProvider settingProvider)
+ {
+ SettingProvider = settingProvider;
+ }
+
+ public async virtual Task GetTokenAsync(CancellationToken cancellationToken = default)
+ {
+ return await SettingProvider.GetOrNullAsync(
+ TuiJuheSettingNames.Security.Token);
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/TuiJuheRemoteCallException.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/TuiJuheRemoteCallException.cs
new file mode 100644
index 000000000..4167416c4
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/TuiJuheRemoteCallException.cs
@@ -0,0 +1,15 @@
+using Volo.Abp;
+using Volo.Abp.ExceptionHandling;
+
+namespace LINGYUN.Abp.TuiJuhe;
+
+public class TuiJuheRemoteCallException : AbpException, IHasErrorCode
+{
+ public string Code { get; }
+
+ public TuiJuheRemoteCallException(string code, string message)
+ : base($"The TuiJuhe api returns an error: {code} - {message}")
+ {
+ Code = code;
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/TuiJuheResult.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/TuiJuheResult.cs
new file mode 100644
index 000000000..7bdd8cacb
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/LINGYUN/Abp/TuiJuhe/TuiJuheResult.cs
@@ -0,0 +1,64 @@
+using Newtonsoft.Json;
+using System;
+
+namespace LINGYUN.Abp.TuiJuhe;
+
+[Serializable]
+public class TuiJuheResult
+{
+ ///
+ /// 状态码
+ ///
+ [JsonProperty("code")]
+ public int Code { get; set; }
+ ///
+ /// 错误消息
+ ///
+ [JsonProperty("reason")]
+ public string Reason { get; set; }
+ ///
+ /// 响应数据
+ ///
+ [JsonProperty("result")]
+ public TResult Result { get; set; }
+ ///
+ /// 响应参数
+ ///
+ [JsonProperty("params")]
+ public TParam Params { get; set; }
+
+ [JsonIgnore]
+ public bool Success => 200 == Code;
+
+ public TuiJuheResult()
+ {
+ }
+
+ public TuiJuheResult(int code, string reason)
+ {
+ Code = code;
+ Reason = reason;
+ }
+
+ public TuiJuheResult(int code, string reason, TResult result)
+ {
+ Code = code;
+ Reason = reason;
+ Result = result;
+ }
+
+ public TResult GetData()
+ {
+ ThrowOfFailed();
+
+ return Result;
+ }
+
+ public void ThrowOfFailed()
+ {
+ if (!Success)
+ {
+ throw new TuiJuheRemoteCallException(Code.ToString(), Reason);
+ }
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/Microsoft/Extensions/DependencyInjection/IServiceCollectionExtensions.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/Microsoft/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
new file mode 100644
index 000000000..c6d1a183f
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/Microsoft/Extensions/DependencyInjection/IServiceCollectionExtensions.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+internal static class IServiceCollectionExtensions
+{
+ public static IServiceCollection AddTuiJuheClient(
+ this IServiceCollection services)
+ {
+ services.AddHttpClient(
+ "_Abp_TuiJuhe_Client",
+ (httpClient) =>
+ {
+ httpClient.BaseAddress = new Uri("https://tui.juhe.cn");
+ });
+
+ return services;
+ }
+}
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/README.md b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/README.md
new file mode 100644
index 000000000..f5ca4369f
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/README.md
@@ -0,0 +1,31 @@
+# LINGYUN.Abp.TuiJuhe
+
+集成TuiJuhe(仅适用于特定消息与人员通知, 不支持群发)
+
+实现TuiJuhe相关Api文档,拥有TuiJuhe开放能力
+
+详情见TuiJuhe文档: https://tui.juhe.cn/docs
+
+## 模块引用
+
+```csharp
+[DependsOn(typeof(AbpTuiJuheModule))]
+public class YouProjectModule : AbpModule
+{
+ // other
+}
+```
+
+## Features
+
+* TuiJuhe TuiJuhe特性分组
+* TuiJuhe.Enable 全局启用TuiJuhe
+* TuiJuhe.Message.Enable 全局启用TuiJuhe消息通道
+* TuiJuhe.Message TuiJuhe消息推送
+* TuiJuhe.Message.Enable 启用TuiJuhe消息推送
+* TuiJuhe.Message.SendLimit TuiJuhe消息推送限制次数
+* TuiJuhe.Message.SendLimitInterval TuiJuhe消息推送限制周期(时)
+
+## Settings
+
+* TuiJuhe.Security.Token 用户令牌.
diff --git a/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/System/Net/Http/IHttpClientFactoryExtensions.cs b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/System/Net/Http/IHttpClientFactoryExtensions.cs
new file mode 100644
index 000000000..e082eb04c
--- /dev/null
+++ b/aspnet-core/modules/tui-juhe/LINGYUN.Abp.TuiJuhe/System/Net/Http/IHttpClientFactoryExtensions.cs
@@ -0,0 +1,10 @@
+namespace System.Net.Http;
+
+internal static class IHttpClientFactoryExtensions
+{
+ public static HttpClient GetTuiJuheClient(
+ this IHttpClientFactory httpClientFactory)
+ {
+ return httpClientFactory.CreateClient("_Abp_TuiJuhe_Client");
+ }
+}
diff --git a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageProvider.cs b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageProvider.cs
index 4a9a48b05..65ade6783 100644
--- a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageProvider.cs
+++ b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageProvider.cs
@@ -13,7 +13,7 @@ public class WxPusherMessageProvider : WxPusherRequestProvider, IWxPusherMessage
int messageId,
CancellationToken cancellationToken = default)
{
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var content = await client.QueryMessageAsync(
messageId,
diff --git a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSender.cs b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSender.cs
index 1ed0290d5..45f889a6a 100644
--- a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSender.cs
+++ b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/Messages/WxPusherMessageSender.cs
@@ -34,7 +34,7 @@ public class WxPusherMessageSender : WxPusherRequestProvider, IWxPusherMessageSe
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var sendMessage = new SendMessage(
token,
content,
diff --git a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/WxPusherQrCodeProvider.cs b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/WxPusherQrCodeProvider.cs
index 17faee998..c755261e6 100644
--- a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/WxPusherQrCodeProvider.cs
+++ b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/QrCode/WxPusherQrCodeProvider.cs
@@ -25,7 +25,7 @@ public class WxPusherQrCodeProvider : WxPusherRequestProvider, IWxPusherQrCodePr
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var request = new CreateQrcodeRequest(token, extra, validTime);
var content = await client.CreateQrcodeAsync(
@@ -43,7 +43,7 @@ public class WxPusherQrCodeProvider : WxPusherRequestProvider, IWxPusherQrCodePr
{
Check.NotNullOrWhiteSpace(code, nameof(code));
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var content = await client.GetScanQrCodeUidAsync(
code,
diff --git a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/WxPusherUserProvider.cs b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/WxPusherUserProvider.cs
index 15f5d0616..119a97beb 100644
--- a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/WxPusherUserProvider.cs
+++ b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/LINGYUN/Abp/WxPusher/User/WxPusherUserProvider.cs
@@ -23,7 +23,7 @@ public class WxPusherUserProvider : WxPusherRequestProvider, IWxPusherUserProvid
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var content = await client.DeleteUserAsync(
token,
@@ -49,7 +49,7 @@ public class WxPusherUserProvider : WxPusherRequestProvider, IWxPusherUserProvid
}
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var content = await client.GetUserListAsync(
token,
@@ -72,7 +72,7 @@ public class WxPusherUserProvider : WxPusherRequestProvider, IWxPusherUserProvid
CancellationToken cancellationToken = default)
{
var token = await WxPusherTokenProvider.GetTokenAsync(cancellationToken);
- var client = HttpClientFactory.GetPushPlusClient();
+ var client = HttpClientFactory.GetWxPusherClient();
var content = await client.RejectUserAsync(
token,
diff --git a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/System/Net/Http/IHttpClientFactoryExtensions.cs b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/System/Net/Http/IHttpClientFactoryExtensions.cs
index 1134d85cb..66fcf2814 100644
--- a/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/System/Net/Http/IHttpClientFactoryExtensions.cs
+++ b/aspnet-core/modules/wx-pusher/LINGYUN.Abp.WxPusher/System/Net/Http/IHttpClientFactoryExtensions.cs
@@ -2,7 +2,7 @@
internal static class IHttpClientFactoryExtensions
{
- public static HttpClient GetPushPlusClient(
+ public static HttpClient GetWxPusherClient(
this IHttpClientFactory httpClientFactory)
{
return httpClientFactory.CreateClient("_Abp_WxPusher_Client");
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider.cs
index 89e87f50a..8b1b02cab 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider.cs
@@ -8,16 +8,23 @@
group.AddNotification(NotificationsTestsNames.Test1,
notificationType: NotificationType.Application,
+ contentType: NotificationContentType.Text,
lifetime: NotificationLifetime.OnlyOne);
group.AddNotification(NotificationsTestsNames.Test2,
notificationType: NotificationType.Application,
+ contentType: NotificationContentType.Html,
lifetime: NotificationLifetime.Persistent);
group.AddNotification(NotificationsTestsNames.Test3,
notificationType: NotificationType.User,
contentType: NotificationContentType.Markdown,
lifetime: NotificationLifetime.OnlyOne);
+
+ group.AddNotification(NotificationsTestsNames.Test4,
+ notificationType: NotificationType.System,
+ contentType: NotificationContentType.Json,
+ lifetime: NotificationLifetime.OnlyOne);
}
}
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider_Tests.cs
index 528e5c3ac..6dd8683d3 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider_Tests.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsDefinitionProvider_Tests.cs
@@ -25,7 +25,7 @@ namespace LINGYUN.Abp.Notifications
public async Task GetNotifications_Test()
{
var notifications = await NotificationDefinitionManager.GetNotificationsAsync();
- notifications.Count.ShouldBe(6);
+ notifications.Count.ShouldBe(7);
}
[Fact]
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsNames.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsNames.cs
index 2516405e3..a9db93e14 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsNames.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Tests/LINGYUN/Abp/Notifications/NotificationsTestsNames.cs
@@ -8,6 +8,8 @@
public const string Test2 = GroupName + ".Test2";
- public const string Test3 = GroupName + ".Test3";
+ public const string Test3 = GroupName + ".Test3";
+
+ public const string Test4 = GroupName + ".Test4";
}
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheTestBase.cs
new file mode 100644
index 000000000..a5816be3b
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheTestBase.cs
@@ -0,0 +1,7 @@
+using LINGYUN.Abp.Tests;
+
+namespace LINGYUN.Abp.Notifications.TuiJuhe;
+
+public abstract class AbpNotificationsTuiJuheTestBase : AbpTestsBase
+{
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheTestModule.cs
new file mode 100644
index 000000000..14c4db473
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/AbpNotificationsTuiJuheTestModule.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.TuiJuhe;
+
+[DependsOn(
+ typeof(AbpNotificationsTuiJuheModule),
+ typeof(AbpTuiJuheTestModule),
+ typeof(AbpNotificationsTestsModule))]
+public class AbpNotificationsTuiJuheTestModule : AbpModule
+{
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/NotificationSenderTests.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/NotificationSenderTests.cs
new file mode 100644
index 000000000..1f01d6ce4
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/NotificationSenderTests.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.TuiJuhe;
+
+public class NotificationSenderTests : AbpNotificationsTuiJuheTestBase
+{
+ protected INotificationSender NotificationSender { get; }
+
+ public NotificationSenderTests()
+ {
+ NotificationSender = GetRequiredService();
+ }
+
+ [Theory]
+ [InlineData(
+ "Form nTest Text",
+ "Text content from the Xunit unit test. \r\n Click the link https://www.baidu.com/ at the top to redirect baidu site.")]
+ public async Task Send_Text_Test(
+ string title,
+ string message)
+ {
+ var notificationData = new NotificationData();
+ notificationData.WriteStandardData(
+ title,
+ message,
+ DateTime.Now,
+ "xUnit Test");
+
+ await NotificationSender.SendNofiterAsync(
+ NotificationsTestsNames.Test1,
+ notificationData);
+ }
+
+ [Theory]
+ [InlineData(
+ "Form nTest Html",
+ "Html content from the Xunit unit test.
Click to redirect baidu site.")]
+ public async Task Send_Html_Test(
+ string title,
+ string message)
+ {
+ var notificationData = new NotificationData();
+ notificationData.WriteStandardData(
+ title,
+ message,
+ DateTime.Now,
+ "xUnit Test");
+
+ await NotificationSender.SendNofiterAsync(
+ NotificationsTestsNames.Test2,
+ notificationData);
+ }
+
+ [Theory]
+ [InlineData(
+ "Form nTest Markdown",
+ "**Markdown content from the Xunit unit test.**
Click to redirect baidu site.")]
+ public async Task Send_Markdown_Test(
+ string title,
+ string message)
+ {
+ var notificationData = new NotificationData();
+ notificationData.WriteStandardData(
+ title,
+ message,
+ DateTime.Now,
+ "xUnit Test");
+
+ await NotificationSender.SendNofiterAsync(
+ NotificationsTestsNames.Test3,
+ notificationData);
+ }
+
+ [Theory]
+ [InlineData(
+ "Form nTest Json",
+ "{\"content\":\"Content from the Xunit unit test.\",\"url\":{\"component\":{\"name\":\"a\",\"value\":\"Click to redirect baidu site.\",\"options\":{\"href\":\"https://www.baidu.com/\"}}}}")]
+ public async Task Send_Json_Test(
+ string title,
+ string message)
+ {
+ var notificationData = new NotificationData();
+ notificationData.WriteStandardData(
+ title,
+ message,
+ DateTime.Now,
+ "xUnit Test");
+
+ await NotificationSender.SendNofiterAsync(
+ NotificationsTestsNames.Test4,
+ notificationData);
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/NotificationsWxPusherTestsDefinitionProvider.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/NotificationsWxPusherTestsDefinitionProvider.cs
new file mode 100644
index 000000000..0cef4a0ba
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Abp/Notifications/TuiJuhe/NotificationsWxPusherTestsDefinitionProvider.cs
@@ -0,0 +1,27 @@
+using System.Linq;
+
+namespace LINGYUN.Abp.Notifications.TuiJuhe;
+
+public class NotificationsWxPusherTestsDefinitionProvider : NotificationDefinitionProvider
+{
+ public override void Define(INotificationDefinitionContext context)
+ {
+ var group = context.GetGroupOrNull(NotificationsTestsNames.GroupName);
+
+ var nt1 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test1));
+ nt1.WithProviders(TuiJuheNotificationPublishProvider.ProviderName)
+ .WithServiceId("1d5v5GuH");
+
+ var nt2 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test2));
+ nt2.WithProviders(TuiJuheNotificationPublishProvider.ProviderName)
+ .WithServiceId("1d5v5GuH");
+
+ var nt3 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test3));
+ nt3.WithProviders(TuiJuheNotificationPublishProvider.ProviderName)
+ .WithServiceId("1d5v5GuH");
+
+ var nt4 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test4));
+ nt4.WithProviders(TuiJuheNotificationPublishProvider.ProviderName)
+ .WithServiceId("1d5v5GuH");
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests.csproj
new file mode 100644
index 000000000..24df96843
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net6.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Usings.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Usings.cs
new file mode 100644
index 000000000..90e686b70
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.TuiJuhe.Tests/Usings.cs
@@ -0,0 +1,4 @@
+global using Xunit;
+global using Shouldly;
+global using LINGYUN.Abp.TuiJuhe;
+global using LINGYUN.Abp.Notifications.TuiJuhe;
\ No newline at end of file
diff --git a/aspnet-core/tests/LINGYUN.Abp.PushPlus.Tests/LINGYUN/Abp/PushPlus/AbpPushPlusTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.PushPlus.Tests/LINGYUN/Abp/PushPlus/AbpPushPlusTestModule.cs
index 17c8c3300..f0d1255c7 100644
--- a/aspnet-core/tests/LINGYUN.Abp.PushPlus.Tests/LINGYUN/Abp/PushPlus/AbpPushPlusTestModule.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.PushPlus.Tests/LINGYUN/Abp/PushPlus/AbpPushPlusTestModule.cs
@@ -1,4 +1,6 @@
-using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.PushPlus.Features;
+using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.Tests.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
@@ -21,4 +23,20 @@ public class AbpPushPlusTestModule : AbpModule
context.Services.ReplaceConfiguration(configuration);
}
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Map(PushPlusFeatureNames.Message.Enable, (_) => "true");
+
+ options.Map(PushPlusFeatureNames.Channel.Email.Enable, (_) => "true");
+ options.Map(PushPlusFeatureNames.Channel.Email.SendLimit, (_) => "500");
+ options.Map(PushPlusFeatureNames.Channel.Email.SendLimitInterval, (_) => "1");
+
+ options.Map(PushPlusFeatureNames.Channel.WeChat.Enable, (_) => "true");
+ options.Map(PushPlusFeatureNames.Channel.WeChat.SendLimit, (_) => "500");
+ options.Map(PushPlusFeatureNames.Channel.WeChat.SendLimitInterval, (_) => "1");
+ });
+ }
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN.Abp.TuiJuhe.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN.Abp.TuiJuhe.Tests.csproj
new file mode 100644
index 000000000..1a416f446
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN.Abp.TuiJuhe.Tests.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/AbpTuiJuheTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/AbpTuiJuheTestBase.cs
new file mode 100644
index 000000000..c34ca83eb
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/AbpTuiJuheTestBase.cs
@@ -0,0 +1,7 @@
+using LINGYUN.Abp.Tests;
+
+namespace LINGYUN.Abp.TuiJuhe;
+
+public class AbpTuiJuheTestBase : AbpTestsBase
+{
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/AbpTuiJuheTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/AbpTuiJuheTestModule.cs
new file mode 100644
index 000000000..d08457ebb
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/AbpTuiJuheTestModule.cs
@@ -0,0 +1,37 @@
+using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.Tests.Features;
+using LINGYUN.Abp.TuiJuhe.Features;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.TuiJuhe;
+
+[DependsOn(
+ typeof(AbpTuiJuheModule),
+ typeof(AbpTestsBaseModule))]
+public class AbpTuiJuheTestModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ var configurationOptions = new AbpConfigurationBuilderOptions
+ {
+ BasePath = @"D:\Projects\Development\Abp\TuiJuhe",
+ EnvironmentName = "Test"
+ };
+ var configuration = ConfigurationHelper.BuildConfiguration(configurationOptions);
+
+ context.Services.ReplaceConfiguration(configuration);
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Map(TuiJuheFeatureNames.Enable, (_) => "true");
+ options.Map(TuiJuheFeatureNames.Message.Enable, (_) => "true");
+ options.Map(TuiJuheFeatureNames.Message.SendLimit, (_) => "50");
+ options.Map(TuiJuheFeatureNames.Message.SendLimitInterval, (_) => "1");
+ });
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/Messages/TuiJuheMessageSenderTests.cs b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/Messages/TuiJuheMessageSenderTests.cs
new file mode 100644
index 000000000..c8ac0fbac
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/LINGYUN/Abp/TuiJuhe/Messages/TuiJuheMessageSenderTests.cs
@@ -0,0 +1,100 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.TuiJuhe.Messages;
+
+public class TuiJuheMessageSenderTests : AbpTuiJuheTestBase
+{
+ protected ITuiJuheMessageSender MessageSender { get; }
+ public TuiJuheMessageSenderTests()
+ {
+ MessageSender = GetRequiredService();
+ }
+
+ [Theory]
+ [InlineData(
+ "Form Test Text",
+ "Content from the xUnit unit test. \r\n Click the link https://www.baidu.com/ at the top to redirect baidu site.",
+ "1d5v5GuH")]
+ public async virtual Task Send_Text_Test(string title, string content, string serviceId)
+ {
+ var result = await MessageSender
+ .SendAsync(
+ title,
+ content,
+ serviceId,
+ MessageContentType.Text);
+
+ result.ShouldNotBeNull();
+ result.Code.ShouldBe(200);
+ result.Reason.ShouldBe("success");
+ result.Success.ShouldBeTrue();
+ result.Params.ShouldBeNull();
+ result.Result.ShouldBeNull();
+ }
+
+ [Theory]
+ [InlineData(
+ "Form Test Html",
+ "Content from the xUnit unit test.
Click to redirect baidu site.",
+ "1d5v5GuH")]
+ public async virtual Task Send_Html_Test(string title, string content, string serviceId)
+ {
+ var result = await MessageSender
+ .SendAsync(
+ title,
+ content,
+ serviceId,
+ MessageContentType.Html);
+
+ result.ShouldNotBeNull();
+ result.Code.ShouldBe(200);
+ result.Reason.ShouldBe("success");
+ result.Success.ShouldBeTrue();
+ result.Params.ShouldBeNull();
+ result.Result.ShouldBeNull();
+ }
+
+ [Theory]
+ [InlineData(
+ "Form Test Markdown",
+ "**Content from the Xunit unit test.**
Click to redirect baidu site.",
+ "1d5v5GuH")]
+ public async virtual Task Send_Markdown_Test(string title, string content, string serviceId)
+ {
+ var result = await MessageSender
+ .SendAsync(
+ title,
+ content,
+ serviceId,
+ MessageContentType.Markdown);
+
+ result.ShouldNotBeNull();
+ result.Code.ShouldBe(200);
+ result.Reason.ShouldBe("success");
+ result.Success.ShouldBeTrue();
+ result.Params.ShouldBeNull();
+ result.Result.ShouldBeNull();
+ }
+
+ [Theory]
+ [InlineData(
+ "Form Test Json",
+ "{\"content\":\"Content from the Xunit unit test.\",\"url\":{\"component\":{\"name\":\"a\",\"value\":\"Click to redirect baidu site.\",\"options\":{\"href\":\"https://www.baidu.com/\"}}}}",
+ "1d5v5GuH")]
+ public async virtual Task Send_Json_Test(string title, string content, string serviceId)
+ {
+ var result = await MessageSender
+ .SendAsync(
+ title,
+ content,
+ serviceId,
+ MessageContentType.Json);
+
+ result.ShouldNotBeNull();
+ result.Code.ShouldBe(200);
+ result.Reason.ShouldBe("success");
+ result.Success.ShouldBeTrue();
+ result.Params.ShouldBeNull();
+ result.Result.ShouldBeNull();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/Usings.cs b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/Usings.cs
new file mode 100644
index 000000000..cc8a010b0
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.TuiJuhe.Tests/Usings.cs
@@ -0,0 +1,3 @@
+global using Xunit;
+global using Shouldly;
+global using LINGYUN.Abp.TuiJuhe;
\ No newline at end of file
diff --git a/aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestModule.cs
index c27d43a09..c923c42bc 100644
--- a/aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestModule.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.WxPusher.Tests/LINGYUN/Abp/WxPusher/AbpWxPusherTestModule.cs
@@ -1,4 +1,6 @@
using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.Tests.Features;
+using LINGYUN.Abp.WxPusher.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
@@ -21,4 +23,15 @@ public class AbpWxPusherTestModule : AbpModule
context.Services.ReplaceConfiguration(configuration);
}
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Map(WxPusherFeatureNames.Enable, (_) => "true");
+ options.Map(WxPusherFeatureNames.Message.Enable, (_) => "true");
+ options.Map(WxPusherFeatureNames.Message.SendLimit, (_) => "500");
+ options.Map(WxPusherFeatureNames.Message.SendLimitInterval, (_) => "1");
+ });
+ }
}