瀏覽代碼

项目初始化

lcl 10 月之前
當前提交
a5dd136e7a
共有 73 個文件被更改,包括 6613 次插入0 次删除
  1. 二進制
      .DS_Store
  2. 4 0
      .gitignore
  3. 53 0
      .vscode/launch.json
  4. 42 0
      .vscode/tasks.json
  5. 100 0
      App/App.cs
  6. 25 0
      App/InternalApp.cs
  7. 32 0
      Attribute/AppServiceAttribute.cs
  8. 35 0
      Attribute/LogAttribute.cs
  9. 93 0
      Base/AppSettings.cs
  10. 59 0
      Base/GlobalConstant.cs
  11. 150 0
      Common/Cache/CacheHelper.cs
  12. 17 0
      Common/Cache/RedisServer.cs
  13. 1173 0
      Common/Function.cs
  14. 37 0
      Common/JsonConverterUtil.cs
  15. 167 0
      Common/JwtUtil.cs
  16. 48 0
      Common/StringConverter.cs
  17. 142 0
      Common/Tools.cs
  18. 134 0
      Common/dbconn.cs
  19. 145 0
      Constant/Helper/DateTimeHelper.cs
  20. 84 0
      Constant/HttpStatus.cs
  21. 二進制
      Controllers/.DS_Store
  22. 234 0
      Controllers/Base/BaseController.cs
  23. 72 0
      Extensions/AppServiceExtensions.cs
  24. 42 0
      Extensions/EntityExtension.cs
  25. 446 0
      Extensions/Extension.Convert.cs
  26. 86 0
      Extensions/Extension.Enum.cs
  27. 18 0
      Extensions/Extension.Exception.cs
  28. 110 0
      Extensions/Extension.Linq.cs
  29. 44 0
      Extensions/Extension.Validate.cs
  30. 245 0
      Extensions/HttpContextExtension.cs
  31. 24 0
      Extensions/IPRateExtension.cs
  32. 35 0
      Extensions/RequestLimitExtension.cs
  33. 232 0
      Extensions/StringExtension.cs
  34. 79 0
      Filters/AuthorizationFilter.cs
  35. 247 0
      Filters/GlobalActionMonitor.cs
  36. 76 0
      Filters/VerifyAttribute.cs
  37. 3 0
      GlobalUsing.cs
  38. 37 0
      LkbShopServer.csproj
  39. 155 0
      Middleware/GlobalExceptionMiddleware.cs
  40. 二進制
      Model/.DS_Store
  41. 127 0
      Model/Base/ApiResult.cs
  42. 128 0
      Model/Base/OptionsSetting.cs
  43. 46 0
      Model/Base/PagedInfo.cs
  44. 37 0
      Model/Base/PagerInfo.cs
  45. 44 0
      Model/Base/TokenModel.cs
  46. 115 0
      Model/Base/UserConstants.cs
  47. 二進制
      Model/Database/.DS_Store
  48. 二進制
      Model/Dto/.DS_Store
  49. 63 0
      Model/Enums/BusinessType.cs
  50. 15 0
      Model/Enums/MenuStatus.cs
  51. 19 0
      Model/Enums/MenuType.cs
  52. 4 0
      Model/Enums/ProteryConstant.cs
  53. 40 0
      Model/Enums/StoreType.cs
  54. 48 0
      Model/Exception/CustomException.cs
  55. 46 0
      Model/Exception/ResultCode.cs
  56. 二進制
      Model/Vo/.DS_Store
  57. 103 0
      Program.cs
  58. 41 0
      Properties/launchSettings.json
  59. 351 0
      Repository/BaseRepository.cs
  60. 80 0
      Repository/IBaseRepository.cs
  61. 二進制
      Services/.DS_Store
  62. 二進制
      Services/Base/.DS_Store
  63. 12 0
      Services/Base/BaseService.cs
  64. 81 0
      Services/Base/CacheService.cs
  65. 二進制
      Services/Base/IService/.DS_Store
  66. 12 0
      Services/Base/IService/IBaseService.cs
  67. 75 0
      SqlSugar/DataPermi.cs
  68. 43 0
      SqlSugar/DataPermiSevice.cs
  69. 32 0
      SqlSugar/InitTable.cs
  70. 66 0
      SqlSugar/SqlSugarCache.cs
  71. 192 0
      SqlSugar/SqlsugarSetup.cs
  72. 38 0
      appsettings.Development.json
  73. 30 0
      iprate.json

二進制
.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/LkbShopServer.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/LkbShopServer.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}"
+        }
+    ]
+}

+ 42 - 0
.vscode/tasks.json

@@ -0,0 +1,42 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/LkbShopServer.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "publish",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "publish",
+                "${workspaceFolder}/LkbShopServer.csproj",
+                "/property:GenerateFullPaths=true",
+                "/consoleloggerparameters:NoSummary"
+            ],
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "watch",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "watch",
+                "run",
+                "${workspaceFolder}/LkbShopServer.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;
+    }
+}

二進制
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);
+        }
+    }
+}

+ 72 - 0
Extensions/AppServiceExtensions.cs

@@ -0,0 +1,72 @@
+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>();
+        }
+
+        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;
+        //}
+    }
+}

+ 245 - 0
Extensions/HttpContextExtension.cs

@@ -0,0 +1,245 @@
+using System.Security.Claims;
+using System.Text;
+using System.Text.RegularExpressions;
+using Base;
+using Extensions;
+using IPTools.Core;
+using Model.Base;
+using UAParser;
+using Util;
+
+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)
+        {
+            TokenModel loginUser = JwtUtil.GetLoginUser(context);
+            return loginUser.UserId;
+            // 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;
+        }
+    }
+}

+ 79 - 0
Filters/AuthorizationFilter.cs

