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

feat: 将登录验证码/二次验证交由各租户隔离管理

喵你个旺呀 пре 1 година
родитељ
комит
a2cf1db368

+ 0 - 10
Admin.NET/Admin.NET.Core/Const/ConfigConst.cs

@@ -41,16 +41,6 @@ public class ConfigConst
     /// </summary>
     public const string SysSingleLogin = "sys_single_login";
 
-    /// <summary>
-    /// 登录二次验证
-    /// </summary>
-    public const string SysSecondVer = "sys_second_ver";
-
-    /// <summary>
-    /// 图形验证码
-    /// </summary>
-    public const string SysCaptcha = "sys_captcha";
-
     /// <summary>
     /// Token过期时间
     /// </summary>

+ 12 - 0
Admin.NET/Admin.NET.Core/Entity/SysTenant.cs

@@ -85,6 +85,18 @@ public partial class SysTenant : EntityBase
     [SugarColumn(ColumnDescription = "默认注册方案")]
     public virtual long? RegWayId { get; set; }
 
+    /// <summary>
+    /// 启用验证码
+    /// </summary>
+    [SugarColumn(ColumnDescription = "启用验证码")]
+    public virtual YesNoEnum? Captcha { get; set; } = YesNoEnum.Y;
+
+    /// <summary>
+    /// 启用二次验证
+    /// </summary>
+    [SugarColumn(ColumnDescription = "启用二次验证")]
+    public virtual YesNoEnum? SecondVer { get; set; } = YesNoEnum.N;
+
     /// <summary>
     /// 图标
     /// </summary>

+ 0 - 2
Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs

@@ -25,8 +25,6 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
             new SysConfig{ Id=1300000000131, Name="日志保留天数", Code=ConfigConst.SysLogRetentionDays, Value="180", SysFlag=YesNoEnum.Y, Remark="日志保留天数(天)", OrderNo=40, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000141, Name="记录操作日志", Code=ConfigConst.SysOpLog, Value="True", SysFlag=YesNoEnum.Y, Remark="是否记录操作日志", OrderNo=50, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000151, Name="单设备登录", Code=ConfigConst.SysSingleLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启单设备登录", OrderNo=60, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000161, Name="登录二次验证", Code=ConfigConst.SysSecondVer, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启登录二次验证", OrderNo=70, GroupCode=ConfigConst.SysWebConfigGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysConfig{ Id=1300000000171, Name="图形验证码", Code=ConfigConst.SysCaptcha, Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启图形验证码", OrderNo=80, GroupCode=ConfigConst.SysWebConfigGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000181, Name="Token过期时间", Code=ConfigConst.SysTokenExpire, Value="10080", SysFlag=YesNoEnum.Y, Remark="Token过期时间(分钟)", OrderNo=90, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },

+ 2 - 2
Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginInput.cs

@@ -29,7 +29,7 @@ public class LoginInput
     /// 租户
     /// </summary>
     [Required(ErrorMessage = "租户不能为空")]
-    public long TenantId { get; set; }
+    public long? TenantId { get; set; }
 
     /// <summary>
     /// 验证码Id
@@ -63,7 +63,7 @@ public class LoginPhoneInput
     /// 租户
     /// </summary>
     [Required(ErrorMessage = "租户不能为空")]
-    public long TenantId { get; set; }
+    public long? TenantId { get; set; }
 }
 
 /// <summary>

+ 32 - 15
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -54,9 +54,6 @@ public class SysAuthService : IDynamicApiController, ITransient
     [DisplayName("账号密码登录")]
     public virtual async Task<LoginOutput> Login([Required] LoginInput input)
     {
-        //// 可以根据域名获取具体租户
-        //var host = _httpContextAccessor.HttpContext.Request.Host;
-
         // 判断密码错误次数(缓存30分钟)
         var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{input.Account}";
         var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
@@ -65,11 +62,8 @@ public class SysAuthService : IDynamicApiController, ITransient
         if (passwordMaxErrorTimes < 1) passwordMaxErrorTimes = 10;
         if (passwordErrorTimes > passwordMaxErrorTimes) throw Oops.Oh(ErrorCodeEnum.D1027);
 
-        // 判断是否开启验证码,其校验验证码
-        if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysCaptcha) && !_captcha.Validate(input.CodeId.ToString(), input.Code)) throw Oops.Oh(ErrorCodeEnum.D0008);
-
         // 获取登录租户和用户
