ソースを参照

feat: 😀增加安全的基本数学运算方法类

喵你个旺呀 1 年間 前
コミット
2ba05c4e92

+ 110 - 0
Admin.NET/Admin.NET.Core/Utils/SafeMath.cs

@@ -0,0 +1,110 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// 
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+// 
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using System.Globalization;
+
+namespace Admin.NET.Core;
+
+using System;
+
+/// <summary>
+/// 安全的基本数学运算方法类
+/// </summary>
+public static class SafeMath
+{
+    /// <summary>
+    /// 安全加法
+    /// </summary>
+    public static T Add<T>(object left, object right, int precision = 2, T defaultValue = default, bool throwOnError = false) where T : struct, IComparable, IConvertible, IFormattable
+    {
+        return PerformOperation(left, right, (a, b) => a + b, precision, defaultValue, throwOnError);
+    }
+
+    /// <summary>
+    /// 安全减法
+    /// </summary>
+    public static T Sub<T>(object left, object right, int precision = 2, T defaultValue = default, bool throwOnError = false) where T : struct, IComparable, IConvertible, IFormattable
+    {
+        return PerformOperation(left, right, (a, b) => a - b, precision, defaultValue, throwOnError);
+    }
+
+    /// <summary>
+    /// 安全乘法
+    /// </summary>
+    public static T Mult<T>(object left, object right, int precision = 2, T defaultValue = default, bool throwOnError = false) where T : struct, IComparable, IConvertible, IFormattable
+    {
+        return PerformOperation(left, right, (a, b) => a * b, precision, defaultValue, throwOnError);
+    }
+
+    /// <summary>
+    /// 安全除法
+    /// </summary>
+    public static T Div<T>(object left, object right, int precision = 2, T defaultValue = default, bool throwOnDivideByZero = false) where T : struct, IComparable, IConvertible, IFormattable
+    {
+        return PerformOperation(left, right, (a, b) =>
+        {
+            if (b != 0) return a / b;
+            if (throwOnDivideByZero) throw new DivideByZeroException("除数不能为0");
+            return SafeConvert<decimal>(defaultValue);
+        }, precision, defaultValue, throwOnDivideByZero);
+    }
+
+    /// <summary>
+    /// 安全类型转换
+    /// </summary>
+    public static T SafeConvert<T>(object value, T defaultValue = default) where T : struct, IComparable, IConvertible, IFormattable
+    {
+        if (value == null) return defaultValue;
+        try
+        {
+            return (T)Convert.ChangeType(value, typeof(T));
+        }
+        catch
+        {
+            return defaultValue;
+        }
+    }
+
+    /// <summary>
+    /// 执行数学运算
+    /// </summary>
+    private static T PerformOperation<T>(object left, object right, Func<decimal, decimal, decimal> operation, int precision, T defaultValue, bool throwOnError) where T : struct, IComparable, IConvertible, IFormattable
+    {
+        try
+        {
+            decimal leftValue = ConvertToDecimal(left);
+            decimal rightValue = ConvertToDecimal(right);
+
+            decimal result = operation(leftValue, rightValue);
+            return SafeConvert(Math.Round(result, precision, MidpointRounding.AwayFromZero), defaultValue);
+        }
+        catch
+        {
+            if (throwOnError) throw;
+            return defaultValue;
+        }
+    }
+
+    /// <summary>
+    /// 将输入值转换为 decimal
+    /// </summary>
+    public static decimal ConvertToDecimal(object value)
+    {
+        return value switch
+        {
+            null => 0m,
+            int intValue => intValue,
+            float floatValue => (decimal)floatValue,
+            double doubleValue => (decimal)doubleValue,
+            decimal decimalValue => decimalValue,
+            long longValue => longValue,
+            short shortValue => shortValue,
+            byte byteValue => byteValue,
+            string stringValue when decimal.TryParse(stringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal parsedValue) => parsedValue, // 尝试解析字符串
+            _ => throw new InvalidCastException($"不支持的类型: {value.GetType().Name}")
+        };
+    }
+}

+ 5 - 0
Admin.NET/Admin.NET.Test/Admin.NET.Test.csproj

