1217 lines
40 KiB
C#
1217 lines
40 KiB
C#
using System.Globalization;
|
|
using System.Reflection;
|
|
|
|
namespace YLErp.Modules.SuperviseReportModule.SAC.Common
|
|
{
|
|
#region CheckAttrbute
|
|
|
|
/// <summary>
|
|
/// 检查字段属性基类
|
|
/// </summary>
|
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
|
|
public abstract class BaseCheckAttribute : Attribute
|
|
{
|
|
/// <summary>
|
|
/// 是否必填
|
|
/// <para>默认为true</para>
|
|
/// </summary>
|
|
public bool Required { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// 处理逻辑名称
|
|
/// <para>该值主要为<see cref="BaseCheckConditionAttribute"/>的派生类提供指定处理逻辑时的绑定名称,如果不需要指定,则该字段不应填写</para>
|
|
/// <para>该值不为Null时,则应在<see cref="BaseCheckConditionAttribute"/>的派生类中<see cref="BaseCheckConditionAttribute.CheckHandleNameArr"/>字段中指定,否则永远不会被执行</para>
|
|
/// </summary>
|
|
public string CheckHandleName { get; set; }
|
|
|
|
/// <summary>
|
|
/// 是否需要修改值
|
|
/// </summary>
|
|
public bool UseChangeValue { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// 修改后的值
|
|
/// </summary>
|
|
public object ChangedValue { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// </summary>
|
|
public abstract string ErrMsg { get; set; }
|
|
|
|
public abstract bool Execute(object value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串字段属性基类
|
|
/// </summary>
|
|
public abstract class BaseCheckStringAttribute : BaseCheckAttribute
|
|
{
|
|
/// <summary>
|
|
/// Null或空白字符是否通过检查
|
|
/// <para>仅Required=true时有效</para>
|
|
/// <para>默认为和Required一致</para>
|
|
/// </summary>
|
|
public bool IgnoreNullOrEmpty { get; set; } = false;
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
var state = false;
|
|
var text = (value?.ToString()) ?? "";
|
|
if (text.Length == 0)
|
|
{
|
|
state = !Required || IgnoreNullOrEmpty;
|
|
}
|
|
else
|
|
{
|
|
state = Execute(value?.ToString());
|
|
}
|
|
if (!state)
|
|
{
|
|
ErrMsg = GenerateErrMsg(value?.ToString());
|
|
}
|
|
return state;
|
|
}
|
|
protected abstract string GenerateErrMsg(string value);
|
|
|
|
protected abstract bool Execute(string value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串长度
|
|
/// <para>字符串长度是否等于预设值</para>
|
|
/// </summary>
|
|
public sealed class CheckStringLengthAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 字符串长度
|
|
/// </summary>
|
|
public int Length { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,长度应为{Length}</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值不应为空,且长度应为{Length}";
|
|
_errMsg = "值不应为空,且长度应为{Length}";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
return (value ?? "").Length == Length;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{Length}", Length.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串最大长度
|
|
/// <para>字符串长度是否超过预设值</para>
|
|
/// </summary>
|
|
public sealed class CheckStringMaxLengthAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 字符串最大长度
|
|
/// </summary>
|
|
public int MaxLength { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,最大长度不应超过{MaxLength}</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值不应为空,且最大长度不应超过{MaxLength}";
|
|
_errMsg = "值不应为空,且最大长度不应超过{MaxLength}";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
return (value ?? "").Length <= MaxLength;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{MaxLength}", MaxLength.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串最小长度
|
|
/// <para>字符串长度是否低于预设值</para>
|
|
/// </summary>
|
|
public sealed class CheckStringMinLengthAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 字符串最小长度
|
|
/// </summary>
|
|
public int MinLength { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,长度不应小于{MinLength}</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值不应为空,且长度不应小于{MinLength}";
|
|
_errMsg = "值不应为空,且长度不应小于{MinLength}";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
return (value ?? "").Length >= MinLength;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{MinLength}", MinLength.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串结尾
|
|
/// <para>检查字符串是否以预设值结尾</para>
|
|
/// </summary>
|
|
public sealed class CheckStringEndsWithAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 指定字符串结尾部分
|
|
/// </summary>
|
|
public string EndsWithString { get; set; }
|
|
/// <summary>
|
|
/// 多选模式
|
|
/// <para>该字段为true时,将使用<see cref="SplitStr"/>对<see cref="EndsWithString"/>分割后,进行匹配</para>
|
|
/// </summary>
|
|
public bool Multiple { get; set; }
|
|
/// <summary>
|
|
/// 多条件分隔符
|
|
/// </summary>
|
|
public string SplitStr { get; set; } = "|";
|
|
/// <summary>
|
|
/// 如字符串为英文字母,是否忽略大小写
|
|
/// <para>默认为:true</para>
|
|
/// </summary>
|
|
public bool IgnoreCase { get; set; } = true;
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,内容应以{EndsWithString}结尾</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值不应为空,且内容应以{EndsWithString}结尾";
|
|
_errMsg = "值不应为空,且内容应以{EndsWithString}结尾";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
var match = false;
|
|
if (Multiple)
|
|
{
|
|
var split = EndsWithString.Split(SplitStr.ToCharArray());
|
|
foreach (var item in split)
|
|
{
|
|
match = (value ?? "").EndsWith(item, IgnoreCase, CultureInfo.CurrentCulture);
|
|
if (match) { break; }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
match = (value ?? "").EndsWith(EndsWithString, IgnoreCase, CultureInfo.CurrentCulture);
|
|
}
|
|
return match;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{EndsWithString}", EndsWithString);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串是否为指定格式的DateTime字符串
|
|
/// </summary>
|
|
public sealed class CheckStringIsDateTimeFormatAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// DateTime类型支持的格式化字符
|
|
/// <para>默认为:yyyy-MM-dd</para>
|
|
/// </summary>
|
|
public string FormatString { get; set; } = "yyyy-MM-dd";
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,不匹配{FormatString}格式</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,不匹配{FormatString}格式";
|
|
_errMsg = "不匹配{FormatString}格式";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
var result = true;
|
|
try
|
|
{
|
|
result = DateTime.TryParseExact(value ?? "", FormatString, CultureInfo.CurrentCulture, DateTimeStyles.None, out _);
|
|
if (!result)
|
|
{
|
|
result = DateTime.TryParse(value ?? "", out var temp);
|
|
if (result)
|
|
{
|
|
UseChangeValue = true;
|
|
ChangedValue = temp.ToString(FormatString);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{FormatString}", FormatString);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串是否为数字或百分数,长度在指定范围内,可选是否检查值的合法区间
|
|
/// </summary>
|
|
public sealed class CheckStringIntOrPercentRangeAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 值为数字时小数点前最大长度
|
|
/// </summary>
|
|
public int BeforePointMaxLength_Number { get; set; }
|
|
/// <summary>
|
|
/// 值为数字时小数点后最大长度
|
|
/// </summary>
|
|
public int AfterPointMaxLength_Number { get; set; }
|
|
/// <summary>
|
|
/// 值为数字时最大值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MaxValue_Number { get; set; } = double.NaN;
|
|
/// <summary>
|
|
/// 值为数字时最小值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MinValue_Number { get; set; } = double.NaN;
|
|
/// <summary>
|
|
/// 值为百分数时小数点前最大长度
|
|
/// </summary>
|
|
public int BeforePointMaxLength_Percent { get; set; }
|
|
/// <summary>
|
|
/// 值为百分数时小数点后最大长度
|
|
/// </summary>
|
|
public int AfterPointMaxLength_Percent { get; set; }
|
|
/// <summary>
|
|
/// 值为百分数时最大值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MaxValue_Percent { get; set; } = double.NaN;
|
|
/// <summary>
|
|
/// 值为百分数时最小值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MinValue_Percent { get; set; } = double.NaN;
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:</para>
|
|
/// <para>{fieldName}={fieldValue}不合法,保留位数为[{BeforePointMaxLength},{AfterPointMaxLength}],取值区间为[>{MinValue},<{MaxValue}]</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值应为数字,且保留位数为[{BeforePointMaxLength},{AfterPointMaxLength}]";
|
|
_errMsg = "值应为数字,且保留位数为[{BeforePointMaxLength},{AfterPointMaxLength}]";
|
|
double minValue, maxValue;
|
|
if (isPercent)
|
|
{
|
|
minValue = MinValue_Percent;
|
|
maxValue = MaxValue_Percent;
|
|
}
|
|
else
|
|
{
|
|
minValue = MinValue_Number;
|
|
maxValue = MaxValue_Number;
|
|
}
|
|
var msgArr = new List<string>();
|
|
if (!double.IsNaN(minValue))
|
|
{
|
|
msgArr.Add(">={MinValue}");
|
|
}
|
|
if (!double.IsNaN(maxValue))
|
|
{
|
|
msgArr.Add("<={MaxValue}");
|
|
}
|
|
if (msgArr.Count > 0)
|
|
{
|
|
_errMsg += $",取值范围为[{string.Join(",", msgArr)}]";
|
|
}
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
private bool isPercent;
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
int beforePointMaxLength, afterPointMaxLength;
|
|
double maxValue, minValue;
|
|
|
|
isPercent = isPercent = value != null && value.EndsWith("%");
|
|
if (isPercent)
|
|
{
|
|
value = value.Remove(value.Length - 1);
|
|
beforePointMaxLength = BeforePointMaxLength_Percent;
|
|
afterPointMaxLength = AfterPointMaxLength_Percent;
|
|
maxValue = MaxValue_Percent;
|
|
minValue = MinValue_Percent;
|
|
}
|
|
else
|
|
{
|
|
beforePointMaxLength = BeforePointMaxLength_Number;
|
|
afterPointMaxLength = AfterPointMaxLength_Number;
|
|
maxValue = MaxValue_Number;
|
|
minValue = MinValue_Number;
|
|
}
|
|
if (!double.TryParse(value, out var temp))
|
|
{
|
|
return false;
|
|
}
|
|
var part = (value ?? "").Split('.');
|
|
if (part.Length > 2)
|
|
{
|
|
return false;
|
|
}
|
|
if (part[0].Length > beforePointMaxLength)
|
|
{
|
|
return false;
|
|
}
|
|
if (!double.IsNaN(maxValue) && temp > maxValue)
|
|
{
|
|
return false;
|
|
}
|
|
if (!double.IsNaN(minValue) && temp < minValue)
|
|
{
|
|
return false;
|
|
}
|
|
if (part.Length > 1 && part[1].Length > afterPointMaxLength)
|
|
{
|
|
UseChangeValue = true;
|
|
string sufix = "0.".PadRight(afterPointMaxLength + 2, '0');
|
|
UseChangeValue = true;
|
|
ChangedValue = temp.ToString(sufix) + (isPercent ? "%" : "");
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
int beforePointMaxLength, afterPointMaxLength;
|
|
double maxValue, minValue;
|
|
|
|
isPercent = isPercent = value != null && value.EndsWith("%");
|
|
if (isPercent)
|
|
{
|
|
value = value.Remove(value.Length - 2);
|
|
beforePointMaxLength = BeforePointMaxLength_Percent;
|
|
afterPointMaxLength = AfterPointMaxLength_Percent;
|
|
maxValue = MaxValue_Percent;
|
|
minValue = MinValue_Percent;
|
|
}
|
|
else
|
|
{
|
|
beforePointMaxLength = BeforePointMaxLength_Number;
|
|
afterPointMaxLength = AfterPointMaxLength_Number;
|
|
maxValue = MaxValue_Number;
|
|
minValue = MinValue_Number;
|
|
}
|
|
return ErrMsg
|
|
.Replace("{fieldValue}", value)
|
|
.Replace("{BeforePointMaxLength}", beforePointMaxLength.ToString())
|
|
.Replace("{AfterPointMaxLength}", afterPointMaxLength.ToString())
|
|
.Replace("{MinValue}", minValue.ToString())
|
|
.Replace("{MaxValue}", maxValue.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串是否为数字,长度在指定范围内,可选是否检查值的合法区间
|
|
/// </summary>
|
|
public sealed class CheckStringIntMaxLengthAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 小数点前最大长度
|
|
/// </summary>
|
|
public int BeforePointMaxLength { get; set; }
|
|
/// <summary>
|
|
/// 小数点后最大长度
|
|
/// </summary>
|
|
public int AfterPointMaxLength { get; set; }
|
|
/// <summary>
|
|
/// 最大值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MaxValue { get; set; } = double.NaN;
|
|
/// <summary>
|
|
/// 最小值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MinValue { get; set; } = double.NaN;
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,最大保留位数为[{BeforePointMaxLength},{AfterPointMaxLength}],取值范围为[>{MinValue},<{MaxValue}]</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值应为数字,且最大保留位数为[{BeforePointMaxLength},{AfterPointMaxLength}]";
|
|
_errMsg = "值应为数字,且最大保留位数为[{BeforePointMaxLength},{AfterPointMaxLength}]";
|
|
var msgArr = new List<string>();
|
|
if (!double.IsNaN(MinValue))
|
|
{
|
|
msgArr.Add(">={MinValue}");
|
|
}
|
|
if (!double.IsNaN(MaxValue))
|
|
{
|
|
msgArr.Add("<={MaxValue}");
|
|
}
|
|
if (msgArr.Count > 0)
|
|
{
|
|
_errMsg += $",取值范围为[{string.Join(",", msgArr)}]";
|
|
}
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
if (!double.TryParse(value, out var temp))
|
|
{
|
|
return false;
|
|
}
|
|
var part = (value ?? "").Split('.');
|
|
if (part.Length > 2)
|
|
{
|
|
return false;
|
|
}
|
|
if (part[0].Length > BeforePointMaxLength)
|
|
{
|
|
return false;
|
|
}
|
|
var state = true;
|
|
if (part.Length == 1 || (part.Length > 1 && part[1].Length != AfterPointMaxLength))
|
|
{
|
|
UseChangeValue = true;
|
|
var sufix = "0.".PadRight(AfterPointMaxLength + 2, '0');
|
|
UseChangeValue = true;
|
|
ChangedValue = temp.ToString(sufix);
|
|
state = true;
|
|
}
|
|
if (!double.IsNaN(MinValue) && temp < MinValue)
|
|
{
|
|
state = false;
|
|
}
|
|
if (!double.IsNaN(MaxValue) && temp > MaxValue)
|
|
{
|
|
state = false;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg
|
|
.Replace("{fieldValue}", value)
|
|
.Replace("{BeforePointMaxLength}", BeforePointMaxLength.ToString())
|
|
.Replace("{AfterPointMaxLength}", AfterPointMaxLength.ToString())
|
|
.Replace("{MinValue}", MinValue.ToString())
|
|
.Replace("{MaxValue}", MaxValue.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串是否为数字,长度在指定范围内(小数长度必须和预设值相等),可选是否检查值的合法区间
|
|
/// </summary>
|
|
public sealed class CheckStringIntLengthAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 小数点前最大长度
|
|
/// </summary>
|
|
public int BeforePointMaxLength { get; set; }
|
|
/// <summary>
|
|
/// 小数点后长度
|
|
/// <para>小数位必须与该值相等,否则应使用<see cref="CheckStringIntMaxLengthAttribute"/></para>
|
|
/// </summary>
|
|
public int AfterPointLength { get; set; }
|
|
/// <summary>
|
|
/// 最大值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MaxValue { get; set; } = double.NaN;
|
|
/// <summary>
|
|
/// 最小值
|
|
/// <para>可选,不设置则不检查</para>
|
|
/// </summary>
|
|
public double MinValue { get; set; } = double.NaN;
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,保留位数必须为{BeforePointMaxLength},{AfterPointLength},取值范围为[>{MinValue},<{MaxValue}]</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值应为数字,且保留位数必须为[{BeforePointMaxLength},{AfterPointLength}]";
|
|
_errMsg = "值应为数字,且保留位数必须为[{BeforePointMaxLength},{AfterPointLength}]";
|
|
var msgArr = new List<string>();
|
|
if (!double.IsNaN(MinValue))
|
|
{
|
|
msgArr.Add(">={MinValue}");
|
|
}
|
|
if (!double.IsNaN(MaxValue))
|
|
{
|
|
msgArr.Add("<={MaxValue}");
|
|
}
|
|
if (msgArr.Count > 0)
|
|
{
|
|
_errMsg += $",取值范围为[{string.Join(",", msgArr)}]";
|
|
}
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
if (!double.TryParse(value, out var temp))
|
|
{
|
|
return false;
|
|
}
|
|
if (!double.IsNaN(MinValue) && temp < MinValue)
|
|
{
|
|
return false;
|
|
}
|
|
if (!double.IsNaN(MaxValue) && temp > MaxValue)
|
|
{
|
|
return false;
|
|
}
|
|
var part = (value ?? "").Split('.');
|
|
if (part[0].Length > BeforePointMaxLength)
|
|
{
|
|
return false;
|
|
}
|
|
if (part.Length == 1 || (part.Length > 1 && part[1].Length != AfterPointLength))
|
|
{
|
|
var sufix = "0.".PadRight(AfterPointLength + 2, '0');
|
|
UseChangeValue = true;
|
|
ChangedValue = temp.ToString(sufix);
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg
|
|
.Replace("{fieldValue}", value)
|
|
.Replace("{BeforePointMaxLength}", BeforePointMaxLength.ToString())
|
|
.Replace("{AfterPointLength}", AfterPointLength.ToString())
|
|
.Replace("{MinValue}", MinValue.ToString())
|
|
.Replace("{MaxValue}", MaxValue.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查IEnumerable基类属性基类
|
|
/// </summary>
|
|
public abstract class BaseCheckIEnumerableTAttribute : BaseCheckAttribute
|
|
{
|
|
public override bool Execute(object value)
|
|
{
|
|
var state = false;
|
|
var objs = value as IEnumerable<object>;
|
|
//if (objs != null)
|
|
//{
|
|
state = Execute(objs);
|
|
//}
|
|
if (!state)
|
|
{
|
|
ErrMsg = GenerateErrMsg(objs);
|
|
}
|
|
return state;
|
|
}
|
|
protected abstract string GenerateErrMsg(IEnumerable<object> value);
|
|
|
|
protected abstract bool Execute(IEnumerable<object> value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查列表最小数量
|
|
/// </summary>
|
|
public sealed class CheckIEnumerableTMinCountAttribute : BaseCheckIEnumerableTAttribute
|
|
{
|
|
/// <summary>
|
|
/// 列表数量
|
|
/// </summary>
|
|
public int MinCount { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}.Length={fieldValue}不合法,应大于{MinCount}</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}.Length={fieldValue}不合法,数量应大于{MinCount}";
|
|
_errMsg = "列表长度={fieldValue}不合法,应大于{MinCount}";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(IEnumerable<object> value)
|
|
{
|
|
return value != null && value.Count() >= MinCount;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(IEnumerable<object> value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", (value?.Count() ?? 0).ToString()).Replace("{MinCount}", MinCount.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查列表最大数量
|
|
/// </summary>
|
|
public sealed class CheckIEnumerableTMaxCountAttribute : BaseCheckIEnumerableTAttribute
|
|
{
|
|
/// <summary>
|
|
/// 列表数量
|
|
/// </summary>
|
|
public int MaxCount { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}.Length={fieldValue}不合法,应小于等于{MaxCount}</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}.Length={fieldValue}不合法,数量应大于{MinCount}";
|
|
_errMsg = "列表长度={fieldValue}不合法,应小于等于{MaxCount}";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(IEnumerable<object> value)
|
|
{
|
|
return value == null || value.Count() <= MaxCount;
|
|
}
|
|
|
|
protected override string GenerateErrMsg(IEnumerable<object> value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", (value?.Count() ?? 0).ToString()).Replace("{MaxCount}", MaxCount.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串是否为指定可选值之一
|
|
/// </summary>
|
|
public sealed class CheckStringInMapAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 可选值列表
|
|
/// </summary>
|
|
public string[] Map { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,值应为{Map}之一</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值应为{Map}";
|
|
_errMsg = "值应为{Map}";
|
|
if (Map.Length > 1)
|
|
{
|
|
_errMsg += "之一";
|
|
}
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
return Map.Contains(value ?? "");
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{Map}", string.Join(",", Map));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查字符串长度是否为可选值之一
|
|
/// </summary>
|
|
public sealed class CheckStringLengthInMapAttribute : BaseCheckStringAttribute
|
|
{
|
|
/// <summary>
|
|
/// 可选值列表
|
|
/// </summary>
|
|
public int[] Map { get; set; }
|
|
|
|
private string _errMsg;
|
|
/// <summary>
|
|
/// 错误提示
|
|
/// <para>默认为:{fieldName}={fieldValue}不合法,长度应为{Map}之一</para>
|
|
/// </summary>
|
|
public override string ErrMsg
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_errMsg))
|
|
{
|
|
//_errMsg = "{fieldName}={fieldValue}不合法,值应为{Map}之一";
|
|
_errMsg = "内容长度={fieldValue}不合法,长度应为{Map}之一";
|
|
}
|
|
return _errMsg;
|
|
}
|
|
set
|
|
{
|
|
_errMsg = value;
|
|
}
|
|
}
|
|
|
|
protected override bool Execute(string value)
|
|
{
|
|
var length = value.Length;
|
|
return Map.Contains(length);
|
|
}
|
|
|
|
protected override string GenerateErrMsg(string value)
|
|
{
|
|
return ErrMsg.Replace("{fieldValue}", value).Replace("{Map}", string.Join(",", Map));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Condition
|
|
|
|
/// <summary>
|
|
/// 检查条件基类
|
|
/// </summary>
|
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
|
|
public abstract class BaseCheckConditionAttribute : Attribute
|
|
{
|
|
/// <summary>
|
|
/// 条件字段名
|
|
/// <para>应填写当前对象可访问到的字段</para>
|
|
/// </summary>
|
|
public string ConditionName { get; set; }
|
|
|
|
private string[] _checkHandleNameArr;
|
|
/// <summary>
|
|
/// 绑定满足条件后的处理逻辑
|
|
/// <para>不填时依次执行CheckName为空的操作</para>
|
|
/// </summary>
|
|
public string[] CheckHandleNameArr
|
|
{
|
|
get
|
|
{
|
|
if (_checkHandleNameArr == null)
|
|
{
|
|
return new string[] { null };
|
|
}
|
|
return _checkHandleNameArr;
|
|
}
|
|
set
|
|
{
|
|
_checkHandleNameArr = value;
|
|
}
|
|
}
|
|
|
|
public abstract bool Execute(object value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查指定字段的值是否等于预设值
|
|
/// </summary>
|
|
public sealed class CheckConditionStringValueAttribute : BaseCheckConditionAttribute
|
|
{
|
|
/// <summary>
|
|
/// 预设值
|
|
/// </summary>
|
|
public string ConditionValue { get; set; }
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
return value?.ToString() == ConditionValue;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 检查指定字段的值是否不等于预设值
|
|
/// </summary>
|
|
public sealed class CheckConditionStringNEQValueAttribute : BaseCheckConditionAttribute
|
|
{
|
|
/// <summary>
|
|
/// 预设值
|
|
/// </summary>
|
|
public string ConditionValue { get; set; }
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
return value?.ToString() != ConditionValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查指定字段的值是否在范围内
|
|
/// </summary>
|
|
public sealed class CheckConditionIntInRangeAttribute : BaseCheckConditionAttribute
|
|
{
|
|
/// <summary>
|
|
/// 最小值
|
|
/// <para>默认为int.MinValue</para>
|
|
/// </summary>
|
|
public int MinValue { get; set; } = int.MinValue;
|
|
|
|
/// <summary>
|
|
/// 最大值
|
|
/// <para>默认为int.MaxValue</para>
|
|
/// </summary>
|
|
public int MaxValue { get; set; } = int.MaxValue;
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
if (double.TryParse((value ?? "").ToString(), out var temp))
|
|
{
|
|
return temp >= MinValue && temp <= MaxValue;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 检查指定字段的值是否不在范围内
|
|
/// </summary>
|
|
public sealed class CheckConditionIntNotInRangeValueAttribute : BaseCheckConditionAttribute
|
|
{
|
|
/// <summary>
|
|
/// 最小值
|
|
/// <para>默认为int.MinValue</para>
|
|
/// </summary>
|
|
public int MinValue { get; set; } = int.MinValue;
|
|
|
|
/// <summary>
|
|
/// 最大值
|
|
/// <para>默认为int.MaxValue</para>
|
|
/// </summary>
|
|
public int MaxValue { get; set; } = int.MaxValue;
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
if (double.TryParse((value ?? "").ToString(), out double temp))
|
|
{
|
|
return temp < MinValue || temp > MaxValue;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查指定字段的值是否为可选值之一
|
|
/// </summary>
|
|
public sealed class CheckConditionStringInMapAttribute : BaseCheckConditionAttribute
|
|
{
|
|
public string[] ConditionMap { get; set; }
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
return ConditionMap.Contains(value?.ToString() ?? "");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查指定字段的值是否不为可选值
|
|
/// </summary>
|
|
public sealed class CheckConditionStringNotInMapAttribute : BaseCheckConditionAttribute
|
|
{
|
|
public string[] ConditionMap { get; set; }
|
|
|
|
public override bool Execute(object value)
|
|
{
|
|
return !ConditionMap.Contains(value?.ToString() ?? "");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 检查帮助类
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
public class CheckHelper<T> where T:notnull
|
|
{
|
|
private readonly PropertyInfo[]? _properties = null;
|
|
/// <summary>
|
|
/// 属性缓存
|
|
/// <para>应通过<see cref="CheckHelper{T}.getPropertyInfos(T)"/>方法访问</para>
|
|
/// </summary>
|
|
private Dictionary<T, Dictionary<string, object>>? _propertyInfoDict = null;
|
|
/// <summary>
|
|
/// 检查不通过时回调的委托
|
|
/// </summary>
|
|
/// <param name="fieldName">字段名</param>
|
|
/// <param name="value">字段值</param>
|
|
/// <param name="errMsg">错误信息</param>
|
|
public delegate void CheckErrorCallback(string fieldName, object value, string errMsg);
|
|
|
|
public CheckHelper()
|
|
{
|
|
_properties = typeof(T).GetProperties();
|
|
}
|
|
|
|
private Dictionary<string, object> getPropertyInfos(T obj)
|
|
{
|
|
_propertyInfoDict ??= new Dictionary<T, Dictionary<string, object>>();
|
|
if (!_propertyInfoDict.ContainsKey(obj))
|
|
{
|
|
_propertyInfoDict[obj] = _properties.ToDictionary(K => K.Name, V => V.GetValue(obj));
|
|
}
|
|
return _propertyInfoDict[obj];
|
|
}
|
|
|
|
private bool checkCondition(List<BaseCheckConditionAttribute> conditionAttributes, Dictionary<string, object> dict)
|
|
{
|
|
var state = true;
|
|
foreach (var item in conditionAttributes)
|
|
{
|
|
if (!dict.ContainsKey(item.ConditionName))
|
|
{
|
|
throw new Exception($"当前对象中不存在 {item.ConditionName} 字段");
|
|
}
|
|
state = item.Execute(dict[item.ConditionName]);
|
|
if (!state)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
private bool checkCondition(PropertyInfo pInfo, T obj, Func<string[], bool> callback)
|
|
{
|
|
var state = true;
|
|
var conditionAttrs = pInfo.GetCustomAttributes(typeof(BaseCheckConditionAttribute), true).Cast<BaseCheckConditionAttribute>();
|
|
var conditGroup = conditionAttrs.GroupBy(O => O.CheckHandleNameArr == null ? "" : string.Join(",", O.CheckHandleNameArr)).ToDictionary(K => K.Key, V => V.ToList());
|
|
if (conditGroup.Count > 0)
|
|
{
|
|
foreach (var condit in conditGroup)
|
|
{
|
|
var dict = getPropertyInfos(obj);
|
|
state = checkCondition(condit.Value, dict);
|
|
if (state)
|
|
{
|
|
if (!callback(condit.Value[0].CheckHandleNameArr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
callback(new[] { "" });
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
private bool checkValue(string checkName, BaseCheckAttribute[] checkAttributes, PropertyInfo pInfo, T obj, out string errMsg)
|
|
{
|
|
errMsg = "";
|
|
var state = true;
|
|
var attrs = checkAttributes.Where(O => (O.CheckHandleName ?? "") == checkName);
|
|
foreach (var item in attrs)
|
|
{
|
|
state = item.Execute(pInfo.GetValue(obj));
|
|
if (!state)
|
|
{
|
|
errMsg = item.ErrMsg.Replace("{fieldName}", pInfo.Name);
|
|
continue;
|
|
}
|
|
if (item.UseChangeValue)
|
|
{
|
|
pInfo.SetValue(obj, item.ChangedValue);
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 执行字段检查
|
|
/// </summary>
|
|
/// <param name="obj">要检查的对象</param>
|
|
/// <param name="callback">检查不通过时的回调</param>
|
|
/// <returns></returns>
|
|
public bool ExecuteCheck(T obj, CheckErrorCallback callback)
|
|
{
|
|
var checkState = false;
|
|
if (obj == null)
|
|
{
|
|
return checkState;
|
|
}
|
|
foreach (var p in _properties)
|
|
{
|
|
var attr = p.GetCustomAttributes(typeof(BaseCheckAttribute), true).Cast<BaseCheckAttribute>().ToArray();
|
|
if (attr.Length == 0)
|
|
{ continue; }
|
|
checkCondition(p, obj, (checkNames) =>
|
|
{
|
|
var status = true;
|
|
foreach (var item in checkNames)
|
|
{
|
|
if (!checkValue(item ?? "", attr, p, obj, out var errMsg))
|
|
{
|
|
callback(p.Name, p.GetValue(obj), errMsg);
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
return status;
|
|
});
|
|
}
|
|
return checkState;
|
|
}
|
|
}
|
|
}
|