Browse Source

初始化

lcl 10 months ago
commit
fca1daf930
100 changed files with 9140 additions and 0 deletions
  1. BIN
      .DS_Store
  2. 4 0
      .gitignore
  3. 53 0
      .vscode/launch.json
  4. 3 0
      .vscode/settings.json
  5. 42 0
      .vscode/tasks.json
  6. 100 0
      App/App.cs
  7. 25 0
      App/InternalApp.cs
  8. 32 0
      Attribute/AppServiceAttribute.cs
  9. 35 0
      Attribute/LogAttribute.cs
  10. 93 0
      Base/AppSettings.cs
  11. 59 0
      Base/GlobalConstant.cs
  12. 150 0
      Common/Cache/CacheHelper.cs
  13. 17 0
      Common/Cache/RedisServer.cs
  14. 1173 0
      Common/Function.cs
  15. 37 0
      Common/JsonConverterUtil.cs
  16. 167 0
      Common/JwtUtil.cs
  17. 48 0
      Common/StringConverter.cs
  18. 142 0
      Common/Tools.cs
  19. 134 0
      Common/dbconn.cs
  20. 145 0
      Constant/Helper/DateTimeHelper.cs
  21. 84 0
      Constant/HttpStatus.cs
  22. BIN
      Controllers/.DS_Store
  23. 234 0
      Controllers/Base/BaseController.cs
  24. 71 0
      Controllers/Base/HomeController.cs
  25. 170 0
      Controllers/Base/SysDeptController.cs
  26. 169 0
      Controllers/Base/SysLoginController.cs
  27. 218 0
      Controllers/Base/SysMenuController.cs
  28. 139 0
      Controllers/Base/SysPostController.cs
  29. 206 0
      Controllers/Base/SysRoleController.cs
  30. 223 0
      Controllers/Base/SysUserController.cs
  31. 91 0
      Controllers/Base/SysUserRoleController.cs
  32. 84 0
      Extensions/AppServiceExtensions.cs
  33. 42 0
      Extensions/EntityExtension.cs
  34. 446 0
      Extensions/Extension.Convert.cs
  35. 86 0
      Extensions/Extension.Enum.cs
  36. 18 0
      Extensions/Extension.Exception.cs
  37. 110 0
      Extensions/Extension.Linq.cs
  38. 44 0
      Extensions/Extension.Validate.cs
  39. 241 0
      Extensions/HttpContextExtension.cs
  40. 24 0
      Extensions/IPRateExtension.cs
  41. 35 0
      Extensions/RequestLimitExtension.cs
  42. 232 0
      Extensions/StringExtension.cs
  43. 92 0
      Filters/ActionPermissionFilter.cs
  44. 72 0
      Filters/AuthorizationFilter.cs
  45. 233 0
      Filters/GlobalActionMonitor.cs
  46. 76 0
      Filters/VerifyAttribute.cs
  47. 3 0
      GlobalUsing.cs
  48. 37 0
      KxsOperateAdminServer.csproj
  49. 142 0
      Middleware/GlobalExceptionMiddleware.cs
  50. BIN
      Model/.DS_Store
  51. 127 0
      Model/Base/ApiResult.cs
  52. 128 0
      Model/Base/OptionsSetting.cs
  53. 46 0
      Model/Base/PagedInfo.cs
  54. 37 0
      Model/Base/PagerInfo.cs
  55. 45 0
      Model/Base/SysBase.cs
  56. 96 0
      Model/Base/SysCity.cs
  57. 78 0
      Model/Base/SysDept.cs
  58. 82 0
      Model/Base/SysDict.cs
  59. 103 0
      Model/Base/SysDictItem.cs
  60. 89 0
      Model/Base/SysFile.cs
  61. 124 0
      Model/Base/SysLog.cs
  62. 58 0
      Model/Base/SysLogininfor.cs
  63. 151 0
      Model/Base/SysMenu.cs
  64. 131 0
      Model/Base/SysOauthClientDetails.cs
  65. 138 0
      Model/Base/SysParam.cs
  66. 47 0
      Model/Base/SysPermission.cs
  67. 82 0
      Model/Base/SysPost.cs
  68. 103 0
      Model/Base/SysPublicParam.cs
  69. 97 0
      Model/Base/SysRole.cs
  70. 13 0
      Model/Base/SysRoleDept.cs
  71. 26 0
      Model/Base/SysRoleMenu.cs
  72. 184 0
      Model/Base/SysUser.cs
  73. 26 0
      Model/Base/SysUserPost.cs
  74. 26 0
      Model/Base/SysUserRole.cs
  75. 44 0
      Model/Base/TokenModel.cs
  76. 115 0
      Model/Base/UserConstants.cs
  77. BIN
      Model/Database/.DS_Store
  78. 82 0
      Model/Database/ApiGroup.cs
  79. BIN
      Model/Dto/.DS_Store
  80. 31 0
      Model/Dto/Base/LoginBodyDto.cs
  81. 20 0
      Model/Dto/Base/LoginDto.cs
  82. 97 0
      Model/Dto/Base/MenuDto.cs
  83. 25 0
      Model/Dto/Base/PhoneLoginDto.cs
  84. 35 0
      Model/Dto/Base/RegisterDto.cs
  85. 28 0
      Model/Dto/Base/RoleMenuExportDto.cs
  86. 28 0
      Model/Dto/Base/RoleUserDto.cs
  87. 33 0
      Model/Dto/Base/SysDeptDto.cs
  88. 27 0
      Model/Dto/Base/SysRoleDto.cs
  89. 57 0
      Model/Dto/Base/SysUserDto.cs
  90. 63 0
      Model/Enums/BusinessType.cs
  91. 15 0
      Model/Enums/MenuStatus.cs
  92. 19 0
      Model/Enums/MenuType.cs
  93. 4 0
      Model/Enums/ProteryConstant.cs
  94. 40 0
      Model/Enums/StoreType.cs
  95. 48 0
      Model/Exception/CustomException.cs
  96. 46 0
      Model/Exception/ResultCode.cs
  97. BIN
      Model/Vo/.DS_Store
  98. 33 0
      Model/Vo/Base/OAuthVo.cs
  99. 74 0
      Model/Vo/Base/RouterVo.cs
  100. 58 0
      Model/Vo/Base/TreeSelectVo.cs

BIN
.DS_Store


+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/bin
+/publish
+/obj
+/appsettings.json

+ 53 - 0
.vscode/launch.json

@@ -0,0 +1,53 @@
+{
+    // 使用 IntelliSense 了解相关属性。 
+    // 悬停以查看现有属性的描述。
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [{
+            "name": ".NET Core Launch (web)",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build",
+            "program": "${workspaceFolder}/bin/Debug/net7.0/KxsOperateAdminServer.dll",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "stopAtEntry": false,
+            "serverReadyAction": {
+                "action": "openExternally",
+                "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)"
+            },
+            "env": {
+                "ASPNETCORE_ENVIRONMENT": "Development"
+            },
+            "sourceFileMap": {
+                "/Views": "${workspaceFolder}/Views"
+            }
+        },
+        {
+            "name": ".NET Core Launch (pro)",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build",
+            "program": "${workspaceFolder}/bin/Debug/net7.0/KxsOperateAdminServer.dll",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "stopAtEntry": false,
+            "serverReadyAction": {
+                "action": "openExternally",
+                "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)"
+            },
+            "env": {
+                "ASPNETCORE_ENVIRONMENT": "Production"
+            },
+            "sourceFileMap": {
+                "/Views": "${workspaceFolder}/Views"
+            }
+        },
+        {
+            "name": ".NET Core Attach",
+            "type": "coreclr",
+            "request": "attach",
+            "processId": "${command:pickProcess}"
+        }
+    ]
+}

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "dotnet.defaultSolution": "KxsOperateAdminServer.sln"
+}

+ 42 - 0
.vscode/tasks.json

@@ -0,0 +1,42 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/KxsOperateAdminServer.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "publish",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "publish",
+                "${workspaceFolder}/KxsOperateAdminServer.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "watch",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "watch",
+                "run",
+                "${workspaceFolder}/KxsOperateAdminServer.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "problemMatcher": "$msCompile"
+        }
+    ]
+}

+ 100 - 0
App/App.cs

@@ -0,0 +1,100 @@
+using Infrastructure.Model;
+using Microsoft.Extensions.Options;
+using System.Security.Claims;
+
+namespace Infrastructure
+{
+    public static class App
+    {
+        /// <summary>
+        /// 全局配置文件
+        /// </summary>
+        public static OptionsSetting OptionsSetting => CatchOrDefault(() => ServiceProvider?.GetService<IOptions<OptionsSetting>>()?.Value);
+
+        /// <summary>
+        /// 服务提供器
+        /// </summary>
+        public static IServiceProvider ServiceProvider => InternalApp.ServiceProvider;
+        /// <summary>
+        /// 获取请求上下文
+        /// </summary>
+        public static HttpContext HttpContext => CatchOrDefault(() => ServiceProvider?.GetService<IHttpContextAccessor>()?.HttpContext);
+        /// <summary>
+        /// 获取请求上下文用户
+        /// </summary>
+        public static ClaimsPrincipal User => HttpContext?.User;
+        /// <summary>
+        /// 获取用户名
+        /// </summary>
+        public static string UserName => User?.Identity?.Name;
+        /// <summary>
+        /// 获取Web主机环境
+        /// </summary>
+        public static IWebHostEnvironment WebHostEnvironment => InternalApp.WebHostEnvironment; 
+        /// <summary>
+        /// 获取全局配置
+        /// </summary>
+        public static IConfiguration Configuration => CatchOrDefault(() => InternalApp.Configuration, new ConfigurationBuilder().Build());
+        /// <summary>
+        /// 获取请求生命周期的服务
+        /// </summary>
+        /// <typeparam name="TService"></typeparam>
+        /// <returns></returns>
+        public static TService GetService<TService>()
+            where TService : class
+        {
+            return GetService(typeof(TService)) as TService;
+        }
+
+        /// <summary>
+        /// 获取请求生命周期的服务
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static object GetService(Type type)
+        {
+            return ServiceProvider.GetService(type);
+        }
+
+        /// <summary>
+        /// 获取请求生命周期的服务
+        /// </summary>
+        /// <typeparam name="TService"></typeparam>
+        /// <returns></returns>
+        public static TService GetRequiredService<TService>()
+            where TService : class
+        {
+            return GetRequiredService(typeof(TService)) as TService;
+        }
+
+        /// <summary>
+        /// 获取请求生命周期的服务
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static object GetRequiredService(Type type)
+        {
+            return ServiceProvider.GetRequiredService(type);
+        }
+
+        /// <summary>
+        /// 处理获取对象异常问题
+        /// </summary>
+        /// <typeparam name="T">类型</typeparam>
+        /// <param name="action">获取对象委托</param>
+        /// <param name="defaultValue">默认值</param>
+        /// <returns>T</returns>
+        private static T CatchOrDefault<T>(Func<T> action, T defaultValue = null)
+            where T : class
+        {
+            try
+            {
+                return action();
+            }
+            catch
+            {
+                return defaultValue ?? null;
+            }
+        }
+    }
+}

+ 25 - 0
App/InternalApp.cs

@@ -0,0 +1,25 @@
+namespace Infrastructure
+{
+    public static class InternalApp
+    {
+        /// <summary>
+        /// 应用服务
+        /// </summary>
+        public static IServiceProvider ServiceProvider;
+
+        /// <summary>
+        /// 全局配置构建器
+        /// </summary>
+        public static IConfiguration Configuration;
+
+        /// <summary>
+        /// 获取Web主机环境
+        /// </summary>
+        public static IWebHostEnvironment WebHostEnvironment;
+
+        /// <summary>
+        /// 获取泛型主机环境
+        /// </summary>
+        //public static IHostEnvironment HostEnvironment;
+    }
+}

+ 32 - 0
Attribute/AppServiceAttribute.cs

@@ -0,0 +1,32 @@
+namespace Attribute
+{
+    /// <summary>
+    /// 参考地址:https://www.cnblogs.com/kelelipeng/p/10643556.html
+    /// 标记服务
+    /// 如何使用?
+    /// 1、如果服务是本身 直接在类上使用[AppService]
+    /// 2、如果服务是接口 在类上使用 [AppService(ServiceType = typeof(实现接口))]
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
+    public class AppServiceAttribute : System.Attribute
+    {
+        /// <summary>
+        /// 服务声明周期
+        /// 不给默认值的话注册的是AddSingleton
+        /// </summary>
+        public LifeTime ServiceLifetime { get; set; } = LifeTime.Scoped;
+        /// <summary>
+        /// 指定服务类型
+        /// </summary>
+        public Type ServiceType { get; set; }
+        /// <summary>
+        /// 是否可以从第一个接口获取服务类型
+        /// </summary>
+        public bool InterfaceServiceType { get; set; }
+    }
+
+    public enum LifeTime
+    {
+        Transient, Scoped, Singleton
+    }
+}

+ 35 - 0
Attribute/LogAttribute.cs

@@ -0,0 +1,35 @@
+using Enums;
+
+namespace Attribute
+{
+    /// <summary>
+    /// 自定义操作日志记录注解
+    /// </summary>
+    public class LogAttribute : System.Attribute
+    {
+        public string Title { get; set; }
+        public BusinessType BusinessType { get; set; }
+        /// <summary>
+        /// 是否保存请求数据
+        /// </summary>
+        public bool IsSaveRequestData { get; set; } = true;
+        /// <summary>
+        /// 是否保存返回数据
+        /// </summary>
+        public bool IsSaveResponseData { get; set; } = true;
+
+        public LogAttribute() { }
+
+        public LogAttribute(string name)
+        {
+            Title = name;
+        }
+        public LogAttribute(string name, BusinessType businessType, bool saveRequestData = true, bool saveResponseData = true)
+        {
+            Title = name;
+            BusinessType = businessType;
+            IsSaveRequestData = saveRequestData;
+            IsSaveResponseData = saveResponseData;
+        }
+    }
+}

+ 93 - 0
Base/AppSettings.cs

@@ -0,0 +1,93 @@
+namespace Base
+{
+    public class AppSettings
+    {
+        static IConfiguration Configuration { get; set; }
+
+        public AppSettings(IConfiguration configuration)
+        {
+            Configuration = configuration;
+        }
+
+        /// <summary>
+        /// 封装要操作的字符
+        /// </summary>
+        /// <param name="sections">节点配置</param>
+        /// <returns></returns>
+        public static string App(params string[] sections)
+        {
+            try
+            {
+                if (sections.Any())
+                {
+                    return Configuration[string.Join(":", sections)];
+                }
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+            }
+
+            return "";
+        }
+
+        /// <summary>
+        /// 递归获取配置信息数组
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="sections"></param>
+        /// <returns></returns>
+        public static List<T> App<T>(params string[] sections)
+        {
+            List<T> list = new();
+            try
+            {
+                if (Configuration != null && sections.Any())
+                {
+                    Configuration.Bind(string.Join(":", sections), list);
+                }
+            }
+            catch
+            {
+                return list;
+            }
+            return list;
+        }
+        public static T Bind<T>(string key, T t)
+        {
+            Configuration.Bind(key, t);
+            return t;
+        }
+
+
+        public static T GetAppConfig<T>(string key, T defaultValue = default)
+        {
+            T setting = (T)Convert.ChangeType(Configuration[key], typeof(T));
+            var value = setting;
+            if (setting == null)
+                value = defaultValue;
+            return value;
+        }
+
+        /// <summary>
+        /// 获取配置文件 
+        /// </summary>
+        /// <param name="key">eg: WeChat:Token</param>
+        /// <returns></returns>
+        public static string GetConfig(string key)
+        {
+            return Configuration[key];
+        }
+
+        /// <summary>
+        /// 获取配置节点并转换成指定类型
+        /// </summary>
+        /// <typeparam name="T">节点类型</typeparam>
+        /// <param name="key">节点路径</param>
+        /// <returns>节点类型实例</returns>
+        public static T Get<T>(string key)
+        {
+            return Configuration.GetSection(key).Get<T>();
+        }
+    }
+}

+ 59 - 0
Base/GlobalConstant.cs

@@ -0,0 +1,59 @@
+namespace Base
+{
+    /// <summary>
+    /// 全局静态常量
+    /// </summary>
+    public class GlobalConstant
+    {
+        /// <summary>
+        /// 管理员权限
+        /// </summary>
+        public static string AdminPerm = "*:*:*";
+        /// <summary>
+        /// 管理员角色
+        /// </summary>
+        public static string AdminRole = "admin";
+        /// <summary>
+        /// 开发版本API映射路径
+        /// </summary>
+        public static string DevApiProxy = "/dev-api/";
+        /// <summary>
+        /// 用户权限缓存key
+        /// </summary>
+        public static string UserPermKEY = "CACHE-USER-PERM";
+
+        /// <summary>
+        /// 欢迎语
+        /// </summary>
+        public static string[] WelcomeMessages = new string[] {
+            "祝你开心每一天!",
+            "忙碌了一周,停一停脚步!",
+            "世间美好,与你环环相扣!",
+            "永远相信美好的事情即将发生!",
+            "每一天,遇见更好的自己!",
+            "保持热爱,奔赴山海!",
+            "生活明朗,万物可爱!",
+            "愿每一天醒来都是美好的开始!",
+            "没有希望的地方,就没有奋斗!",
+            "我最珍贵的时光都行走在路上!",
+            "成功,往往住在失败的隔壁!",
+            "人只要不失去方向,就不会失去自己!",
+            "每条堵住的路,都有一个出口!",
+            "没有谁能击垮你,除非你自甘堕落!",
+            "微笑着的人并非没有痛苦!",
+            "生活变的再糟糕,也不妨碍我变得更好!",
+            "你要悄悄努力,然后惊艳众人!",
+            "人与人之间最大的信任是精诚相见",
+            "人生就像爬坡,要一步一步来。",
+            "今天的目标完成了吗?",
+            "高效工作,告别996",
+            "销售是从别人拒绝开始的!"
+        };
+
+
+
+
+        public static string ApiKey = "CBTU1dD4Kd5pyiGWTsI10jRQ3SvKusSV";
+        public static string ApiIv = "DYgjCEIMVrj2W9xN";
+    }
+}

+ 150 - 0
Common/Cache/CacheHelper.cs

@@ -0,0 +1,150 @@
+using Microsoft.Extensions.Caching.Memory;
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Reflection;
+
+namespace Common
+{
+    public class CacheHelper
+    {
+        public static MemoryCache Cache { get; set; }
+        static CacheHelper()
+        {
+            Cache = new MemoryCache(new MemoryCacheOptions
+            {
+                //SizeLimit = 1024
+            });
+        }
+
+        /// <summary>
+        /// 获取缓存
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public static T GetCache<T>(string key) where T : class
+        {
+            if (key == null)
+                throw new ArgumentNullException(nameof(key));
+            //return Cache.Get(key) as T; //或者
+            return Cache.Get<T>(key);
+        }
+
+        /// <summary>
+        /// 获取缓存
+        /// </summary>
+        /// <param name="CacheKey"></param>
+        /// <returns></returns>
+        public static object GetCache(string CacheKey)
+        {
+            return Cache.Get<object>(CacheKey);
+        }
+
+        public static object Get(string CacheKey)
+        {
+            return Cache.Get(CacheKey);
+        }
+
+        /// <summary>
+        /// 设置缓存,永久缓存
+        /// </summary>
+        /// <param name="CacheKey">key</param>
+        /// <param name="objObject">值</param>
+        public static object SetCache(string CacheKey, object objObject)
+        {
+            return Cache.Set(CacheKey, objObject);
+        }
+
+        /// <summary>
+        /// 设置缓存
+        /// </summary>
+        /// <param name="CacheKey">key</param>
+        /// <param name="objObject">值</param>
+        /// <param name="Timeout">过期时间(分钟)</param>
+        public static object SetCache(string CacheKey, object objObject, int Timeout)
+        {
+            return Cache.Set(CacheKey, objObject, DateTime.Now.AddMinutes(Timeout));
+        }
+
+        /// <summary>
+        /// 设置缓存(秒)
+        /// </summary>
+        /// <param name="CacheKey">key</param>
+        /// <param name="objObject">值</param>
+        /// <param name="Timeout">过期时间(秒)</param>
+        public static void SetCaches(string CacheKey, object objObject, int Timeout)
+        {
+            Cache.Set(CacheKey, objObject, DateTime.Now.AddSeconds(Timeout));
+        }
+
+        /// <summary>
+        /// 设置缓存
+        /// </summary>
+        /// <param name="CacheKey">key</param>
+        /// <param name="objObject">值</param>
+        /// <param name="absoluteExpiration">过期时间</param>
+        /// <param name="slidingExpiration">过期时间间隔</param>
+        public static object SetCache(string CacheKey, object objObject, DateTime absoluteExpiration, TimeSpan slidingExpiration)
+        {
+            return Cache.Set(CacheKey, objObject, absoluteExpiration);
+        }
+
+        /// <summary>
+        /// 设定绝对的过期时间
+        /// </summary>
+        /// <param name="CacheKey"></param>
+        /// <param name="objObject"></param>
+        /// <param name="Seconds">超过多少秒后过期</param>
+        public static void SetCacheDateTime(string CacheKey, object objObject, long Seconds)
+        {
+            Cache.Set(CacheKey, objObject, DateTime.Now.AddSeconds(Seconds));
+        }
+
+        /// <summary>
+        /// 删除缓存
+        /// </summary>
+        /// <param name="key">key</param>
+        public static void Remove(string key)
+        {
+            Cache.Remove(key);
+        }
+
+        /// <summary>
+        /// 验证缓存项是否存在
+        /// </summary>
+        /// <param name="key">缓存Key</param>
+        /// <returns></returns>
+        public static bool Exists(string key)
+        {
+            if (key == null)
+                throw new ArgumentNullException(nameof(key));
+            return Cache.TryGetValue(key, out _);
+        }
+
+
+        /// <summary>
+        /// 获取所有缓存键
+        /// </summary>
+        /// <returns></returns>
+        public static List<string> GetCacheKeys()
+        {
+            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
+            //var entries = Cache.GetType().GetField("_entries", flags).GetValue(Cache);
+
+            //.net7需要这样写 
+            var coherentState = Cache.GetType().GetField("_coherentState", flags).GetValue(Cache);
+            var entries = coherentState.GetType().GetField("_entries", flags).GetValue(coherentState);
+
+            var keys = new List<string>();
+            if (entries is not IDictionary cacheItems) return keys;
+            foreach (DictionaryEntry cacheItem in cacheItems)
+            {
+                keys.Add(cacheItem.Key.ToString());
+                //Console.WriteLine("缓存key=" +cacheItem.Key);
+            }
+            return keys;
+        }
+    }
+}
+

+ 17 - 0
Common/Cache/RedisServer.cs

@@ -0,0 +1,17 @@
+using Base;
+using CSRedis;
+
+namespace Common
+{
+    public class RedisServer
+    {
+        public static CSRedisClient Cache;
+        public static CSRedisClient Session;
+
+        public static void Initalize()
+        {
+            Cache = new CSRedisClient(AppSettings.GetConfig("RedisServer:Cache"));
+            Session = new CSRedisClient(AppSettings.GetConfig("RedisServer:Session"));
+        }
+    }
+}

+ 1173 - 0
Common/Function.cs