-        var (tenant, user) = await GetLoginUserAndTenant(input.TenantId, account: input.Account);
+        var (tenant, user) = await GetLoginUserAndTenant(input.TenantId, codeId: input.CodeId, code: input.Code, account: input.Account);
 
         // 账号是否被冻结
         if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017);
@@ -101,14 +95,22 @@ public class SysAuthService : IDynamicApiController, ITransient
     /// 获取登录租户和用户
     /// </summary>
     /// <param name="tenantId"></param>
+    /// <param name="codeId"></param>
+    /// <param name="code"></param>
     /// <param name="account"></param>
     /// <param name="phone"></param>
     /// <returns></returns>
     [NonAction]
-    public async Task<(SysTenant tenant, SysUser user)> GetLoginUserAndTenant(long tenantId, string account = null, string phone = null)
+    public async Task<(SysTenant tenant, SysUser user)> GetLoginUserAndTenant(long? tenantId, long codeId = 0, string code = null, string account = null, string phone = null)
     {
-        // 租户是否存在或已禁用
+        // 如果租户为空,使用默认租户
+        tenantId ??= SqlSugarConst.DefaultTenantId;
         var tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == tenantId);
+
+        // 校验验证码
+        if (tenant?.Captcha == YesNoEnum.Y && !_captcha.Validate(codeId.ToString(), code)) throw Oops.Oh(ErrorCodeEnum.D0008);
+
+        // 租户是否存在或已禁用
         if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
         
         // 判断账号是否存在
@@ -403,26 +405,41 @@ public class SysAuthService : IDynamicApiController, ITransient
     [ApiDescriptionSettings(Description = "Swagger登录提交", DisableInherite = true)]
     public async Task<int> SwaggerSubmitUrl([FromForm] SpecificationAuth auth)
     {
+        // 尝试从发起请求页的地址栏中获取租户id,为空则使用默认租户
+        var tenantIdStr = Regex.Match(App.HttpContext.Request.Headers.Referer.ToString() ?? "", @"(?<=t=)(\d+)").Value;
+        var tenantId = string.IsNullOrWhiteSpace(tenantIdStr)
+            ? SqlSugarConst.DefaultTenantId
+            : long.Parse(tenantIdStr);
         try
         {
-            _sysCacheService.Set($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}", false);
+            // 关闭验证码验证
+            await _sysUserRep.Context
+                .Updateable(new SysTenant { Captcha = YesNoEnum.N })
+                .UpdateColumns(u => u.Captcha)
+                .Where(u => u.Id == tenantId)
+                .ExecuteCommandAsync();
 
-            // 尝试从发起请求页的地址栏中获取租户id
-            var tenantId = Regex.Match(App.HttpContext.Request.Headers.Referer.ToString() ?? "", @"(?<=t=)(\d+)").Value;
             await Login(new LoginInput
             {
                 Account = auth.UserName,
                 Password = CryptogramUtil.SM2Encrypt(auth.Password),
-                TenantId = string.IsNullOrWhiteSpace(tenantId) ? SqlSugarConst.DefaultTenantId : long.Parse(tenantId)
+                TenantId = tenantId
             });
 
-            _sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}");
-
             return 200;
         }
         catch (Exception)
         {
             return 401;
         }
+        finally
+        {
+            // 开启验证码验证
+            await _sysUserRep.Context
+                .Updateable(new SysTenant { Captcha = YesNoEnum.N })
+                .UpdateColumns(u => u.Captcha)
+                .Where(u => u.Id == tenantId)
+                .ExecuteCommandAsync();
+        }
     }
 }

+ 13 - 13
Admin.NET/Admin.NET.Core/Service/Config/Dto/InfoInput.cs

@@ -14,72 +14,72 @@ public class InfoSaveInput
     /// <summary>
     /// 系统图标(Data URI scheme base64 编码)
     /// </summary>
-    public string SysLogoBase64 { get; set; }
+    public string LogoBase64 { get; set; }
 
     /// <summary>
     /// 系统图标文件名
     /// </summary>
