DiamondAmountValidator.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. namespace App\Module\OpenAPI\Validators;
  3. use App\Module\Fund\Enums\FUND_CURRENCY_TYPE;
  4. use App\Module\Fund\Services\CurrencyService;
  5. use UCore\Validator;
  6. /**
  7. * 钻石金额验证器
  8. *
  9. * 验证钻石金额的格式和精度
  10. */
  11. class DiamondAmountValidator extends Validator
  12. {
  13. /**
  14. * 验证钻石金额
  15. *
  16. * @param mixed $value 要验证的值
  17. * @param array $data 完整的数据数组
  18. * @return bool
  19. */
  20. public function validate(mixed $value, array $data): bool
  21. {
  22. // 检查是否为数字
  23. if (!is_numeric($value)) {
  24. $this->addError('金额必须为数字');
  25. return false;
  26. }
  27. $amount = (float)$value;
  28. // 检查金额是否为正数
  29. if ($amount <= 0) {
  30. $this->addError('金额必须大于0');
  31. return false;
  32. }
  33. // 获取钻石币种的精度
  34. $precision = FUND_CURRENCY_TYPE::ZUANSHI->getPrecision();
  35. // 验证金额精度
  36. if (!$this->validatePrecision($amount, $precision)) {
  37. $this->addError("金额精度不能超过{$precision}位小数");
  38. return false;
  39. }
  40. // 检查金额范围(最大值)
  41. $maxAmount = 999999999.9999999999; // 10位小数的最大合理值
  42. if ($amount > $maxAmount) {
  43. $this->addError("金额不能超过{$maxAmount}");
  44. return false;
  45. }
  46. // 格式化金额到正确的精度
  47. $formattedAmount = $this->formatAmountToPrecision($amount, $precision);
  48. // 将格式化后的金额设置到验证对象中
  49. if (isset($this->args['formattedAmount'])) {
  50. $this->validation->formattedAmount = $formattedAmount;
  51. }
  52. return true;
  53. }
  54. /**
  55. * 验证金额精度
  56. *
  57. * @param float $amount 金额
  58. * @param int $precision 允许的小数位数
  59. * @return bool
  60. */
  61. private function validatePrecision(float $amount, int $precision): bool
  62. {
  63. // 将金额转换为字符串以检查小数位数
  64. $amountStr = (string)$amount;
  65. // 如果包含小数点
  66. if (strpos($amountStr, '.') !== false) {
  67. $parts = explode('.', $amountStr);
  68. $decimalPart = $parts[1];
  69. // 移除末尾的0
  70. $decimalPart = rtrim($decimalPart, '0');
  71. // 检查小数位数
  72. if (strlen($decimalPart) > $precision) {
  73. return false;
  74. }
  75. }
  76. return true;
  77. }
  78. /**
  79. * 格式化金额到指定精度
  80. *
  81. * @param float $amount 原始金额
  82. * @param int $precision 精度
  83. * @return float
  84. */
  85. private function formatAmountToPrecision(float $amount, int $precision): float
  86. {
  87. // 使用 bcmath 进行高精度计算
  88. if (function_exists('bcadd')) {
  89. return (float)bcadd((string)$amount, '0', $precision);
  90. }
  91. // 如果没有 bcmath,使用 number_format
  92. return (float)number_format($amount, $precision, '.', '');
  93. }
  94. /**
  95. * 获取验证器描述
  96. *
  97. * @return string
  98. */
  99. public function getDescription(): string
  100. {
  101. return '验证钻石金额的格式和精度,确保符合系统要求';
  102. }
  103. }