@@ -0,0 +1,1173 @@
+using System;
+using System.Data;
+using System.Web;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Net.Mail;
+using System.Net;
+using LitJson;
+using Microsoft.AspNetCore.Http;
+using ThoughtWorks.QRCode.Codec;
+
+namespace Common
+{
+    public class Function
+    {
+
+        /// <summary>
+        /// hmacSha1算法加密(生成长度40)
+        /// </summary>
+        /// <param name="encryptText">加密明文</param>
+        /// <param name="encryptKey">加密密钥</param>
+        /// <returns></returns>
+        public static string hmacSha1(string encryptText, string encryptKey)
+        {
+            HMACSHA1 myHMACSHA1 = new HMACSHA1(Encoding.UTF8.GetBytes(encryptKey));
+            byte[] RstRes = myHMACSHA1.ComputeHash(Encoding.UTF8.GetBytes(encryptText));
+
+            StringBuilder EnText = new StringBuilder();
+            foreach (byte Byte in RstRes)
+            {
+                EnText.AppendFormat("{0:x2}", Byte);
+            }
+            return EnText.ToString();
+        }
+
+        public static string hmacmd5(string encryptText, string encryptKey)
+        {
+            HMACMD5 myHMACSHA1 = new HMACMD5(Encoding.UTF8.GetBytes(encryptKey));
+            byte[] RstRes = myHMACSHA1.ComputeHash(Encoding.UTF8.GetBytes(encryptText));
+
+            StringBuilder EnText = new StringBuilder();
+            foreach (byte Byte in RstRes)
+            {
+                EnText.AppendFormat("{0:x2}", Byte);
+            }
+            return EnText.ToString();
+        }
+
+
+        /// <summary>
+        /// MD5 32位加密字符串
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static string MD5_32(string str)
+        {
+            string cl = str + "@$1212#";
+            string pwd = "";
+            MD5 md5 = MD5.Create();//实例化一个md5对像
+            // 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择 
+            byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
+            // 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
+            for (int i = 0; i < s.Length; i++)
+            {
+                // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符 
+                pwd = pwd + s[i].ToString("X").ToLower().PadLeft(2, '0');
+            }
+            return pwd;
+        }
+
+        public static string MD532(string str)
+        {
+            string cl = str;
+            string pwd = "";
+            MD5 md5 = MD5.Create();//实例化一个md5对像
+            // 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择 
+            byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
+            // 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
+            for (int i = 0; i < s.Length; i++)
+            {
+                // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符 
+                pwd = pwd + s[i].ToString("X").ToLower().PadLeft(2, '0');
+            }
+            return pwd;
+        }
+        /// <summary>
+        /// 获取MD5值
+        /// </summary>
+        /// <param name="str">加密的字符串</param>
+        /// <returns>返回MD5值</returns>
+        public static string MD5_16(string str)
+        {
+            return MD5_32(str).Substring(8, 16);
+        }
+        /// <summary>
+        /// 写日志(错误报告)
+        /// </summary>
+        /// <param name="str"></param>
+        public static void WriteLog(string str)
+        {
+            try
+            {
+                string path = getPath("/log/message/" + DateTime.Now.Year.ToString() + "/" + DateTime.Now.Month.ToString() + "/" + DateTime.Now.Day.ToString() + "/");
+                if (!Directory.Exists(path))
+                {
+                    Directory.CreateDirectory(path);
+                }
+                StreamWriter sw = File.AppendText(path + "content.log");
+                sw.WriteLine(str);
+                sw.Flush();
+                sw.Dispose();
+            }
+            catch
+            { }
+        }
+        /// <summary>
+        /// 写日志(错误报告)
+        /// </summary>
+        /// <param name="str"></param>
+        public static void WriteLog(string str, string filename)
+        {
+            try
+            {
+                string path = getPath("/log/" + filename + "/" + DateTime.Now.Year.ToString() + "/" + DateTime.Now.Month.ToString() + "/" + DateTime.Now.Day.ToString() + "/");
+                if (!Directory.Exists(path))
+                {
+                    Directory.CreateDirectory(path);
+                }
+                StreamWriter sw = File.AppendText(path + "content.log");
+                sw.WriteLine(str);
+                sw.Flush();
+                sw.Dispose();
+            }
+            catch
+            { }
+        }
+
+        public static void WriteLog(string path_str, string file_name, string page_content)
+        {
+            try
+            {
+                string path = getPath(path_str);
+                if (!Directory.Exists(path))
+                {
+                    Directory.CreateDirectory(path);
+                }
+                StreamWriter sw = File.AppendText(path + "/" + file_name);
+                sw.WriteLine(page_content);
+                sw.Flush();
+                sw.Dispose();
+            }
+            catch
+            { }
+        }
+
+        /// <summary>
+        /// 过滤html代码
+        /// </summary>
+        /// <param name="Htmlstring"></param>
+        public static string NoHTML(string Htmlstring) //去除HTML标记
+        {
+            if (Htmlstring == null || Htmlstring == string.Empty)
+            {
+                return "";
+            }
+            Htmlstring = Regex.Replace(Htmlstring, @"<script[^>]*?>.*?</script>", "", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase);
+            //Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+", "", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"<!--.*", "", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "\"", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", "<", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);", " ", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "\xa1", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "\xa2", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "\xa3", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "\xa9", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&#(\d+);", "", RegexOptions.IgnoreCase);
+            Htmlstring = Regex.Replace(Htmlstring, @"&.*?;", "", RegexOptions.IgnoreCase);
+
+            Htmlstring = Htmlstring.Replace("<", "〈");
+            Htmlstring = Htmlstring.Replace(">", "〉");
+            //Htmlstring = Htmlstring.Replace("\r\n", "");
+            //Htmlstring = HttpContext.Current.Server.HtmlEncode(Htmlstring).Trim();
+
+            return Htmlstring;
+        }
+
+        public static bool IsIncludeChinese(string str)
+        {
+            Regex r = new Regex(@"[\u4E00-\u9FA5]", RegexOptions.IgnoreCase);
+            if (r.IsMatch(str))
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public static bool IsInt(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return false;
+            }
+            Regex rCode = new Regex("^\\d+$");
+            if (!rCode.IsMatch(numberString))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+        }
+
+        public static string CheckInt(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return "0";
+            }
+            Regex rCode = new Regex("^\\d+$");
+            if (!rCode.IsMatch(numberString))
+            {
+                return "0";
+            }
+            else
+            {
+                return numberString;
+            }
+        }
+
+        public static DateTime CheckDateTime(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return DateTime.Now;
+            }
+            DateTime result;
+            if (DateTime.TryParse(numberString, out result))
+            {
+                return result;
+            }
+            else
+            {
+                return DateTime.Now;
+            }
+        }
+
+        public static bool ChkDateTime(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return false;
+            }
+            DateTime result;
+            if (DateTime.TryParse(numberString, out result))
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public static string CheckNull(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return "";
+            }
+            else
+            {
+                return numberString;
+            }
+        }
+
+        public static string CheckUrl(string str)
+        {
+            if (str == null || str == string.Empty)
+            {
+                return "";
+            }
+            Regex rCode = new Regex(@"((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?");
+            if (!rCode.IsMatch(str))
+            {
+                return "";
+            }
+            else
+            {
+                return str;
+            }
+        }
+
+        public static string CheckEmail(string str)
+        {
+            if (str == null || str == string.Empty)
+            {
+                return "";
+            }
+            Regex rCode = new Regex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
+            if (!rCode.IsMatch(str))
+            {
+                return "";
+            }
+            else
+            {
+                return str;
+            }
+        }
+
+        public static string CheckMobile(string str)
+        {
+            if (str == null || str == string.Empty)
+            {
+                return "";
+            }
+            Regex rCode = new Regex(@"^1[3456789]\d{9}$");
+            if (!rCode.IsMatch(str))
+            {
+                return "";
+            }
+            else
+            {
+                return str;
+            }
+        }
+
+        public static string CheckIdCard(string str)
+        {
+            if (str == null || str == string.Empty)
+            {
+                return "";
+            }
+            Regex rCode = new Regex(@"^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$");
+            if (!rCode.IsMatch(str))
+            {
+                return "";
+            }
+            else
+            {
+                return str;
+            }
+        }
+
+        public static bool IsNum(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return false;
+            }
+            Regex rCode = new Regex(@"^\d+(\.\d+)?$");
+            if (!rCode.IsMatch(numberString))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+        }
+
+        public static string CheckNum(string numberString)
+        {
+            if (string.IsNullOrEmpty(numberString))
+            {
+                return "0";
+            }
+            Regex rCode = new Regex(@"^\d+(\.\d+)?$");
+            if (!rCode.IsMatch(numberString))
+            {
+                return "0";
+            }
+            else
+            {
+                return numberString;
+            }
+        }
+
+        public static string CheckString(string str)
+        {
+            if (string.IsNullOrEmpty(str))
+            {
+                return "";
+            }
+            str = str.Replace("'", "&acute;");
+            str = str.Replace("\"", "&quot;");
+            //str = str.Replace(" ", "&nbsp;");
+            str = str.Replace("<", "&lt;");
+            str = str.Replace(">", "&gt;");
+            str = str.Replace("(", "(");
+            str = str.Replace(")", ")");
+            str = ToDBC(str);
+            return str;
+        }
+
+        public static string unCheckString(string str)
+        {
+            if (str == null || str == string.Empty)
+            {
+                return "";
+            }
+            str = str.Replace("&acute;", "'");
+            str = str.Replace("&quot;", "\"");
+            //str = str.Replace("&nbsp;", " ");
+            str = str.Replace("&lt;", "<");
+            str = str.Replace("&gt;", ">");
+            str = str.Replace("(", "(");
+            str = str.Replace(")", ")");
+            return str;
+        }
+
+        public static string CheckString2(string str)
+        {
+            if (str == null || str == string.Empty)
+            {
+                return "";
+            }
+            str = ToDBC(str);
+            str = Regex.Replace(str, @"<script.*?>[\s\S]*?</script>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<script.*?>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<object.*?>[\s\S]*?</object>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<object.*?>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<iframe.*?>[\s\S]*?</iframe>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<frameset.*?>[\s\S]*?</frameset>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<frameset.*?>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<frame.*?>[\s\S]*?</frame>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<frame.*?>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<form.*?>[\s\S]*?</form>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<input.*?>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<select.*?>[\s\S]*?</select>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<textarea.*?>[\s\S]*?</textarea>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<button.*?>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<noframes.*?>[\s\S]*?</noframes>", "", RegexOptions.IgnoreCase);
+            str = Regex.Replace(str, @"<noframes.*?>", "", RegexOptions.IgnoreCase);
+            return str;
+        }
+
+        public static string GetSession(HttpContext context, string strName)
+        {
+            var value = context.Session.GetString(strName);
+            if (string.IsNullOrEmpty(value))
+            {
+                value = "";
+            }
+            return value;
+        }
+
+        public static void WriteSession(HttpContext context, string strName, string strValue)
+        {
+            context.Session.SetString(strName, strValue);
+        }
+
+        public static void DelSession(HttpContext context, string strName)
+        {
+            context.Session.Remove(strName);
+        }
+
+        public static string rootIndex = System.AppDomain.CurrentDomain.BaseDirectory;
+        private static string Read(string absoluteFileLocation)
+        {
+            string result = "";
+            if (System.IO.File.Exists(absoluteFileLocation))
+            {
+                using (FileStream fs = new FileStream(absoluteFileLocation, FileMode.Open, FileAccess.Read))
+                {
+                    using (StreamReader sr = new StreamReader(fs, System.Text.Encoding.UTF8))
+                    {
+                        try
+                        {
+                            result = sr.ReadToEnd();
+                            //dbconn.InsertCache(absoluteFileLocation, result, 30);
+                        }
+                        catch
+                        { }
+                    }
+                }
+            }
+            return result;
+        }
+
+        public static string getPath(string path_str)
+        {
+            string result = AppContext.BaseDirectory + path_str;
+            return result;
+        }
+
+        public static string ReadInstance(string path_str)
+        {
+            //string path = System.IO.Path.Combine(rootIndex, path_str).Replace('/', System.IO.Path.DirectorySeparatorChar);
+            string path = getPath(path_str);
+            string content = "";
+            content = Read(path);
+            return content;
+        }
+        public static string ReadInstanceNoAuth(string path_str)
+        {
+            //string path = System.IO.Path.Combine(rootIndex, path_str).Replace('/', System.IO.Path.DirectorySeparatorChar);
+            string path = getPath(path_str);
+            string content = "";
+            content = Read(path);
+            return content;
+        }
+
+        public static string ReadInstanceByFull(string path_str)
+        {
+            string content = "";
+            content = Read(path_str);
+            return content;
+        }
+
+        public static void WritePage(string path_str, string file_name, string page_content)
+        {
+            try
+            {
+                string path = getPath(path_str);
+                if (!Directory.Exists(path))
+                {
+                    Directory.CreateDirectory(path);
+                }
+                Encoding nobom = new UTF8Encoding(false, false);
+                StreamWriter sw = new StreamWriter(path + "/" + file_name, false, nobom);
+                sw.Write(page_content);
+                sw.Flush();
+                sw.Dispose();
+            }
+            catch
+            { }
+        }
+
+        public static void WritePageFullPath(string path_str, string file_name, string page_content)
+        {
+            try
+            {
+                if (!Directory.Exists(path_str))
+                {
+                    Directory.CreateDirectory(path_str);
+                }
+                Encoding nobom = new UTF8Encoding(false, false);
+                StreamWriter sw = new StreamWriter(path_str + file_name, false, nobom);
+                sw.Write(page_content);
+                sw.Flush();
+                sw.Dispose();
+            }
+            catch
+            { }
+        }
+
+        public static void send_email(string mailTitle, string mailTo, string mailContent, string file_path, string host, int port, string username, string pwd, string displayname)
+        {
+            try
+            {
+                MailAddress from = new MailAddress(username, displayname); //邮件的发件人
+                MailMessage mail = new MailMessage();
+                //设置邮件的标题
+                mail.Subject = mailTitle;
+                mail.SubjectEncoding = Encoding.UTF8;
+                //设置邮件的发件人
+                //Pass:如果不想显示自己的邮箱地址,这里可以填符合mail格式的任意名称,真正发mail的用户不在这里设定,这个仅仅只做显示用
+                mail.From = from;
+                //设置邮件的收件人
+                string address = "";
+                string displayName = "";
+                /**/
+                /*  这里这样写是因为可能发给多个联系人,每个地址用 ; 号隔开
+                一般从地址簿中直接选择联系人的时候格式都会是 :用户名1 < mail1 >; 用户名2 < mail 2>; 
+                因此就有了下面一段逻辑不太好的代码
+                如果永远都只需要发给一个收件人那么就简单了 mail.To.Add("收件人mail");
+                */
+                string[] mailNames = (mailTo + ";").Split(';');
+                foreach (string name in mailNames)
+                {
+                    if (name != string.Empty)
+                    {
+                        if (name.IndexOf('<') > 0)
+                        {
+                            displayName = name.Substring(0, name.IndexOf('<'));
+                            address = name.Substring(name.IndexOf('<') + 1).Replace('>', ' ');
+                        }
+                        else
+                        {
+                            displayName = string.Empty;
+                            address = name.Substring(name.IndexOf('<') + 1).Replace('>', ' ');
+                        }
+                        mail.To.Add(new MailAddress(address, displayName));
+                    }
+                }
+
+                //设置邮件的抄送收件人
+                //这个就简单多了,如果不想快点下岗重要文件还是CC一份给领导比较好
+                //mail.CC.Add(new MailAddress("Manage@hotmail.com", "尊敬的领导"));
+
+                //设置邮件的内容
+                mail.Body = mailContent;
+                //设置邮件的格式
+                mail.BodyEncoding = Encoding.UTF8;
+                mail.IsBodyHtml = true;
+                //设置邮件的发送级别
+                mail.Priority = MailPriority.Normal;
+                //设置邮件的附件,将在客户端选择的附件先上传到服务器保存一个,然后加入到mail中
+                //string fileName = file_path;
+                //fileName = "D:/UpFile/" + fileName.Substring(fileName.LastIndexOf("/") + 1);
+                //txtUpFile.PostedFile.SaveAs(fileName); // 将文件保存至服务器
+                //mail.Attachments.Add(new Attachment(fileName));
+                mail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnSuccess;
+                SmtpClient client = new SmtpClient();
+                //设置用于 SMTP 事务的主机的名称,填IP地址也可以了
+                //client.Host = "smtp.163.com";
+                client.Host = host;
+                //设置用于 SMTP 事务的端口,默认的是 25
+                //client.Port = 25;
+                client.Port = port;
+                client.UseDefaultCredentials = false;
+                client.EnableSsl = true;
+                //这里才是真正的邮箱登陆名和密码,比如我的邮箱地址是 hbgx@hotmail, 我的用户名为 hbgx ,我的密码是 xgbh
+                client.Credentials = new System.Net.NetworkCredential(username, pwd);
+                client.DeliveryMethod = SmtpDeliveryMethod.Network;
+                client.Send(mail);
+            }
+            catch (Exception ex)
+            {
+                Function.WriteLog(ex.ToString());
+            }
+        }
+
+        public static string GetWebRequest(string url)
+        {
+            return GetWebRequest(url, new Dictionary<string, string>());
+        }
+
+        public static string GetWebRequest(string url, Dictionary<string, string> header)
+        {
+            string result = "";
+            try
+            {
+                HttpWebRequest webReq = (HttpWebRequest)HttpWebRequest.Create(url);
+                webReq.Method = "GET";
+                webReq.KeepAlive = true;
+                webReq.Timeout = 1200000;
+                webReq.ContentType = "text/html";//application/x-www-form-urlencoded
+                if (header.Count > 0)
+                {
+                    foreach (string key in header.Keys)
+                    {
+                        webReq.Headers.Add(key, header[key]);
+                    }
+                }
+                webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
+                using (HttpWebResponse response = (HttpWebResponse)webReq.GetResponse())
+                {
+                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
+                    {
+                        result = reader.ReadToEnd();
+                        //function.WriteLog(context.Request.QueryString["mobile"] + "  " + reader.ReadToEnd());
+                    }
+                    if (response != null)
+                        response.Close();
+                }
+            }
+            catch (Exception ex)
+            {
+                result = ex.ToString();
+            }
+            return result;
+        }
+
+        public static string get_Random(int num)
+        {
+            string[] str = new string[num];
+            string serverCode = "";
+            //生成随机生成器 
+            Random random = new Random(GetRandomSeed());
+            for (int i = 0; i < num; i++)
+            {
+                str[i] = random.Next(10).ToString().Substring(0, 1);
+            }
+            foreach (string s in str)
+            {
+                serverCode += s;
+            }
+            return serverCode;
+        }
+
+        public static int get_Random(int min, int max)
+        {
+            int serverCode = 0;
+            Random random = new Random(GetRandomSeed());
+            serverCode = random.Next(max - min) + min;
+            return serverCode;
+        }
+
+        public static string get_Random_string(int count)
+        {
+            string str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+            string result = "";
+            for (int i = 0; i < count; i++)
+            {
+                Random r = new Random(GetRandomSeed());
+                int serverCode = r.Next(str.Length - 0) + 0;
+                result += str.Substring(serverCode, 1);
+            }
+            return result;
+        }
+
+        public static int GetRandomSeed()
+        {
+            //字节数组,用于存储
+            byte[] bytes = new byte[4];
+            //创建加密服务,实现加密随机数生成器
+            System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
+            //加密数据存入字节数组
+            rng.GetBytes(bytes);
+            //转成整型数据返回,作为随机数生成种子
+            return BitConverter.ToInt32(bytes, 0);
+        }
+
+        public static string get_weekday(DateTime date)
+        {
+            string[] week = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
+            return week[(int)date.DayOfWeek];
+        }
+
+        public static string get_substr(string s, int length)
+        {
+            byte[] bytes = System.Text.Encoding.Unicode.GetBytes(s);
+            int n = 0;  //  表示当前的字节数
+            int i = 0;  //  要截取的字节数
+            for (; i < bytes.GetLength(0) && n < length; i++)
+            {
+                //  偶数位置,如0、2、4等,为UCS2编码中两个字节的第一个字节
+                if (i % 2 == 0)
+                {
+                    n++;      //  在UCS2第一个字节时n加1
+                }
+                else
+                {
+                    //  当UCS2编码的第二个字节大于0时,该UCS2字符为汉字,一个汉字算两个字节
+                    if (bytes[i] > 0)
+                    {
+                        n++;
+                    }
+                }
+            }
+            //  如果i为奇数时,处理成偶数
+            if (i % 2 == 1)
+            {
+                //  该UCS2字符是汉字时,去掉这个截一半的汉字 
+                if (bytes[i] > 0)
+                    i = i - 1;
+                //  该UCS2字符是字母或数字,则保留该字符
+                else
+                    i = i + 1;
+            }
+            return System.Text.Encoding.Unicode.GetString(bytes, 0, i);
+        }
+
+        public static DateTime ConvertIntDateTime(double d)
+        {
+            DateTime time = DateTime.MinValue;
+            DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
+            time = startTime.AddSeconds(d);
+            return time;
+        }
+
+        public static DateTime ConvertIntDateTimeMini(long TimeStamp)
+        {
+            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // 当地时区
+            return startTime.AddTicks(TimeStamp * 10000);
+        }
+
+        public static int ConvertDateTimeInt(DateTime time)
+        {
+            double intResult = 0;
+            DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
+            TimeSpan ts = time - startTime;
+            intResult = Math.Round(ts.TotalSeconds, 0);
+            return int.Parse(intResult.ToString());
+        }
+
+        public static long GetCurTimestamp()
+        {
+            var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+            long times = Convert.ToInt64(ts.TotalMilliseconds);
+            return times;
+        }
+
+        public static DateTime checkDateTimeNull(DateTime? datetime)
+        {
+            if (datetime != null)
+            {
+                return datetime.Value;
+            }
+            return DateTime.Now;
+        }
+
+        public static bool IsDate(string datetime)
+        {
+            DateTime check = DateTime.Now;
+            return DateTime.TryParse(datetime + " 00:00:00", out check);
+        }
+
+        public static bool IsDateTime(string datetime)
+        {
+            DateTime check = DateTime.Now;
+            return DateTime.TryParse(datetime, out check);
+        }
+
+        /// <summary>  
+        /// 获取时间戳  
+        /// </summary>  
+        /// <returns></returns>  
+        public static string getTimeStamp()
+        {
+            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+            return Convert.ToInt64(ts.TotalSeconds).ToString();
+        }
+
+        public static string get_timespan(DateTime s, string show_type)
+        {
+            string result = get_timespan(s, DateTime.Now, show_type);
+            return result;
+        }
+
+        public static string get_timespan(DateTime s, DateTime e, string show_type)
+        {
+            string result = "";
+            if (e > s)
+            {
+                int total = 0;
+                TimeSpan ts = e - s;
+                switch (show_type)
+                {
+                    case "d":
+                        result = ts.Days.ToString() + "天";
+                        break;
+                    case "h":
+                        if (ts.Days > 0)
+                        {
+                            total += ts.Days * 24;
+                        }
+                        total += ts.Hours;
+                        result = total.ToString() + "小时";
+                        break;
+                    case "m":
+                        if (ts.Days > 0)
+                        {
+                            total += ts.Days * 24 * 60;
+                        }
+                        if (ts.Hours > 0)
+                        {
+                            total += ts.Hours * 60;
+                        }
+                        total += ts.Hours;
+                        result = total.ToString() + "分钟";
+                        break;
+                    case "s":
+                        if (ts.Days > 0)
+                        {
+                            total += ts.Days * 24 * 60 * 60;
+                        }
+                        if (ts.Hours > 0)
+                        {
+                            total += ts.Hours * 60 * 60;
+                        }
+                        if (ts.Minutes > 0)
+                        {
+                            total += ts.Hours * 60;
+                        }
+                        total += ts.Hours;
+                        result = total.ToString() + "秒";
+                        break;
+                    case "time":
+                        if (ts.Days > 1)
+                        {
+                            result = s.ToString("yyyy-MM-dd");
+                        }
+                        else
+                        {
+                            if (ts.Days > 0)
+                            {
+                                result += ts.Days + "天";
+                            }
+                            if (ts.Hours > 0)
+                            {
+                                result += ts.Hours + "小时";
+                            }
+                            if (ts.Minutes > 0)
+                            {
+                                result += ts.Minutes + "分钟";
+                            }
+                            if (string.IsNullOrEmpty(result))
+                            {
+                                result = "刚刚";
+                            }
+                            else
+                            {
+                                result += "前";
+                            }
+                        }
+                        break;
+                    default: break;
+                }
+            }
+            return result;
+        }
+
+        // 半角转全角
+        public static string ToSBC(string input)
+        {
+            char[] c = input.ToCharArray();
+            for (int i = 0; i < c.Length; i++)
+            {
+                if (c[i] == 32)
+                {
+                    c[i] = (char)12288;
+                    continue;
+                }
+                if (c[i] < 127)
+                    c[i] = (char)(c[i] + 65248);
+            }
+            return new string(c);
+        }
+
+        // 全角转半角
+        public static string ToDBC(string input)
+        {
+            char[] c = input.ToCharArray();
+            for (int i = 0; i < c.Length; i++)
+            {
+                if (c[i] == 12288)
+                {
+                    c[i] = (char)32;
+                    continue;
+                }
+                if (c[i] > 65280 && c[i] < 65375)
+                    c[i] = (char)(c[i] - 65248);
+            }
+            return new string(c);
+        }
+
+        public static string PostWebRequest(string postUrl, string paramData, string ContentType = "application/x-www-form-urlencoded")
+        {
+            //return PostWebRequest(postUrl, paramData, new Dictionary<string, string>());
+            string ret = string.Empty;
+            try
+            {
+                byte[] postData = Encoding.UTF8.GetBytes(paramData);
+                // 设置提交的相关参数 
+                HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
+                Encoding myEncoding = Encoding.UTF8;
+                request.Method = "POST";
+                request.KeepAlive = false;
+                request.AllowAutoRedirect = true;
+                request.ContentType = ContentType;
+                //request.ContentType = "multipart/form-data; boundary=" + boundary;
+                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR  3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)";
+                request.ContentLength = postData.Length;
+
+                // 提交请求数据 
+                System.IO.Stream outputStream = request.GetRequestStream();
+                outputStream.Write(postData, 0, postData.Length);
+                outputStream.Close();
+
+                HttpWebResponse response;
+                Stream responseStream;
+                StreamReader reader;
+                string srcString;
+                response = request.GetResponse() as HttpWebResponse;
+                responseStream = response.GetResponseStream();
+                reader = new System.IO.StreamReader(responseStream, Encoding.UTF8);
+                srcString = reader.ReadToEnd();
+                ret = srcString;   //返回值赋值
+                reader.Close();
+            }
+            catch (Exception ex)
+            {
+                ret = "fail";
+                WriteLog(ex.ToString(), "PostWebRequest");
+            }
+            return ret;
+        }
+
+        public static string PostWebRequest(string postUrl, string paramData, Dictionary<string, string> header, string ContentType = "application/x-www-form-urlencoded")
+        {
+            //return PostWebRequest(postUrl, paramData, new Dictionary<string, string>());
+            string ret = string.Empty;
+            try
+            {
+                byte[] postData = Encoding.UTF8.GetBytes(paramData);
+                // 设置提交的相关参数 
+                HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
+                Encoding myEncoding = Encoding.UTF8;
+                request.Method = "POST";
+                request.KeepAlive = false;
+                request.AllowAutoRedirect = true;
+                request.ContentType = ContentType;
+                //request.ContentType = "multipart/form-data; boundary=" + boundary;
+                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR  3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)";
+                request.ContentLength = postData.Length;
+                if (header.Count > 0)
+                {
+                    foreach (string key in header.Keys)
+                    {
+                        request.Headers.Add(key, header[key]);
+                    }
+                }
+
+                // 提交请求数据 
+                System.IO.Stream outputStream = request.GetRequestStream();
+                outputStream.Write(postData, 0, postData.Length);
+                outputStream.Close();
+
+                HttpWebResponse response;
+                Stream responseStream;
+                StreamReader reader;
+                string srcString;
+                response = request.GetResponse() as HttpWebResponse;
+                responseStream = response.GetResponseStream();
+                reader = new System.IO.StreamReader(responseStream, Encoding.UTF8);
+                srcString = reader.ReadToEnd();
+                ret = srcString;   //返回值赋值
+                reader.Close();
+            }
+            catch (Exception ex)
+            {
+                ret = "fail";
+                WriteLog(ex.ToString(), "PostWebRequest");
+            }
+            return ret;
+        }
+
+        /// <summary>
+        /// 从ftp服务器上获得文件夹列表
+        /// </summary>
+        /// <param name="RequedstPath">服务器下的相对路径</param>
+        /// <returns></returns>
+        public static List<string> GetFtpDirctoryList(string path, string username, string password)
+        {
+            List<string> strs = new List<string>();
+            try
+            {
+                string uri = path;   //目标路径 path为服务器地址
+                FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
+                // ftp用户名和密码
+                reqFTP.Credentials = new NetworkCredential(username, password);
+                reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
+                WebResponse response = reqFTP.GetResponse();
+                StreamReader reader = new StreamReader(response.GetResponseStream());//中文文件名
+ 
+                string line = reader.ReadLine();
+                while (line != null)
+                {
+                    if (line.Contains("<DIR>"))
+                    {
+                        string msg = line.Substring(line.LastIndexOf("<DIR>") + 5).Trim();
+                        strs.Add(msg);
+                    }
+                    line = reader.ReadLine();
+                }
+                reader.Close();
+                response.Close();
+                return strs;
+            }
+            catch (Exception ex)
+            {
+                Function.WriteLog(DateTime.Now + ":" + ex.ToString(), "从ftp服务器上获得文件夹列表异常");
+            }
+            return strs;
+        }
+ 
+        /// <summary>
+        /// 从ftp服务器上获得文件列表
+        /// </summary>
+        /// <param name="RequedstPath">服务器下的相对路径</param>
+        /// <returns></returns>
+        public static List<string> GetFtpFileList(string path, string username, string password)
+        {
+            List<string> strs = new List<string>();
+            try
+            {
+                string uri = path;   //目标路径 path为服务器地址
+                FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
+                // ftp用户名和密码
+                reqFTP.Credentials = new NetworkCredential(username, password);
+                reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
+                WebResponse response = reqFTP.GetResponse();
+                StreamReader reader = new StreamReader(response.GetResponseStream());//中文文件名
+ 
+                string line = reader.ReadLine();
+                while (line != null)
+                {
+                    if (!line.Contains("<DIR>"))
+                    {
+                        string msg = line.Substring(39).Trim();
+                        strs.Add(msg);
+                    }
+                    line = reader.ReadLine();
+                }
+                reader.Close();
+                response.Close();
+                return strs;
+            }
+            catch (Exception ex)
+            {
+                Function.WriteLog(DateTime.Now + ":" + ex.ToString(), "从ftp服务器上获得文件列表异常");
+            }
+            return strs;
+        }
+        //从ftp服务器上下载文件
+        public static string FtpDownload(string path, string fileName, string username, string password)
+        {
+            FtpWebRequest reqFTP;
+            string savePath = "";
+            try
+            {
+                string filePath = getPath("/FtpDownloadFile/" + fileName);
+                FileStream outputStream = new FileStream(filePath, FileMode.Create);
+                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(path + fileName));
+                reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
+                reqFTP.UseBinary = true;
+                reqFTP.Credentials = new NetworkCredential(username, password);
+                reqFTP.UsePassive = false;
+                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
+                Stream ftpStream = response.GetResponseStream();
+                long cl = response.ContentLength;
+                int bufferSize = 2048;
+                int readCount;
+                byte[] buffer = new byte[bufferSize];
+                readCount = ftpStream.Read(buffer, 0, bufferSize);
+                while (readCount > 0)
+                {
+                    outputStream.Write(buffer, 0, readCount);
+                    readCount = ftpStream.Read(buffer, 0, bufferSize);
+                }
+                ftpStream.Close();
+                outputStream.Close();
+                response.Close();
+                savePath = "/FtpDownloadFile/" + fileName;
+            }
+            catch (Exception ex)
+            {
+                Function.WriteLog(DateTime.Now + ":" + ex.ToString(), "从ftp服务器上下载文件异常");
+            }
+            return savePath;
+        } 
+
+        public static string base64StringToImage(string base64String, string path, string filename)
+        {
+            string fullpath = getPath(path);
+            if (!System.IO.Directory.Exists(fullpath))
+            {
+                System.IO.Directory.CreateDirectory(fullpath);
+            }
+            byte[] arr = Convert.FromBase64String(base64String);
+            System.IO.File.WriteAllBytes(fullpath + filename, arr);
+            return path + filename;
+        }
+
+        public static String BuildQueryString(SortedList<String, String> kvp)
+        {
+            return String.Join("&", kvp.Select(item => String.Format("{0}={1}", item.Key.Trim(), item.Value)).ToArray());
+        }
+    }
+}

+ 37 - 0
Common/JsonConverterUtil.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Util
+{
+    public class JsonConverterUtil
+    {
+        public class DateTimeNullConverter : JsonConverter<DateTime?>
+        {
+            public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+                => string.IsNullOrEmpty(reader.GetString()) ? default : ParseDateTime(reader.GetString());
+
+            public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
+                => writer.WriteStringValue(value?.ToString("yyyy-MM-dd HH:mm:ss"));
+        }
+
+        public class DateTimeConverter : JsonConverter<DateTime>
+        {
+            public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                var dateTime = ParseDateTime(reader.GetString());
+                return dateTime == null ? DateTime.MinValue : dateTime.Value;
+            }
+
+            public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
+                => writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
+        }
+
+        public static DateTime? ParseDateTime(string dateStr)
+        {
+            if (System.Text.RegularExpressions.Regex.IsMatch(dateStr, @"^\d{4}[/-]") && DateTime.TryParse(dateStr, null, System.Globalization.DateTimeStyles.AssumeLocal, out var dateVal))
+                return dateVal;
+            return null;
+        }
+    }
+}

+ 167 - 0
Common/JwtUtil.cs

