using cylsg.utility.Extend; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Net.Http.Json; using System.Reflection; using System.Runtime.Loader; using System.Security.Cryptography; using System.Text; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Threading.Tasks; using static cylsg.utility.untilityModels; namespace cylsg.utility { /// /// 通用帮助类 /// public static class CommonHelper { #region 判断字符串是否为手机号码 /// /// 判断字符串是否为手机号码 /// /// /// public static bool IsMobile(string mobilePhoneNumber) { if (mobilePhoneNumber.Length < 11) { return false; } //电信手机号码正则 string dianxin = @"^1[345789][01379]\d{8}$"; Regex regexDx = new Regex(dianxin); //联通手机号码正则 string liantong = @"^1[345678][01256]\d{8}$"; Regex regexLt = new Regex(liantong); //移动手机号码正则 string yidong = @"^1[345789][0123456789]\d{8}$"; Regex regexYd = new Regex(yidong); if (regexDx.IsMatch(mobilePhoneNumber) || regexLt.IsMatch(mobilePhoneNumber) || regexYd.IsMatch(mobilePhoneNumber)) { return true; } else { return false; } } #endregion #region 检测是否符合email格式 /// /// 检测是否符合email格式 /// /// 要判断的email字符串 /// 判断结果 public static bool IsValidEmail(string strEmail) { return Regex.IsMatch(strEmail, @"^[\w\.]+([-]\w+)*@[A-Za-z0-9-_]+[\.][A-Za-z0-9-_]"); } public static bool IsValidDoEmail(string strEmail) { return Regex.IsMatch(strEmail, @"^@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"); } #endregion #region 检测是否是正确的Url /// /// 检测是否是正确的Url /// /// 要验证的Url /// 判断结果 public static bool IsUrl(string strUrl) { return Regex.IsMatch(strUrl, @"^(http|https)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{1,10}))(\:[0-9]+)*(/($|[a-zA-Z0-9\.\,\?\'\\\+&%\$#\=~_\-]+))*$"); } #endregion #region string 转int数组 public static int[] StringToIntArray(string str) { try { if (string.IsNullOrEmpty(str)) return new int[0]; if (str.EndsWith(",")) { str = str.Remove(str.Length - 1, 1); } var idstrarr = str.Split(','); var idintarr = new int[idstrarr.Length]; for (int i = 0; i < idstrarr.Length; i++) { idintarr[i] = Convert.ToInt32(idstrarr[i]); } return idintarr; } catch { return new int[0]; } } #endregion #region String转数组 public static string[] StringToStringArray(string str) { try { if (string.IsNullOrEmpty(str)) return new string[0]; if (str.EndsWith(",")) str = str.Remove(str.Length - 1, 1); return str.Split(','); } catch { return new string[0]; } } #endregion #region String数组转Int数组 public static int[] StringArrAyToIntArray(string[] str) { try { int[] iNums = Array.ConvertAll(str, s => int.Parse(s)); return iNums; } catch { return new int[0]; } } #endregion #region string转Guid数组 public static Guid[] StringToGuidArray(string str) { try { if (string.IsNullOrEmpty(str)) return new Guid[0]; if (str.EndsWith(",")) str = str.Remove(str.Length - 1, 1); var strarr = str.Split(','); Guid[] guids = new Guid[strarr.Length]; for (int index = 0; index < strarr.Length; index++) { guids[index] = Guid.Parse(strarr[index]); } return guids; } catch { return new Guid[0]; } } #endregion #region 转MD5 /// /// 转MD5 /// /// /// public static string ToMd5(string str) { MD5 md5 = MD5.Create(); // 将字符串转换成字节数组 byte[] byteOld = Encoding.UTF8.GetBytes(str); // 调用加密方法 byte[] byteNew = md5.ComputeHash(byteOld); // 将加密结果转换为字符串 StringBuilder sb = new StringBuilder(); foreach (byte b in byteNew) { // 将字节转换成16进制表示的字符串, sb.Append(b.ToString("x2")); } // 返回加密的字符串 return sb.ToString(); } #endregion #region 获取32位md5加密 /// /// 通过创建哈希字符串适用于任何 MD5 哈希函数 (在任何平台) 上创建 32 个字符的十六进制格式哈希字符串 /// /// /// 32位md5加密字符串 public static string Md5For32(string source) { using (MD5 md5Hash = MD5.Create()) { byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(source)); StringBuilder sBuilder = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("x2")); } string hash = sBuilder.ToString(); return hash.ToUpper(); } } #endregion #region 获取16位md5加密 /// /// 获取16位md5加密 /// /// /// 16位md5加密字符串 public static string Md5For16(string source) { using (MD5 md5Hash = MD5.Create()) { byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(source)); //转换成字符串,并取9到25位 string sBuilder = BitConverter.ToString(data, 4, 8); //BitConverter转换出来的字符串会在每个字符中间产生一个分隔符,需要去除掉 sBuilder = sBuilder.Replace("-", ""); return sBuilder.ToUpper(); } } #endregion #region 返回当前的毫秒时间戳 /// /// 返回当前的毫秒时间戳 /// public static string Msectime() { long timeTicks = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000; return timeTicks.ToString(); } #endregion #region 剩余多久时间文字描述 /// /// 剩余多久时间 /// /// /// 文字描述 public static string GetRemainingTime(DateTime remainingTime) { TimeSpan timeSpan = remainingTime - DateTime.Now; var day = timeSpan.Days; var hours = timeSpan.Hours; var minute = timeSpan.Minutes; var seconds = timeSpan.Seconds; if (day > 0) { return day + "天" + hours + "小时" + minute + "分" + seconds + "秒"; } else { if (hours > 0) { return hours + "小时" + minute + "分" + seconds + "秒"; } else { return minute + "分" + seconds + "秒"; } } } #endregion #region 剩余多久时间返回时间类型 /// /// 剩余多久时间 /// /// /// 返回时间类型 public static void GetBackTime(DateTime remainingTime, out int day, out int hours, out int minute, out int seconds) { TimeSpan timeSpan = remainingTime - DateTime.Now; day = timeSpan.Days; hours = timeSpan.Hours; minute = timeSpan.Minutes; seconds = timeSpan.Seconds; } #endregion #region 计算时间戳剩余多久时间 /// /// 计算时间戳剩余多久时间 /// /// 提交时间(要是以前的时间) /// public static string TimeAgo(DateTime postTime) { //当前时间的时间戳 var nowtimes = ConvertTicks(DateTime.Now); //提交的时间戳 var posttimes = ConvertTicks(postTime); //相差时间戳 var counttime = nowtimes - posttimes; //进行时间转换 if (counttime <= 60) { return "刚刚"; } else if (counttime > 60 && counttime <= 120) { return "1分钟前"; } else if (counttime > 120 && counttime <= 180) { return "2分钟前"; } else if (counttime > 180 && counttime < 3600) { return Convert.ToInt32(counttime / 60) + "分钟前"; } else if (counttime >= 3600 && counttime < 3600 * 24) { return Convert.ToInt32(counttime / 3600) + "小时前"; } else if (counttime >= 3600 * 24 && counttime < 3600 * 24 * 2) { return "昨天"; } else if (counttime >= 3600 * 24 * 2 && counttime < 3600 * 24 * 3) { return "前天"; } else if (counttime >= 3600 * 24 * 3 && counttime <= 3600 * 24 * 7) { return Convert.ToInt32(counttime / (3600 * 24)) + "天前"; } else if (counttime >= 3600 * 24 * 7 && counttime <= 3600 * 24 * 30) { return Convert.ToInt32(counttime / (3600 * 24 * 7)) + "周前"; } else if (counttime >= 3600 * 24 * 30 && counttime <= 3600 * 24 * 365) { return Convert.ToInt32(counttime / (3600 * 24 * 30)) + "个月前"; } else if (counttime >= 3600 * 24 * 365) { return Convert.ToInt32(counttime / (3600 * 24 * 365)) + "年前"; } else { return ""; } } /// /// 时间转换为秒的时间戳 /// /// /// private static long ConvertTicks(DateTime time) { long currentTicks = time.Ticks; DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0); long currentMillis = (currentTicks - dtFrom.Ticks) / 10000000; //转换为秒为Ticks/10000000,转换为毫秒Ticks/10000 return currentMillis; } #endregion #region 清除HTML中指定样式 /// /// 清除HTML中指定样式 /// /// /// /// public static string ClearHtml(string content, string[] rule) { if (!rule.Any()) { return content; } foreach (var item in rule) { content = Regex.Replace(content, "/" + item + @"\s*=\s*\d+\s*/i", ""); content = Regex.Replace(content, "/" + item + @"\s*=\s*.+?[""]/i", ""); content = Regex.Replace(content, "/" + item + @"\s*:\s*\d+\s*px\s*;?/i", ""); } return content; } #endregion #region list随机排序方法 /// /// list随机排序方法 /// /// /// /// public static List RandomSortList(List ListT) { Random random = new Random(); List newList = new List(); foreach (T item in ListT) { newList.Insert(random.Next(newList.Count + 1), item); } return newList; } #endregion #region 截前后字符(串) /// /// 截前后字符(串) /// ///原字符串 ///要截掉的字符串 ///是否贪婪 /// public static string GetCaptureInterceptedText(string val, string str, bool all = false) { return Regex.Replace(val, @"(^(" + str + ")" + (all ? "*" : "") + "|(" + str + ")" + (all ? "*" : "") + "$)", ""); } #endregion #region 密码加密方法 /// /// 密码加密方法 /// /// 要加密的字符串 /// 时间组合 /// public static string EnPassword(string password, DateTime createTime) { var dtStr = createTime.ToString("yyyyMMddHHmmss"); var md5 = Md5For32(password); var enPwd = Md5For32(md5 + dtStr); return enPwd; } #endregion #region 获取现在是星期几 /// /// 获取现在是星期几 /// /// public static string GetWeek() { string week = string.Empty; switch (DateTime.Now.DayOfWeek) { case DayOfWeek.Monday: week = "周一"; break; case DayOfWeek.Tuesday: week = "周二"; break; case DayOfWeek.Wednesday: week = "周三"; break; case DayOfWeek.Thursday: week = "周四"; break; case DayOfWeek.Friday: week = "周五"; break; case DayOfWeek.Saturday: week = "周六"; break; case DayOfWeek.Sunday: week = "周日"; break; default: week = "N/A"; break; } return week; } #endregion #region UrlEncode (URL编码) /// /// UrlEncode (URL编码) /// /// /// public static string UrlEncode(string str) { StringBuilder sb = new StringBuilder(); byte[] byStr = Encoding.UTF8.GetBytes(str); //默认是System.Text.Encoding.Default.GetBytes(str) for (int i = 0; i < byStr.Length; i++) { sb.Append(@"%" + Convert.ToString(byStr[i], 16)); } return sb.ToString(); } #endregion #region 获取10位时间戳 /// /// 获取10位时间戳 /// /// public static long GetTimeStampByTotalSeconds() { TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds); } #endregion #region 获取13位时间戳 /// /// 获取13位时间戳 /// /// public static long GetTimeStampByTotalMilliseconds() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalMilliseconds); } /// /// 获取时间戳 /// /// /// public static int GetDateTimeStamp(DateTime dt) { DateTime dateStart = new DateTime(1970, 1, 1, 0, 0, 0); int timeStamp = Convert.ToInt32((dt.ToUniversalTime() - dateStart).TotalSeconds); return timeStamp; } #endregion #region 获取随机字符串 /// /// 获取随机字符串 /// /// public static string GetSerialNumber() { var str = string.Empty; Random rand = new Random(); var charsStr2 = new[] { 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; var charsLen2 = charsStr2.Length - 1; // shuffle($chars); str = ""; for (int i = 0; i < 16; i++) { str += charsStr2[rand.Next(0, charsLen2)]; } return str; } #endregion #region Sha1签名 /// /// Sha1签名 /// /// 内容 /// 编码 /// public static string Sha1Signature(string str, Encoding encoding = null) { if (encoding == null) encoding = Encoding.UTF8; var buffer = encoding.GetBytes(str); var data = SHA1.Create().ComputeHash(buffer); StringBuilder sub = new StringBuilder(); foreach (var t in data) { sub.Append(t.ToString("x2")); } return sub.ToString(); } #endregion /// /// 精确计算base64字符串文件大小(单位:B) /// param base64String /// return double 字节大小 /// /// /// public static double Base64FileSize(string base64String) { //检测是否含有base64,文件头) if (base64String.LastIndexOf(",", StringComparison.Ordinal) > -1) { base64String = base64String[(base64String.LastIndexOf(",", StringComparison.Ordinal) + 1)..]; } //获取base64字符串长度(不含data:audio/wav;base64,文件头) var size0 = base64String.Length; if (size0 <= 10) return size0 - (double)size0 / 8 * 2; //获取字符串的尾巴的最后10个字符,用于判断尾巴是否有等号,正常生成的base64文件'等号'不会超过4个 var tail = base64String[(size0 - 10)..]; //找到等号,把等号也去掉,(等号其实是空的意思,不能算在文件大小里面) int equalIndex = tail.IndexOf("=", StringComparison.Ordinal); if (equalIndex > 0) { size0 = size0 - (10 - equalIndex); } //计算后得到的文件流大小,单位为字节 return size0 - (double)size0 / 8 * 2; } /// /// 判断文件大小 /// /// /// /// /// public static bool CheckBase64Size(string base64, int size, string unit = "M") { // 上传文件的大小, 单位为字节. var len = Base64FileSize(base64); // 准备接收换算后文件大小的容器 double fileSize = unit.ToUpperInvariant() switch { "B" => len, "K" => (double)len / 1024, "M" => (double)len / 1048576, "G" => (double)len / 1073741824, _ => 0 }; // 如果上传文件大于限定的容量 return !(fileSize > size); } /// /// 10位时间戳 转化 /// /// /// public static long ConvertDateTimeToInt(DateTime time) { DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, 0)); long t = (time.Ticks - startTime.Ticks) / 10000000; //除10000000调整为10位 return t; } #region 反射相关 private static List? _allAssemblies = null; /// /// 获取所有程序目录下和常用的程序集 包括一些系统引用程序集 /// /// 当前工程下的程序集 public static List GetAllAssembly() { if (_allAssemblies == null) { _allAssemblies = new List(); string? path = null; string singlefile = null; try { path = Assembly.GetEntryAssembly()?.Location; } catch { } if (string.IsNullOrEmpty(path)) { singlefile = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; path = Path.GetDirectoryName(singlefile); } if (string.IsNullOrEmpty(path)) throw new Exception("获取程序目录出错"); var dir = new DirectoryInfo(Path.GetDirectoryName(path) ?? ""); var dlls = dir.GetFiles("*.dll", SearchOption.TopDirectoryOnly); string[] systemdll = new string[] { "Microsoft.", "System.", "Swashbuckle.", "ICSharpCode", "Newtonsoft.", "Oracle.", "Pomelo.", "SQLitePCLRaw.", "Aliyun.OSS", "BouncyCastle.", "FreeSql.", "Google.Protobuf.dll", "Humanizer.dll", "IdleBus.dll", "K4os.", "MySql.Data.", "Npgsql.", "NPOI.", "netstandard", "MySqlConnector", "VueCliMiddleware" }; var filtered = dlls.Where(x => systemdll.Any(y => x.Name.StartsWith(y)) == false); foreach (var dll in filtered) { try { AssemblyLoadContext.Default.LoadFromAssemblyPath(dll.FullName); } catch { } } var dlllist = AssemblyLoadContext.Default.Assemblies.Where(x => systemdll.Any(y => x.FullName.StartsWith(y)) == false).ToList(); _allAssemblies.AddRange(dlllist); } return _allAssemblies; } #endregion #region 枚举获取所有项 /// /// 获取枚举的所有项 /// /// /// public static List GetEnumItemsWithAttributes() where T : Enum { var result = new List(); foreach (var value in Enum.GetValues(typeof(T))) { var member = typeof(T).GetMember(value.ToString())[0]; var descriptionAttribute = member.GetCustomAttribute(); var item = new MenmItem { Key = value.ToString(), Value = (int)value, // 或者直接使用 value.ToString() 如果不需要转换为整数字符串 Description = descriptionAttribute?.Description ?? string.Empty }; result.Add(item); } return result; } /// /// 获取枚举所有的项,返回名称 值 和Description 描述 /// /// /// /// public static List EnumerateWithDescriptions(Type enumType) { if (!enumType.IsEnum) { throw new ArgumentException(" 这不是枚举"); } var result = new List(); foreach (var value in Enum.GetValues(enumType)) { var member = enumType.GetMember(value.ToString())[0]; var descriptionAttribute = member.GetCustomAttribute(); var item = new MenmItem { Key = value.ToString(), Value = (int)value, // 或者直接使用 value.ToString() 如果不需要转换为整数字符串 Description = descriptionAttribute?.Description ?? string.Empty }; result.Add(item); } return result; } /// /// 是否是系统类型 /// /// /// public static bool IsCustomType(Type type) { // 定义一个或多个基础命名空间列表,这些命名空间下的类型通常认为是系统类型 string[] frameworkNamespaces = { "System", "Microsoft", "mscorlib", "netstandard", "System.Private.CoreLib" }; // 获取类型所在的命名空间 string typeNameSpace = type.Namespace; // 检查该命名空间是否在基础命名空间列表中,如果不在,则认为是自定义类型 return frameworkNamespaces.Any(n => typeNameSpace.StartsWith(n, StringComparison.OrdinalIgnoreCase)); } #endregion /// /// 根据json字符穿处理 /// /// /// /// /// private static Expression> CreateWhereExpressionRecursive(string jsonString, ParameterExpression parameter) { var conditions = new List(); JObject jsonObject = JObject.Parse(jsonString); foreach (var property in jsonObject.Properties()) { var propertyName = property.Name; var propertyValue = property.Value.ToString(); if (propertyValue == "") //空值不处理 continue; var propertyInfo = typeof(T).GetProperty(propertyName); if (propertyInfo == null) { propertyInfo = typeof(T).GetProperty(propertyName.FirstToCapitalize()); if (propertyInfo == null) continue; // 如果属性不存在,跳过 } var propertyType = propertyInfo.PropertyType; var TypeName = propertyType.GenericTypeArguments.Length > 0 ? propertyType.GenericTypeArguments[0].Name + "?" : propertyType.Name; if (propertyType.IsClass && TypeName != "String") { // 递归处理嵌套模型 var nestedParameter = Expression.Parameter(propertyType, propertyName); var nestedJsonString = property.Value.ToString(); // 使用 MakeGenericType 动态创建泛型方法 var genericMethod = typeof(CommonHelper).GetMethod("CreateWhereExpressionRecursive", BindingFlags.Static | BindingFlags.NonPublic) .MakeGenericMethod(propertyType); // 调用泛型方法并获取返回的表达式 var nestedConditions = (Expression>)genericMethod.Invoke(null, new object[] { nestedJsonString, nestedParameter }); var propExpr = Expression.Property(parameter, propertyName); var lambda = Expression.Lambda(nestedConditions.Body, nestedParameter); var invoke = Expression.Invoke(lambda, propExpr); conditions.Add(invoke); } else if (propertyType == typeof(string)) { var propExpr = Expression.Property(parameter, propertyName); var constant = Expression.Constant(propertyValue); var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var containsExpression = Expression.Call(propExpr, containsMethod, constant); conditions.Add(containsExpression); } else if (TypeName.Contains( "DateTime")) //兼容DateTime? { var dateRange = propertyValue.Split('~'); if (dateRange.Length == 2) { var startDate = DateTime.Parse(dateRange[0]); var endDate = DateTime.Parse(dateRange[1]); var propExpr = Expression.Property(parameter, propertyName); var startConstant = Expression.Constant(startDate); var endConstant = Expression.Constant(endDate); if (TypeName == "DateTime?") // 将常量表达式转换为 Nullable { var startNullableConstant = Expression.Convert(startConstant, typeof(DateTime?)); var endNullableConstant = Expression.Convert(endConstant, typeof(DateTime?)); var greaterThanOrEqual = Expression.GreaterThanOrEqual(propExpr, startNullableConstant); var lessThanOrEqual = Expression.LessThanOrEqual(propExpr, endNullableConstant); var andExpression = Expression.AndAlso(greaterThanOrEqual, lessThanOrEqual); conditions.Add(andExpression); } else { var greaterThanOrEqual = Expression.GreaterThanOrEqual(propExpr, startConstant); var lessThanOrEqual = Expression.LessThanOrEqual(propExpr, endConstant); var andExpression = Expression.AndAlso(greaterThanOrEqual, lessThanOrEqual); conditions.Add(andExpression); } } else if (DateTime.TryParse(propertyValue, out var dateTime)) { var propExpr = Expression.Property(parameter, propertyName); var constant = Expression.Constant(dateTime); var equalExpression = Expression.Equal(propExpr, constant); conditions.Add(equalExpression); } } else { var propExpr = Expression.Property(parameter, propertyName); var constant = Expression.Constant(Convert.ChangeType(propertyValue, propertyType)); var equalExpression = Expression.Equal(propExpr, constant); conditions.Add(equalExpression); } } var body = conditions.Count > 1 ? conditions.Aggregate(Expression.AndAlso) : conditions.FirstOrDefault(); return Expression.Lambda>(body ?? Expression.Constant(true), parameter); } /// /// json格式化为表达式树 /// /// /// /// public static Expression> FormatWhereExpression(string jsonString) { var parameter = Expression.Parameter(typeof(T), "x"); return CreateWhereExpressionRecursive(jsonString, parameter); } /// /// 更具属性名称生成表达式树,如x.id /// /// /// /// public static Expression> FormatPropertyExpression(string propertyName) { // 获取参数表达式 var parameter = Expression.Parameter(typeof(T), "x"); // 获取属性表达式 var propertyAccess = Expression.PropertyOrField(parameter, propertyName); if (propertyAccess == null) { ///首字母转大写 propertyAccess = Expression.PropertyOrField(parameter, propertyName.FirstToCapitalize()); } // 创建 Lambda 表达式,并在返回值中进行类型转换 return Expression.Lambda>( Expression.Convert(propertyAccess, typeof(object)), parameter); } } /// /// 枚举项属性 /// [Description("枚举项属性")] public class MenmItem { /// /// 枚举key /// [Description("枚举key")] public string? Key { get; set; } /// /// 枚举值 /// [Description("枚举值")] public int Value { get; set; } /// /// 枚举描述 /// [Description("枚举项属性")] public string? Description { get; set; } } }