// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 // // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! using System.Text.Json; namespace Admin.NET.Core; /// /// 对象拓展方法 /// public static partial class ObjectExtension { /// /// 类型属性列表映射表 /// private static readonly ConcurrentDictionary PropertyCache = new(); /// /// 脱敏特性缓存映射表 /// private static readonly ConcurrentDictionary AttributeCache = new(); /// /// 判断类型是否实现某个泛型 /// /// 类型 /// 泛型类型 /// bool public static bool HasImplementedRawGeneric(this Type type, Type generic) { // 检查接口类型 var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType); if (isTheRawGenericType) return true; // 检查类型 while (type != null && type != typeof(object)) { isTheRawGenericType = IsTheRawGenericType(type); if (isTheRawGenericType) return true; type = type.BaseType; } return false; // 判断逻辑 bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); } /// /// 将字典转化为QueryString格式 /// /// /// /// public static string ToQueryString(this Dictionary dict, bool urlEncode = true) { return string.Join("&", dict.Select(p => $"{(urlEncode ? p.Key?.UrlEncode() : p.Key)}={(urlEncode ? p.Value?.UrlEncode() : p.Value)}")); } /// /// 根据[CustomJsonProperty]特性转换属性为Query参数 /// /// /// /// public static string ToCustomJsonPropertyQueryString(this object obj, bool urlEncode = true) { var properties = obj.GetType().GetProperties(); return string.Join("&", properties.Select(p => { var name = p.GetCustomAttribute()?.Name ?? p.Name.ToFirstLetterLowerCase(); var val = p.GetValue(obj)?.ToString(); if (!urlEncode) return $"{name}={val}"; val = val?.UrlEncode(); name = name?.UrlEncode(); return $"{name}={val}"; })); } /// /// 将字符串URL编码 /// /// /// public static string UrlEncode(this string str) { return string.IsNullOrEmpty(str) ? "" : System.Uri.EscapeDataString(str); } /// /// 对象序列化成Json字符串 /// /// /// public static string ToJson(this object obj) { return JSON.GetJsonSerializer().Serialize(obj); } /// /// 将 string 时间日期格式转换成字符串 如 {yyyy} => 2024 /// /// /// public static string ParseToDateTimeForRep(this string str) { if (string.IsNullOrWhiteSpace(str)) str = $"{DateTime.Now.Year}/{DateTime.Now.Month}/{DateTime.Now.Day}"; var date = DateTime.Now; var reg = new Regex(@"(\{.+?})"); var match = reg.Matches(str); match.ToList().ForEach(u => { var temp = date.ToString(u.ToString().Substring(1, u.Length - 2)); str = str.Replace(u.ToString(), temp); }); return str; } /// /// 是否有值 /// /// /// public static bool IsNullOrEmpty(this object obj) { return obj == null || string.IsNullOrEmpty(obj.ToString()); } /// /// 字符串掩码 /// /// 字符串 /// 掩码符 /// public static string Mask(this string str, char mask = '*') { if (string.IsNullOrWhiteSpace(str?.Trim())) return str; str = str.Trim(); var masks = mask.ToString().PadLeft(4, mask); return str.Length switch { >= 11 => Regex.Replace(str, "(.{3}).*(.{4})", $"$1{masks}$2"), 10 => Regex.Replace(str, "(.{3}).*(.{3})", $"$1{masks}$2"), 9 => Regex.Replace(str, "(.{2}).*(.{3})", $"$1{masks}$2"), 8 => Regex.Replace(str, "(.{2}).*(.{2})", $"$1{masks}$2"), 7 => Regex.Replace(str, "(.{1}).*(.{2})", $"$1{masks}$2"), 6 => Regex.Replace(str, "(.{1}).*(.{1})", $"$1{masks}$2"), _ => Regex.Replace(str, "(.{1}).*", $"$1{masks}") }; } /// /// 姓名掩码 /// /// 姓名 /// 掩码符 /// public static string MaskName(this string name, char mask = '*') { if (string.IsNullOrEmpty(name) || name.Length < 2) return name; return string.Concat(name.AsSpan(0, 1), mask.ToString(), name.AsSpan(2)); } /// /// 身份证号掩码 /// /// 身份证号 /// 掩码符 /// public static string MaskIdCard(this string idCard, char mask = '*') { if (!idCard.TryValidate(ValidationTypes.IDCard).IsValid) return idCard; var masks = mask.ToString().PadLeft(8, mask); return Regex.Replace(idCard, @"^(.{6})(.*)(.{4})$", $"$1{masks}$3"); } /// /// 邮箱掩码 /// /// 邮箱 /// 掩码符 /// public static string MaskEmail(this string email, char mask = '*') { if (!email.TryValidate(ValidationTypes.EmailAddress).IsValid) return email; var pos = email.IndexOf('@'); return Mask(email[..pos], mask) + email[pos..]; } /// /// 将字符串转为值类型,若没有得到或者错误返回为空 /// /// 指定值类型 /// 传入字符串 /// 可空值 public static T? ParseTo(this string str) where T : struct { try { if (!string.IsNullOrWhiteSpace(str)) { MethodInfo method = typeof(T).GetMethod("Parse", [typeof(string)]); if (method != null) { T result = (T)method.Invoke(null, [str]); return result; } } } catch { } return null; } /// /// 将字符串转为值类型,若没有得到或者错误返回为空 /// /// 传入字符串 /// 目标类型 /// 可空值 public static object ParseTo(this string str, Type type) { try { if (type.Name == "String") return str; if (!string.IsNullOrWhiteSpace(str)) { var _type = type; if (type.Name.StartsWith("Nullable")) _type = type.GetGenericArguments()[0]; MethodInfo method = _type.GetMethod("Parse", [typeof(string)]); if (method != null) return method.Invoke(null, [str]); } } catch { } return null; } /// /// 将一个对象属性值赋给另一个指定对象属性, 只复制相同属性的 /// /// 原数据对象 /// 目标数据对象 /// 属性集,键为原属性,值为目标属性 /// 属性集,目标不修改的属性 public static void CopyTo(object src, object target, Dictionary changeProperties = null, string[] unChangeProperties = null) { if (src == null || target == null) throw new ArgumentException("src == null || target == null "); var SourceType = src.GetType(); var TargetType = target.GetType(); if (changeProperties == null || changeProperties.Count == 0) { var fields = TargetType.GetProperties(); changeProperties = fields.Select(m => m.Name).ToDictionary(m => m); } if (unChangeProperties == null || unChangeProperties.Length == 0) { foreach (var item in changeProperties) { var srcProperty = SourceType.GetProperty(item.Key); if (srcProperty != null) { var sourceVal = srcProperty.GetValue(src, null); var tarProperty = TargetType.GetProperty(item.Value); tarProperty?.SetValue(target, sourceVal, null); } } } else { foreach (var item in changeProperties) { if (!unChangeProperties.Any(m => m == item.Value)) { var srcProperty = SourceType.GetProperty(item.Key); if (srcProperty != null) { var sourceVal = srcProperty.GetValue(src, null); var tarProperty = TargetType.GetProperty(item.Value); tarProperty?.SetValue(target, sourceVal, null); } } } } } /// /// 对象深复制 /// /// 深复制源对象 /// 对象 /// public static T DeepCopy(this T obj) { var json = JSON.Serialize(obj); return JSON.Deserialize(json); } /// /// 对带有特性字段进行脱敏处理 /// public static T MaskSensitiveData(this T obj) where T : class { if (obj == null) return null; var type = typeof(T); // 获取或缓存属性集合 var properties = PropertyCache.GetOrAdd(type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.PropertyType == typeof(string) && p.GetCustomAttribute() != null) .ToArray()); // 并行处理可写属性 Parallel.ForEach(properties, prop => { if (!prop.CanWrite) return; // 获取或缓存特性 var maskAttr = AttributeCache.GetOrAdd(prop, p => p.GetCustomAttribute()); if (maskAttr == null) return; // 处理非空字符串 if (prop.GetValue(obj) is string { Length: > 0 } value) { prop.SetValue(obj, maskAttr.Mask(value)); } }); return obj; } /// /// 对象转为自定义JSON字符串 /// public static string ToCustomJson(this T obj, JsonSerializerOptions options = null) => CustomJsonHelper.Serialize(obj, options); /// /// 自定义JSON字符串转为对象 /// public static T FromCustomJson(this string json, JsonSerializerOptions options = null) => CustomJsonHelper.Deserialize(json, options); }