Browse Source

新增 任务调度TaskScheduler功能
修复 命令台实时Sql显示重复的问题
修复 缓存管理无法读取App项目缓存键的问题,需CacheKeys类上添加特性[ScanCacheKeys]

zhontai 2 years ago
parent
commit
f7e7d0d1c8

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

@@ -9,7 +9,7 @@ using ZhonTai.ApiUI;
 
 new HostApp(new HostAppOptions
 {
-    ConfigurePostMiddleware = context =>
+	ConfigurePostMiddleware = context =>
     {
 		var app = context.App;
 		var env = app.Environment;

+ 9 - 0
src/platform/ZhonTai.Admin/Core/Attributes/ScanCacheKeysAttribute.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace ZhonTai.Admin.Core.Attributes
+{
+    [AttributeUsage(AttributeTargets.Class)]
+    public class ScanCacheKeysAttribute : Attribute
+    {
+    }
+}

+ 4 - 2
src/platform/ZhonTai.Admin/Services/Base/CacheKey.cs → src/platform/ZhonTai.Admin/Core/Consts/CacheKeys.cs

@@ -1,11 +1,13 @@
 using System.ComponentModel;
+using ZhonTai.Admin.Core.Attributes;
 
-namespace ZhonTai.Admin.Services.Contracts
+namespace ZhonTai.Admin.Core.Consts
 {
     /// <summary>
     /// 缓存键
     /// </summary>
-    public static partial class CacheKey
+    [ScanCacheKeys]
+    public static partial class CacheKeys
     {
         /// <summary>
         /// 验证码 admin:captcha:guid

+ 1 - 2
src/platform/ZhonTai.Admin/Core/Db/DBServiceCollectionExtensions.cs

@@ -65,7 +65,7 @@ namespace ZhonTai.Admin.Core.Db
             var appConfig = ConfigHelper.Get<AppConfig>("appconfig", env.EnvironmentName);
             DbHelper.ConfigEntity(fsql, appConfig);
 
-            hostAppOptions?.ConfigureEntity?.Invoke(fsql);
+            hostAppOptions?.ConfigureFreeSql?.Invoke(fsql);
             #region 初始化数据库
 
             //同步结构
@@ -122,7 +122,6 @@ namespace ZhonTai.Admin.Core.Db
                     {
                         MiniProfiler.Current.CustomTiming("CurdAfter", $"{e.ElapsedMilliseconds}");
                     }
-                    Console.WriteLine($"{e.Sql}\r\n");
                 };
             }
 

+ 3 - 5
src/platform/ZhonTai.Admin/Core/Db/DbHelper.cs

@@ -61,7 +61,7 @@ namespace ZhonTai.Admin.Core.Db
                 .Where(a => appConfig.AssemblyNames.Contains(a.Name) || a.Name == "ZhonTai.Admin")
                 .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray();
 
-            List<Type> entityTypes = new List<Type>();
+            var entityTypes = new List<Type>();
 
             foreach (var assembly in assemblies)
             {
@@ -306,8 +306,7 @@ namespace ZhonTai.Admin.Core.Db
 
                 List<ISyncData> syncDatas = assemblies.Select(assembly => assembly.GetTypes()
                 .Where(x => typeof(ISyncData).GetTypeInfo().IsAssignableFrom(x.GetTypeInfo()) && x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract))
-                .SelectMany(registerTypes =>
-                    registerTypes.Select(registerType => (ISyncData)Activator.CreateInstance(registerType))).ToList();
+                .SelectMany(registerTypes => registerTypes.Select(registerType => (ISyncData)Activator.CreateInstance(registerType))).ToList();
 
                 foreach (ISyncData syncData in syncDatas)
                 {
@@ -343,8 +342,7 @@ namespace ZhonTai.Admin.Core.Db
 
                 List<IGenerateData> generateDatas = assemblies.Select(assembly => assembly.GetTypes()
                 .Where(x => typeof(IGenerateData).GetTypeInfo().IsAssignableFrom(x.GetTypeInfo()) && x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract))
-                .SelectMany(registerTypes =>
-                    registerTypes.Select(registerType => (IGenerateData)Activator.CreateInstance(registerType))).ToList();
+                .SelectMany(registerTypes => registerTypes.Select(registerType => (IGenerateData)Activator.CreateInstance(registerType))).ToList();
 
                 foreach (IGenerateData generateData in generateDatas)
                 {

+ 46 - 47
src/platform/ZhonTai.Admin/Core/Startup/HostAppOptions.cs

@@ -3,62 +3,61 @@ using System;
 using Yitter.IdGenerator;
 using ZhonTai.DynamicApi;
 
-namespace ZhonTai.Admin.Core.Startup
+namespace ZhonTai.Admin.Core.Startup;
+
+/// <summary>
+/// HostApp配置
+/// </summary>
+public class HostAppOptions
 {
     /// <summary>
-    /// HostApp配置
+    /// 注入前置服务
     /// </summary>
-    public class HostAppOptions
-    {
-        /// <summary>
-        /// 注入前置服务
-        /// </summary>
-        public Action<HostAppContext> ConfigurePreServices { get; set; }
+    public Action<HostAppContext> ConfigurePreServices { get; set; }
 
-        /// <summary>
-        /// 注入服务
-        /// </summary>
-        public Action<HostAppContext> ConfigureServices { get; set; }
+    /// <summary>
+    /// 注入服务
+    /// </summary>
+    public Action<HostAppContext> ConfigureServices { get; set; }
+
+    /// <summary>
+    /// 注入后置服务
+    /// </summary>
+    public Action<HostAppContext> ConfigurePostServices { get; set; }
 
-        /// <summary>
-        /// 注入后置服务
-        /// </summary>
-        public Action<HostAppContext> ConfigurePostServices { get; set; }
+    /// <summary>
+    /// 注入前置中间件
+    /// </summary>
+    public Action<HostAppMiddlewareContext> ConfigurePreMiddleware { get; set; }
 
-        /// <summary>
-        /// 注入前置中间件
-        /// </summary>
-        public Action<HostAppMiddlewareContext> ConfigurePreMiddleware { get; set; }
+    /// <summary>
+    /// 注入中间件
+    /// </summary>
+    public Action<HostAppMiddlewareContext> ConfigureMiddleware { get; set; }
 
-        /// <summary>
-        /// 注入中间件
-        /// </summary>
-        public Action<HostAppMiddlewareContext> ConfigureMiddleware { get; set; }
+    /// <summary>
+    /// 注入后置中间件
+    /// </summary>
+    public Action<HostAppMiddlewareContext> ConfigurePostMiddleware { get; set; }
 
-        /// <summary>
-        /// 注入后置中间件
-        /// </summary>
-        public Action<HostAppMiddlewareContext> ConfigurePostMiddleware { get; set; }
+    /// <summary>
+    /// 配置FreeSql构建器
+    /// </summary>
+    public Action<FreeSqlBuilder> ConfigureFreeSqlBuilder { get; set; }
 
-        /// <summary>
-        /// 配置FreeSql构建器
-        /// </summary>
-        public Action<FreeSqlBuilder> ConfigureFreeSqlBuilder { get; set; }
+    /// <summary>
+    /// 配置FreeSql
+    /// </summary>
+    public Action<IFreeSql> ConfigureFreeSql { get; set; }
 
-        /// <summary>
-        /// 配置实体
-        /// </summary>
-        public Action<IFreeSql> ConfigureEntity { get; set; }
+    /// <summary>
+    /// 配置动态Api
+    /// </summary>
+    public Action<DynamicApiOptions> ConfigureDynamicApi { get; set; }
 
-        /// <summary>
-        /// 配置动态Api
-        /// </summary>
-        public Action<DynamicApiOptions> ConfigureDynamicApi { get; set; }
+    /// <summary>
+    /// 配置雪花漂移算法
+    /// </summary>
+    public Action<IdGeneratorOptions> ConfigureIdGenerator { get; set; }
 
-        /// <summary>
-        /// 配置雪花漂移算法
-        /// </summary>
-        public Action<IdGeneratorOptions> ConfigureIdGenerator { get; set; }
-        
-    }
-}
+}

+ 5 - 6
src/platform/ZhonTai.Admin/Services/Auth/AuthService.cs

@@ -10,7 +10,6 @@ using ZhonTai.Admin.Domain.Tenant;
 using ZhonTai.Admin.Services.Auth.Dto;
 using ZhonTai.Admin.Domain.RolePermission;
 using ZhonTai.Admin.Domain.UserRole;
-using ZhonTai.Admin.Services.Contracts;
 using ZhonTai.Admin.Tools.Captcha;
 using ZhonTai.DynamicApi;
 using ZhonTai.DynamicApi.Attributes;
@@ -100,7 +99,7 @@ namespace ZhonTai.Admin.Services.Auth
         {
             //写入Redis
             var guid = Guid.NewGuid().ToString("N");
-            var key = string.Format(CacheKey.PassWordEncryptKey, guid);
+            var key = string.Format(CacheKeys.PassWordEncryptKey, guid);
             var encyptKey = StringHelper.GenerateRandom(8);
             await Cache.SetAsync(key, encyptKey, TimeSpan.FromMinutes(5));
             var data = new { key = guid, encyptKey };
@@ -171,7 +170,7 @@ namespace ZhonTai.Admin.Services.Auth
             if (_appConfig.VarifyCode.Enable)
             {
                 input.Captcha.DeleteCache = true;
-                input.Captcha.CaptchaKey = CacheKey.CaptchaKey;
+                input.Captcha.CaptchaKey = CacheKeys.CaptchaKey;
                 var isOk = await _captchaTool.CheckAsync(input.Captcha);
                 if (!isOk)
                 {
@@ -194,7 +193,7 @@ namespace ZhonTai.Admin.Services.Auth
 
             if (input.PasswordKey.NotNull())
             {
-                var passwordEncryptKey = string.Format(CacheKey.PassWordEncryptKey, input.PasswordKey);
+                var passwordEncryptKey = string.Format(CacheKeys.PassWordEncryptKey, input.PasswordKey);
                 var existsPasswordKey = await Cache.ExistsAsync(passwordEncryptKey);
                 if (existsPasswordKey)
                 {
@@ -312,7 +311,7 @@ namespace ZhonTai.Admin.Services.Auth
         {
             using (MiniProfiler.Current.Step("获取滑块验证"))
             {
-                var data = await _captchaTool.GetAsync(CacheKey.CaptchaKey);
+                var data = await _captchaTool.GetAsync(CacheKeys.CaptchaKey);
                 return ResultOutput.Ok(data);
             }
         }
@@ -327,7 +326,7 @@ namespace ZhonTai.Admin.Services.Auth
         [EnableCors(AdminConsts.AllowAnyPolicyName)]
         public async Task<IResultOutput> CheckCaptcha([FromQuery] CaptchaInput input)
         {
-            input.CaptchaKey = CacheKey.CaptchaKey;
+            input.CaptchaKey = CacheKeys.CaptchaKey;
             var result = await _captchaTool.CheckAsync(input);
             return ResultOutput.Result(result);
         }

+ 27 - 12
src/platform/ZhonTai.Admin/Services/Cache/CacheService.cs

@@ -1,11 +1,14 @@
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyModel;
+using Microsoft.Extensions.Logging;
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using System.Reflection;
 using System.Threading.Tasks;
+using ZhonTai.Admin.Core.Attributes;
+using ZhonTai.Admin.Core.Configs;
 using ZhonTai.Admin.Core.Dto;
-using ZhonTai.Admin.Services.Contracts;
 using ZhonTai.DynamicApi;
 using ZhonTai.DynamicApi.Attributes;
 
@@ -28,18 +31,30 @@ namespace ZhonTai.Admin.Services.Cache
         public IResultOutput GetList()
         {
             var list = new List<object>();
-            var cacheKey = typeof(CacheKey);
-            var fields = cacheKey.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
-            foreach (var field in fields)
-            {
-                var descriptionAttribute = field.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;
 
-                list.Add(new
+            var appConfig = LazyGetRequiredService<AppConfig>();
+            Assembly[] assemblies = DependencyContext.Default.RuntimeLibraries
+                .Where(a => appConfig.AssemblyNames.Contains(a.Name) || a.Name == "ZhonTai.Admin")
+                .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray();
+
+            foreach (Assembly assembly in assemblies)
+            {
+                var types = assembly.GetExportedTypes().Where(a => a.GetCustomAttribute<ScanCacheKeysAttribute>() != null);
+                foreach (Type type in types)
                 {
-                    field.Name,
-                    Value = field.GetRawConstantValue().ToString(),
-                    descriptionAttribute?.Description
-                });
+                    var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
+                    foreach (FieldInfo field in fields)
+                    {
+                        var descriptionAttribute = field.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;
+
+                        list.Add(new
+                        {
+                            field.Name,
+                            Value = field.GetRawConstantValue().ToString(),
+                            descriptionAttribute?.Description
+                        });
+                    }
+                }
             }
 
             return ResultOutput.Ok(list);

+ 4 - 4
src/platform/ZhonTai.Admin/Services/Permission/PermissionService.cs

@@ -17,10 +17,10 @@ using ZhonTai.Admin.Domain.PermissionApi;
 using ZhonTai.Admin.Domain.Role;
 using ZhonTai.Admin.Domain.Api;
 using ZhonTai.Admin.Domain.User;
-using ZhonTai.Admin.Services.Contracts;
 using ZhonTai.DynamicApi;
 using ZhonTai.DynamicApi.Attributes;
 using ZhonTai.Admin.Core.Db;
+using ZhonTai.Admin.Core.Consts;
 
 namespace ZhonTai.Admin.Services.Permission
 {
@@ -71,7 +71,7 @@ namespace ZhonTai.Admin.Services.Permission
             ).ToListAsync(a => a.UserId);
             foreach (var userId in userIds)
             {
-                await Cache.DelAsync(string.Format(CacheKey.UserPermissions, userId));
+                await Cache.DelAsync(string.Format(CacheKeys.UserPermissions, userId));
             }
         }
 
@@ -471,7 +471,7 @@ namespace ZhonTai.Admin.Services.Permission
             var userIds = await _userRoleRepository.Select.Where(a => a.RoleId == input.RoleId).ToListAsync(a => a.UserId);
             foreach (var userId in userIds)
             {
-                await Cache.DelAsync(string.Format(CacheKey.UserPermissions, userId));
+                await Cache.DelAsync(string.Format(CacheKeys.UserPermissions, userId));
             }
 
             return ResultOutput.Ok();
@@ -523,7 +523,7 @@ namespace ZhonTai.Admin.Services.Permission
             {
                 foreach (var userId in userIds)
                 {
-                    await Cache.DelAsync(string.Format(CacheKey.UserPermissions, userId));
+                    await Cache.DelAsync(string.Format(CacheKeys.UserPermissions, userId));
                 }
             }
 

+ 3 - 3
src/platform/ZhonTai.Admin/Services/User/UserService.cs

@@ -13,7 +13,6 @@ using ZhonTai.Admin.Domain.RolePermission;
 using ZhonTai.Admin.Domain.Tenant;
 using ZhonTai.Admin.Domain.User;
 using ZhonTai.Admin.Domain.UserRole;
-using ZhonTai.Admin.Services.Contracts;
 using ZhonTai.Admin.Services.Auth.Dto;
 using ZhonTai.Admin.Services.User.Dto;
 using ZhonTai.DynamicApi;
@@ -22,6 +21,7 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Options;
 using ZhonTai.Admin.Core.Helpers;
+using ZhonTai.Admin.Core.Consts;
 
 namespace ZhonTai.Admin.Services.User
 {
@@ -149,7 +149,7 @@ namespace ZhonTai.Admin.Services.User
         /// <returns></returns>
         public async Task<IList<UserPermissionsOutput>> GetPermissionsAsync()
         {
-            var key = string.Format(CacheKey.UserPermissions, User.Id);
+            var key = string.Format(CacheKeys.UserPermissions, User.Id);
             var result = await Cache.GetOrSetAsync(key, async () =>
             {
                 return await _apiRepository
@@ -239,7 +239,7 @@ namespace ZhonTai.Admin.Services.User
             var result = (await _userRepository.UpdateAsync(entity)) > 0;
 
             //清除用户缓存
-            await Cache.DelAsync(string.Format(CacheKey.UserInfo, input.Id));
+            await Cache.DelAsync(string.Format(CacheKeys.UserInfo, input.Id));
 
             return ResultOutput.Result(result);
         }

+ 15 - 16
src/platform/ZhonTai.Admin/Tools/Captcha/ICaptchaTool.cs

@@ -1,23 +1,22 @@
 using System.Threading.Tasks;
 
-namespace ZhonTai.Admin.Tools.Captcha
+namespace ZhonTai.Admin.Tools.Captcha;
+
+/// <summary>
+/// 验证接口
+/// </summary>
+public interface ICaptchaTool
 {
     /// <summary>
-    /// 验证接口
+    /// 获得验证数据
     /// </summary>
-    public interface ICaptchaTool
-    {
-        /// <summary>
-        /// 获得验证数据
-        /// </summary>
-        /// <returns></returns>
-        Task<CaptchaOutput> GetAsync(string captchaKey);
+    /// <returns></returns>
+    Task<CaptchaOutput> GetAsync(string captchaKey);
 
-        /// <summary>
-        /// 检查验证数据
-        /// </summary>
-        /// <param name="input"></param>
-        /// <returns></returns>
-        Task<bool> CheckAsync(CaptchaInput input);
-    }
+    /// <summary>
+    /// 检查验证数据
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    Task<bool> CheckAsync(CaptchaInput input);
 }

+ 201 - 202
src/platform/ZhonTai.Admin/Tools/Captcha/SlideJigsawCaptchaTool.cs

@@ -12,266 +12,265 @@ using SixLabors.ImageSharp.PixelFormats;
 using ZhonTai.Admin.Core.Attributes;
 using ZhonTai.Admin.Tools.Cache;
 
-namespace ZhonTai.Admin.Tools.Captcha
+namespace ZhonTai.Admin.Tools.Captcha;
+
+/// <summary>
+/// 滑块拼图验证
+/// </summary>
+[SingleInstance]
+public class SlideJigsawCaptchaTool : ICaptchaTool
 {
+    private readonly ICacheTool _cache;
+
+    private readonly Random _random = new();
+
+    public SlideJigsawCaptchaTool(ICacheTool cache)
+    {
+        _cache = cache;
+    }
+
     /// <summary>
-    /// 滑块拼图验证
+    /// 随机范围内数字
     /// </summary>
-    [SingleInstance]
-    public class SlideJigsawCaptchaTool : ICaptchaTool
+    /// <param name="startNum"></param>
+    /// <param name="endNum"></param>
+    /// <returns></returns>
+    private int GetRandomInt(int startNum, int endNum)
     {
-        private readonly ICacheTool _cache;
-
-        private readonly Random _random = new();
+        return (endNum > startNum ? _random.Next(endNum - startNum) : 0) + startNum;
+    }
 
-        public SlideJigsawCaptchaTool(ICacheTool cache)
+    /// <summary>
+    /// 随机生成拼图坐标
+    /// </summary>
+    /// <param name="originalWidth"></param>
+    /// <param name="originalHeight"></param>
+    /// <param name="templateWidth"></param>
+    /// <param name="templateHeight"></param>
+    /// <returns></returns>
+    private PointModel GeneratePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight)
+    {
+        int widthDifference = originalWidth - templateWidth;
+        int heightDifference = originalHeight - templateHeight;
+        int x;
+        if (widthDifference <= 0)
         {
-            _cache = cache;
+            x = 5;
         }
-
-        /// <summary>
-        /// 随机范围内数字
-        /// </summary>
-        /// <param name="startNum"></param>
-        /// <param name="endNum"></param>
-        /// <returns></returns>
-		private int GetRandomInt(int startNum, int endNum)
+        else
         {
-            return (endNum > startNum ? _random.Next(endNum - startNum) : 0) + startNum;
+            x = _random.Next(originalWidth - templateWidth - 100) + 100;
         }
 
-        /// <summary>
-        /// 随机生成拼图坐标
-        /// </summary>
-        /// <param name="originalWidth"></param>
-        /// <param name="originalHeight"></param>
-        /// <param name="templateWidth"></param>
-        /// <param name="templateHeight"></param>
-        /// <returns></returns>
-        private PointModel GeneratePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight)
+        int y;
+        if (heightDifference <= 0)
         {
-            int widthDifference = originalWidth - templateWidth;
-            int heightDifference = originalHeight - templateHeight;
-            int x;
-            if (widthDifference <= 0)
-            {
-                x = 5;
-            }
-            else
-            {
-                x = _random.Next(originalWidth - templateWidth - 100) + 100;
-            }
+            y = 5;
+        }
+        else
+        {
+            y = _random.Next(originalHeight - templateHeight - 5) + 5;
+        }
 
-            int y;
-            if (heightDifference <= 0)
-            {
-                y = 5;
-            }
-            else
-            {
-                y = _random.Next(originalHeight - templateHeight - 5) + 5;
-            }
+        return new PointModel(x, y);
+    }
 
-            return new PointModel(x, y);
+    /// <summary>
+    /// 随机生成干扰图坐标
+    /// </summary>
+    /// <param name="originalWidth"></param>
+    /// <param name="originalHeight"></param>
+    /// <param name="templateWidth"></param>
+    /// <param name="templateHeight"></param>
+    /// <param name="blockX"></param>
+    /// <param name="blockY"></param>
+    /// <returns></returns>
+    private PointModel GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight, int blockX, int blockY)
+    {
+        int x;
+        if (originalWidth - blockX - 5 > templateWidth * 2)
+        {
+            //在原扣图右边插入干扰图
+            x = GetRandomInt(blockX + templateWidth + 5, originalWidth - templateWidth);
         }
-
-        /// <summary>
-        /// 随机生成干扰图坐标
-        /// </summary>
-        /// <param name="originalWidth"></param>
-        /// <param name="originalHeight"></param>
-        /// <param name="templateWidth"></param>
-        /// <param name="templateHeight"></param>
-        /// <param name="blockX"></param>
-        /// <param name="blockY"></param>
-        /// <returns></returns>
-        private PointModel GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight, int blockX, int blockY)
+        else
         {
-            int x;
-            if (originalWidth - blockX - 5 > templateWidth * 2)
-            {
-                //在原扣图右边插入干扰图
-                x = GetRandomInt(blockX + templateWidth + 5, originalWidth - templateWidth);
-            }
-            else
-            {
-                //在原扣图左边插入干扰图
-                x = GetRandomInt(100, blockX - templateWidth - 5);
-            }
-
-            int y;
-            if (originalHeight - blockY - 5 > templateHeight * 2)
-            {
-                //在原扣图下边插入干扰图
-                y = GetRandomInt(blockY + templateHeight + 5, originalHeight - templateHeight);
-            }
-            else
-            {
-                //在原扣图上边插入干扰图
-                y = GetRandomInt(5, blockY - templateHeight - 5);
-            }
+            //在原扣图左边插入干扰图
+            x = GetRandomInt(100, blockX - templateWidth - 5);
+        }
 
-            return new PointModel(x, y);
+        int y;
+        if (originalHeight - blockY - 5 > templateHeight * 2)
+        {
+            //在原扣图下边插入干扰图
+            y = GetRandomInt(blockY + templateHeight + 5, originalHeight - templateHeight);
+        }
+        else
+        {
+            //在原扣图上边插入干扰图
+            y = GetRandomInt(5, blockY - templateHeight - 5);
         }
 
-        private static ComplexPolygon CalcBlockShape(Image<Rgba32> templateDarkImage)
+        return new PointModel(x, y);
+    }
+
+    private static ComplexPolygon CalcBlockShape(Image<Rgba32> templateDarkImage)
+    {
+        int temp = 0;
+        var pathList = new List<IPath>();
+        templateDarkImage.ProcessPixelRows(accessor =>
         {
-            int temp = 0;
-            var pathList = new List<IPath>();
-            templateDarkImage.ProcessPixelRows(accessor =>
+            for (int y = 0; y < templateDarkImage.Height; y++)
             {
-                for (int y = 0; y < templateDarkImage.Height; y++)
+                var rowSpan = accessor.GetRowSpan(y);
+                for (int x = 0; x < rowSpan.Length; x++)
                 {
-                    var rowSpan = accessor.GetRowSpan(y);
-                    for (int x = 0; x < rowSpan.Length; x++)
+                    ref Rgba32 pixel = ref rowSpan[x];
+                    if (pixel.A != 0)
                     {
-                        ref Rgba32 pixel = ref rowSpan[x];
-                        if (pixel.A != 0)
+                        if (temp == 0)
                         {
-                            if (temp == 0)
-                            {
-                                temp = x;
-                            }
+                            temp = x;
                         }
-                        else
+                    }
+                    else
+                    {
+                        if (temp != 0)
                         {
-                            if (temp != 0)
-                            {
-                                pathList.Add(new RectangularPolygon(temp, y, x - temp, 1));
-                                temp = 0;
-                            }
+                            pathList.Add(new RectangularPolygon(temp, y, x - temp, 1));
+                            temp = 0;
                         }
                     }
                 }
-            });
+            }
+        });
 
-            return new ComplexPolygon(new PathCollection(pathList));
-        }
+        return new ComplexPolygon(new PathCollection(pathList));
+    }
 
-        /// <summary>
-        /// 获得验证数据
-        /// </summary>
-        /// <param name="captchaKey"></param>
-        /// <returns></returns>
-        public async Task<CaptchaOutput> GetAsync(string captchaKey)
-        {
-            //获取网络图片
-            //var client = new HttpClient();
-            //var stream = await client.GetStreamAsync("https://picsum.photos/310/155");
-            //client.Dispose();
+    /// <summary>
+    /// 获得验证数据
+    /// </summary>
+    /// <param name="captchaKey"></param>
+    /// <returns></returns>
+    public async Task<CaptchaOutput> GetAsync(string captchaKey)
+    {
+        //获取网络图片
+        //var client = new HttpClient();
+        //var stream = await client.GetStreamAsync("https://picsum.photos/310/155");
+        //client.Dispose();
 
-            //底图
-            using var baseImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\backgrounds\{_random.Next(1, 6)}.jpg".ToPath());
-            var randomTemplate = _random.Next(1, 7);
-            //深色模板图
-            using var darkTemplateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{randomTemplate}\dark.png".ToPath());
-            //透明模板图
-            using var transparentTemplateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{randomTemplate}\transparent.png".ToPath());
+        //底图
+        using var baseImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\backgrounds\{_random.Next(1, 6)}.jpg".ToPath());
+        var randomTemplate = _random.Next(1, 7);
+        //深色模板图
+        using var darkTemplateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{randomTemplate}\dark.png".ToPath());
+        //透明模板图
+        using var transparentTemplateImage = await Image.LoadAsync<Rgba32>($@"{Directory.GetCurrentDirectory()}\wwwroot\captcha\jigsaw\templates\{randomTemplate}\transparent.png".ToPath());
 
-            int baseWidth = baseImage.Width;
-            int baseHeight = baseImage.Height;
-            int blockWidth = 50;
-            int blockHeight = 50;
+        int baseWidth = baseImage.Width;
+        int baseHeight = baseImage.Height;
+        int blockWidth = 50;
+        int blockHeight = 50;
 
-            //调整模板图大小
-            darkTemplateImage.Mutate(x =>
-            {
-                x.Resize(blockWidth, blockHeight);
-            });
-            transparentTemplateImage.Mutate(x =>
-            {
-                x.Resize(blockWidth, blockHeight);
-            });
+        //调整模板图大小
+        darkTemplateImage.Mutate(x =>
+        {
+            x.Resize(blockWidth, blockHeight);
+        });
+        transparentTemplateImage.Mutate(x =>
+        {
+            x.Resize(blockWidth, blockHeight);
+        });
 
-            //新建拼图
-            using var blockImage = new Image<Rgba32>(blockWidth, blockHeight);
-            //新建滑块拼图
-            using var sliderBlockImage = new Image<Rgba32>(blockWidth, baseHeight);
+        //新建拼图
+        using var blockImage = new Image<Rgba32>(blockWidth, blockHeight);
+        //新建滑块拼图
+        using var sliderBlockImage = new Image<Rgba32>(blockWidth, baseHeight);
 
-            //随机生成拼图坐标
-            PointModel blockPoint = GeneratePoint(baseWidth, baseHeight, blockWidth, blockHeight);
+        //随机生成拼图坐标
+        PointModel blockPoint = GeneratePoint(baseWidth, baseHeight, blockWidth, blockHeight);
 
-            //根据深色模板图计算轮廓形状
-            var blockShape = CalcBlockShape(darkTemplateImage);
+        //根据深色模板图计算轮廓形状
+        var blockShape = CalcBlockShape(darkTemplateImage);
 
-            //生成拼图
-            blockImage.Mutate(x =>
-            {
-                x.Clip(blockShape, p => p.DrawImage(baseImage, new Point(-blockPoint.X, -blockPoint.Y), 1));
-            });
-            //拼图叠加透明模板图层
-            blockImage.Mutate(x => x.DrawImage(transparentTemplateImage, new Point(0, 0), 1));
+        //生成拼图
+        blockImage.Mutate(x =>
+        {
+            x.Clip(blockShape, p => p.DrawImage(baseImage, new Point(-blockPoint.X, -blockPoint.Y), 1));
+        });
+        //拼图叠加透明模板图层
+        blockImage.Mutate(x => x.DrawImage(transparentTemplateImage, new Point(0, 0), 1));
 
-            //生成滑块拼图
-            sliderBlockImage.Mutate(x => x.DrawImage(blockImage, new Point(0, blockPoint.Y), 1));
+        //生成滑块拼图
+        sliderBlockImage.Mutate(x => x.DrawImage(blockImage, new Point(0, blockPoint.Y), 1));
 
-            var opacity = (float)(_random.Next(70, 100) * 0.01);
-            //底图叠加深色模板图
-            baseImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(blockPoint.X, blockPoint.Y), opacity));
-            //生成干扰图坐标
-            PointModel interferencePoint = GenerateInterferencePoint(baseWidth, baseHeight, blockWidth, blockHeight, blockPoint.X, blockPoint.Y);
-            //底图叠加深色干扰模板图
-            baseImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(interferencePoint.X, interferencePoint.Y), opacity));
-            
-            var token = Guid.NewGuid().ToString();
-            var captchaData = new CaptchaOutput
+        var opacity = (float)(_random.Next(70, 100) * 0.01);
+        //底图叠加深色模板图
+        baseImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(blockPoint.X, blockPoint.Y), opacity));
+        //生成干扰图坐标
+        PointModel interferencePoint = GenerateInterferencePoint(baseWidth, baseHeight, blockWidth, blockHeight, blockPoint.X, blockPoint.Y);
+        //底图叠加深色干扰模板图
+        baseImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(interferencePoint.X, interferencePoint.Y), opacity));
+
+        var token = Guid.NewGuid().ToString();
+        var captchaData = new CaptchaOutput
+        {
+            Token = token,
+            Data = new SlideJigsawCaptchaDto()
             {
-                Token = token,
-                Data = new SlideJigsawCaptchaDto()
-                {
-                    BaseImage = baseImage.ToBase64String(PngFormat.Instance),
-                    BlockImage = sliderBlockImage.ToBase64String(PngFormat.Instance)
-                }
-            };
+                BaseImage = baseImage.ToBase64String(PngFormat.Instance),
+                BlockImage = sliderBlockImage.ToBase64String(PngFormat.Instance)
+            }
+        };
 
-            var key = string.Format(captchaKey, token);
-            await _cache.SetAsync(key, blockPoint.X);
+        var key = string.Format(captchaKey, token);
+        await _cache.SetAsync(key, blockPoint.X);
 
-            return captchaData;
-        }
+        return captchaData;
+    }
 
