Forráskód Böngészése

😎重命名文件夹(Util => Utils)

zuohuaijun 1 éve
szülő
commit
5b7e8a2f3d

+ 0 - 0
Admin.NET/Admin.NET.Core/Util/AdminResultProvider.cs → Admin.NET/Admin.NET.Core/Utils/AdminResultProvider.cs


+ 74 - 74
Admin.NET/Admin.NET.Core/Util/BaseFilter.cs → Admin.NET/Admin.NET.Core/Utils/BaseFilter.cs

@@ -1,75 +1,75 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// 模糊查询条件
-/// </summary>
-public class Search
-{
-    /// <summary>
-    /// 字段名称集合
-    /// </summary>
-    public List<string> Fields { get; set; }
-
-    /// <summary>
-    /// 关键字
-    /// </summary>
-    public string? Keyword { get; set; }
-}
-
-/// <summary>
-/// 筛选过滤条件
-/// </summary>
-public class Filter
-{
-    /// <summary>
-    /// 过滤条件
-    /// </summary>
-    public FilterLogicEnum? Logic { get; set; }
-
-    /// <summary>
-    /// 筛选过滤条件子项
-    /// </summary>
-    public IEnumerable<Filter>? Filters { get; set; }
-
-    /// <summary>
-    /// 字段名称
-    /// </summary>
-    public string? Field { get; set; }
-
-    /// <summary>
-    /// 逻辑运算符
-    /// </summary>
-    public FilterOperatorEnum? Operator { get; set; }
-
-    /// <summary>
-    /// 字段值
-    /// </summary>
-    public object? Value { get; set; }
-}
-
-/// <summary>
-/// 过滤条件基类
-/// </summary>
-public abstract class BaseFilter
-{
-    /// <summary>
-    /// 模糊查询条件
-    /// </summary>
-    public Search? Search { get; set; }
-
-    /// <summary>
-    /// 模糊查询关键字
-    /// </summary>
-    public string? Keyword { get; set; }
-
-    /// <summary>
-    /// 筛选过滤条件
-    /// </summary>
-    public Filter? Filter { get; set; }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 模糊查询条件
+/// </summary>
+public class Search
+{
+    /// <summary>
+    /// 字段名称集合
+    /// </summary>
+    public List<string> Fields { get; set; }
+
+    /// <summary>
+    /// 关键字
+    /// </summary>
+    public string? Keyword { get; set; }
+}
+
+/// <summary>
+/// 筛选过滤条件
+/// </summary>
+public class Filter
+{
+    /// <summary>
+    /// 过滤条件
+    /// </summary>
+    public FilterLogicEnum? Logic { get; set; }
+
+    /// <summary>
+    /// 筛选过滤条件子项
+    /// </summary>
+    public IEnumerable<Filter>? Filters { get; set; }
+
+    /// <summary>
+    /// 字段名称
+    /// </summary>
+    public string? Field { get; set; }
+
+    /// <summary>
+    /// 逻辑运算符
+    /// </summary>
+    public FilterOperatorEnum? Operator { get; set; }
+
+    /// <summary>
+    /// 字段值
+    /// </summary>
+    public object? Value { get; set; }
+}
+
+/// <summary>
+/// 过滤条件基类
+/// </summary>
+public abstract class BaseFilter
+{
+    /// <summary>
+    /// 模糊查询条件
+    /// </summary>
+    public Search? Search { get; set; }
+
+    /// <summary>
+    /// 模糊查询关键字
+    /// </summary>
+    public string? Keyword { get; set; }
+
+    /// <summary>
+    /// 筛选过滤条件
+    /// </summary>
+    public Filter? Filter { get; set; }
 }

+ 0 - 0
Admin.NET/Admin.NET.Core/Util/BaseIdInput.cs → Admin.NET/Admin.NET.Core/Utils/BaseIdInput.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/BasePageInput.cs → Admin.NET/Admin.NET.Core/Utils/BasePageInput.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/ChinaDateTimeConverter.cs → Admin.NET/Admin.NET.Core/Utils/ChinaDateTimeConverter.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/CodeGenUtil.cs → Admin.NET/Admin.NET.Core/Utils/CodeGenUtil.cs


+ 454 - 454
Admin.NET/Admin.NET.Core/Util/CommonUtil.cs → Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs

@@ -1,455 +1,455 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using IPTools.Core;
-using Magicodes.ExporterAndImporter.Core.Models;
-using System.Xml;
-using System.Xml.Linq;
-using System.Xml.Serialization;
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// 通用工具类
-/// </summary>
-public static class CommonUtil
-{
-    /// <summary>
-    /// 生成百分数
-    /// </summary>
-    /// <param name="PassCount"></param>
-    /// <param name="allCount"></param>
-    /// <returns></returns>
-    public static string ExecPercent(decimal PassCount, decimal allCount)
-    {
-        string res = "";
-        if (allCount > 0)
-        {
-            var value = (double)Math.Round(PassCount / allCount * 100, 1);
-            if (value < 0)
-                res = Math.Round(value + 5 / Math.Pow(10, 0 + 1), 0, MidpointRounding.AwayFromZero).ToString();
-            else
-                res = Math.Round(value, 0, MidpointRounding.AwayFromZero).ToString();
-        }
-        if (res == "") res = "0";
-        return res + "%";
-    }
-
-    /// <summary>
-    /// 获取服务地址
-    /// </summary>
-    /// <returns></returns>
-    public static string GetLocalhost()
-    {
-        string result = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Host.Value}";
-
-        // 代理模式:获取真正的本机地址
-        // X-Original-Host=原始请求
-        // X-Forwarded-Server=从哪里转发过来
-        if (App.HttpContext.Request.Headers.ContainsKey("Origin")) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
-            result = $"{App.HttpContext.Request.Headers["Origin"]}";
-        else if (App.HttpContext.Request.Headers.ContainsKey("X-Original")) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
-            result = $"{App.HttpContext.Request.Headers["X-Original"]}";
-        else if (App.HttpContext.Request.Headers.ContainsKey("X-Original-Host"))
-            result = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Headers["X-Original-Host"]}";
-        return result;
-    }
-
-    /// <summary>
-    /// 对象序列化XML
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="obj"></param>
-    /// <returns></returns>
-    public static string SerializeObjectToXml<T>(T obj)
-    {
-        if (obj == null) return string.Empty;
-
-        var xs = new XmlSerializer(obj.GetType());
-        var stream = new MemoryStream();
-        var setting = new XmlWriterSettings
-        {
-            Encoding = new UTF8Encoding(false), // 不包含BOM
-            Indent = true // 设置格式化缩进
-        };
-        using (var writer = XmlWriter.Create(stream, setting))
-        {
-            var ns = new XmlSerializerNamespaces();
-            ns.Add("", ""); // 去除默认命名空间
-            xs.Serialize(writer, obj, ns);
-        }
-        return Encoding.UTF8.GetString(stream.ToArray());
-    }
-
-    /// <summary>
-    /// 字符串转XML格式
-    /// </summary>
-    /// <param name="xmlStr"></param>
-    /// <returns></returns>
-    public static XElement SerializeStringToXml(string xmlStr)
-    {
-        try
-        {
-            return XElement.Parse(xmlStr);
-        }
-        catch
-        {
-            return null;
-        }
-    }
-
-    /// <summary>
-    /// 导出模板Excel
-    /// </summary>
-    /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelTemplate<T>(string fileName = null) where T : class, new()
-    {
-        IImporter importer = new ExcelImporter();
-        var res = await importer.GenerateTemplateBytes<T>();
-
-        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
-    }
-
-    /// <summary>
-    /// 导出数据excel
-    /// </summary>
-    /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelData<T>(ICollection<T> data, string fileName = null) where T : class, new()
-    {
-        var export = new ExcelExporter();
-        var res = await export.ExportAsByteArray<T>(data);
-
-        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
-    }
-
-    /// <summary>
-    /// 导出数据excel,包括字典转换
-    /// </summary>
-    /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelData<TSource, TTarget>(ISugarQueryable<TSource> query, Func<TSource, TTarget, TTarget> action = null)
-        where TSource : class, new() where TTarget : class, new()
-    {
-        var PropMappings = GetExportPropertMap<TSource, TTarget>();
-        var data = query.ToList();
-        //相同属性复制值,字典值转换
-        var result = new List<TTarget>();
-        foreach (var item in data)
-        {
-            var newData = new TTarget();
-            foreach (var dict in PropMappings)
-            {
-                var targeProp = dict.Value.Item3;
-                if (targeProp != null)
-                {
-                    var propertyInfo = dict.Value.Item2;
-                    var sourceVal = propertyInfo.GetValue(item, null);
-                    if (sourceVal == null)
-                    {
-                        continue;
-                    }
-
-                    var map = dict.Value.Item1;
-                    if (map != null && map.ContainsKey(sourceVal))
-                    {
-                        var newVal = map[sourceVal];
-                        targeProp.SetValue(newData, newVal);
-                    }
-                    else
-                    {
-                        if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
-                        {
-                            targeProp.SetValue(newData, sourceVal);
-                        }
-                        else
-                        {
-                            var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
-                            targeProp.SetValue(newData, newVal);
-                        }
-                    }
-                }
-                if (action != null)
-                {
-                    newData = action(item, newData);
-                }
-            }
-            result.Add(newData);
-        }
-        var export = new ExcelExporter();
-        var res = await export.ExportAsByteArray(result);
-
-        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(TTarget).Name + ".xlsx" };
-    }
-
-    /// <summary>
-    /// 导入数据Excel
-    /// </summary>
-    /// <param name="file"></param>
-    /// <returns></returns>
-    public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new()
-    {
-        IImporter importer = new ExcelImporter();
-        var res = await importer.Import<T>(file.OpenReadStream());
-        var message = string.Empty;
-        if (res.HasError)
-        {
-            if (res.Exception != null)
-                message += $"\r\n{res.Exception.Message}";
-            foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
-            {
-                int rowNum = drErrorInfo.RowIndex;
-                foreach (var item in drErrorInfo.FieldErrors)
-                    message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
-            }
-            message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
-            throw Oops.Oh("导入异常:" + message);
-        }
-        return res.Data;
-    }
-
-    /// <summary>
-    /// 导入Excel数据并错误标记
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="file"></param>
-    /// <param name="importResultCallback"></param>
-    /// <returns></returns>
-    public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file, Func<ImportResult<T>, ImportResult<T>> importResultCallback = null) where T : class, new()
-    {
-        IImporter importer = new ExcelImporter();
-        var resultStream = new MemoryStream();
-        var res = await importer.Import<T>(file.OpenReadStream(), resultStream, importResultCallback);
-        resultStream.Seek(0, SeekOrigin.Begin);
-        var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
-
-        App.GetRequiredService<SysCacheService>().Remove(CacheConst.KeyExcelTemp + userId);
-        App.GetRequiredService<SysCacheService>().Set(CacheConst.KeyExcelTemp + userId, resultStream, TimeSpan.FromMinutes(5));
-
-        var message = string.Empty;
-        if (res.HasError)
-        {
-            if (res.Exception != null)
-                message += $"\r\n{res.Exception.Message}";
-            foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
-            {
-                int rowNum = drErrorInfo.RowIndex;
-                foreach (var item in drErrorInfo.FieldErrors)
-                    message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
-            }
-            if (res.TemplateErrors.Count > 0)
-                message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
-
-            if (message.Length > 200)
-                message = message.Substring(0, 200) + "...\r\n异常过多,建议下载错误标记文件查看详细错误信息并重新导入。";
-            throw Oops.Oh("导入异常:" + message);
-        }
-        return res.Data;
-    }
-
-    // 例:List<Dm_ApplyDemo> ls = CommonUtil.ParseList<Dm_ApplyDemoInport, Dm_ApplyDemo>(importResult.Data);
-    /// <summary>
-    /// 对象转换 含字典转换
-    /// </summary>
-    /// <typeparam name="TSource"></typeparam>
-    /// <typeparam name="TTarget"></typeparam>
-    /// <param name="data"></param>
-    /// <param name="action"></param>
-    /// <returns></returns>
-    public static List<TTarget> ParseList<TSource, TTarget>(IEnumerable<TSource> data, Func<TSource, TTarget, TTarget> action = null) where TTarget : new()
-    {
-        var propMappings = GetImportPropertMap<TSource, TTarget>();
-        // 相同属性复制值,字典值转换
-        var result = new List<TTarget>();
-        foreach (var item in data)
-        {
-            var newData = new TTarget();
-            foreach (var dict in propMappings)
-            {
-                var targeProp = dict.Value.Item3;
-                if (targeProp != null)
-                {
-                    var propertyInfo = dict.Value.Item2;
-                    var sourceVal = propertyInfo.GetValue(item, null);
-                    if (sourceVal == null)
-                        continue;
-
-                    var map = dict.Value.Item1;
-                    if (map != null && map.ContainsKey(sourceVal.ToString()))
-                    {
-                        var newVal = map[sourceVal.ToString()];
-                        targeProp.SetValue(newData, newVal);
-                    }
-                    else
-                    {
-                        if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
-                        {
-                            targeProp.SetValue(newData, sourceVal);
-                        }
-                        else
-                        {
-                            var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
-                            targeProp.SetValue(newData, newVal);
-                        }
-                    }
-                }
-            }
-            if (action != null)
-                newData = action(item, newData);
-
-            if (newData != null)
-                result.Add(newData);
-        }
-        return result;
-    }
-
-    /// <summary>
-    /// 获取导入属性映射
-    /// </summary>
-    /// <typeparam name="TSource"></typeparam>
-    /// <typeparam name="TTarget"></typeparam>
-    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
-    private static Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>> GetImportPropertMap<TSource, TTarget>() where TTarget : new()
-    {
-        // 整理导入对象的属性名称,<字典数据,原属性信息,目标属性信息>
-        var propMappings = new Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>>();
-
-        var dictService = App.GetRequiredService<SqlSugarRepository<SysDictData>>();
-        var tSourceProps = typeof(TSource).GetProperties().ToList();
-        var tTargetProps = typeof(TTarget).GetProperties().ToDictionary(u => u.Name);
-        foreach (var propertyInfo in tSourceProps)
-        {
-            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
-            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
-            {
-                var targetProp = tTargetProps[attrs.TargetPropName];
-                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((u, a) =>
-                    new JoinQueryInfos(JoinType.Inner, u.Id == a.DictTypeId))
-                    .Where(u => u.Code == attrs.TypeCode)
-                    .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
-                    .Select((u, a) => new
-                    {
-                        Label = a.Value,
-                        Value = a.Code
-                    }).ToList()
-                    .ToDictionary(u => u.Label, u => u.Value.ParseTo(targetProp.PropertyType));
-                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(mappingValues, propertyInfo, targetProp));
-            }
-            else
-            {
-                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(
-                    null, propertyInfo, tTargetProps.ContainsKey(propertyInfo.Name) ? tTargetProps[propertyInfo.Name] : null));
-            }
-        }
-
-        return propMappings;
-    }
-
-    /// <summary>
-    /// 获取导出属性映射
-    /// </summary>
-    /// <typeparam name="TSource"></typeparam>
-    /// <typeparam name="TTarget"></typeparam>
-    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
-    private static Dictionary<string, Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>> GetExportPropertMap<TSource, TTarget>() where TTarget : new()
-    {
-        // 整理导入对象的属性名称,<字典数据,原属性信息,目标属性信息>
-        var propMappings = new Dictionary<string, Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>>();
-
-        var dictService = App.GetRequiredService<SqlSugarRepository<SysDictData>>();
-        var targetProps = typeof(TTarget).GetProperties().ToList();
-        var sourceProps = typeof(TSource).GetProperties().ToDictionary(u => u.Name);
-        foreach (var propertyInfo in targetProps)
-        {
-            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
-            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
-            {
-                var targetProp = sourceProps[attrs.TargetPropName];
-                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((u, a) =>
-                    new JoinQueryInfos(JoinType.Inner, u.Id == a.DictTypeId))
-                    .Where(u => u.Code == attrs.TypeCode)
-                    .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
-                    .Select((u, a) => new
-                    {
-                        Label = a.Value,
-                        Value = a.Code
-                    }).ToList()
-                    .ToDictionary(u => u.Value.ParseTo(targetProp.PropertyType), u => u.Label);
-                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(mappingValues, targetProp, propertyInfo));
-            }
-            else
-            {
-                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(
-                    null, sourceProps.ContainsKey(propertyInfo.Name) ? sourceProps[propertyInfo.Name] : null, propertyInfo));
-            }
-        }
-
-        return propMappings;
-    }
-
-    /// <summary>
-    /// 获取属性映射
-    /// </summary>
-    /// <typeparam name="TTarget"></typeparam>
-    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
-    private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<TTarget>() where TTarget : new()
-    {
-        // 整理导入对象的属性名称,目标属性名,字典Code
-        var propMappings = new Dictionary<string, Tuple<string, string>>();
-        var tTargetProps = typeof(TTarget).GetProperties();
-        foreach (var propertyInfo in tTargetProps)
-        {
-            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
-            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
-            {
-                propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
-            }
-        }
-
-        return propMappings;
-    }
-
-    /// <summary>
-    /// 解析IP地址
-    /// </summary>
-    /// <param name="ip"></param>
-    /// <returns></returns>
-    public static (string ipLocation, double? longitude, double? latitude) GetIpAddress(string ip)
-    {
-        try
-        {
-            var ipInfo = IpTool.SearchWithI18N(ip); // 国际化查询,默认中文 中文zh-CN、英文en
-            var addressList = new List<string>() { ipInfo.Country, ipInfo.Province, ipInfo.City, ipInfo.NetworkOperator };
-            return (string.Join(" ", addressList.Where(u => u != "0" && !string.IsNullOrWhiteSpace(u)).ToList()), ipInfo.Longitude, ipInfo.Latitude); // 去掉0及空并用空格连接
-        }
-        catch
-        {
-            // 不做处理
-        }
-        return ("未知", 0, 0);
-    }
-
-    /// <summary>
-    /// 获取客户端设备信息(操作系统+浏览器)
-    /// </summary>
-    /// <param name="userAgent"></param>
-    /// <returns></returns>
-    public static string GetClientDeviceInfo(string userAgent)
-    {
-        try
-        {
-            if (userAgent != null)
-            {
-                var client = Parser.GetDefault().Parse(userAgent);
-                if (client.Device.IsSpider)
-                    return "爬虫";
-                return $"{client.OS.Family} {client.OS.Major} {client.OS.Minor}" +
-                    $"|{client.UA.Family} {client.UA.Major}.{client.UA.Minor} / {client.Device.Family}";
-            }
-        }
-        catch
-        { }
-        return "未知";
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using IPTools.Core;
+using Magicodes.ExporterAndImporter.Core.Models;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.Serialization;
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 通用工具类
+/// </summary>
+public static class CommonUtil
+{
+    /// <summary>
+    /// 生成百分数
+    /// </summary>
+    /// <param name="PassCount"></param>
+    /// <param name="allCount"></param>
+    /// <returns></returns>
+    public static string ExecPercent(decimal PassCount, decimal allCount)
+    {
+        string res = "";
+        if (allCount > 0)
+        {
+            var value = (double)Math.Round(PassCount / allCount * 100, 1);
+            if (value < 0)
+                res = Math.Round(value + 5 / Math.Pow(10, 0 + 1), 0, MidpointRounding.AwayFromZero).ToString();
+            else
+                res = Math.Round(value, 0, MidpointRounding.AwayFromZero).ToString();
+        }
+        if (res == "") res = "0";
+        return res + "%";
+    }
+
+    /// <summary>
+    /// 获取服务地址
+    /// </summary>
+    /// <returns></returns>
+    public static string GetLocalhost()
+    {
+        string result = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Host.Value}";
+
+        // 代理模式:获取真正的本机地址
+        // X-Original-Host=原始请求
+        // X-Forwarded-Server=从哪里转发过来
+        if (App.HttpContext.Request.Headers.ContainsKey("Origin")) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
+            result = $"{App.HttpContext.Request.Headers["Origin"]}";
+        else if (App.HttpContext.Request.Headers.ContainsKey("X-Original")) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
+            result = $"{App.HttpContext.Request.Headers["X-Original"]}";
+        else if (App.HttpContext.Request.Headers.ContainsKey("X-Original-Host"))
+            result = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Headers["X-Original-Host"]}";
+        return result;
+    }
+
+    /// <summary>
+    /// 对象序列化XML
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="obj"></param>
+    /// <returns></returns>
+    public static string SerializeObjectToXml<T>(T obj)
+    {
+        if (obj == null) return string.Empty;
+
+        var xs = new XmlSerializer(obj.GetType());
+        var stream = new MemoryStream();
+        var setting = new XmlWriterSettings
+        {
+            Encoding = new UTF8Encoding(false), // 不包含BOM
+            Indent = true // 设置格式化缩进
+        };
+        using (var writer = XmlWriter.Create(stream, setting))
+        {
+            var ns = new XmlSerializerNamespaces();
+            ns.Add("", ""); // 去除默认命名空间
+            xs.Serialize(writer, obj, ns);
+        }
+        return Encoding.UTF8.GetString(stream.ToArray());
+    }
+
+    /// <summary>
+    /// 字符串转XML格式
+    /// </summary>
+    /// <param name="xmlStr"></param>
+    /// <returns></returns>
+    public static XElement SerializeStringToXml(string xmlStr)
+    {
+        try
+        {
+            return XElement.Parse(xmlStr);
+        }
+        catch
+        {
+            return null;
+        }
+    }
+
+    /// <summary>
+    /// 导出模板Excel
+    /// </summary>
+    /// <returns></returns>
+    public static async Task<IActionResult> ExportExcelTemplate<T>(string fileName = null) where T : class, new()
+    {
+        IImporter importer = new ExcelImporter();
+        var res = await importer.GenerateTemplateBytes<T>();
+
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
+    }
+
+    /// <summary>
+    /// 导出数据excel
+    /// </summary>
+    /// <returns></returns>
+    public static async Task<IActionResult> ExportExcelData<T>(ICollection<T> data, string fileName = null) where T : class, new()
+    {
+        var export = new ExcelExporter();
+        var res = await export.ExportAsByteArray<T>(data);
+
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
+    }
+
+    /// <summary>
+    /// 导出数据excel,包括字典转换
+    /// </summary>
+    /// <returns></returns>
+    public static async Task<IActionResult> ExportExcelData<TSource, TTarget>(ISugarQueryable<TSource> query, Func<TSource, TTarget, TTarget> action = null)
+        where TSource : class, new() where TTarget : class, new()
+    {
+        var PropMappings = GetExportPropertMap<TSource, TTarget>();
+        var data = query.ToList();
+        //相同属性复制值,字典值转换
+        var result = new List<TTarget>();
+        foreach (var item in data)
+        {
+            var newData = new TTarget();
+            foreach (var dict in PropMappings)
+            {
+                var targeProp = dict.Value.Item3;
+                if (targeProp != null)
+                {
+                    var propertyInfo = dict.Value.Item2;
+                    var sourceVal = propertyInfo.GetValue(item, null);
+                    if (sourceVal == null)
+                    {
+                        continue;
+                    }
+
+                    var map = dict.Value.Item1;
+                    if (map != null && map.ContainsKey(sourceVal))
+                    {
+                        var newVal = map[sourceVal];
+                        targeProp.SetValue(newData, newVal);
+                    }
+                    else
+                    {
+                        if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
+                        {
+                            targeProp.SetValue(newData, sourceVal);
+                        }
+                        else
+                        {
+                            var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
+                            targeProp.SetValue(newData, newVal);
+                        }
+                    }
+                }
+                if (action != null)
+                {
+                    newData = action(item, newData);
+                }
+            }
+            result.Add(newData);
+        }
+        var export = new ExcelExporter();
+        var res = await export.ExportAsByteArray(result);
+
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(TTarget).Name + ".xlsx" };
+    }
+
+    /// <summary>
+    /// 导入数据Excel
+    /// </summary>
+    /// <param name="file"></param>
+    /// <returns></returns>
+    public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new()
+    {
+        IImporter importer = new ExcelImporter();
+        var res = await importer.Import<T>(file.OpenReadStream());
+        var message = string.Empty;
+        if (res.HasError)
+        {
+            if (res.Exception != null)
+                message += $"\r\n{res.Exception.Message}";
+            foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
+            {
+                int rowNum = drErrorInfo.RowIndex;
+                foreach (var item in drErrorInfo.FieldErrors)
+                    message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
+            }
+            message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
+            throw Oops.Oh("导入异常:" + message);
+        }
+        return res.Data;
+    }
+
+    /// <summary>
+    /// 导入Excel数据并错误标记
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="file"></param>
+    /// <param name="importResultCallback"></param>
+    /// <returns></returns>
+    public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file, Func<ImportResult<T>, ImportResult<T>> importResultCallback = null) where T : class, new()
+    {
+        IImporter importer = new ExcelImporter();
+        var resultStream = new MemoryStream();
+        var res = await importer.Import<T>(file.OpenReadStream(), resultStream, importResultCallback);
+        resultStream.Seek(0, SeekOrigin.Begin);
+        var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
+
+        App.GetRequiredService<SysCacheService>().Remove(CacheConst.KeyExcelTemp + userId);
+        App.GetRequiredService<SysCacheService>().Set(CacheConst.KeyExcelTemp + userId, resultStream, TimeSpan.FromMinutes(5));
+
+        var message = string.Empty;
+        if (res.HasError)
+        {
+            if (res.Exception != null)
+                message += $"\r\n{res.Exception.Message}";
+            foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
+            {
+                int rowNum = drErrorInfo.RowIndex;
+                foreach (var item in drErrorInfo.FieldErrors)
+                    message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
+            }
+            if (res.TemplateErrors.Count > 0)
+                message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
+
+            if (message.Length > 200)
+                message = message.Substring(0, 200) + "...\r\n异常过多,建议下载错误标记文件查看详细错误信息并重新导入。";
+            throw Oops.Oh("导入异常:" + message);
+        }
+        return res.Data;
+    }
+
+    // 例:List<Dm_ApplyDemo> ls = CommonUtil.ParseList<Dm_ApplyDemoInport, Dm_ApplyDemo>(importResult.Data);
+    /// <summary>
+    /// 对象转换 含字典转换
+    /// </summary>
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <param name="data"></param>
+    /// <param name="action"></param>
+    /// <returns></returns>
+    public static List<TTarget> ParseList<TSource, TTarget>(IEnumerable<TSource> data, Func<TSource, TTarget, TTarget> action = null) where TTarget : new()
+    {
+        var propMappings = GetImportPropertMap<TSource, TTarget>();
+        // 相同属性复制值,字典值转换
+        var result = new List<TTarget>();
+        foreach (var item in data)
+        {
+            var newData = new TTarget();
+            foreach (var dict in propMappings)
+            {
+                var targeProp = dict.Value.Item3;
+                if (targeProp != null)
+                {
+                    var propertyInfo = dict.Value.Item2;
+                    var sourceVal = propertyInfo.GetValue(item, null);
+                    if (sourceVal == null)
+                        continue;
+
+                    var map = dict.Value.Item1;
+                    if (map != null && map.ContainsKey(sourceVal.ToString()))
+                    {
+                        var newVal = map[sourceVal.ToString()];
+                        targeProp.SetValue(newData, newVal);
+                    }
+                    else
+                    {
+                        if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
+                        {
+                            targeProp.SetValue(newData, sourceVal);
+                        }
+                        else
+                        {
+                            var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
+                            targeProp.SetValue(newData, newVal);
+                        }
+                    }
+                }
+            }
+            if (action != null)
+                newData = action(item, newData);
+
+            if (newData != null)
+                result.Add(newData);
+        }
+        return result;
+    }
+
+    /// <summary>
+    /// 获取导入属性映射
+    /// </summary>
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
+    private static Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>> GetImportPropertMap<TSource, TTarget>() where TTarget : new()
+    {
+        // 整理导入对象的属性名称,<字典数据,原属性信息,目标属性信息>
+        var propMappings = new Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>>();
+
+        var dictService = App.GetRequiredService<SqlSugarRepository<SysDictData>>();
+        var tSourceProps = typeof(TSource).GetProperties().ToList();
+        var tTargetProps = typeof(TTarget).GetProperties().ToDictionary(u => u.Name);
+        foreach (var propertyInfo in tSourceProps)
+        {
+            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
+            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
+            {
+                var targetProp = tTargetProps[attrs.TargetPropName];
+                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((u, a) =>
+                    new JoinQueryInfos(JoinType.Inner, u.Id == a.DictTypeId))
+                    .Where(u => u.Code == attrs.TypeCode)
+                    .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
+                    .Select((u, a) => new
+                    {
+                        Label = a.Value,
+                        Value = a.Code
+                    }).ToList()
+                    .ToDictionary(u => u.Label, u => u.Value.ParseTo(targetProp.PropertyType));
+                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(mappingValues, propertyInfo, targetProp));
+            }
+            else
+            {
+                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(
+                    null, propertyInfo, tTargetProps.ContainsKey(propertyInfo.Name) ? tTargetProps[propertyInfo.Name] : null));
+            }
+        }
+
+        return propMappings;
+    }
+
+    /// <summary>
+    /// 获取导出属性映射
+    /// </summary>
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
+    private static Dictionary<string, Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>> GetExportPropertMap<TSource, TTarget>() where TTarget : new()
+    {
+        // 整理导入对象的属性名称,<字典数据,原属性信息,目标属性信息>
+        var propMappings = new Dictionary<string, Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>>();
+
+        var dictService = App.GetRequiredService<SqlSugarRepository<SysDictData>>();
+        var targetProps = typeof(TTarget).GetProperties().ToList();
+        var sourceProps = typeof(TSource).GetProperties().ToDictionary(u => u.Name);
+        foreach (var propertyInfo in targetProps)
+        {
+            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
+            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
+            {
+                var targetProp = sourceProps[attrs.TargetPropName];
+                var mappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((u, a) =>
+                    new JoinQueryInfos(JoinType.Inner, u.Id == a.DictTypeId))
+                    .Where(u => u.Code == attrs.TypeCode)
+                    .Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
+                    .Select((u, a) => new
+                    {
+                        Label = a.Value,
+                        Value = a.Code
+                    }).ToList()
+                    .ToDictionary(u => u.Value.ParseTo(targetProp.PropertyType), u => u.Label);
+                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(mappingValues, targetProp, propertyInfo));
+            }
+            else
+            {
+                propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(
+                    null, sourceProps.ContainsKey(propertyInfo.Name) ? sourceProps[propertyInfo.Name] : null, propertyInfo));
+            }
+        }
+
+        return propMappings;
+    }
+
+    /// <summary>
+    /// 获取属性映射
+    /// </summary>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
+    private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<TTarget>() where TTarget : new()
+    {
+        // 整理导入对象的属性名称,目标属性名,字典Code
+        var propMappings = new Dictionary<string, Tuple<string, string>>();
+        var tTargetProps = typeof(TTarget).GetProperties();
+        foreach (var propertyInfo in tTargetProps)
+        {
+            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
+            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
+            {
+                propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
+            }
+        }
+
+        return propMappings;
+    }
+
+    /// <summary>
+    /// 解析IP地址
+    /// </summary>
+    /// <param name="ip"></param>
+    /// <returns></returns>
+    public static (string ipLocation, double? longitude, double? latitude) GetIpAddress(string ip)
+    {
+        try
+        {
+            var ipInfo = IpTool.SearchWithI18N(ip); // 国际化查询,默认中文 中文zh-CN、英文en
+            var addressList = new List<string>() { ipInfo.Country, ipInfo.Province, ipInfo.City, ipInfo.NetworkOperator };
+            return (string.Join(" ", addressList.Where(u => u != "0" && !string.IsNullOrWhiteSpace(u)).ToList()), ipInfo.Longitude, ipInfo.Latitude); // 去掉0及空并用空格连接
+        }
+        catch
+        {
+            // 不做处理
+        }
+        return ("未知", 0, 0);
+    }
+
+    /// <summary>
+    /// 获取客户端设备信息(操作系统+浏览器)
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    public static string GetClientDeviceInfo(string userAgent)
+    {
+        try
+        {
+            if (userAgent != null)
+            {
+                var client = Parser.GetDefault().Parse(userAgent);
+                if (client.Device.IsSpider)
+                    return "爬虫";
+                return $"{client.OS.Family} {client.OS.Major} {client.OS.Minor}" +
+                    $"|{client.UA.Family} {client.UA.Major}.{client.UA.Minor} / {client.Device.Family}";
+            }
+        }
+        catch
+        { }
+        return "未知";
+    }
 }

