62 changed files with 1517 additions and 11 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -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> |
||||
@ -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.TuiJuhe\LINGYUN.Abp.TuiJuhe.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -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"; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取消息内容类型
|
||||
|
/// </summary>
|
||||
|
/// <param name="notification"></param>
|
||||
|
/// <param name="defaultContentType"></param>
|
||||
|
/// <returns></returns>
|
||||
|
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, |
||||
|
}; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 指定服务编号
|
||||
|
/// </summary>
|
||||
|
/// <param name="notification">群组编码</param>
|
||||
|
/// <param name="serviceId">服务编号,在服务创建后自动生成ServiceID,是服务的唯一标识,可在每个服务的详情页中查看</param>
|
||||
|
/// <returns>
|
||||
|
/// <see cref="NotificationDefinition"/>
|
||||
|
/// </returns>
|
||||
|
public static NotificationDefinition WithServiceId( |
||||
|
this NotificationDefinition notification, |
||||
|
string serviceId) |
||||
|
{ |
||||
|
return notification.WithProperty(ServiceIdKey, serviceId); |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 获取服务编号
|
||||
|
/// </summary>
|
||||
|
/// <param name="notification"></param>
|
||||
|
public static string GetServiceIdOrNull( |
||||
|
this NotificationDefinition notification) |
||||
|
{ |
||||
|
if (notification.Properties.TryGetValue(ServiceIdKey, out var serviceIdDefine)) |
||||
|
{ |
||||
|
return serviceIdDefine.ToString(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -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<AbpNotificationsPublishOptions>(options => |
||||
|
{ |
||||
|
options.PublishProviders.Add<TuiJuheNotificationPublishProvider>(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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<AbpLocalizationOptions> localizationOptions, |
||||
|
INotificationDefinitionManager notificationDefinitionManager) |
||||
|
{ |
||||
|
FeatureChecker = featureChecker; |
||||
|
TuiJuheMessageSender = tuiJuheMessageSender; |
||||
|
LocalizerFactory = localizerFactory; |
||||
|
LocalizationOptions = localizationOptions.Value; |
||||
|
NotificationDefinitionManager = notificationDefinitionManager; |
||||
|
} |
||||
|
|
||||
|
protected async override Task<bool> 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<UserIdentifier> 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<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 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)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
# LINGYUN.Abp.Notifications.TuiJuhe |
||||
|
|
||||
|
通知模块的TuiJuhe实现 |
||||
|
|
||||
|
使应用可通过TuiJuhe发布实时通知 |
||||
|
|
||||
|
## 模块引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpNotificationsTuiJuheModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
// other |
||||
|
} |
||||
|
``` |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -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> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\TuiJuhe\SettingManagement\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\TuiJuhe\SettingManagement\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\TuiJuhe\SettingManagement\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" /> |
||||
|
<PackageReference Include="Volo.Abp.SettingManagement.Domain" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\settings\LINGYUN.Abp.SettingManagement.Application.Contracts\LINGYUN.Abp.SettingManagement.Application.Contracts.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.TuiJuhe\LINGYUN.Abp.TuiJuhe.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -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<IMvcBuilder>(mvcBuilder => |
||||
|
{ |
||||
|
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpWxPusherSettingManagementModule).Assembly); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpWxPusherSettingManagementModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<TuiJuheResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Abp/TuiJuhe/SettingManagement/Localization/Resources"); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<TuiJuheResource>() |
||||
|
.AddBaseTypes( |
||||
|
typeof(AbpUiResource) |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using LINGYUN.Abp.SettingManagement; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TuiJuhe.SettingManagement |
||||
|
{ |
||||
|
public interface ITuiJuheSettingAppService : IReadonlySettingAppService |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"Permission:TuiJuhe": "TuiJuhe", |
||||
|
"Permission:ManageSetting": "Manage Setting" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"Permission:TuiJuhe": "聚合云推", |
||||
|
"Permission:ManageSetting": "管理设置" |
||||
|
} |
||||
|
} |
||||
@ -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<SettingGroupResult> GetAllForCurrentTenantAsync() |
||||
|
{ |
||||
|
return await GetAllForProviderAsync(TenantSettingValueProvider.ProviderName, CurrentTenant.GetId().ToString()); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<SettingGroupResult> GetAllForGlobalAsync() |
||||
|
{ |
||||
|
return await GetAllForProviderAsync(GlobalSettingValueProvider.ProviderName, null); |
||||
|
} |
||||
|
|
||||
|
protected async virtual Task<SettingGroupResult> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<SettingGroupResult> GetAllForCurrentTenantAsync() |
||||
|
{ |
||||
|
return await Service.GetAllForCurrentTenantAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("by-global")] |
||||
|
public async virtual Task<SettingGroupResult> GetAllForGlobalAsync() |
||||
|
{ |
||||
|
return await Service.GetAllForGlobalAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<TuiJuheResource>(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace LINGYUN.Abp.TuiJuhe.SettingManagement |
||||
|
{ |
||||
|
public class TuiJuheSettingPermissionNames |
||||
|
{ |
||||
|
public const string GroupName = "Abp.TuiJuhe"; |
||||
|
|
||||
|
public const string ManageSetting = GroupName + ".ManageSetting"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -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> |
||||
@ -0,0 +1,29 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\TuiJuhe\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\TuiJuhe\Localization\Resources\*.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> |
||||
@ -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<AbpSystemTextJsonSerializerOptions>(options => |
||||
|
{ |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpTuiJuheModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<TuiJuheResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Abp/TuiJuhe/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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<TuiJuheResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
namespace LINGYUN.Abp.TuiJuhe.Features; |
||||
|
|
||||
|
public static class TuiJuheFeatureNames |
||||
|
{ |
||||
|
public const string GroupName = "TuiJuhe"; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 启用TuiJuhe
|
||||
|
/// </summary>
|
||||
|
public const string Enable = GroupName + ".Enable"; |
||||
|
|
||||
|
public static class Message |
||||
|
{ |
||||
|
public const string GroupName = TuiJuheFeatureNames.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"; |
||||
|
} |
||||
|
} |
||||
@ -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" |
||||
|
} |
||||
|
} |
||||
@ -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": "安全选项" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TuiJuhe.Localization; |
||||
|
|
||||
|
[LocalizationResourceName("TuiJuhe")] |
||||
|
public class TuiJuheResource |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TuiJuhe.Messages; |
||||
|
|
||||
|
public interface ITuiJuheMessageSender |
||||
|
{ |
||||
|
Task<TuiJuheResult<object, object>> SendAsync( |
||||
|
[NotNull] string title, |
||||
|
[NotNull] string content, |
||||
|
[NotNull] string serviceId, |
||||
|
MessageContentType contentType = MessageContentType.Text, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace LINGYUN.Abp.TuiJuhe.Messages; |
||||
|
|
||||
|
public enum MessageContentType |
||||
|
{ |
||||
|
Text = 0, |
||||
|
Html = 1, |
||||
|
Markdown = 2, |
||||
|
Json = 3 |
||||
|
} |
||||
@ -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<string> 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<string, string> |
||||
|
{ |
||||
|
{ "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(); |
||||
|
} |
||||
|
} |
||||
@ -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<TuiJuheResult<object, object>> 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<TuiJuheResult<object, object>>(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", |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -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<TuiJuheResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -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"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TuiJuhe.Token; |
||||
|
|
||||
|
public interface ITuiJuheTokenProvider |
||||
|
{ |
||||
|
Task<string> GetTokenAsync(CancellationToken cancellationToken = default); |
||||
|
} |
||||
@ -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<string> GetTokenAsync(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await SettingProvider.GetOrNullAsync( |
||||
|
TuiJuheSettingNames.Security.Token); |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TuiJuhe; |
||||
|
|
||||
|
[Serializable] |
||||
|
public class TuiJuheResult<TParam, TResult> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 状态码
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("code")] |
||||
|
public int Code { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 错误消息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("reason")] |
||||
|
public string Reason { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 响应数据
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("result")] |
||||
|
public TResult Result { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 响应参数
|
||||
|
/// </summary>
|
||||
|
[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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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 用户令牌. |
||||
@ -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"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
using LINGYUN.Abp.Tests; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.TuiJuhe; |
||||
|
|
||||
|
public abstract class AbpNotificationsTuiJuheTestBase : AbpTestsBase<AbpNotificationsTuiJuheTestModule> |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.TuiJuhe; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpNotificationsTuiJuheModule), |
||||
|
typeof(AbpTuiJuheTestModule), |
||||
|
typeof(AbpNotificationsTestsModule))] |
||||
|
public class AbpNotificationsTuiJuheTestModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
@ -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<INotificationSender>(); |
||||
|
} |
||||
|
|
||||
|
[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", |
||||
|
"<h>Html content from the Xunit unit test.</h> <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>")] |
||||
|
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.** <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>")] |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
@ -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"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<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\tui-juhe\LINGYUN.Abp.Notifications.TuiJuhe\LINGYUN.Abp.Notifications.TuiJuhe.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.TuiJuhe.Tests\LINGYUN.Abp.TuiJuhe.Tests.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications.Tests\LINGYUN.Abp.Notifications.Tests.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,4 @@ |
|||||
|
global using Xunit; |
||||
|
global using Shouldly; |
||||
|
global using LINGYUN.Abp.TuiJuhe; |
||||
|
global using LINGYUN.Abp.Notifications.TuiJuhe; |
||||
@ -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\tui-juhe\LINGYUN.Abp.TuiJuhe\LINGYUN.Abp.TuiJuhe.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,7 @@ |
|||||
|
using LINGYUN.Abp.Tests; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TuiJuhe; |
||||
|
|
||||
|
public class AbpTuiJuheTestBase : AbpTestsBase<AbpTuiJuheTestModule> |
||||
|
{ |
||||
|
} |
||||
@ -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<FakeFeatureOptions>(options => |
||||
|
{ |
||||
|
options.Map(TuiJuheFeatureNames.Enable, (_) => "true"); |
||||
|
options.Map(TuiJuheFeatureNames.Message.Enable, (_) => "true"); |
||||
|
options.Map(TuiJuheFeatureNames.Message.SendLimit, (_) => "50"); |
||||
|
options.Map(TuiJuheFeatureNames.Message.SendLimitInterval, (_) => "1"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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<ITuiJuheMessageSender>(); |
||||
|
} |
||||
|
|
||||
|
[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", |
||||
|
"<h>Content from the xUnit unit test.</h> <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>", |
||||
|
"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.** <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>", |
||||
|
"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(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
global using Xunit; |
||||
|
global using Shouldly; |
||||
|
global using LINGYUN.Abp.TuiJuhe; |
||||
Loading…
Reference in new issue