-        /// <summary>
-        /// 检查验证数据
-        /// </summary>
-        /// <param name="input"></param>
-        /// <returns></returns>
-        public async Task<bool> CheckAsync(CaptchaInput input)
+    /// <summary>
+    /// 检查验证数据
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    public async Task<bool> CheckAsync(CaptchaInput input)
+    {
+        if (input == null || input.Data.IsNull())
         {
-            if (input == null || input.Data.IsNull())
-            {
-                return false;
-            }
-            var key = string.Format(input.CaptchaKey, input.Token);
-            if (await _cache.ExistsAsync(key))
+            return false;
+        }
+        var key = string.Format(input.CaptchaKey, input.Token);
+        if (await _cache.ExistsAsync(key))
+        {
+            try
             {
-                try
+                var point = JsonConvert.DeserializeObject<PointModel>(input.Data);
+                var x = await _cache.GetAsync<int>(key);
+                if (Math.Abs(x - point.X) < 5)
                 {
-                    var point = JsonConvert.DeserializeObject<PointModel>(input.Data);
-                    var x = await _cache.GetAsync<int>(key);
-                    if (Math.Abs(x - point.X) < 5)
-                    {
-                        if (input.DeleteCache)
-                        {
-                            await _cache.DelAsync(key);
-                        }
-                        return true;
-                    }
-                    else
+                    if (input.DeleteCache)
                     {
                         await _cache.DelAsync(key);
-                        return false;
                     }
+                    return true;
                 }
-                catch
+                else
                 {
                     await _cache.DelAsync(key);
                     return false;
                 }
             }
-            else
+            catch
             {
+                await _cache.DelAsync(key);
                 return false;
             }
         }
+        else
+        {
+            return false;
+        }
     }
 }