+ 0 - 0
Admin.NET/Admin.NET.Core/Util/ComputerUtil.cs → Admin.NET/Admin.NET.Core/Utils/ComputerUtil.cs


+ 119 - 119
Admin.NET/Admin.NET.Core/Util/CryptogramUtil.cs → Admin.NET/Admin.NET.Core/Utils/CryptogramUtil.cs

@@ -1,120 +1,120 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-namespace Admin.NET.Core;
-
-public class CryptogramUtil
-{
-    public static readonly bool StrongPassword = App.GetConfig<bool>("Cryptogram:StrongPassword"); // 是否开启密码强度验证
-    public static readonly string PasswordStrengthValidation = App.GetConfig<string>("Cryptogram:PasswordStrengthValidation"); // 密码强度验证正则表达式
-    public static readonly string PasswordStrengthValidationMsg = App.GetConfig<string>("Cryptogram:PasswordStrengthValidationMsg"); // 密码强度验证提示
-    public static readonly string CryptoType = App.GetConfig<string>("Cryptogram:CryptoType"); // 加密类型
-    public static readonly string PublicKey = App.GetConfig<string>("Cryptogram:PublicKey"); // 公钥
-    public static readonly string PrivateKey = App.GetConfig<string>("Cryptogram:PrivateKey"); // 私钥
-
-    public static readonly string SM4_key = "0123456789abcdeffedcba9876543210";
-    public static readonly string SM4_iv = "595298c7c6fd271f0402f804c33d3f66";
-
-    /// <summary>
-    /// 加密
-    /// </summary>
-    /// <param name="plainText"></param>
-    /// <returns></returns>
-    public static string Encrypt(string plainText)
-    {
-        if (CryptoType == CryptogramEnum.MD5.ToString())
-        {
-            return MD5Encryption.Encrypt(plainText);
-        }
-        else if (CryptoType == CryptogramEnum.SM2.ToString())
-        {
-            return SM2Encrypt(plainText);
-        }
-        else if (CryptoType == CryptogramEnum.SM4.ToString())
-        {
-            return SM4EncryptECB(plainText);
-        }
-        return plainText;
-    }
-
-    /// <summary>
-    /// 解密
-    /// </summary>
-    /// <param name="cipherText"></param>
-    /// <returns></returns>
-    public static string Decrypt(string cipherText)
-    {
-        if (CryptoType == CryptogramEnum.SM2.ToString())
-        {
-            return SM2Decrypt(cipherText);
-        }
-        else if (CryptoType == CryptogramEnum.SM4.ToString())
-        {
-            return SM4DecryptECB(cipherText);
-        }
-        return cipherText;
-    }
-
-    /// <summary>
-    /// SM2加密
-    /// </summary>
-    /// <param name="plainText"></param>
-    /// <returns></returns>
-    public static string SM2Encrypt(string plainText)
-    {
-        return GMUtil.SM2Encrypt(PublicKey, plainText);
-    }
-
-    /// <summary>
-    /// SM2解密
-    /// </summary>
-    /// <param name="cipherText"></param>
-    /// <returns></returns>
-    public static string SM2Decrypt(string cipherText)
-    {
-        return GMUtil.SM2Decrypt(PrivateKey, cipherText);
-    }
-
-    /// <summary>
-    /// SM4加密(ECB)
-    /// </summary>
-    /// <param name="plainText"></param>
-    /// <returns></returns>
-    public static string SM4EncryptECB(string plainText)
-    {
-        return GMUtil.SM4EncryptECB(SM4_key, plainText);
-    }
-
-    /// <summary>
-    /// SM4解密(ECB)
-    /// </summary>
-    /// <param name="cipherText"></param>
-    /// <returns></returns>
-    public static string SM4DecryptECB(string cipherText)
-    {
-        return GMUtil.SM4DecryptECB(SM4_key, cipherText);
-    }
-
-    /// <summary>
-    /// SM4加密(CBC)
-    /// </summary>
-    /// <param name="plainText"></param>
-    /// <returns></returns>
-    public static string SM4EncryptCBC(string plainText)
-    {
-        return GMUtil.SM4EncryptCBC(SM4_key, SM4_iv, plainText);
-    }
-
-    /// <summary>
-    /// SM4解密(CBC)
-    /// </summary>
-    /// <param name="cipherText"></param>
-    /// <returns></returns>
-    public static string SM4DecryptCBC(string cipherText)
-    {
-        return GMUtil.SM4DecryptCBC(SM4_key, SM4_iv, cipherText);
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+public class CryptogramUtil
+{
+    public static readonly bool StrongPassword = App.GetConfig<bool>("Cryptogram:StrongPassword"); // 是否开启密码强度验证
+    public static readonly string PasswordStrengthValidation = App.GetConfig<string>("Cryptogram:PasswordStrengthValidation"); // 密码强度验证正则表达式
+    public static readonly string PasswordStrengthValidationMsg = App.GetConfig<string>("Cryptogram:PasswordStrengthValidationMsg"); // 密码强度验证提示
+    public static readonly string CryptoType = App.GetConfig<string>("Cryptogram:CryptoType"); // 加密类型
+    public static readonly string PublicKey = App.GetConfig<string>("Cryptogram:PublicKey"); // 公钥
+    public static readonly string PrivateKey = App.GetConfig<string>("Cryptogram:PrivateKey"); // 私钥
+
+    public static readonly string SM4_key = "0123456789abcdeffedcba9876543210";
+    public static readonly string SM4_iv = "595298c7c6fd271f0402f804c33d3f66";
+
+    /// <summary>
+    /// 加密
+    /// </summary>
+    /// <param name="plainText"></param>
+    /// <returns></returns>
+    public static string Encrypt(string plainText)
+    {
+        if (CryptoType == CryptogramEnum.MD5.ToString())
+        {
+            return MD5Encryption.Encrypt(plainText);
+        }
+        else if (CryptoType == CryptogramEnum.SM2.ToString())
+        {
+            return SM2Encrypt(plainText);
+        }
+        else if (CryptoType == CryptogramEnum.SM4.ToString())
+        {
+            return SM4EncryptECB(plainText);
+        }
+        return plainText;
+    }
+
+    /// <summary>
+    /// 解密
+    /// </summary>
+    /// <param name="cipherText"></param>
+    /// <returns></returns>
+    public static string Decrypt(string cipherText)
+    {
+        if (CryptoType == CryptogramEnum.SM2.ToString())
+        {
+            return SM2Decrypt(cipherText);
+        }
+        else if (CryptoType == CryptogramEnum.SM4.ToString())
+        {
+            return SM4DecryptECB(cipherText);
+        }
+        return cipherText;
+    }
+
+    /// <summary>
+    /// SM2加密
+    /// </summary>
+    /// <param name="plainText"></param>
+    /// <returns></returns>
+    public static string SM2Encrypt(string plainText)
+    {
+        return GMUtil.SM2Encrypt(PublicKey, plainText);
+    }
+
+    /// <summary>
+    /// SM2解密
+    /// </summary>
+    /// <param name="cipherText"></param>
+    /// <returns></returns>
+    public static string SM2Decrypt(string cipherText)
+    {
+        return GMUtil.SM2Decrypt(PrivateKey, cipherText);
+    }
+
+    /// <summary>
+    /// SM4加密(ECB)
+    /// </summary>
+    /// <param name="plainText"></param>
+    /// <returns></returns>
+    public static string SM4EncryptECB(string plainText)
+    {
+        return GMUtil.SM4EncryptECB(SM4_key, plainText);
+    }
+
+    /// <summary>
+    /// SM4解密(ECB)
+    /// </summary>
+    /// <param name="cipherText"></param>
+    /// <returns></returns>
+    public static string SM4DecryptECB(string cipherText)
+    {
+        return GMUtil.SM4DecryptECB(SM4_key, cipherText);
+    }
+
+    /// <summary>
+    /// SM4加密(CBC)
+    /// </summary>
+    /// <param name="plainText"></param>
+    /// <returns></returns>
+    public static string SM4EncryptCBC(string plainText)
+    {
+        return GMUtil.SM4EncryptCBC(SM4_key, SM4_iv, plainText);
+    }
+
+    /// <summary>
+    /// SM4解密(CBC)
+    /// </summary>
+    /// <param name="cipherText"></param>
+    /// <returns></returns>
+    public static string SM4DecryptCBC(string cipherText)
+    {
+        return GMUtil.SM4DecryptCBC(SM4_key, SM4_iv, cipherText);
+    }
 }

+ 0 - 0
Admin.NET/Admin.NET.Core/Util/DateTimeUtil.cs → Admin.NET/Admin.NET.Core/Utils/DateTimeUtil.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/GM/GM.cs → Admin.NET/Admin.NET.Core/Utils/GM/GM.cs


+ 150 - 150
Admin.NET/Admin.NET.Core/Util/GM/GMUtil.cs → Admin.NET/Admin.NET.Core/Utils/GM/GMUtil.cs

@@ -1,151 +1,151 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Utilities.Encoders;
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// GM工具类
-/// </summary>
-public class GMUtil
-{
-    /// <summary>
-    /// SM2加密
-    /// </summary>
-    /// <param name="publicKeyHex"></param>
-    /// <param name="data_string"></param>
-    /// <returns></returns>
-    public static string SM2Encrypt(string publicKeyHex, string data_string)
-    {
-        // 如果是130位公钥,.NET使用的话,把开头的04截取掉
-        if (publicKeyHex.Length == 130)
-        {
-            publicKeyHex = publicKeyHex.Substring(2, 128);
-        }
-        // 公钥X,前64位
-        string x = publicKeyHex.Substring(0, 64);
-        // 公钥Y,后64位
-        string y = publicKeyHex.Substring(64);
-        // 获取公钥对象
-        AsymmetricKeyParameter publicKey1 = GM.GetPublickeyFromXY(new BigInteger(x, 16), new BigInteger(y, 16));
-        // Sm2Encrypt: C1C3C2
-        // Sm2EncryptOld: C1C2C3
-        byte[] digestByte = GM.Sm2Encrypt(Encoding.UTF8.GetBytes(data_string), publicKey1);
-        string strSM2 = Hex.ToHexString(digestByte);
-        return strSM2;
-    }
-
-    /// <summary>
-    /// SM2解密
-    /// </summary>
-    /// <param name="privateKey_string"></param>
-    /// <param name="encryptedData_string"></param>
-    /// <returns></returns>
-    public static string SM2Decrypt(string privateKey_string, string encryptedData_string)
-    {
-        if (!encryptedData_string.StartsWith("04"))
-            encryptedData_string = "04" + encryptedData_string;
-        BigInteger d = new(privateKey_string, 16);
-        // 先拿到私钥对象,用ECPrivateKeyParameters 或 AsymmetricKeyParameter 都可以
-        // ECPrivateKeyParameters bcecPrivateKey = GmUtil.GetPrivatekeyFromD(d);
-        AsymmetricKeyParameter bcecPrivateKey = GM.GetPrivatekeyFromD(d);
-        byte[] byToDecrypt = Hex.Decode(encryptedData_string);
-        byte[] byDecrypted = GM.Sm2Decrypt(byToDecrypt, bcecPrivateKey);
-        string strDecrypted = Encoding.UTF8.GetString(byDecrypted);
-        return strDecrypted;
-    }
-
-    /// <summary>
-    /// SM4加密(ECB)
-    /// </summary>
-    /// <param name="key_string"></param>
-    /// <param name="plainText"></param>
-    /// <returns></returns>
-    public static string SM4EncryptECB(string key_string, string plainText)
-    {
-        byte[] key = Hex.Decode(key_string);
-        byte[] bs = GM.Sm4EncryptECB(key, Encoding.UTF8.GetBytes(plainText), GM.SM4_ECB_PKCS7PADDING);//NoPadding 的情况下需要校验数据长度是16的倍数. 使用 HandleSm4Padding 处理
-        return Hex.ToHexString(bs);
-    }
-
-    /// <summary>
-    /// SM4解密(ECB)
-    /// </summary>
-    /// <param name="key_string"></param>
-    /// <param name="cipherText"></param>
-    /// <returns></returns>
-    public static string SM4DecryptECB(string key_string, string cipherText)
-    {
-        byte[] key = Hex.Decode(key_string);
-        byte[] bs = GM.Sm4DecryptECB(key, Hex.Decode(cipherText), GM.SM4_ECB_PKCS7PADDING);
-        return Encoding.UTF8.GetString(bs);
-    }
-
-    /// <summary>
-    /// SM4加密(CBC)
-    /// </summary>
-    /// <param name="key_string"></param>
-    /// <param name="iv_string"></param>
-    /// <param name="plainText"></param>
-    /// <returns></returns>
-    public static string SM4EncryptCBC(string key_string, string iv_string, string plainText)
-    {
-        byte[] key = Hex.Decode(key_string);
-        byte[] iv = Hex.Decode(iv_string);
-        byte[] bs = GM.Sm4EncryptCBC(key, Encoding.UTF8.GetBytes(plainText), iv, GM.SM4_CBC_PKCS7PADDING);
-        return Hex.ToHexString(bs);
-    }
-
-    /// <summary>
-    /// SM4解密(CBC)
-    /// </summary>
-    /// <param name="key_string"></param>
-    /// <param name="iv_string"></param>
-    /// <param name="cipherText"></param>
-    /// <returns></returns>
-    public static string SM4DecryptCBC(string key_string, string iv_string, string cipherText)
-    {
-        byte[] key = Hex.Decode(key_string);
-        byte[] iv = Hex.Decode(iv_string);
-        byte[] bs = GM.Sm4DecryptCBC(key, Hex.Decode(cipherText), iv, GM.SM4_CBC_PKCS7PADDING);
-        return Encoding.UTF8.GetString(bs);
-    }
-
-    /// <summary>
-    /// 补足 16 进制字符串的 0 字符,返回不带 0x 的16进制字符串
-    /// </summary>
-    /// <param name="input"></param>
-    /// <param name="mode">1表示加密,0表示解密</param>
-    /// <returns></returns>
-    private static byte[] HandleSm4Padding(byte[] input, int mode)
-    {
-        if (input == null)
-        {
-            return null;
-        }
-        byte[] ret = (byte[])null;
-        if (mode == 1)
-        {
-            int p = 16 - input.Length % 16;
-            ret = new byte[input.Length + p];
-            Array.Copy(input, 0, ret, 0, input.Length);
-            for (int i = 0; i < p; i++)
-            {
-                ret[input.Length + i] = (byte)p;
-            }
-        }
-        else
-        {
-            int p = input[input.Length - 1];
-            ret = new byte[input.Length - p];
-            Array.Copy(input, 0, ret, 0, input.Length - p);
-        }
-        return ret;
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// GM工具类
+/// </summary>
+public class GMUtil
+{
+    /// <summary>
+    /// SM2加密
+    /// </summary>
+    /// <param name="publicKeyHex"></param>
+    /// <param name="data_string"></param>
+    /// <returns></returns>
+    public static string SM2Encrypt(string publicKeyHex, string data_string)
+    {
+        // 如果是130位公钥,.NET使用的话,把开头的04截取掉
+        if (publicKeyHex.Length == 130)
+        {
+            publicKeyHex = publicKeyHex.Substring(2, 128);
+        }
+        // 公钥X,前64位
+        string x = publicKeyHex.Substring(0, 64);
+        // 公钥Y,后64位
+        string y = publicKeyHex.Substring(64);
+        // 获取公钥对象
+        AsymmetricKeyParameter publicKey1 = GM.GetPublickeyFromXY(new BigInteger(x, 16), new BigInteger(y, 16));
+        // Sm2Encrypt: C1C3C2
+        // Sm2EncryptOld: C1C2C3
+        byte[] digestByte = GM.Sm2Encrypt(Encoding.UTF8.GetBytes(data_string), publicKey1);
+        string strSM2 = Hex.ToHexString(digestByte);
+        return strSM2;
+    }
+
+    /// <summary>
+    /// SM2解密
+    /// </summary>
+    /// <param name="privateKey_string"></param>
+    /// <param name="encryptedData_string"></param>
+    /// <returns></returns>
+    public static string SM2Decrypt(string privateKey_string, string encryptedData_string)
+    {
+        if (!encryptedData_string.StartsWith("04"))
+            encryptedData_string = "04" + encryptedData_string;
+        BigInteger d = new(privateKey_string, 16);
+        // 先拿到私钥对象,用ECPrivateKeyParameters 或 AsymmetricKeyParameter 都可以
+        // ECPrivateKeyParameters bcecPrivateKey = GmUtil.GetPrivatekeyFromD(d);
+        AsymmetricKeyParameter bcecPrivateKey = GM.GetPrivatekeyFromD(d);
+        byte[] byToDecrypt = Hex.Decode(encryptedData_string);
+        byte[] byDecrypted = GM.Sm2Decrypt(byToDecrypt, bcecPrivateKey);
+        string strDecrypted = Encoding.UTF8.GetString(byDecrypted);
+        return strDecrypted;
+    }
+
+    /// <summary>
+    /// SM4加密(ECB)
+    /// </summary>
+    /// <param name="key_string"></param>
+    /// <param name="plainText"></param>
+    /// <returns></returns>
+    public static string SM4EncryptECB(string key_string, string plainText)
+    {
+        byte[] key = Hex.Decode(key_string);
+        byte[] bs = GM.Sm4EncryptECB(key, Encoding.UTF8.GetBytes(plainText), GM.SM4_ECB_PKCS7PADDING);//NoPadding 的情况下需要校验数据长度是16的倍数. 使用 HandleSm4Padding 处理
+        return Hex.ToHexString(bs);
+    }
+
+    /// <summary>
+    /// SM4解密(ECB)
+    /// </summary>
+    /// <param name="key_string"></param>
+    /// <param name="cipherText"></param>
+    /// <returns></returns>
+    public static string SM4DecryptECB(string key_string, string cipherText)
+    {
+        byte[] key = Hex.Decode(key_string);
+        byte[] bs = GM.Sm4DecryptECB(key, Hex.Decode(cipherText), GM.SM4_ECB_PKCS7PADDING);
+        return Encoding.UTF8.GetString(bs);
+    }
+
+    /// <summary>
+    /// SM4加密(CBC)
+    /// </summary>
+    /// <param name="key_string"></param>
+    /// <param name="iv_string"></param>
+    /// <param name="plainText"></param>
+    /// <returns></returns>
+    public static string SM4EncryptCBC(string key_string, string iv_string, string plainText)
+    {
+        byte[] key = Hex.Decode(key_string);
+        byte[] iv = Hex.Decode(iv_string);
+        byte[] bs = GM.Sm4EncryptCBC(key, Encoding.UTF8.GetBytes(plainText), iv, GM.SM4_CBC_PKCS7PADDING);
+        return Hex.ToHexString(bs);
+    }
+
+    /// <summary>
+    /// SM4解密(CBC)
+    /// </summary>
+    /// <param name="key_string"></param>
+    /// <param name="iv_string"></param>
+    /// <param name="cipherText"></param>
+    /// <returns></returns>
+    public static string SM4DecryptCBC(string key_string, string iv_string, string cipherText)
+    {
+        byte[] key = Hex.Decode(key_string);
+        byte[] iv = Hex.Decode(iv_string);
+        byte[] bs = GM.Sm4DecryptCBC(key, Hex.Decode(cipherText), iv, GM.SM4_CBC_PKCS7PADDING);
+        return Encoding.UTF8.GetString(bs);
+    }
+
+    /// <summary>
+    /// 补足 16 进制字符串的 0 字符,返回不带 0x 的16进制字符串
+    /// </summary>
+    /// <param name="input"></param>
+    /// <param name="mode">1表示加密,0表示解密</param>
+    /// <returns></returns>
+    private static byte[] HandleSm4Padding(byte[] input, int mode)
+    {
+        if (input == null)
+        {
+            return null;
+        }
+        byte[] ret = (byte[])null;
+        if (mode == 1)
+        {
+            int p = 16 - input.Length % 16;
+            ret = new byte[input.Length + p];
+            Array.Copy(input, 0, ret, 0, input.Length);
+            for (int i = 0; i < p; i++)
+            {
+                ret[input.Length + i] = (byte)p;
+            }
+        }
+        else
+        {
+            int p = input[input.Length - 1];
+            ret = new byte[input.Length - p];
+            Array.Copy(input, 0, ret, 0, input.Length - p);
+        }
+        return ret;
+    }
 }

+ 0 - 0
Admin.NET/Admin.NET.Core/Util/NewtonsoftJsonSerializerProvider.cs → Admin.NET/Admin.NET.Core/Utils/NewtonsoftJsonSerializerProvider.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/ReflectionUtil.cs → Admin.NET/Admin.NET.Core/Utils/ReflectionUtil.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/RegularValidate.cs → Admin.NET/Admin.NET.Core/Utils/RegularValidate.cs


+ 0 - 0
Admin.NET/Admin.NET.Core/Util/SSHHelper.cs → Admin.NET/Admin.NET.Core/Utils/SSHHelper.cs


+ 52 - 52
Admin.NET/Admin.NET.Core/Util/TripleDES.cs → Admin.NET/Admin.NET.Core/Utils/TripleDES.cs

@@ -1,53 +1,53 @@
-// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
-//
-// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
-//
-// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
-
-using System.Security.Cryptography;
-
-namespace Admin.NET.Core;
-
-/// <summary>
-/// 3DES文件加解密
-/// </summary>
-public static class TripleDES
-{
-    /// <summary>
-    /// 加密文件
-    /// </summary>
-    /// <param name="inputFile">待加密文件路径</param>
-    /// <param name="outputFile">加密后的文件路径</param>
-    /// <param name="password">密码 (24位长度)</param>
-    [Obsolete]
-    public static void EncryptFile(string inputFile, string outputFile, string password)
-    {
-        using var tdes = new TripleDESCryptoServiceProvider();
-        tdes.Mode = CipherMode.ECB;
-        tdes.Padding = PaddingMode.PKCS7;
-        tdes.Key = Encoding.UTF8.GetBytes(password);
-        using var inputFileStream = new FileStream(inputFile, FileMode.Open);
-        using var encryptedFileStream = new FileStream(outputFile, FileMode.Create);
-        using var cryptoStream = new CryptoStream(encryptedFileStream, tdes.CreateEncryptor(), CryptoStreamMode.Write);
-        inputFileStream.CopyTo(cryptoStream);
-    }
-
-    /// <summary>
-    /// 加密文件
-    /// </summary>
-    /// <param name="inputFile">加密的文件路径</param>
-    /// <param name="outputFile">解密后的文件路径</param>
-    /// <param name="password">密码 (24位长度)</param>
-    [Obsolete]
-    public static void DecryptFile(string inputFile, string outputFile, string password)
-    {
-        using var tdes = new TripleDESCryptoServiceProvider();
-        tdes.Mode = CipherMode.ECB;
-        tdes.Padding = PaddingMode.PKCS7;
-        tdes.Key = Encoding.UTF8.GetBytes(password);
-        using var encryptedFileStream = new FileStream(inputFile, FileMode.Open);
-        using var decryptedFileStream = new FileStream(outputFile, FileMode.Create);
-        using var cryptoStream = new CryptoStream(encryptedFileStream, tdes.CreateDecryptor(), CryptoStreamMode.Read);
-        cryptoStream.CopyTo(decryptedFileStream);
-    }
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using System.Security.Cryptography;
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 3DES文件加解密
+/// </summary>
+public static class TripleDES
+{
+    /// <summary>
+    /// 加密文件
+    /// </summary>
+    /// <param name="inputFile">待加密文件路径</param>
+    /// <param name="outputFile">加密后的文件路径</param>
+    /// <param name="password">密码 (24位长度)</param>
+    [Obsolete]
+    public static void EncryptFile(string inputFile, string outputFile, string password)
+    {
+        using var tdes = new TripleDESCryptoServiceProvider();
+        tdes.Mode = CipherMode.ECB;
+        tdes.Padding = PaddingMode.PKCS7;
+        tdes.Key = Encoding.UTF8.GetBytes(password);
+        using var inputFileStream = new FileStream(inputFile, FileMode.Open);
+        using var encryptedFileStream = new FileStream(outputFile, FileMode.Create);
+        using var cryptoStream = new CryptoStream(encryptedFileStream, tdes.CreateEncryptor(), CryptoStreamMode.Write);
+        inputFileStream.CopyTo(cryptoStream);
+    }
+
+    /// <summary>
+    /// 加密文件
+    /// </summary>
+    /// <param name="inputFile">加密的文件路径</param>
+    /// <param name="outputFile">解密后的文件路径</param>
+    /// <param name="password">密码 (24位长度)</param>
+    [Obsolete]
+    public static void DecryptFile(string inputFile, string outputFile, string password)
+    {
+        using var tdes = new TripleDESCryptoServiceProvider();
+        tdes.Mode = CipherMode.ECB;
+        tdes.Padding = PaddingMode.PKCS7;
+        tdes.Key = Encoding.UTF8.GetBytes(password);
+        using var encryptedFileStream = new FileStream(inputFile, FileMode.Open);
+        using var decryptedFileStream = new FileStream(outputFile, FileMode.Create);
+        using var cryptoStream = new CryptoStream(encryptedFileStream, tdes.CreateDecryptor(), CryptoStreamMode.Read);
+        cryptoStream.CopyTo(decryptedFileStream);
+    }
 }

+ 0 - 0
Admin.NET/Admin.NET.Core/Util/VerifyFileExtensionName.cs → Admin.NET/Admin.NET.Core/Utils/VerifyFileExtensionName.cs