@@ -0,0 +1,167 @@
+using Base;
+using Extensions;
+using Infrastructure.Model;
+using Microsoft.IdentityModel.Tokens;
+using Model.Base;
+using Newtonsoft.Json;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+
+namespace Util
+{
+    /// <summary>
+    /// 2023-8-29已从WebApi移至此
+    /// </summary>
+    public class JwtUtil
+    {
+        /// <summary>
+        /// 获取用户身份信息
+        /// </summary>
+        /// <param name="httpContext"></param>
+        /// <returns></returns>
+        public static TokenModel GetLoginUser(HttpContext httpContext)
+        {
+            string token = httpContext.GetToken();
+
+            if (!string.IsNullOrEmpty(token))
+            {
+                return ValidateJwtToken(ParseToken(token));
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// 生成token
+        /// </summary>
+        /// <param name="claims"></param>
+        /// <returns></returns>
+        public static string GenerateJwtToken(List<Claim> claims)
+        {
+            JwtSettings jwtSettings = new();
+            AppSettings.Bind("JwtSettings", jwtSettings);
+
+            var authTime = DateTime.Now;
+            var expiresAt = authTime.AddMinutes(jwtSettings.Expire);
+            var tokenHandler = new JwtSecurityTokenHandler();
+            var key = Encoding.ASCII.GetBytes(jwtSettings.SecretKey);
+            claims.Add(new Claim("Audience", jwtSettings.Audience));
+            claims.Add(new Claim("Issuer", jwtSettings.Issuer));
+
+            var tokenDescriptor = new SecurityTokenDescriptor
+            {
+                Subject = new ClaimsIdentity(claims),
+                Issuer = jwtSettings.Issuer,
+                Audience = jwtSettings.Audience,
+                IssuedAt = authTime,//token生成时间
+                Expires = expiresAt,
+                //NotBefore = authTime,
+                TokenType = jwtSettings.TokenType,
+                //对称秘钥,签名证书
+                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
+            };
+            var token = tokenHandler.CreateToken(tokenDescriptor);
+            return tokenHandler.WriteToken(token);
+        }
+        /// <summary>
+        /// 验证Token
+        /// </summary>
+        /// <returns></returns>
+        public static TokenValidationParameters ValidParameters()
+        {
+            JwtSettings jwtSettings = new();
+            AppSettings.Bind("JwtSettings", jwtSettings);
+
+            if (jwtSettings == null || jwtSettings.SecretKey.IsEmpty())
+            {
+                throw new Exception("JwtSettings获取失败");
+            }
+            var key = Encoding.ASCII.GetBytes(jwtSettings.SecretKey);
+
+            var tokenDescriptor = new TokenValidationParameters
+            {
+                ValidateIssuerSigningKey = true,
+                ValidateIssuer = true,
+                ValidateAudience = true,
+                ValidIssuer = jwtSettings.Issuer,
+                ValidAudience = jwtSettings.Audience,
+                IssuerSigningKey = new SymmetricSecurityKey(key),
+                ValidateLifetime = true,//是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
+                ClockSkew = TimeSpan.FromSeconds(30)
+                //RequireExpirationTime = true,//过期时间
+            };
+            return tokenDescriptor;
+        }
+        /// <summary>
+        /// 从令牌中获取数据声明
+        /// </summary>
+        /// <param name="token">令牌</param>
+        /// <returns></returns>
+        public static JwtSecurityToken? ParseToken(string token)
+        {
+            var tokenHandler = new JwtSecurityTokenHandler();
+            var validateParameter = ValidParameters();
+            token = token.Replace("Bearer ", "");
+            try
+            {
+                tokenHandler.ValidateToken(token, validateParameter, out SecurityToken validatedToken);
+
+                return tokenHandler.ReadJwtToken(token);
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+                // return null if validation fails
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// jwt token校验
+        /// </summary>
+        /// <param name="jwtSecurityToken"></param>
+        /// <returns></returns>
+        public static TokenModel? ValidateJwtToken(JwtSecurityToken jwtSecurityToken)
+        {
+            try
+            {
+                if (jwtSecurityToken == null) return null;
+                IEnumerable<Claim> claims = jwtSecurityToken?.Claims;
+                TokenModel loginUser = null;
+
+                var userData = claims.FirstOrDefault(x => x.Type == ClaimTypes.UserData)?.Value;
+                if (userData != null)
+                {
+                    loginUser = JsonConvert.DeserializeObject<TokenModel>(userData);
+                    loginUser.ExpireTime = jwtSecurityToken.ValidTo;
+                }
+                return loginUser;
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+                return null;
+            }
+        }
+
+        /// <summary>
+        ///组装Claims
+        /// </summary>
+        /// <param name="user"></param>
+        /// <returns></returns>
+        public static List<Claim> AddClaims(TokenModel user)
+        {
+            var claims = new List<Claim>()
+                {
+                    new(ClaimTypes.PrimarySid, user.UserId.ToString()),
+                    new(ClaimTypes.NameIdentifier, user.UserId.ToString()),
+                    new(ClaimTypes.Name, user.Username),
+                    new(ClaimTypes.GroupSid, user.DeptId.ToString()),
+                    new(ClaimTypes.UserData, JsonConvert.SerializeObject(user))
+                };
+
+            return claims;
+        }
+
+    }
+}

+ 48 - 0
Common/StringConverter.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Buffers;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+
+namespace Util
+{
+    /// <summary>
+    /// Json任何类型读取到字符串属性
+    /// 因为 System.Text.Json 必须严格遵守类型一致,当非字符串读取到字符属性时报错:
+    /// The JSON value could not be converted to System.String.
+    /// </summary>
+    public class StringConverter : System.Text.Json.Serialization.JsonConverter<string>
+    {
+        public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            if (reader.TokenType == JsonTokenType.String)
+            {
+                return reader.GetString();
+            }
+            else
+            {
+                //非字符类型,返回原生内容
+                return GetRawPropertyValue(reader);
+            }
+
+            throw new JsonException();
+        }
+
+        public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
+        {
+            writer.WriteStringValue(value);
+        }
+        /// <summary>
+        /// 非字符类型,返回原生内容
+        /// </summary>
+        /// <param name="jsonReader"></param>
+        /// <returns></returns>
+        private static string GetRawPropertyValue(Utf8JsonReader jsonReader)
+        {
+            ReadOnlySpan<byte> utf8Bytes = jsonReader.HasValueSequence ?
+            jsonReader.ValueSequence.ToArray() :
+            jsonReader.ValueSpan;
+            return Encoding.UTF8.GetString(utf8Bytes);
+        }
+    }
+}

+ 142 - 0
Common/Tools.cs

@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Common
+{
+    public class Tools
+    {
+        /// <summary>
+        /// 要分割的字符串 eg: 1,3,10,00
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="split">分割的字符串</param>
+        /// <returns></returns>
+        public static long[] SpitLongArrary(string str, char split = ',')
+        {
+            if (string.IsNullOrEmpty(str)) { return Array.Empty<long>(); }
+            str = str.TrimStart(split).TrimEnd(split);
+            string[] strIds = str.Split(split, (char)StringSplitOptions.RemoveEmptyEntries);
+            long[] infoIdss = Array.ConvertAll(strIds, s => long.Parse(s));
+            return infoIdss;
+        }
+
+        public static int[] SpitIntArrary(string str, char split = ',')
+        {
+            if (string.IsNullOrEmpty(str)) { return Array.Empty<int>(); }
+            string[] strIds = str.Split(split, (char)StringSplitOptions.RemoveEmptyEntries);
+            int[] infoIdss = Array.ConvertAll(strIds, s => int.Parse(s));
+            return infoIdss;
+        }
+        public static T[] SplitAndConvert<T>(string input, char split = ',')
+        {
+            if (string.IsNullOrEmpty(input)) { return Array.Empty<T>(); }
+            string[] parts = input.Split(split, (char)StringSplitOptions.RemoveEmptyEntries);
+            T[] result =  Array.ConvertAll(parts, s => (T)Convert.ChangeType(s, typeof(T)));
+            //for (int i = 0; i < parts.Length; i++)
+            //{
+            //    result[i] = (T)Convert.ChangeType(parts[i], typeof(T));
+            //}
+
+            return result;
+        }
+
+        /// <summary>
+        /// 根据日期获取星期几
+        /// </summary>
+        public static string GetWeekByDate(DateTime dt)
+        {
+            var day = new[] { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
+            return day[Convert.ToInt32(dt.DayOfWeek.ToString("d"))];
+        }
+
+        /// <summary>
+        /// 得到这个月的第几周
+        /// </summary>
+        /// <param name="daytime">年月日</param>
+        /// <returns>传递过来的时间是第几周</returns>
+        public static int GetWeekNumInMonth(DateTime daytime)
+        {
+            int dayInMonth = daytime.Day;
+            //本月第一天
+            DateTime firstDay = daytime.AddDays(1 - daytime.Day);
+            //本月第一天是周几
+            int weekday = (int)firstDay.DayOfWeek == 0 ? 7 : (int)firstDay.DayOfWeek;
+            //本月第一周有几天
+            int firstWeekEndDay = 7 - (weekday - 1);
+            //当前日期和第一周之差
+            int diffday = dayInMonth - firstWeekEndDay;
+            diffday = diffday > 0 ? diffday : 1;
+            //当前是第几周,如果整除7就减一天
+            int weekNumInMonth = ((diffday % 7) == 0
+                ? (diffday / 7 - 1)
+                : (diffday / 7)) + 1 + (dayInMonth > firstWeekEndDay ? 1 : 0);
+            return weekNumInMonth;
+        }
+        /// <summary>
+        /// 判断一个字符串是否为url
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static bool IsUrl(string str)
+        {
+            try
+            {
+                string Url = @"^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?$";
+                return Regex.IsMatch(str, Url);
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+                return false;
+            }
+        }
+        public static bool CheckUserName(string str)
+        {
+            try
+            {
+                string rg = @"^[a-z][a-z0-9-_]*$";
+                return Regex.IsMatch(str, rg);
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 计算密码强度
+        /// </summary>
+        /// <param name="password">密码字符串</param>
+        /// <returns></returns>
+        public static bool PasswordStrength(string password)
+        {
+            //空字符串强度值为0
+            if (string.IsNullOrEmpty(password)) return false;
+
+            //字符统计
+            int iNum = 0, iLtt = 0, iSym = 0;
+            foreach (char c in password)
+            {
+                if (c >= '0' && c <= '9') iNum++;
+                else if (c >= 'a' && c <= 'z') iLtt++;
+                else if (c >= 'A' && c <= 'Z') iLtt++;
+                else iSym++;
+            }
+
+            if (iLtt == 0 && iSym == 0) return false; //纯数字密码
+            if (iNum == 0 && iLtt == 0) return false; //纯符号密码
+            if (iNum == 0 && iSym == 0) return false; //纯字母密码
+
+            if (password.Length >= 6 && password.Length < 16) return true;//长度不大于6的密码
+
+            if (iLtt == 0) return true; //数字和符号构成的密码
+            if (iSym == 0) return true; //数字和字母构成的密码
+            if (iNum == 0) return true; //字母和符号构成的密码
+
+            return true; //由数字、字母、符号构成的密码
+        }
+    }
+}

+ 134 - 0
Common/dbconn.cs

@@ -0,0 +1,134 @@
+using System;
+using System.Data;
+using System.Text;
+using System.Security.Cryptography;
+using Microsoft.Extensions.Caching.Memory;
+using System.IO;
+
+namespace Common
+{
+    public class Dbconn
+    {        
+        public static void InsertCache(string key, object val)
+        {
+            new MemoryCache(new MemoryCacheOptions()).Set(key, val, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(10)));
+        }
+
+        public static void InsertCache(string key, object val, int minutes)
+        {
+            new MemoryCache(new MemoryCacheOptions()).Set(key, val, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(minutes)));
+        }
+
+        public static void InsertCache(string key, object val, DateTime endtime)
+        {
+            new MemoryCache(new MemoryCacheOptions()).Set(key, val);
+        }
+
+        /// <summary>
+        /// des解密
+        /// </summary>
+        /// <param name="content"></param>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public static string DesDecrypt(string content, string key = "yun1mu23")
+        {
+            try
+            {
+                content = content.Replace(" ", "+");
+                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
+                byte[] inputByteArray = Convert.FromBase64String(content);
+                des.Key = ASCIIEncoding.ASCII.GetBytes(key);
+                des.IV = ASCIIEncoding.ASCII.GetBytes(key);
+                MemoryStream ms = new MemoryStream();
+                CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
+                cs.Write(inputByteArray, 0, inputByteArray.Length);
+                cs.FlushFinalBlock();
+                return System.Text.Encoding.Default.GetString(ms.ToArray());
+            }
+            catch (Exception ex)
+            {
+                Function.WriteLog(DateTime.Now.ToString() + "\r\n" + content + "\r\n" + ex.ToString(), "des解密异常");
+                return "{}";
+            }
+        }
+
+        public static string DesEncrypt(string content, string key = "yun1mu23")
+        {
+            try
+            {
+                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
+                byte[] inputByteArray = System.Text.Encoding.Default.GetBytes(content);
+                des.Key = ASCIIEncoding.ASCII.GetBytes(key);
+                des.IV = ASCIIEncoding.ASCII.GetBytes(key);
+                MemoryStream ms = new MemoryStream();
+                CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
+                cs.Write(inputByteArray, 0, inputByteArray.Length);
+                cs.FlushFinalBlock();
+                return Convert.ToBase64String(ms.ToArray());
+            }
+            catch (Exception ex)
+            {
+                Function.WriteLog(DateTime.Now.ToString() + "\r\n" + content + "\r\n" + ex.ToString(), "des加密异常");
+                return "{}";
+            }
+        }
+
+        public static string Encrypt3DES(string a_strString, string Key3des = "yun1mu23")
+        {
+            DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
+            DES.Key = Encoding.UTF8.GetBytes(Key3des);
+            DES.Mode = CipherMode.ECB;
+            DES.Padding = PaddingMode.Zeros;
+            ICryptoTransform DESEncrypt = DES.CreateEncryptor();
+            byte[] Buffer = Encoding.UTF8.GetBytes(a_strString);
+            return Convert.ToBase64String(DESEncrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
+        }
+
+        public static string Decrypt3DES(string a_strString, string Key3des = "yun1mu23")
+        {
+            DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
+            DES.Key = Encoding.UTF8.GetBytes(Key3des);
+            DES.Mode = CipherMode.ECB;
+            DES.Padding = PaddingMode.Zeros;
+            ICryptoTransform DESDecrypt = DES.CreateDecryptor();
+            byte[] Buffer = Convert.FromBase64String(a_strString);
+            return UTF8Encoding.UTF8.GetString(DESDecrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
+        }
+
+
+        public static string AesEncrypt(string str, string key, string iv)
+        {
+            if (string.IsNullOrEmpty(str)) return null;
+            Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);
+
+            System.Security.Cryptography.RijndaelManaged rm = new System.Security.Cryptography.RijndaelManaged
+            {
+                Key = Encoding.UTF8.GetBytes(key),
+                IV = Encoding.UTF8.GetBytes(iv),
+                Mode = System.Security.Cryptography.CipherMode.CBC,
+                Padding = System.Security.Cryptography.PaddingMode.PKCS7
+            };
+            System.Security.Cryptography.ICryptoTransform cTransform = rm.CreateEncryptor();
+            Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
+            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
+        }
+
+        public static string AesDecrypt(string str, string key, string iv)
+        {
+            if (string.IsNullOrEmpty(str)) return null;
+            Byte[] toEncryptArray = Convert.FromBase64String(str);
+
+            System.Security.Cryptography.RijndaelManaged rm = new System.Security.Cryptography.RijndaelManaged
+            {
+                Key = Encoding.UTF8.GetBytes(key),
+                IV = Encoding.UTF8.GetBytes(iv),
+                Mode = System.Security.Cryptography.CipherMode.CBC,
+                Padding = System.Security.Cryptography.PaddingMode.PKCS7
+            };
+
+            System.Security.Cryptography.ICryptoTransform cTransform = rm.CreateDecryptor();
+            Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
+            return Encoding.UTF8.GetString(resultArray);
+        }
+    }
+}

+ 145 - 0
Constant/Helper/DateTimeHelper.cs

@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Infrastructure
+{
+    public class DateTimeHelper
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="dateTime"></param>
+        /// <returns></returns>
+        public static DateTime GetBeginTime(DateTime? dateTime, int days = 0)
+        {
+            if (dateTime == DateTime.MinValue || dateTime == null)
+            {
+                return DateTime.Now.AddDays(days);
+            }
+            return dateTime ?? DateTime.Now;
+        }
+        #region 时间戳转换
+
+        /// <summary>
+        ///  时间戳转本地时间-时间戳精确到秒
+        /// </summary> 
+        public static DateTime ToLocalTimeDateBySeconds(long unix)
+        {
+            var dto = DateTimeOffset.FromUnixTimeSeconds(unix);
+            return dto.ToLocalTime().DateTime;
+        }
+
+        /// <summary>
+        ///  时间转时间戳Unix-时间戳精确到秒
+        /// </summary> 
+        public static long ToUnixTimestampBySeconds(DateTime dt)
+        {
+            DateTimeOffset dto = new DateTimeOffset(dt);
+            return dto.ToUnixTimeSeconds();
+        }
+
+        /// <summary>
+        ///  时间戳转本地时间-时间戳精确到毫秒
+        /// </summary> 
+        public static DateTime ToLocalTimeDateByMilliseconds(long unix)
+        {
+            var dto = DateTimeOffset.FromUnixTimeMilliseconds(unix);
+            return dto.ToLocalTime().DateTime;
+        }
+
+        /// <summary>
+        ///  时间转时间戳Unix-时间戳精确到毫秒
+        /// </summary> 
+        public static long ToUnixTimestampByMilliseconds(DateTime dt)
+        {
+            DateTimeOffset dto = new DateTimeOffset(dt);
+            return dto.ToUnixTimeMilliseconds();
+        }
+
+        #endregion
+
+        #region 毫秒转天时分秒
+        /// <summary>
+        /// 毫秒转天时分秒
+        /// </summary>
+        /// <param name="ms"></param>
+        /// <returns></returns>
+        public static string FormatTime(long ms)
+        {
+            int ss = 1000;
+            int mi = ss * 60;
+            int hh = mi * 60;
+            int dd = hh * 24;
+
+            long day = ms / dd;
+            long hour = (ms - day * dd) / hh;
+            long minute = (ms - day * dd - hour * hh) / mi;
+            long second = (ms - day * dd - hour * hh - minute * mi) / ss;
+            long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss;
+
+            string sDay = day < 10 ? "0" + day : "" + day; //天
+            string sHour = hour < 10 ? "0" + hour : "" + hour;//小时
+            string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟
+            string sSecond = second < 10 ? "0" + second : "" + second;//秒
+            string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
+            sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond;
+
+            return string.Format("{0} 天 {1} 小时 {2} 分 {3} 秒", sDay, sHour, sMinute, sSecond);
+        }
+        #endregion
+
+        #region 获取unix时间戳
+        /// <summary>
+        /// 获取unix时间戳(毫秒)
+        /// </summary>
+        /// <param name="dt"></param>
+        /// <returns></returns>
+        public static long GetUnixTimeStamp(DateTime dt)
+        {
+            long unixTime = ((DateTimeOffset)dt).ToUnixTimeMilliseconds();
+            return unixTime;
+        }
+
+        public static long GetUnixTimeSeconds(DateTime dt)
+        {
+            long unixTime = ((DateTimeOffset)dt).ToUnixTimeSeconds();
+            return unixTime;
+        }
+        #endregion
+
+        #region 获取日期天的最小时间
+        public static DateTime GetDayMinDate(DateTime dt)
+        {
+            DateTime min = new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0);
+            return min;
+        }
+        #endregion
+
+        #region 获取日期天的最大时间
+        public static DateTime GetDayMaxDate(DateTime dt)
+        {
+            DateTime max = new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59);
+            return max;
+        }
+        #endregion
+
+        #region 获取日期天的最大时间
+        public static string FormatDateTime(DateTime? dt)
+        {
+            if (dt != null)
+            {
+                if (dt.Value.Year == DateTime.Now.Year)
+                {
+                    return dt.Value.ToString("MM-dd HH:mm");
+                }
+                else
+                {
+                    return dt.Value.ToString("yyyy-MM-dd HH:mm");
+                }
+            }
+            return string.Empty;
+        }
+        #endregion
+    }
+}

+ 84 - 0
Constant/HttpStatus.cs

@@ -0,0 +1,84 @@
+namespace Infrastructure.Constant
+{
+    public class HttpStatus
+    {
+        /// <summary>
+        /// 操作成功
+        /// </summary>
+        public static readonly int SUCCESS = 200;
+        /// <summary>
+        /// 对象创建成功
+        /// </summary>
+        public static readonly int CREATED = 201;
+
+        /// <summary>
+        /// 请求已经被接受
+        /// </summary>
+        public static readonly int ACCEPTED = 202;
+
+        /// <summary>
+        /// 操作已经执行成功,但是没有返回数据
+        /// </summary>
+        public static readonly int NO_CONTENT = 204;
+
+        /// <summary>
+        /// 资源已被移除
+        /// </summary>
+        public static readonly int MOVED_PERM = 301;
+
+        /// <summary>
+        /// 重定向
+        /// </summary>
+        public static readonly int SEE_OTHER = 303;
+
+        /// <summary>
+        /// 资源没有被修改
+        /// </summary>
+        public static readonly int NOT_MODIFIED = 304;
+
+        /// <summary>
+        /// 参数列表错误(缺少,格式不匹配)
+        /// </summary>
+        public static readonly int BAD_REQUEST = 400;
+
+        /// <summary>
+        /// 未授权
+        /// </summary>
+        public static readonly int UNAUTHORIZED = 401;
+
+        /// <summary>
+        /// 访问受限,授权过期
+        /// </summary>
+        public static readonly int FORBIDDEN = 403;
+
+        /// <summary>
+        /// 资源,服务未找到
+        /// </summary>
+        public static readonly int NOT_FOUND = 404;
+
+        /// <summary>
+        /// 不允许的http方法
+        /// </summary>
+        public static readonly int BAD_METHOD = 405;
+
+        /// <summary>
+        /// 资源冲突,或者资源被锁
+        /// </summary>
+        public static readonly int CONFLICT = 409;
+
+        /// <summary>
+        /// 不支持的数据,媒体类型
+        /// </summary>
+        public static readonly int UNSUPPORTED_TYPE = 415;
+
+        /// <summary>
+        /// 系统内部错误
+        /// </summary>
+        public static readonly int ERROR = 500;
+
+        /// <summary>
+        /// 接口未实现
+        /// </summary>
+        public static readonly int NOT_IMPLEMENTED = 501;
+    }
+}

BIN
Controllers/.DS_Store


+ 234 - 0
Controllers/Base/BaseController.cs

@@ -0,0 +1,234 @@
+using Extensions;
+using Infrastructure;
+using Infrastructure.Model;
+using Microsoft.AspNetCore.Mvc;
+using MiniExcelLibs;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using System.Web;
+
+namespace Controllers
+{
+    /// <summary>
+    /// web层通用数据处理
+    /// </summary>
+    //[ApiController]
+    public class BaseController : ControllerBase
+    {
+        public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
+
+        /// <summary>
+        /// 返回成功封装
+        /// </summary>
+        /// <param name="data"></param>
+        /// <param name="timeFormatStr"></param>
+        /// <returns></returns>
+        protected IActionResult SUCCESS(object data, string timeFormatStr = "yyyy-MM-dd HH:mm:ss")
+        {
+            string jsonStr = GetJsonStr(GetApiResult(data != null ? ResultCode.SUCCESS : ResultCode.NO_DATA, data), timeFormatStr);
+            return Content(jsonStr, "application/json");
+        }
+
+        /// <summary>
+        /// json输出带时间格式的
+        /// </summary>
+        /// <param name="apiResult"></param>
+        /// <returns></returns>
+        protected IActionResult ToResponse(ApiResult apiResult)
+        {
+            string jsonStr = GetJsonStr(apiResult, TIME_FORMAT_FULL);
+
+            return Content(jsonStr, "application/json");
+        }
+
+        protected IActionResult ToResponse(long rows, string timeFormatStr = "yyyy-MM-dd HH:mm:ss")
+        {
+            string jsonStr = GetJsonStr(ToJson(rows), timeFormatStr);
+
+            return Content(jsonStr, "application/json");
+        }
+
+        protected IActionResult ToResponse(ResultCode resultCode, string msg = "")
+        {
+            return ToResponse(new ApiResult((int)resultCode, msg));
+        }
+
+        /// <summary>
+        /// 导出Excel
+        /// </summary>
+        /// <param name="path">完整文件路径</param>
+        /// <param name="fileName">带扩展文件名</param>
+        /// <returns></returns>
+        protected IActionResult ExportExcel(string path, string fileName)
+        {
+            //var webHostEnvironment = App.WebHostEnvironment;
+            if (!Path.Exists(path))
+            {
+                throw new CustomException(fileName + "文件不存在");
+            }
+            var stream = System.IO.File.OpenRead(path);  //创建文件流
+
+            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
+            return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName));
+        }
+
+        /// <summary>
+        /// 下载文件
+        /// </summary>
+        /// <param name="path"></param>
+        /// <param name="fileName">文件名,一定要带扩展名</param>
+        /// <returns></returns>
+        protected IActionResult DownFile(string path, string fileName)
+        {
+            if (!System.IO.File.Exists(path))
+            {
+                return NotFound();
+            }
+            var stream = System.IO.File.OpenRead(path);  //创建文件流
+            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
+            return File(stream, "application/octet-stream", HttpUtility.UrlEncode(fileName));
+        }
+
+        #region 方法
+
+        /// <summary>
+        /// 响应返回结果
+        /// </summary>
+        /// <param name="rows">受影响行数</param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        protected ApiResult ToJson(long rows, object? data = null)
+        {
+            return rows > 0 ? ApiResult.Success("success", data) : GetApiResult(ResultCode.FAIL);
+        }
+
+        /// <summary>
+        /// 全局Code使用
+        /// </summary>
+        /// <param name="resultCode"></param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        protected ApiResult GetApiResult(ResultCode resultCode, object? data = null)
+        {
+            var msg = resultCode.GetDescription();
+
+            return new ApiResult((int)resultCode, msg, data);
+        }
+        protected ApiResult Success()
+        {
+            return GetApiResult(ResultCode.SUCCESS);
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="apiResult"></param>
+        /// <param name="timeFormatStr"></param>
+        /// <returns></returns>
+        private static string GetJsonStr(ApiResult apiResult, string timeFormatStr)
+        {
+            if (string.IsNullOrEmpty(timeFormatStr))
+            {
+                timeFormatStr = TIME_FORMAT_FULL;
+            }
+            var serializerSettings = new JsonSerializerSettings
+            {
+                // 设置为驼峰命名
+                ContractResolver = new CamelCasePropertyNamesContractResolver(),
+                DateFormatString = timeFormatStr
+            };
+
+            return JsonConvert.SerializeObject(apiResult, Formatting.Indented, serializerSettings);
+        }
+        #endregion
+
+        /// <summary>
+        /// 导出Excel
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="list"></param>
+        /// <param name="sheetName"></param>
+        /// <param name="fileName"></param>
+        protected string ExportExcel<T>(List<T> list, string sheetName, string fileName)
+        {
+            return ExportExcelMini(list, sheetName, fileName).Item1;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="list"></param>
+        /// <param name="sheetName"></param>
+        /// <param name="fileName"></param>
+        /// <returns></returns>
+        protected (string, string) ExportExcelMini<T>(List<T> list, string sheetName, string fileName)
+        {
+            IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment));
+            string sFileName = $"{fileName}{DateTime.Now:MM-dd-HHmmss}.xlsx";
+            string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "export", sFileName);
+
+            Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
+
+            MiniExcel.SaveAs(fullPath, list, sheetName: sheetName);
+            return (sFileName, fullPath);
+        }
+
+        /// <summary>
+        /// 导出多个工作表(Sheet)
+        /// </summary>
+        /// <param name="sheets"></param>
+        /// <param name="fileName"></param>
+        /// <returns></returns>
+        protected (string, string) ExportExcelMini(Dictionary<string, object> sheets, string fileName)
+        {
+            IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment));
+            string sFileName = $"{fileName}{DateTime.Now:MM-dd-HHmmss}.xlsx";
+            string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "export", sFileName);
+
+            Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
+
+            MiniExcel.SaveAs(fullPath, sheets);
+            return (sFileName, fullPath);
+        }
+
+        /// <summary>
+        /// 下载导入模板
+        /// </summary>
+        /// <typeparam name="T">数据类型</typeparam>
+        /// <param name="list">空数据类型集合</param>
+        /// <param name="fileName">下载文件名</param>
+        /// <returns></returns>
+        protected (string, string) DownloadImportTemplate<T>(List<T> list, string fileName)
+        {
+            IWebHostEnvironment webHostEnvironment = App.WebHostEnvironment;
+            string sFileName = $"{fileName}.xlsx";
+            string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName);
+
+            //不存在模板创建模板
+            if (!Directory.Exists(fullPath))
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
+            }
+            if (!Path.Exists(fullPath))
+            {
+                MiniExcel.SaveAs(fullPath, list, overwriteFile: true);
+            }
+            return (sFileName, fullPath);
+        }
+
+        /// <summary>
+        /// 下载指定文件模板
+        /// </summary>
+        /// <param name="fileName">下载文件名</param>
+        /// <returns></returns>
+        protected (string, string) DownloadImportTemplate(string fileName)
+        {
+            IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment));
+            string sFileName = $"{fileName}.xlsx";
+            string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName);
+
+            return (sFileName, fullPath);
+        }
+    }
+}

+ 71 - 0
Controllers/Base/HomeController.cs

@@ -0,0 +1,71 @@
+using Attribute;
+using Base;
+using Common;
+using Dto;
+using Enums;
+using Filters;
+using Infrastructure;
+using Infrastructure.Model;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using Model;
+using Model.Base;
+using Services;
+using Util;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 主界面Controller
+    /// </summary>
+    
+    [Route("/")]
+    public class HomeController : BaseController
+    {
+
+        private readonly ISysPermissionService permissionService;
+
+        public HomeController(ISysPermissionService permissionService)
+        {
+            this.permissionService = permissionService;
+        }
+
+        [HttpPost("testlogin")]
+        public IActionResult Login([FromBody] LoginDto parm)
+        {
+            TokenModel loginUser = new(new TokenModel()
+            {
+                Username = parm.AdminName,
+                UserId = 1
+            }, new List<Roles>());
+            List<string> permissions = permissionService.List();
+            CacheService.SetUserPerms(GlobalConstant.UserPermKEY + "1", permissions);
+            return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
+        }
+
+        [HttpGet(template: "test")]
+        public IActionResult Test([FromQuery] LoginDto parm)
+        {
+            TokenModel loginUser = new(new TokenModel()
+            {
+                Username = parm.AdminName,
+                UserId = 1
+            }, new List<Roles>());
+            List<string> permissions = permissionService.List();
+            CacheService.SetUserPerms(GlobalConstant.UserPermKEY + "1", permissions);
+            return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
+        }
+
+        // [HttpGet(template: "test1")]
+        // public IActionResult Test1()
+        // {
+        //     List<TestVo> list = new List<TestVo>();
+        //     list.Add(new TestVo());
+        //     list.Add(new TestVo(){
+        //         Subs = new TestSubVo()
+        //     });
+        //     return SUCCESS(list);
+        // }
+    }
+}

+ 170 - 0
Controllers/Base/SysDeptController.cs

@@ -0,0 +1,170 @@
+using Attribute;
+using Common;
+using Dto;
+using Enums;
+using Extensions;
+using Filters;
+using Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using Model;
+using Services;
+using System.Collections;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 部门
+    /// </summary>
+    [Verify]
+    [Route("system/dept")]
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysDeptController : BaseController
+    {
+        public ISysDeptService DeptService;
+        public ISysUserService UserService;
+        public SysDeptController(ISysDeptService deptService
+            , ISysUserService userService)
+        {
+            DeptService = deptService;
+            UserService = userService;
+        }
+
+        /// <summary>
+        /// 获取部门列表
+        /// </summary>
+        /// <returns></returns>
+        [ActionPermissionFilter(Permission = "system:dept:list")]
+        [HttpGet("list")]
+        public IActionResult List([FromQuery] SysDeptQueryDto dept)
+        {
+            return SUCCESS(DeptService.GetSysDepts(dept), TIME_FORMAT_FULL);
+        }
+
+        /// <summary>
+        /// 查询部门列表(排除节点)
+        /// </summary>
+        /// <param name="deptId"></param>
+        /// <returns></returns>
+        [HttpGet("list/exclude/{deptId}")]
+        public IActionResult ExcludeChild(long deptId)
+        {
+            var depts = DeptService.GetSysDepts(new SysDeptQueryDto());
+
+            // for (int i = 0; i < depts.Count; i++)
+            // {
+            //     SysDept d = depts[i];
+            //     long[] deptIds = Tools.SpitLongArrary(d.Ancestors);
+            //     if (d.DeptId == deptId || ((IList)deptIds).Contains(deptId))
+            //     {
+            //         depts.Remove(d);
+            //     }
+            // }
+            return SUCCESS(depts);
+        }
+
+        /// <summary>
+        /// 获取部门下拉树列表
+        /// </summary>
+        /// <param name="dept"></param>
+        /// <returns></returns>
+        [HttpGet("treeselect")]
+        public IActionResult TreeSelect(SysDeptQueryDto dept)
+        {
+            var depts = DeptService.GetSysDepts(dept);
+
+            return SUCCESS(DeptService.BuildDeptTreeSelect(depts), TIME_FORMAT_FULL);
+        }
+
+        /// <summary>
+        /// 获取角色部门信息
+        /// 加载对应角色部门列表树
+        /// </summary>
+        /// <param name="roleId"></param>
+        /// <returns></returns>        
+        [HttpGet("roleDeptTreeselect/{roleId}")]
+        public IActionResult RoleMenuTreeselect(int roleId)
+        {
+            var depts = DeptService.GetSysDepts(new SysDeptQueryDto());
+            var checkedKeys = DeptService.SelectRoleDepts(roleId);
+            return SUCCESS(new
+            {
+                checkedKeys,
+                depts = DeptService.BuildDeptTreeSelect(depts),
+            });
+        }
+
+        /// <summary>
+        /// 根据部门编号获取详细信息
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("{deptId}")]
+        [ActionPermissionFilter(Permission = "system:dept:query")]
+        public IActionResult GetInfo(long deptId)
+        {
+            var info = DeptService.GetFirst(f => f.DeptId == deptId);
+            return SUCCESS(info);
+        }
+
+        /// <summary>
+        /// 新增部门
+        /// </summary>
+        /// <param name="dept"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [Log(Title = "部门管理", BusinessType = BusinessType.INSERT)]
+        [ActionPermissionFilter(Permission = "system:dept:add")]
+        public IActionResult Add([FromBody] SysDept dept)
+        {
+            if (UserConstants.NOT_UNIQUE.Equals(DeptService.CheckDeptNameUnique(dept)))
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, $"新增部门{dept.Name}失败,部门名称已存在");
+            }
+            dept.CreateBy = HttpContext.GetName();
+            return ToResponse(DeptService.InsertDept(dept));
+        }
+
+        /// <summary>
+        /// 修改部门
+        /// </summary>
+        /// <param name="dept"></param>
+        /// <returns></returns>
+        [HttpPut]
+        [Log(Title = "部门管理", BusinessType = BusinessType.UPDATE)]
+        [ActionPermissionFilter(Permission = "system:dept:update")]
+        public IActionResult Update([FromBody] SysDept dept)
+        {
+            if (UserConstants.NOT_UNIQUE.Equals(DeptService.CheckDeptNameUnique(dept)))
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, $"修改部门{dept.Name}失败,部门名称已存在");
+            }
+            else if (dept.ParentId.Equals(dept.DeptId))
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, $"修改部门{dept.Name}失败,上级部门不能是自己");
+            }
+            dept.UpdateBy = HttpContext.GetName();
+            return ToResponse(DeptService.UpdateDept(dept));
+        }
+
+        /// <summary>
+        /// 删除部门
+        /// </summary>
+        /// <returns></returns>
+        [HttpDelete("{deptId}")]
+        [ActionPermissionFilter(Permission = "system:dept:remove")]
+        [Log(Title = "部门管理", BusinessType = BusinessType.DELETE)]
+        public IActionResult Remove(long deptId)
+        {
+            if (DeptService.Queryable().Count(it => it.ParentId == deptId && it.DelFlag == "0") > 0)
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, $"存在下级部门,不允许删除");
+            }
+            if (UserService.Queryable().Count(it => it.DeptId == deptId && it.DelFlag == "0") > 0)
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, $"部门存在用户,不允许删除");
+            }
+
+            return SUCCESS(DeptService.Delete(deptId));
+        }
+    }
+}

+ 169 - 0
Controllers/Base/SysLoginController.cs