-    public string SysLogoFileName { get; set; }
+    public string LogoFileName { get; set; }
 
     /// <summary>
     /// 系统主标题
     /// </summary>
     [Required(ErrorMessage = "系统主标题不能为空")]
-    public string SysTitle { get; set; }
+    public string Title { get; set; }
 
     /// <summary>
     /// 系统副标题
     /// </summary>
     [Required(ErrorMessage = "系统副标题不能为空")]
-    public string SysViceTitle { get; set; }
+    public string ViceTitle { get; set; }
 
     /// <summary>
     /// 系统描述
     /// </summary>
     [Required(ErrorMessage = "系统描述不能为空")]
-    public string SysViceDesc { get; set; }
+    public string ViceDesc { get; set; }
 
     /// <summary>
     /// 水印内容
     /// </summary>
     [Required(ErrorMessage = "水印内容不能为空")]
-    public string SysWatermark { get; set; }
+    public string Watermark { get; set; }
 
     /// <summary>
     /// 版权说明
     /// </summary>
     [Required(ErrorMessage = "版权说明不能为空")]
-    public string SysCopyright { get; set; }
+    public string Copyright { get; set; }
 
     /// <summary>
     /// ICP备案号
     /// </summary>
     [Required(ErrorMessage = "ICP备案号不能为空")]
-    public string SysIcp { get; set; }
+    public string Icp { get; set; }
 
     /// <summary>
     /// ICP地址
     /// </summary>
     [Required(ErrorMessage = "ICP地址不能为空")]
-    public string SysIcpUrl { get; set; }
+    public string IcpUrl { get; set; }
     
     /// <summary>
     /// 启用注册功能
     /// </summary>
-    public bool SysRegistration { get; set; }
+    public YesNoEnum EnableReg { get; set; }
 
     /// <summary>
     /// 登录二次验证
     /// </summary>
-    public bool SysSecondVer { get; set; }
+    public YesNoEnum SecondVer { get; set; }
 
     /// <summary>
     /// 图形验证码
     /// </summary>
-    public bool SysCaptcha { get; set; }
+    public YesNoEnum Captcha { get; set; }
     
     /// <summary>
     /// 默认注册方案Id
     /// </summary>
-    public virtual long SysRegWayId { get; set; }
+    public virtual long RegWayId { get; set; }
 }

+ 21 - 30
Admin.NET/Admin.NET.Core/Service/Config/SysConfigService.cs

@@ -5,6 +5,7 @@
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
 using Microsoft.AspNetCore.Mvc.Rendering;
+using NewLife.Reflection;
 
 namespace Admin.NET.Core.Service;
 
@@ -245,26 +246,26 @@ public class SysConfigService : IDynamicApiController, ITransient
         var tenant = await App.GetService<SysTenantService>().GetCurrentTenant();
         tenant ??= await _sysConfigRep.Context.Queryable<SysTenant>().FirstAsync(u => u.Id == SqlSugarConst.DefaultTenantId);
         _ = tenant ?? throw Oops.Oh(ErrorCodeEnum.D1002);
-        
-        var sysSecondVer = await GetConfigValue<bool>(ConfigConst.SysSecondVer);
-        var sysCaptcha = await GetConfigValue<bool>(ConfigConst.SysCaptcha);
 
-        var wayList = await _sysConfigRep.Context.Queryable<SysUserRegWay>().Select(u => new { Label = u.Name, Value = u.Id }).ToListAsync();
+        var wayList = await _sysConfigRep.Context.Queryable<SysUserRegWay>().ClearFilter()
+            .Where(u => u.TenantId == tenant.Id)
+            .Select(u => new { Label = u.Name, Value = u.Id })
+            .ToListAsync();
         return new
         {
-            SysLogo = tenant.Logo,
-            SysTitle = tenant.Title,
-            SysViceTitle = tenant.ViceTitle,
-            SysViceDesc = tenant.ViceDesc,
-            SysWatermark = tenant.Watermark,
-            SysCopyright = tenant.Copyright,
-            SysIcp = tenant.Icp,
-            SysIcpUrl = tenant.IcpUrl,
-            SysRegWayId = tenant.RegWayId,
-            SysRegistration = tenant.EnableReg == YesNoEnum.Y,
-            SysSecondVer = sysSecondVer,
-            SysCaptcha = sysCaptcha,
-            SysUserRegWayList = wayList
+            tenant.Logo,
+            tenant.Title,
+            tenant.ViceTitle,
+            tenant.ViceDesc,
+            tenant.Watermark,
+            tenant.Copyright,
+            tenant.Icp,
+            tenant.IcpUrl,
+            tenant.RegWayId,
+            tenant.EnableReg,
+            tenant.SecondVer,
+            tenant.Captcha,
+            WayList = wayList
         };
     }
 
