84 changed files with 1965 additions and 241 deletions
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
|||
<None Remove="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Account.Application.Contracts" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain.Shared\LINGYUN.Abp.Account.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,27 @@ |
|||
using Volo.Abp.Account.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
[DependsOn(typeof(AbpAccountDomainSharedModule))] |
|||
public class AbpAccountApplicationContractsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAccountApplicationContractsModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<Localization.AccountResource>() |
|||
.AddBaseTypes(typeof(AccountResource)) |
|||
.AddVirtualJson("/LINGYUN/Abp/Account/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public class RegisterDto |
|||
{ |
|||
[Required] |
|||
[Phone] |
|||
[StringLength(IdentityUserConsts.MaxPhoneNumberLength)] |
|||
public string PhoneNumber { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public class RegisterVerifyDto |
|||
{ |
|||
[Required] |
|||
[Phone] |
|||
[StringLength(IdentityUserConsts.MaxPhoneNumberLength)] |
|||
public string PhoneNumber { get; set; } |
|||
|
|||
[StringLength(IdentityUserConsts.MaxUserNameLength)] |
|||
public string UserName { get; set; } |
|||
|
|||
[EmailAddress] |
|||
[StringLength(IdentityUserConsts.MaxEmailLength)] |
|||
public string EmailAddress { get; set; } |
|||
|
|||
[Required] |
|||
[StringLength(IdentityUserConsts.MaxPasswordLength)] |
|||
[DataType(DataType.Password)] |
|||
[DisableAuditing] |
|||
public string Password { get; set; } |
|||
|
|||
[Required] |
|||
[StringLength(6)] |
|||
public string VerifyCode { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public class VerifyDto |
|||
{ |
|||
[Required] |
|||
[Phone] |
|||
public string PhoneNumber { get; set; } |
|||
|
|||
[Required] |
|||
public PhoneNumberVerifyType VerifyType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public interface IAccountAppService : IApplicationService |
|||
{ |
|||
Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input); |
|||
|
|||
Task VerifyPhoneNumberAsync(VerifyDto input); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"PhoneNumberNotRegisterd": "The registered mobile phone number is not registered!", |
|||
"PhoneVerifyCodeInvalid": "The phone verification code is invalid or expired!", |
|||
"PhoneVerifyCodeNotRepeatSend": "Phone verification code cannot be sent repeatedly within {0} minutes!" |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"PhoneNumberNotRegisterd": "手机号码未注册!", |
|||
"PhoneVerifyCodeInvalid": "手机验证码无效或已经过期!", |
|||
"PhoneVerifyCodeNotRepeatSend": "手机验证码不能在 {0} 分钟内重复发送!" |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Account.Application" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.Sms" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Account.Application.Contracts\LINGYUN.Abp.Account.Application.Contracts.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain\LINGYUN.Abp.Account.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAccountDomainModule), |
|||
typeof(Volo.Abp.Account.AbpAccountApplicationModule), |
|||
typeof(AbpAccountApplicationContractsModule))] |
|||
public class AbpAccountApplicationModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,187 @@ |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.Settings; |
|||
using Volo.Abp.Sms; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
/// <summary>
|
|||
/// 用户注册服务
|
|||
/// </summary>
|
|||
public class AccountAppService : ApplicationService, IAccountAppService |
|||
{ |
|||
protected ISmsSender SmsSender { get; } |
|||
protected IdentityUserManager UserManager { get; } |
|||
protected IIdentityUserRepository UserRepository { get; } |
|||
protected IDistributedCache<AccountRegisterVerifyCacheItem> Cache { get; } |
|||
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; } |
|||
public AccountAppService( |
|||
ISmsSender smsSender, |
|||
IdentityUserManager userManager, |
|||
IIdentityUserRepository userRepository, |
|||
IDistributedCache<AccountRegisterVerifyCacheItem> cache, |
|||
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider) |
|||
{ |
|||
Cache = cache; |
|||
SmsSender = smsSender; |
|||
UserManager = userManager; |
|||
UserRepository = userRepository; |
|||
PhoneNumberTokenProvider = phoneNumberTokenProvider; |
|||
LocalizationResource = typeof(Localization.AccountResource); |
|||
} |
|||
/// <summary>
|
|||
/// 用户注册
|
|||
/// </summary>
|
|||
/// <param name="input"></param>
|
|||
/// <returns></returns>
|
|||
/// <remarks>
|
|||
/// 用户通过VerifyPhoneNumber接口发送到手机的验证码,传递注册信息注册用户
|
|||
/// 如果没有此手机号的缓存记录或验证码不匹配,抛出验证码无效的异常
|
|||
/// 用户注册成功,清除缓存的验证码记录
|
|||
/// </remarks>
|
|||
public virtual async Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input) |
|||
{ |
|||
var phoneVerifyCacheKey = NormalizeCacheKey(input.PhoneNumber); |
|||
|
|||
var phoneVerifyCacheItem = await Cache.GetAsync(phoneVerifyCacheKey); |
|||
if(phoneVerifyCacheItem == null || !phoneVerifyCacheItem.VerifyCode.Equals(input.VerifyCode)) |
|||
{ |
|||
throw new UserFriendlyException(L["PhoneVerifyCodeInvalid"]); |
|||
} |
|||
|
|||
await CheckSelfRegistrationAsync(); |
|||
|
|||
var userEmail = input.EmailAddress ?? input.PhoneNumber + "@abp.io"; |
|||
var userName = input.UserName ?? input.PhoneNumber; |
|||
var user = new IdentityUser(GuidGenerator.Create(), userName, userEmail, CurrentTenant.Id); |
|||
|
|||
(await UserManager.CreateAsync(user, input.Password)).CheckErrors(); |
|||
|
|||
await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber); |
|||
await UserManager.SetEmailAsync(user, userEmail); |
|||
await UserManager.AddDefaultRolesAsync(user); |
|||
|
|||
await Cache.RemoveAsync(phoneVerifyCacheKey); |
|||
|
|||
return ObjectMapper.Map<IdentityUser, IdentityUserDto>(user); |
|||
} |
|||
/// <summary>
|
|||
/// 验证手机号码
|
|||
/// </summary>
|
|||
/// <param name="input"></param>
|
|||
/// <returns></returns>
|
|||
/// <remarks>
|
|||
/// 用户传递手机号码及认证类型
|
|||
/// 1、如果认证类型为注册:
|
|||
/// 先查询是否存在此手机号的缓存验证码信息,如果存在,抛出不能重复发送验证码异常
|
|||
/// 随机生成6位纯数字验证码,通过短信接口服务发送到用户手机,并缓存验证码,设定一个有效时长
|
|||
///
|
|||
/// 2、如果认证类型为登录:
|
|||
/// 先查询是否存在此手机号的缓存验证码信息,如果存在,抛出不能重复发送验证码异常
|
|||
/// 通过手机号查询用户信息,如果用户不存在,抛出手机号未注册异常
|
|||
/// 调用PhoneNumberTokenProvider接口生成6位手机验证码,用途为 phone_verify
|
|||
/// 发送手机验证码到用户手机,并缓存验证码,设定一个有效时长
|
|||
///
|
|||
/// 用户调用 IdentityServer4/connect/token 登录系统(需要引用LINGYUN.Abp.IdentityServer.SmsValidator模块)
|
|||
/// 参数1:grant_type=phone_verify
|
|||
/// 参数2:phone_number=手机号码
|
|||
/// 参数3:phone_verify_code=手机验证码
|
|||
/// 参数4:client_id=客户端标识
|
|||
/// 参数5:client_secret=客户端密钥
|
|||
/// </remarks>
|
|||
public virtual async Task VerifyPhoneNumberAsync(VerifyDto input) |
|||
{ |
|||
var verifyCodeExpiration = await SettingProvider.GetAsync<int>(AccountSettingNames.PhoneVerifyCodeExpiration); |
|||
var phoneVerifyCacheKey = NormalizeCacheKey(input.PhoneNumber); |
|||
var verifyCacheItem = await Cache.GetAsync(phoneVerifyCacheKey); |
|||
if (verifyCacheItem != null) |
|||
{ |
|||
throw new UserFriendlyException(L["PhoneVerifyCodeNotRepeatSend", verifyCodeExpiration]); |
|||
} |
|||
verifyCacheItem = new AccountRegisterVerifyCacheItem |
|||
{ |
|||
PhoneNumber = input.PhoneNumber, |
|||
}; |
|||
if (input.VerifyType == PhoneNumberVerifyType.Register) |
|||
{ |
|||
var phoneVerifyCode = new Random().Next(100000, 999999); |
|||
verifyCacheItem.VerifyCode = phoneVerifyCode.ToString(); |
|||
var templateCode = await SettingProvider.GetOrNullAsync(AccountSettingNames.SmsRegisterTemplateCode); |
|||
await SendPhoneVerifyMessageAsync(templateCode, input.PhoneNumber, phoneVerifyCode.ToString()); |
|||
} |
|||
else |
|||
{ |
|||
var phoneVerifyCode = await SendSigninVerifyCodeAsync(input.PhoneNumber); |
|||
verifyCacheItem.VerifyCode = phoneVerifyCode; |
|||
} |
|||
var cacheOptions = new DistributedCacheEntryOptions |
|||
{ |
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(verifyCodeExpiration) |
|||
}; |
|||
await Cache.SetAsync(phoneVerifyCacheKey, verifyCacheItem, cacheOptions); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 发送登录验证码
|
|||
/// </summary>
|
|||
/// <param name="phoneNumber">手机号</param>
|
|||
/// <returns>返回登录验证码</returns>
|
|||
protected virtual async Task<string> SendSigninVerifyCodeAsync(string phoneNumber) |
|||
{ |
|||
// 查找用户信息
|
|||
var user = await UserRepository.FindByPhoneNumberAsync(phoneNumber); |
|||
if (user == null) |
|||
{ |
|||
throw new UserFriendlyException(L["PhoneNumberNotRegisterd"]); |
|||
} |
|||
// 获取登录验证码模板号
|
|||
var templateCode = await SettingProvider.GetOrNullAsync(AccountSettingNames.SmsSigninTemplateCode); |
|||
// 生成手机验证码
|
|||
var phoneVerifyCode = await PhoneNumberTokenProvider.GenerateAsync("phone_verify", UserManager, user); |
|||
// 发送短信验证码
|
|||
await SendPhoneVerifyMessageAsync(templateCode, user.PhoneNumber, phoneVerifyCode); |
|||
|
|||
return phoneVerifyCode; |
|||
} |
|||
/// <summary>
|
|||
/// 检查是否允许用户注册
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
protected virtual async Task CheckSelfRegistrationAsync() |
|||
{ |
|||
if (!await SettingProvider.IsTrueAsync(Volo.Abp.Account.Settings.AccountSettingNames.IsSelfRegistrationEnabled)) |
|||
{ |
|||
throw new UserFriendlyException(L["SelfRegistrationDisabledMessage"]); |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 发送短信验证码
|
|||
/// </summary>
|
|||
/// <param name="templateCode"></param>
|
|||
/// <param name="phoneNumber"></param>
|
|||
/// <param name="verifyCode"></param>
|
|||
/// <returns></returns>
|
|||
protected virtual async Task SendPhoneVerifyMessageAsync(string templateCode, string phoneNumber, string verifyCode) |
|||
{ |
|||
var sendMessage = new SmsMessage(phoneNumber, "SendSmsMessage"); |
|||
sendMessage.Properties.Add("code", verifyCode); |
|||
sendMessage.Properties.Add("TemplateCode", templateCode); |
|||
await SmsSender.SendAsync(sendMessage); |
|||
} |
|||
/// <summary>
|
|||
/// 格式化缓存主键
|
|||
/// </summary>
|
|||
/// <param name="phoneNumber">手机号码</param>
|
|||
/// <returns></returns>
|
|||
protected string NormalizeCacheKey(string phoneNumber) |
|||
{ |
|||
return $"ACCOUNT-PHONE:{phoneNumber}"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,19 @@ |
|||
using LINGYUN.Abp.Account.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class AbpAccountDomainSharedModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<AccountResource>("zh-Hans"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public class AccountRegisterVerifyCacheItem |
|||
{ |
|||
public string PhoneNumber { get; set; } |
|||
public string VerifyCode { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public class AccountSettingNames |
|||
{ |
|||
public const string GroupName = "Abp.Account"; |
|||
/// <summary>
|
|||
/// 短信验证码过期时间
|
|||
/// </summary>
|
|||
public const string PhoneVerifyCodeExpiration = GroupName + ".PhoneVerifyCodeExpiration"; |
|||
/// <summary>
|
|||
/// 用户注册短信验证码模板号
|
|||
/// </summary>
|
|||
public const string SmsRegisterTemplateCode = GroupName + ".SmsRegisterTemplateCode"; |
|||
/// <summary>
|
|||
/// 用户登录短信验证码模板号
|
|||
/// </summary>
|
|||
public const string SmsSigninTemplateCode = GroupName + ".SmsSigninTemplateCode"; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Account.Localization |
|||
{ |
|||
[LocalizationResourceName("LINYUNAbpAccount")] |
|||
public class AccountResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public enum PhoneNumberVerifyType : sbyte |
|||
{ |
|||
Register = 0, |
|||
Signin = 10 |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
|||
<None Remove="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Identity.Domain" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain.Shared\LINGYUN.Abp.Account.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,25 @@ |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
[DependsOn(typeof(AbpAccountDomainSharedModule))] |
|||
public class AbpAccountDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAccountDomainModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<Localization.AccountResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/Account/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using LINGYUN.Abp.Account.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public class AbpAccountSettingDefinitionProvider : SettingDefinitionProvider |
|||
{ |
|||
public override void Define(ISettingDefinitionContext context) |
|||
{ |
|||
|
|||
context.Add(GetAccountSettings()); |
|||
} |
|||
|
|||
protected SettingDefinition[] GetAccountSettings() |
|||
{ |
|||
return new SettingDefinition[] |
|||
{ |
|||
new SettingDefinition(AccountSettingNames.SmsRegisterTemplateCode, |
|||
"SMS_190728520", L("DisplayName:SmsRegisterTemplateCode"), L("Description:SmsRegisterTemplateCode")), |
|||
new SettingDefinition(AccountSettingNames.SmsSigninTemplateCode, |
|||
"SMS_190728516", L("DisplayName:SmsSigninTemplateCode"), L("Description:SmsSigninTemplateCode")), |
|||
new SettingDefinition(AccountSettingNames.PhoneVerifyCodeExpiration, |
|||
"3", L("DisplayName:PhoneVerifyCodeExpiration"), L("Description:PhoneVerifyCodeExpiration")), |
|||
}; |
|||
} |
|||
|
|||
protected LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<AccountResource>(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
public interface IIdentityUserRepository : IReadOnlyRepository<IdentityUser, Guid> |
|||
{ |
|||
Task<IdentityUser> FindByPhoneNumberAsync(string phoneNumber); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"DisplayName:SmsRegisterTemplateCode": "Register sms template", |
|||
"Description:SmsRegisterTemplateCode": "When the user registers, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration", |
|||
"DisplayName:SmsSigninTemplateCode": "Signin sms template", |
|||
"Description:SmsSigninTemplateCode": "When the user logs in, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration", |
|||
"DisplayName:PhoneVerifyCodeExpiration": "SMS verification code validity", |
|||
"Description:PhoneVerifyCodeExpiration": "The valid time for the user to send SMS verification code, unit m, default 3m" |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"DisplayName:SmsRegisterTemplateCode": "用户注册短信模板", |
|||
"Description:SmsRegisterTemplateCode": "用户注册时发送短信验证码的模板号,填写对应云平台注册的模板号", |
|||
"DisplayName:SmsSigninTemplateCode": "用户登录短信模板", |
|||
"Description:SmsSigninTemplateCode": "用户登录时发送短信验证码的模板号,填写对应云平台注册的模板号", |
|||
"DisplayName:PhoneVerifyCodeExpiration": "短信验证码有效期", |
|||
"Description:PhoneVerifyCodeExpiration": "用户发送短信验证码的有效时长,单位m,默认3m" |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Account.Application.Contracts\LINGYUN.Abp.Account.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAccountApplicationContractsModule), |
|||
typeof(AbpAspNetCoreMvcModule))] |
|||
public class AbpAccountHttpApiModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<IMvcBuilder>(mvcBuilder => |
|||
{ |
|||
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAccountHttpApiModule).Assembly); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Account; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace LINGYUN.Abp.Account |
|||
{ |
|||
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)] |
|||
[Area("account")] |
|||
[Route("api/account/phone")] |
|||
public class AccountController : AbpController, IAccountAppService |
|||
{ |
|||
protected IAccountAppService AccountAppService { get; } |
|||
public AccountController( |
|||
IAccountAppService accountAppService) |
|||
{ |
|||
AccountAppService = accountAppService; |
|||
} |
|||
|
|||
[HttpPost] |
|||
[Route("register")] |
|||
public virtual async Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input) |
|||
{ |
|||
return await AccountAppService.RegisterAsync(input); |
|||
} |
|||
|
|||
[HttpPost] |
|||
[Route("verify")] |
|||
public virtual async Task VerifyPhoneNumberAsync(VerifyDto input) |
|||
{ |
|||
await AccountAppService.VerifyPhoneNumberAsync(input); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
<?xml version="1.0"?> |
|||
<doc> |
|||
<assembly> |
|||
<name>LINGYUN.Abp.EventBus.CAP</name> |
|||
</assembly> |
|||
<members> |
|||
<member name="T:DotNetCore.CAP.ConsumerServiceSelector"> |
|||
<summary> |
|||
消费者查找器 |
|||
</summary> |
|||
</member> |
|||
<member name="P:DotNetCore.CAP.ConsumerServiceSelector.AbpDistributedEventBusOptions"> |
|||
<summary> |
|||
Abp分布式事件配置 |
|||
</summary> |
|||
</member> |
|||
<member name="P:DotNetCore.CAP.ConsumerServiceSelector.ServiceProvider"> |
|||
<summary> |
|||
服务提供者 |
|||
</summary> |
|||
</member> |
|||
<member name="M:DotNetCore.CAP.ConsumerServiceSelector.#ctor(System.IServiceProvider,Microsoft.Extensions.Options.IOptions{Volo.Abp.EventBus.Distributed.AbpDistributedEventBusOptions})"> |
|||
<summary> |
|||
Creates a new <see cref="T:DotNetCore.CAP.Internal.ConsumerServiceSelector" />. |
|||
</summary> |
|||
</member> |
|||
<member name="M:DotNetCore.CAP.ConsumerServiceSelector.FindConsumersFromInterfaceTypes(System.IServiceProvider)"> |
|||
<summary> |
|||
查找消费者集合 |
|||
</summary> |
|||
<param name="provider"></param> |
|||
<returns></returns> |
|||
</member> |
|||
<member name="M:DotNetCore.CAP.ConsumerServiceSelector.GetHandlerDescription(System.Type,System.Type)"> |
|||
<summary> |
|||
获取事件处理器集合 |
|||
</summary> |
|||
<param name="eventType"></param> |
|||
<param name="typeInfo"></param> |
|||
<returns></returns> |
|||
</member> |
|||
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusModule"> |
|||
<summary> |
|||
AbpCAPEventBusModule |
|||
</summary> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusModule.ConfigureServices(Volo.Abp.Modularity.ServiceConfigurationContext)"> |
|||
<summary> |
|||
ConfigureServices |
|||
</summary> |
|||
<param name="context"></param> |
|||
</member> |
|||
<member name="T:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus"> |
|||
<summary> |
|||
CAP分布式事件总线 |
|||
</summary> |
|||
</member> |
|||
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.AbpDistributedEventBusOptions"> |
|||
<summary> |
|||
Abp分布式事件总线配置 |
|||
</summary> |
|||
</member> |
|||
<member name="F:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CapPublisher"> |
|||
<summary> |
|||
CAP消息发布接口 |
|||
</summary> |
|||
</member> |
|||
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.HandlerFactories"> |
|||
<summary> |
|||
本地事件处理器工厂对象集合 |
|||
</summary> |
|||
</member> |
|||
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.EventTypes"> |
|||
<summary> |
|||
本地事件集合 |
|||
</summary> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.#ctor(Microsoft.Extensions.DependencyInjection.IServiceScopeFactory,Microsoft.Extensions.Options.IOptions{Volo.Abp.EventBus.Distributed.AbpDistributedEventBusOptions},DotNetCore.CAP.ICapPublisher)"> |
|||
<summary> |
|||
constructor |
|||
</summary> |
|||
<param name="serviceScopeFactory"></param> |
|||
<param name="distributedEventBusOptions"></param> |
|||
<param name="capPublisher"></param> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Subscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)"> |
|||
<summary> |
|||
订阅事件 |
|||
</summary> |
|||
<param name="eventType"></param> |
|||
<param name="factory"></param> |
|||
<returns></returns> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe``1(System.Func{``0,System.Threading.Tasks.Task})"> |
|||
<summary> |
|||
退订事件 |
|||
</summary> |
|||
<typeparam name="TEvent">事件类型</typeparam> |
|||
<param name="action"></param> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe(System.Type,Volo.Abp.EventBus.IEventHandler)"> |
|||
<summary> |
|||
退订事件 |
|||
</summary> |
|||
<param name="eventType">事件类型</param> |
|||
<param name="handler">事件处理器</param> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)"> |
|||
<summary> |
|||
退订事件 |
|||
</summary> |
|||
<param name="eventType">事件类型</param> |
|||
<param name="factory">事件处理器工厂</param> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.UnsubscribeAll(System.Type)"> |
|||
<summary> |
|||
退订所有事件 |
|||
</summary> |
|||
<param name="eventType">事件类型</param> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Subscribe``1(Volo.Abp.EventBus.Distributed.IDistributedEventHandler{``0})"> |
|||
<summary> |
|||
订阅事件 |
|||
</summary> |
|||
<typeparam name="TEvent">事件类型</typeparam> |
|||
<param name="handler">事件处理器</param> |
|||
<returns></returns> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.PublishAsync(System.Type,System.Object)"> |
|||
<summary> |
|||
发布事件 |
|||
</summary> |
|||
<param name="eventType">事件类型</param> |
|||
<param name="eventData">事件数据对象</param> |
|||
<returns></returns> |
|||
</member> |
|||
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.GetHandlerFactories(System.Type)"> |
|||
<summary> |
|||
获取事件处理器工厂列表 |
|||
</summary> |
|||
<param name="eventType"></param> |
|||
<returns></returns> |
|||
</member> |
|||
<member name="T:Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions"> |
|||
<summary> |
|||
CAP ServiceCollectionExtensions |
|||
</summary> |
|||
</member> |
|||
<member name="M:Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.AddCAPEventBus(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{DotNetCore.CAP.CapOptions})"> |
|||
<summary> |
|||
Adds and configures the consistence services for the consistency. |
|||
</summary> |
|||
<param name="services"></param> |
|||
<param name="capAction"></param> |
|||
<returns></returns> |
|||
</member> |
|||
</members> |
|||
</doc> |
|||
@ -0,0 +1,25 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINYUN\Abp\Sms\Aliyun\Localization\Resources\en.json" /> |
|||
<None Remove="LINYUN\Abp\Sms\Aliyun\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINYUN\Abp\Sms\Aliyun\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINYUN\Abp\Sms\Aliyun\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="aliyun-net-sdk-core" Version="1.5.6" /> |
|||
<PackageReference Include="Volo.Abp.Json" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.Sms" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,35 @@ |
|||
using LINYUN.Abp.Sms.Aliyun.Localization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Sms; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINYUN.Abp.Sms.Aliyun |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpSmsModule), |
|||
typeof(AbpJsonModule), |
|||
typeof(AbpLocalizationModule))] |
|||
public class AbpAliyunSmsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AliyunSmsOptions>(configuration.GetSection("Aliyun:Sms")); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAliyunSmsModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<AliyunSmsResource>("en") |
|||
.AddVirtualJson("/LINYUN/Abp/Sms/Aliyun/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
namespace LINYUN.Abp.Sms.Aliyun |
|||
{ |
|||
public class AliyunSmsOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 区域Id
|
|||
/// </summary>
|
|||
public string RegionId { get; set; } = "default"; |
|||
/// <summary>
|
|||
/// 阿里云sms服务域名
|
|||
/// </summary>
|
|||
public string Domain { get; set; } = "dysmsapi.aliyuncs.com"; |
|||
/// <summary>
|
|||
/// 调用方法名称
|
|||
/// </summary>
|
|||
public string ActionName { get; set; } = "SendSms"; |
|||
/// <summary>
|
|||
/// ApiKey
|
|||
/// </summary>
|
|||
public string AccessKeyId { get; set; } |
|||
/// <summary>
|
|||
/// Api密钥
|
|||
/// </summary>
|
|||
public string AccessKeySecret { get; set; } |
|||
/// <summary>
|
|||
/// 默认版本号
|
|||
/// </summary>
|
|||
public string DefaultVersion { get; set; } = "2017-05-25"; |
|||
/// <summary>
|
|||
/// 默认签名
|
|||
/// </summary>
|
|||
public string DefaultSignName { get; set; } |
|||
/// <summary>
|
|||
/// 默认短信模板号
|
|||
/// </summary>
|
|||
public string DefaultTemplateCode { get; set; } |
|||
/// <summary>
|
|||
/// 开发人员号码,当应用处于开发模式时,默认所有信息都会发送到此号码
|
|||
/// </summary>
|
|||
public string DeveloperPhoneNumber { get; set; } = "13800138000"; |
|||
/// <summary>
|
|||
/// 展示错误给客户端
|
|||
/// </summary>
|
|||
public bool VisableErrorToClient { get; set; } = false; |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
using LINYUN.Abp.Sms.Aliyun.Localization; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINYUN.Abp.Sms.Aliyun |
|||
{ |
|||
public class AliyunSmsResponse |
|||
{ |
|||
public string Code { get; set; } |
|||
public string Message { get; set; } |
|||
public string RequestId { get; set; } |
|||
|
|||
public bool IsSuccess() |
|||
{ |
|||
return "ok".Equals(Code, StringComparison.CurrentCultureIgnoreCase); |
|||
} |
|||
|
|||
public ILocalizableString GetErrorMessage() |
|||
{ |
|||
Check.NotNullOrWhiteSpace(Code, nameof(Code)); |
|||
switch (Code) |
|||
{ |
|||
case "isv.SMS_TEMPLATE_ILLEGAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("SMS_TEMPLATE_ILLEGAL"); |
|||
case "isv.SMS_SIGNATURE_ILLEGAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("SMS_SIGNATURE_ILLEGAL"); |
|||
case "isv.MOBILE_NUMBER_ILLEGAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("MOBILE_NUMBER_ILLEGAL"); |
|||
case "isv.TEMPLATE_MISSING_PARAMETERS": |
|||
return LocalizableString.Create<AliyunSmsResource>("TEMPLATE_MISSING_PARAMETERS"); |
|||
case "isv.EXTEND_CODE_ERROR": |
|||
return LocalizableString.Create<AliyunSmsResource>("EXTEND_CODE_ERROR"); |
|||
case "isv.DOMESTIC_NUMBER_NOT_SUPPORTED": |
|||
return LocalizableString.Create<AliyunSmsResource>("DOMESTIC_NUMBER_NOT_SUPPORTED"); |
|||
case "isv.DAY_LIMIT_CONTROL": |
|||
return LocalizableString.Create<AliyunSmsResource>("DAY_LIMIT_CONTROL"); |
|||
case "isv.SMS_CONTENT_ILLEGAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("SMS_CONTENT_ILLEGAL"); |
|||
case "isv.SMS_SIGN_ILLEGAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("SMS_SIGN_ILLEGAL"); |
|||
case "isp.RAM_PERMISSION_DENY": |
|||
return LocalizableString.Create<AliyunSmsResource>("RAM_PERMISSION_DENY"); |
|||
case "isp.OUT_OF_SERVICE": |
|||
return LocalizableString.Create<AliyunSmsResource>("OUT_OF_SERVICE"); |
|||
case "isv.PRODUCT_UN_SUBSCRIPT": |
|||
return LocalizableString.Create<AliyunSmsResource>("PRODUCT_UN_SUBSCRIPT"); |
|||
case "isv.PRODUCT_UNSUBSCRIBE": |
|||
return LocalizableString.Create<AliyunSmsResource>("PRODUCT_UNSUBSCRIBE"); |
|||
case "isv.ACCOUNT_NOT_EXISTS": |
|||
return LocalizableString.Create<AliyunSmsResource>("ACCOUNT_NOT_EXISTS"); |
|||
case "isv.ACCOUNT_ABNORMAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("ACCOUNT_ABNORMAL"); |
|||
case "isv.INVALID_PARAMETERS": |
|||
return LocalizableString.Create<AliyunSmsResource>("INVALID_PARAMETERS"); |
|||
case "isv.SYSTEM_ERROR": |
|||
return LocalizableString.Create<AliyunSmsResource>("SYSTEM_ERROR"); |
|||
case "isv.INVALID_JSON_PARAM": |
|||
return LocalizableString.Create<AliyunSmsResource>("INVALID_JSON_PARAM"); |
|||
case "isv.BLACK_KEY_CONTROL_LIMIT": |
|||
return LocalizableString.Create<AliyunSmsResource>("BLACK_KEY_CONTROL_LIMIT"); |
|||
case "isv.PARAM_LENGTH_LIMIT": |
|||
return LocalizableString.Create<AliyunSmsResource>("PARAM_LENGTH_LIMIT"); |
|||
case "isv.PARAM_NOT_SUPPORT_URL": |
|||
return LocalizableString.Create<AliyunSmsResource>("PARAM_NOT_SUPPORT_URL"); |
|||
case "isv.AMOUNT_NOT_ENOUGH": |
|||
return LocalizableString.Create<AliyunSmsResource>("AMOUNT_NOT_ENOUGH"); |
|||
case "isv.TEMPLATE_PARAMS_ILLEGAL": |
|||
return LocalizableString.Create<AliyunSmsResource>("TEMPLATE_PARAMS_ILLEGAL"); |
|||
default: |
|||
throw new AbpException("no error code define!"); |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
using Aliyun.Acs.Core; |
|||
using Aliyun.Acs.Core.Exceptions; |
|||
using Aliyun.Acs.Core.Http; |
|||
using Aliyun.Acs.Core.Profile; |
|||
using LINYUN.Abp.Sms.Aliyun.Localization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Sms; |
|||
|
|||
namespace LINYUN.Abp.Sms.Aliyun |
|||
{ |
|||
[Dependency(ServiceLifetime.Singleton)] |
|||
[ExposeServices(typeof(ISmsSender), typeof(AliyunSmsSender))] |
|||
public class AliyunSmsSender : ISmsSender |
|||
{ |
|||
protected AliyunSmsOptions Options { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IHostEnvironment Environment { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
public AliyunSmsSender( |
|||
IHostEnvironment environment, |
|||
IJsonSerializer jsonSerializer, |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AliyunSmsOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
Environment = environment; |
|||
JsonSerializer = jsonSerializer; |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public Task SendAsync(SmsMessage smsMessage) |
|||
{ |
|||
CommonRequest request = new CommonRequest |
|||
{ |
|||
Method = MethodType.POST, |
|||
Domain = Options.Domain, |
|||
Action = Options.ActionName, |
|||
Version = Options.DefaultVersion |
|||
}; |
|||
if (smsMessage.Properties.TryGetValue("TemplateCode", out object template)) |
|||
{ |
|||
request.AddQueryParameters("TemplateCode", template.ToString()); |
|||
smsMessage.Properties.Remove("TemplateCode"); |
|||
} |
|||
else |
|||
{ |
|||
Check.NotNullOrWhiteSpace(Options.DefaultTemplateCode, nameof(Options.DefaultTemplateCode)); |
|||
request.AddQueryParameters("TemplateCode", Options.DefaultTemplateCode); |
|||
} |
|||
|
|||
if (smsMessage.Properties.TryGetValue("SignName", out object signName)) |
|||
{ |
|||
request.AddQueryParameters("SignName", signName.ToString()); |
|||
smsMessage.Properties.Remove("SignName"); |
|||
} |
|||
else |
|||
{ |
|||
Check.NotNullOrWhiteSpace(Options.DefaultSignName, nameof(Options.DefaultSignName)); |
|||
request.AddQueryParameters("SignName", Options.DefaultSignName); |
|||
} |
|||
if (Environment.IsDevelopment()) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(Options.DeveloperPhoneNumber, nameof(Options.DeveloperPhoneNumber)); |
|||
request.AddQueryParameters("PhoneNumbers", Options.DeveloperPhoneNumber); |
|||
} |
|||
else |
|||
{ |
|||
request.AddQueryParameters("PhoneNumbers", smsMessage.PhoneNumber); |
|||
} |
|||
|
|||
var queryParamJson = JsonSerializer.Serialize(smsMessage.Properties); |
|||
request.AddQueryParameters("TemplateParam", queryParamJson); |
|||
try |
|||
{ |
|||
IClientProfile profile = DefaultProfile.GetProfile(Options.RegionId, Options.AccessKeyId, Options.AccessKeySecret); |
|||
IAcsClient client = new DefaultAcsClient(profile); |
|||
CommonResponse response = client.GetCommonResponse(request); |
|||
var responseContent = Encoding.Default.GetString(response.HttpResponse.Content); |
|||
var aliyunResponse = JsonSerializer.Deserialize<AliyunSmsResponse>(responseContent); |
|||
if (!aliyunResponse.IsSuccess()) |
|||
{ |
|||
var localizerFactory = ServiceProvider.GetRequiredService<IStringLocalizerFactory>(); |
|||
var localizerError = aliyunResponse.GetErrorMessage().Localize(localizerFactory); |
|||
if (Options.VisableErrorToClient) |
|||
{ |
|||
var localizer = ServiceProvider.GetRequiredService<IStringLocalizer<AliyunSmsResource>>(); |
|||
localizerError = localizer["SendMessageFailed", localizerError]; |
|||
throw new UserFriendlyException(localizerError); |
|||
} |
|||
throw new AbpException($"Text message sending failed:{localizerError}!"); |
|||
} |
|||
} |
|||
catch(ServerException se) |
|||
{ |
|||
throw new AbpException("Sending text messages to aliyun server is abnormal", se); |
|||
} |
|||
catch(ClientException ce) |
|||
{ |
|||
throw new AbpException("A client exception occurred in sending SMS messages", ce); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINYUN.Abp.Sms.Aliyun |
|||
{ |
|||
public class AliyunSmsSuccessResponse : AliyunSmsResponse |
|||
{ |
|||
public string BizId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINYUN.Abp.Sms.Aliyun.Localization |
|||
{ |
|||
[LocalizationResourceName("AliyunSms")] |
|||
public class AliyunSmsResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"SendMessageFailed": "Text message sending failed:{0}", |
|||
"SMS_TEMPLATE_ILLEGAL": "template is not valid (not present or blocked)", |
|||
"SMS_SIGNATURE_ILLEGAL": "the signature is not valid (not present or blocked)", |
|||
"MOBILE_NUMBER_ILLEGAL": "invalid phone number ", |
|||
"TEMPLATE_MISSING_PARAMETERS": "template variable missing corresponding parameter value ", |
|||
"EXTEND_CODE_ERROR": "Error using extension code", |
|||
"DOMESTIC_NUMBER_NOT_SUPPORTED": "the template for international/Hong Kong, Macao and Taiwan messages does not support the sending of domestic Numbers", |
|||
"DAY_LIMIT_CONTROL": "trigger daily send limit ", |
|||
"SMS_CONTENT_ILLEGAL": "SMS content contains prohibited content ", |
|||
"SMS_SIGN_ILLEGAL": "signature is not allowed ", |
|||
"RAM_PERMISSION_DENY": "RAM permission DENY", |
|||
"OUT_OF_SERVICE": "business down ", |
|||
"PRODUCT_UN_SUBSCRIPT": "ali cloud customers who have not opened cloud communication products", |
|||
"PRODUCT_UNSUBSCRIBE": "product not opened ", |
|||
"ACCOUNT_NOT_EXISTS": "the account does not exist ", |
|||
"ACCOUNT_ABNORMAL": "account abnormal ", |
|||
"INVALID_PARAMETERS": "parameter exception ", |
|||
"SYSTEM_ERROR": "system error ", |
|||
"INVALID_JSON_PARAM": "invalid JSON parameter, only accept string value ", |
|||
"BLACK_KEY_CONTROL_LIMIT": "blacklist control ", |
|||
"PARAM_LENGTH_LIMIT": "the parameter exceeds the length limit ", |
|||
"PARAM_NOT_SUPPORT_URL": "unsupported URL", |
|||
"AMOUNT_NOT_ENOUGH": "insufficient account balance ", |
|||
"TEMPLATE_PARAMS_ILLEGAL": "template variable contains illegal keyword" |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"SendMessageFailed": "发送短信错误:{0}", |
|||
"SMS_TEMPLATE_ILLEGAL": "模板不合法(不存在或被拉黑)", |
|||
"SMS_SIGNATURE_ILLEGAL": "签名不合法(不存在或被拉黑)", |
|||
"MOBILE_NUMBER_ILLEGAL": "无效的电话号码", |
|||
"TEMPLATE_MISSING_PARAMETERS": "模板变量缺少对应参数值", |
|||
"EXTEND_CODE_ERROR": "扩展码使用错误", |
|||
"DOMESTIC_NUMBER_NOT_SUPPORTED": "国际/港澳台消息模板不支持发送境内号码", |
|||
"DAY_LIMIT_CONTROL": "触发日发送限额", |
|||
"SMS_CONTENT_ILLEGAL": "短信内容包含禁止发送内容", |
|||
"SMS_SIGN_ILLEGAL": "签名禁止使用", |
|||
"RAM_PERMISSION_DENY": "没有访问权限", |
|||
"OUT_OF_SERVICE": "业务停机", |
|||
"PRODUCT_UN_SUBSCRIPT": "未开通云通信产品的阿里云客户", |
|||
"PRODUCT_UNSUBSCRIBE": "产品未开通", |
|||
"ACCOUNT_NOT_EXISTS": "账户不存在", |
|||
"ACCOUNT_ABNORMAL": "账户异常", |
|||
"INVALID_PARAMETERS": "参数异常", |
|||
"SYSTEM_ERROR": "系统错误", |
|||
"INVALID_JSON_PARAM": "JSON参数不合法,只接受字符串值", |
|||
"BLACK_KEY_CONTROL_LIMIT": "黑名单管控", |
|||
"PARAM_LENGTH_LIMIT": "参数超出长度限制", |
|||
"PARAM_NOT_SUPPORT_URL": "不支持URL", |
|||
"AMOUNT_NOT_ENOUGH": "账户余额不足", |
|||
"TEMPLATE_PARAMS_ILLEGAL": "模版变量里包含非法关键字" |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\IdentityServer\Localization\Resources\en.json" /> |
|||
<None Remove="LINGYUN\Abp\IdentityServer\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\IdentityServer\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\IdentityServer\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.IdentityServer.Domain" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\account\LINGYUN.Abp.Account.Domain\LINGYUN.Abp.Account.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,37 @@ |
|||
using LINGYUN.Abp.IdentityServer.SmsValidator; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.IdentityServer; |
|||
using Volo.Abp.IdentityServer.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.IdentityServer |
|||
{ |
|||
[DependsOn(typeof(AbpIdentityServerDomainModule))] |
|||
public class AbpIdentityServerSmValidatorModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<IIdentityServerBuilder>(builder => |
|||
{ |
|||
builder.AddExtensionGrantValidator<SmsTokenGrantValidator>(); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpIdentityServerSmValidatorModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<AbpIdentityServerResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/IdentityServer/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"InvalidGrant:GrantTypeInvalid": "Type of authorization not allowed!", |
|||
"InvalidGrant:PhoneVerifyInvalid": "phone verification code failed!", |
|||
"InvalidGrant:PhoneOrTokenCodeNotFound": "Phone number or verify code not found!", |
|||
"InvalidGrant:PhoneNumberNotRegister": "The registered mobile phone number is not registered!" |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"InvalidGrant:GrantTypeInvalid": "不被允许的授权类型!", |
|||
"InvalidGrant:PhoneVerifyInvalid": "手机验证码无效或已过期!", |
|||
"InvalidGrant:PhoneOrTokenCodeNotFound": "手机号码或手机验证码未输入!", |
|||
"InvalidGrant:PhoneNumberNotRegister": "登录的手机号码未注册!" |
|||
} |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
using IdentityModel; |
|||
using IdentityServer4.Events; |
|||
using IdentityServer4.Models; |
|||
using IdentityServer4.Services; |
|||
using IdentityServer4.Validation; |
|||
using LINGYUN.Abp.Account; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.IdentityServer.Localization; |
|||
using Volo.Abp.Security.Claims; |
|||
using IdentityUser = Volo.Abp.Identity.IdentityUser; |
|||
|
|||
namespace LINGYUN.Abp.IdentityServer.SmsValidator |
|||
{ |
|||
public class SmsTokenGrantValidator : IExtensionGrantValidator |
|||
{ |
|||
protected ILogger<SmsTokenGrantValidator> Logger { get; } |
|||
protected IEventService EventService { get; } |
|||
protected IIdentityUserRepository UserRepository { get; } |
|||
protected UserManager<IdentityUser> UserManager { get; } |
|||
protected SignInManager<IdentityUser> SignInManager { get; } |
|||
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; } |
|||
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; } |
|||
|
|||
|
|||
public SmsTokenGrantValidator( |
|||
IEventService eventService, |
|||
UserManager<IdentityUser> userManager, |
|||
SignInManager<IdentityUser> signInManager, |
|||
IIdentityUserRepository userRepository, |
|||
IStringLocalizer<AbpIdentityServerResource> stringLocalizer, |
|||
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider, |
|||
ILogger<SmsTokenGrantValidator> logger) |
|||
{ |
|||
Logger = logger; |
|||
EventService = eventService; |
|||
UserManager = userManager; |
|||
SignInManager = signInManager; |
|||
Localizer = stringLocalizer; |
|||
UserRepository = userRepository; |
|||
PhoneNumberTokenProvider = phoneNumberTokenProvider; |
|||
} |
|||
|
|||
public string GrantType => SmsValidatorConsts.SmsValidatorGrantTypeName; |
|||
|
|||
public async Task ValidateAsync(ExtensionGrantValidationContext context) |
|||
{ |
|||
var raw = context.Request.Raw; |
|||
var credential = raw.Get(OidcConstants.TokenRequest.GrantType); |
|||
if (credential == null || !credential.Equals(GrantType)) |
|||
{ |
|||
Logger.LogWarning("Invalid grant type: not allowed"); |
|||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
|||
Localizer["InvalidGrant:GrantTypeInvalid"]); |
|||
return; |
|||
} |
|||
var phoneNumber = raw.Get(SmsValidatorConsts.SmsValidatorParamName); |
|||
var phoneToken = raw.Get(SmsValidatorConsts.SmsValidatorTokenName); |
|||
if (phoneNumber.IsNullOrWhiteSpace() || phoneToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
Logger.LogWarning("Invalid grant type: phone number or token code not found"); |
|||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
|||
Localizer["InvalidGrant:PhoneOrTokenCodeNotFound"]); |
|||
return; |
|||
} |
|||
var currentUser = await UserRepository.FindByPhoneNumberAsync(phoneNumber); |
|||
if(currentUser == null) |
|||
{ |
|||
Logger.LogWarning("Invalid grant type: phone number not register"); |
|||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
|||
Localizer["InvalidGrant:PhoneNumberNotRegister"]); |
|||
return; |
|||
} |
|||
var validResult = await PhoneNumberTokenProvider.ValidateAsync(SmsValidatorConsts.SmsValidatorPurpose, phoneToken, UserManager, currentUser); |
|||
if (!validResult) |
|||
{ |
|||
Logger.LogWarning("Authentication failed for token: {0}, reason: invalid token", phoneToken); |
|||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
|||
Localizer["InvalidGrant:PhoneVerifyInvalid"]); |
|||
await EventService.RaiseAsync(new UserLoginFailureEvent(currentUser.UserName, $"invalid phone verify code {phoneToken}", false)); |
|||
return; |
|||
} |
|||
var sub = await UserManager.GetUserIdAsync(currentUser); |
|||
|
|||
var additionalClaims = new List<Claim>(); |
|||
if (currentUser.TenantId.HasValue) |
|||
{ |
|||
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString())); |
|||
} |
|||
|
|||
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, phoneNumber, null)); |
|||
context.Result = new GrantValidationResult(sub, OidcConstants.AuthenticationMethods.ConfirmationBySms, additionalClaims.ToArray()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace LINGYUN.Abp.IdentityServer.SmsValidator |
|||
{ |
|||
public class SmsValidatorConsts |
|||
{ |
|||
public const string SmsValidatorGrantTypeName = "phone_verify"; |
|||
|
|||
public const string SmsValidatorParamName = "phone_number"; |
|||
|
|||
public const string SmsValidatorTokenName = "phone_verify_code"; |
|||
|
|||
public const string SmsValidatorPurpose = "phone_verify"; |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.Identity.EntityFrameworkCore; |
|||
|
|||
namespace AuthServer.Host.EntityFrameworkCore.Identity |
|||
{ |
|||
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser, Guid>, LINGYUN.Abp.Account.IIdentityUserRepository, |
|||
ITransientDependency |
|||
{ |
|||
public EfCoreIdentityUserRepository( |
|||
IDbContextProvider<IdentityDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<IdentityUser> FindByPhoneNumberAsync(string phoneNumber) |
|||
{ |
|||
return await WithDetails() |
|||
.Where(usr => usr.PhoneNumber.Equals(phoneNumber)) |
|||
.FirstOrDefaultAsync(); |
|||
} |
|||
|
|||
public override IQueryable<IdentityUser> WithDetails() |
|||
{ |
|||
return DbSet |
|||
.Include(x => x.Claims) |
|||
.Include(x => x.Roles) |
|||
.Include(x => x.Logins) |
|||
.Include(x => x.Tokens); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.Identity.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Platform.EntityFrameworkCore.Identity |
|||
{ |
|||
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser, Guid>, Abp.Account.IIdentityUserRepository, |
|||
ITransientDependency |
|||
{ |
|||
public EfCoreIdentityUserRepository( |
|||
IDbContextProvider<IdentityDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<IdentityUser> FindByPhoneNumberAsync(string phoneNumber) |
|||
{ |
|||
return await DbSet.Where(usr => usr.PhoneNumber.Equals(phoneNumber)).FirstOrDefaultAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,204 @@ |
|||
<template> |
|||
<el-form |
|||
ref="formCreateUser" |
|||
label-width="110px" |
|||
:model="createUser" |
|||
:rules="createUserRules" |
|||
> |
|||
<el-tabs v-model="activedTabPane"> |
|||
<el-tab-pane |
|||
:label="$t('userProfile.basic')" |
|||
name="basic" |
|||
> |
|||
<el-form-item |
|||
prop="userName" |
|||
:label="$t('users.userName')" |
|||
> |
|||
<el-input |
|||
v-model="createUser.userName" |
|||
:placeholder="$t('userProfile.pleaseInputUserName')" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item |
|||
prop="name" |
|||
:label="$t('users.name')" |
|||
> |
|||
<el-input |
|||
v-model="createUser.name" |
|||
:placeholder="$t('userProfile.pleaseInputName')" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item |
|||
prop="surname" |
|||
:label="$t('users.surname')" |
|||
> |
|||
<el-input |
|||
v-model="createUser.surname" |
|||
:placeholder="$t('userProfile.pleaseInputSurname')" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item |
|||
prop="phoneNumber" |
|||
:label="$t('users.phoneNumber')" |
|||
> |
|||
<el-input |
|||
v-model="createUser.phoneNumber" |
|||
:placeholder="$t('userProfile.pleaseInputPhoneNumber')" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item |
|||
prop="email" |
|||
:label="$t('users.email')" |
|||
> |
|||
<el-input |
|||
v-model="createUser.email" |
|||
:placeholder="$t('userProfile.pleaseInputEmail')" |
|||
/> |
|||
</el-form-item> |
|||
</el-tab-pane> |
|||
<el-tab-pane |
|||
:label="$t('userProfile.security')" |
|||
name="security" |
|||
> |
|||
<el-form-item |
|||
prop="password" |
|||
label-width="100px" |
|||
:label="$t('userProfile.password')" |
|||
> |
|||
<el-input |
|||
v-model="createUser.password" |
|||
type="password" |
|||
:placeholder="$t('userProfile.pleaseInputPassword')" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item |
|||
prop="twoFactorEnabled" |
|||
label-width="100px" |
|||
:label="$t('users.twoFactorEnabled')" |
|||
> |
|||
<el-switch v-model="createUser.twoFactorEnabled" /> |
|||
</el-form-item> |
|||
<el-form-item |
|||
prop="lockoutEnabled" |
|||
label-width="100px" |
|||
:label="$t('users.lockoutEnabled')" |
|||
> |
|||
<el-switch v-model="createUser.lockoutEnabled" /> |
|||
</el-form-item> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
<el-form-item> |
|||
<el-button |
|||
class="cancel" |
|||
style="width:100px" |
|||
@click="onCancel" |
|||
> |
|||
{{ $t('table.cancel') }} |
|||
</el-button> |
|||
<el-button |
|||
class="confirm" |
|||
type="primary" |
|||
style="width:100px" |
|||
@click="onSubmit" |
|||
> |
|||
{{ $t('table.confirm') }} |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import { Component, Vue } from 'vue-property-decorator' |
|||
import UserApiService, { UserCreateDto } from '@/api/users' |
|||
import { checkPermission } from '@/utils/permission' |
|||
|
|||
@Component({ |
|||
name: 'UserProfile', |
|||
methods: { |
|||
checkPermission |
|||
} |
|||
}) |
|||
export default class extends Vue { |
|||
private createUser: UserCreateDto |
|||
|
|||
private activedTabPane: string |
|||
|
|||
constructor() { |
|||
super() |
|||
this.activedTabPane = 'basic' |
|||
this.createUser = new UserCreateDto() |
|||
} |
|||
|
|||
private validatePhoneNumberValue = (rule: any, value: string, callback: any) => { |
|||
const phoneReg = /^1[34578]\d{9}$/ |
|||
if (!value || !phoneReg.test(value)) { |
|||
callback(new Error(this.l('global.pleaseInputBy', { key: this.l('global.correctPhoneNumber') }))) |
|||
} else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
private createUserRules = { |
|||
userName: [ |
|||
{ required: true, message: this.l('userProfile.pleaseInputUserName'), trigger: 'blur' }, |
|||
{ min: 3, max: 20, message: this.l('global.charLengthRange', { min: 3, max: 20 }), trigger: 'blur' } |
|||
], |
|||
name: [ |
|||
{ required: true, message: this.l('userProfile.pleaseInputName'), trigger: 'blur' }, |
|||
{ min: 3, max: 50, message: this.l('global.charLengthRange', { min: 3, max: 50 }), trigger: 'blur' } |
|||
], |
|||
email: [ |
|||
{ required: true, message: this.l('userProfile.pleaseInputEmail'), trigger: 'blur' }, |
|||
{ type: 'email', message: this.l('global.pleaseInputBy', { key: this.l('global.correctEmailAddress') }), trigger: ['blur', 'change'] } |
|||
], |
|||
password: [ |
|||
{ required: true, message: this.l('userProfile.pleaseInputPassword'), trigger: 'blur' }, |
|||
{ min: 6, max: 15, message: this.l('global.charLengthRange', { min: 6, max: 15 }), trigger: 'blur' } |
|||
], |
|||
phoneNumber: [ |
|||
{ required: true, validator: this.validatePhoneNumberValue, trigger: 'blur' } |
|||
] |
|||
} |
|||
|
|||
private onSubmit() { |
|||
const frmCreateUser = this.$refs.formCreateUser as any |
|||
frmCreateUser.validate(async(valid: boolean) => { |
|||
if (valid) { |
|||
UserApiService.createUser(this.createUser).then(user => { |
|||
this.$message.success(this.l('users.createUserSuccess', { name: user.name })) |
|||
this.$emit('onUserProfileChanged', user.id) |
|||
this.resetForm() |
|||
this.onCancel() |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
private onCancel() { |
|||
this.resetForm() |
|||
this.$emit('onClose') |
|||
} |
|||
|
|||
private resetForm() { |
|||
this.activedTabPane = 'basic' |
|||
this.createUser = new UserCreateDto() |
|||
const frmCreateUser = this.$refs.formCreateUser as any |
|||
frmCreateUser.resetFields() |
|||
} |
|||
|
|||
private l(name: string, values?: any[] | { [key: string]: any }) { |
|||
return this.$t(name, values).toString() |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.confirm { |
|||
position: absolute; |
|||
right: 10px; |
|||
} |
|||
.cancel { |
|||
position: absolute; |
|||
right: 120px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,14 @@ |
|||
@echo off |
|||
cls |
|||
chcp 65001 |
|||
|
|||
echo. 启动后台管理UI |
|||
|
|||
if '%1' equ '--run' goto run |
|||
if '%1' equ '' goto run |
|||
exit |
|||
|
|||
:run |
|||
npm run serve-nomock |
|||
pause |
|||
exit |
|||
Loading…
Reference in new issue