@@ -0,0 +1,79 @@
+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);
+                Dictionary<string, string> dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(content);
+                string queryString = "";
+                foreach(string key in dic.Keys)
+                {
+                    queryString += key + "=" + dic[key] + "&";
+                }
+                request.QueryString = new QueryString("?" + queryString.TrimEnd('&'));
+            }
+            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 = str.Trim('"');
+            str = Encoding.UTF8.GetString(Convert.FromBase64String(str));
+            return Dbconn.AesDecrypt(str, Base.GlobalConstant.ApiKey, Base.GlobalConstant.ApiIv);
+        }
+    }
+}

+ 247 - 0
Filters/GlobalActionMonitor.cs

@@ -0,0 +1,247 @@
+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);
+                if(!string.IsNullOrEmpty(content))
+                {
+                    string jsonString = "";
+                    string[] dataList = content.Split('&');
+                    foreach(string sub in dataList)
+                    {
+                        string[] item = sub.Split('=');
+                        jsonString += "\"" + item[0] + "\":\"" + item[1] + "\",";
+                    }
+                    content = "{" + jsonString.TrimEnd(',') + "}";
+                }
+            }
+            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
+            {
+                if(context.ActionDescriptor.Parameters.Count > 0)
+                {
+                    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
LkbShopServer.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>

+ 155 - 0
Middleware/GlobalExceptionMiddleware.cs

@@ -0,0 +1,155 @@
+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
+            {
+                // 设置允许跨域的域名、方法等
+                context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
+                context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+                context.Response.Headers.Add("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization");
+
+                // 如果请求方法是预检请求(OPTIONS),则直接返回成功状态码
+                if (context.Request.Method == "OPTIONS")
+                {
+                    context.Response.StatusCode = 200;
+                    await context.Response.WriteAsync("OK");
+                    return;
+                }
+
+                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;
+        }
+    }
+}

二進制
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;
+        }
+    }
+}

+ 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.";
+    }
+}

二進制
Model/Database/.DS_Store


二進制
Model/Dto/.DS_Store


+ 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
+    }
+}

二進制
Model/Vo/.DS_Store


+ 103 - 0
Program.cs

@@ -0,0 +1,103 @@
+using System.Text.Json;
+using Base;
+using Common;
+using Extensions;
+using Filters;
+using Infrastructure;
+using Infrastructure.Model;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Middleware;
+using Services;
+using Util;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+
+builder.Services.AddControllers();
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+//注入HttpContextAccessor
+builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
+//IPRatelimit
+// builder.Services.AddIPRate(builder.Configuration);
+builder.Services.AddSession();
+builder.Services.AddHttpContextAccessor();
+builder.Services.Configure<KestrelServerOptions>(x => x.AllowSynchronousIO = true).Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);
+
+//绑定整个对象到Model上
+builder.Services.Configure<OptionsSetting>(builder.Configuration);
+// builder.Configuration.AddJsonFile("iprate.json");
+//配置文件
+builder.Services.AddSingleton(new AppSettings(builder.Configuration));
+//请求大小限制
+builder.Services.AddRequestLimit(builder.Configuration);
+//注册REDIS 服务
+var openRedis = builder.Configuration["RedisServer:open"];
+if (openRedis == "1")
+{
+    RedisServer.Initalize();
+}
+//配置文件
+builder.Services.AddSingleton(new AppSettings(builder.Configuration));
+//app服务注册
+builder.Services.AddAppService();
+
+builder.Services.AddMvc(options =>
+{
+    options.Filters.Add(typeof(GlobalActionMonitor));//全局注册
+    options.Filters.Add(typeof(AuthorizationFilter));
+})
+.AddJsonOptions(options =>
+{
+    //options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString;
+    options.JsonSerializerOptions.WriteIndented = true;
+    options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeConverter());
+    options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeNullConverter());
+    options.JsonSerializerOptions.Converters.Add(new StringConverter());
+    //PropertyNamingPolicy属性用于前端传过来的属性的格式策略,目前内置的仅有一种策略CamelCase
+    options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+    //options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;//属性可以忽略大小写格式,开启后性能会降低
+});
+
+var app = builder.Build();
+InternalApp.ServiceProvider = app.Services;
+InternalApp.Configuration = builder.Configuration;
+InternalApp.WebHostEnvironment = app.Environment;
+//初始化db
+builder.Services.AddDb(app.Environment);
+var workId = builder.Configuration["workId"].ParseToInt();
+if (app.Environment.IsDevelopment())
+{
+    workId += 1;
+}
+SnowFlakeSingle.WorkId = workId;
+
+//使用全局异常中间件
+app.UseMiddleware<GlobalExceptionMiddleware>();
+
+app.Use(next => new RequestDelegate(
+    async context =>
+    {
+        context.Request.EnableBuffering();
+        await next(context);
+    }
+));
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+}
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
+app.MapControllerRoute(
+    name: "default",
+    pattern: "{controller=Home}/{action=Index}/{id?}");
+
+app.MapControllers();
+
+app.Run();

+ 41 - 0
Properties/launchSettings.json

