Преглед изворни кода

新增 认证授权服务新增手机号登录
新增 验证码服务新增发送短信验证码
新增 订阅命名类SubscribeNames
修改 用户表姓名和昵称长度20调整到60
新增 用户员工表新增企业微信名片
更新 nuget依赖包,升级freesql至3.2.692

zhontai пре 2 година
родитељ
комит
1c2b5f97f7

+ 1 - 1
build/pkg.props

@@ -1,6 +1,6 @@
 <Project>
   <PropertyGroup>
-    <Version>3.4.1</Version>
+    <Version>3.5.0</Version>
 	<TargetFramework>net7.0</TargetFramework>
 	<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
 	<GenerateDocumentationFile>true</GenerateDocumentationFile>

+ 1 - 1
src/hosts/ZhonTai.Host/Configs/appconfig.json

@@ -38,7 +38,7 @@
       {
         "name": "中台Admin",
         "code": "admin",
-        "version": "v3.4.1",
+        "version": "v3.5.0",
         "description": ""
       }
     ]

+ 1 - 1
src/hosts/ZhonTai.Host/Configs/cacheconfig.json

@@ -6,7 +6,7 @@
   //Redis配置
   "redis": {
     //连接字符串
-    "connectionString": "127.0.0.1:6379,password=,defaultDatabase=2",
+    "connectionString": "127.0.0.1:6379,password=,defaultDatabase=0",
     //限流连接字符串
     "connectionStringRateLimit": "127.0.0.1:6379,password=,defaultDatabase=0"
   }

+ 4 - 4
src/hosts/ZhonTai.Host/Program.cs

