文件内容 HalfHideHelper.cs 12 KB 11984 字符
using System.Collections;
using System.Reflection;

namespace PasteApeart.Helper
{
    /// <summary>
    /// 
    /// </summary>
    public class HalfHideHelper
    {

        #region 脱敏处理 跟级的Class一定要标记PasteHalfHide特性

        /// <summary>
        /// 对字段进行脱敏处理,注意[PasteHalfHide]如果没在Class标记,则使用默认值,就近原则!
        /// </summary>
        /// <param name="data"></param>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public static object PasteMaskData(object data, Type targetType)
        {
            if (data == null || targetType == null)
            {
                return data;
            }

            var classAttribute = targetType.GetCustomAttribute<PasteHalfHideAttribute>();
            if (classAttribute == null)
            {
                //跟级一定要有class的特性,这样可以避免无用扫描
                return data;
            }

            var properties = ReadTypeProperties(targetType);
            bool createNew = classAttribute.CreateNewObj;   // 唯一决策点,向下传

            if (data is IEnumerable list && data is not string)
            {
                if (createNew)
                {
                    var resultList = new List<object>();
                    foreach (var item in list)
                    {
                        if (item == null || !targetType.IsInstanceOfType(item))
                        {
                            resultList.Add(item);
                            continue;
                        }
                        resultList.Add(MaskNewSingleObject(item, targetType, properties, createNew, classAttribute));
                    }
                    return resultList;
                }
                else
                {
                    foreach (var item in list)
                    {
                        if (item == null || !targetType.IsInstanceOfType(item))
                        {
                            continue;
                        }
                        MaskSingleObject(item, targetType, properties, createNew, classAttribute);
                    }
                    return data;
                }
            }

            if (createNew)
            {
                return MaskNewSingleObject(data, targetType, properties, createNew, classAttribute);
            }
            MaskSingleObject(data, targetType, properties, createNew, classAttribute);
            return data;
        }

        /// <summary>
        /// 原地修改模式(createNew 恒 false)
        /// 字段特性就近优先;字段没标但类标了 -> 用类级;都没标 -> 跳过
        /// </summary>
        private static void MaskSingleObject(object obj, Type type, PropertyInfo[] properties, bool createNew, PasteHalfHideAttribute inputAttribute)
        {
            if (obj == null) return;

            var classAttr = type.GetCustomAttribute<PasteHalfHideAttribute>();
            if (classAttr == null)
            {
                classAttr = inputAttribute;
            }

            foreach (var property in properties)
            {
                if (!property.CanRead || !property.CanWrite) continue;

                var propAttr = property.GetCustomAttribute<PasteHalfHideAttribute>();
                var value = property.GetValue(obj);

                //bool shouldMask = propAttr != null || classAttr != null;
                if (propAttr==null) continue;

                // string
                if (property.PropertyType == typeof(string))
                {
                    var s = value as string;
                    if (string.IsNullOrEmpty(s)) continue;
                    var attr = propAttr ?? classAttr;   // 就近优先
                    property.SetValue(obj, Desensitize(s, attr));
                    continue;
                }

                // 值类型
                if (property.PropertyType.IsValueType || property.PropertyType == typeof(DateTime))
                    continue;

                // 集合
                if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
                {
                    if (value is not IEnumerable srcList) continue;
                    var elementType = GetEnumerableElementType(property.PropertyType);
                    if (elementType == null) continue;
                    var elementProps = ReadTypeProperties(elementType);
                    foreach (var item in srcList)
                    {
                        if (item == null || !elementType.IsInstanceOfType(item)) continue;
                        MaskSingleObject(item, elementType, elementProps, createNew, classAttr);
                    }
                    continue;
                }

                // 复杂对象
                if (value == null) continue;
                var childType = property.PropertyType;
                var childProps = ReadTypeProperties(childType);
                MaskSingleObject(value, childType, childProps, createNew, classAttr);
            }
        }

        /// <summary>
        /// 副本模式(createNew 恒 true)
        /// 不吞值:字段没标 -> 原样复制原值;字段标了 -> 处理
        /// 类没标不再提前 return 空对象
        /// </summary>
        private static object MaskNewSingleObject(object obj, Type type, PropertyInfo[] properties, bool createNew, PasteHalfHideAttribute inputAttribute)
        {
            if (obj == null) return null;

            var classAttr = type.GetCustomAttribute<PasteHalfHideAttribute>();
            if (classAttr == null)
            {
                classAttr = inputAttribute;
            }
            var result = Activator.CreateInstance(type);