@@ -0,0 +1,41 @@
+{
+  "$schema": "https://json.schemastore.org/launchsettings.json",
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://192.168.1.151:49293",
+      "sslPort": 44376
+    }
+  },
+  "profiles": {
+    "http": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "launchBrowser": true,
+      "launchUrl": "swagger",
+      "applicationUrl": "http://192.168.1.151:5296",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "https": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "launchBrowser": true,
+      "launchUrl": "swagger",
+      "applicationUrl": "http://192.168.1.151:5296",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "launchUrl": "swagger",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 351 - 0
Repository/BaseRepository.cs

@@ -0,0 +1,351 @@
+using Model.Base;
+using Extensions;
+using SqlSugar.IOC;
+using System.Data;
+using System.Linq.Expressions;
+using System.Reflection;
+using Mapster;
+
+namespace Repository
+{
+    /// <summary>
+    /// 数据仓库类
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class BaseRepository<T> : SimpleClient<T> where T : class, new()
+    {
+        public ITenant itenant = null;//多租户事务
+        public BaseRepository(ISqlSugarClient context = null) : base(context)
+        {
+            //通过特性拿到ConfigId
+            var configId = typeof(T).GetCustomAttribute<TenantAttribute>()?.configId;
+            if (configId != null)
+            {
+                Context = DbScoped.SugarScope.GetConnectionScope(configId);//根据类传入的ConfigId自动选择
+            }
+            else
+            {
+                Context = context ?? DbScoped.SugarScope.GetConnectionScope(0);//没有默认db0
+            }
+            //Context = DbScoped.SugarScope.GetConnectionScopeWithAttr<T>();
+            itenant = DbScoped.SugarScope;//设置租户接口
+        }
+
+        #region add
+
+        /// <summary>
+        /// 插入实体
+        /// </summary>
+        /// <param name="t"></param>
+        /// <returns></returns>
+        public int Add(T t, bool ignoreNull = true)
+        {
+            return Context.Insertable(t).IgnoreColumns(ignoreNullColumn: ignoreNull).ExecuteCommand();
+        }
+
+        public int Insert(List<T> t)
+        {
+            return InsertRange(t) ? 1 : 0;
+        }
+        public int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true)
+        {
+            return Context.Insertable(parm).InsertColumns(iClumns).IgnoreColumns(ignoreNullColumn: ignoreNull).ExecuteCommand();
+        }
+        public IInsertable<T> Insertable(T t)
+        {
+            return Context.Insertable(t);
+        }
+        #endregion add
+
+        #region update
+        //public IUpdateable<T> Updateable(T entity)
+        //{
+        //    return Context.Updateable(entity);
+        //}
+
+        /// <summary>
+        /// 实体根据主键更新
+        /// </summary>
+        /// <param name="entity"></param>
+        /// <param name="ignoreNullColumns"></param>
+        /// <returns></returns>
+        public int Update(T entity, bool ignoreNullColumns = false, object data = null)
+        {
+            return Context.Updateable(entity).IgnoreColumns(ignoreNullColumns)
+                .EnableDiffLogEventIF(data.IsNotEmpty(), data).ExecuteCommand();
+        }
+
+        /// <summary>
+        /// 实体根据主键更新指定字段
+        /// return Update(new SysUser(){ Status = 1 }, t => new { t.NickName, }, true);
+        /// </summary>
+        /// <param name="entity"></param>
+        /// <param name="expression"></param>
+        /// <param name="ignoreAllNull"></param>
+        /// <returns></returns>
+        public int Update(T entity, Expression<Func<T, object>> expression, bool ignoreAllNull = false)
+        {
+            return Context.Updateable(entity).UpdateColumns(expression).IgnoreColumns(ignoreAllNull).ExecuteCommand();
+        }
+
+        /// <summary>
+        /// 根据指定条件更新指定列 eg:Update(new SysUser(){ Status = 1 }, it => new { it.Status }, f => f.Userid == 1));
+        /// 只更新Status列,条件是包含
+        /// </summary>
+        /// <param name="entity">实体类</param>
+        /// <param name="expression">要更新列的表达式</param>
+        /// <param name="where">where表达式</param>
+        /// <returns></returns>
+        public int Update(T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where)
+        {
+            return Context.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommand();
+        }
+
+        /// <summary>
+        /// 更新指定列 eg:Update(w => w.NoticeId == model.NoticeId, it => new SysNotice(){ Update_time = DateTime.Now, Title = "通知标题" });
+        /// </summary>
+        /// <param name="where"></param>
+        /// <param name="columns"></param>
+        /// <returns></returns>
+        public int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns)
+        {
+            return Context.Updateable<T>().SetColumns(columns).Where(where).RemoveDataCache().ExecuteCommand();
+        }
+        #endregion update
+
+        public DbResult<bool> UseTran(Action action)
+        {
+            try
+            {
+                var result = Context.Ado.UseTran(() => action());
+                return result;
+            }
+            catch (Exception ex)
+            {
+                Context.Ado.RollbackTran();
+                Console.WriteLine(ex.Message);
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="action">增删改查方法</param>
+        /// <returns></returns>
+        public DbResult<bool> UseTran(SqlSugarClient client, Action action)
+        {
+            try
+            {
+                var result = client.AsTenant().UseTran(() => action());
+                return result;
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine("事务异常" + ex.Message);
+                client.AsTenant().RollbackTran();
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// 使用事务
+        /// </summary>
+        /// <param name="action"></param>
+        /// <returns></returns>
+        public bool UseTran2(Action action)
+        {
+            var result = Context.Ado.UseTran(() => action());
+            return result.IsSuccess;
+        }
+
+        #region delete
+        public IDeleteable<T> Deleteable()
+        {
+            return Context.Deleteable<T>();
+        }
+
+        /// <summary>
+        /// 批量删除
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public int Delete(object[] obj, string title = "")
+        {
+            return Context.Deleteable<T>().In(obj).EnableDiffLogEventIF(title.IsNotEmpty(), title).ExecuteCommand();
+        }
+        public int Delete(object id, string title = "")
+        {
+            return Context.Deleteable<T>(id).EnableDiffLogEventIF(title.IsNotEmpty(), title).ExecuteCommand();
+        }
+        public int DeleteTable()
+        {
+            return Context.Deleteable<T>().ExecuteCommand();
+        }
+        public bool Truncate()
+        {
+            return Context.DbMaintenance.TruncateTable<T>();
+        }
+        #endregion delete
+
+        #region query
+
+        public bool Any(Expression<Func<T, bool>> expression)
+        {
+            return Context.Queryable<T>().Where(expression).Any();
+        }
+
+        public ISugarQueryable<T> Queryable()
+        {
+            return Context.Queryable<T>();
+        }
+
+        public List<T> SqlQueryToList(string sql, object obj = null)
+        {
+            return Context.Ado.SqlQuery<T>(sql, obj);
+        }
+
+        /// <summary>
+        /// 根据主值查询单条数据
+        /// </summary>
+        /// <param name="pkValue">主键值</param>
+        /// <returns>泛型实体</returns>
+        public T GetId(object pkValue)
+        {
+            return Context.Queryable<T>().InSingle(pkValue);
+        }
+        /// <summary>
+        /// 根据条件查询分页数据
+        /// </summary>
+        /// <param name="where"></param>
+        /// <param name="parm"></param>
+        /// <returns></returns>
+        public PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm)
+        {
+            var source = Context.Queryable<T>().Where(where);
+
+            return source.ToPage(parm);
+        }
+
+        /// <summary>
+        /// 分页获取数据
+        /// </summary>
+        /// <param name="where">条件表达式</param>
+        /// <param name="parm"></param>
+        /// <param name="order"></param>
+        /// <param name="orderEnum"></param>
+        /// <returns></returns>
+        public PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm, Expression<Func<T, object>> order, OrderByType orderEnum = OrderByType.Asc)
+        {
+            var source = Context
+                .Queryable<T>()
+                .Where(where)
+                .OrderByIF(orderEnum == OrderByType.Asc, order, OrderByType.Asc)
+                .OrderByIF(orderEnum == OrderByType.Desc, order, OrderByType.Desc);
+
+            return source.ToPage(parm);
+        }
+
+        public PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm, Expression<Func<T, object>> order, string orderByType)
+        {
+            return GetPages(where, parm, order, orderByType == "desc" ? OrderByType.Desc : OrderByType.Asc);
+        }
+
+        /// <summary>
+        /// 查询所有数据(无分页,请慎用)
+        /// </summary>
+        /// <returns></returns>
+        public List<T> GetAll(bool useCache = false, int cacheSecond = 3600)
+        {
+            return Context.Queryable<T>().WithCacheIF(useCache, cacheSecond).ToList();
+        }
+
+        #endregion query
+
+        /// <summary>
+        /// 此方法不带output返回值
+        /// var list = new List<SugarParameter>();
+        /// list.Add(new SugarParameter(ParaName, ParaValue)); input
+        /// </summary>
+        /// <param name="procedureName"></param>
+        /// <param name="parameters"></param>
+        /// <returns></returns>
+        public DataTable UseStoredProcedureToDataTable(string procedureName, List<SugarParameter> parameters)
+        {
+            return Context.Ado.UseStoredProcedure().GetDataTable(procedureName, parameters);
+        }
+
+        /// <summary>
+        /// 带output返回值
+        /// var list = new List<SugarParameter>();
+        /// list.Add(new SugarParameter(ParaName, ParaValue, true));  output
+        /// list.Add(new SugarParameter(ParaName, ParaValue)); input
+        /// </summary>
+        /// <param name="procedureName"></param>
+        /// <param name="parameters"></param>
+        /// <returns></returns>
+        public (DataTable, List<SugarParameter>) UseStoredProcedureToTuple(string procedureName, List<SugarParameter> parameters)
+        {
+            var result = (Context.Ado.UseStoredProcedure().GetDataTable(procedureName, parameters), parameters);
+            return result;
+        }
+    }
+
+    /// <summary>
+    /// 分页查询扩展
+    /// </summary>
+    public static class QueryableExtension
+    {
+        /// <summary>
+        /// 读取列表
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source">查询表单式</param>
+        /// <param name="parm">分页参数</param>
+        /// <returns></returns>
+        public static PagedInfo<T> ToPage<T>(this ISugarQueryable<T> source, PagerInfo parm)
+        {
+            var page = new PagedInfo<T>();
+            var total = 0;
+            page.PageSize = parm.PageSize;
+            page.PageIndex = parm.PageNum;
+            if (parm.Sort.IsNotEmpty())
+            {
+                source.OrderByPropertyName(parm.Sort, parm.SortType.Contains("desc") ? OrderByType.Desc : OrderByType.Asc);
+            }
+            page.Result = source
+                //.OrderByIF(parm.Sort.IsNotEmpty(), $"{parm.Sort.ToSqlFilter()} {(!string.IsNullOrWhiteSpace(parm.SortType) && parm.SortType.Contains("desc") ? "desc" : "asc")}")
+                .ToPageList(parm.PageNum, parm.PageSize, ref total);
+            page.TotalNum = total;
+            return page;
+        }
+
+        /// <summary>
+        /// 转指定实体类Dto
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T2"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="parm"></param>
+        /// <returns></returns>
+        public static PagedInfo<T2> ToPage<T, T2>(this ISugarQueryable<T> source, PagerInfo parm)
+        {
+            var page = new PagedInfo<T2>();
+            var total = 0;
+            page.PageSize = parm.PageSize;
+            page.PageIndex = parm.PageNum;
+            if (parm.Sort.IsNotEmpty())
+            {
+                source.OrderByPropertyName(parm.Sort, parm.SortType.Contains("desc") ? OrderByType.Desc : OrderByType.Asc);
+            }
+            var result = source
+                //.OrderByIF(parm.Sort.IsNotEmpty(), $"{parm.Sort.ToSqlFilter()} {(!string.IsNullOrWhiteSpace(parm.SortType) && parm.SortType.Contains("desc") ? "desc" : "asc")}")
+                .ToPageList(parm.PageNum, parm.PageSize, ref total);
+
+            page.TotalNum = total;
+            page.Result = result.Adapt<List<T2>>();
+            return page;
+        }
+    }
+}