+ 74 - 75
src/platform/ZhonTai.Admin/Tools/Captcha/VerifyCodeHelper.cs

@@ -10,101 +10,100 @@ using System.Text;
 using ZhonTai.Admin.Core.Attributes;
 using ZhonTai.Admin.Core.Configs;
 
-namespace ZhonTai.Admin.Tools.Captcha
+namespace ZhonTai.Admin.Tools.Captcha;
+
+[SingleInstance]
+public class VerifyCodeHelper
 {
-    [SingleInstance]
-    public class VerifyCodeHelper
+    private readonly AppConfig _appConfig;
+
+    public VerifyCodeHelper(AppConfig appConfig)
     {
-        private readonly AppConfig _appConfig;
+        _appConfig = appConfig;
+    }
 
-        public VerifyCodeHelper(AppConfig appConfig)
+    private static string GenerateRandom(int length)
+    {
+        var chars = new StringBuilder();
+        //验证码的字符集,去掉了一些容易混淆的字符
+        char[] character = { '2', '3', '4', '5', '6', '8', '9', 'a', 'b', 'd', 'e', 'f', 'h', 'k', 'm', 'n', 'r', 'x', 'y', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', 'X', 'Y' };
+        var rnd = new Random();
+        //生成验证码字符串
+        for (int i = 0; i < length; i++)
         {
-            _appConfig = appConfig;
+            chars.Append(character[rnd.Next(character.Length)]);
         }
+        return chars.ToString();
+    }
 
-        private static string GenerateRandom(int length)
-        {
-            var chars = new StringBuilder();
-            //验证码的字符集,去掉了一些容易混淆的字符
-            char[] character = { '2', '3', '4', '5', '6', '8', '9', 'a', 'b', 'd', 'e', 'f', 'h', 'k', 'm', 'n', 'r', 'x', 'y', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', 'X', 'Y' };
-            var rnd = new Random();
-            //生成验证码字符串
-            for (int i = 0; i < length; i++)
-            {
-                chars.Append(character[rnd.Next(character.Length)]);
-            }
-            return chars.ToString();
-        }
+    public byte[] Draw(out string code, int length = 4)
+    {
+        int width = 110;
+        int height = 36;
+        int fontSize = 22;
 
-        public byte[] Draw(out string code, int length = 4)
-        {
-            int width = 110;
-            int height = 36;
-            int fontSize = 22;
+        //颜色列表,用于验证码、噪线、噪点
+        Color[] colors = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.Brown, Color.DarkBlue };
+        //字体列表,用于验证码
+        string[] fonts = _appConfig.VarifyCode.Fonts;
 
-            //颜色列表,用于验证码、噪线、噪点
-            Color[] colors = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.Brown, Color.DarkBlue };
-            //字体列表,用于验证码
-            string[] fonts = _appConfig.VarifyCode.Fonts;
+        var chars = GenerateRandom(length);
+        code = chars;
 
-            var chars = GenerateRandom(length);
-            code = chars;
+        using var img = new Image<Rgba32>(width, height, Color.White);
 
-            using var img = new Image<Rgba32>(width, height, Color.White);
+        img.Mutate(ctx =>
+        {
+            var rnd = new Random();
+            //画噪线
+            for (int i = 0; i < 1; i++)
+            {
+                int x1 = rnd.Next(width), y1 = rnd.Next(height);
+                int x2 = rnd.Next(width), y2 = rnd.Next(height);
+                int ctrlx1 = rnd.Next(width / 4, width / 4 * 3), ctrly1 = rnd.Next(5, height - 5);
+                int ctrlx2 = rnd.Next(width / 4, width / 4 * 3), ctrly2 = rnd.Next(5, height - 5);
+                Color color = colors[rnd.Next(colors.Length)];
+                ctx.DrawBeziers(new DrawingOptions
+                {
+                    GraphicsOptions = new GraphicsOptions
+                    {
+                        BlendPercentage = 1
+                    }
+                }, color, 1, new PointF(x1, y1), new PointF(ctrlx1, ctrly1), new PointF(ctrlx2, ctrly2), new PointF(x2, y2));
+            }
 
-            img.Mutate(ctx =>
+            //画验证码字符串
             {
-                var rnd = new Random();
-                //画噪线
-                for (int i = 0; i < 1; i++)
+                Color color;
+                Font font;
+                string fontName;
+                FontFamily fontFamily;
+                for (int i = 0; i < length; i++)
                 {
-                    int x1 = rnd.Next(width), y1 = rnd.Next(height);
-                    int x2 = rnd.Next(width), y2 = rnd.Next(height);
-                    int ctrlx1 = rnd.Next(width / 4, width / 4 * 3), ctrly1 = rnd.Next(5, height - 5);
-                    int ctrlx2 = rnd.Next(width / 4, width / 4 * 3), ctrly2 = rnd.Next(5, height - 5);
-                    Color color = colors[rnd.Next(colors.Length)];
-                    ctx.DrawBeziers(new DrawingOptions
+                    fontName = fonts[rnd.Next(fonts.Length)];
+                    fontFamily = SystemFonts.Families.Where(a => a.Name == fontName).FirstOrDefault();
+                    fontFamily = fontFamily.Name.NotNull() ? fontFamily : SystemFonts.Families.FirstOrDefault();
+                    font = new Font(fontFamily, fontSize);
+                    color = colors[rnd.Next(colors.Length)];
+                    ctx.DrawText(new DrawingOptions
                     {
                         GraphicsOptions = new GraphicsOptions
                         {
                             BlendPercentage = 1
                         }
-                    }, color, 1, new PointF(x1, y1), new PointF(ctrlx1, ctrly1), new PointF(ctrlx2, ctrly2), new PointF(x2, y2));
+                    }, chars[i].ToString(), font, color, new PointF((float)i * 24 + 2, 0));
                 }
+            }
+        });
 
-                //画验证码字符串
-                {
-                    Color color;
-                    Font font;
-                    string fontName;
-                    FontFamily fontFamily;
-                    for (int i = 0; i < length; i++)
-                    {
-                        fontName = fonts[rnd.Next(fonts.Length)];
-                        fontFamily = SystemFonts.Families.Where(a => a.Name == fontName).FirstOrDefault();
-                        fontFamily = fontFamily.Name.NotNull() ? fontFamily : SystemFonts.Families.FirstOrDefault();
-                        font = new Font(fontFamily, fontSize);
-                        color = colors[rnd.Next(colors.Length)];
-                        ctx.DrawText(new DrawingOptions
-                        {
-                            GraphicsOptions = new GraphicsOptions
-                            {
-                                BlendPercentage = 1
-                            }
-                        }, chars[i].ToString(), font, color, new PointF((float)i * 24 + 2, 0));
-                    }
-                }
-            });
-
-            //将验证码图片写入内存流,并将其以 "image/Png" 格式输出
-            using var ms = new MemoryStream();
-            img.SaveAsPng(ms);
-            return ms.ToArray();
-        }
+        //将验证码图片写入内存流,并将其以 "image/Png" 格式输出
+        using var ms = new MemoryStream();
+        img.SaveAsPng(ms);
+        return ms.ToArray();
+    }
 