@@ -33,10 +33,10 @@ new HostApp(new HostAppOptions
         {
             config.UseInMemoryStorage();
             config.UseInMemoryMessageQueue();
-            
-            //DotNetCore.CAP.MySql
-            //DotNetCore.CAP.RabbitMQ
-            
+
+            //<PackageReference Include="DotNetCore.CAP.MySql" Version="7.1.0" />
+            //<PackageReference Include="DotNetCore.CAP.RabbitMQ" Version="7.1.0" />
+
             //config.UseMySql(dbConfig.ConnectionString);
             //config.UseRabbitMQ(mqConfig => {
             //    mqConfig.HostName = rabbitMQ.HostName;

+ 6 - 6
src/hosts/ZhonTai.Host/ZhonTai.Host.csproj

@@ -16,12 +16,12 @@
 	</ItemGroup>
 
 	<ItemGroup Condition="'$(Configuration)'=='Debug'">
-		<PackageReference Include="FreeSql.Provider.MySql" Version="3.2.691" />
-		<PackageReference Include="FreeSql.Provider.SqlServer" Version="3.2.691" />
-		<PackageReference Include="FreeSql.Provider.PostgreSQL" Version="3.2.691" />
-		<PackageReference Include="FreeSql.Provider.Oracle" Version="3.2.691" />
-		<PackageReference Include="FreeSql.Provider.Sqlite" Version="3.2.691" />
-		<PackageReference Include="FreeSql.Provider.MySqlConnector" Version="3.2.691" />
+		<PackageReference Include="FreeSql.Provider.MySql" Version="3.2.692" />
+		<PackageReference Include="FreeSql.Provider.SqlServer" Version="3.2.692" />
+		<PackageReference Include="FreeSql.Provider.PostgreSQL" Version="3.2.692" />
+		<PackageReference Include="FreeSql.Provider.Oracle" Version="3.2.692" />
+		<PackageReference Include="FreeSql.Provider.Sqlite" Version="3.2.692" />
+		<PackageReference Include="FreeSql.Provider.MySqlConnector" Version="3.2.692" />
 	</ItemGroup>
 	
 	<ItemGroup>

+ 6 - 0
src/platform/ZhonTai.Admin/Core/Consts/CacheKeys.cs

@@ -32,4 +32,10 @@ public static partial class CacheKeys
     /// </summary>
     [Description("数据权限")]
     public const string DataPermission = "admin:user:data:permission:";
+
+    /// <summary>
+    /// 短信验证码 admin:sms:code:guid
+    /// </summary>
+    [Description("短信验证码")]
+    public const string SmsCode = "admin:sms:code:";
 }

+ 15 - 0
src/platform/ZhonTai.Admin/Core/Consts/SubscribeNames.cs

@@ -0,0 +1,15 @@
+using System.ComponentModel;
+
+namespace ZhonTai.Admin.Core.Consts;
+
+/// <summary>
+/// 订阅命名
+/// </summary>
+public class SubscribeNames
+{
+    /// <summary>
+    /// 短信单发
+    /// </summary>
+    [Description("短信单发")]
+    public static string SmsSingleSend { get; set; } = "zhontai.admin.smsSingleSend";
+}

+ 2 - 2
src/platform/ZhonTai.Admin/Domain/User/UserEntity.cs

@@ -41,7 +41,7 @@ public partial class UserEntity : EntityTenant
     /// <summary>
     /// 姓名
     /// </summary>
-    [Column(StringLength = 20)]
+    [Column(StringLength = 60)]
     public string Name { get; set; }
 
     /// <summary>
@@ -79,7 +79,7 @@ public partial class UserEntity : EntityTenant
     /// <summary>
     /// 昵称
     /// </summary>
-    [Column(StringLength = 20)]
+    [Column(StringLength = 60)]
     public string NickName { get; set; }
 
     /// <summary>

+ 6 - 0
src/platform/ZhonTai.Admin/Domain/UserStaff/UserStaffEntity.cs

@@ -32,6 +32,12 @@ public partial class UserStaffEntity : EntityTenant
     /// </summary>
     public DateTime? EntryTime { get; set; }
 
+    /// <summary>
+    /// 企业微信名片
+    /// </summary>
+    [Column(StringLength = 500)]
+    public string WorkWeChatCard { get; set; }
+
     /// <summary>
     /// 个人简介
     /// </summary>

+ 83 - 1
src/platform/ZhonTai.Admin/Services/Auth/AuthService.cs

@@ -413,7 +413,89 @@ public class AuthService : BaseService, IAuthService, IDynamicApi
                 ElapsedMilliseconds = sw.ElapsedMilliseconds,
                 Status = true,
                 CreatedUserId = authLoginOutput.Id,
-                CreatedUserName = input.UserName,
+                CreatedUserName = user.UserName,
+            };
+
+            await LazyGetRequiredService<ILoginLogService>().AddAsync(loginLogAddInput);
+
+            #endregion 添加登录日志
+
+            return new { token };
+        }
+    }
+
+    /// <summary>
+    /// 手机号登录
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [AllowAnonymous]
+    [NoOprationLog]
+    public async Task<dynamic> MobileLoginAsync(AuthMobileLoginInput input)
+    {
+        using (_userRepository.DataFilter.DisableAll())
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+
+            #region 短信验证码验证
+            var codeKey = CacheKeys.SmsCode + input.CodeId;
+            if (await Cache.ExistsAsync(codeKey))
+            {
+                var code = await Cache.GetAsync(codeKey);
+                if (code != input.Code)
+                {
+                    throw ResultOutput.Exception("验证码输入有误,请重新输入");
+                }
+                await Cache.DelAsync(codeKey);
+            }
+            else
+            {
+                throw ResultOutput.Exception("验证码已过期,请重新发送");
+            }
+
+            #endregion
+
+            #region 登录
+            var user = await _userRepository.Select.Where(a => a.Mobile == input.Mobile).ToOneAsync();
+            if (!(user?.Id > 0))
+            {
+                throw ResultOutput.Exception("账号不存在");
+            }
+
+            if (!user.Enabled)
+            {
+                throw ResultOutput.Exception("账号已停用,禁止登录");
+            }
+            #endregion
+
+            #region 获得token
+            var authLoginOutput = Mapper.Map<AuthLoginOutput>(user);
+            if (_appConfig.Tenant)
+            {
+                var tenant = await _tenantRepository.Select.WhereDynamic(user.TenantId).ToOneAsync<AuthLoginTenantDto>();
+                if (!(tenant != null && tenant.Enabled))
+                {
+                    throw ResultOutput.Exception("企业已停用,禁止登录");
+                }
+                authLoginOutput.Tenant = tenant;
+            }
+            string token = GetToken(authLoginOutput);
+            #endregion
+
+            sw.Stop();
+
+            #region 添加登录日志
+
+            var loginLogAddInput = new LoginLogAddInput
+            {
+                TenantId = authLoginOutput.TenantId,
+                Name = authLoginOutput.Name,
+                ElapsedMilliseconds = sw.ElapsedMilliseconds,
+                Status = true,
+                CreatedUserId = authLoginOutput.Id,
+                CreatedUserName = user.UserName,
             };
 
             await LazyGetRequiredService<ILoginLogService>().AddAsync(loginLogAddInput);

+ 2 - 2
src/platform/ZhonTai.Admin/Services/Auth/Dto/AuthLoginInput.cs

@@ -10,13 +10,13 @@ public class AuthLoginInput
     /// <summary>
     /// 账号
     /// </summary>
-    [Required(ErrorMessage = "用户名不能为空")]
+    [Required(ErrorMessage = "用户名不能为空")]
     public string UserName { get; set; }
 
     /// <summary>
     /// 密码
     /// </summary>
-    [Required(ErrorMessage = "密码不能为空")]
+    [Required(ErrorMessage = "密码不能为空")]
     public string Password { get; set; }
 
     /// <summary>

+ 27 - 0
src/platform/ZhonTai.Admin/Services/Auth/Dto/AuthMobileLoginInput.cs

@@ -0,0 +1,27 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace ZhonTai.Admin.Services.Auth.Dto;
+
+/// <summary>
+/// 手机号登录信息
+/// </summary>
+public class AuthMobileLoginInput
+{
+    /// <summary>
+    /// 手机号
+    /// </summary>
+    [Required(ErrorMessage = "手机号不能为空")]
+    public string Mobile { get; set; }
+
+    /// <summary>
+    /// 验证码
+    /// </summary>
+    [Required(ErrorMessage = "验证码不能为空")]
+    public string Code { get; set; }
+
+    /// <summary>
+    /// 验证码Id
+    /// </summary>
+    [Required(ErrorMessage = "验证码Id不能为空")]
+    public string CodeId { get; set; }
+}

+ 0 - 1
src/platform/ZhonTai.Admin/Services/Auth/IAuthService.cs

@@ -1,7 +1,6 @@
 using Microsoft.AspNetCore.Mvc.ModelBinding;
 using System.Threading.Tasks;
 using ZhonTai.Admin.Services.Auth.Dto;
-using ZhonTai.Admin.Tools.Captcha;
 
 namespace ZhonTai.Admin.Services.Auth;
 

+ 52 - 0
src/platform/ZhonTai.Admin/Services/Captcha/CaptchaService.cs

@@ -7,6 +7,13 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Authorization;
 using ZhonTai.Admin.Core.Attributes;
 using ZhonTai.Admin.Core.Captcha;
+using ZhonTai.Admin.Core.Dto;
+using static Lazy.SlideCaptcha.Core.ValidateResult;
+using System.Threading.Tasks;
+using System;
+using ZhonTai.Common.Helpers;
+using DotNetCore.CAP;
+using ZhonTai.Admin.Services.Captcha.Dto;
 
 namespace ZhonTai.Admin.Services.Cache;
 
@@ -45,6 +52,51 @@ public class CaptchaService : BaseService, IDynamicApi
     [NoOprationLog]
     public ValidateResult CheckAsync([FromQuery] string captchaId, SlideTrack track)
     {
+        if (captchaId.IsNull() || track == null)
+        {
+            throw ResultOutput.Exception("请完成安全验证");
+        }
+
         return _slideCaptcha.Validate(captchaId, track, false);
     }
+
+    /// <summary>
+    /// 发送短信验证码
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [AllowAnonymous]
+    [NoOprationLog]
+    public async Task<string> SendSmsCodeAsync(SendSmsCodeInput input)
+    {
+        if (input.Mobile.IsNull())
+        {
+            throw ResultOutput.Exception("请输入手机号");
+        }
+
+        if (input.CaptchaId.IsNull() || input.Track == null)
+        {
+            throw ResultOutput.Exception("请完成安全验证");
+        }
+
+        var validateResult = _captcha.Validate(input.CaptchaId, input.Track);
+        if (validateResult.Result != ValidateResultType.Success)
+        {
+            throw ResultOutput.Exception($"安全{validateResult.Message}");
+        }
+
+        var codeId = input.CodeId.IsNull() ? Guid.NewGuid().ToString() : input.CodeId;
+        var code = StringHelper.GenerateRandomNumber();
+        await Cache.SetAsync(CacheKeys.SmsCode + codeId, code, TimeSpan.FromMinutes(5));
+
+        //发送短信
+        await LazyGetRequiredService<ICapPublisher>().PublishAsync(SubscribeNames.SmsSingleSend,
+        new
+        {
+            input.Mobile,
+            Text = code
+        });
+
+        return codeId;
+    }
 }