@@ -277,21 +278,11 @@ public class SysConfigService : IDynamicApiController, ITransient
     public async Task SaveSysInfo(InfoSaveInput input)
     {
         var tenant = await App.GetService<SysTenantService>().GetCurrentTenant() ?? throw Oops.Oh(ErrorCodeEnum.D1002);
-        if (!string.IsNullOrEmpty(input.SysLogoBase64)) App.GetService<SysTenantService>().SetLogoUrl(tenant, input.SysLogoBase64, input.SysLogoFileName);
-
-        tenant.Title = input.SysTitle;
-        tenant.ViceTitle = input.SysViceTitle;
-        tenant.ViceDesc = input.SysViceDesc;
-        tenant.Watermark = input.SysWatermark;
-        tenant.Copyright = input.SysCopyright;
-        tenant.IcpUrl = input.SysIcpUrl;
-        tenant.Icp = input.SysIcp;
-        tenant.RegWayId = input.SysRegistration ? input.SysRegWayId : null;
-        tenant.EnableReg = input.SysRegistration ? YesNoEnum.Y : YesNoEnum.N;
+        if (!string.IsNullOrEmpty(input.LogoBase64)) App.GetService<SysTenantService>().SetLogoUrl(tenant, input.LogoBase64, input.LogoFileName);
 
+        tenant.Copy(input);
+        tenant.RegWayId = input.EnableReg == YesNoEnum.Y ? input.RegWayId : null;
         await _sysConfigRep.Context.Updateable(tenant).ExecuteCommandAsync();
-        await UpdateConfigValue(ConfigConst.SysSecondVer, input.SysSecondVer.ToString());
-        await UpdateConfigValue(ConfigConst.SysCaptcha, input.SysCaptcha.ToString());
     }
 
     private void Remove(SysConfig config)

+ 2 - 1
Admin.NET/Admin.NET.Core/Service/Tenant/SysTenantService.cs

@@ -122,7 +122,7 @@ public class SysTenantService : IDynamicApiController, ITransient
         var host = App.HttpContext.Request.Host.Host.ToLower();
         return await _sysTenantRep.AsQueryable()
             .WhereIF(tenantId > 0, u => u.Id == tenantId)
-            .WhereIF(tenantId <= 0, u => SqlFunc.ToLower(u.Host).Equals(host))
+            .WhereIF(!(tenantId > 0), u => SqlFunc.ToLower(u.Host).Equals(host))
             .FirstAsync();
     }
 
@@ -557,6 +557,7 @@ public class SysTenantService : IDynamicApiController, ITransient
         var singleLogin = _sysCacheService.Get<bool>($"{CacheConst.KeyConfig}{ConfigConst.SysSingleLogin}");
         try
         {
+            _sysCacheService.Set($"{CacheConst.KeyConfig}{ConfigConst.SysSingleLogin}", false);
             return await App.GetService<SysAuthService>().CreateToken(user);
         }
         finally

+ 6 - 0
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewSys/Dto/GoViewLoginInput.cs

@@ -22,4 +22,10 @@ public class GoViewLoginInput
     /// </summary>
     [Required(ErrorMessage = "密码不能为空")]
     public string Password { get; set; }
+
+    /// <summary>
+    /// 租户
+    /// </summary>
+    [Required(ErrorMessage = "租户不能为空")]
+    public long? TenantId { get; set; }
 }

+ 6 - 2
Admin.NET/Plugins/Admin.NET.Plugin.GoView/Service/GoViewSys/GoViewSysService.cs

@@ -30,11 +30,14 @@ public class GoViewSysService : IDynamicApiController
     /// GoView 登录 🔖
     /// </summary>
     /// <returns></returns>