-        public string GetBase64String(out string code, int length = 4)
-        {
-            return Convert.ToBase64String(Draw(out code, length));
-        }
+    public string GetBase64String(out string code, int length = 4)
+    {
+        return Convert.ToBase64String(Draw(out code, length));
     }
 }

+ 25 - 0
src/platform/ZhonTai.Admin/Tools/TaskScheduler/HostAppOptions.cs

@@ -0,0 +1,25 @@
+using IdleScheduler;
+using System;
+
+namespace ZhonTai.Admin.Tools.TaskScheduler;
+
+/// <summary>
+/// TaskScheduler配置
+/// </summary>
+public class TaskSchedulerOptions
+{
+    /// <summary>
+    /// 数据库实例
+    /// </summary>
+    public IFreeSql FreeSql { get; set; }
+
+    /// <summary>
+    /// 配置FreeSql
+    /// </summary>
+    public Action<IFreeSql> ConfigureFreeSql { get; set; }
+
+    /// <summary>
+    /// 任务处理器
+    /// </summary>
+    public ITaskHandler TaskHandler{ get; set; } = null;
+}

+ 34 - 0
src/platform/ZhonTai.Admin/Tools/TaskScheduler/TaskHandler.cs

@@ -0,0 +1,34 @@
+using IdleScheduler;
+using System.Collections.Generic;
+
+namespace ZhonTai.Admin.Tools.TaskScheduler;
+
+public class TaskHandler : ITaskHandler
+{
+    readonly IFreeSql _fsql;
+
+    public TaskHandler(IFreeSql fsql)
+    {
+        _fsql = fsql;
+    }
+
+    public IEnumerable<TaskInfo> LoadAll() => _fsql.Select<TaskInfo>().Where(a => a.Status == TaskStatus.Running && (a.Round < 0 || a.CurrentRound < a.Round)).ToList();
+    public TaskInfo Load(string id) => _fsql.Select<TaskInfo>().Where(a => a.Id == id).First();
+    public void OnAdd(TaskInfo task) => _fsql.Insert<TaskInfo>().NoneParameter().AppendData(task).ExecuteAffrows();
+    public void OnRemove(TaskInfo task) => _fsql.Delete<TaskInfo>().Where(a => a.Id == task.Id).ExecuteAffrows();
+    public void OnExecuted(Scheduler scheduler, TaskInfo task, TaskLog result)
+    {
+        _fsql.Transaction(() =>
+        {
+            _fsql.Update<TaskInfo>().NoneParameter().SetSource(task)
+                .UpdateColumns(a => new { a.CurrentRound, a.ErrorTimes, a.LastRunTime, a.Status })
+                .ExecuteAffrows();
+            _fsql.Insert<TaskLog>().NoneParameter().AppendData(result).ExecuteAffrows();
+        });
+    }
+
+    public virtual void OnExecuting(Scheduler scheduler, TaskInfo task)
+    {
+
+    }
+}