+ 80 - 0
Repository/IBaseRepository.cs

@@ -0,0 +1,80 @@
+using System.Data;
+using System.Linq.Expressions;
+using Model.Base;
+
+namespace Repository
+{
+    public interface IBaseRepository<T> : ISimpleClient<T> where T : class, new()
+    {
+        #region add
+        int Add(T t, bool ignoreNull = true);
+
+        int Insert(List<T> t);
+        int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true);
+
+        IInsertable<T> Insertable(T t);
+        #endregion add
+
+        #region update
+        int Update(T entity, bool ignoreNullColumns = false, object data = null);
+
+        /// <summary>
+        /// 只更新表达式的值
+        /// </summary>
+        /// <param name="entity"></param>
+        /// <param name="expression"></param>
+        /// <returns></returns>
+        int Update(T entity, Expression<Func<T, object>> expression, bool ignoreAllNull = false);
+
+        int Update(T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where);
+
+        int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns);
+
+        #endregion update
+        DbResult<bool> UseTran(Action action);
+
+        DbResult<bool> UseTran(SqlSugarClient client, Action action);
+
+        bool UseTran2(Action action);
+
+        #region delete
+        IDeleteable<T> Deleteable();
+        int Delete(object[] obj, string title = "");
+        int Delete(object id, string title = "");
+        int DeleteTable();
+        bool Truncate();
+
+        #endregion delete
+
+        #region query
+        /// <summary>
+        /// 根据条件查询分页数据
+        /// </summary>
+        /// <param name="where"></param>
+        /// <param name="parm"></param>
+        /// <returns></returns>
+        PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm);
+
+        PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm, Expression<Func<T, object>> order, OrderByType orderEnum = OrderByType.Asc);
+        PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm, Expression<Func<T, object>> order, string orderByType);
+
+        bool Any(Expression<Func<T, bool>> expression);
+
+        ISugarQueryable<T> Queryable();
+        List<T> GetAll(bool useCache = false, int cacheSecond = 3600);
+
+        List<T> SqlQueryToList(string sql, object obj = null);
+
+        T GetId(object pkValue);
+
+        #endregion query
+
+        #region Procedure
+
+        DataTable UseStoredProcedureToDataTable(string procedureName, List<SugarParameter> parameters);
+
+        (DataTable, List<SugarParameter>) UseStoredProcedureToTuple(string procedureName, List<SugarParameter> parameters);
+
+        #endregion Procedure
+    }
+}