@@ -17,5 +17,10 @@
       <PackageReference Include="Selenium.Support" Version="4.27.0" />
       <PackageReference Include="Selenium.WebDriver" Version="4.27.0" />
       <PackageReference Include="Selenium.WebDriver.MSEdgeDriver" Version="131.0.2903.48" />
+      <PackageReference Include="xunit.assert" Version="2.9.3" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Admin.NET.Core\Admin.NET.Core.csproj" />
     </ItemGroup>
 </Project>

+ 311 - 0
Admin.NET/Admin.NET.Test/Utils/SafeMathTests.cs

@@ -0,0 +1,311 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Admin.NET.Core;
+using Xunit;
+
+namespace Admin.NET.Test.Utils;
+
+public class SafeMathTests
+{
+    [Fact]
+    public void Add_IntAndDouble_ReturnsCorrectResult()
+    {
+        // Arrange
+        int left = 10;
+        double right = 20.5;
+
+        // Act
+        var result = SafeMath.Add<int>(left, right, precision: 2);
+
+        // Assert
+        Assert.Equal(30, result); // 10 + 20.5 = 30.5,四舍五入后为 30
+    }
+
+    [Fact]
+    public void Add_StringAndDecimal_ReturnsCorrectResult()
+    {
+        // Arrange
+        string left = "15.75";
+        decimal right = 4.25m;
+
+        // Act
+        var result = SafeMath.Add<decimal>(left, right, precision: 2);
+
+        // Assert
+        Assert.Equal(20.00m, result); // 15.75 + 4.25 = 20.00
+    }
+
+    [Fact]
+    public void Sub_DoubleAndInt_ReturnsCorrectResult()
+    {
+        // Arrange
+        double left = 50.75;
+        int right = 25;
+
+        // Act
+        var result = SafeMath.Sub<double>(left, right, precision: 2);
+
+        // Assert
+        Assert.Equal(25.75, result); // 50.75 - 25 = 25.75
+    }
+
+    [Fact]
+    public void Mult_DecimalAndFloat_ReturnsCorrectResult()
+    {
+        // Arrange
+        decimal left = 10.5m;
+        float right = 2.0f;
+
+        // Act
+        var result = SafeMath.Mult<decimal>(left, right, precision: 2);
+
+        // Assert
+        Assert.Equal(21.00m, result); // 10.5 * 2.0 = 21.00
+    }
+
+    [Fact]
+    public void Div_IntAndInt_ReturnsCorrectResult()
+    {
+        // Arrange
+        int left = 10;
+        int right = 3;
+
+        // Act
+        var result = SafeMath.Div<double>(left, right, precision: 4);
+
+        // Assert
+        Assert.Equal(3.3333, result); // 10 / 3 = 3.3333
+    }
+
+    [Fact]
+    public void Div_ByZero_ReturnsDefaultValue()
+    {
+        // Arrange
+        int left = 10;
+        int right = 0;
+
+        // Act
+        int result = SafeMath.Div(left, right, defaultValue: -1);
+
+        // Assert
+        Assert.Equal(-1, result); // 除数为 0,返回默认值 -1
+    }
+
+    [Fact]
+    public void Div_ByZero_ThrowsException()
+    {
+        // Arrange
+        int left = 10;
+        int right = 0;
+
+        // Act & Assert
+        Assert.Throws<DivideByZeroException>(() =>
+        {
+            SafeMath.Div<double>(left, right, throwOnDivideByZero: true);
+        });
+    }
+
+    [Fact]
+    public void SafeConvert_StringToInt_ReturnsCorrectResult()
+    {
+        // Arrange
+        string value = "42";
+
+        // Act
+        int result = SafeMath.SafeConvert(value, defaultValue: -1);
+
+        // Assert
+        Assert.Equal(42, result); // 字符串 "42" 转换为 int 42
+    }
+
+    [Fact]
+    public void SafeConvert_InvalidString_ReturnsDefaultValue()
+    {
+        // Arrange
+        string value = "invalid";
+
+        // Act
+        int result = SafeMath.SafeConvert(value, defaultValue: -1);
+
+        // Assert
+        Assert.Equal(-1, result); // 转换失败,返回默认值 -1
+    }
+
+    [Fact]
+    public void ConvertToDecimal_Int_ReturnsCorrectResult()
+    {
+        // Arrange
+        int value = 42;
+
+        // Act
+        decimal result = SafeMath.ConvertToDecimal(value);
+
+        // Assert
+        Assert.Equal(42m, result); // int 42 转换为 decimal 42m
+    }
+
+    [Fact]
+    public void ConvertToDecimal_String_ReturnsCorrectResult()
+    {
+        // Arrange
+        string value = "42.75";
+
+        // Act
+        decimal result = SafeMath.ConvertToDecimal(value);
+
+        // Assert
+        Assert.Equal(42.75m, result); // 字符串 "42.75" 转换为 decimal 42.75m
+    }
+
+    [Fact]
+    public void ConvertToDecimal_InvalidString_ReturnsZero()
+    {
+        // Arrange
+        string value = "invalid";
+
+        // Act & Assert
+        Assert.Throws<InvalidCastException>(() => SafeMath.ConvertToDecimal(value));
+    }
+
+    [Fact]
+    public void Add_LeftNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        object left = null;
+        int right = 20;
+
+        // Act
+        int result = SafeMath.Add<int>(left, right);
+
+        // Assert
+        Assert.Equal(20, result); // 左操作数为 null
+    }
+
+    [Fact]
+    public void Add_RightNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        int left = 10;
+        object right = null;
+
+        // Act
+        var result = SafeMath.Add<int>(left, right);
+
+        // Assert
+        Assert.Equal(10, result); // 右操作数为 null
+    }
+
+    [Fact]
+    public void Sub_LeftNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        object left = null;
+        int right = 20;
+
+        // Act
+        int result = SafeMath.Sub<int>(left, right);
+
+        // Assert
+        Assert.Equal(-20, result); // 左操作数为 null
+    }
+
+    [Fact]
+    public void Sub_RightNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        int left = 10;
+        object right = null;
+
+        // Act
+        var result = SafeMath.Sub<int>(left, right);
+
+        // Assert
+        Assert.Equal(10, result); // 右操作数为 null
+    }
+
+    [Fact]
+    public void Mult_LeftNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        object left = null;
+        int right = 20;
+
+        // Act
+        int result = SafeMath.Mult<int>(left, right);
+
+        // Assert
+        Assert.Equal(0, result); // 左操作数为 null
+    }
+
+    [Fact]
+    public void Mult_RightNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        int left = 10;
+        object right = null;
+
+        // Act
+        int result = SafeMath.Mult<int>(left, right);
+
+        // Assert
+        Assert.Equal(0, result); // 右操作数为 null
+    }
+
+    [Fact]
+    public void Div_LeftNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        object left = null;
+        int right = 20;
+
+        // Act
+        int result = SafeMath.Div<int>(left, right);
+
+        // Assert
+        Assert.Equal(0, result); // 左操作数为 null
+    }
+
+    [Fact]
+    public void Div_RightNull_ReturnsDefaultValue()
+    {
+        // Arrange
+        int left = 10;
+        object right = null;
+
+        // Act
+        int result = SafeMath.Div<int>(left, right, defaultValue: -1);
+
+        // Assert
+        Assert.Equal(-1, result); // 右操作数为 null,返回默认值 -1
+    }
+
+    [Fact]
+    public void SafeConvert_NullInput_ReturnsDefaultValue()
+    {
+        // Arrange
+        object value = null;
+
+        // Act
+        int result = SafeMath.SafeConvert<int>(value, defaultValue: -1);
+
+        // Assert
+        Assert.Equal(-1, result); // 输入为 null,返回默认值 -1
+    }
+
+    [Fact]
+    public void ConvertToDecimal_NullInput_ReturnsZero()
+    {
+        // Arrange
+        object value = null;
+
+        // Act
+        decimal result = SafeMath.ConvertToDecimal(value);
+
+        // Assert
+        Assert.Equal(0m, result); // 输入为 null,返回默认值 0m
+    }
+}