+ 74 - 0
src/platform/ZhonTai.Admin/Tools/TaskScheduler/TaskSchedulerServiceExtensions.cs

@@ -0,0 +1,74 @@
+using IdleScheduler;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using ZhonTai.Admin.Core.Configs;
+
+namespace ZhonTai.Admin.Tools.TaskScheduler;
+
+public static class TaskSchedulerServiceExtensions
+{
+    public static IServiceProvider ServiceProvider { get; private set; }
+
+    /// <summary>
+    /// 添加任务调度
+    /// </summary>
+    /// <param name="services"></param>
+    /// <param name="configureOptions"></param>
+    public static void AddTaskScheduler(this IServiceCollection services, Action<TaskSchedulerOptions> configureOptions = null)
+    {
+        ServiceProvider = services.BuildServiceProvider();
+        var options = new TaskSchedulerOptions()
+        {
+            FreeSql = ServiceProvider.GetService<IFreeSql>()
+        };
+        configureOptions?.Invoke(options);
+
+        var freeSql = options.FreeSql;
+
+        freeSql.CodeFirst
+        .ConfigEntity<TaskInfo>(a =>
+        {
+            a.Name("ad_task");
+            a.Property(b => b.Id).IsPrimary(true);
+            a.Property(b => b.Body).StringLength(-1);
+            a.Property(b => b.Interval).MapType(typeof(string));
+            a.Property(b => b.IntervalArgument).StringLength(1024);
+            a.Property(b => b.Status).MapType(typeof(string));
+            a.Property(b => b.CreateTime).ServerTime(DateTimeKind.Local);
+            a.Property(b => b.LastRunTime).ServerTime(DateTimeKind.Local);
+        })
+        .ConfigEntity<TaskLog>(a =>
+        {
+            a.Name("ad_task_log");
+            a.Property(b => b.Exception).StringLength(-1);
+            a.Property(b => b.Remark).StringLength(-1);
+            a.Property(b => b.CreateTime).ServerTime(DateTimeKind.Local);
+        });
+
+        options.ConfigureFreeSql?.Invoke(freeSql);
+
+        var dbConfig = ServiceProvider.GetService<DbConfig>();
+        if (dbConfig.SyncStructure)
+        {
+            freeSql.CodeFirst.SyncStructure<TaskInfo>();
+            freeSql.CodeFirst.SyncStructure<TaskLog>();
+        }
+
+        if(options.TaskHandler != null)
+        {
+            //开启任务
+            var scheduler = new Scheduler(options.TaskHandler);
+            services.AddSingleton(scheduler);
+        }
+    }
+
+    /// <summary>
+    /// 使用任务调度
+    /// </summary>
+    /// <param name="app"></param>
+    public static void UseTaskScheduler(this IApplicationBuilder app)
+    {
+        ServiceProvider = app.ApplicationServices;
+    }
+}