+    [UnitOfWork]
     [AllowAnonymous]
     [DisplayName("GoView 登录")]
     public async Task<GoViewLoginOutput> Login(GoViewLoginInput input)
     {
-        _sysCacheService.Set($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}", false);
+        // 关闭验证码验证
+        var tenant = await _sysUserRep.Context.Queryable<SysTenant>().FirstAsync(u => u.Id == input.TenantId) ?? throw Oops.Oh(ErrorCodeEnum.Z1003);
+        if (tenant.Captcha == YesNoEnum.Y) await _sysUserRep.Context.Updateable(new SysTenant{ Captcha = YesNoEnum.N }).UpdateColumns(u => u.Captcha).ExecuteCommandAsync();
 
         input.Password = CryptogramUtil.SM2Encrypt(input.Password);
         var loginResult = await _sysAuthService.Login(new LoginInput()
@@ -43,7 +46,8 @@ public class GoViewSysService : IDynamicApiController
             Password = input.Password,
         });
 
-        _sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}");
+        // 启用验证码
+        if (tenant.Captcha == YesNoEnum.Y) await _sysUserRep.Context.Updateable(new SysTenant{ Captcha = YesNoEnum.Y }).UpdateColumns(u => u.Captcha).ExecuteCommandAsync();
 
         var sysUser = await _sysUserRep.AsQueryable().ClearFilter().FirstAsync(u => u.Account.Equals(input.Username));
         return new GoViewLoginOutput()

+ 12 - 0
Web/src/api-services/models/add-tenant-input.ts

@@ -129,6 +129,18 @@ export interface AddTenantInput {
      * @memberof AddTenantInput
      */
     regWayId?: number | null;
+    /**
+     * 
+     * @type {YesNoEnum}
+     * @memberof AddTenantInput
+     */
+    captcha?: YesNoEnum;
+    /**
+     * 
+     * @type {YesNoEnum}
+     * @memberof AddTenantInput
+     */
+    secondVer?: YesNoEnum;
     /**
      * 图标
      * @type {string}

+ 20 - 19
Web/src/api-services/models/info-save-input.ts

@@ -11,6 +11,7 @@
  * https://github.com/swagger-api/swagger-codegen.git
  * Do not edit the class manually.
  */
+import { YesNoEnum } from './yes-no-enum';
 /**
  * 系统信息保存输入参数
  * @export
@@ -22,77 +23,77 @@ export interface InfoSaveInput {
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysLogoBase64?: string | null;
+    logoBase64?: string | null;
     /**
      * 系统图标文件名
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysLogoFileName?: string | null;
+    logoFileName?: string | null;
     /**
      * 系统主标题
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysTitle: string;
+    title: string;
     /**
      * 系统副标题
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysViceTitle: string;
+    viceTitle: string;
     /**
      * 系统描述
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysViceDesc: string;
+    viceDesc: string;
     /**
      * 水印内容
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysWatermark: string;
+    watermark: string;
     /**
      * 版权说明
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysCopyright: string;
+    copyright: string;
     /**
      * ICP备案号
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysIcp: string;
+    icp: string;
     /**
      * ICP地址
      * @type {string}
      * @memberof InfoSaveInput
      */
-    sysIcpUrl: string;
+    icpUrl: string;
     /**
-     * 启用注册功能
-     * @type {boolean}
+     * 
+     * @type {YesNoEnum}
      * @memberof InfoSaveInput
      */
-    sysRegistration?: boolean;
+    enableReg?: YesNoEnum;
     /**
-     * 登录二次验证
-     * @type {boolean}
+     * 
+     * @type {YesNoEnum}
      * @memberof InfoSaveInput
      */
-    sysSecondVer?: boolean;
+    secondVer?: YesNoEnum;
     /**
-     * 图形验证码
-     * @type {boolean}
+     * 
+     * @type {YesNoEnum}
      * @memberof InfoSaveInput
      */
-    sysCaptcha?: boolean;
+    captcha?: YesNoEnum;
     /**
      * 默认注册方案Id
      * @type {number}
      * @memberof InfoSaveInput
      */
-    sysRegWayId?: number;
+    regWayId?: number;
 }

+ 12 - 0
Web/src/api-services/models/tenant-output.ts

