diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs
index 1d9bcc629..32c12c193 100644
--- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs
+++ b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs
@@ -152,6 +152,20 @@ public class AliyunSettingAppService : ApplicationService, IAliyunSettingAppServ
await SettingManager.GetOrNullAsync(AliyunSettingNames.Sms.VisableErrorToClient, providerName, providerKey),
ValueType.Boolean,
providerName);
+
+ var smsVerifyCodeSetting = aliyunSettingGroup.AddSetting(L["DisplayName:Aliyun.SmsVerifyCode"], L["Description:Aliyun.SmsVerifyCode"]);
+ smsVerifyCodeSetting.AddDetail(
+ await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName, providerName, providerKey),
+ ValueType.String,
+ providerName);
+ smsVerifyCodeSetting.AddDetail(
+ await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode),
+ StringLocalizerFactory,
+ await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode, providerName, providerKey),
+ ValueType.String,
+ providerName);
}
#endregion
diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json
index 314098cdf..0c2e819f4 100644
--- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json
+++ b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json
@@ -34,6 +34,8 @@
"Description:Policy": "Policy",
"DisplayName:Aliyun.Sms": "Sms",
"Description:Aliyun.Sms": "Sms",
+ "DisplayName:Aliyun.SmsVerifyCode": "Sms Verify Code",
+ "Description:Aliyun.SmsVerifyCode": "Sms Verify Code",
"DisplayName:ActionName": "Action Name",
"Description:ActionName": "Action Name",
"DisplayName:DefaultSignName": "Default Sign Name",
diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json
index 1e9a8b872..e88e82bf4 100644
--- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json
+++ b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json
@@ -34,6 +34,8 @@
"Description:Policy": "生成STS Token时可以指定一个额外的权限策略,以进一步限制STS Token的权限",
"DisplayName:Aliyun.Sms": "短信服务",
"Description:Aliyun.Sms": "阿里云短信服务",
+ "DisplayName:Aliyun.SmsVerifyCode": "短信认证服务",
+ "Description:Aliyun.SmsVerifyCode": "阿里云短信认证服务",
"DisplayName:ActionName": "发送短信方法",
"Description:ActionName": "发送短信方法名称,详情见阿里云Sms服务",
"DisplayName:DefaultSignName": "默认短信签名",
diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs
index 0220a6ae2..54ea316eb 100644
--- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs
+++ b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs
@@ -79,4 +79,20 @@ public static class AliyunSettingNames
///
public const string VisableErrorToClient = Prefix + ".VisableErrorToClient";
}
+
+ ///
+ /// 云通信号码认证服务
+ ///
+ public class SmsVerifyCode
+ {
+ public const string Prefix = AliyunSettingNames.Prefix + ".SmsVerifyCode";
+ ///
+ /// 默认签名
+ ///
+ public const string DefaultSignName = Prefix + ".DefaultSignName";
+ ///
+ /// 默认短信模板号
+ ///
+ public const string DefaultTemplateCode = Prefix + ".DefaultTemplateCode";
+ }
}
diff --git a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs
index 55713d7af..3a8133c95 100644
--- a/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs
+++ b/aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs
@@ -10,6 +10,7 @@ public class AliyunSettingProvider : SettingDefinitionProvider
{
context.Add(GetAuthorizationSettings());
context.Add(GetSmsSettings());
+ context.Add(GetSmsVerifyCodeSettings());
}
private SettingDefinition[] GetAuthorizationSettings()
@@ -204,6 +205,37 @@ public class AliyunSettingProvider : SettingDefinitionProvider
TenantSettingValueProvider.ProviderName)
};
}
+
+ private SettingDefinition[] GetSmsVerifyCodeSettings()
+ {
+ return new SettingDefinition[]
+ {
+ new SettingDefinition(
+ AliyunSettingNames.SmsVerifyCode.DefaultSignName,
+ displayName: L("DisplayName:DefaultSignName"),
+ description: L("Description:DefaultSignName"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode,
+ displayName: L("DisplayName:DefaultTemplateCode"),
+ description: L("Description:DefaultTemplateCode"),
+ isVisibleToClients: false,
+ isEncrypted: true
+ )
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ };
+ }
private ILocalizableString L(string name)
{
return LocalizableString.Create(name);
diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
index 31326203b..5ce110221 100644
--- a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
+++ b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
@@ -20,9 +20,12 @@ using Volo.Abp.Sms;
namespace LINGYUN.Abp.Sms.Aliyun;
[Dependency(ServiceLifetime.Singleton)]
-[ExposeServices(typeof(ISmsSender), typeof(AliyunSmsSender))]
+[ExposeServices(
+ typeof(ISmsSender),
+ typeof(IAliyunSmsVerifyCodeSender),
+ typeof(AliyunSmsSender))]
[RequiresFeature(AliyunFeatureNames.Sms.Enable)]
-public class AliyunSmsSender : ISmsSender
+public class AliyunSmsSender : ISmsSender, IAliyunSmsVerifyCodeSender
{
protected IJsonSerializer JsonSerializer { get; }
protected ISettingProvider SettingProvider { get; }
@@ -94,6 +97,63 @@ public class AliyunSmsSender : ISmsSender
}
}
+ [RequiresLimitFeature(
+ AliyunFeatureNames.Sms.SendLimit,
+ AliyunFeatureNames.Sms.SendLimitInterval,
+ LimitPolicy.Month,
+ AliyunFeatureNames.Sms.DefaultSendLimit,
+ AliyunFeatureNames.Sms.DefaultSendLimitInterval)]
+ public async virtual Task SendAsync(SmsVerifyCodeMessage message)
+ {
+ var domain = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Domain);
+ var version = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Version);
+ var signName = message.SignName ??
+ await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName);
+ var templateCode = message.TemplateCode ??
+ await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode);
+
+ Check.NotNullOrWhiteSpace(domain, AliyunSettingNames.Sms.Domain);
+ Check.NotNullOrWhiteSpace(version, AliyunSettingNames.Sms.Version);
+ Check.NotNullOrWhiteSpace(signName, AliyunSettingNames.SmsVerifyCode.DefaultSignName);
+ Check.NotNullOrWhiteSpace(templateCode, AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode);
+
+ var request = new CommonRequest
+ {
+ Domain = domain,
+ Version = version,
+ Method = MethodType.POST,
+ Action = "SendSmsVerifyCode",
+ };
+ request.AddBodyParameters("PhoneNumber", message.PhoneNumber);
+ request.AddBodyParameters("SignName", signName);
+ request.AddBodyParameters("TemplateCode", templateCode);
+ request.AddBodyParameters("TemplateParam", JsonSerializer.Serialize(message.TemplateParam));
+
+ try
+ {
+ var client = await AcsClientFactory.CreateAsync();
+ var response = client.GetCommonResponse(request);
+ var responseContent = Encoding.Default.GetString(response.HttpResponse.Content);
+ var aliyunResponse = JsonSerializer.Deserialize(responseContent);
+ if (!aliyunResponse.Success)
+ {
+ if (await SettingProvider.IsTrueAsync(AliyunSettingNames.Sms.VisableErrorToClient))
+ {
+ throw new UserFriendlyException(aliyunResponse.Code, aliyunResponse.Message);
+ }
+ throw new AliyunSmsException(aliyunResponse.Code, $"Text message sending failed, code:{aliyunResponse.Code}, message:{aliyunResponse.Message}!");
+ }
+ }
+ catch (ServerException se)
+ {
+ throw new AliyunSmsException(se.ErrorCode, $"Sending text messages to aliyun server is abnormal,type: {se.ErrorType}, error: {se.ErrorMessage}");
+ }
+ catch (ClientException ce)
+ {
+ throw new AliyunSmsException(ce.ErrorCode, $"A client exception occurred in sending SMS messages,type: {ce.ErrorType}, error: {ce.ErrorMessage}");
+ }
+ }
+
private async Task TryAddTemplateCodeAsync(CommonRequest request, SmsMessage smsMessage)
{
if (smsMessage.Properties.TryGetValue("TemplateCode", out object template) && template != null)
diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs
new file mode 100644
index 000000000..49d33db37
--- /dev/null
+++ b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs
@@ -0,0 +1,40 @@
+namespace LINGYUN.Abp.Sms.Aliyun;
+public class AliyunSmsVerifyCodeResponse
+{
+ ///
+ /// 请求状态码, OK代表请求成功
+ ///
+ public string Code { get; set; }
+ ///
+ /// 状态码的描述
+ ///
+ public string Message { get; set; }
+ ///
+ /// 请求是否成功
+ ///
+ public bool Success { get; set; }
+ ///
+ /// 请求结果数据
+ ///
+ public AliyunSmsVerifyCodeModel Model { get; set; }
+}
+
+public class AliyunSmsVerifyCodeModel
+{
+ ///
+ /// 请求Id
+ ///
+ public string RequestId { get; set; }
+ ///
+ /// 业务Id
+ ///
+ public string BizId { get; set; }
+ ///
+ /// 外部流水号
+ ///
+ public string OutId { get; set; }
+ ///
+ /// 验证码, 仅当使用阿里云短信验证服务生成验证码时携带
+ ///
+ public string VerifyCode { get; set; }
+}
diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs
new file mode 100644
index 000000000..f9b61feae
--- /dev/null
+++ b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs
@@ -0,0 +1,15 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Sms.Aliyun;
+///
+/// 阿里云发送短信验证码接口
+///
+public interface IAliyunSmsVerifyCodeSender
+{
+ ///
+ /// 发送短信验证码
+ ///
+ ///
+ ///
+ Task SendAsync(SmsVerifyCodeMessage message);
+}
diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs
new file mode 100644
index 000000000..745c9947f
--- /dev/null
+++ b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs
@@ -0,0 +1,88 @@
+namespace LINGYUN.Abp.Sms.Aliyun;
+public class SmsVerifyCodeMessage
+{
+ ///
+ /// 方案名称,如果不填则为“默认方案”。最多不超过 20 个字符。
+ ///
+ public string SchemeName { get; set; }
+ ///
+ /// 号码国家编码。默认为 86,目前也仅支持中国国内号码发送。
+ ///
+ public string CountryCode { get; set; }
+ ///
+ /// 上行短信扩展码。上行短信指发送给通信服务提供商的短信,用于定制某种服务、完成查询,或是办理某种业务等,需要收费,按运营商普通短信资费进行扣费。
+ ///
+ public string SmsUpExtendCode { get; set; }
+ ///
+ /// 外部流水号。
+ ///
+ public string OutId { get; set; }
+ ///
+ /// 验证码长度支持 4~8 位长度,默认是 4 位。
+ ///
+ public long? CodeLength { get; set; }
+ ///
+ /// 验证码有效时长,单位秒,默认为 300 秒。
+ ///
+ public long? ValidTime { get; set; }
+ ///
+ /// 核验规则,当有效时间内对同场景内的同号码重复发送验证码时,旧验证码如何处理。
+ ///
+ ///
+ /// 1 - 覆盖处理(默认),即旧验证码会失效掉。
+ /// 2 - 保留,即多个验证码都是在有效期内都可以校验通过。
+ ///
+ public long? DuplicatePolicy { get; set; }
+ ///
+ /// 时间间隔,单位:秒。即多久间隔可以发送一次验证码,用于频控,默认 60 秒。
+ ///
+ public long? Interval { get; set; }
+ ///
+ /// 生成的验证码类型。当参数 TemplateParam 传入占位符时,此参数必填,将由系统根据指定的规则生成验证码。
+ ///
+ ///
+ /// 1 - 纯数字(默认)。
+ /// 2 - 纯大写字母。
+ /// 3 - 纯小写字母。
+ /// 4 - 大小字母混合。
+ /// 5 - 数字+大写字母混合。
+ /// 6 - 数字+小写字母混合。
+ /// 7 - 数字+大小写字母混合。
+ ///
+ public long? CodeType { get; set; }
+ ///
+ /// 是否返回验证码。
+ ///
+ public bool? ReturnVerifyCode { get; set; }
+ ///
+ /// 是否自动替换签名重试(默认开启。
+ ///
+ public bool? AutoRetry { get; set; }
+ ///
+ /// 短信接收方手机号。
+ ///
+ public string PhoneNumber { get; }
+ ///
+ /// 签名名称。暂不支持使用自定义签名,请使用系统赠送的签名。
+ ///
+ public string SignName { get; }
+ ///
+ /// 短信模板 CODE。参数SignName选择赠送签名时,必须搭配赠送模板下发短信。您可在赠送模板配置页面选择适用您业务场景的模板。
+ ///
+ public string TemplateCode { get; }
+ ///
+ /// 短信模板参数。
+ ///
+ public SmsVerifyCodeMessageParam TemplateParam { get; }
+ public SmsVerifyCodeMessage(
+ string phoneNumber,
+ SmsVerifyCodeMessageParam templateParam,
+ string signName = null,
+ string templateCode = null)
+ {
+ PhoneNumber = phoneNumber;
+ TemplateParam = templateParam;
+ SignName = signName;
+ TemplateCode = templateCode;
+ }
+}
diff --git a/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs
new file mode 100644
index 000000000..4158d44c6
--- /dev/null
+++ b/aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs
@@ -0,0 +1,11 @@
+namespace LINGYUN.Abp.Sms.Aliyun;
+public class SmsVerifyCodeMessageParam
+{
+ public string Code { get; }
+ public string Min { get; }
+ public SmsVerifyCodeMessageParam(string code, string min = "5")
+ {
+ Code = code;
+ Min = min;
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Aliyun.Tests/LINGYUN/Abp/Aliyun/AbpAliyunTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.Aliyun.Tests/LINGYUN/Abp/Aliyun/AbpAliyunTestModule.cs
index 19ea7986a..25fb01f07 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Aliyun.Tests/LINGYUN/Abp/Aliyun/AbpAliyunTestModule.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Aliyun.Tests/LINGYUN/Abp/Aliyun/AbpAliyunTestModule.cs
@@ -1,4 +1,8 @@
-using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.Aliyun.Features;
+using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.Tests.Features;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Aliyun
@@ -8,5 +12,19 @@ namespace LINGYUN.Abp.Aliyun
typeof(AbpTestsBaseModule))]
public class AbpAliyunTestModule : AbpModule
{
+ private const string UserSecretsId = "09233B21-9A8A-43A3-AA75-8D83C8A9537D";
+
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(builderAction: builder =>
+ {
+ builder.AddUserSecrets(UserSecretsId);
+ }));
+
+ Configure(options =>
+ {
+ options.Map(AliyunFeatureNames.Enable, (_) => "true");
+ });
+ }
}
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AbpAliyunSmsTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AbpAliyunSmsTestModule.cs
index 35078fdff..379ea4735 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AbpAliyunSmsTestModule.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AbpAliyunSmsTestModule.cs
@@ -1,5 +1,6 @@
using LINGYUN.Abp.Aliyun;
-using LINGYUN.Abp.Sms.Aliyun;
+using LINGYUN.Abp.Aliyun.Features;
+using LINGYUN.Abp.Tests.Features;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Sms.Aliyun
@@ -9,5 +10,12 @@ namespace LINGYUN.Abp.Sms.Aliyun
typeof(AbpAliyunSmsModule))]
public class AbpAliyunSmsTestModule : AbpModule
{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Map(AliyunFeatureNames.Sms.Enable, (_) => "true");
+ });
+ }
}
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs
new file mode 100644
index 000000000..04e15c5d8
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.Configuration;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace LINGYUN.Abp.Sms.Aliyun;
+public class AliyunSmsVerifyCodeSender_Tests : AbpAliyunTestBase
+{
+ protected IAliyunSmsVerifyCodeSender SmsSender { get; }
+ protected IConfiguration Configuration { get; }
+
+ public AliyunSmsVerifyCodeSender_Tests()
+ {
+ SmsSender = GetRequiredService();
+ Configuration = GetRequiredService();
+ }
+
+ ///
+ /// 阿里云短信测试
+ ///
+ ///
+ ///
+ [Theory]
+ [InlineData("123456")]
+ public async Task Send_Test(string code)
+ {
+ var signName = Configuration["Aliyun:Sms:Sender:SignName"];
+ var phone = Configuration["Aliyun:Sms:Sender:PhoneNumber"];
+ var template = Configuration["Aliyun:Sms:Sender:TemplateCode"];
+
+ await SmsSender.SendAsync(
+ new SmsVerifyCodeMessage(
+ phone,
+ new SmsVerifyCodeMessageParam(code),
+ signName,
+ template));
+ }
+}