+ 1 - 0
src/platform/ZhonTai.Admin/ZhonTai.Admin.csproj

@@ -19,6 +19,7 @@
 		<PackageReference Include="FreeSql" Version="3.2.665" />
 		<PackageReference Include="FreeSql.Repository" Version="3.2.665" />
 		<PackageReference Include="IdleBus" Version="1.5.2" />
+		<PackageReference Include="IdleScheduler" Version="1.5.2.9" />
 		<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
 		<PackageReference Include="Mapster" Version="7.3.0" />
 		<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />

+ 65 - 32
src/platform/ZhonTai.Admin/ZhonTai.Admin.xml

@@ -555,6 +555,36 @@
             允许所有源访问策略
             </summary>
         </member>
+        <member name="T:ZhonTai.Admin.Core.Consts.CacheKeys">
+            <summary>
+            缓存键
+            </summary>
+        </member>
+        <member name="F:ZhonTai.Admin.Core.Consts.CacheKeys.CaptchaKey">
+            <summary>
+            验证码 admin:captcha:guid
+            </summary>
+        </member>
+        <member name="F:ZhonTai.Admin.Core.Consts.CacheKeys.PassWordEncryptKey">
+            <summary>
+            密码加密 admin:password:encrypt:guid
+            </summary>
+        </member>
+        <member name="F:ZhonTai.Admin.Core.Consts.CacheKeys.UserPermissions">
+            <summary>
+            用户权限 admin:user:permissions:用户主键
+            </summary>
+        </member>
+        <member name="F:ZhonTai.Admin.Core.Consts.CacheKeys.UserInfo">
+            <summary>
+            用户信息 admin:user:info:用户主键
+            </summary>
+        </member>
+        <member name="F:ZhonTai.Admin.Core.Consts.CacheKeys.TenantInfo">
+            <summary>
+            租户信息 admin:tenant:info:租户主键
+            </summary>
+        </member>
         <member name="T:ZhonTai.Admin.Core.Dbs.MySqlDb">
             <summary>
             多数据库命名
