GiftDiscountCalculator.php 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. <?php
  2. namespace Longyi\FreeGift\Services;
  3. /**
  4. * 赠品折扣计算。
  5. *
  6. * 对应 Amasty 的 Model\DiscountCalculator,保持同样的 4 种写法:
  7. * - '' → 全免(等同 100%)
  8. * - '100%' → 全免
  9. * - '50%' → 半价
  10. * - '-10' → 立减 10
  11. * - '99' → 一口价 99(原价 - 99)
  12. */
  13. class GiftDiscountCalculator
  14. {
  15. /**
  16. * 计算减免金额。
  17. *
  18. * @param string|null $promo
  19. * @param float $price
  20. */
  21. public function discountFor($promo, float $price): float
  22. {
  23. $promo = trim((string) $promo);
  24. if ($promo === '' || $promo === '100%') {
  25. return $price;
  26. }
  27. if (str_contains($promo, '%')) {
  28. $percent = (float) $promo;
  29. // 支持 "0.5" 与 "50" 两种百分比写法
  30. $percent = $percent > 1 ? $percent / 100 : $percent;
  31. return $price * min(1, max(0, $percent));
  32. }
  33. if (str_starts_with($promo, '-')) {
  34. return min(abs((float) $promo), $price);
  35. }
  36. // 一口价
  37. return max(0, $price - (float) $promo);
  38. }
  39. /**
  40. * minimal_items_price 兜底:折后单价不得低于最低价。
  41. */
  42. public function clampToMinimal(float $discount, float $price, $minimal): float
  43. {
  44. $minimal = (float) $minimal;
  45. if ($minimal > 0 && ($price - $discount) < $minimal) {
  46. return max(0, $price - $minimal);
  47. }
  48. return $discount;
  49. }
  50. /**
  51. * 一步算出赠品的实际单价。
  52. *
  53. * @param string|null $promo
  54. * @param float $price
  55. * @param float|null $minimal
  56. */
  57. public function priceFor($promo, float $price, $minimal = null): float
  58. {
  59. $discount = $this->discountFor($promo, $price);
  60. $discount = $this->clampToMinimal($discount, $price, $minimal);
  61. return round(max(0, $price - $discount), 4);
  62. }
  63. }