@@ -0,0 +1,169 @@
+using Attribute;
+using Base;
+using Dto;
+using Enums;
+using Extensions;
+using Filters;
+using Infrastructure;
+using Mapster;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Model;
+using Model.Base;
+using Services;
+using Util;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 登录
+    /// </summary>
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysLoginController : BaseController
+    {
+        private readonly ISysUserService sysUserService;
+        private readonly ISysMenuService sysMenuService;
+        private readonly ISysLoginService sysLoginService;
+        private readonly ISysPermissionService permissionService;
+        private readonly ISysRoleService roleService;
+
+        public SysLoginController(
+            ISysMenuService sysMenuService,
+            ISysUserService sysUserService,
+            ISysLoginService sysLoginService,
+            ISysPermissionService permissionService,
+            ISysRoleService sysRoleService)
+        {
+            this.sysMenuService = sysMenuService;
+            this.sysUserService = sysUserService;
+            this.sysLoginService = sysLoginService;
+            this.permissionService = permissionService;
+            roleService = sysRoleService;
+        }
+
+
+        /// <summary>
+        /// 登录
+        /// </summary>
+        /// <param name="loginBody">登录对象</param>
+        /// <returns></returns>
+        [Route("/v1/skb/sysServer/oauth2/token")]
+        [HttpPost]
+        [Log(Title = "登录")]
+        public IActionResult Login([FromBody] LoginBodyDto loginBody)
+        {
+            if (loginBody == null) { throw new CustomException("请求参数错误"); }
+            loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
+
+            sysLoginService.CheckLockUser(loginBody.Username);
+            string location = ""; //HttpContextExtension.GetIpInfo(loginBody.LoginIP);
+            var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
+
+            List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
+            //权限集合 eg *:*:*,system:user:list
+            List<string> permissions = permissionService.GetMenuPermission(user);
+
+            TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>());
+            CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
+            
+            return SUCCESS(sysLoginService.GetLoginInfo(loginUser));
+        }
+
+        /// <summary>
+        /// 注销
+        /// </summary>
+        /// <returns></returns>
+        [Log(Title = "注销")]
+        [HttpPost("logout")]
+        public IActionResult LogOut()
+        {
+            //Task.Run(async () =>
+            //{
+            //    //注销登录的用户,相当于ASP.NET中的FormsAuthentication.SignOut  
+            //    await HttpContext.SignOutAsync();
+            //}).Wait();
+            var userid = HttpContext.GetUId();
+            var name = HttpContext.GetName();
+
+            CacheService.RemoveUserPerms(GlobalConstant.UserPermKEY + userid);
+            return SUCCESS(new { name, id = userid });
+        }
+
+        /// <summary>
+        /// 获取用户信息
+        /// </summary>
+        /// <returns></returns>
+        [Verify]
+        [HttpGet("getInfo")]
+        public IActionResult GetUserInfo()
+        {
+            long userid = HttpContext.GetUId();
+            var user = sysUserService.SelectUserById(userid);
+
+            //前端校验按钮权限使用
+            //角色集合 eg: admin,yunying,common
+            List<string> roles = permissionService.GetRolePermission(user);
+            //权限集合 eg *:*:*,system:user:list
+            List<string> permissions = permissionService.GetMenuPermission(user);
+
+            return SUCCESS(new { user, roles, permissions });
+        }
+
+        /// <summary>
+        /// 获取路由信息
+        /// </summary>
+        /// <returns></returns>
+        [Verify]
+        [HttpGet("getRouters")]
+        public IActionResult GetRouters()
+        {
+            long uid = HttpContext.GetUId();
+            var menus = sysMenuService.SelectMenuTreeByUserId(uid);
+
+            return SUCCESS(sysMenuService.BuildMenus(menus));
+        }
+
+        /// <summary>
+        /// 注册
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("/register")]
+        [AllowAnonymous]
+        [Log(Title = "注册", BusinessType = BusinessType.INSERT)]
+        public IActionResult Register([FromBody] RegisterDto dto)
+        {
+            dto.UserIP = HttpContext.GetClientUserIp();
+            SysUser user = sysUserService.Register(dto);
+            if (user.UserId > 0)
+            {
+                return SUCCESS(user);
+            }
+            return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员");
+        }
+        
+        /// <summary>
+        /// 生成二维码
+        /// </summary>
+        /// <param name="uuid"></param>
+        /// <param name="deviceId"></param>
+        /// <returns></returns>
+        [HttpGet("/GenerateQrcode")]
+        public IActionResult GenerateQrcode(string uuid, string deviceId)
+        {
+            var state = Guid.NewGuid().ToString();
+            var dict = new Dictionary<string, object>
+            {
+                { "state", state }
+            };
+            CacheService.SetScanLogin(uuid, dict);
+            return SUCCESS(new
+            {
+                status = 1,
+                state,
+                uuid,
+                codeContent = new { uuid, deviceId }// "https://qm.qq.com/cgi-bin/qm/qr?k=kgt4HsckdljU0VM-0kxND6d_igmfuPlL&authKey=r55YUbruiKQ5iwC/folG7KLCmZ++Y4rQVgNlvLbUniUMkbk24Y9+zNuOmOnjAjRc&noverify=0"
+            });
+        }
+    }
+}

+ 218 - 0
Controllers/Base/SysMenuController.cs

@@ -0,0 +1,218 @@
+using Attribute;
+using Dto;
+using Enums;
+using Extensions;
+using Filters;
+using Infrastructure;
+using Infrastructure.Model;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using Model;
+using Services;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 系统菜单
+    /// </summary>
+    [Verify]
+    [Route("/system/menu")]
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysMenuController : BaseController
+    {
+        private readonly ISysRoleService sysRoleService;
+        private readonly ISysMenuService sysMenuService;
+        private readonly ISysRoleMenuService sysRoleMenuService;
+
+        public SysMenuController(
+            ISysRoleService sysRoleService,
+            ISysMenuService sysMenuService,
+            ISysRoleMenuService sysRoleMenuService)
+        {
+            this.sysRoleService = sysRoleService;
+            this.sysMenuService = sysMenuService;
+            this.sysRoleMenuService = sysRoleMenuService;
+        }
+
+        /// <summary>
+        /// 获取菜单树列表
+        /// </summary>
+        /// <returns></returns>
+        [ActionPermissionFilter(Permission = "system:menu:list")]
+        [HttpGet("treelist")]
+        public IActionResult TreeMenuList([FromQuery] MenuQueryDto menu)
+        {
+            long userId = HttpContext.GetUId();
+            return SUCCESS(sysMenuService.SelectTreeMenuList(menu, userId), "yyyy-MM-dd HH:mm:ss");
+        }
+
+        /// <summary>
+        /// 获取菜单列表
+        /// </summary>
+        /// <returns></returns>
+        [ActionPermissionFilter(Permission = "system:menu:list")]
+        [HttpGet("list")]
+        public IActionResult MenuList([FromQuery] MenuQueryDto menu)
+        {
+            long userId = HttpContext.GetUId();
+            return SUCCESS(sysMenuService.SelectMenuList(menu, userId), "yyyy-MM-dd HH:mm:ss");
+        }
+
+        /// <summary>
+        /// 根据菜单编号获取详细信息
+        /// </summary>
+        /// <param name="menuId"></param>
+        /// <returns></returns>
+        [HttpGet("{menuId}")]
+        [ActionPermissionFilter(Permission = "system:menu:query")]
+        public IActionResult GetMenuInfo(int menuId = 0)
+        {
+            return SUCCESS(sysMenuService.GetMenuByMenuId(menuId), "yyyy-MM-dd HH:mm:ss");
+        }
+
+        /// <summary>
+        /// 根据菜单编号获取菜单列表,菜单管理首次进入
+        /// </summary>
+        /// <param name="menuId"></param>
+        /// <returns></returns>
+        [HttpGet("list/{menuId}")]
+        [ActionPermissionFilter(Permission = "system:menu:query")]
+        public IActionResult GetMenuList(int menuId = 0)
+        {
+            long userId = HttpContext.GetUId();
+            return SUCCESS(sysMenuService.GetMenusByMenuId(menuId, userId), "yyyy-MM-dd HH:mm:ss");
+        }
+
+        /// <summary>
+        /// 获取角色菜单信息
+        /// 加载对应角色菜单列表树
+        /// </summary>
+        /// <param name="roleId"></param>
+        /// <returns></returns>        
+        [HttpGet("roleMenuTreeselect/{roleId}")]
+        public IActionResult RoleMenuTreeselect(int roleId)
+        {
+            long userId = HttpContext.GetUId();
+            var menus = sysMenuService.SelectMenuList(new MenuQueryDto(), userId);
+            var checkedKeys = sysRoleService.SelectUserRoleMenus(roleId);
+            return SUCCESS(new
+            {
+                checkedKeys,
+                menus = sysMenuService.BuildMenuTreeSelect(menus),
+            });
+        }
+
+        /// <summary>
+        /// 修改菜单
+        /// </summary>
+        /// <param name="menuDto"></param>
+        /// <returns></returns>
+        [HttpPost("edit")]
+        [Log(Title = "菜单管理", BusinessType = BusinessType.UPDATE)]
+        [ActionPermissionFilter(Permission = "system:menu:edit")]
+        public IActionResult MenuEdit([FromBody] MenuDto menuDto)
+        {
+            if (menuDto == null) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+            //if (UserConstants.NOT_UNIQUE.Equals(sysMenuService.CheckMenuNameUnique(MenuDto)))
+            //{
+            //    return ToResponse(ApiResult.Error($"修改菜单'{MenuDto.menuName}'失败,菜单名称已存在"));
+            //}
+            var config = new TypeAdapterConfig();
+            //映射规则
+            config.ForType<SysMenu, MenuDto>()
+                .NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);//忽略字段名称的大小写;//忽略除以上配置的所有字段
+
+            var modal = menuDto.Adapt<SysMenu>(config).ToUpdate(HttpContext);
+            if (modal.MenuType == "3" && !modal.Path.StartsWith("http"))
+            {
+                return ToResponse(ApiResult.Error($"修改菜单'{modal.Name}'失败,地址必须以http(s)://开头"));
+            }
+            if (modal.MenuId.Equals(modal.ParentId))
+            {
+                return ToResponse(ApiResult.Error($"修改菜单'{modal.Name}'失败,上级菜单不能选择自己"));
+            }
+            modal.UpdateBy = HttpContext.GetName();
+            long result = sysMenuService.EditMenu(modal);
+
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 添加菜单
+        /// </summary>
+        /// <param name="menuDto"></param>
+        /// <returns></returns>
+        [HttpPut("add")]
+        [Log(Title = "菜单管理", BusinessType = BusinessType.INSERT)]
+        [ActionPermissionFilter(Permission = "system:menu:add")]
+        public IActionResult MenuAdd([FromBody] MenuDto menuDto)
+        {
+            var config = new TypeAdapterConfig();
+            //映射规则
+            config.ForType<SysMenu, MenuDto>()
+                .NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);
+            var menu = menuDto.Adapt<SysMenu>(config).ToCreate(HttpContext);
+
+            if (menu == null) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+            if (UserConstants.NOT_UNIQUE.Equals(sysMenuService.CheckMenuNameUnique(menu)))
+            {
+                return ToResponse(ApiResult.Error($"新增菜单'{menu.Name}'失败,菜单名称已存在"));
+            }
+            if (menu.MenuType == "3" && !menu.Path.StartsWith("http"))
+            {
+                return ToResponse(ApiResult.Error($"新增菜单'{menu.Name}'失败,地址必须以http(s)://开头"));
+            }
+
+            menu.CreateBy = HttpContext.GetName();
+            long result = sysMenuService.AddMenu(menu);
+
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 菜单删除
+        /// </summary>
+        /// <param name="menuId"></param>
+        /// <returns></returns>
+        [HttpDelete("{menuId}")]
+        [Log(Title = "菜单管理", BusinessType = BusinessType.DELETE)]
+        [ActionPermissionFilter(Permission = "system:menu:remove")]
+        public IActionResult Remove(int menuId = 0)
+        {
+            if (sysMenuService.HasChildByMenuId(menuId))
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, "存在子菜单,不允许删除");
+            }
+            if (sysRoleMenuService.CheckMenuExistRole(menuId))
+            {
+                return ToResponse(ResultCode.CUSTOM_ERROR, "菜单已分配,不允许删除");
+            }
+            int result = sysMenuService.DeleteMenuById(menuId);
+
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 保存排序
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        [ActionPermissionFilter(Permission = "system:menu:update")]
+        [HttpGet("ChangeSort")]
+        [Log(Title = "保存排序", BusinessType = BusinessType.UPDATE)]
+        public IActionResult ChangeSort(int id = 0, int value = 0)
+        {
+            MenuDto MenuDto = new()
+            {
+                MenuId = id,
+                SortOrder = value
+            };
+            if (MenuDto == null) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+
+            int result = sysMenuService.ChangeSortMenu(MenuDto);
+            return ToResponse(result);
+        }
+    }
+}

+ 139 - 0
Controllers/Base/SysPostController.cs

@@ -0,0 +1,139 @@
+using Attribute;
+using Common;
+using Enums;
+using Filters;
+using Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using Model;
+using Model.Base;
+using Services;
+
+
+namespace Controllers
+{
+    /// <summary>
+    /// 岗位管理
+    /// </summary>
+    [Verify]
+    [Route("system/post")]
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysPostController : BaseController
+    {
+        private readonly ISysPostService PostService;
+        public SysPostController(ISysPostService postService)
+        {
+            PostService = postService;
+        }
+
+        /// <summary>
+        /// 岗位列表查询
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("list")]
+        [ActionPermissionFilter(Permission = "system:post:list")]
+        public IActionResult List([FromQuery] SysPost post, [FromQuery] PagerInfo pagerInfo)
+        {
+            var predicate = Expressionable.Create<SysPost>();
+            var list = PostService.GetPages(predicate.ToExpression(), pagerInfo, s => new { s.PostSort });
+
+            return SUCCESS(list);
+        }
+
+        /// <summary>
+        /// 岗位查询
+        /// </summary>
+        /// <param name="postId"></param>
+        /// <returns></returns>
+        [HttpGet("{postId}")]
+        [ActionPermissionFilter(Permission = "system:post:query")]
+        public IActionResult Query(long postId = 0)
+        {
+            return SUCCESS(PostService.GetId(postId));
+        }
+
+        /// <summary>
+        /// 岗位管理
+        /// </summary>
+        /// <param name="post"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [ActionPermissionFilter(Permission = "system:post:add")]
+        [Log(Title = "岗位添加", BusinessType = BusinessType.INSERT)]
+        public IActionResult Add([FromBody] SysPost post)
+        {
+            if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostNameUnique(post)))
+            {
+                throw new CustomException($"修改岗位{post.PostName}失败,岗位名已存在");
+            }
+            if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostCodeUnique(post)))
+            {
+                throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在");
+            }
+            post.ToCreate(HttpContext);
+
+            return ToResponse(PostService.Add(post));
+        }
+
+        /// <summary>
+        /// 岗位管理
+        /// </summary>
+        /// <param name="post"></param>
+        /// <returns></returns>
+        [HttpPut]
+        [ActionPermissionFilter(Permission = "system:post:edit")]
+        [Log(Title = "岗位编辑", BusinessType = BusinessType.UPDATE)]
+        public IActionResult Update([FromBody] SysPost post)
+        {
+            if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostNameUnique(post)))
+            {
+                throw new CustomException($"修改岗位{post.PostName}失败,岗位名已存在");
+            }
+            if (UserConstants.NOT_UNIQUE.Equals(PostService.CheckPostCodeUnique(post)))
+            {
+                throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在");
+            }
+            post.ToUpdate(HttpContext);
+            return ToResponse(PostService.Update(post));
+        }
+
+        /// <summary>
+        /// 岗位删除
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpDelete("{id}")]
+        [ActionPermissionFilter(Permission = "system:post:remove")]
+        [Log(Title = "岗位删除", BusinessType = BusinessType.DELETE)]
+        public IActionResult Delete(string id)
+        {
+            int[] ids = Tools.SpitIntArrary(id);
+            return ToResponse(PostService.Delete(ids));
+        }
+
+        /// <summary>
+        /// 获取岗位选择框列表
+        /// </summary>
+        [HttpGet("optionselect")]
+        public IActionResult Optionselect()
+        {
+            List<SysPost> posts = PostService.GetAll();
+            return SUCCESS(posts);
+        }
+
+        /// <summary>
+        /// 岗位导出
+        /// </summary>
+        /// <returns></returns>
+        [Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title = "岗位导出")]
+        [HttpGet("export")]
+        [ActionPermissionFilter(Permission = "system:post:export")]
+        public IActionResult Export()
+        {
+            var list = PostService.GetAll();
+
+            var result = ExportExcelMini(list, "post", "岗位列表");
+            return ExportExcel(result.Item2, result.Item1);
+        }
+    }
+}

+ 206 - 0
Controllers/Base/SysRoleController.cs

@@ -0,0 +1,206 @@
+using Attribute;
+using Common;
+using Dto;
+using Enums;
+using Extensions;
+using Filters;
+using Infrastructure;
+using Infrastructure.Model;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using Model;
+using Model.Base;
+using Services;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 角色信息
+    /// </summary>
+    [Verify]
+    [Route("system/role")]
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysRoleController : BaseController
+    {
+        private readonly ISysRoleService sysRoleService;
+        private readonly ISysMenuService sysMenuService;
+
+        public SysRoleController(
+            ISysRoleService sysRoleService,
+            ISysMenuService sysMenuService)
+        {
+            this.sysRoleService = sysRoleService;
+            this.sysMenuService = sysMenuService;
+        }
+
+        /// <summary>
+        /// 获取系统角色管理
+        /// </summary>
+        /// <returns></returns>
+        [ActionPermissionFilter(Permission = "system:role:list")]
+        [HttpGet("list")]
+        public IActionResult List([FromQuery] SysRole role, [FromQuery] PagerInfo pager)
+        {
+            var list = sysRoleService.SelectRoleList(role, pager);
+
+            return SUCCESS(list, TIME_FORMAT_FULL);
+        }
+
+        /// <summary>
+        /// 根据角色编号获取详细信息
+        /// </summary>
+        /// <param name="roleId"></param>
+        /// <returns></returns>
+        [HttpGet("{roleId}")]
+        public IActionResult GetInfo(long roleId = 0)
+        {
+            var info = sysRoleService.SelectRoleById(roleId);
+
+            return SUCCESS(info, TIME_FORMAT_FULL);
+        }
+
+        /// <summary>
+        /// 添加角色
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [ActionPermissionFilter(Permission = "system:role:add")]
+        [Log(Title = "角色管理", BusinessType = BusinessType.INSERT)]
+        [Route("edit")]
+        public IActionResult RoleAdd([FromBody] SysRoleDto dto)
+        {
+            if (dto == null) return ToResponse(ApiResult.Error(101, "请求参数错误"));
+            SysRole sysRoleDto = dto.Adapt<SysRole>();
+            if (UserConstants.NOT_UNIQUE.Equals(sysRoleService.CheckRoleKeyUnique(sysRoleDto)))
+            {
+                return ToResponse(ApiResult.Error((int)ResultCode.CUSTOM_ERROR, $"新增角色'{sysRoleDto.RoleName}'失败,角色权限已存在"));
+            }
+
+            sysRoleDto.CreateBy = HttpContext.GetName();
+            long roleId = sysRoleService.InsertRole(sysRoleDto);
+
+            return ToResponse(roleId);
+        }
+
+        /// <summary>
+        /// 修改角色
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut]
+        [ActionPermissionFilter(Permission = "system:role:edit")]
+        [Log(Title = "角色管理", BusinessType = BusinessType.UPDATE)]
+        [Route("edit")]
+        public IActionResult RoleEdit([FromBody] SysRoleDto dto)
+        {
+            if (dto == null || dto.RoleId <= 0 || string.IsNullOrEmpty(dto.RoleKey))
+            {
+                return ToResponse(ApiResult.Error(101, "请求参数错误"));
+            }
+            SysRole sysRoleDto = dto.Adapt<SysRole>();
+            sysRoleService.CheckRoleAllowed(sysRoleDto);
+            var info = sysRoleService.SelectRoleById(sysRoleDto.RoleId);
+            if (info != null && info.RoleCode != sysRoleDto.RoleCode)
+            {
+                if (UserConstants.NOT_UNIQUE.Equals(sysRoleService.CheckRoleKeyUnique(sysRoleDto)))
+                {
+                    return ToResponse(ApiResult.Error($"编辑角色'{sysRoleDto.RoleName}'失败,角色权限已存在"));
+                }
+            }
+            sysRoleDto.UpdateBy = HttpContext.GetName();
+            int upResult = sysRoleService.UpdateRole(sysRoleDto);
+            if (upResult > 0)
+            {
+                return SUCCESS(upResult);
+            }
+            return ToResponse(ApiResult.Error($"修改角色'{sysRoleDto.RoleName}'失败,请联系管理员"));
+        }
+
+        /// <summary>
+        /// 根据角色分配菜单
+        /// </summary>
+        /// <param name="sysRoleDto"></param>
+        /// <returns></returns>
+        [HttpPut("dataScope")]
+        [ActionPermissionFilter(Permission = "system:role:authorize")]
+        [Log(Title = "角色管理", BusinessType = BusinessType.UPDATE)]
+        public IActionResult DataScope([FromBody] SysRoleDto sysRoleDto)
+        {
+            if (sysRoleDto == null || sysRoleDto.RoleId <= 0) return ToResponse(ApiResult.Error(101, "请求参数错误"));
+            SysRole sysRole = sysRoleDto.Adapt<SysRole>();
+            sysRoleDto.CreateBy = HttpContext.GetName();
+            sysRoleService.CheckRoleAllowed(sysRole);
+
+            bool result = sysRoleService.AuthDataScope(sysRoleDto);
+
+            return SUCCESS(result);
+        }
+
+        /// <summary>
+        /// 角色删除
+        /// </summary>
+        /// <param name="roleId"></param>
+        /// <returns></returns>
+        [HttpDelete("{roleId}")]
+        [Log(Title = "角色管理", BusinessType = BusinessType.DELETE)]
+        [ActionPermissionFilter(Permission = "system:role:remove")]
+        public IActionResult Remove(string roleId)
+        {
+            long[] roleIds = Tools.SpitLongArrary(roleId);
+            int result = sysRoleService.DeleteRoleByRoleId(roleIds);
+
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 修改角色状态
+        /// </summary>
+        /// <param name="roleDto">角色对象</param>
+        /// <returns></returns>
+        [HttpPut("changeStatus")]
+        [Log(Title = "修改角色状态", BusinessType = BusinessType.UPDATE)]
+        [ActionPermissionFilter(Permission = "system:role:edit")]
+        public IActionResult ChangeStatus([FromBody] SysRole roleDto)
+        {
+            sysRoleService.CheckRoleAllowed(roleDto);
+            int result = sysRoleService.UpdateRoleStatus(roleDto);
+
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 角色导出
+        /// </summary>
+        /// <returns></returns>
+        [Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title = "角色导出")]
+        [HttpGet("export")]
+        [ActionPermissionFilter(Permission = "system:role:export")]
+        public IActionResult Export()
+        {
+            var list = sysRoleService.SelectRoleAll();
+
+            string sFileName = ExportExcel(list, "sysrole", "角色");
+            return SUCCESS(new { path = "/export/" + sFileName, fileName = sFileName });
+        }
+
+        /// <summary>
+        /// 导出角色菜单
+        /// </summary>
+        /// <param name="roleId"></param>
+        /// <returns></returns>
+        [Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title = "角色菜单导出")]
+        [HttpGet("exportRoleMenu")]
+        [ActionPermissionFilter(Permission = "system:role:export")]
+        public IActionResult ExportRoleMenu(int roleId)
+        {
+            MenuQueryDto dto = new() { DelFlag = "0", MenuTypeIds = "M,C,F" };
+
+            var list = sysMenuService.SelectRoleMenuListByRole(dto, roleId);
+
+            var result = ExportExcelMini(list, roleId.ToString(), "角色菜单");
+            return ExportExcel(result.Item2, result.Item1);
+        }
+    }
+}

+ 223 - 0
Controllers/Base/SysUserController.cs

@@ -0,0 +1,223 @@
+using Attribute;
+using Common;
+using Dto;
+using Enums;
+using Filters;
+using Infrastructure;
+using Infrastructure.Model;
+using Mapster;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using MiniExcelLibs;
+using Model;
+using Model.Base;
+using Services;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 用户管理
+    /// </summary>
+    [Verify]
+    [Route("system/user")]
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysUserController : BaseController
+    {
+        private readonly ISysUserService UserService;
+        private readonly ISysRoleService RoleService;
+        private readonly ISysPostService PostService;
+        private readonly ISysUserPostService UserPostService;
+
+        public SysUserController(
+            ISysUserService userService,
+            ISysRoleService roleService,
+            ISysPostService postService,
+            ISysUserPostService userPostService)
+        {
+            UserService = userService;
+            RoleService = roleService;
+            PostService = postService;
+            UserPostService = userPostService;
+        }
+
+        /// <summary>
+        /// 用户管理 -> 获取用户
+        /// /system/user/list
+        /// </summary>
+        /// <returns></returns>
+        [ActionPermissionFilter(Permission = "system:user:list")]
+        [HttpGet("list")]
+        public IActionResult List([FromQuery] SysUserQueryDto user, PagerInfo pager)
+        {
+            var list = UserService.SelectUserList(user, pager);
+
+            return SUCCESS(list);
+        }
+
+        /// <summary>
+        /// 用户管理 -> 编辑、添加用户获取用户,信息查询
+        /// </summary>
+        /// <param name="userId"></param>
+        /// <returns></returns>
+        [HttpGet("")]
+        [HttpGet("{userId:int=0}")]
+        [ActionPermissionFilter(Permission = "system:user:query")]
+        public IActionResult GetInfo(int userId)
+        {
+            Dictionary<string, object> dic = new();
+            var roles = RoleService.SelectRoleAll();
+            dic.Add("roles", roles);
+            //dic.Add("roles", SysUser.IsAdmin(userId) ? roles : roles.FindAll(f => !f.IsAdmin()));
+            dic.Add("posts", PostService.GetAll());
+
+            //编辑
+            if (userId > 0)
+            {
+                SysUser sysUser = UserService.SelectUserById(userId);
+                dic.Add("user", sysUser);
+                dic.Add("postIds", UserPostService.GetUserPostsByUserId(userId));
+                dic.Add("roleIds", sysUser.RoleIds);
+            }
+
+            return SUCCESS(dic);
+        }
+
+        /// <summary>
+        /// 添加用户
+        /// </summary>
+        /// <param name="parm"></param>
+        /// <returns></returns>
+        [HttpPost("add")]
+        [Log(Title = "用户管理", BusinessType = BusinessType.INSERT)]
+        [ActionPermissionFilter(Permission = "system:user:add")]
+        public IActionResult AddUser([FromBody] SysUserDto parm)
+        {
+            var user = parm.Adapt<SysUser>().ToCreate(HttpContext);
+            if (user == null) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+            if (UserConstants.NOT_UNIQUE.Equals(UserService.CheckUserNameUnique(user.Username)))
+            {
+                return ToResponse(ApiResult.Error($"新增用户 '{user.Username}'失败,登录账号已存在"));
+            }
+
+            user.Password = Function.MD532(user.Password);
+
+            return SUCCESS(UserService.InsertUser(user));
+        }
+
+        /// <summary>
+        /// 修改用户
+        /// </summary>
+        /// <param name="parm"></param>
+        /// <returns></returns>
+        [HttpPut("edit")]
+        [Log(Title = "用户管理", BusinessType = BusinessType.UPDATE)]
+        [ActionPermissionFilter(Permission = "system:user:edit")]
+        public IActionResult UpdateUser([FromBody] SysUserDto parm)
+        {
+            var user = parm.Adapt<SysUser>().ToUpdate(HttpContext);
+            if (user == null || user.UserId <= 0) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+
+            int upResult = UserService.UpdateUser(user);
+
+            return ToResponse(upResult);
+        }
+
+        /// <summary>
+        /// 改变用户状态
+        /// </summary>
+        /// <param name="user"></param>
+        /// <returns></returns>
+        [HttpPut("changeStatus")]
+        [Log(Title = "修改用户状态", BusinessType = BusinessType.UPDATE)]
+        [ActionPermissionFilter(Permission = "system:user:update")]
+        public IActionResult ChangeStatus([FromBody] SysUser user)
+        {
+            if (user == null) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+
+            int result = UserService.ChangeUserStatus(user);
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 删除用户
+        /// </summary>
+        /// <param name="userid"></param>
+        /// <returns></returns>
+        [HttpDelete("{userId}")]
+        [Log(Title = "用户管理", BusinessType = BusinessType.DELETE)]
+        [ActionPermissionFilter(Permission = "system:user:remove")]
+        public IActionResult Remove(int userid = 0)
+        {
+            if (userid <= 0) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
+            if (userid == 1) return ToResponse(ResultCode.FAIL, "不能删除管理员账号");
+            int result = UserService.DeleteUser(userid);
+
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 重置密码
+        /// </summary>
+        /// <returns></returns>
+        [HttpPut("resetPwd")]
+        [Log(Title = "重置密码", BusinessType = BusinessType.UPDATE)]
+        [ActionPermissionFilter(Permission = "system:user:resetPwd")]
+        public IActionResult ResetPwd([FromBody] SysUserDto sysUser)
+        {
+            //密码md5
+            sysUser.Password = Function.MD532(sysUser.Password);
+
+            int result = UserService.ResetPwd(sysUser.UserId, sysUser.Password);
+            return ToResponse(result);
+        }
+
+        /// <summary>
+        /// 导入
+        /// </summary>
+        /// <param name="formFile">使用IFromFile必须使用name属性否则获取不到文件</param>
+        /// <returns></returns>
+        [HttpPost("importData")]
+        [Log(Title = "用户导入", BusinessType = BusinessType.IMPORT, IsSaveRequestData = false, IsSaveResponseData = true)]
+        [ActionPermissionFilter(Permission = "system:user:import")]
+        public IActionResult ImportData([FromForm(Name = "file")] IFormFile formFile)
+        {
+            List<SysUser> users = new();
+            using (var stream = formFile.OpenReadStream())
+            {
+                users = stream.Query<SysUser>(startCell: "A2").ToList();
+            }
+
+            return SUCCESS(UserService.ImportUsers(users));
+        }
+
+        /// <summary>
+        /// 用户导入模板下载
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("importTemplate")]
+        [Log(Title = "用户模板", BusinessType = BusinessType.EXPORT, IsSaveRequestData = true, IsSaveResponseData = false)]
+        [AllowAnonymous]
+        public IActionResult ImportTemplateExcel()
+        {
+            (string, string) result = DownloadImportTemplate("user");
+            return ExportExcel(result.Item2, result.Item1);
+        }
+
+        /// <summary>
+        /// 用户导出
+        /// </summary>
+        /// <param name="user"></param>
+        /// <returns></returns>
+        [HttpGet("export")]
+        [Log(Title = "用户导出", BusinessType = BusinessType.EXPORT)]
+        [ActionPermissionFilter(Permission = "system:user:export")]
+        public IActionResult UserExport([FromQuery] SysUserQueryDto user)
+        {
+            var list = UserService.SelectUserList(user, new PagerInfo(1, 10000));
+
+            var result = ExportExcelMini(list.Result, "user", "用户列表");
+            return ExportExcel(result.Item2, result.Item1);
+        }
+    }
+}

+ 91 - 0
Controllers/Base/SysUserRoleController.cs