二進制
Services/.DS_Store


二進制
Services/Base/.DS_Store


+ 12 - 0
Services/Base/BaseService.cs

@@ -0,0 +1,12 @@
+using Repository;
+
+namespace Service
+{
+    /// <summary>
+    /// 基础服务定义
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class BaseService<T> : BaseRepository<T> where T : class, new()
+    {
+    }
+}

+ 81 - 0
Services/Base/CacheService.cs

@@ -0,0 +1,81 @@
+using Common;
+
+namespace Services
+{
+    public class CacheService
+    {
+        private readonly static string CK_verifyScan = "verifyScan_";
+        private readonly static string CK_phoneSmsCode = "phone_sms_code_";
+        #region 用户权限 缓存
+        public static List<string> GetUserPerms(string key)
+        {
+            return (List<string>)CacheHelper.GetCache(key);
+            //return RedisServer.Cache.Get<List<string>>(key).ToList();
+        }
+
+        public static void SetUserPerms(string key, object data)
+        {
+            CacheHelper.SetCache(key, data);
+            //RedisServer.Cache.Set(key, data);
+        }
+        public static void RemoveUserPerms(string key)
+        {
+            CacheHelper.Remove(key);
+            //RedisServer.Cache.Del(key);
+        }
+        #endregion
+
+        public static object SetScanLogin(string key, Dictionary<string, object> val)
+        {
+            var ck = CK_verifyScan + key;
+            
+            return CacheHelper.SetCache(ck,val , 1);
+        }
+        public static object GetScanLogin(string key)
+        {
+            var ck = CK_verifyScan + key;
+            return CacheHelper.Get(ck);
+        }
+        public static void RemoveScanLogin(string key)
+        {
+            var ck = CK_verifyScan + key;
+            CacheHelper.Remove(ck);
+        }
+
+        public static void SetLockUser(string key, long val, int time)
+        {
+            var CK = "lock_user_" + key;
+
+            CacheHelper.SetCache(CK, val, time);
+        }
+
+        public static long GetLockUser(string key)
+        {
+            var CK = "lock_user_" + key;
+
+            if (CacheHelper.Get(CK) is long t)
+            {
+                return t;
+            }
+            return 0;
+        }
+
+        public static object SetPhoneCode(string key, string val)
+        {
+            var ck = CK_phoneSmsCode + key;
+
+            return CacheHelper.SetCache(ck, val, 10);
+        }
+        public static bool CheckPhoneCode(string key, string val)
+        {
+            var ck = CK_phoneSmsCode + key;
+            var save_code = CacheHelper.Get(ck);
+
+            if (save_code != null && save_code.Equals(val))
+            {
+                return true;
+            }
+            return false;
+        }
+    }
+}

二進制
Services/Base/IService/.DS_Store


+ 12 - 0
Services/Base/IService/IBaseService.cs

@@ -0,0 +1,12 @@
+using Repository;
+
+namespace Services
+{
+    /// <summary>
+    /// 基础服务定义
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public interface IBaseService<T> : IBaseRepository<T> where T : class, new()
+    {
+    }
+}