@@ -135,6 +135,18 @@ export interface TenantOutput {
      * @memberof TenantOutput
      */
     regWayId?: number | null;
+    /**
+     * 
+     * @type {YesNoEnum}
+     * @memberof TenantOutput
+     */
+    captcha?: YesNoEnum;
+    /**
+     * 
+     * @type {YesNoEnum}
+     * @memberof TenantOutput
+     */
+    secondVer?: YesNoEnum;
     /**
      * 图标
      * @type {string}

+ 12 - 0
Web/src/api-services/models/update-tenant-input.ts

@@ -129,6 +129,18 @@ export interface UpdateTenantInput {
      * @memberof UpdateTenantInput
      */
     regWayId?: number | null;
+    /**
+     * 
+     * @type {YesNoEnum}
+     * @memberof UpdateTenantInput
+     */
+    captcha?: YesNoEnum;
+    /**
+     * 
+     * @type {YesNoEnum}
+     * @memberof UpdateTenantInput
+     */
+    secondVer?: YesNoEnum;
     /**
      * 图标
      * @type {string}

+ 41 - 78
Web/src/views/system/infoSetting/index.vue

@@ -6,58 +6,53 @@
 					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Setting /> </el-icon> 系统信息配置
 				</template>
 				<el-descriptions-item label="系统图标" :span="2">
-					<!-- <template #label>
-						<div class="cell-item">
-							<el-icon><ele-PictureRounded /></el-icon> 系统图标
-						</div>
-					</template> -->
 					<el-upload ref="uploadRef" class="avatar-uploader" :showFileList="false" :autoUpload="false" accept=".jpg,.png,.svg" action :limit="1" :onChange="handleUploadChange">
-						<img v-if="state.formData.sysLogo" :src="state.formData.sysLogo" class="avatar" />
+						<img v-if="state.formData.logo" :src="state.formData.logo" class="avatar" />
 						<SvgIcon v-else class="avatar-uploader-icon" name="ele-Plus" :size="28" />
 					</el-upload>
 				</el-descriptions-item>
 				<el-descriptions-item label="系统主标题">
-					<el-input v-model="state.formData.sysTitle" />
+					<el-input v-model="state.formData.title" />
 				</el-descriptions-item>
 				<el-descriptions-item label="系统副标题">
-					<el-input v-model="state.formData.sysViceTitle" />
+					<el-input v-model="state.formData.viceTitle" />
 				</el-descriptions-item>
 				<el-descriptions-item label="系统描述" :span="2">
-					<el-input v-model="state.formData.sysViceDesc" />
+					<el-input v-model="state.formData.viceDesc" />
 				</el-descriptions-item>
 				<el-descriptions-item label="水印内容" :span="2">
-					<el-input v-model="state.formData.sysWatermark" />
+					<el-input v-model="state.formData.watermark" />
 				</el-descriptions-item>
 				<el-descriptions-item label="版权说明" :span="2">
-					<el-input v-model="state.formData.sysCopyright" />
+					<el-input v-model="state.formData.copyright" />
 				</el-descriptions-item>
 				<el-descriptions-item label="ICP备案号">
-					<el-input v-model="state.formData.sysIcp" />
+					<el-input v-model="state.formData.icp" />
 				</el-descriptions-item>
 				<el-descriptions-item label="ICP地址">
-					<el-input v-model="state.formData.sysIcpUrl" />
+					<el-input v-model="state.formData.icpUrl" />
 				</el-descriptions-item>
 				<el-descriptions-item label="登录二次验证">
-					<el-radio-group v-model="state.formData.sysSecondVer">
-						<el-radio :value="true">启用</el-radio>
-						<el-radio :value="false">禁用</el-radio>
+					<el-radio-group v-model="state.formData.secondVer">
+						<el-radio :value="1">启用</el-radio>
+						<el-radio :value="2">禁用</el-radio>
 					</el-radio-group>
 				</el-descriptions-item>
 				<el-descriptions-item label="图形验证码">
-					<el-radio-group v-model="state.formData.sysCaptcha">
-						<el-radio :value="true">启用</el-radio>
-						<el-radio :value="false">禁用</el-radio>
+					<el-radio-group v-model="state.formData.captcha">
+						<el-radio :value="1">启用</el-radio>
+						<el-radio :value="2">禁用</el-radio>
 					</el-radio-group>
 				</el-descriptions-item>
 				<el-descriptions-item label="用户注册">
-					<el-radio-group v-model="state.formData.sysRegistration">
-						<el-radio :value="true">启用</el-radio>
-						<el-radio :value="false">禁用</el-radio>
+					<el-radio-group v-model="state.formData.enableReg">
+						<el-radio :value="1">启用</el-radio>
+						<el-radio :value="2">禁用</el-radio>
 					</el-radio-group>
 				</el-descriptions-item>
-				<el-descriptions-item label="注册方案" v-if="state.formData.sysRegistration">
+				<el-descriptions-item label="注册方案" v-if="state.formData.enableReg">
 					<el-select v-model="state.formData.regWayId" placeholder="注册方案" clearable class="w100">
-						<el-option :label="item.label" :value="item.value" v-for="(item, index) in state.regWayData" :key="index" />
+						<el-option :label="item.label" :value="item.value" v-for="(item, index) in state.wayList" :key="index" />
 					</el-select>
 				</el-descriptions-item>
 				<template #extra>
@@ -80,67 +75,49 @@ const uploadRef = ref<UploadInstance>();
 const state = reactive({
 	isLoading: false,
 	file: undefined as any,
-	regWayData: [] as Array<any>,
+	wayList: [] as Array<any>,
 	formData: {
-		sysLogoBlob: undefined,
-		sysLogo: '',
-		sysLogoFileName: '',
-		sysTitle: '',
-		sysViceTitle: '',
-		sysViceDesc: '',
-		sysWatermark: '',
-		sysCopyright: '',
-		sysIcp: '',
-		sysIcpUrl: '',
+		logo: '',
+		logoBase64: '',
+		logoFileName: '',
+		title: '',
+		viceTitle: '',
+		viceDesc: '',
+		watermark: '',
+		copyright: '',
+		icp: '',
+		icpUrl: '',
 		regWayId: undefined,
-		sysRegistration: undefined,
-		sysSecondVer: undefined,
-		sysCaptcha: undefined,
+		enableReg: undefined,
+		secondVer: undefined,
+		captcha: undefined,
 	},
 });
 
 // 通过onChange方法获得文件列表
 const handleUploadChange = (file: any) => {
 	uploadRef.value!.clearFiles();
-
 	state.file = file;
-	state.formData.sysLogo = URL.createObjectURL(state.file.raw); // 显示预览logo
+	state.formData.logo = URL.createObjectURL(state.file.raw); // 显示预览logo
 };
 
 // 保存
 const onSave = async () => {
 	// 如果有选择图标,则转换为 base64
-	let sysLogoBase64 = '';
-	let sysLogoFileName = '';
 	if (state.file) {
-		sysLogoBase64 = (await fileToBase64(state.file.raw)) as string;
-		sysLogoFileName = state.file.raw.name;
+		state.formData.logoBase64 = (await fileToBase64(state.file.raw)) as string;
+		state.formData.logoFileName = state.file.raw.name;
 	}
 
 	try {
 		state.isLoading = true;
-		if (!state.formData.sysRegistration) {
+		if (!state.formData.enableReg) {
 			state.formData.regWayId = undefined;
 		} else if (!state.formData.regWayId) {
 			ElMessage.error('注册方案不能为空');
 			return;
 		}
-		const res = await getAPI(SysConfigApi).apiSysConfigSaveSysInfoPost({
-			sysLogoBase64: sysLogoBase64,
-			sysLogoFileName: sysLogoFileName,
-			sysTitle: state.formData.sysTitle,
-			sysViceTitle: state.formData.sysViceTitle,
-			sysViceDesc: state.formData.sysViceDesc,
-			sysWatermark: state.formData.sysWatermark,
-			sysCopyright: state.formData.sysCopyright,
-			sysIcp: state.formData.sysIcp,
-			sysRegWayId: state.formData.regWayId,
-			sysIcpUrl: state.formData.sysIcpUrl,
-			sysSecondVer: state.formData.sysSecondVer,
-			sysCaptcha: state.formData.sysCaptcha,
-			sysRegistration: state.formData.sysRegistration,
-		});
-		if (res.data!.type !== 'success') return;
+		await getAPI(SysConfigApi).apiSysConfigSaveSysInfoPost(state.formData);
 
 		// 清空 file 变量
 		state.file = undefined;
@@ -161,23 +138,9 @@ const loadData = async () => {
 		if (res.data!.type !== 'success') return;
 
 		const result = res.data.result;
-		state.formData = {
-			sysLogoBlob: undefined,
-			sysLogo: result.sysLogo,
-			sysLogoFileName: '',
-			sysTitle: result.sysTitle,
-			sysViceTitle: result.sysViceTitle,
-			sysViceDesc: result.sysViceDesc,
-			sysWatermark: result.sysWatermark,
-			sysCopyright: result.sysCopyright,
-			sysIcp: result.sysIcp,
-			sysIcpUrl: result.sysIcpUrl,
-			sysSecondVer: result.sysSecondVer,
-			sysCaptcha: result.sysCaptcha,
-			regWayId: result.sysRegWayId,
-			sysRegistration: result.sysRegistration,
-		};
-		state.regWayData = result.sysUserRegWayList ?? [];
+		state.wayList = result.wayList ?? [];
+		result.wayList = undefined;
+		state.formData = res.data.result;
 	} finally {
 		nextTick(() => {
 			state.isLoading = false;

+ 27 - 3
Web/src/views/system/tenant/component/editTenant.vue

@@ -146,7 +146,31 @@
 								</el-form-item>
 							</el-col>
 							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
-								<el-form-item label="启用注册" prop="watermark">
+								<el-form-item label="启用验证码" prop="captcha">
+									<el-switch
+											v-model="state.ruleForm.captcha"
+											inline-prompt
+											:active-value="1"
+											:inactive-value="2"
+											active-text="开启"
+											inactive-text="关闭"
+									/>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="启用二次验证" prop="secondVer">
+									<el-switch
+											v-model="state.ruleForm.secondVer"
+											inline-prompt
+											:active-value="1"
+											:inactive-value="2"
+											active-text="开启"
+											inactive-text="关闭"
+									/>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="启用注册" prop="enableReg">
 									<el-switch
 											v-model="state.ruleForm.enableReg"
 											inline-prompt
@@ -212,13 +236,13 @@ const handleUploadChange = (file: any) => {
 // 打开弹窗
 const openDialog = async (row: any) => {
 	state.selectedTabName = '0';
+	ruleFormRef.value?.resetFields();
 	state.ruleForm = JSON.parse(JSON.stringify(row));
 	state.ruleForm.icp ??= '省ICP备12345678号';
 	state.ruleForm.icpUrl ??= 'https://beian.miit.gov.cn';
 	state.ruleForm.copyright ??= 'Copyright \u00a9 2024-present xxxxx All rights reserved.';
 	state.isShowDialog = true;
-	state.regWayData = await getAPI(SysUserRegWayApi).apiSysUserRegWayListPost({}).then((res) => res.data.result ?? []);
-	ruleFormRef.value?.resetFields();
+	state.regWayData = await getAPI(SysUserRegWayApi).apiSysUserRegWayListPost({ tenantId: row.id }).then((res) => res.data.result ?? []);
 };
 
 // 关闭弹窗

+ 10 - 0
Web/src/views/system/tenant/index.vue

@@ -43,6 +43,16 @@
 						<g-sys-dict v-model="scope.row.enableReg" code="YesNoEnum" />
 					</template>
 				</el-table-column>
+				<el-table-column prop="captcha" label="启用验证码" width="280" show-overflow-tooltip>
+					<template #default="scope">
+						<g-sys-dict v-model="scope.row.captcha" code="YesNoEnum" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="secondVer" label="启用二次验证" width="280" show-overflow-tooltip>
+					<template #default="scope">
+						<g-sys-dict v-model="scope.row.secondVer" code="YesNoEnum" />
+					</template>
+				</el-table-column>
 				<el-table-column prop="adminAccount" label="租管账号" align="center" width="120" show-overflow-tooltip />
 				<el-table-column prop="phone" label="电话" width="120" align="center" show-overflow-tooltip />
 				<el-table-column prop="host" label="域名" width="150" show-overflow-tooltip />