@@ -0,0 +1,91 @@
+using Attribute;
+using Dto;
+using Enums;
+using Filters;
+using Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+using Middleware;
+using Services;
+
+namespace Controllers
+{
+    /// <summary>
+    /// 用户角色管理
+    /// </summary>
+    [Verify]
+    [Route("system/userRole")]
+    [ApiExplorerSettings(GroupName = "sys")]
+    public class SysUserRoleController : BaseController
+    {
+        private readonly ISysUserRoleService SysUserRoleService;
+        private readonly ISysUserService UserService;
+
+        public SysUserRoleController(
+            ISysUserRoleService sysUserRoleService,
+            ISysUserService userService)
+        {
+            SysUserRoleService = sysUserRoleService;
+            UserService = userService;
+        }
+
+        /// <summary>
+        /// 根据角色编号获取已分配的用户
+        /// </summary>
+        /// <param name="roleUserQueryDto"></param>
+        /// <returns></returns>
+        [HttpGet("list")]
+        [ActionPermissionFilter(Permission = "system:roleusers:list")]
+        public IActionResult GetList([FromQuery] RoleUserQueryDto roleUserQueryDto)
+        {
+            var list = SysUserRoleService.GetSysUsersByRoleId(roleUserQueryDto);
+
+            return SUCCESS(list, TIME_FORMAT_FULL);
+        }
+
+        /// <summary>
+        /// 添加角色用户
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("create")]
+        [ActionPermissionFilter(Permission = "system:roleusers:add")]
+        [Log(Title = "添加角色用户", BusinessType = BusinessType.INSERT)]
+        public IActionResult Create([FromBody] RoleUsersCreateDto roleUsersCreateDto)
+        {
+            var response = SysUserRoleService.InsertRoleUser(roleUsersCreateDto);
+
+            return SUCCESS(response);
+        }
+
+        /// <summary>
+        /// 删除角色用户
+        /// </summary>
+        /// <param name="roleUsersCreateDto"></param>
+        /// <returns></returns>
+        [HttpPost("delete")]
+        [ActionPermissionFilter(Permission = "system:roleusers:remove")]
+        [Log(Title = "删除角色用户", BusinessType = BusinessType.DELETE)]
+        public IActionResult Delete([FromBody] RoleUsersCreateDto roleUsersCreateDto)
+        {
+            return SUCCESS(SysUserRoleService.DeleteRoleUserByUserIds(roleUsersCreateDto.RoleId, roleUsersCreateDto.UserIds));
+        }
+
+        /// <summary>
+        /// 获取未分配用户角色
+        /// </summary>
+        /// <param name="roleUserQueryDto"></param>
+        /// <returns></returns>
+        [HttpGet("GetExcludeUsers")]
+        public IActionResult GetExcludeUsers([FromQuery] RoleUserQueryDto roleUserQueryDto)
+        {
+            if (roleUserQueryDto.RoleId <= 0)
+            {
+                throw new CustomException(ResultCode.PARAM_ERROR, "roleId不能为空");
+            }
+
+            // 获取未添加用户
+            var list = SysUserRoleService.GetExcludedSysUsersByRoleId(roleUserQueryDto);
+
+            return SUCCESS(list, TIME_FORMAT_FULL);
+        }
+    }
+}

+ 84 - 0
Extensions/AppServiceExtensions.cs

@@ -0,0 +1,84 @@
+using Attribute;
+using Base;
+using Services;
+using System.Reflection;
+
+namespace Infrastructure
+{
+    /// <summary>
+    /// App服务注册
+    /// </summary>
+    public static class AppServiceExtensions
+    {
+        /// <summary>
+        /// 注册引用程序域中所有有AppService标记的类的服务
+        /// </summary>
+        /// <param name="services"></param>
+        public static void AddAppService(this IServiceCollection services)
+        {
+            // var cls = AppSettings.Get<string[]>("InjectClass");
+            // if (cls == null || cls.Length <= 0)
+            // {
+            //     throw new Exception("请更新appsettings类");
+            // }
+            // foreach (var item in cls)
+            // {
+            //     Register(services, item);
+            // }
+            // services.AddTransient<ISysOperLogService, SysOperLogService>();
+            services.AddTransient<ISysPermissionService, SysPermissionService>();
+            services.AddTransient<ISysDeptService, SysDeptService>();
+            services.AddTransient<ISysLoginService, SysLoginService>();
+            services.AddTransient<ISysMenuService, SysMenuService>();
+            services.AddTransient<ISysRoleMenuService, SysRoleMenuService>();
+            services.AddTransient<ISysPostService, SysPostService>();
+            services.AddTransient<ISysRoleService, SysRoleService>();
+            services.AddTransient<ISysUserPostService, SysUserPostService>();
+            services.AddTransient<ISysUserRoleService, SysUserRoleService>();
+            services.AddTransient<ISysRoleDeptService, SysRoleDeptService>();
+            services.AddTransient<ISysUserService, SysUserService>();
+            services.AddTransient<ISysOauthClientDetailsService, SysOauthClientDetailsService>();
+        }
+
+        private static void Register(IServiceCollection services, string item)
+        {
+            Assembly assembly = Assembly.Load(item);
+            foreach (var type in assembly.GetTypes())
+            {
+                var serviceAttribute = type.GetCustomAttribute<AppServiceAttribute>();
+
+                if (serviceAttribute != null)
+                {
+                    var serviceType = serviceAttribute.ServiceType;
+                    //情况1 适用于依赖抽象编程,注意这里只获取第一个
+                    if (serviceType == null && serviceAttribute.InterfaceServiceType)
+                    {
+                        serviceType = type.GetInterfaces().FirstOrDefault();
+                    }
+                    //情况2 不常见特殊情况下才会指定ServiceType,写起来麻烦
+                    if (serviceType == null)
+                    {
+                        serviceType = type;
+                    }
+
+                    switch (serviceAttribute.ServiceLifetime)
+                    {
+                        case LifeTime.Singleton:
+                            services.AddSingleton(serviceType, type);
+                            break;
+                        case LifeTime.Scoped:
+                            services.AddScoped(serviceType, type);
+                            break;
+                        case LifeTime.Transient:
+                            services.AddTransient(serviceType, type);
+                            break;
+                        default:
+                            services.AddTransient(serviceType, type);
+                            break;
+                    }
+                    //System.Console.WriteLine($"注册:{serviceType}");
+                }
+            }
+        }
+    }
+}

+ 42 - 0
Extensions/EntityExtension.cs

@@ -0,0 +1,42 @@
+using System.Reflection;
+using Extensions;
+
+namespace Infrastructure
+{
+    public static class EntityExtension
+    {
+        public static TSource ToCreate<TSource>(this TSource source, HttpContext? context = null)
+        {
+            var types = source?.GetType();
+            if (types == null || context == null) return source;
+            BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
+
+            // types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null);
+            // types.GetProperty("AddTime", flag)?.SetValue(source, DateTime.Now, null);
+            // types.GetProperty("CreateBy", flag)?.SetValue(source, context.GetName(), null);
+            // types.GetProperty("Create_by", flag)?.SetValue(source, context.GetName(), null);
+            // types.GetProperty("UserId", flag)?.SetValue(source, context.GetUId(), null);
+            // types.GetProperty("DeptId", flag)?.SetValue(source, context.GetDeptId(), null);
+            types.GetProperty("CreateDate", flag)?.SetValue(source, DateTime.Now, null);
+            types.GetProperty("UpdateDate", flag)?.SetValue(source, DateTime.Now, null);
+
+            return source;
+        }
+
+        public static TSource ToUpdate<TSource>(this TSource source, HttpContext? context = null)
+        {
+            var types = source?.GetType();
+            if (types == null || context == null) return source;
+            BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
+
+            // types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null);
+            // types.GetProperty("Update_time", flag)?.SetValue(source, DateTime.Now, null);
+            // types.GetProperty("UpdateBy", flag)?.SetValue(source, context.GetName(), null);
+            // types.GetProperty("Update_by", flag)?.SetValue(source, context.GetName(), null);
+            types.GetProperty("UpdateDate", flag)?.SetValue(source, DateTime.Now, null);
+
+            return source;
+        }
+
+    }
+}

+ 446 - 0
Extensions/Extension.Convert.cs