+ 75 - 0
SqlSugar/DataPermi.cs

@@ -0,0 +1,75 @@
+using Base;
+using Infrastructure;
+using Model;
+using SqlSugar.IOC;
+using Util;
+
+namespace SqlSugar
+{
+    public enum DataPermiEnum
+    {
+        None = 0,
+        /// <summary>
+        /// 全部数据权限
+        /// </summary>
+        All = 1,
+        /// <summary>
+        /// 仅本人数据权限
+        /// </summary>
+        SELF = 5,
+        /// <summary>
+        /// 部门数据权限
+        /// </summary>
+        DEPT = 3,
+        /// <summary>
+        /// 自定数据权限
+        /// </summary>
+        CUSTOM = 2,
+        /// <summary>
+        /// 部门及以下数据权限
+        /// </summary>
+        DEPT_CHILD = 4
+    }
+    /// <summary>
+    /// 数据权限
+    /// </summary>
+    public class DataPermi
+    {
+        /// <summary>
+        /// 数据过滤
+        /// </summary>
+        /// <param name="configId">多库id</param>
+        public static void FilterData(int configId)
+        {
+            //获取当前用户的信息
+            var user = JwtUtil.GetLoginUser(App.HttpContext);
+            if (user == null) return;
+            //管理员不过滤
+            // if (user.RoleIds.Any(f => f.Equals(GlobalConstant.AdminRole))) return;
+            var db = DbScoped.SugarScope.GetConnectionScope(configId);
+            // var expUser = Expressionable.Create<SysUser>();
+            // var expRole = Expressionable.Create<SysRole>();
+
+            // foreach (var role in user.Roles.OrderBy(f => f.DataScope))
+            // {
+            //     var dataScope = (DataPermiEnum)role.DataScope;
+            //     if (DataPermiEnum.All.Equals(dataScope))//所有权限
+            //     {
+            //         break;
+            //     }
+            //     else if (DataPermiEnum.DEPT.Equals(dataScope))//本部门数据
+            //     {
+            //         expUser.Or(it => it.DeptId == user.DeptId);
+            //     }
+            //     else if (DataPermiEnum.SELF.Equals(dataScope))//仅本人数据
+            //     {
+            //         expUser.Or(it => it.UserId == user.UserId);
+            //         expRole.Or(it => user.RoleIds.Contains(it.RoleKey));
+            //     }
+            // }
+
+            // db.QueryFilter.AddTableFilter(expUser.ToExpression());
+            // db.QueryFilter.AddTableFilter(expRole.ToExpression());
+        }
+    }
+}

+ 43 - 0
SqlSugar/DataPermiSevice.cs

@@ -0,0 +1,43 @@
+using Infrastructure;
+using SqlSugar.IOC;
+using Util;
+
+namespace ServiceCore
+{
+    public class DataPermiSevice
+    {
+        /// <summary>
+        /// 你的业务库数据权限过滤方法
+        /// </summary>
+        /// <param name="configId"></param>
+        public static void FilterData(int configId)
+        {
+            //获取当前用户的信息
+            var user = JwtUtil.GetLoginUser(App.HttpContext);
+            if (user == null) return;
+            var db = DbScoped.SugarScope.GetConnectionScope(configId);
+
+            foreach (var role in user.Roles.OrderBy(f => f.DataScope))
+            {
+                var dataScope = (DataPermiEnum)role.DataScope;
+                if (DataPermiEnum.All.Equals(dataScope))//所有权限
+                {
+                    break;
+                }
+                else if (DataPermiEnum.CUSTOM.Equals(dataScope))//自定数据权限
+                {
+                }
+                else if (DataPermiEnum.DEPT.Equals(dataScope))//本部门数据
+                {
+                }
+                else if (DataPermiEnum.DEPT_CHILD.Equals(dataScope))//本部门及以下数据
+                {
+
+                }
+                else if (DataPermiEnum.SELF.Equals(dataScope))//仅本人数据
+                {
+                }
+            }
+        }
+    }
+}

+ 32 - 0
SqlSugar/InitTable.cs

@@ -0,0 +1,32 @@
+using Model;
+using SqlSugar.IOC;
+
+namespace SqlSugar
+{
+    /// <summary>
+    /// 初始化表
+    /// </summary>
+    public class InitTable
+    {
+        /// <summary>
+        /// 创建db、表
+        /// </summary>
+        public static void InitDb()
+        {
+            var db = DbScoped.SugarScope;
+            //建库:如果不存在创建数据库存在不会重复创建 
+            db.DbMaintenance.CreateDatabase();// 注意 :Oracle和个别国产库需不支持该方法,需要手动建库 
+
+            //var baseType = typeof(SysBase);
+            //var entityes = AssemblyUtils.GetAllTypes().Where(p => !p.IsAbstract && p != baseType && p.GetCustomAttribute<SugarTable>() != null).ToArray();
+            //db.CodeFirst.InitTables(entityes);
+
+            //27个表,建议先使用下面方法初始化表,方便排查问题
+            // db.CodeFirst.InitTables(typeof(SysUser));
+            // db.CodeFirst.InitTables(typeof(SysRole));
+            // db.CodeFirst.InitTables(typeof(ApiGroup));
+            // db.CodeFirst.InitTables(typeof(Article));
+            // db.CodeFirst.InitTables(typeof(ArticleCategory));
+        }
+    }
+}

+ 66 - 0
SqlSugar/SqlSugarCache.cs

