A:Common配置

This commit is contained in:
jimmy committed 2025-11-13 19:12:19 +08:00
1 parent 61e769a0f6
commit c8fde9759f
8 files changed
+3742 -703

No files matched your search

+515
View File
@@ -0,0 +1,515 @@
using System.Text.RegularExpressions;
using Common.Results;
namespace Common.Utils;
/// <summary>
/// 验证工具类
/// 提供常见数据验证功能,并与 Result 模式集成
/// </summary>
public static class ValidationHelper
{
#region 邮箱验证
private static readonly Regex EmailRegex = new(
@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
RegexOptions.Compiled | RegexOptions.IgnoreCase
);
/// <summary>
/// 验证邮箱地址格式
/// </summary>
/// <param name="email">邮箱地址</param>
/// <returns>是否有效</returns>
public static bool IsValidEmail(string? email)
{
if (string.IsNullOrWhiteSpace(email))
return false;
return EmailRegex.IsMatch(email);
}
/// <summary>
/// 验证邮箱地址格式(Result 模式)
/// </summary>
/// <param name="email">邮箱地址</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<string> ValidateEmail(string? email, long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(email))
return Result.Failure<string>(errorCode, "邮箱地址不能为空");
if (!EmailRegex.IsMatch(email))
return Result.Failure<string>(errorCode, "邮箱地址格式无效");
return Result.Success(email);
}
#endregion
#region 手机号验证
// 中国大陆手机号:1 开头,第二位是 3-9,共 11 位
private static readonly Regex ChinaMobileRegex = new(
@"^1[3-9]\d{9}$",
RegexOptions.Compiled
);
/// <summary>
/// 验证中国大陆手机号格式
/// </summary>
/// <param name="mobile">手机号</param>
/// <returns>是否有效</returns>
public static bool IsValidChinaMobile(string? mobile)
{
if (string.IsNullOrWhiteSpace(mobile))
return false;
return ChinaMobileRegex.IsMatch(mobile);
}
/// <summary>
/// 验证中国大陆手机号格式(Result 模式)
/// </summary>
/// <param name="mobile">手机号</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<string> ValidateChinaMobile(string? mobile, long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(mobile))
return Result.Failure<string>(errorCode, "手机号不能为空");
if (!ChinaMobileRegex.IsMatch(mobile))
return Result.Failure<string>(errorCode, "手机号格式无效(应为11位数字,1开头)");
return Result.Success(mobile);
}
/// <summary>
/// 验证国际手机号格式(包含国际区号)
/// </summary>
/// <param name="mobile">手机号(格式:+区号-号码,如 +86-13812345678)</param>
/// <returns>是否有效</returns>
public static bool IsValidInternationalMobile(string? mobile)
{
if (string.IsNullOrWhiteSpace(mobile))
return false;
// 匹配 +区号-号码 或 +区号号码 格式
var regex = new Regex(@"^\+\d{1,3}[-\s]?\d{6,14}$");
return regex.IsMatch(mobile);
}
#endregion
#region 身份证验证
// 18 位身份证号(最后一位可以是数字或 X)
private static readonly Regex IdCardRegex = new(
@"^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$",
RegexOptions.Compiled
);
/// <summary>
/// 验证中国大陆身份证号格式(18位)
/// </summary>
/// <param name="idCard">身份证号</param>
/// <returns>是否有效</returns>
public static bool IsValidChinaIdCard(string? idCard)
{
if (string.IsNullOrWhiteSpace(idCard))
return false;
if (!IdCardRegex.IsMatch(idCard))
return false;
// 验证校验码
return ValidateIdCardChecksum(idCard);
}
/// <summary>
/// 验证中国大陆身份证号格式(Result 模式)
/// </summary>
/// <param name="idCard">身份证号</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<string> ValidateChinaIdCard(string? idCard, long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(idCard))
return Result.Failure<string>(errorCode, "身份证号不能为空");
if (!IdCardRegex.IsMatch(idCard))
return Result.Failure<string>(errorCode, "身份证号格式无效(应为18位)");
if (!ValidateIdCardChecksum(idCard))
return Result.Failure<string>(errorCode, "身份证号校验码无效");
return Result.Success(idCard.ToUpperInvariant());
}
private static bool ValidateIdCardChecksum(string idCard)
{
if (idCard.Length != 18)
return false;
// 加权因子
int[] weights = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
// 校验码对应值
char[] checkCodes = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
int sum = 0;
for (int i = 0; i < 17; i++)
{
if (!char.IsDigit(idCard[i]))
return false;
sum += (idCard[i] - '0') * weights[i];
}
int mod = sum % 11;
char expectedCheckCode = checkCodes[mod];
char actualCheckCode = char.ToUpperInvariant(idCard[17]);
return expectedCheckCode == actualCheckCode;
}
/// <summary>
/// 从身份证号提取出生日期
/// </summary>
/// <param name="idCard">身份证号</param>
/// <returns>出生日期(如果格式无效返回 null)</returns>
public static DateTime? ExtractBirthDateFromIdCard(string? idCard)
{
if (string.IsNullOrWhiteSpace(idCard) || idCard.Length != 18)
return null;
try
{
int year = int.Parse(idCard.Substring(6, 4));
int month = int.Parse(idCard.Substring(10, 2));
int day = int.Parse(idCard.Substring(12, 2));
return new DateTime(year, month, day);
}
catch
{
return null;
}
}
/// <summary>
/// 从身份证号提取性别
/// </summary>
/// <param name="idCard">身份证号</param>
/// <returns>性别(Male/Female,如果格式无效返回 null)</returns>
public static string? ExtractGenderFromIdCard(string? idCard)
{
if (string.IsNullOrWhiteSpace(idCard) || idCard.Length != 18)
return null;
try
{
int genderCode = int.Parse(idCard.Substring(16, 1));
return genderCode % 2 == 0 ? "Female" : "Male";
}
catch
{
return null;
}
}
#endregion
#region URL 验证
/// <summary>
/// 验证 URL 格式(使用 Uri.TryCreate)
/// </summary>
/// <param name="url">URL</param>
/// <returns>是否有效</returns>
public static bool IsValidUrl(string? url)
{
if (string.IsNullOrWhiteSpace(url))
return false;
return Uri.TryCreate(url, UriKind.Absolute, out var uriResult)
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
}
/// <summary>
/// 验证 URL 格式(Result 模式)
/// </summary>
/// <param name="url">URL</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<string> ValidateUrl(string? url, long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(url))
return Result.Failure<string>(errorCode, "URL 不能为空");
if (!Uri.TryCreate(url, UriKind.Absolute, out var uriResult))
return Result.Failure<string>(errorCode, "URL 格式无效");
if (uriResult.Scheme != Uri.UriSchemeHttp && uriResult.Scheme != Uri.UriSchemeHttps)
return Result.Failure<string>(errorCode, "URL 必须是 HTTP 或 HTTPS 协议");
return Result.Success(url);
}
#endregion
#region 数值范围验证
/// <summary>
/// 验证整数在指定范围内(Result 模式)
/// </summary>
/// <param name="value">要验证的值</param>
/// <param name="min">最小值(包含)</param>
/// <param name="max">最大值(包含)</param>
/// <param name="fieldName">字段名称</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<int> ValidateRange(int value, int min, int max, string fieldName = "值", long errorCode = 400)
{
if (value < min || value > max)
return Result.Failure<int>(errorCode, $"{fieldName}必须在 {min} 到 {max} 之间");
return Result.Success(value);
}
/// <summary>
/// 验证浮点数在指定范围内(Result 模式)
/// </summary>
public static Result<double> ValidateRange(double value, double min, double max, string fieldName = "值", long errorCode = 400)
{
if (value < min || value > max)
return Result.Failure<double>(errorCode, $"{fieldName}必须在 {min} 到 {max} 之间");
return Result.Success(value);
}
#endregion
#region 字符串长度验证
/// <summary>
/// 验证字符串长度(Result 模式)
/// </summary>
/// <param name="value">字符串值</param>
/// <param name="minLength">最小长度</param>
/// <param name="maxLength">最大长度</param>
/// <param name="fieldName">字段名称</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<string> ValidateLength(string? value, int minLength, int maxLength, string fieldName = "字段", long errorCode = 400)
{
if (string.IsNullOrEmpty(value))
return Result.Failure<string>(errorCode, $"{fieldName}不能为空");
if (value.Length < minLength)
return Result.Failure<string>(errorCode, $"{fieldName}长度不能少于 {minLength} 个字符");
if (value.Length > maxLength)
return Result.Failure<string>(errorCode, $"{fieldName}长度不能超过 {maxLength} 个字符");
return Result.Success(value);
}
/// <summary>
/// 验证字符串非空(Result 模式)
/// </summary>
public static Result<string> ValidateNotEmpty(string? value, string fieldName = "字段", long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(value))
return Result.Failure<string>(errorCode, $"{fieldName}不能为空");
return Result.Success(value);
}
#endregion
#region 密码强度验证
/// <summary>
/// 验证密码强度
/// </summary>
/// <param name="password">密码</param>
/// <param name="minLength">最小长度(默认 8)</param>
/// <param name="requireDigit">是否必须包含数字</param>
/// <param name="requireLowercase">是否必须包含小写字母</param>
/// <param name="requireUppercase">是否必须包含大写字母</param>
/// <param name="requireSpecialChar">是否必须包含特殊字符</param>
/// <returns>是否符合强度要求</returns>
public static bool IsStrongPassword(
string? password,
int minLength = 8,
bool requireDigit = true,
bool requireLowercase = true,
bool requireUppercase = true,
bool requireSpecialChar = true)
{
if (string.IsNullOrWhiteSpace(password))
return false;
if (password.Length < minLength)
return false;
if (requireDigit && !password.Any(char.IsDigit))
return false;
if (requireLowercase && !password.Any(char.IsLower))
return false;
if (requireUppercase && !password.Any(char.IsUpper))
return false;
if (requireSpecialChar && !password.Any(c => !char.IsLetterOrDigit(c)))
return false;
return true;
}
/// <summary>
/// 验证密码强度(Result 模式)
/// </summary>
public static Result<string> ValidatePassword(
string? password,
int minLength = 8,
bool requireDigit = true,
bool requireLowercase = true,
bool requireUppercase = true,
bool requireSpecialChar = true,
long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(password))
return Result.Failure<string>(errorCode, "密码不能为空");
if (password.Length < minLength)
return Result.Failure<string>(errorCode, $"密码长度不能少于 {minLength} 个字符");
var errors = new List<string>();
if (requireDigit && !password.Any(char.IsDigit))
errors.Add("至少包含一个数字");
if (requireLowercase && !password.Any(char.IsLower))
errors.Add("至少包含一个小写字母");
if (requireUppercase && !password.Any(char.IsUpper))
errors.Add("至少包含一个大写字母");
if (requireSpecialChar && !password.Any(c => !char.IsLetterOrDigit(c)))
errors.Add("至少包含一个特殊字符");
if (errors.Any())
return Result.Failure<string>(errorCode, $"密码强度不足:{string.Join("、", errors)}");
return Result.Success(password);
}
#endregion
#region 通用正则表达式验证
/// <summary>
/// 使用自定义正则表达式验证
/// </summary>
/// <param name="value">要验证的值</param>
/// <param name="pattern">正则表达式模式</param>
/// <param name="errorMessage">错误消息</param>
/// <param name="errorCode">错误码(默认 400)</param>
/// <returns>验证结果</returns>
public static Result<string> ValidateRegex(string? value, string pattern, string errorMessage = "格式无效", long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(value))
return Result.Failure<string>(errorCode, "值不能为空");
var regex = new Regex(pattern);
if (!regex.IsMatch(value))
return Result.Failure<string>(errorCode, errorMessage);
return Result.Success(value);
}
#endregion
#region IP 地址验证
/// <summary>
/// 验证 IPv4 地址格式
/// </summary>
public static bool IsValidIPv4(string? ip)
{
if (string.IsNullOrWhiteSpace(ip))
return false;
return System.Net.IPAddress.TryParse(ip, out var address)
&& address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork;
}
/// <summary>
/// 验证 IP 地址格式(Result 模式)
/// </summary>
public static Result<string> ValidateIPAddress(string? ip, long errorCode = 400)
{
if (string.IsNullOrWhiteSpace(ip))
return Result.Failure<string>(errorCode, "IP 地址不能为空");
if (!System.Net.IPAddress.TryParse(ip, out _))
return Result.Failure<string>(errorCode, "IP 地址格式无效");
return Result.Success(ip);
}
#endregion
#region 组合验证
/// <summary>
/// 组合多个验证规则(所有规则都必须通过)
/// </summary>
/// <typeparam name="T">值类型</typeparam>
/// <param name="value">要验证的值</param>
/// <param name="validators">验证器列表</param>
/// <returns>验证结果</returns>
public static Result<T> ValidateAll<T>(T value, params Func<T, Result<T>>[] validators)
{
var result = Result.Success(value);
foreach (var validator in validators)
{
result = result.Bind(validator);
if (result.IsFailure)
break;
}
return result;
}
/// <summary>
/// 组合多个验证规则(任一规则通过即可)
/// </summary>
/// <typeparam name="T">值类型</typeparam>
/// <param name="value">要验证的值</param>
/// <param name="validators">验证器列表</param>
/// <returns>验证结果</returns>
public static Result<T> ValidateAny<T>(T value, params Func<T, Result<T>>[] validators)
{
var errors = new List<string>();
foreach (var validator in validators)
{
var result = validator(value);
if (result.IsSuccess)
return result;
if (result.ErrorMessage != null)
errors.Add(result.ErrorMessage);
}
return Result.Failure<T>($"所有验证规则都未通过:{string.Join("; ", errors)}");
}
#endregion
}