@@ -0,0 +1,446 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Extensions
+{
+    public static partial class Extensions
+    {
+        #region 转换为long
+        /// <summary>
+        /// 将object转换为long,若转换失败,则返回0。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static long ParseToLong(this object obj)
+        {
+            try
+            {
+                return long.Parse(obj.ToString());
+            }
+            catch
+            {
+                return 0L;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为long,若转换失败,则返回指定值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="defaultValue"></param>
+        /// <returns></returns>
+        public static long ParseToLong(this string str, long defaultValue)
+        {
+            try
+            {
+                return long.Parse(str);
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+        #endregion
+
+        #region 转换为int
+        /// <summary>
+        /// 将object转换为int,若转换失败,则返回0。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static int ParseToInt(this object str)
+        {
+            try
+            {
+                return Convert.ToInt32(str);
+            }
+            catch
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为int,若转换失败,则返回指定值。不抛出异常。 
+        /// null返回默认值
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="defaultValue"></param>
+        /// <returns></returns>
+        public static int ParseToInt(this object str, int defaultValue)
+        {
+            if (str == null)
+            {
+                return defaultValue;
+            }
+            try
+            {
+                return Convert.ToInt32(str);
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+        #endregion
+
+        #region 转换为short
+        /// <summary>
+        /// 将object转换为short,若转换失败,则返回0。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static short ParseToShort(this object obj)
+        {
+            try
+            {
+                return short.Parse(obj.ToString());
+            }
+            catch
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为short,若转换失败,则返回指定值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static short ParseToShort(this object str, short defaultValue)
+        {
+            try
+            {
+                return short.Parse(str.ToString());
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+        #endregion
+
+        #region 转换为demical
+        /// <summary>
+        /// 将object转换为demical,若转换失败,则返回指定值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static decimal ParseToDecimal(this object str, decimal defaultValue)
+        {
+            try
+            {
+                return decimal.Parse(str.ToString());
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为demical,若转换失败,则返回0。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static decimal ParseToDecimal(this object str)
+        {
+            try
+            {
+                return decimal.Parse(str.ToString());
+            }
+            catch
+            {
+                return 0;
+            }
+        }
+        #endregion
+
+        #region 转化为bool
+        /// <summary>
+        /// 将object转换为bool,若转换失败,则返回false。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static bool ParseToBool(this object str)
+        {
+            try
+            {
+                return bool.Parse(str.ToString());
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为bool,若转换失败,则返回指定值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static bool ParseToBool(this object str, bool result)
+        {
+            try
+            {
+                return bool.Parse(str.ToString());
+            }
+            catch
+            {
+                return result;
+            }
+        }
+        #endregion
+
+        #region 转换为float
+        /// <summary>
+        /// 将object转换为float,若转换失败,则返回0。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static float ParseToFloat(this object str)
+        {
+            try
+            {
+                return float.Parse(str.ToString());
+            }
+            catch
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为float,若转换失败,则返回指定值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static float ParseToFloat(this object str, float result)
+        {
+            try
+            {
+                return float.Parse(str.ToString());
+            }
+            catch
+            {
+                return result;
+            }
+        }
+        #endregion
+
+        #region 转换为Guid
+        /// <summary>
+        /// 将string转换为Guid,若转换失败,则返回Guid.Empty。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static Guid ParseToGuid(this string str)
+        {
+            try
+            {
+                return new Guid(str);
+            }
+            catch
+            {
+                return Guid.Empty;
+            }
+        }
+        #endregion
+
+        #region 转换为DateTime
+        /// <summary>
+        /// 将string转换为DateTime,若转换失败,则返回日期最小值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static DateTime ParseToDateTime(this string str)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(str))
+                {
+                    return DateTime.MinValue;
+                }
+                if (str.Contains("-") || str.Contains("/"))
+                {
+                    return DateTime.Parse(str);
+                }
+                else
+                {
+                    int length = str.Length;
+                    switch (length)
+                    {
+                        case 4:
+                            return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
+                        case 6:
+                            return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
+                        case 8:
+                            return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
+                        case 10:
+                            return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
+                        case 12:
+                            return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
+                        case 14:
+                            return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+                        default:
+                            return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+                    }
+                }
+            }
+            catch
+            {
+                return DateTime.MinValue;
+            }
+        }
+
+        /// <summary>
+        /// 将string转换为DateTime,若转换失败,则返回默认值。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="defaultValue"></param>
+        /// <returns></returns>
+        public static DateTime ParseToDateTime(this string str, DateTime? defaultValue)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(str))
+                {
+                    return defaultValue.GetValueOrDefault();
+                }
+                if (str.Contains("-") || str.Contains("/"))
+                {
+                    return DateTime.Parse(str);
+                }
+                else
+                {
+                    int length = str.Length;
+                    switch (length)
+                    {
+                        case 4:
+                            return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
+                        case 6:
+                            return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
+                        case 8:
+                            return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
+                        case 10:
+                            return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
+                        case 12:
+                            return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
+                        case 14:
+                            return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+                        default:
+                            return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+                    }
+                }
+            }
+            catch
+            {
+                return defaultValue.GetValueOrDefault();
+            }
+        }
+        #endregion
+
+        #region 转换为string
+        /// <summary>
+        /// 将object转换为string,若转换失败,则返回""。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static string ParseToString(this object obj)
+        {
+            try
+            {
+                if (obj == null)
+                {
+                    return string.Empty;
+                }
+                else
+                {
+                    return obj.ToString();
+                }
+            }
+            catch
+            {
+                return string.Empty;
+            }
+        }
+        public static string ParseToStrings<T>(this object obj)
+        {
+            try
+            {
+                var list = obj as IEnumerable<T>;
+                if (list != null)
+                {
+                    return string.Join(",", list);
+                }
+                else
+                {
+                    return obj.ToString();
+                }
+            }
+            catch
+            {
+                return string.Empty;
+            }
+
+        }
+        #endregion
+
+        #region 转换为double
+        /// <summary>
+        /// 将object转换为double,若转换失败,则返回0。不抛出异常。  
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public static double ParseToDouble(this object obj)
+        {
+            try
+            {
+                return double.Parse(obj.ToString());
+            }
+            catch
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// 将object转换为double,若转换失败,则返回指定值。不抛出异常。  
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="defaultValue"></param>
+        /// <returns></returns>
+        public static double ParseToDouble(this object str, double defaultValue)
+        {
+            try
+            {
+                return double.Parse(str.ToString());
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+        #endregion
+
+        #region 强制转换类型
+        /// <summary>
+        /// 强制转换类型
+        /// </summary>
+        /// <typeparam name="TResult"></typeparam>
+        /// <param name="source"></param>
+        /// <returns></returns>
+        public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
+        {
+            foreach (object item in source)
+            {
+                yield return (TResult)Convert.ChangeType(item, typeof(TResult));
+            }
+        }
+        #endregion
+    }
+}

+ 86 - 0
Extensions/Extension.Enum.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Reflection;
+
+namespace Extensions
+{
+    public static partial class Extensions
+    {
+        #region 枚举成员转成dictionary类型
+        /// <summary>
+        /// 转成dictionary类型
+        /// </summary>
+        /// <param name="enumType"></param>
+        /// <returns></returns>
+        public static Dictionary<int, string> EnumToDictionary(this Type enumType)
+        {
+            Dictionary<int, string> dictionary = new Dictionary<int, string>();
+            Type typeDescription = typeof(DescriptionAttribute);
+            FieldInfo[] fields = enumType.GetFields();
+            int sValue = 0;
+            string sText = string.Empty;
+            foreach (FieldInfo field in fields)
+            {
+                if (field.FieldType.IsEnum)
+                {
+                    sValue = ((int)enumType.InvokeMember(field.Name, BindingFlags.GetField, null, null, null));
+                    object[] arr = field.GetCustomAttributes(typeDescription, true);
+                    if (arr.Length > 0)
+                    {
+                        DescriptionAttribute da = (DescriptionAttribute)arr[0];
+                        sText = da.Description;
+                    }
+                    else
+                    {
+                        sText = field.Name;
+                    }
+                    dictionary.Add(sValue, sText);
+                }
+            }
+            return dictionary;
+        }
+        /// <summary>
+        /// 枚举成员转成键值对Json字符串
+        /// </summary>
+        /// <param name="enumType"></param>
+        /// <returns></returns>
+        //public static string EnumToDictionaryString(this Type enumType)
+        //{
+        //    List<KeyValuePair<int, string>> dictionaryList = EnumToDictionary(enumType).ToList();
+        //    var sJson = JsonConvert.SerializeObject(dictionaryList);
+        //    return sJson;
+        //}
+        #endregion
+
+        #region 获取枚举的描述
+        /// <summary>
+        /// 获取枚举值对应的描述
+        /// </summary>
+        /// <param name="enumType"></param>
+        /// <returns></returns>
+        public static string GetDescription(this System.Enum enumType)
+        {
+            FieldInfo EnumInfo = enumType.GetType().GetField(enumType.ToString());
+            if (EnumInfo != null)
+            {
+                DescriptionAttribute[] EnumAttributes = (DescriptionAttribute[])EnumInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
+                if (EnumAttributes.Length > 0)
+                {
+                    return EnumAttributes[0].Description;
+                }
+            }
+            return enumType.ToString();
+        }
+        #endregion
+
+        #region 根据值获取枚举的描述
+        public static string GetDescriptionByEnum<T>(this object obj)
+        {
+            var tEnum = System.Enum.Parse(typeof(T), obj.ParseToString()) as System.Enum;
+            var description = tEnum.GetDescription();
+            return description;
+        }
+        #endregion
+    }
+}

+ 18 - 0
Extensions/Extension.Exception.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Extensions
+{
+    public static partial class Extensions
+    {
+        public static Exception GetOriginalException(this Exception ex)
+        {
+            if (ex.InnerException == null) return ex;
+
+            return ex.InnerException.GetOriginalException();
+        }
+    }
+}

+ 110 - 0
Extensions/Extension.Linq.cs

@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Extensions
+{
+    public static class LinqExtensions
+    {
+        public static Expression Property(this Expression expression, string propertyName)
+        {
+            return Expression.Property(expression, propertyName);
+        }
+        public static Expression AndAlso(this Expression left, Expression right)
+        {
+            return Expression.AndAlso(left, right);
+        }
+        public static Expression Call(this Expression instance, string methodName, params Expression[] arguments)
+        {
+            return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments);
+        }
+        public static Expression GreaterThan(this Expression left, Expression right)
+        {
+            return Expression.GreaterThan(left, right);
+        }
+        public static Expression<T> ToLambda<T>(this Expression body, params ParameterExpression[] parameters)
+        {
+            return Expression.Lambda<T>(body, parameters);
+        }
+
+        public static Expression<Func<T, bool>> True<T>() { return param => true; }
+
+        public static Expression<Func<T, bool>> False<T>() { return param => false; }
+
+        /// <summary>
+        /// 组合And
+        /// </summary>
+        /// <returns></returns>
+        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
+        {
+            return first.Compose(second, Expression.AndAlso);
+        }
+        /// <summary>
+        /// 组合Or
+        /// </summary>
+        /// <returns></returns>
+        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
+        {
+            return first.Compose(second, Expression.OrElse);
+        }
+
+        /// <summary>
+        /// Combines the first expression with the second using the specified merge function.
+        /// </summary>
+        static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
+        {
+            var map = first.Parameters
+                .Select((f, i) => new { f, s = second.Parameters[i] })
+                .ToDictionary(p => p.s, p => p.f);
+            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
+            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
+        }
+
+        /// <summary>
+        /// ParameterRebinder
+        /// </summary>
+        private class ParameterRebinder : ExpressionVisitor
+        {
+            /// <summary>
+            /// The ParameterExpression map
+            /// </summary>
+            readonly Dictionary<ParameterExpression, ParameterExpression> map;
+            /// <summary>
+            /// Initializes a new instance of the <see cref="ParameterRebinder"/> class.
+            /// </summary>
+            /// <param name="map">The map.</param>
+            ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
+            {
+                this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
+            }
+            /// <summary>
+            /// Replaces the parameters.
+            /// </summary>
+            /// <param name="map">The map.</param>
+            /// <param name="exp">The exp.</param>
+            /// <returns>Expression</returns>
+            public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
+            {
+                return new ParameterRebinder(map).Visit(exp);
+            }
+            /// <summary>
+            /// Visits the parameter.
+            /// </summary>
+            /// <param name="p">The p.</param>
+            /// <returns>Expression</returns>
+            protected override Expression VisitParameter(ParameterExpression p)
+            {
+                ParameterExpression replacement;
+
+                if (map.TryGetValue(p, out replacement))
+                {
+                    p = replacement;
+                }
+                return base.VisitParameter(p);
+            }
+        }
+    }
+}

+ 44 - 0
Extensions/Extension.Validate.cs

@@ -0,0 +1,44 @@
+//using Microsoft.AspNetCore.Http;
+
+namespace Extensions
+{
+    public static partial class Extensions
+    {
+        public static bool IsEmpty(this object value)
+        {
+            if (value != null && !string.IsNullOrEmpty(value.ParseToString()))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+        }
+        public static bool IsNotEmpty(this object value)
+        {
+            return !IsEmpty(value);
+        }
+        public static bool IsNullOrZero(this object value)
+        {
+            if (value == null || value.ParseToString().Trim() == "0")
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        //public static bool IsAjaxRequest(this HttpRequest request)
+        //{
+        //    if (request == null)
+        //        throw new ArgumentNullException("request");
+
+        //    if (request.Headers != null)
+        //        return request.Headers["X-Requested-With"] == "XMLHttpRequest";
+        //    return false;
+        //}
+    }
+}

+ 241 - 0
Extensions/HttpContextExtension.cs

@@ -0,0 +1,241 @@
+using System.Security.Claims;
+using System.Text;
+using System.Text.RegularExpressions;
+using Base;
+using Extensions;
+using IPTools.Core;
+using UAParser;
+
+namespace Extensions
+{
+    /// <summary>
+    /// HttpContext扩展类
+    /// </summary>
+    public static partial class HttpContextExtension
+    {
+        /// <summary>
+        /// 是否是ajax请求
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        public static bool IsAjaxRequest(this HttpRequest request)
+        {
+            if (request == null)
+            {
+                throw new ArgumentNullException(nameof(request));
+            }
+
+            //return request.Headers.ContainsKey("X-Requested-With") &&
+            //       request.Headers["X-Requested-With"].Equals("XMLHttpRequest");
+
+            return request.Headers["X-Requested-With"] == "XMLHttpRequest" || request.Headers != null && request.Headers["X-Requested-With"] == "XMLHttpRequest";
+        }
+
+        /// <summary>
+        /// 获取客户端IP
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static string GetClientUserIp(this HttpContext context)
+        {
+            if (context == null) return "";
+            var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
+            if (string.IsNullOrEmpty(result))
+            {
+                result = context.Connection.RemoteIpAddress?.ToString();
+            }
+            if (string.IsNullOrEmpty(result))
+                throw new Exception("获取IP失败");
+
+            if (result.Contains("::1"))
+                result = "127.0.0.1";
+
+            result = result.Replace("::ffff:", "");
+            result = result.Split(':')?.FirstOrDefault() ?? "127.0.0.1";
+            result = IsIP(result) ? result : "127.0.0.1";
+            return result;
+        }
+
+        /// <summary>
+        /// 判断是否IP
+        /// </summary>
+        /// <param name="ip"></param>
+        /// <returns></returns>
+        public static bool IsIP(string ip)
+        {
+            return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
+        }
+
+        /// <summary>
+        /// 获取登录用户id
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static long GetUId(this HttpContext context)
+        {
+            var uid = context.User.FindFirstValue(ClaimTypes.PrimarySid);
+            return !string.IsNullOrEmpty(uid) ? long.Parse(uid) : 0;
+        }
+
+        /// <summary>
+        /// 获取部门id
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static long GetDeptId(this HttpContext context)
+        {
+            var deptId = context.User.FindFirstValue(ClaimTypes.GroupSid);
+            return !string.IsNullOrEmpty(deptId) ? long.Parse(deptId) : 0;
+        }
+
+        /// <summary>
+        /// 获取登录用户名
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static string GetName(this HttpContext context)
+        {
+            var uid = context.User?.Identity?.Name;
+
+            return uid;
+        }
+
+        /// <summary>
+        /// 判断是否是管理员
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static bool IsAdmin(this HttpContext context)
+        {
+            var userName = context.GetName();
+            return userName == GlobalConstant.AdminRole;
+        }
+
+        /// <summary>
+        /// ClaimsIdentity
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static IEnumerable<ClaimsIdentity> GetClaims(this HttpContext context)
+        {
+            return context.User?.Identities;
+        }
+        //public static int GetRole(this HttpContext context)
+        //{
+        //    var roleid = context.User.FindFirstValue(ClaimTypes.Role) ?? "0";
+
+        //    return int.Parse(roleid);
+        //}
+
+        public static string GetUserAgent(this HttpContext context)
+        {
+            return context.Request.Headers["User-Agent"];
+        }
+
+        /// <summary>
+        /// 获取请求令牌
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static string GetToken(this HttpContext context)
+        {
+            return context.Request.Headers["Authorization"];
+        }
+
+        /// <summary>
+        /// 获取请求Url
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static string GetRequestUrl(this HttpContext context)
+        {
+            return context != null ? context.Request.Path.Value : "";
+        }
+
+        /// <summary>
+        /// 获取请求参数
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static string GetQueryString(this HttpContext context)
+        {
+            return context != null ? context.Request.QueryString.Value : "";
+        }
+
+        /// <summary>
+        /// 获取body请求参数
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static string GetBody(this HttpContext context)
+        {
+            context.Request.EnableBuffering();
+            //context.Request.Body.Seek(0, SeekOrigin.Begin);
+            //using var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
+            ////需要使用异步方式才能获取
+            //return reader.ReadToEndAsync().Result;
+            string body = string.Empty;
+            var buffer = new MemoryStream();
+            context.Request.Body.Seek(0, SeekOrigin.Begin);
+            context.Request.Body.CopyToAsync(buffer);
+            buffer.Position = 0;
+            try
+            {
+                using StreamReader streamReader = new(buffer, Encoding.UTF8);
+                body = streamReader.ReadToEndAsync().Result;
+            }
+            finally
+            {
+                buffer?.Dispose();
+            }
+            return body;
+        }
+
+        /// <summary>
+        /// 获取浏览器信息
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public static ClientInfo GetClientInfo(this HttpContext context)
+        {
+            var str = context.GetUserAgent();
+            var uaParser = Parser.GetDefault();
+            ClientInfo c = uaParser.Parse(str);
+
+            return c;
+        }
+
+        /// <summary>
+        /// 根据IP获取地理位置
+        /// </summary>
+        /// <returns></returns>
+        public static string GetIpInfo(string IP)
+        {
+            var ipInfo = IpTool.Search(IP);
+            return ipInfo?.Province + "-" + ipInfo?.City + "-" + ipInfo?.NetworkOperator;
+        }
+
+        /// <summary>
+        /// 设置请求参数
+        /// </summary>
+        /// <param name="reqMethod"></param>
+        /// <param name="context"></param>
+        public static string GetRequestValue(this HttpContext context, string reqMethod)
+        {
+            string param = string.Empty;
+
+            if (HttpMethods.IsPost(reqMethod) || HttpMethods.IsPut(reqMethod) || HttpMethods.IsDelete(reqMethod))
+            {
+                param = context.GetBody();
+                string regex = "(?<=\"password\":\")[^\",]*";
+                param = Regex.Replace(param, regex, "***");
+            }
+            if (param.IsEmpty())
+            {
+                param = context.GetQueryString();
+            }
+            return param;
+        }
+    }
+
+}

+ 24 - 0
Extensions/IPRateExtension.cs

@@ -0,0 +1,24 @@
+using AspNetCoreRateLimit;
+
+namespace Extensions
+{
+    public static class IPRateExtension
+    {
+        public static void AddIPRate(this IServiceCollection services, IConfiguration configuration)
+        {
+            if (services == null) throw new ArgumentNullException(nameof(services));
+
+            //从appsettings.json中加载常规配置,IpRateLimiting与配置文件中节点对应
+            services.Configure<IpRateLimitOptions>(configuration.GetSection("IpRateLimiting"));
+
+            //从appsettings.json中加载Ip规则
+            services.Configure<IpRateLimitPolicies>(configuration.GetSection("IpRateLimitPolicies"));
+            //注入计数器和规则存储
+            services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
+            services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
+            //配置(解析器、计数器密钥生成器)
+            services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
+            services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
+        }
+    }
+}

+ 35 - 0
Extensions/RequestLimitExtension.cs

@@ -0,0 +1,35 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Extensions
+{
+    public static class RequestLimitExtension
+    {
+        /// <summary>
+        /// 请求body大小设置
+        /// </summary>
+        /// <param name="services"></param>
+        /// <param name="configuration"></param>
+        public static void AddRequestLimit(this IServiceCollection services, IConfiguration configuration)
+        {
+            var sizeM = configuration.GetSection("upload:requestLimitSize").Get<int>();
+            services.Configure<FormOptions>(x =>
+            {
+                x.MultipartBodyLengthLimit = sizeM * 1024 * 1024;
+                x.MemoryBufferThreshold = sizeM * 1024 * 1024;
+                x.ValueLengthLimit = int.MaxValue;
+            });
+            services.Configure<KestrelServerOptions>(options =>
+            {
+                options.Limits.MaxRequestBodySize = sizeM * 1024 * 1024;
+            });
+            services.Configure<IISServerOptions>(options =>
+            {
+                options.MaxRequestBodySize = sizeM * 1024 * 1024; // 设置最大请求体大小为500MB
+            });
+        }
+    }
+}

+ 232 - 0
Extensions/StringExtension.cs

@@ -0,0 +1,232 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Extensions
+{
+    public static class StringExtension
+    {
+
+        /// <summary>
+        /// SQL条件拼接
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="condition"></param>
+        /// <returns></returns>
+        public static string If(this string str, bool condition)
+        {
+            return condition ? str : string.Empty;
+        }
+        /// <summary>
+        /// 判断是否为空
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static bool IfNotEmpty(this string str)
+        {
+            return !string.IsNullOrEmpty(str);
+        }
+
+        /// <summary>
+        /// 注意:如果替换的旧值中有特殊符号,替换将会失败,解决办法 例如特殊符号是“(”:  要在调用本方法前加oldValue=oldValue.Replace("(","//(");
+        /// </summary>
+        /// <param name="input"></param>
+        /// <param name="oldValue"></param>
+        /// <param name="newValue"></param>
+        /// <returns></returns>
+        public static string ReplaceFirst(this string input, string oldValue, string newValue)
+        {
+            Regex regEx = new Regex(oldValue, RegexOptions.Multiline);
+            return regEx.Replace(input, newValue == null ? "" : newValue, 1);
+        }
+
+        /// <summary>
+        /// 骆驼峰转下划线
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        public static string ToSmallCamelCase(string name)
+        {
+            var stringBuilder = new StringBuilder();
+            stringBuilder.Append(name.Substring(0, 1).ToLower());
+
+            for (var i = 0; i < name.Length; i++)
+            {
+                if (i == 0)
+                {
+                    stringBuilder.Append(name.Substring(0, 1).ToLower());
+                }
+                else
+                {
+                    if (name[i] >= 'A' && name[i] <= 'Z')
+                    {
+                        stringBuilder.Append($"_{name.Substring(i, 1).ToLower()}");
+                    }
+                    else
+                    {
+                        stringBuilder.Append(name[i]);
+                    }
+                }
+            }
+
+            return stringBuilder.ToString();
+        }
+
+        /// <summary>
+        /// 下划线命名转驼峰命名
+        /// </summary>
+        /// <param name="underscore"></param>
+        /// <returns></returns>
+        public static string UnderScoreToCamelCase(this string underscore)
+        {
+            string[] ss = underscore.Split("_");
+            if (ss.Length == 1)
+            {
+                return underscore;
+            }
+
+            StringBuilder sb = new();
+            sb.Append(ss[0]);
+            for (int i = 1; i < ss.Length; i++)
+            {
+                sb.Append(ss[i].FirstUpperCase());
+            }
+
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// 首字母转大写
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static string FirstUpperCase(this string str)
+        {
+            return string.IsNullOrEmpty(str) ? str : str[..1].ToUpper() + str[1..];
+        }
+
+        /// <summary>
+        /// 首字母转小写
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static string FirstLowerCase(this string str)
+        {
+            return string.IsNullOrEmpty(str) ? str : str.Substring(0, 1).ToLower() + str[1..];
+        }
+
+        /// <summary>
+        /// 截取指定字符串中间内容
+        /// </summary>
+        /// <param name="sourse"></param>
+        /// <param name="startstr"></param>
+        /// <param name="endstr"></param>
+        /// <returns></returns>
+        public static string SubStringBetween(this string sourse, string startstr, string endstr)
+        {
+            string result = string.Empty;
+            int startindex, endindex;
+            try
+            {
+                startindex = sourse.IndexOf(startstr);
+                if (startindex == -1)
+                    return result;
+                string tmpstr = sourse[(startindex + startstr.Length)..];
+                endindex = tmpstr.IndexOf(endstr);
+                if (endindex == -1)
+                    return result;
+                result = tmpstr.Remove(endindex);
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine("MidStrEx Err:" + ex.Message);
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 转换为Pascal风格-每一个单词的首字母大写
+        /// </summary>
+        /// <param name="fieldName">字段名</param>
+        /// <param name="fieldDelimiter">分隔符</param>
+        /// <returns></returns>
+        public static string ConvertToPascal(this string fieldName, string fieldDelimiter)
+        {
+            string result = string.Empty;
+            if (fieldName.Contains(fieldDelimiter))
+            {
+                //全部小写
+                string[] array = fieldName.ToLower().Split(fieldDelimiter.ToCharArray());
+                foreach (var t in array)
+                {
+                    //首字母大写
+                    result += t.Substring(0, 1).ToUpper() + t[1..];
+                }
+            }
+            else if (string.IsNullOrWhiteSpace(fieldName))
+            {
+                result = fieldName;
+            }
+            else if (fieldName.Length == 1)
+            {
+                result = fieldName.ToUpper();
+            }
+            else if (fieldName.Length == CountUpper(fieldName))
+            {
+                result = fieldName[..1].ToUpper() + fieldName[1..].ToLower();
+            }
+            else
+            {
+                result = fieldName[..1].ToUpper() + fieldName[1..];
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 大写字母个数
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static int CountUpper(this string str)
+        {
+            int count1 = 0;
+            char[] chars = str.ToCharArray();
+            foreach (char num in chars)
+            {
+                if (num >= 'A' && num <= 'Z')
+                {
+                    count1++;
+                }
+                //else if (num >= 'a' && num <= 'z')
+                //{
+                //    count2++;
+                //}
+            }
+            return count1;
+        }
+
+        /// <summary>
+        /// 转换为Camel风格-第一个单词小写,其后每个单词首字母大写
+        /// </summary>
+        /// <param name="fieldName">字段名</param>
+        /// <param name="fieldDelimiter">分隔符</param>
+        /// <returns></returns>
+        public static string ConvertToCamel(this string fieldName, string fieldDelimiter)
+        {
+            //先Pascal
+            string result = ConvertToPascal(fieldName, fieldDelimiter);
+            //然后首字母小写
+            if (result.Length == 1)
+            {
+                result = result.ToLower();
+            }
+            else
+            {
+                result = result[..1].ToLower() + result[1..];
+            }
+
+            return result;
+        }
+    }
+}

+ 92 - 0
Filters/ActionPermissionFilter.cs

@@ -0,0 +1,92 @@
+using Base;
+using Infrastructure;
+using Infrastructure.Model;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Model;
+using Model.Base;
+using Services;
+using Util;
+
+namespace Middleware
+{
+    /// <summary>
+    /// API授权判断
+    /// </summary>
+    public class ActionPermissionFilter : ActionFilterAttribute//, IAsyncActionFilter
+    {
+        private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+        /// <summary>
+        /// 权限字符串,例如 system:user:view
+        /// </summary>
+        public string Permission { get; set; } = string.Empty;
+        /// <summary>
+        /// 角色字符串,例如 common,admin
+        /// </summary>
+        public string RolePermi { get; set; } = string.Empty;
+        private bool HasPermi { get; set; }
+        public ActionPermissionFilter() { }
+        public ActionPermissionFilter(string permission)
+        {
+            Permission = permission;
+            HasPermi = !string.IsNullOrEmpty(Permission);
+        }
+
+        /// <summary>
+        /// 执行Action前校验是否有权限访问
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="next"></param>
+        /// <returns></returns>
+        public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+        {
+            TokenModel info = JwtUtil.GetLoginUser(context.HttpContext);
+
+            if (info != null && info?.UserId > 0)
+            {
+                long userId = info.UserId;
+                List<string> perms = CacheService.GetUserPerms(GlobalConstant.UserPermKEY + userId);
+                List<string> rolePerms = info.RoleIds;
+
+                if (perms == null)
+                {
+                    var sysPermissionService = App.GetService<ISysPermissionService>();
+                    perms = sysPermissionService.List();
+
+                    CacheService.SetUserPerms(GlobalConstant.UserPermKEY + userId, perms);
+                }
+
+                if (perms.Exists(f => f.Equals(GlobalConstant.AdminPerm)))
+                {
+                    HasPermi = true;
+                }
+                else if (rolePerms.Exists(f => f.Equals(GlobalConstant.AdminRole)))
+                {
+                    HasPermi = true;
+                }
+                else if (!string.IsNullOrEmpty(Permission))
+                {
+                    HasPermi = perms.Exists(f => f.ToLower() == Permission.ToLower());
+                }
+                if (!HasPermi && !string.IsNullOrEmpty(RolePermi))
+                {
+                    HasPermi = info.RoleIds.Contains(RolePermi);
+                }
+                var url = context.HttpContext.Request.Path;
+                if (!HasPermi && !Permission.Equals("common"))
+                {
+                    logger.Info($"用户{info.Username}没有权限访问{url},当前权限[{Permission}]");
+                    JsonResult result = new(new ApiResult((int)ResultCode.FORBIDDEN, $"你当前没有权限访问,请联系管理员", url))
+                    {
+                        ContentType = "application/json",
+                    };
+                    context.HttpContext.Response.StatusCode = 403;
+                    context.Result = result;
+                }
+            }
+
+            return base.OnActionExecutionAsync(context, next);
+        }
+    }
+}

+ 72 - 0
Filters/AuthorizationFilter.cs

@@ -0,0 +1,72 @@
+using System.Text;
+using Common;
+using Extensions;
+using Infrastructure;
+using Infrastructure.Model;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Services;
+
+//本命名空间暂时先不改,改动比较大2023年9月2日
+namespace Filters
+{
+    /// <summary>
+    /// </summary>
+    public class AuthorizationFilter : IAuthorizationFilter
+    {
+        
+        private readonly ISysLoginService SysLoginService;
+
+        public AuthorizationFilter(ISysLoginService sysLoginService)
+        {
+            this.SysLoginService = sysLoginService;
+        }
+
+        /// <summary>
+        /// </summary>
+        /// <param name="context"></param>
+        public void OnAuthorization(AuthorizationFilterContext context)
+        {
+            var request = context.HttpContext.Request;
+            string content = "";
+            if(context.HttpContext.Request.Method.ToLower() == "get")
+            {
+                content = context.HttpContext.GetQueryString();
+                content = content.Substring(content.IndexOf("=") + 1);
+                content = Decrypt(content);
+                request.QueryString = new QueryString("value=" + content);
+            }
+            else if(context.HttpContext.Request.Method.ToLower() == "delete")
+            {
+                string path = request.Path.Value;
+                path = path.Substring(0, path.LastIndexOf("/") + 1) + Decrypt(path.Substring(path.LastIndexOf("/") + 1));
+            }
+            else
+            {
+                content = context.HttpContext.GetBody();
+                content = Decrypt(content);
+                //{"username":"admin","password":"000000"}
+                request.Body = new MemoryStream(Encoding.UTF8.GetBytes(content));
+
+                //验证登录接口
+                if(request.Path.Value.EndsWith("/oauth2/token"))
+                {
+                    var scope = request.Query["scope"].ToString();
+                    var grantType = request.Query["grant_type"].ToString();
+                    bool checkLogin = SysLoginService.CheckLogin(scope, grantType, context.HttpContext.GetToken().Replace("Basic ", ""));
+                    if(!checkLogin)
+                    {
+                        string msg = $"请求访问失败,无法访问系统资源";
+                        context.Result = new JsonResult(ApiResult.Error(ResultCode.DENY, msg));
+                    }
+                }
+            }
+        }
+
+        public string Decrypt(string str)
+        {
+            str = Encoding.UTF8.GetString(Convert.FromBase64String(str));
+            return Dbconn.AesDecrypt(str, Base.GlobalConstant.ApiKey, Base.GlobalConstant.ApiIv);
+        }
+    }
+}

+ 233 - 0
Filters/GlobalActionMonitor.cs

@@ -0,0 +1,233 @@
+using Attribute;
+using Base;
+using Common;
+using Extensions;
+using Infrastructure;
+using Infrastructure.Model;
+using IPTools.Core;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Diagnostics;
+using Microsoft.AspNetCore.Mvc.Filters;
+using NLog;
+using System.Text;
+using System.Web;
+
+namespace Middleware
+{
+    public class GlobalActionMonitor : ActionFilterAttribute
+    {
+        static readonly Logger logger = LogManager.GetCurrentClassLogger();
+        // private readonly ISysOperLogService OperLogService;
+        public GlobalActionMonitor()
+        {
+            // OperLogService = operLogService;
+        }
+
+        /// <summary>
+        /// Action请求前
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="next"></param>
+        /// <returns></returns>
+        public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+        {
+            string content = "";
+            if(context.HttpContext.Request.Method.ToLower() == "get")
+            {
+                content = context.HttpContext.GetQueryString();
+                content = content.Substring(content.IndexOf("=") + 1);
+            }
+            else
+            {
+                content = context.HttpContext.GetBody();
+            }
+            if(!string.IsNullOrEmpty(content))
+            {                
+                if(content.Contains("{") && content.Contains("}"))
+                {
+                    Dictionary<string, object> dictionary = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(content);
+                    if(context.ActionDescriptor.Parameters.Count > 0)
+                    {
+                        var parameters = context.ActionDescriptor.Parameters;
+                        foreach(var parameter in parameters)
+                        {
+                            string parameterName = parameter.Name;
+                            Type objectType = parameter.ParameterType;
+                            if(objectType.FullName != "System.String")
+                            {
+                                System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(objectType);
+                                var entry = assembly.CreateInstance(objectType.FullName);
+                                Type type = entry.GetType();
+                                System.Reflection.PropertyInfo[] propertyInfos = type.GetProperties();
+                                for (int i = 0; i < propertyInfos.Length; i++)
+                                {
+                                    foreach (string key in dictionary.Keys)
+                                    {
+                                        string field = key.Substring(0, 1).ToUpper() + key.Substring(1);
+                                        if (propertyInfos[i].Name == field)
+                                        {
+                                            object value = dictionary[key];
+                                            if (propertyInfos[i].GetMethod.ReturnParameter.ParameterType.Name == "Int32")
+                                            {
+                                                value = Convert.ToInt32(value);
+                                            }
+                                            propertyInfos[i].SetValue(entry, value, null);
+                                            break;
+                                        }
+                                    }
+                                }
+                                if(context.ActionArguments.ContainsKey(parameterName))
+                                {
+                                    context.ActionArguments[parameterName] = entry;
+                                }
+                                else
+                                {
+                                    context.ActionArguments.Add(parameterName, entry);
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    string ParamName = context.ActionDescriptor.Parameters[0].Name;
+                    if(context.ActionArguments.ContainsKey(ParamName))
+                    {
+                        context.ActionArguments[ParamName] = content;
+                    }
+                    else
+                    {
+                        context.ActionArguments.Add(ParamName, content);
+                    }
+                }
+            }
+            else
+            {
+                string ParamName = context.ActionArguments.Keys.First();
+                object ParamValue = context.ActionArguments.Values.First();
+                // ParamValue = DesDecrypt(ParamValue.ToString());
+                if(ParamValue.GetType() == typeof(int))
+                {
+                    ParamValue = (int)ParamValue;
+                }
+                if(context.ActionArguments.ContainsKey(ParamName))
+                {
+                    context.ActionArguments[ParamName] = ParamValue;
+                }
+                else
+                {
+                    context.ActionArguments.Add(ParamName, ParamValue);
+                }
+            }
+            string msg = string.Empty;
+            var values = context.ModelState.Values;
+            foreach (var item in values)
+            {
+                foreach (var err in item.Errors)
+                {
+                    if (!string.IsNullOrEmpty(msg))
+                    {
+                        msg += " | ";
+                    }
+
+                    msg += err.ErrorMessage;
+                }
+            }
+            if (!string.IsNullOrEmpty(msg))
+            {
+                ApiResult response = new((int)ResultCode.PARAM_ERROR, msg);                
+                context.Result = new JsonResult(response);
+            }
+            return base.OnActionExecutionAsync(context, next);
+        }
+
+        /// <summary>
+        /// OnActionExecuted是在Action中的代码执行之后运行的方法。
+        /// </summary>
+        /// <param name="context"></param>
+        public override void OnResultExecuted(ResultExecutedContext context)
+        {
+            if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) return;
+
+            //获得注解信息
+            LogAttribute logAttribute = GetLogAttribute(controllerActionDescriptor);
+            if (logAttribute == null) return;
+
+            try
+            {
+                string method = context.HttpContext.Request.Method.ToUpper();
+                // 获取当前的用户
+                string userName = context.HttpContext.GetName() ?? context.HttpContext.Request.Headers["userName"];
+                string jsonResult = string.Empty;
+                if (context.Result is ContentResult result && result.ContentType == "application/json")
+                {
+                    jsonResult = result.Content.Replace("\r\n", "").Trim();
+                }
+                if (context.Result is JsonResult result2)
+                {
+                    jsonResult = result2.Value?.ToString();
+                }
+                //获取当前执行方法的类名
+                //string className =  System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
+                //获取当前成员的名称
+                //string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
+                string controller = context.RouteData.Values["Controller"].ToString();
+                string action = context.RouteData.Values["Action"].ToString();
+
+                string ip = HttpContextExtension.GetClientUserIp(context.HttpContext);
+                var ip_info = IpTool.Search(ip);
+
+                // SysOperLog sysOperLog = new()
+                // {
+                //     Status = 0,
+                //     OperName = userName,
+                //     OperIp = ip,
+                //     OperUrl = HttpContextExtension.GetRequestUrl(context.HttpContext),
+                //     RequestMethod = method,
+                //     JsonResult = jsonResult,
+                //     OperLocation = HttpContextExtension.GetIpInfo(ip),
+                //     Method = controller + "." + action + "()",
+                //     //Elapsed = _stopwatch.ElapsedMilliseconds,
+                //     OperTime = DateTime.Now,
+                //     OperParam = HttpContextExtension.GetRequestValue(context.HttpContext, method)
+                // };
+
+                if (logAttribute != null)
+                {
+                    // sysOperLog.Title = logAttribute?.Title;
+                    // sysOperLog.BusinessType = (int)logAttribute.BusinessType;
+                    // sysOperLog.OperParam = logAttribute.IsSaveRequestData ? sysOperLog.OperParam : "";
+                    // sysOperLog.JsonResult = logAttribute.IsSaveResponseData ? sysOperLog.JsonResult : "";
+                }
+
+                LogEventInfo ei = new(NLog.LogLevel.Info, "GlobalActionMonitor", "");
+
+                ei.Properties["jsonResult"] = !HttpMethods.IsGet(method) ? jsonResult : "";
+                // ei.Properties["requestParam"] = sysOperLog.OperParam;
+                ei.Properties["user"] = userName;
+                logger.Log(ei);
+
+                // OperLogService.InsertOperlog(sysOperLog);
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex, $"记录操作日志出错了#{ex.Message}");
+            }
+        }
+
+        private LogAttribute GetLogAttribute(ControllerActionDescriptor controllerActionDescriptor)
+        {
+            var attribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
+                .FirstOrDefault(a => a.GetType().Equals(typeof(LogAttribute)));
+
+            return attribute as LogAttribute;
+        }
+
+        private string DesDecrypt(string content)
+        {
+            content = HttpUtility.UrlDecode(content);
+            return Dbconn.DesDecrypt(content, AppSettings.GetConfig("ApiKey"));
+        }
+    }
+}

+ 76 - 0
Filters/VerifyAttribute.cs

@@ -0,0 +1,76 @@
+using System.Web;
+using Common;
+using Extensions;
+using Infrastructure;
+using Infrastructure.Model;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Model.Base;
+using Util;
+
+//本命名空间暂时先不改,改动比较大2023年9月2日
+namespace Filters
+{
+    /// <summary>
+    /// 授权校验访问
+    /// 如果跳过授权登录在Action 或controller加上 AllowAnonymousAttribute
+    /// </summary>
+    [AttributeUsage(AttributeTargets.All)]
+    public class VerifyAttribute : System.Attribute, IAuthorizationFilter
+    {
+        private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+        /// <summary>
+        /// 只判断token是否正确,不判断权限
+        /// 如果需要判断权限的在Action上加上ApiActionPermission属性标识权限类别,ActionPermissionFilter作权限处理
+        /// </summary>
+        /// <param name="context"></param>
+        public void OnAuthorization(AuthorizationFilterContext context)
+        {
+            var noNeedCheck = false;
+            if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
+            {
+                noNeedCheck = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
+                  .Any(a => a.GetType().Equals(typeof(AllowAnonymousAttribute)));
+            }
+
+            if (noNeedCheck) return;
+
+            string ip = HttpContextExtension.GetClientUserIp(context.HttpContext);
+            string url = context.HttpContext.Request.Path;
+            var isAuthed = context.HttpContext.User.Identity.IsAuthenticated;
+            string osType = context.HttpContext.Request.Headers["os"];
+            //使用jwt token校验2020-11-21
+            TokenModel loginUser = JwtUtil.GetLoginUser(context.HttpContext);
+            if (loginUser != null)
+            {
+                var nowTime = DateTime.UtcNow;
+                TimeSpan ts = loginUser.ExpireTime - nowTime;
+
+                //Console.WriteLine($"jwt到期剩余:{ts.TotalMinutes}分,{ts.TotalSeconds}秒");
+
+                var CK = "token_" + loginUser.UserId;
+                if (!CacheHelper.Exists(CK) && ts.TotalMinutes < 5)
+                {
+                    var newToken = JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser));
+                    
+                    CacheHelper.SetCache(CK, CK, 1);
+                    //移动端不加下面这个获取不到自定义Header
+                    if (osType != null)
+                    {
+                        context.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "X-Refresh-Token");
+                    }
+                    logger.Info($"刷新token,userName={loginUser.Username},token={newToken}");
+                    context.HttpContext.Response.Headers.Add("X-Refresh-Token", newToken);
+                }
+            }
+            if (loginUser == null)
+            {
+                string msg = $"请求访问[{url}]失败,无法访问系统资源";
+                //logger.Info(msg);
+                context.Result = new JsonResult(ApiResult.Error(ResultCode.DENY, msg));
+            }
+        }
+    }
+}

+ 3 - 0
GlobalUsing.cs

@@ -0,0 +1,3 @@
+global using System;
+global using System.Collections.Generic;
+global using SqlSugar;

+ 37 - 0
KxsOperateAdminServer.csproj

@@ -0,0 +1,37 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net7.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
+    <PackageReference Include="CSRedisCore" Version="3.8.802" />
+    <PackageReference Include="IPTools.China" Version="1.6.0" />
+    <PackageReference Include="LitJson" Version="0.19.0" />
+    <PackageReference Include="Mapster" Version="7.4.0" />
+    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.15" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
+    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.0" />
+    <PackageReference Include="MiniExcel" Version="1.31.3" />
+    <PackageReference Include="MySqlConnector" Version="2.3.4" />
+    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+    <PackageReference Include="NLog" Version="5.2.8" />
+    <PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
+    <PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.136" />
+    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
+    <PackageReference Include="ThoughtWorks.QRCode" Version="1.1.0" />
+    <PackageReference Include="UAParser" Version="3.1.47" />
+  </ItemGroup>
+
+</Project>

+ 142 - 0
Middleware/GlobalExceptionMiddleware.cs

@@ -0,0 +1,142 @@
+using Attribute;
+using Extensions;
+using Infrastructure;
+using Infrastructure.Model;
+using IPTools.Core;
+using Microsoft.AspNetCore.Http.Features;
+using Model;
+using NLog;
+using Services;
+using System.Text.Encodings.Web;
+using textJson = System.Text.Json;
+
+namespace Middleware
+{
+    /// <summary>
+    /// 全局异常处理中间件
+    /// 调用 app.UseMiddlewareGlobalExceptionMiddleware>();
+    /// </summary>
+    public class GlobalExceptionMiddleware
+    {
+        private readonly RequestDelegate next;
+        // private readonly ISysOperLogService SysOperLogService;
+
+        static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+
+        public GlobalExceptionMiddleware(RequestDelegate next)
+        {
+            this.next = next;
+            // this.SysOperLogService = sysOperLog;
+        }
+
+        public async Task Invoke(HttpContext context)
+        {
+            try
+            {
+                await next(context);
+            }
+            catch (Exception ex)
+            {
+                await HandleExceptionAsync(context, ex);
+            }
+        }
+
+        private async Task HandleExceptionAsync(HttpContext context, Exception ex)
+        {
+            NLog.LogLevel logLevel = NLog.LogLevel.Info;
+            int code = (int)ResultCode.GLOBAL_ERROR;
+            string msg;
+            string error = string.Empty;
+            bool notice = true;
+            //自定义异常
+            if (ex is CustomException customException)
+            {
+                code = customException.Code;
+                msg = customException.Message;
+                error = customException.LogMsg;
+                notice = customException.Notice;
+            }
+            else if (ex is ArgumentException)//参数异常
+            {
+                code = (int)ResultCode.PARAM_ERROR;
+                msg = ex.Message;
+            }
+            else
+            {
+                msg = "服务器好像出了点问题,请联系系统管理员...";
+                error = $"{ex.Message}";
+                logLevel = NLog.LogLevel.Error;
+                context.Response.StatusCode = 500;
+            }
+            var options = new textJson.JsonSerializerOptions
+            {
+                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+                PropertyNamingPolicy = textJson.JsonNamingPolicy.CamelCase,
+                WriteIndented = true
+            };
+
+            ApiResult apiResult = new(code, msg);
+            string responseResult = textJson.JsonSerializer.Serialize(apiResult, options);
+            // string ip = HttpContextExtension.GetClientUserIp(context);
+            // var ip_info = IpTool.Search(ip);
+
+            // SysOperLog sysOperLog = new()
+            // {
+            //     Status = 1,
+            //     // OperIp = ip,
+            //     OperUrl = HttpContextExtension.GetRequestUrl(context),
+            //     RequestMethod = context.Request.Method,
+            //     JsonResult = responseResult,
+            //     ErrorMsg = string.IsNullOrEmpty(error) ? msg : error,
+            //     OperName = HttpContextExtension.GetName(context),
+            //     // OperLocation = ip_info.Province + " " + ip_info.City,
+            //     OperTime = DateTime.Now,
+            //     OperParam = HttpContextExtension.GetRequestValue(context, context.Request.Method)
+            // };
+            var endpoint = GetEndpoint(context);
+            if (endpoint != null)
+            {
+                var logAttribute = endpoint.Metadata.GetMetadata<LogAttribute>();
+                if (logAttribute != null)
+                {
+                    // sysOperLog.BusinessType = (int)logAttribute.BusinessType;
+                    // sysOperLog.Title = logAttribute?.Title;
+                    // sysOperLog.OperParam = logAttribute.IsSaveRequestData ? sysOperLog.OperParam : "";
+                    // sysOperLog.JsonResult = logAttribute.IsSaveResponseData ? sysOperLog.JsonResult : "";
+                }
+            }
+            LogEventInfo ei = new(logLevel, "GlobalExceptionMiddleware", error)
+            {
+                Exception = ex,
+                Message = error
+            };
+            ei.Properties["status"] = 1;//走正常返回都是通过走GlobalExceptionFilter不通过
+            ei.Properties["jsonResult"] = responseResult;
+            // ei.Properties["requestParam"] = sysOperLog.OperParam;
+            // ei.Properties["user"] = sysOperLog.OperName;
+
+            Logger.Log(ei);
+            context.Response.ContentType = "text/json;charset=utf-8";
+            await context.Response.WriteAsync(responseResult, System.Text.Encoding.UTF8);
+
+            // string errorMsg = $"> 操作人:{sysOperLog.OperName}" +
+            //     $"\n> 操作地区:{sysOperLog.OperIp}({sysOperLog.OperLocation})" +
+            //     $"\n> 操作模块:{sysOperLog.Title}" +
+            //     $"\n> 操作地址:{sysOperLog.OperUrl}" +
+            //     $"\n> 错误信息:{msg}\n\n> {error}";
+
+            // SysOperLogService.InsertOperlog(sysOperLog);
+            if (!notice) return;
+        }
+
+        public static Endpoint GetEndpoint(HttpContext context)
+        {
+            if (context == null)
+            {
+                throw new ArgumentNullException(nameof(context));
+            }
+
+            return context.Features.Get<IEndpointFeature>()?.Endpoint;
+        }
+    }
+}

BIN
Model/.DS_Store


+ 127 - 0
Model/Base/ApiResult.cs

@@ -0,0 +1,127 @@
+using Infrastructure.Constant;
+using System.Collections.Generic;
+
+namespace Infrastructure.Model
+{
+    public class ApiResult : Dictionary<string, object>
+    {
+        /// <summary>
+        /// 状态码
+        /// </summary>
+        public static readonly string CODE_TAG = "status";
+
+        /// <summary>
+        /// 返回内容
+        /// </summary>
+        public static readonly string MSG_TAG = "msg";
+
+        /// <summary>
+        /// 数据对象
+        /// </summary>
+        public static readonly string DATA_TAG = "data";
+
+        /// <summary>
+        /// 初始化一个新创建的APIResult对象,使其表示一个空消息
+        /// </summary>
+        public ApiResult()
+        {
+        }
+
+        /// <summary>
+        /// 初始化一个新创建的 ApiResult 对象
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="msg"></param>
+        public ApiResult(int code, string msg)
+        {
+            Add(CODE_TAG, code);
+            Add(MSG_TAG, msg);
+        }
+
+        /// <summary>
+        /// 初始化一个新创建的 ApiResult 对象
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="msg"></param>
+        /// <param name="data"></param>
+        public ApiResult(int code, string msg, object data)
+        {
+            Add(CODE_TAG, code);
+            Add(MSG_TAG, msg);
+            if (data != null)
+            {
+                Add(DATA_TAG, data);
+            }
+        }
+
+        /// <summary>
+        /// 返回成功消息
+        /// </summary>
+        /// < returns > 成功消息 </ returns >
+        public static ApiResult Success() { return new ApiResult(HttpStatus.SUCCESS, "success"); }
+
+        /// <summary>
+        /// 返回成功消息
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns> 成功消息 </returns >
+        public static ApiResult Success(object data) { return new ApiResult(HttpStatus.SUCCESS, "success", data); }
+
+        /// <summary>
+        /// 返回成功消息
+        /// </summary>
+        /// <param name="msg">返回内容</param>
+        /// <returns>成功消息</returns>
+        public static ApiResult Success(string msg) { return new ApiResult(HttpStatus.SUCCESS, msg, null); }
+
+        /// <summary>
+        /// 返回成功消息
+        /// </summary>
+        /// <param name="msg">返回内容</param>
+        /// <param name="data">数据对象</param>
+        /// <returns>成功消息</returns>
+        public static ApiResult Success(string msg, object data) { return new ApiResult(HttpStatus.SUCCESS, msg, data); }
+
+        public static ApiResult Error(ResultCode code, string msg = "")
+        {
+            return Error((int)code, msg);
+        }
+
+        /// <summary>
+        /// 返回失败消息
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="msg"></param>
+        /// <returns></returns>
+        public static ApiResult Error(int code, string msg) { return new ApiResult(code, msg); }
+
+        /// <summary>
+        /// 返回失败消息
+        /// </summary>
+        /// <param name="msg"></param>
+        /// <returns></returns>
+        public static ApiResult Error(string msg) { return new ApiResult((int)ResultCode.CUSTOM_ERROR, msg); }
+
+
+        /// <summary>
+        /// 是否为成功消息
+        /// </summary>
+        /// <returns></returns>
+        public bool IsSuccess()
+        {
+            return HttpStatus.SUCCESS == (int)this[CODE_TAG];
+        }
+
+        /// <summary>
+        /// 方便链式调用
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public ApiResult Put(string key, object value)
+        {
+            Add(key, value);
+            return this;
+        }
+    }
+}

+ 128 - 0
Model/Base/OptionsSetting.cs

@@ -0,0 +1,128 @@
+using System.Collections.Generic;
+
+namespace Infrastructure.Model
+{
+    /// <summary>
+    /// 获取配置文件POCO实体类
+    /// </summary>
+    public class OptionsSetting
+    {
+        /// <summary>
+        /// 是否单点登录
+        /// </summary>
+        public bool SingleLogin { get; set; }
+        /// <summary>
+        /// 是否演示模式
+        /// </summary>
+        public bool DemoMode { get; set; }
+        /// <summary>
+        /// 初始化db
+        /// </summary>
+        public bool InitDb { get; set; }
+        public MailOptions MailOptions { get; set; }
+        public Upload Upload { get; set; }
+        public ALIYUN_OSS ALIYUN_OSS { get; set; }
+        public JwtSettings JwtSettings { get; set; }
+        public CodeGen CodeGen { get; set; }
+        public List<DbConfigs> DbConfigs { get; set; }
+        public DbConfigs CodeGenDbConfig { get; set; }
+    }
+    /// <summary>
+    /// 发送邮件数据配置
+    /// </summary>
+    public class MailOptions
+    {
+        public string FromName { get; set; }
+        public string FromEmail { get; set; }
+        public string Password { get; set; }
+        public string Smtp { get; set; }
+        public int Port { get; set; }
+        public bool UseSsl { get; set; }
+        public string Signature { get; set; }
+    }
+    /// <summary>
+    /// 上传
+    /// </summary>
+    public class Upload
+    {
+        public string UploadUrl { get; set; }
+        public string LocalSavePath { get; set; }
+        public int MaxSize { get; set; }
+        public string[] NotAllowedExt { get; set; } = new string[0];
+    }
+    /// <summary>
+    /// 阿里云存储
+    /// </summary>
+    public class ALIYUN_OSS
+    {
+        public string REGIONID { get; set; }
+        public string KEY { get; set; }
+        public string SECRET { get; set; }
+        public string BucketName { get; set; }
+        public string DomainUrl { get; set; }
+        public int MaxSize { get; set; } = 100;
+    }
+
+    /// <summary>
+    /// Jwt
+    /// </summary>
+    public class JwtSettings
+    {
+        /// <summary>
+        /// token是谁颁发的
+        /// </summary>
+        public string Issuer { get; set; }
+        /// <summary>
+        /// token可以给那些客户端使用
+        /// </summary>
+        public string Audience { get; set; }
+        /// <summary>
+        /// 加密的key(SecretKey必须大于16个,是大于,不是大于等于)
+        /// </summary>
+        public string SecretKey { get; set; }
+        /// <summary>
+        /// token时间(分)
+        /// </summary>
+        public int Expire { get; set; } = 1440;
+        /// <summary>
+        /// 刷新token时长
+        /// </summary>
+        public int RefreshTokenTime { get; set; }
+        /// <summary>
+        /// token类型
+        /// </summary>
+        public string TokenType { get; set; } = "Bearer";
+    }
+
+    public class CodeGen
+    {
+        public bool ShowApp { get; set; }
+        public bool AutoPre { get; set; }
+        public string VuePath { get; set; }
+        public string Author { get; set; }
+        public string TablePrefix { get; set; }
+        public string ModuleName { get; set; }
+        public int FrontTpl { get; set; }
+        public CsharpTypeArr CsharpTypeArr { get; set; }
+    }
+
+    public class DbConfigs
+    {
+        public string Conn { get; set; }
+        public int DbType { get; set; }
+        public string ConfigId { get; set; }
+        public bool IsAutoCloseConnection { get; set; }
+        public string DbName { get; set; }
+    }
+
+    public class CsharpTypeArr
+    {
+        public string[] String { get; set; }
+        public string[] Int { get; set; }
+        public string[] Long { get; set; }
+        public string[] DateTime { get; set; }
+        public string[] Float { get; set; }
+        public string[] Decimal { get; set; }
+        public string[] Bool { get; set; }
+    }
+}

+ 46 - 0
Model/Base/PagedInfo.cs

@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+
+namespace Model.Base
+{
+    /// <summary>
+    /// 分页参数
+    /// </summary>
+    public class PagedInfo<T>
+    {
+        /// <summary>
+        /// 每页行数
+        /// </summary>
+        public int PageSize { get; set; } = 10;
+        /// <summary>
+        /// 当前页
+        /// </summary>
+        public int PageIndex { get; set; } = 1;
+        /// <summary>
+        /// 总记录数
+        /// </summary>
+        public int TotalNum { get; set; }
+        /// <summary>
+        /// 总页数
+        /// </summary>
+        public int TotalPage
+        {
+            get
+            {
+                if (TotalNum > 0)
+                {
+                    return TotalNum % this.PageSize == 0 ? TotalNum / this.PageSize : TotalNum / this.PageSize + 1;
+                }
+                else
+                {
+                    return 0;
+                }
+            }
+            set { }
+        }
+        public List<T> Result { get; set; }
+        public Dictionary<string, object> Extra { get; set; } = new Dictionary<string, object>();
+        public PagedInfo()
+        {
+        }
+    }
+}

+ 37 - 0
Model/Base/PagerInfo.cs

@@ -0,0 +1,37 @@
+namespace Model.Base
+{
+    public class PagerInfo
+    {
+        /// <summary>
+        /// 当前页码
+        /// </summary>
+        public int PageNum { get; set; }
+        /// <summary>
+        /// 每页显示多少条
+        /// </summary>
+        public int PageSize { get; set; }
+        /// <summary>
+        /// 总记录数
+        /// </summary>
+        public int TotalNum { get; set; }
+        /// <summary>
+        /// 排序字段
+        /// </summary>
+        public string Sort { get; set; } = string.Empty;
+        /// <summary>
+        /// 排序类型,前端传入的是"ascending","descending"
+        /// </summary>
+        public string SortType { get; set; } = string.Empty;
+        public PagerInfo()
+        {
+            PageNum = 1;
+            PageSize = 20;
+        }
+
+        public PagerInfo(int page = 1, int pageSize = 20)
+        {
+            PageNum = page;
+            PageSize = pageSize;
+        }
+    }
+}

+ 45 - 0
Model/Base/SysBase.cs

@@ -0,0 +1,45 @@
+using MiniExcelLibs.Attributes;
+using Newtonsoft.Json;
+
+namespace Model
+{
+    //[EpplusTable(PrintHeaders = true, AutofitColumns = true, AutoCalculate = true, ShowTotal = true)]
+    public class SysBase
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "id")]
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        [SugarColumn(ColumnDescription = "排序", DefaultValue = "0", ColumnName = "sort")]
+        public int Sort { get; set; }
+
+        /// <summary>
+        /// 状态
+        /// </summary>
+        [SugarColumn(ColumnDescription = "状态", DefaultValue = "0", ColumnName = "status")]
+        public int Status { get; set; }
+
+        /// <summary>
+        /// 版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "版本号", DefaultValue = "0", ColumnName = "version")]
+        public int Version { get; set; }
+
+        /// <summary>
+        /// 发布时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "发布时间", ColumnName = "create_date")]
+        public DateTime? CreateDate { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_date")]
+        public DateTime? UpdateDate { get; set; }
+    }
+}

+ 96 - 0
Model/Base/SysCity.cs

@@ -0,0 +1,96 @@
+namespace Model
+{
+    /// <summary>
+    /// 城市表 sys_city
+    /// </summary>
+    [SugarTable("sys_city", "城市表")]
+    [Tenant("0")]
+    public class SysCity
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        [SugarColumn(ColumnDescription = "", ColumnName = "id")]
+        public int Id { get; set; }
+
+
+        /// <summary>
+        /// 城市id
+        /// </summary>
+        [SugarColumn(ColumnDescription = "城市id", ColumnName = "city_id")]
+        public int CityId { get; set; }
+
+
+        /// <summary>
+        /// 省市级别
+        /// </summary>
+        [SugarColumn(ColumnDescription = "省市级别", ColumnName = "level")]
+        public int Level { get; set; }
+
+
+        /// <summary>
+        /// 父级id
+        /// </summary>
+        [SugarColumn(ColumnDescription = "父级id", ColumnName = "parent_id")]
+        public int ParentId { get; set; }
+
+
+        /// <summary>
+        /// 区号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "区号", Length = 30, ColumnName = "area_code")]
+        public string AreaCode { get; set; }
+
+
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "名称", Length = 100, ColumnName = "name")]
+        public string Name { get; set; }
+
+
+        /// <summary>
+        /// 合并名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "合并名称", Length = 255, ColumnName = "merger_name")]
+        public string MergerName { get; set; }
+
+
+        /// <summary>
+        /// 经度
+        /// </summary>
+        [SugarColumn(ColumnDescription = "经度", Length = 50, ColumnName = "lng")]
+        public string Lng { get; set; }
+
+
+        /// <summary>
+        /// 纬度
+        /// </summary>
+        [SugarColumn(ColumnDescription = "纬度", Length = 50, ColumnName = "lat")]
+        public string Lat { get; set; }
+
+
+        /// <summary>
+        /// 是否展示
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否展示", ColumnName = "is_show")]
+        public string IsShow { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public string CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public string UpdateTime { get; set; }
+
+
+
+    }
+}

+ 78 - 0
Model/Base/SysDept.cs

@@ -0,0 +1,78 @@
+namespace Model
+{
+    /// <summary>
+    /// 部门管理 sys_dept
+    /// </summary>
+    [SugarTable("sys_dept", "部门管理")]
+    [Tenant("0")]
+    public class SysDept
+    {
+        /// <summary>
+        /// 部门ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "部门ID", ColumnName = "dept_id")]
+        public long DeptId { get; set; }
+
+
+        /// <summary>
+        /// 部门名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "部门名称", Length = 50, ColumnName = "name")]
+        public string Name { get; set; }
+
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        [SugarColumn(ColumnDescription = "排序", ColumnName = "sort_order")]
+        public int SortOrder { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 修改时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 删除标志
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标志", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 父级部门ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "父级部门ID", ColumnName = "parent_id")]
+        public long ParentId { get; set; }
+
+
+        /// <summary>
+        /// 子菜单
+        /// </summary>
+        public List<SysDept> children = new();
+    }
+}

+ 82 - 0
Model/Base/SysDict.cs

@@ -0,0 +1,82 @@
+namespace Model
+{
+    /// <summary>
+    /// 字典表 sys_dict
+    /// </summary>
+    [SugarTable("sys_dict", "字典表")]
+    [Tenant("0")]
+    public class SysDict
+    {
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号", ColumnName = "id")]
+        public long Id { get; set; }
+
+
+        /// <summary>
+        /// 字典类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "字典类型", Length = 100, ColumnName = "dict_type")]
+        public string DictType { get; set; }
+
+
+        /// <summary>
+        /// 描述
+        /// </summary>
+        [SugarColumn(ColumnDescription = "描述", Length = 100, ColumnName = "description")]
+        public string Description { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 备注信息
+        /// </summary>
+        [SugarColumn(ColumnDescription = "备注信息", Length = 255, ColumnName = "remarks")]
+        public string Remarks { get; set; }
+
+
+        /// <summary>
+        /// 系统标志
+        /// </summary>
+        [SugarColumn(ColumnDescription = "系统标志", Length = 1, ColumnName = "system_flag")]
+        public string SystemFlag { get; set; }
+
+
+        /// <summary>
+        /// 删除标志
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标志", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+
+    }
+}

+ 103 - 0
Model/Base/SysDictItem.cs

@@ -0,0 +1,103 @@
+namespace Model
+{
+    /// <summary>
+    /// 字典项 sys_dict_item
+    /// </summary>
+    [SugarTable("sys_dict_item", "字典项")]
+    [Tenant("0")]
+    public class SysDictItem
+    {
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号", ColumnName = "id")]
+        public long Id { get; set; }
+
+
+        /// <summary>
+        /// 字典ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "字典ID", ColumnName = "dict_id")]
+        public long DictId { get; set; }
+
+
+        /// <summary>
+        /// 字典项值
+        /// </summary>
+        [SugarColumn(ColumnDescription = "字典项值", Length = 100, ColumnName = "item_value")]
+        public string ItemValue { get; set; }
+
+
+        /// <summary>
+        /// 字典项名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "字典项名称", Length = 100, ColumnName = "label")]
+        public string Label { get; set; }
+
+
+        /// <summary>
+        /// 字典类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "字典类型", Length = 100, ColumnName = "dict_type")]
+        public string DictType { get; set; }
+
+
+        /// <summary>
+        /// 字典项描述
+        /// </summary>
+        [SugarColumn(ColumnDescription = "字典项描述", Length = 100, ColumnName = "description")]
+        public string Description { get; set; }
+
+
+        /// <summary>
+        /// 排序(升序)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "排序(升序)", ColumnName = "sort_order")]
+        public int SortOrder { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 备注信息
+        /// </summary>
+        [SugarColumn(ColumnDescription = "备注信息", Length = 255, ColumnName = "remarks")]
+        public string Remarks { get; set; }
+
+
+        /// <summary>
+        /// 删除标志
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标志", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+
+    }
+}

+ 89 - 0
Model/Base/SysFile.cs

@@ -0,0 +1,89 @@
+namespace Model
+{
+    /// <summary>
+    /// 文件管理表 sys_file
+    /// </summary>
+    [SugarTable("sys_file", "文件管理表")]
+    [Tenant("0")]
+    public class SysFile
+    {
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号", ColumnName = "id")]
+        public long Id { get; set; }
+
+
+        /// <summary>
+        /// 文件名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "文件名", Length = 100, ColumnName = "file_name")]
+        public string FileName { get; set; }
+
+
+        /// <summary>
+        /// 文件存储桶名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "文件存储桶名称", Length = 200, ColumnName = "bucket_name")]
+        public string BucketName { get; set; }
+
+
+        /// <summary>
+        /// 原始文件名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "原始文件名", Length = 100, ColumnName = "original")]
+        public string Original { get; set; }
+
+
+        /// <summary>
+        /// 文件类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "文件类型", Length = 50, ColumnName = "type")]
+        public string Type { get; set; }
+
+
+        /// <summary>
+        /// 文件大小
+        /// </summary>
+        [SugarColumn(ColumnDescription = "文件大小", ColumnName = "file_size")]
+        public long FileSize { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 上传时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "上传时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 删除标志
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标志", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+
+    }
+}

+ 124 - 0
Model/Base/SysLog.cs

@@ -0,0 +1,124 @@
+namespace Model
+{
+    /// <summary>
+    /// 日志表 sys_log
+    /// </summary>
+    [SugarTable("sys_log", "日志表")]
+    [Tenant("0")]
+    public class SysLog
+    {
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号", ColumnName = "id")]
+        public long Id { get; set; }
+
+
+        /// <summary>
+        /// 日志类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "日志类型", Length = 1, ColumnName = "log_type")]
+        public string LogType { get; set; }
+
+
+        /// <summary>
+        /// 日志标题
+        /// </summary>
+        [SugarColumn(ColumnDescription = "日志标题", Length = 255, ColumnName = "title")]
+        public string Title { get; set; }
+
+
+        /// <summary>
+        /// 服务ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "服务ID", Length = 32, ColumnName = "service_id")]
+        public string ServiceId { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 远程地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "远程地址", Length = 255, ColumnName = "remote_addr")]
+        public string RemoteAddr { get; set; }
+
+
+        /// <summary>
+        /// 用户代理
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户代理", Length = 1000, ColumnName = "user_agent")]
+        public string UserAgent { get; set; }
+
+
+        /// <summary>
+        /// 请求URI
+        /// </summary>
+        [SugarColumn(ColumnDescription = "请求URI", Length = 255, ColumnName = "request_uri")]
+        public string RequestUri { get; set; }
+
+
+        /// <summary>
+        /// 请求方法
+        /// </summary>
+        [SugarColumn(ColumnDescription = "请求方法", Length = 10, ColumnName = "method")]
+        public string Method { get; set; }
+
+
+        /// <summary>
+        /// 请求参数
+        /// </summary>
+        [SugarColumn(ColumnDescription = "请求参数", ColumnName = "params")]
+        public string Params { get; set; }
+
+
+        /// <summary>
+        /// 执行时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "执行时间", ColumnName = "time")]
+        public long Time { get; set; }
+
+
+        /// <summary>
+        /// 删除标志
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标志", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 异常信息
+        /// </summary>
+        [SugarColumn(ColumnDescription = "异常信息", ColumnName = "exception")]
+        public string Exception { get; set; }
+
+
+
+    }
+}

+ 58 - 0
Model/Base/SysLogininfor.cs

@@ -0,0 +1,58 @@
+namespace Model
+{
+    /// <summary>
+    /// sys_logininfor 表
+    /// </summary>
+    [SugarTable("sys_logininfor", "登录日志表")]
+    [Tenant("0")]
+    public class SysLogininfor
+    {
+        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
+        public long InfoId { get; set; }
+
+        /// <summary>
+        /// 用户账号
+        /// </summary>
+        public string UserName { get; set; }
+
+        /// <summary>
+        /// 登录状态 0成功 1失败
+        /// </summary>
+        [SugarColumn(Length = 1, DefaultValue = "0")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 登录IP地址
+        /// </summary>
+        public string Ipaddr { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 登录地点
+        /// </summary>
+        public string LoginLocation { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 浏览器类型
+        /// </summary>
+        public string Browser { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 操作系统
+        /// </summary>
+        public string Os { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 提示消息
+        /// </summary>
+        public string Msg { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 访问时间
+        /// </summary>
+        public DateTime LoginTime { get; set; } = DateTime.Now;
+        [SugarColumn(IsIgnore = true)]
+        public DateTime? BeginTime { get; set; }
+        [SugarColumn(IsIgnore = true)]
+        public DateTime? EndTime { get; set; }
+    }
+}

+ 151 - 0
Model/Base/SysMenu.cs

@@ -0,0 +1,151 @@
+namespace Model
+{
+    /// <summary>
+    /// 菜单权限表 sys_menu
+    /// </summary>
+    [SugarTable("sys_menu", "菜单权限表")]
+    [Tenant("0")]
+    public class SysMenu
+    {
+        /// <summary>
+        /// 菜单ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "菜单ID", ColumnName = "menu_id")]
+        public long MenuId { get; set; }
+
+
+        /// <summary>
+        /// 菜单名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "菜单名称", Length = 32, ColumnName = "name")]
+        public string Name { get; set; }
+
+
+        /// <summary>
+        /// 英文名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "英文名称", Length = 128, ColumnName = "en_name")]
+        public string EnName { get; set; }
+
+
+        /// <summary>
+        /// 权限标识
+        /// </summary>
+        [SugarColumn(ColumnDescription = "权限标识", Length = 32, ColumnName = "permission")]
+        public string Permission { get; set; }
+
+
+        /// <summary>
+        /// 路由路径
+        /// </summary>
+        [SugarColumn(ColumnDescription = "路由路径", Length = 128, ColumnName = "path")]
+        public string Path { get; set; }
+
+
+        /// <summary>
+        /// 父菜单ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "父菜单ID", ColumnName = "parent_id")]
+        public long ParentId { get; set; }
+
+
+        /// <summary>
+        /// 菜单图标
+        /// </summary>
+        [SugarColumn(ColumnDescription = "菜单图标", Length = 64, ColumnName = "icon")]
+        public string Icon { get; set; }
+
+
+        /// <summary>
+        /// 是否可见,0隐藏,1显示
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否可见,0隐藏,1显示", Length = 1, ColumnName = "visible")]
+        public string Visible { get; set; }
+
+
+        /// <summary>
+        /// 排序值,越小越靠前
+        /// </summary>
+        [SugarColumn(ColumnDescription = "排序值,越小越靠前", ColumnName = "sort_order")]
+        public int SortOrder { get; set; }
+
+
+        /// <summary>
+        /// 是否缓存,0否,1是
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否缓存,0否,1是", Length = 1, ColumnName = "keep_alive")]
+        public string KeepAlive { get; set; }
+
+
+        /// <summary>
+        /// 是否内嵌,0否,1是
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否内嵌,0否,1是", Length = 1, ColumnName = "embedded")]
+        public string Embedded { get; set; }
+
+
+        /// <summary>
+        /// 菜单类型,0目录,1菜单,2按钮
+        /// </summary>
+        [SugarColumn(ColumnDescription = "菜单类型,0目录,1菜单,2按钮", Length = 1, ColumnName = "menu_type")]
+        public string MenuType { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 删除标志,0未删除,1已删除
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标志,0未删除,1已删除", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 子菜单
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public List<SysMenu> Children { get; set; } = new List<SysMenu>();
+        /// <summary>
+        /// 子菜单个数
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public int SubNum { get; set; }
+        /// <summary>
+        /// 是否包含子节点,前端用
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public bool HasChildren
+        {
+            get
+            {
+                return SubNum > 0 || Children.Count > 0;
+            }
+        }
+    }
+}

+ 131 - 0
Model/Base/SysOauthClientDetails.cs

@@ -0,0 +1,131 @@
+namespace Model
+{
+    /// <summary>
+    /// 终端信息表 sys_oauth_client_details
+    /// </summary>
+    [SugarTable("sys_oauth_client_details", "终端信息表")]
+    [Tenant("0")]
+    public class SysOauthClientDetails
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "ID", ColumnName = "id")]
+        public long Id { get; set; }
+
+
+        /// <summary>
+        /// 客户端ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "客户端ID", Length = 32, ColumnName = "client_id")]
+        public string ClientId { get; set; }
+
+
+        /// <summary>
+        /// 资源ID集合
+        /// </summary>
+        [SugarColumn(ColumnDescription = "资源ID集合", Length = 256, ColumnName = "resource_ids")]
+        public string ResourceIds { get; set; }
+
+
+        /// <summary>
+        /// 客户端秘钥
+        /// </summary>
+        [SugarColumn(ColumnDescription = "客户端秘钥", Length = 256, ColumnName = "client_secret")]
+        public string ClientSecret { get; set; }
+
+
+        /// <summary>
+        /// 授权范围
+        /// </summary>
+        [SugarColumn(ColumnDescription = "授权范围", Length = 256, ColumnName = "scope")]
+        public string Scope { get; set; }
+
+
+        /// <summary>
+        /// 授权类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "授权类型", Length = 256, ColumnName = "authorized_grant_types")]
+        public string AuthorizedGrantTypes { get; set; }
+
+
+        /// <summary>
+        /// 回调地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "回调地址", Length = 256, ColumnName = "web_server_redirect_uri")]
+        public string WebServerRedirectUri { get; set; }
+
+
+        /// <summary>
+        /// 权限集合
+        /// </summary>
+        [SugarColumn(ColumnDescription = "权限集合", Length = 256, ColumnName = "authorities")]
+        public string Authorities { get; set; }
+
+
+        /// <summary>
+        /// 访问令牌有效期(秒)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "访问令牌有效期(秒)", ColumnName = "access_token_validity")]
+        public int AccessTokenValidity { get; set; }
+
+
+        /// <summary>
+        /// 刷新令牌有效期(秒)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "刷新令牌有效期(秒)", ColumnName = "refresh_token_validity")]
+        public int RefreshTokenValidity { get; set; }
+
+
+        /// <summary>
+        /// 附加信息
+        /// </summary>
+        [SugarColumn(ColumnDescription = "附加信息", Length = 4096, ColumnName = "additional_information")]
+        public string AdditionalInformation { get; set; }
+
+
+        /// <summary>
+        /// 自动授权
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自动授权", Length = 256, ColumnName = "autoapprove")]
+        public string Autoapprove { get; set; }
+
+
+        /// <summary>
+        /// 删除标记,0未删除,1已删除
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标记,0未删除,1已删除", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+
+    }
+}

+ 138 - 0
Model/Base/SysParam.cs

@@ -0,0 +1,138 @@
+namespace Model
+{
+    /// <summary>
+    /// 第三方平台设置 sys_param
+    /// </summary>
+    [SugarTable("sys_param", "第三方平台设置")]
+    [Tenant("0")]
+    public class SysParam
+    {
+        /// <summary>
+        /// Id
+        /// </summary>
+        [SugarColumn(ColumnDescription = "Id", ColumnName = "id")]
+        public int Id { get; set; }
+
+
+        /// <summary>
+        /// 自动缩略图质量
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自动缩略图质量", ColumnName = "upload_auto_zoom_quality")]
+        public int UploadAutoZoomQuality { get; set; }
+
+
+        /// <summary>
+        /// 是否上传到OSS
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否上传到OSS", ColumnName = "upload_oss")]
+        public bool UploadOss { get; set; }
+
+
+        /// <summary>
+        /// Logo图片
+        /// </summary>
+        [SugarColumn(ColumnDescription = "Logo图片", Length = 200, ColumnName = "logo")]
+        public string Logo { get; set; }
+
+
+        /// <summary>
+        /// 网站统计代码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "网站统计代码", ColumnName = "web_stat_code")]
+        public string WebStatCode { get; set; }
+
+
+        /// <summary>
+        /// 联系电话
+        /// </summary>
+        [SugarColumn(ColumnDescription = "联系电话", Length = 100, ColumnName = "phone")]
+        public string Phone { get; set; }
+
+
+        /// <summary>
+        /// 企业地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "企业地址", Length = 100, ColumnName = "address")]
+        public string Address { get; set; }
+
+
+        /// <summary>
+        /// 版权信息
+        /// </summary>
+        [SugarColumn(ColumnDescription = "版权信息", ColumnName = "right_info")]
+        public string RightInfo { get; set; }
+
+
+        /// <summary>
+        /// 水印图片
+        /// </summary>
+        [SugarColumn(ColumnDescription = "水印图片", Length = 200, ColumnName = "upload_water_path")]
+        public string UploadWaterPath { get; set; }
+
+
+        /// <summary>
+        /// 水印位置
+        /// </summary>
+        [SugarColumn(ColumnDescription = "水印位置", Length = 15, ColumnName = "upload_water_position")]
+        public string UploadWaterPosition { get; set; }
+
+
+        /// <summary>
+        /// 自动水印
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自动水印", ColumnName = "upload_auto_water")]
+        public bool UploadAutoWater { get; set; }
+
+
+        /// <summary>
+        /// 自动缩略图高
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自动缩略图高", ColumnName = "upload_auto_zoom_height")]
+        public int UploadAutoZoomHeight { get; set; }
+
+
+        /// <summary>
+        /// 自动缩略图宽
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自动缩略图宽", ColumnName = "upload_auto_zoom_width")]
+        public int UploadAutoZoomWidth { get; set; }
+
+
+        /// <summary>
+        /// 自动缩略图
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自动缩略图", ColumnName = "upload_auto_zoom_")]
+        public bool UploadAutoZoom { get; set; }
+
+
+        /// <summary>
+        /// 允许最大上传
+        /// </summary>
+        [SugarColumn(ColumnDescription = "允许最大上传", ColumnName = "upload_max_size")]
+        public int UploadMaxSize { get; set; }
+
+
+        /// <summary>
+        /// 允许上传类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "允许上传类型", Length = 100, ColumnName = "upload_ext")]
+        public string UploadExt { get; set; }
+
+
+        /// <summary>
+        /// 项目名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "项目名称", Length = 50, ColumnName = "project_name")]
+        public string ProjectName { get; set; }
+
+
+        /// <summary>
+        /// 官网地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "官网地址", Length = 200, ColumnName = "web_url")]
+        public string WebUrl { get; set; }
+
+
+
+    }
+}

+ 47 - 0
Model/Base/SysPermission.cs

@@ -0,0 +1,47 @@
+namespace Model
+{
+    /// <summary>
+    /// 角色表 sys_role
+    /// </summary>
+    [Tenant("0")]
+    public class SysPermission : SysBase
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        [SugarColumn(ColumnDescription = "排序", DefaultValue = "0")]
+        public int Sort { get; set; }
+
+        /// <summary>
+        /// 状态
+        /// </summary>
+        [SugarColumn(ColumnDescription = "状态", DefaultValue = "0")]
+        public int Status { get; set; }
+
+        /// <summary>
+        /// 版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "版本号", DefaultValue = "0")]
+        public int Version { get; set; }
+
+        /// <summary>
+        /// 发布时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "发布时间")]
+        public DateTime? CreateDate { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(IsOnlyIgnoreInsert = true, ColumnDescription = "更新时间")]
+        public DateTime? UpdateDate { get; set; }
+
+
+    }
+}

+ 82 - 0
Model/Base/SysPost.cs

@@ -0,0 +1,82 @@
+namespace Model
+{
+    /// <summary>
+    /// 岗位信息表 sys_post
+    /// </summary>
+    [SugarTable("sys_post", "岗位信息表")]
+    [Tenant("0")]
+    public class SysPost
+    {
+        /// <summary>
+        /// 岗位ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "岗位ID", ColumnName = "post_id")]
+        public long PostId { get; set; }
+
+
+        /// <summary>
+        /// 岗位编码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "岗位编码", Length = 64, ColumnName = "post_code")]
+        public string PostCode { get; set; }
+
+
+        /// <summary>
+        /// 岗位名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "岗位名称", Length = 50, ColumnName = "post_name")]
+        public string PostName { get; set; }
+
+
+        /// <summary>
+        /// 岗位排序
+        /// </summary>
+        [SugarColumn(ColumnDescription = "岗位排序", ColumnName = "post_sort")]
+        public int PostSort { get; set; }
+
+
+        /// <summary>
+        /// 岗位描述
+        /// </summary>
+        [SugarColumn(ColumnDescription = "岗位描述", Length = 500, ColumnName = "remark")]
+        public string Remark { get; set; }
+
+
+        /// <summary>
+        /// 是否删除  -1:已删除  0:正常
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否删除  -1:已删除  0:正常", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+
+    }
+}

+ 103 - 0
Model/Base/SysPublicParam.cs

@@ -0,0 +1,103 @@
+namespace Model
+{
+    /// <summary>
+    /// 公共参数配置表 sys_public_param
+    /// </summary>
+    [SugarTable("sys_public_param", "公共参数配置表")]
+    [Tenant("0")]
+    public class SysPublicParam
+    {
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号", ColumnName = "public_id")]
+        public long PublicId { get; set; }
+
+
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "名称", Length = 128, ColumnName = "public_name")]
+        public string PublicName { get; set; }
+
+
+        /// <summary>
+        /// 键
+        /// </summary>
+        [SugarColumn(ColumnDescription = "键", Length = 128, ColumnName = "public_key")]
+        public string PublicKey { get; set; }
+
+
+        /// <summary>
+        /// 值
+        /// </summary>
+        [SugarColumn(ColumnDescription = "值", Length = 128, ColumnName = "public_value")]
+        public string PublicValue { get; set; }
+
+
+        /// <summary>
+        /// 状态,0禁用,1启用
+        /// </summary>
+        [SugarColumn(ColumnDescription = "状态,0禁用,1启用", Length = 1, ColumnName = "status")]
+        public string Status { get; set; }
+
+
+        /// <summary>
+        /// 校验码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "校验码", Length = 64, ColumnName = "validate_code")]
+        public string ValidateCode { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 类型,0未知,1系统,2业务
+        /// </summary>
+        [SugarColumn(ColumnDescription = "类型,0未知,1系统,2业务", Length = 1, ColumnName = "public_type")]
+        public string PublicType { get; set; }
+
+
+        /// <summary>
+        /// 系统标识,0非系统,1系统
+        /// </summary>
+        [SugarColumn(ColumnDescription = "系统标识,0非系统,1系统", Length = 1, ColumnName = "system_flag")]
+        public string SystemFlag { get; set; }
+
+
+        /// <summary>
+        /// 删除标记,0未删除,1已删除
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标记,0未删除,1已删除", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+
+    }
+}

+ 97 - 0
Model/Base/SysRole.cs

@@ -0,0 +1,97 @@
+namespace Model
+{
+    /// <summary>
+    /// 系统角色表 sys_role
+    /// </summary>
+    [SugarTable("sys_role", "系统角色表")]
+    [Tenant("0")]
+    public class SysRole
+    {
+        /// <summary>
+        /// 角色ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "角色ID", ColumnName = "role_id")]
+        public long RoleId { get; set; }
+
+
+        /// <summary>
+        /// 角色名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "角色名称", Length = 64, ColumnName = "role_name")]
+        public string RoleName { get; set; }
+
+
+        /// <summary>
+        /// 角色编码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "角色编码", Length = 64, ColumnName = "role_code")]
+        public string RoleCode { get; set; }
+
+
+        /// <summary>
+        /// 角色描述
+        /// </summary>
+        [SugarColumn(ColumnDescription = "角色描述", Length = 255, ColumnName = "role_desc")]
+        public string RoleDesc { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 删除标记,0未删除,1已删除
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标记,0未删除,1已删除", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 用户个数
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public int UserNum { get; set; }
+
+
+        public SysRole() { }
+
+        public SysRole(long roleId)
+        {
+            RoleId = roleId;
+        }
+
+        public bool IsAdmin()
+        {
+            return IsAdmin(RoleId);
+        }
+
+        public static bool IsAdmin(long roleId)
+        {
+            return 1 == roleId;
+        }
+    }
+}

+ 13 - 0
Model/Base/SysRoleDept.cs

@@ -0,0 +1,13 @@
+namespace System
+{
+    [SugarTable("sys_role_dept", "角色部门")]
+    [Tenant(0)]
+    public class SysRoleDept
+    {
+        [SugarColumn(ExtendedAttribute = ProteryConstant.NOTNULL, IsPrimaryKey = true)]
+        public long RoleId { get; set; }
+
+        [SugarColumn(ExtendedAttribute = ProteryConstant.NOTNULL, IsPrimaryKey = true)]
+        public long DeptId { get; set; }
+    }
+}

+ 26 - 0
Model/Base/SysRoleMenu.cs

@@ -0,0 +1,26 @@
+namespace Model
+{
+    /// <summary>
+    /// 角色菜单表 sys_role_menu
+    /// </summary>
+    [SugarTable("sys_role_menu", "角色菜单表")]
+    [Tenant("0")]
+    public class SysRoleMenu
+    {
+        /// <summary>
+        /// 角色ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "角色ID", ColumnName = "role_id")]
+        public long RoleId { get; set; }
+
+
+        /// <summary>
+        /// 菜单ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "菜单ID", ColumnName = "menu_id")]
+        public long MenuId { get; set; }
+
+
+
+    }
+}

+ 184 - 0
Model/Base/SysUser.cs

@@ -0,0 +1,184 @@
+using MiniExcelLibs.Attributes;
+
+namespace Model
+{
+    /// <summary>
+    /// 用户表 sys_user
+    /// </summary>
+    [SugarTable("sys_user", "用户表")]
+    [Tenant("0")]
+    public class SysUser
+    {
+        /// <summary>
+        /// 用户ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户ID", ColumnName = "user_id")]
+        public long UserId { get; set; }
+
+
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户名", Length = 64, ColumnName = "username")]
+        public string Username { get; set; }
+
+
+        /// <summary>
+        /// 密码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "密码", Length = 255, ColumnName = "password")]
+        public string Password { get; set; }
+
+
+        /// <summary>
+        /// 盐值
+        /// </summary>
+        [SugarColumn(ColumnDescription = "盐值", Length = 255, ColumnName = "salt")]
+        public string Salt { get; set; }
+
+
+        /// <summary>
+        /// 电话号码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "电话号码", Length = 20, ColumnName = "phone")]
+        public string Phone { get; set; }
+
+
+        /// <summary>
+        /// 头像
+        /// </summary>
+        [SugarColumn(ColumnDescription = "头像", Length = 255, ColumnName = "avatar")]
+        public string Avatar { get; set; }
+
+
+        /// <summary>
+        /// 昵称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "昵称", Length = 64, ColumnName = "nickname")]
+        public string Nickname { get; set; }
+
+
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "姓名", Length = 64, ColumnName = "name")]
+        public string Name { get; set; }
+
+
+        /// <summary>
+        /// 邮箱地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "邮箱地址", Length = 128, ColumnName = "email")]
+        public string Email { get; set; }
+
+
+        /// <summary>
+        /// 所属部门ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "所属部门ID", ColumnName = "dept_id")]
+        public long DeptId { get; set; }
+
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建人", Length = 64, ColumnName = "create_by")]
+        public string CreateBy { get; set; }
+
+
+        /// <summary>
+        /// 修改人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改人", Length = 64, ColumnName = "update_by")]
+        public string UpdateBy { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_time")]
+        public DateTime? CreateTime { get; set; }
+
+
+        /// <summary>
+        /// 修改时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "修改时间", ColumnName = "update_time")]
+        public DateTime? UpdateTime { get; set; }
+
+
+        /// <summary>
+        /// 锁定标记,0未锁定,9已锁定
+        /// </summary>
+        [SugarColumn(ColumnDescription = "锁定标记,0未锁定,9已锁定", Length = 1, ColumnName = "lock_flag")]
+        public string LockFlag { get; set; }
+
+
+        /// <summary>
+        /// 删除标记,0未删除,1已删除
+        /// </summary>
+        [SugarColumn(ColumnDescription = "删除标记,0未删除,1已删除", Length = 1, ColumnName = "del_flag")]
+        public string DelFlag { get; set; }
+
+
+        /// <summary>
+        /// 微信登录openId
+        /// </summary>
+        [SugarColumn(ColumnDescription = "微信登录openId", Length = 32, ColumnName = "wx_openid")]
+        public string WxOpenid { get; set; }
+
+
+        /// <summary>
+        /// 小程序openId
+        /// </summary>
+        [SugarColumn(ColumnDescription = "小程序openId", Length = 32, ColumnName = "mini_openid")]
+        public string MiniOpenid { get; set; }
+
+
+        /// <summary>
+        /// QQ openId
+        /// </summary>
+        [SugarColumn(ColumnDescription = "QQ openId", Length = 32, ColumnName = "qq_openid")]
+        public string QqOpenid { get; set; }
+
+
+        /// <summary>
+        /// 码云标识
+        /// </summary>
+        [SugarColumn(ColumnDescription = "码云标识", Length = 100, ColumnName = "gitee_login")]
+        public string GiteeLogin { get; set; }
+
+
+        /// <summary>
+        /// 开源中国标识
+        /// </summary>
+        [SugarColumn(ColumnDescription = "开源中国标识", Length = 100, ColumnName = "osc_id")]
+        public string OscId { get; set; }
+
+
+        /// <summary>
+        /// 角色id集合
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        [ExcelIgnore]
+        public long[] RoleIds { get; set; }
+
+
+        /// <summary>
+        /// 岗位集合
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        [ExcelIgnore]
+        public int[] PostIds { get; set; }
+
+
+        public bool IsAdmin()
+        {
+            return IsAdmin(UserId);
+        }
+        public static bool IsAdmin(long userId)
+        {
+            return 1 == userId;
+        }
+    }
+}

+ 26 - 0
Model/Base/SysUserPost.cs

@@ -0,0 +1,26 @@
+namespace Model
+{
+    /// <summary>
+    /// 用户与岗位关联表 sys_user_post
+    /// </summary>
+    [SugarTable("sys_user_post", "用户与岗位关联表")]
+    [Tenant("0")]
+    public class SysUserPost
+    {
+        /// <summary>
+        /// 用户ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户ID", ColumnName = "user_id")]
+        public long UserId { get; set; }
+
+
+        /// <summary>
+        /// 岗位ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "岗位ID", ColumnName = "post_id")]
+        public long PostId { get; set; }
+
+
+
+    }
+}

+ 26 - 0
Model/Base/SysUserRole.cs

@@ -0,0 +1,26 @@
+namespace Model
+{
+    /// <summary>
+    /// 用户角色表 sys_user_role
+    /// </summary>
+    [SugarTable("sys_user_role", "用户角色表")]
+    [Tenant("0")]
+    public class SysUserRole
+    {
+        /// <summary>
+        /// 用户ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "用户ID", ColumnName = "user_id")]
+        public long UserId { get; set; }
+
+
+        /// <summary>
+        /// 角色ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "角色ID", ColumnName = "role_id")]
+        public long RoleId { get; set; }
+
+
+
+    }
+}

+ 44 - 0
Model/Base/TokenModel.cs

@@ -0,0 +1,44 @@
+namespace Model.Base
+{
+    public class TokenModel
+    {
+        public long UserId { get; set; }
+        public long DeptId { get; set; }
+        public string Username { get; set; }
+        /// <summary>
+        /// 角色集合
+        /// </summary>
+        public List<string> RoleIds { get; set; }
+        /// <summary>
+        /// 角色集合(数据权限过滤使用)
+        /// </summary>
+        public List<Roles> Roles { get; set; }
+        /// <summary>
+        /// Jwt过期时间
+        /// </summary>
+        public DateTime ExpireTime { get; set; }
+        /// <summary>
+        /// 权限集合
+        /// </summary>
+        //public List<string> Permissions { get; set; } = new List<string>();
+        public TokenModel()
+        {
+        }
+
+        public TokenModel(TokenModel info, List<Roles> roles)
+        {
+            UserId = info.UserId;
+            Username = info.Username;
+            DeptId = info.DeptId;
+            Roles = roles;
+            RoleIds = roles.Select(f => f.RoleKey).ToList();
+        }
+    }
+
+    public class Roles
+    {
+        public long RoleId { get; set; }
+        public string RoleKey { get; set; }
+        public int DataScope { get; set; }
+    }
+}

+ 115 - 0
Model/Base/UserConstants.cs

@@ -0,0 +1,115 @@
+namespace Model
+{
+    public class UserConstants
+    {
+        /// <summary>
+        /// 平台内系统用户的唯一标志
+        /// </summary>
+        public static string SYS_USER = "SYS_USER";
+
+        /// <summary>
+        /// 正常状态
+        /// </summary>
+        public static string NORMAL = "0";
+
+        /// <summary>
+        /// 异常状态
+        /// </summary>
+        public static string EXCEPTION = "1";
+
+        /// <summary>
+        /// 用户封禁状态
+        /// </summary>
+        public static string USER_DISABLE = "1";
+
+        /// <summary>
+        /// 角色封禁状态
+        /// </summary>
+        public static string ROLE_DISABLE = "1";
+
+        /// <summary>
+        /// 部门正常状态
+        /// </summary>
+        public static int DEPT_NORMAL = 0;
+
+        /// <summary>
+        /// 部门停用状态
+        /// </summary>
+        public static string DEPT_DISABLE = "1";
+
+        /// <summary>
+        /// 字典正常状态
+        /// </summary>
+        public static string DICT_NORMAL = "0";
+
+        /// <summary>
+        /// 是否为系统默认(是)
+        /// </summary>
+        public static string YES = "Y";
+
+        /// <summary>
+        /// 是否菜单外链(是)
+        /// </summary>
+        public static string YES_FRAME = "1";
+
+        /// <summary>
+        /// 是否菜单外链(否)
+        /// </summary>
+        public static string NO_FRAME = "0";
+
+        /// <summary>
+        /// 菜单类型(目录)
+        /// </summary>
+        public static string TYPE_DIR = "M";
+
+        /// <summary>
+        /// 菜单类型(菜单)
+        /// </summary>
+        public static string TYPE_MENU = "C";
+
+        /// <summary>
+        /// 菜单类型(按钮)
+        /// </summary>
+        public static string TYPE_BUTTON = "F";
+
+        ///// <summary>
+        ///// 菜单类型(链接)
+        ///// </summary>
+        //public static string TYPE_LINK = "L";
+
+        /// <summary>
+        /// Layout组件标识
+        /// </summary>
+        public static string LAYOUT = "Layout";
+
+        /// <summary>
+        /// ParentView组件标识
+        /// </summary>
+        public static string PARENT_VIEW = "ParentView";
+
+        /// <summary>
+        /// InnerLink组件标识
+        /// </summary>
+        public static string INNER_LINK = "InnerLink";
+
+        /// <summary>
+        /// 校验返回结果码
+        /// </summary>
+        public static string UNIQUE = "0";
+        public static string NOT_UNIQUE = "1";
+
+        /// <summary>
+        /// http请求
+        /// </summary>
+        public static string HTTP = "http://";
+
+        /// <summary>
+        /// https请求
+        /// </summary>
+        public static string HTTPS = "https://";
+        /// <summary>
+        /// www主域
+        /// </summary>
+        public static string WWW = "www.";
+    }
+}

BIN
Model/Database/.DS_Store


+ 82 - 0
Model/Database/ApiGroup.cs

@@ -0,0 +1,82 @@
+namespace Model
+{
+    /// <summary>
+    /// api接口分组 api_group
+    /// </summary>
+    [SugarTable("api_group", "api接口分组")]
+    [Tenant("0")]
+    public class ApiGroup
+    {
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "名称", Length = 50, ColumnName = "group_name")]
+        public string GroupName { get; set; }
+
+
+        /// <summary>
+        /// 说明
+        /// </summary>
+        [SugarColumn(ColumnDescription = "说明", Length = 200, ColumnName = "group_remark")]
+        public string GroupRemark { get; set; }
+
+
+        /// <summary>
+        /// 版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "版本号", Length = 20, ColumnName = "group_version")]
+        public string GroupVersion { get; set; }
+
+
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "ID", ColumnName = "id")]
+        public int Id { get; set; }
+
+
+        /// <summary>
+        /// 状态
+        /// </summary>
+        [SugarColumn(ColumnDescription = "状态", ColumnName = "status")]
+        public int Status { get; set; }
+
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        [SugarColumn(ColumnDescription = "排序", ColumnName = "sort")]
+        public int Sort { get; set; }
+
+
+        /// <summary>
+        /// 版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "版本号", ColumnName = "version")]
+        public int Version { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "创建时间", ColumnName = "create_date")]
+        public DateTime? CreateDate { get; set; }
+
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间", ColumnName = "update_date")]
+        public DateTime? UpdateDate { get; set; }
+
+
+        /// <summary>
+        /// 类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "类型", ColumnName = "group_kind")]
+        public int GroupKind { get; set; }
+
+
+
+    }
+}

BIN
Model/Dto/.DS_Store


+ 31 - 0
Model/Dto/Base/LoginBodyDto.cs

@@ -0,0 +1,31 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Dto
+{
+    public class LoginBodyDto
+    {
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        [Required(ErrorMessage = "用户名不能为空")]
+        public string Username { get; set; }
+
+        /// <summary>
+        /// 用户密码
+        /// </summary>
+        [Required(ErrorMessage = "密码不能为空")]
+        public string Password { get; set; }
+
+        /// <summary>
+        /// 验证码
+        /// </summary>
+        public string? Code { get; set; }
+
+        /// <summary>
+        /// 唯一标识
+        /// </summary>
+        public string? Uuid { get; set; } = "";
+        public string? LoginIP { get; set; }
+        public string? ClientId { get; set; }
+    }
+}

+ 20 - 0
Model/Dto/Base/LoginDto.cs

@@ -0,0 +1,20 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Dto
+{
+    public class LoginDto
+    {
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        [Required(ErrorMessage = "用户名不能为空")]
+        public string AdminName { get; set; }
+
+        /// <summary>
+        /// 登录密码
+        /// </summary>
+        [Required(ErrorMessage = "登录密码不能为空")]
+        public string Password { get; set; }
+    }
+}

+ 97 - 0
Model/Dto/Base/MenuDto.cs

@@ -0,0 +1,97 @@
+using System.ComponentModel.DataAnnotations;
+using MiniExcelLibs.Attributes;
+
+namespace Dto
+{
+    public class MenuDto
+    {
+        //{"parentId":0,"menuName":"aaa","icon":"documentation","menuType":"M","orderNum":999,"visible":0,"status":0,"path":"aaa"}
+        [Required(ErrorMessage = "菜单id不能为空")]
+        [ExcelColumn(Name = "菜单id")]
+        public int MenuId { get; set; }
+        [ExcelColumn(Name = "菜单名")]
+        public string MenuName { get; set; }
+        /// <summary>
+        /// 父菜单ID
+        /// </summary>
+        public long? ParentId { get; set; }
+
+        /// <summary>
+        /// 显示顺序
+        /// </summary>
+        public int SortOrder { get; set; }
+
+        /// <summary>
+        /// 路由地址
+        /// </summary>
+        [ExcelColumn(Name = "路由地址")] 
+        public string Path { get; set; } = "#";
+
+        /// <summary>
+        /// 组件路径
+        /// </summary>
+        [ExcelColumn(Name = "组件地址")]
+        public string Component { get; set; }
+
+        /// <summary>
+        /// 是否缓存(1缓存 0不缓存)
+        /// </summary>
+        [Required(ErrorMessage = "是否缓存不能为空")]
+        [ExcelColumn(Name = "是否缓存")]
+        public int IsCache { get; set; }
+        /// <summary>
+        /// 是否外链 1、是 0、否
+        /// </summary>
+        public int IsFrame { get; set; }
+
+        /// <summary>
+        /// 类型(M目录 C菜单 F按钮 L链接)
+        /// </summary>
+        [Required(ErrorMessage = "菜单类型不能为空")]
+        public string MenuType { get; set; }
+
+        /// <summary>
+        /// 显示状态(0显示 1隐藏)
+        /// </summary>
+        [Required(ErrorMessage = "显示状态不能为空")]
+        public string Visible { get; set; }
+
+        /// <summary>
+        /// 菜单状态(0正常 1停用)
+        /// </summary>
+        [Required(ErrorMessage = "菜单状态不能为空")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 权限字符串
+        /// </summary>
+        [ExcelColumn(Name = "权限字符串")]
+        public string Perms { get; set; }
+
+        /// <summary>
+        /// 菜单图标
+        /// </summary>
+        public string Icon { get; set; } = string.Empty;
+        /// <summary>
+        /// 翻译key
+        /// </summary>
+        public string MenuNameKey { get; set; }
+        public List<MenuDto> Children { get; set; } = new List<MenuDto>();
+    }
+
+    public class MenuQueryDto
+    {
+        public string MenuName { get; set; }
+        public string Visible { get; set; }
+        public string DelFlag { get; set; }
+        public string MenuTypeIds { get; set; } = string.Empty;
+        public int? ParentId { get; set; }
+        public string[] MenuTypeIdArr
+        {
+            get
+            {
+                return MenuTypeIds?.Split(',', StringSplitOptions.RemoveEmptyEntries);
+            }
+        }
+    }
+}

+ 25 - 0
Model/Dto/Base/PhoneLoginDto.cs

@@ -0,0 +1,25 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Dto
+{
+    public class PhoneLoginDto
+    {
+        /// <summary>
+        /// 验证码
+        /// </summary>
+        public string Code { get; set; }
+
+        /// <summary>
+        /// 唯一标识
+        /// </summary>
+        public string Uuid { get; set; } = "";
+        public string LoginIP { get; set; }
+        [Required(ErrorMessage = "手机号不能为空")]
+        public string PhoneNum { get; set; }
+        /// <summary>
+        /// 手机短信验证码
+        /// </summary>
+        //[Required(ErrorMessage = "短信验证码不能为空")]
+        public string PhoneCode { get; set; }
+    }
+}

+ 35 - 0
Model/Dto/Base/RegisterDto.cs

@@ -0,0 +1,35 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Dto
+{
+    public class RegisterDto
+    {
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        [Required(ErrorMessage = "用户名不能为空")]
+        public string Username { get; set; }
+
+        /// <summary>
+        /// 用户密码
+        /// </summary>
+        [Required(ErrorMessage = "密码不能为空")]
+        public string Password { get; set; }
+        [Required(ErrorMessage = "确认密码不能为空")]
+        public string ConfirmPassword { get; set; }
+        /// <summary>
+        /// 验证码
+        /// </summary>
+        public string Code { get; set; }
+
+        /// <summary>
+        /// 唯一标识
+        /// </summary>
+        public string Uuid { get; set; } = "";
+        /// <summary>
+        /// 头像
+        /// </summary>
+        public string Photo { get; set; }
+        public string UserIP { get; set; }
+    }
+}

+ 28 - 0
Model/Dto/Base/RoleMenuExportDto.cs

@@ -0,0 +1,28 @@
+using Enums;
+using MiniExcelLibs.Attributes;
+
+namespace Dto
+{
+    public class RoleMenuExportDto
+    {
+        /// <summary>
+        /// 一级目录名
+        /// </summary>
+        [ExcelColumn(Name = "菜单", Width = 50)]
+        public string MenuName { get; set; }
+        //[ExcelColumn(Name = "菜单名", Width = 20)]
+        //public string MenuName1 { get; set; }
+        //[ExcelColumn(Name = "权限按钮", Width = 20)]
+        //public string MenuName2 { get; set; }
+        [ExcelColumn(Name = "路径", Width = 20)]
+        public string Path { get; set; }
+        [ExcelColumn(Name = "组件名", Width = 20)]
+        public string Component { get; set; }
+        [ExcelColumn(Name = "权限字符", Width = 20)]
+        public string Perms { get; set; }
+        [ExcelColumn(Name = "菜单类型")]
+        public MenuType MenuType { get; set; }
+        [ExcelColumn(Name = "菜单状态")]
+        public MenuStatus Status { get; set; }
+    }
+}

+ 28 - 0
Model/Dto/Base/RoleUserDto.cs

@@ -0,0 +1,28 @@
+using System.ComponentModel.DataAnnotations;
+using Model.Base;
+
+namespace Dto
+{
+    public class RoleUserQueryDto : PagerInfo
+    {
+        public long RoleId { get; set; }
+
+        public string UserName { get; set; }
+    }
+
+    public class RoleUsersCreateDto
+    {
+        /// <summary>
+        /// 角色id
+        /// </summary>
+        [Display(Name = "角色id")]
+        [Required(ErrorMessage = "roleId 不能为空")]
+        public long RoleId { get; set; }
+
+        /// <summary>
+        /// 用户编码 [1,2,3,4] 
+        /// </summary>
+        [Display(Name = "用户编码 [1,2,3,4]")]
+        public List<long> UserIds { get; set; }
+    }
+}

+ 33 - 0
Model/Dto/Base/SysDeptDto.cs

@@ -0,0 +1,33 @@
+using Model;
+using Model.Base;
+
+namespace Dto
+{
+    public class SysDeptQueryDto : PagerInfo
+    {
+        public int? DelFlag { get; set; }
+        public string DeptName { get; set; }
+    }
+    public class SysDeptDto : SysBase
+    {
+        public long DeptId { get; set; }
+
+        public long ParentId { get; set; }
+
+        public string Ancestors { get; set; }
+
+        public string DeptName { get; set; }
+
+        public int OrderNum { get; set; }
+
+        public string Leader { get; set; }
+
+        public string Phone { get; set; }
+
+        public string Email { get; set; }
+
+        public int Status { get; set; }
+
+        public int DelFlag { get; set; }
+    }
+}

+ 27 - 0
Model/Dto/Base/SysRoleDto.cs

@@ -0,0 +1,27 @@
+using Model;
+
+namespace Dto
+{
+    public class SysRoleDto : SysBase
+    {
+        public long RoleId { get; set; }
+        /// <summary>
+        /// 要添加的菜单集合
+        /// </summary>
+        public List<long> MenuIds { get; set; } = new List<long>();
+        public string RoleName { get; set; }
+        public string RoleKey { get; set; }
+        public int RoleSort { get; set; }
+        public int Status { get; set; }
+        public int DataScope { get; set; }
+        public int[] DeptIds { get; set; }
+        public string CreateBy { get; set; }
+        /// <summary>
+        /// 减少菜单集合
+        /// </summary>
+        public List<long> DelMenuIds { get; set; } = new List<long>();
+        public bool MenuCheckStrictly { get; set; }
+        public bool DeptCheckStrictly { get; set; }
+
+    }
+}

+ 57 - 0
Model/Dto/Base/SysUserDto.cs

@@ -0,0 +1,57 @@
+namespace Dto
+{
+    public class SysUserDto
+    {
+        public long UserId { get; set; }
+        public string UserName { get; set; }
+        public string NickName { get; set; }
+        public string Email { get; set; }
+        public string Remark { get; set; }
+        public string Phonenumber { get; set; }
+        /// <summary>
+        /// 用户性别(0男 1女 2未知)
+        /// </summary>
+        public int Sex { get; set; }
+        public string Password { get; set; }
+
+        /// <summary>
+        /// 帐号状态(0正常 1停用)
+        /// </summary>
+        public int Status { get; set; }
+
+        /// <summary>
+        /// 最后登录IP
+        /// </summary>
+        public string LoginIP { get; set; }
+
+        /// <summary>
+        /// 部门Id
+        /// </summary>
+        public long DeptId { get; set; }
+        /// <summary>
+        /// 角色id集合
+        /// </summary>
+        public long[] RoleIds { get; set; }
+        /// <summary>
+        /// 岗位集合
+        /// </summary>
+        public int[] PostIds { get; set; }
+    }
+
+    public class SysUserQueryDto
+    {
+        public long? UserId { get; set; }
+        public string UserName { get; set; }
+        public string NickName { get; set; }
+        public string Email { get; set; }
+        public string Remark { get; set; }
+        public string Phonenumber { get; set; }
+        /// <summary>
+        /// 用户性别(0男 1女 2未知)
+        /// </summary>
+        public int Sex { get; set; }
+        public DateTime? BeginTime { get; set; }
+        public DateTime? EndTime { get; set; }
+        public long DeptId { get; set; }
+    }
+}

+ 63 - 0
Model/Enums/BusinessType.cs

@@ -0,0 +1,63 @@
+namespace Enums
+{
+    /// <summary>
+    /// 业务操作类型 0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据
+    /// </summary>
+    public enum BusinessType
+    {
+        /// <summary>
+        /// 其它
+        /// </summary>
+        OTHER = 0,
+
+        /// <summary>
+        /// 新增
+        /// </summary>
+        INSERT = 1,
+
+        /// <summary>
+        /// 修改
+        /// </summary>
+        UPDATE = 2,
+
+        /// <summary>
+        /// 删除
+        /// </summary>
+        DELETE = 3,
+
+        /// <summary>
+        /// 授权
+        /// </summary>
+        GRANT = 4,
+
+        /// <summary>
+        /// 导出
+        /// </summary>
+        EXPORT = 5,
+
+        /// <summary>
+        /// 导入
+        /// </summary>
+        IMPORT = 6,
+
+        /// <summary>
+        /// 强退
+        /// </summary>
+        FORCE = 7,
+
+        /// <summary>
+        /// 生成代码
+        /// </summary>
+        GENCODE = 8,
+
+        /// <summary>
+        /// 清空数据
+        /// </summary>
+        CLEAN = 9,
+
+        /// <summary>
+        /// 下载
+        /// </summary>
+        DOWNLOAD = 10,
+    }
+}

+ 15 - 0
Model/Enums/MenuStatus.cs

@@ -0,0 +1,15 @@
+using System.ComponentModel;
+
+namespace Enums
+{
+    /// <summary>
+    /// 菜单状态(0正常 1停用)
+    /// </summary>
+    public enum MenuStatus
+    {
+        [Description("正常")]
+        正常 = 0,
+        [Description("停用")]
+        停用 = 1,
+    }
+}

+ 19 - 0
Model/Enums/MenuType.cs

@@ -0,0 +1,19 @@
+using System.ComponentModel;
+
+namespace Enums
+{
+    /// <summary>
+    /// M目录 C菜单 F按钮 L链接
+    /// </summary>
+    public enum MenuType
+    {
+        [Description("目录")]
+        M,
+        [Description("菜单")]
+        C,
+        [Description("按钮")]
+        F,
+        [Description("链接")]
+        L
+    }
+}

+ 4 - 0
Model/Enums/ProteryConstant.cs

@@ -0,0 +1,4 @@
+public enum ProteryConstant
+{
+    NOTNULL = 0
+}

+ 40 - 0
Model/Enums/StoreType.cs

@@ -0,0 +1,40 @@
+using System.ComponentModel;
+
+namespace Enums
+{
+    /// <summary>
+    /// 文件存储位置
+    /// </summary>
+    public enum StoreType
+    {
+        /// <summary>
+        /// 本地
+        /// </summary>
+        [Description("本地")]
+        LOCAL = 1,
+
+        /// <summary>
+        /// 阿里云
+        /// </summary>
+        [Description("阿里云")]
+        ALIYUN = 2,
+
+        /// <summary>
+        /// 腾讯云
+        /// </summary>
+        [Description("腾讯云")]
+        TENCENT = 3,
+
+        /// <summary>
+        /// 七牛
+        /// </summary>
+        [Description("七牛云")]
+        QINIU = 4,
+
+        /// <summary>
+        /// 远程
+        /// </summary>
+        [Description("远程")]
+        REMOTE = 5
+    }
+}

+ 48 - 0
Model/Exception/CustomException.cs

@@ -0,0 +1,48 @@
+using System;
+
+namespace Infrastructure
+{
+    public class CustomException : Exception
+    {
+        public int Code { get; set; }
+        /// <summary>
+        /// 前端提示语
+        /// </summary>
+        public string Msg { get; set; }
+        /// <summary>
+        /// 记录到日志的详细内容
+        /// </summary>
+        public string LogMsg { get; set; }
+        /// <summary>
+        /// 是否通知
+        /// </summary>
+        public bool Notice { get; set; } = true;
+
+        public CustomException(string msg) : base(msg)
+        {
+        }
+        public CustomException(int code, string msg) : base(msg)
+        {
+            Code = code;
+            Msg = msg;
+        }
+
+        public CustomException(ResultCode resultCode, string msg, bool notice = true) : base(msg)
+        {
+            Code = (int)resultCode;
+            Notice = notice;
+        }
+
+        /// <summary>
+        /// 自定义异常
+        /// </summary>
+        /// <param name="resultCode"></param>
+        /// <param name="msg"></param>
+        /// <param name="errorMsg">用于记录详细日志到输出介质</param>
+        public CustomException(ResultCode resultCode, string msg, object errorMsg) : base(msg)
+        {
+            Code = (int)resultCode;
+            LogMsg = errorMsg.ToString();
+        }
+    }
+}

+ 46 - 0
Model/Exception/ResultCode.cs

@@ -0,0 +1,46 @@
+using System.ComponentModel;
+
+namespace Infrastructure
+{
+    public enum ResultCode
+    {
+        [Description("success")]
+        SUCCESS = 1,
+
+        [Description("没有更多数据")]
+        NO_DATA = 210,
+
+        [Description("参数错误")]
+        PARAM_ERROR = 101,
+
+        [Description("验证码错误")]
+        CAPTCHA_ERROR = 103,
+
+        [Description("登录错误")]
+        LOGIN_ERROR = 105,
+
+        [Description("操作失败")]
+        FAIL = 100,
+
+        [Description("服务端出错啦")]
+        GLOBAL_ERROR = 500,
+
+        [Description("自定义异常")]
+        CUSTOM_ERROR = 110,
+
+        [Description("非法请求")]
+        INVALID_REQUEST = 116,
+
+        [Description("授权失败")]
+        OAUTH_FAIL = 201,
+
+        [Description("未授权")]
+        DENY = 401,
+
+        [Description("授权访问失败")]
+        FORBIDDEN = 403,
+
+        [Description("Bad Request")]
+        BAD_REQUEST = 400
+    }
+}

BIN
Model/Vo/.DS_Store


+ 33 - 0
Model/Vo/Base/OAuthVo.cs

@@ -0,0 +1,33 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Vo
+{
+    public class OAuthVo
+    {
+        /// <summary>
+        /// 授权token
+        /// </summary>
+        public string access_token { get; set; }
+
+        /// <summary>
+        /// 刷新token
+        /// </summary>
+        public string refresh_token { get; set; }
+
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        public string username { get; set; }
+
+        /// <summary>
+        /// 过期时间
+        /// </summary>
+        public DateTime exp { get; set; }
+
+        /// <summary>
+        /// 用户id
+        /// </summary>
+        public long user_id { get; set; }
+    }
+}

+ 74 - 0
Model/Vo/Base/RouterVo.cs

@@ -0,0 +1,74 @@
+using Model;
+using Newtonsoft.Json;
+
+namespace Vo
+{
+    /// <summary>
+    /// 路由展示
+    /// </summary>
+    public class RouterVo
+    {
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public bool AlwaysShow { get; set; }
+        private string component;
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public bool Hidden { get; set; }
+        public string Name { get; set; }
+        public string Path { get; set; }
+        public string Redirect { get; set; }
+        public Meta Meta { get; set; }
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public List<RouterVo> Children { get; set; }
+        public string Component { get => component; set => component = value; }
+    }
+
+    public class Meta
+    {
+        /// <summary>
+        /// 设置该路由在侧边栏和面包屑中展示的名字
+        /// </summary>
+        public string Title { get; set; }
+        /// <summary>
+        /// 设置该路由的图标,对应路径src/assets/icons/svg
+        /// </summary>
+        public string Icon { get; set; }
+        /// <summary>
+        /// 设置为true,则不会被 <keep-alive>缓存
+        /// </summary>
+        public bool NoCache { get; set; }
+        public string TitleKey { get; set; } = string.Empty;
+        public string Link { get; set; } = string.Empty;
+        public int IsNew { get; set; }
+
+        public Meta(string title, string icon)
+        {
+            Title = title;
+            Icon = icon;
+        }
+        public Meta(string title, string icon, string path)
+        {
+            Title = title;
+            Icon = icon;
+            Link = path;
+        }
+        public Meta(string title, string icon, bool noCache, string titleKey, string path, DateTime addTime)
+        {
+            Title = title;
+            Icon = icon;
+            NoCache = noCache;
+            TitleKey = titleKey;
+            if (!string.IsNullOrEmpty(path) && (path.StartsWith(UserConstants.HTTP) || path.StartsWith(UserConstants.HTTPS)))
+            {
+                Link = path;
+            }
+            if (addTime != DateTime.MinValue)
+            {
+                TimeSpan ts = DateTime.Now - addTime;
+                if (ts.Days < 7)
+                {
+                    IsNew = 1;
+                }
+            }
+        }
+    }
+}

+ 58 - 0
Model/Vo/Base/TreeSelectVo.cs

@@ -0,0 +1,58 @@
+using Model;
+using Newtonsoft.Json;
+
+namespace Vo
+{
+    /// <summary>
+    /// Treeselect树结构实体类
+    /// </summary>
+    public class TreeSelectVo
+    {
+        /// <summary>
+        /// 节点Id
+        /// </summary>
+        public long Id { get; set; }
+        /// <summary>
+        /// 节点名称
+        /// </summary>
+        public string Label { get; set; }
+        public string Status { get; set; }
+        public string MenuType { get; set; }
+
+        public TreeSelectVo() { }
+
+        public TreeSelectVo(SysMenu menu)
+        {
+            Id = menu.MenuId;
+            Label = menu.Name;
+            Status = menu.Visible;
+            MenuType = menu.MenuType;
+
+            List<TreeSelectVo> child = new List<TreeSelectVo>();
+            foreach (var item in menu.Children)
+            {
+                child.Add(new TreeSelectVo(item));
+            }
+
+            Children = child;
+        }
+
+        public TreeSelectVo(SysDept dept)
+        {
+            Id = dept.DeptId;
+            Label = dept.Name;
+
+            //menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); java写法
+            List<TreeSelectVo> child = new List<TreeSelectVo>();
+            foreach (var item in dept.children)
+            {
+                child.Add(new TreeSelectVo(item));
+            }
+
+            Children = child;
+        }
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public List<TreeSelectVo> Children { get; set; }
+    }
+}

Some files were not shown because too many files changed in this diff