+ 33 - 0
src/platform/ZhonTai.Admin/Services/Captcha/Dto/SendSmsCodeInput.cs

@@ -0,0 +1,33 @@
+using Lazy.SlideCaptcha.Core.Validator;
+using System.ComponentModel.DataAnnotations;
+
+namespace ZhonTai.Admin.Services.Captcha.Dto;
+
+/// <summary>
+/// 发送短信验证码
+/// </summary>
+public class SendSmsCodeInput
+{
+    /// <summary>
+    /// 手机号
+    /// </summary>
+    [Required(ErrorMessage = "请输入手机号")]
+    public string Mobile { get; set; }
+
+    /// <summary>
+    /// 验证码Id
+    /// </summary>
+    public string? CodeId { get; set; }
+
+    /// <summary>
+    /// 验证码Id
+    /// </summary>
+    [Required(ErrorMessage = "请完成安全验证")]
+    public string CaptchaId { get; set; }
+
+    /// <summary>
+    /// 滑动轨迹
+    /// </summary>
+    [Required(ErrorMessage = "请完成安全验证")]
+    public SlideTrack Track { get; set; }
+}

+ 5 - 0
src/platform/ZhonTai.Admin/Services/User/Dto/StaffAddInput.cs

@@ -28,6 +28,11 @@ public class StaffAddInput
     /// </summary>
     public DateTime? EntryTime { get; set; }
 