@@ -0,0 +1,66 @@
+using Common;
+
+namespace SqlSugar
+{
+    public class SqlSugarCache : ICacheService
+    {
+        public void Add<V>(string key, V value)
+        {
+            //RedisServer.Cache.Set(key, value, 3600 + RedisHelper.RandomExpired(5, 30));
+            CacheHelper.SetCache(key, value);
+        }
+
+        public void Add<V>(string key, V value, int cacheDurationInSeconds)
+        {
+            //RedisServer.Cache.Set(key, value, cacheDurationInSeconds);
+            CacheHelper.SetCaches(key, value, cacheDurationInSeconds);
+        }
+
+        public bool ContainsKey<V>(string key)
+        {
+            //return RedisServer.Cache.Exists(key);
+            return CacheHelper.Exists(key);
+        }
+
+        public V Get<V>(string key)
+        {
+            //return RedisServer.Cache.Get<V>(key);
+            return (V)CacheHelper.Get(key);
+        }
+
+        public IEnumerable<string> GetAllKey<V>()
+        {
+            //return RedisServer.Cache.Keys("*");
+            return CacheHelper.GetCacheKeys();
+        }
+
+        public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
+        {
+            if (ContainsKey<V>(cacheKey))
+            {
+                var result = Get<V>(cacheKey);
+                if (result == null)
+                {
+                    return create();
+                }
+                else
+                {
+                    return result;
+                }
+            }
+            else
+            {
+                var restul = create();
+
+                Add(cacheKey, restul, cacheDurationInSeconds);
+                return restul;
+            }
+        }
+
+        public void Remove<V>(string key)
+        {
+            //RedisServer.Cache.Del(key);
+            CacheHelper.Remove(key);
+        }
+    }
+}

+ 192 - 0
SqlSugar/SqlsugarSetup.cs

@@ -0,0 +1,192 @@
+using Base;
+using Infrastructure;
+using Infrastructure.Model;
+using SqlSugar.IOC;
+
+namespace SqlSugar
+{
+    public static class SqlsugarSetup
+    {
+        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+        /// <summary>
+        /// 初始化db
+        /// </summary>
+        /// <param name="services"></param>
+        /// <param name="environment"></param>
+        public static void AddDb(this IServiceCollection services, IWebHostEnvironment environment)
+        {
+            var options = App.OptionsSetting;
+            List<DbConfigs> dbConfigs = options.DbConfigs;
+
+            var iocList = new List<IocConfig>();
+            foreach (var item in dbConfigs)
+            {
+                iocList.Add(new IocConfig()
+                {
+                    ConfigId = item.ConfigId,
+                    ConnectionString = item.Conn,
+                    DbType = (IocDbType)item.DbType,
+                    IsAutoCloseConnection = item.IsAutoCloseConnection
+                });
+            }
+            SugarIocServices.AddSqlSugar(iocList);
+            ICacheService cache = new SqlSugarCache();
+            SugarIocServices.ConfigurationSugar(db =>
+            {
+                var u = App.User;
+                if (u != null)
+                {
+                    DataPermi.FilterData(0);
+                    //ConfigId = 1的数据权限过滤
+                    //DataPermiSevice.FilterData(1);
+                }
+
+                iocList.ForEach(iocConfig =>
+                {
+                    SetSugarAop(db, iocConfig, cache);
+                });
+            });
+
+            if (options.InitDb && environment.IsDevelopment())
+            {
+                InitTable.InitDb();
+            }
+        }
+
+        /// <summary>
+        /// 数据库Aop设置
+        /// </summary>
+        /// <param name="db"></param>
+        /// <param name="iocConfig"></param>
+        /// <param name="cache"></param>
+        private static void SetSugarAop(SqlSugarClient db, IocConfig iocConfig, ICacheService cache)
+        {
+            var config = db.GetConnectionScope(iocConfig.ConfigId).CurrentConnectionConfig;
+            var showDbLog = AppSettings.Get<bool>("ShowDbLog");
+            string configId = config.ConfigId;
+            db.GetConnectionScope(configId).Aop.OnLogExecuting = (sql, pars) =>
+            {
+                if (showDbLog)
+                {
+                    string log = $"【db{configId} SQL】{UtilMethods.GetSqlString(config.DbType, sql, pars)}\n";
+                    if (sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
+                    {
+                        logger.Info(log);
+                    }
+                    else if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
+                    {
+                        logger.Warn(log);
+                    }
+                    else if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("TRUNCATE", StringComparison.OrdinalIgnoreCase))
+                    {
+                        logger.Error(log);
+                    }
+                    else
+                    {
+                        log = $"【db{configId} SQL语句】dbo.{sql} {string.Join(", ", pars.Select(x => x.ParameterName + " = " + GetParsValue(x)))};\n";
+                        logger.Info(log);
+                    }
+                }
+            };
+            db.GetConnectionScope(configId).Aop.OnError = (ex) =>
+            {
+                //var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
+
+                string sql = "【错误SQL】" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n";
+                logger.Error(ex, $"{sql}\r\n{ex.Message}\r\n{ex.StackTrace}");
+            };
+            db.GetConnectionScope(configId).Aop.DataExecuting = (oldValue, entiyInfo) =>
+            {
+            };
+            //差异日志功能
+            db.GetConnectionScope(configId).Aop.OnDiffLogEvent = it =>
+            {
+                //操作前记录  包含: 字段描述 列名 值 表名 表描述
+                var editBeforeData = it.BeforeData;//插入Before为null,之前还没进库
+                //操作后记录   包含: 字段描述 列名 值  表名 表描述
+                var editAfterData = it.AfterData;
+                var sql = it.Sql;
+                var parameter = it.Parameters;
+                var data = it.BusinessData;//这边会显示你传进来的对象
+                var time = it.Time;
+                var diffType = it.DiffType;//enum insert 、update and delete  
+                string name = App.UserName;
+
+                foreach (var item in editBeforeData)
+                {
+                    var pars = db.Utilities.SerializeObject(item.Columns.ToDictionary(it => it.ColumnName, it => it.Value));
+
+                    // SqlDiffLog log = new()
+                    // {
+                    //     BeforeData = pars,
+                    //     BusinessData = data?.ToString(),
+                    //     DiffType = diffType.ToString(),
+                    //     Sql = sql,
+                    //     TableName = item.TableName,
+                    //     UserName = name,
+                    //     AddTime = DateTime.Now,
+                    //     ConfigId = configId
+                    // };
+                    // if (editAfterData != null)
+                    // {
+                    //     var afterData = editAfterData?.First(x => x.TableName == item.TableName);
+                    //     var afterPars = db.Utilities.SerializeObject(afterData?.Columns.ToDictionary(it => it.ColumnName, it => it.Value));
+                    //     log.AfterData = afterPars;
+                    // }
+                    logger.WithProperty("title", data).Info(pars);
+                    // db.GetConnectionScope(0)
+                    // .Insertable(log)
+                    // .ExecuteReturnSnowflakeId();
+                }
+            };
+            db.GetConnectionScope(configId).CurrentConnectionConfig.MoreSettings = new ConnMoreSettings()
+            {
+                IsAutoRemoveDataCache = true
+            };
+            db.GetConnectionScope(configId).CurrentConnectionConfig.ConfigureExternalServices = new ConfigureExternalServices()
+            {
+                DataInfoCacheService = cache,
+                EntityService = (c, p) =>
+                {
+                    if (p.IsPrimarykey == true)//主键不能为null
+                    {
+                        p.IsNullable = false;
+                    }
+                    else if (p.ExtendedAttribute?.ToString() == "0")
+                    {
+                        p.IsNullable = false;
+                    }
+                    else//则否默认为null
+                    {
+                        p.IsNullable = true;
+                    }
+                }
+            };
+            db.GetConnectionScope(configId).Aop.OnLogExecuted = (sql, pars) =>
+            {
+                var sqlExecutionTime = AppSettings.Get<int>("sqlExecutionTime");
+                if (db.Ado.SqlExecutionTime.TotalSeconds > sqlExecutionTime)
+                {
+                    //代码CS文件名
+                    var fileName = db.Ado.SqlStackTrace.FirstFileName;
+                    //代码行数
+                    var fileLine = db.Ado.SqlStackTrace.FirstLine;
+                    //方法名
+                    var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
+                    var logInfo = $"Sql执行超时,用时{db.Ado.SqlExecutionTime.TotalSeconds}秒【{sql}】,fileName={fileName},line={fileLine},methodName={FirstMethodName}";
+                    logger.Warn(logInfo);
+                }
+            };
+        }
+
+        private static object GetParsValue(SugarParameter x)
+        {
+            if (x.DbType == System.Data.DbType.String || x.DbType == System.Data.DbType.DateTime || x.DbType == System.Data.DbType.String)
+            {
+                return "'" + x.Value + "'";
+            }
+            return x.Value;
+        }
+    }
+}