@@ -1376,9 +1406,9 @@
             配置FreeSql构建器
             </summary>
         </member>
-        <member name="P:ZhonTai.Admin.Core.Startup.HostAppOptions.ConfigureEntity">
+        <member name="P:ZhonTai.Admin.Core.Startup.HostAppOptions.ConfigureFreeSql">
             <summary>
-            配置实体
+            配置FreeSql
             </summary>
         </member>
         <member name="P:ZhonTai.Admin.Core.Startup.HostAppOptions.ConfigureDynamicApi">
@@ -3458,36 +3488,6 @@
             <param name="serviceType">服务类型</param>
             <returns></returns>
         </member>
-        <member name="T:ZhonTai.Admin.Services.Contracts.CacheKey">
-            <summary>
-            缓存键
-            </summary>
-        </member>
-        <member name="F:ZhonTai.Admin.Services.Contracts.CacheKey.CaptchaKey">
-            <summary>
-            验证码 admin:captcha:guid
-            </summary>
-        </member>
-        <member name="F:ZhonTai.Admin.Services.Contracts.CacheKey.PassWordEncryptKey">
-            <summary>
-            密码加密 admin:password:encrypt:guid
-            </summary>
-        </member>
-        <member name="F:ZhonTai.Admin.Services.Contracts.CacheKey.UserPermissions">
-            <summary>
-            用户权限 admin:user:permissions:用户主键
-            </summary>
-        </member>
-        <member name="F:ZhonTai.Admin.Services.Contracts.CacheKey.UserInfo">
-            <summary>
-            用户信息 admin:user:info:用户主键
-            </summary>
-        </member>
-        <member name="F:ZhonTai.Admin.Services.Contracts.CacheKey.TenantInfo">
-            <summary>
-            租户信息 admin:tenant:info:租户主键
-            </summary>
-        </member>
         <member name="T:ZhonTai.Admin.Services.Cache.CacheService">
             <summary>
             缓存服务