+    /// <summary>
+    /// 企业微信名片
+    /// </summary>
+    public string WorkWeChatCard { get; set; }
+
     /// <summary>
     /// 个人简介
     /// </summary>

+ 6 - 6
src/platform/ZhonTai.Admin/ZhonTai.Admin.csproj

@@ -16,17 +16,17 @@
 		<PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
 		<PackageReference Include="DotNetCore.CAP" Version="7.1.0" />
 		<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
-		<PackageReference Include="FreeRedis" Version="1.0.7" />
+		<PackageReference Include="FreeRedis" Version="1.0.8" />
 		<PackageReference Include="FreeRedis.DistributedCache" Version="1.0.5" />
-		<PackageReference Include="FreeSql" Version="3.2.691" />
-		<PackageReference Include="FreeSql.Cloud" Version="1.6.3" />
-		<PackageReference Include="FreeSql.Repository" Version="3.2.691" />
-		<PackageReference Include="FreeScheduler" Version="1.0.9" />
+		<PackageReference Include="FreeSql" Version="3.2.692" />
+		<PackageReference Include="FreeSql.Cloud" Version="1.6.4" />
+		<PackageReference Include="FreeSql.Repository" Version="3.2.692" />
+		<PackageReference Include="FreeScheduler" Version="1.1.0" />
 		<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
 		<PackageReference Include="Lazy.SlideCaptcha.Core" Version="2.0.0" />
 		<PackageReference Include="Mapster" Version="7.3.0" />
 		<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
-		<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.3" />
+		<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.4" />
 		<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.2.22" />
 		<PackageReference Include="NLog" Version="5.1.2" />
 		<PackageReference Include="NLog.Web.AspNetCore" Version="5.2.2" />

+ 84 - 0
src/platform/ZhonTai.Admin/ZhonTai.Admin.xml

@@ -990,6 +990,11 @@
             数据权限 admin:user:data:permission:用户主键
             </summary>
         </member>
+        <member name="F:ZhonTai.Admin.Core.Consts.CacheKeys.SmsCode">
+            <summary>
+            短信验证码 admin:sms:code:guid
+            </summary>
+        </member>
         <member name="T:ZhonTai.Admin.Core.Consts.DbKeys">
             <summary>
             数据库键名
@@ -1030,6 +1035,16 @@
             会员
             </summary>
         </member>
+        <member name="T:ZhonTai.Admin.Core.Consts.SubscribeNames">
+            <summary>
+            订阅命名
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Core.Consts.SubscribeNames.SmsSingleSend">
+            <summary>
+            短信单发
+            </summary>
+        </member>
         <member name="T:ZhonTai.Admin.Core.Conventions.ApiGroupConvention">
             <summary>
             Api分组约定
@@ -3180,6 +3195,11 @@
             入职时间
             </summary>
         </member>