+ 38 - 0
appsettings.Development.json

@@ -0,0 +1,38 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.AspNetCore": "Warning"
+    }
+  },
+  "Upload": {
+    "uploadUrl": "http://localhost:8888", //本地存储资源访问路径
+    "localSavePath": "", //本地上传默认文件存储目录 wwwroot
+    "maxSize": 15, //上传文件大小限制 15M
+    "notAllowedExt": [ ".bat", ".exe", ".jar", ".js" ],
+    "requestLimitSize": 50//请求body大小限制
+  },
+  "dbConfigs": [
+    {
+      "Conn": "server=47.109.31.237;port=3306;user=KxsConfigServer;password=brVSLE47Y32WUy3R;database=KxsConfigServer;charset=utf8;",
+      "DbType": 0,
+      "ConfigId": "0",
+      "IsAutoCloseConnection": true
+    }
+  ],
+  "RedisServer": {
+    "open": 1,
+    "Cache": "47.109.31.237:6379,password=klm@redis,defaultDatabase=13,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
+    "Session": "47.109.31.237:6379,password=klm@redis,defaultDatabase=13,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
+  },
+  "JwtSettings": {
+    "Issuer": "ym", //即token的签发者。
+    "Audience": "ym", //指该token是服务于哪个群体的(群体范围)
+    "SecretKey": "123456789012345678901234567890",
+    "Expire": 1440, //jwt登录过期时间(分)
+    "RefreshTokenTime": 30, //分钟
+    "TokenType": "Bearer"
+  },
+  "InitDb": false, //是否初始化db
+  "ApiKey": "*ga34|^7" //webapi解密的key
+}

+ 30 - 0
iprate.json

@@ -0,0 +1,30 @@
+{
+  //接口请求限制
+  "IpRateLimiting": {
+    "EnableEndpointRateLimiting": true,
+    "StackBlockedRequests": false,
+    "RealIpHeader": "X-Real-IP",
+    "ClientIdHeader": "X-ClientId",
+    "HttpStatusCode": 429,
+    "EndpointWhitelist": [ "post:/system/dict/data/types", "*:/msghub/negotiate", "*:/LogOut", "*:/common/uploadfile", "*:/VerifyScan" ],
+    "QuotaExceededResponse": {
+      "Content": "{{\"code\":429,\"msg\":\"访问过于频繁,请稍后重试\"}}",
+      "ContentType": "application/json",
+      "StatusCode": 429
+    },
+    //通用规则,api规则,结尾一定要带*
+    "GeneralRules": [
+      {
+        "Endpoint": "*:/captchaImage",
+        //时间段,格式:{数字}{单位};可使用单位:s, m, h, d
+        "Period": "3s",
+        "Limit": 5
+      },
+      {
+        "Endpoint": "((post)|(put)):*",
+        "Period": "3s",
+        "Limit": 1
+      }
+    ]
+  }
+}