@@ -6388,6 +6388,39 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="T:ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerOptions">
+            <summary>
+            TaskScheduler配置
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerOptions.FreeSql">
+            <summary>
+            数据库实例
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerOptions.ConfigureFreeSql">
+            <summary>
+            配置FreeSql
+            </summary>
+        </member>
+        <member name="P:ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerOptions.TaskHandler">
+            <summary>
+            任务处理器
+            </summary>
+        </member>
+        <member name="M:ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerServiceExtensions.AddTaskScheduler(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerOptions})">
+            <summary>
+            添加任务调度
+            </summary>
+            <param name="services"></param>
+            <param name="configureOptions"></param>
+        </member>
+        <member name="M:ZhonTai.Admin.Tools.TaskScheduler.TaskSchedulerServiceExtensions.UseTaskScheduler(Microsoft.AspNetCore.Builder.IApplicationBuilder)">
+            <summary>
+            使用任务调度
+            </summary>
+            <param name="app"></param>
+        </member>
         <member name="M:FreeSqlDbContextExtensions.GetRepositoryBase``2(IFreeSql,System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}})">
             <summary>
             返回默认仓库类

+ 4 - 4
src/tests/ZhonTai.Tests/BaseControllerTest.cs

@@ -12,9 +12,9 @@ using ZhonTai.Admin.Tools.Cache;
 using ZhonTai.Admin.Tools.Captcha;
 using ZhonTai.Admin.Core.Configs;
 using ZhonTai.Admin.Services.Auth.Dto;
-using ZhonTai.Admin.Services.Contracts;
 using ZhonTai.Admin.Core.Enums;
 using System.Collections.Generic;
+using ZhonTai.Admin.Core.Consts;
 
 namespace ZhonTai.Tests
 {
@@ -168,10 +168,10 @@ namespace ZhonTai.Tests
                 };
                 if (_appConfig.VarifyCode.Enable)
                 {
-                    var res = await _captcha.GetAsync(CacheKey.CaptchaKey);
-                    var captchaKey = string.Format(CacheKey.CaptchaKey, res.Token);
+                    var res = await _captcha.GetAsync(CacheKeys.CaptchaKey);
+                    var captchaKey = string.Format(CacheKeys.CaptchaKey, res.Token);
                     var captchaData = await _cache.GetAsync(captchaKey);
-                    input.Captcha = new CaptchaInput { CaptchaKey = CacheKey.CaptchaKey, Token = res.Token, Data = JsonConvert.SerializeObject(new { X = captchaData }) };
+                    input.Captcha = new CaptchaInput { CaptchaKey = CacheKeys.CaptchaKey, Token = res.Token, Data = JsonConvert.SerializeObject(new { X = captchaData }) };
                 }
             }