            foreach (var property in properties)
            {
                if (!property.CanRead || !property.CanWrite) continue;

                var propAttr = property.GetCustomAttribute<PasteHalfHideAttribute>();
                var value = property.GetValue(obj);

                bool shouldMask = propAttr != null;

                // string
                if (property.PropertyType == typeof(string))
                {
                    var s = value as string;
                    if (!shouldMask || string.IsNullOrEmpty(s))
                    {
                        property.SetValue(result, s);   // 没标/空值:原样复制,不吞
                        continue;
                    }
                    var attr = propAttr ?? classAttr;
                    property.SetValue(result, Desensitize(s, attr));
                    continue;
                }

                // 值类型
                if (property.PropertyType.IsValueType || property.PropertyType == typeof(DateTime))
                {
                    property.SetValue(result, value);
                    continue;
                }

                // 集合
                if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
                {
                    if (value is not IEnumerable srcList)
                    {
                        property.SetValue(result, value);
                        continue;
                    }
                    var elementType = GetEnumerableElementType(property.PropertyType);
                    if (elementType == null)
                    {
                        property.SetValue(result, value);
                        continue;
                    }
                    if (!shouldMask)
                    {
                        // 没标:原样复制集合引用
                        property.SetValue(result, value);
                        continue;
                    }
                    var elementProps = ReadTypeProperties(elementType);
                    var newList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
                    foreach (var item in srcList)
                    {
                        if (item == null || !elementType.IsInstanceOfType(item))
                        {
                            newList.Add(item);
                            continue;
                        }
                        newList.Add(MaskNewSingleObject(item, elementType, elementProps, createNew, classAttr));
                    }
                    property.SetValue(result, newList);
                    continue;
                }

                // 复杂对象
                if (!shouldMask || value == null)
                {
                    property.SetValue(result, value);   // 没标/空值:原样复制,不吞
                    continue;
                }

                var childType = property.PropertyType;
                var childProps = ReadTypeProperties(childType);
                property.SetValue(result, MaskNewSingleObject(value, childType, childProps, createNew, classAttr));
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static Type GetEnumerableElementType(Type t)
        {
            if (t.IsArray) return t.GetElementType();
            if (t.IsGenericType && t.GetGenericArguments().Length == 1)
                return t.GetGenericArguments()[0];
            var iface = t.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
            return iface?.GetGenericArguments()[0];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="attr"></param>
        /// <returns></returns>
        private static string Desensitize(string value, PasteHalfHideAttribute attr)
        {
            if (string.IsNullOrEmpty(value)) return value;
            return DesensitizeCustom(value, attr.StartLength, attr.EndLength, attr.MaskChar);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="startLength"></param>
        /// <param name="endLength"></param>
        /// <param name="maskChar"></param>
        /// <returns></returns>
        private static string DesensitizeCustom(string value, int startLength, int endLength, char maskChar)
        {
            if (string.IsNullOrEmpty(value)) return value;
            if (startLength < 0) startLength = 0;
            if (endLength < 0) endLength = 0;
            if (value.Length <= startLength + endLength) return value;

            return value.Substring(0, startLength) +
                   new string(maskChar, value.Length - startLength - endLength) +
                   value.Substring(value.Length - endLength);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="searchType"></param>
        /// <returns></returns>
        private static PropertyInfo[] ReadTypeProperties(Type searchType)
        {

            var typeName = searchType.FullName;

            var allProperties = searchType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            // 辅助方法:计算类型的继承深度
            int GetInheritanceDepth(Type type)
            {
                int depth = 0;
                while (type.BaseType != null && type.BaseType != typeof(object))
                {
                    depth++;
                    type = type.BaseType;
                }
                return depth;
            }

            // 按继承深度从远到近排序
            var sortedProperties = allProperties
                .OrderBy(p => GetInheritanceDepth(p.DeclaringType))
                .ToArray();

            var hasDuplicates = sortedProperties.Length != sortedProperties.Select(p => p.Name).Distinct().Count();
            if (!hasDuplicates)
            {
                return sortedProperties;
            }

            // 去重:保留最近的(后面的覆盖前面的)
            var propertyDict = new Dictionary<string, PropertyInfo>();
            foreach (var prop in sortedProperties)
            {
                propertyDict[prop.Name] = prop;
            }

            return propertyDict.Values
                .OrderBy(p => GetInheritanceDepth(p.DeclaringType))
                .ToArray();

        }

    }
}
​ ​