+        <member name="P:ZhonTai.Admin.Domain.UserStaff.UserStaffEntity.WorkWeChatCard">
+            <summary>
+            企业微信名片
+            </summary>
+        </member>
         <member name="P:ZhonTai.Admin.Domain.UserStaff.UserStaffEntity.Introduce">
             <summary>
             个人简介
@@ -3375,6 +3395,11 @@
             入职时间
             </summary>
         </member>
+        <member name="P:ZhonTai.Admin.Domain.User.StaffAddInput.WorkWeChatCard">
+            <summary>
+            企业微信名片
+            </summary>
+        </member>
         <member name="P:ZhonTai.Admin.Domain.User.StaffAddInput.Introduce">
             <summary>
             个人简介
@@ -3740,6 +3765,13 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="M:ZhonTai.Admin.Services.Auth.AuthService.MobileLoginAsync(ZhonTai.Admin.Services.Auth.Dto.AuthMobileLoginInput)">
+            <summary>
+            手机号登录
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
         <member name="M:ZhonTai.Admin.Services.Auth.AuthService.Refresh(System.String)">
             <summary>
             刷新Token
@@ -3889,6 +3921,26 @@
             启用
             </summary>
         </member>
+        <member name="T:ZhonTai.Admin.Services.Auth.Dto.AuthMobileLoginInput">
+            <summary>
+            手机号登录信息
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Auth.Dto.AuthMobileLoginInput.Mobile">
+            <summary>
+            手机号
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Auth.Dto.AuthMobileLoginInput.Code">
+            <summary>
+            验证码
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Auth.Dto.AuthMobileLoginInput.CodeId">
+            <summary>
+            验证码Id
+            </summary>
+        </member>
         <member name="P:ZhonTai.Admin.Services.Auth.Dto.AuthUserMenuDto.Id">
             <summary>
             权限Id
@@ -4099,6 +4151,38 @@
             <param name="track">滑动轨迹</param>
             <returns></returns>
         </member>
+        <member name="M:ZhonTai.Admin.Services.Cache.CaptchaService.SendSmsCodeAsync(ZhonTai.Admin.Services.Captcha.Dto.SendSmsCodeInput)">
+            <summary>
+            发送短信验证码
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="T:ZhonTai.Admin.Services.Captcha.Dto.SendSmsCodeInput">
+            <summary>
+            发送短信验证码
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Captcha.Dto.SendSmsCodeInput.Mobile">
+            <summary>
+            手机号
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Captcha.Dto.SendSmsCodeInput.CodeId">
+            <summary>
+            验证码Id
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Captcha.Dto.SendSmsCodeInput.CaptchaId">
+            <summary>
+            验证码Id
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Services.Captcha.Dto.SendSmsCodeInput.Track">
+            <summary>
+            滑动轨迹
+            </summary>
+        </member>
         <member name="T:ZhonTai.Admin.Services.DictionaryType.DictionaryTypeService">
             <summary>
             数据字典类型服务

+ 5 - 8
src/platform/ZhonTai.Common/Helpers/StringHelper.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
 
@@ -9,6 +10,7 @@ namespace ZhonTai.Common.Helpers;
 /// </summary>
 public class StringHelper
 {
+    private static readonly string _chars = "0123456789";
     private static readonly char[] _constant = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
 
     /// <summary>
@@ -28,19 +30,14 @@ public class StringHelper
     }
 
     /// <summary>
-    /// 生成随机字符串,只包含数字
+    /// 生成随机6位数
     /// </summary>
     /// <param name="length"></param>
     /// <returns></returns>
     public static string GenerateRandomNumber(int length = 6)
     {
-        var newRandom = new StringBuilder();
-        var rd = new Random();
-        for (int i = 0; i < length; i++)
-        {
-            newRandom.Append(_constant[rd.Next(10)]);
-        }
-        return newRandom.ToString();
+        var random = new Random();
+        return new string(Enumerable.Repeat(_chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
     }
 
     public static string Format(string str, object obj)

+ 1 - 1
src/platform/ZhonTai.Common/ZhonTai.Common.xml

@@ -551,7 +551,7 @@
         </member>
         <member name="M:ZhonTai.Common.Helpers.StringHelper.GenerateRandomNumber(System.Int32)">
             <summary>
-            生成随机字符串,只包含数字
+            生成随机6位数
             </summary>
             <param name="length"></param>
             <returns></returns>