| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941 | using MathNet.Numerics.Statistics;using Newtonsoft.Json;using Newtonsoft.Json.Linq;using OASystem.Domain.ViewModels.Groups;using System.Collections;using System.Globalization;using System.Reflection;using TinyPinyin;namespace OASystem.Infrastructure.Tools;/// <summary>/// 工具类/// </summary>public static class CommonFun{    public static string GUID => Guid.NewGuid().ToString("N");    public static bool IsNull(this string s)    {        return string.IsNullOrWhiteSpace(s);    }    public static bool NotNull(this string s)    {        return !string.IsNullOrWhiteSpace(s);    }    public static int GetRandom(int minNum, int maxNum)    {        var seed = BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0);        return new Random(seed).Next(minNum, maxNum);    }    public static string GetSerialNumber(string prefix = "")    {        return prefix + DateTime.Now.ToString("yyyyMMddHHmmssfff") + GetRandom(1000, 9999).ToString();    }    public static string ToJson(this object obj)    {        return System.Text.Json.JsonSerializer.Serialize(obj);    }    public static T ToObject<T>(this string json)    {        return System.Text.Json.JsonSerializer.Deserialize<T>(json);    }    public static object GetDefaultVal(string typename)    {        return typename switch        {            "Boolean" => false,            "DateTime" => default(DateTime),            "Date" => default(DateTime),            "Double" => 0.0,            "Single" => 0f,            "Int32" => 0,            "String" => string.Empty,            "Decimal" => 0m,            _ => null,        };    }    public static void CoverNull<T>(T model) where T : class    {        if (model == null)        {            return;        }        var typeFromHandle = typeof(T);        var properties = typeFromHandle.GetProperties();        var array = properties;        for (var i = 0; i < array.Length; i++)        {            var propertyInfo = array[i];            if (propertyInfo.GetValue(model, null) == null)            {                propertyInfo.SetValue(model, GetDefaultVal(propertyInfo.PropertyType.Name), null);            }        }    }    public static void CoverNull<T>(List<T> models) where T : class    {        if (models.Count == 0)        {            return;        }        foreach (var model in models)        {            CoverNull(model);        }    }    public static bool ToBool(this object thisValue, bool errorvalue = false)    {        if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out bool reval))        {            return reval;        }        return errorvalue;    }    #region 文件操作    public static FileInfo[] GetFiles(string directoryPath)    {        if (!IsExistDirectory(directoryPath))        {            throw new DirectoryNotFoundException();        }        var root = new DirectoryInfo(directoryPath);        return root.GetFiles();    }    public static bool IsExistDirectory(string directoryPath)    {        return Directory.Exists(directoryPath);    }    public static string ReadFile(string Path)    {        string s;        if (!File.Exists(Path))            s = "不存在相应的目录";        else        {            var f2 = new StreamReader(Path, Encoding.Default);            s = f2.ReadToEnd();            f2.Close();            f2.Dispose();        }        return s;    }    public static void FileMove(string OrignFile, string NewFile)    {        File.Move(OrignFile, NewFile);    }    public static void CreateDir(string dir)    {        if (dir.Length == 0) return;        if (!Directory.Exists(dir))            Directory.CreateDirectory(dir);    }    /// <summary>    /// 生成唯一存储文件名    /// </summary>    /// <param name="originalFileName"> 文件原始名称 </param>    /// <returns></returns>    public static string GenerateStoredFileName(string originalFileName)    {        var extension = Path.GetExtension(originalFileName);        var fileNameWithoutExt = Path.GetFileNameWithoutExtension(originalFileName);        var timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");        var randomStr = Guid.NewGuid().ToString("N").Substring(0, 8);        // 清理文件名中的非法字符        var safeFileName = Regex.Replace(fileNameWithoutExt, @"[^\w\.-]", "");        return $"{safeFileName}_{timestamp}_{randomStr}{extension}";    }    /// <summary>    /// 验证文件名称    /// </summary>    /// <param name="fileName"></param>    /// <returns></returns>    public static string ValidFileName(string fileName)    {        if (string.IsNullOrEmpty(fileName)) return Guid.NewGuid().ToString();        // 获取非法文件名字符        char[] invalidChars = Path.GetInvalidFileNameChars();        return new string(fileName.Where(c => !invalidChars.Contains(c)).ToArray());    }    /// <summary>    /// 文件后缀名签名    /// </summary>    public static Dictionary<string, List<byte[]>> FileSignature => new Dictionary<string, List<byte[]>>        {            { ".jpeg", new List<byte[]>                {                    new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },                    new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },                    new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },                }            },            { ".png", new List<byte[]>                {                    new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }                }            },            { ".pdf", new List<byte[]>                {                    new byte[] { 0x25, 0x50, 0x44, 0x46 }                }            },            { ".xls", new List<byte[]>                {                     new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }                 }             },            { ".xlsx", new List<byte[]>                 {                     new byte[] { 0x50, 0x4B, 0x03, 0x04 }                 }             }        };    #endregion    #region IP    /// <summary>    /// 是否为ip    /// </summary>    /// <param name="ip"></param>    /// <returns></returns>    public static bool IsIP(string ip)    {        return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");    }    public static string GetIP(HttpRequest request)    {        if (request == null) return "";        var ip = request.Headers["X-Real-IP"].FirstOrDefault();        if (ip.IsNull())        {            ip = request.Headers["X-Forwarded-For"].FirstOrDefault();        }        if (ip.IsNull())        {            ip = request.HttpContext?.Connection?.RemoteIpAddress?.ToString();        }        if (ip.IsNull() || !IsIP(ip))        {            ip = "127.0.0.1";        }        return ip;    }    /// <summary>    /// IP地址获取方法(serilog 单独处理)    /// </summary>    /// <param name="httpContext"></param>    /// <returns></returns>    public static string GetClientIpAddress(HttpContext httpContext)    {        // 1. 尝试从X-Forwarded-For获取(处理代理/负载均衡情况)        if (httpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor))        {            // X-Forwarded-For可能包含逗号分隔的IP列表(客户端,代理1,代理2,...)            var ip = forwardedFor.FirstOrDefault()?.Split(',').FirstOrDefault()?.Trim();            if (!string.IsNullOrEmpty(ip))                return ip;        }        // 2. 尝试从X-Real-IP获取        if (httpContext.Request.Headers.TryGetValue("X-Real-IP", out var realIp))        {            var ip = realIp.FirstOrDefault();            if (!string.IsNullOrEmpty(ip))                return ip;        }        // 3. 使用连接远程IP地址        var remoteIp = httpContext.Connection.RemoteIpAddress;        // 处理IPv6映射的IPv4地址        if (remoteIp != null && remoteIp.IsIPv4MappedToIPv6)        {            remoteIp = remoteIp.MapToIPv4();        }        return remoteIp?.ToString() ?? "unknown";    }    #endregion    #region UserAgent    /// <summary>    /// 分层检测策略实现操作系统识别    /// </summary>    /// <param name="userAgent"></param>    /// <returns></returns>    public static string DetectOS(string userAgent)    {        if (string.IsNullOrEmpty(userAgent)) return "Unknown";        // 移动端优先检测        if (IsMobile(userAgent))        {            if (userAgent.Contains("iPhone")) return ParseIOSVersion(userAgent);            if (userAgent.Contains("Android")) return ParseAndroidVersion(userAgent);            if (userAgent.Contains("Windows Phone")) return "Windows Mobile";        }        // PC端检测        if (userAgent.Contains("Windows NT")) return ParseWindowsVersion(userAgent);        if (userAgent.Contains("Macintosh")) return "macOS";        if (userAgent.Contains("Linux")) return "Linux";        return "Other";    }    /// <summary>    /// 移动设备判断    /// </summary>    /// <param name="userAgent"></param>    /// <returns></returns>    private static bool IsMobile(string userAgent)    {        string[] mobileKeywords = { "iPhone", "Android", "Windows Phone", "iPad", "iPod" };        return mobileKeywords.Any(key => userAgent.Contains(key));    }    /// <summary>    /// iOS版本解析    /// </summary>    /// <param name="userAgent"></param>    /// <returns></returns>    private static string ParseIOSVersion(string userAgent)    {        var match = Regex.Match(userAgent, @"iPhone OS (\d+_\d+)");        return match.Success ? $"iOS {match.Groups[1].Value.Replace('_', '.')}" : "iOS";    }    /// <summary>    ///  Android版本解析    /// </summary>    /// <param name="userAgent"></param>    /// <returns></returns>    private static string ParseAndroidVersion(string userAgent)    {        var match = Regex.Match(userAgent, @"Android (\d+)");        return match.Success ? $"Android {match.Groups[1].Value}" : "Android";    }    /// <summary>    /// Windows版本解析    /// </summary>    /// <param name="userAgent"></param>    /// <returns></returns>    private static string ParseWindowsVersion(string userAgent)    {        if (userAgent.Contains("Windows NT 10.0")) return "Windows 10/11";        if (userAgent.Contains("Windows NT 6.3")) return "Windows 8.1";        if (userAgent.Contains("Windows NT 6.2")) return "Windows 8";        return "Windows";    }    #endregion    #region 随机数    /// <summary>    /// 根据自定义随机包含的字符获取指定长度的随机字符    /// </summary>    /// <param name="length">随机字符长度</param>    /// <returns>随机字符</returns>    public static string GetRandomStr(int length)    {        string a = "ABCDEFGHJKLMNPQRSTUVWXYZ012356789";        StringBuilder sb = new StringBuilder();        for (int i = 0; i < length; i++)        {            sb.Append(a[new Random(Guid.NewGuid().GetHashCode()).Next(0, a.Length - 1)]);        }        return sb.ToString();    }    /// <summary>    /// 根据自定义随机包含的字符获取指定长度的随机字符    /// </summary>    /// <param name="length">随机字符长度</param>    /// <returns>随机字符</returns>    public static string GetRandomAllStr(int length)    {        string a = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz012356789";        StringBuilder sb = new StringBuilder();        for (int i = 0; i < length; i++)        {            sb.Append(a[new Random(Guid.NewGuid().GetHashCode()).Next(0, a.Length - 1)]);        }        return sb.ToString();    }    /// <summary>    /// 根据自定义随机包含的字符获取指定长度的随机字母(含大小写)    /// </summary>    /// <param name="length">随机字符长度</param>    /// <returns>随机字符</returns>    public static string GetRandomLetter(int length)    {        string a = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz";        StringBuilder sb = new StringBuilder();        for (int i = 0; i < length; i++)        {            sb.Append(a[new Random(Guid.NewGuid().GetHashCode()).Next(0, a.Length - 1)]);        }        return sb.ToString();    }    /// <summary>    /// 生成不重复随机数字    /// 操作者:037    /// 2021-07-26 15:39    /// </summary>    /// <param name="CodeCount">输入字符串长度</param>    /// <returns>字符串</returns>    public static string GetRandomNumber(int len)    {        string allChar = "0,1,2,3,4,5,6,7,8,9";        string[] allCharArray = allChar.Split(',');        string RandomCode = "";        int temp = -1;        Random rand = new Random();        for (int i = 0; i < len; i++)        {            if (temp != -1)            {                rand = new Random(temp * i * ((int)DateTime.Now.Ticks));            }            int t = rand.Next(allCharArray.Length - 1);            while (temp == t)            {                t = rand.Next(allCharArray.Length - 1);            }            temp = t;            RandomCode += allCharArray[t];        }        return RandomCode;    }    #endregion    #region decimal 截取    /// <summary>    ///     /// </summary>    /// <param name="d"></param>    /// <param name="n"></param>    /// <returns></returns>    public static decimal CutDecimalWithN(decimal d, int n)    {        string strDecimal = d.ToString();        int index = strDecimal.IndexOf(".");        if (index == -1 || strDecimal.Length < index + n + 1)        {            strDecimal = string.Format("{0:F" + n + "}", d);        }        else        {            int length = index;            if (n != 0)            {                length = index + n + 1;            }            strDecimal = strDecimal.Substring(0, length);        }        return Decimal.Parse(strDecimal);    }    /// <summary>    /// decimal 保留小数,不四舍五入    /// </summary>    /// <param name="value"></param>    /// <param name="decimalPlaces"></param>    /// <returns></returns>    public static decimal TruncDecimals(this decimal value, int decimalPlaces)    {        decimal scaleFactor = (decimal)Math.Pow(10, decimalPlaces);        var truncated = Math.Truncate(scaleFactor * value) / scaleFactor;        // 检查是否需要补零        if (GetDecimalDigits(value) < decimalPlaces)        {            string format = "0." + new string('0', decimalPlaces);            truncated = decimal.Parse(truncated.ToString(format));        }        return truncated;    }    private static int GetDecimalDigits(decimal number)    {        string[] parts = number.ToString().Split('.');        return parts.Length > 1 ? parts[1].Length : 0;    }    #endregion    #region decimal 保留两位小数    /// <summary>    /// decimal 保留两位小数 不四舍五入    /// </summary>    /// <param name="number"></param>    /// <returns></returns>    public static decimal DecimalsKeepTwo(this decimal myDecimal)    {        var subDecimal = Math.Floor(myDecimal * 100) / 100;//保留两位小数,直接截取        return subDecimal;    }    #endregion    #region 团组模块 - 汇率相关存储解析    /// <summary>    /// 团组模块 - 汇率相关 To List    /// </summary>    /// <param name="rateStr"></param>    /// <returns></returns>    public static List<CurrencyInfo> GetCurrencyChinaToList(string? rateStr)    {        List<CurrencyInfo> currencyInfos = new List<CurrencyInfo>();        if (string.IsNullOrEmpty(rateStr)) return currencyInfos;        if (rateStr.Contains("|"))        {            string[] currencyArr = rateStr.Split("|");                        foreach (string currency in currencyArr)            {                if (!string.IsNullOrEmpty(currency))                {                    string[] currency1 = currency.Split(":");                    string[] currency2 = currency1[0].Split("(");                    CurrencyInfo rateInfo = new CurrencyInfo()                    {                        CurrencyCode = currency2[1].Replace(")", "").TrimEnd(),                        CurrencyName = currency2[0],                        Rate = decimal.Parse(currency1[1]),                    };                    currencyInfos.Add(rateInfo);                }            }        }        return currencyInfos;    }    /// <summary>    /// 团组模块 - 汇率相关存储解析 To String    /// </summary>    /// <param name="rates"></param>    /// <returns></returns>    public static string GetCurrencyChinaToString(List<CurrencyInfo>? rates)    {        string rateStr = string.Empty;        if (rates == null) return rateStr;        if (rates.Count <= 0) return rateStr;        if (rates.Count == 1)        {            var rate = rates[0];            return string.Format("{0}({1}):{2}|", rate.CurrencyName, rate.CurrencyCode, rate.Rate);        }        foreach (CurrencyInfo rate in rates)        {            //存储方式: 美元(USD):6.2350|.......|墨西哥比索(MXN):1.0000            rateStr += string.Format("{0}({1}):{2}|", rate.CurrencyName, rate.CurrencyCode, rate.Rate);        }        if (rateStr.Length > 0)        {            rateStr = rateStr.Substring(0, rateStr.Length - 1);        }        return rateStr;    }    #endregion    #region 验证身份证号码    /// <summary>    /// 正则表达式    /// 验证身份证号码    /// </summary>    /// <param name="idNumber"></param>    /// <returns></returns>    public static bool IsValidChineseId(this string idNumber)    {        string pattern = @"^[1-9]\d{5}(18|19|20|21|22)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|[Xx])$";        Regex regex = new Regex(pattern);        return regex.IsMatch(idNumber);    }    /// <summary>    /// 通过身份证提取生日    /// </summary>    /// <param name="identityCard"></param>    /// <returns></returns>    public static DateTime? GetBirthDateFromIdentityCard(string identityCard)    {        // 身份证号码正则表达式验证,支持18位身份证号码        if (!Regex.IsMatch(identityCard, @"^\d{17}(\d|X|x)$"))        {            return null;        }        // 获取出生日期(8位数字)        string birthDateString = identityCard.Substring(6, 8);        // 尝试将出生日期字符串转换为DateTime类型        if (DateTime.TryParse(birthDateString, out DateTime birthDate))        {            return birthDate;        }        return null;    }    /// <summary>    /// 通过身份证判断性别    /// </summary>    /// <param name="idNumber"></param>    /// <returns>0 男1 女 -1 未设置</returns>    /// <exception cref="ArgumentException"></exception>    public static int GetGenderFromIdentityCard(string idNumber)    {        if (string.IsNullOrEmpty(idNumber) || idNumber.Length != 18)            return -1;        char genderChar = idNumber[16];        return int.Parse(genderChar.ToString()) % 2 == 0 ? 1 : 0;    }    #endregion    #region string格式日期格式化    /// <summary>    /// string格式日期格式化    /// </summary>    /// <param name="dateStr"></param>    /// <param name="format">    /// 格式化标准    /// yyyy-MM-dd  yyyy/MM/dd    /// </param>    /// <returns></returns>    public static string DateFormat(this string dateStr, string format)    {        if (!string.IsNullOrEmpty(dateStr))        {            if (!string.IsNullOrEmpty(format))            {                DateTime result;                if (DateTime.TryParse(dateStr, out result))                {                    return result.ToString(format);                }            }        }        return "";    }    #endregion    /// <summary>    /// List to DataTable    /// 集合转换成datatable    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="aIList"></param>    /// <returns></returns>    public static DataTable GetDataTableFromIList<T>(List<T> aIList)    {        DataTable _returnTable = new DataTable();        if (aIList != null && aIList.Count > 0)        {            object _baseObj = aIList[0];            Type objectType = _baseObj.GetType();            PropertyInfo[] properties = objectType.GetProperties();            DataColumn _col;            foreach (PropertyInfo property in properties)            {                _col = new DataColumn();                _col.ColumnName = (string)property.Name;                _col.DataType = property.PropertyType;                _returnTable.Columns.Add(_col);            }            //Adds the rows to the table            DataRow _row;            foreach (object objItem in aIList)            {                _row = _returnTable.NewRow();                foreach (PropertyInfo property in properties)                {                    _row[property.Name] = property.GetValue(objItem, null);                }                _returnTable.Rows.Add(_row);            }        }        return _returnTable;    }    /// <summary>    /// List to DataTable    /// 集合转换成datatable    /// </summary>    public static DataTable ToDataTableArray(IList list)    {        DataTable result = new DataTable();        if (list.Count > 0)        {            PropertyInfo[] propertys = list[0].GetType().GetProperties();            foreach (PropertyInfo pi in propertys)            {                try                {                    result.Columns.Add(pi.Name, pi.PropertyType);                }                catch (Exception ex)                {                    Console.WriteLine($"{pi.Name}:{ex.Message}");                }            }            for (int i = 0; i < list.Count; i++)            {                ArrayList tempList = new ArrayList();                foreach (PropertyInfo pi in propertys)                {                    object obj = pi.GetValue(list[i], null);                    tempList.Add(obj);                }                object?[] array = tempList.ToArray();                result.LoadDataRow(array, true);            }        }        return result;    }    /// <summary>    ///获取指定月份起止日期    /// </summary>    /// <param name="year"></param>    /// <param name="month"></param>    /// <returns></returns>    public static (DateTime StartDate, DateTime EndDate) GetMonthStartAndEndDates(int year, int month)    {        var calendar = new GregorianCalendar();        var daysInMonth = calendar.GetDaysInMonth(year, month);        var startDate = new DateTime(year, month, 1);        var endDate = new DateTime(year, month, daysInMonth);        return (startDate, endDate);    }    /// <summary>    /// 验证json字符串是否合法    /// </summary>    /// <param name="jsonString"></param>    /// <returns></returns>    public static bool IsValidJson(string jsonString)    {        try        {            JToken.Parse(jsonString);            return true;        }        catch (JsonReaderException)        {            return false;        }    }    /// <summary>    /// 常见的双字姓氏列表    /// </summary>    private static readonly HashSet<string> commonDoubleSurnames = new HashSet<string>    {        "欧阳", "司马", "上官", "夏侯", "诸葛", "东方", "赫连", "皇甫", "尉迟", "公羊",        "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠", "公孙", "仲孙",        "轩辕", "令狐", "钟离", "宇文", "长孙", "慕容", "鲜于", "闾丘", "司徒", "司空",        "亓官", "司寇", "仉督", "子车", "颛孙", "端木", "巫马", "公西", "漆雕", "乐正",        "壤驷", "公良", "拓跋", "夹谷", "宰父", "谷梁", "晋楚", "闫法", "汝鄢", "涂钦",        "段干", "百里", "东郭", "南门", "呼延", "归海", "羊舌", "微生", "岳帅", "缑亢",        "况后", "有琴", "梁丘", "左丘", "东门", "西门", "商牟", "佘佴", "伯赏", "南宫"    };    /// <summary>    /// 中文名字取姓氏和名字    /// </summary>    /// <param name="fullName"></param>    /// <returns>姓:lastName 名:firstName</returns>    public static (string lastName, string firstName) GetLastNameAndFirstName(string fullName)    {        if (string.IsNullOrEmpty(fullName) || fullName.Length < 2) return ("", "");        // 尝试匹配双字姓氏        if (fullName.Length > 2)        {            string potentialDoubleSurname = fullName.Substring(0, 2);            if (commonDoubleSurnames.Contains(potentialDoubleSurname))            {                string lastName = potentialDoubleSurname;                string firstName = fullName.Substring(2);                return (lastName, firstName);            }        }        // 默认单字姓氏        string singleSurname = fullName.Substring(0, 1);        string remainingName = fullName.Substring(1);        return (singleSurname, remainingName);    }    /// <summary>    /// 中文名字转拼音    /// </summary>    /// <param name="chineseName"></param>    /// <returns></returns>    /// <exception cref="ArgumentException"></exception>    public static string ConvertToPinyin(string chineseName)    {        if (string.IsNullOrEmpty(chineseName)) return "";        return PinyinHelper.GetPinyin(chineseName, "").ToUpper();    }    #region stringBuilder 扩展    /// <summary>    /// StringBuilder 验证是否有值    /// </summary>    /// <param name="sb"></param>    /// <returns></returns>    public static bool HasValue(this StringBuilder sb)    {        return sb != null && sb.Length > 0;    }    #endregion    /// <summary>    /// 安全获取列表指定索引的值    /// </summary>    public static string? GetValueOrDefault(this List<string> list, int index)    {        if (list == null)            throw new ArgumentNullException(nameof(list));        return index >= 0 && index < list.Count ? list[index] : null;    }    #region 姓名 取姓和名    /// <summary>    /// 取姓    /// </summary>    public static string GetLastName(this string fullName)    {        if (string.IsNullOrWhiteSpace(fullName)) return string.Empty;        // 1. 英文/拼音:有逗号        var comma = fullName.IndexOf(',');        if (comma >= 0) return fullName[..comma].Trim();        // 2. 英文/拼音:无逗号,按最后一个空格切        var lastSpace = fullName.LastIndexOf(' ');        if (lastSpace >= 0) return fullName[..lastSpace].Trim();        // 3. 中文:默认首字为姓        return fullName.Length switch        {            0 => string.Empty,            1 => fullName,            _ => fullName[..1]   // 复姓可改成 ..2        };    }    /// <summary>    /// 取名(去掉姓)    /// </summary>    public static string GetFirstName(this string fullName)    {        if (string.IsNullOrWhiteSpace(fullName)) return string.Empty;        var comma = fullName.IndexOf(',');        if (comma >= 0) return fullName[(comma + 1)..].Trim();        var lastSpace = fullName.LastIndexOf(' ');        if (lastSpace >= 0) return fullName[(lastSpace + 1)..].Trim();        // 中文:去掉首字        return fullName.Length <= 1 ? string.Empty : fullName[1..];    }    #endregion    /// <summary>    /// 从地址字符串取出省份和城市    /// </summary>    public static (string province, string city) SplitProvinceCity(string src)    {        if (string.IsNullOrWhiteSpace(src)) return ("", "");        ReadOnlySpan<char> s = src.AsSpan();        // 1. 省        int pEnd = s.IndexOf("省");        if (pEnd < 0) pEnd = s.IndexOf("自治区");        string province = pEnd < 0 ? "" : s[..pEnd].ToString();        // 2. 市        int cStart = pEnd + (pEnd >= 0 ? (province.EndsWith("自治区") ? 3 : 1) : 0);        int cEnd = s[cStart..].IndexOf("市");        string city = cEnd < 0 ? "" : s.Slice(cStart, cEnd).ToString();        return (province, city);    }}
 |