ソースを参照

花掌柜手动开单、计算器开单,界面显示会员折扣/店铺优惠。手动开单不能再次计算优惠,支付金额以实际金额为准

ouyang 19 時間 前
コミット
dc262792c4
1 ファイル変更139 行追加0 行削除
  1. 139 0
      biz-hd/order/classes/WholeOrderDiscountClass.php

+ 139 - 0
biz-hd/order/classes/WholeOrderDiscountClass.php

@@ -0,0 +1,139 @@
+<?php
+/**
+ * 用途:整单会员折扣与门店满减互斥,只取优惠金额更大的一项。
+ * 谁用:mall 混合下单 actionCreateMixOrder;结算页 affirmMix 按同一口径展示。
+ * 解决什么问题:原先两层会叠加,导致实付低于预期;互斥后前后端比较规则一致。
+ */
+
+namespace bizHd\order\classes;
+
+class WholeOrderDiscountClass
+{
+    /** 选用客户等级折扣 */
+    const TYPE_MEMBER = 'member';
+    /** 选用门店满减 */
+    const TYPE_SHOP = 'shop';
+    /** 两项都不满足 */
+    const TYPE_NONE = 'none';
+
+    /**
+     * 在红包后的应付上比较会员折扣额与门店满减额,只选较大者。
+     * 相等时用会员折扣,避免两层同时落地。
+     *
+     * @param float|string $basePrice 商品+运费/包装-红包
+     * @param float|string $bigNum 花材扎数 + 花束份数(与订单 bigNum 一致)
+     * @param float|string $customDiscount 客户折扣 0~1,1 表示无折扣
+     * @param object|array $shop 门店 meetNum/meetAmount/cutAmount/cutStyle
+     * @return array type/amount/afterPrice/memberSave/shopSave
+     */
+    public static function pick($basePrice, $bigNum, $customDiscount, $shop)
+    {
+        $base = self::toMoney($basePrice);
+        $memberSave = self::calcMemberSave($base, $customDiscount);
+        $shopSave = self::calcShopSave($base, $bigNum, $shop);
+
+        $type = self::TYPE_NONE;
+        $amount = '0.00';
+        // 相等时优先会员折扣(长期权益),只扣一笔
+        if (bccomp($memberSave, $shopSave, 2) >= 0 && bccomp($memberSave, '0', 2) > 0) {
+            $type = self::TYPE_MEMBER;
+            $amount = $memberSave;
+        } elseif (bccomp($shopSave, '0', 2) > 0) {
+            $type = self::TYPE_SHOP;
+            $amount = $shopSave;
+        }
+
+        $afterPrice = bcsub($base, $amount, 2);
+        if (bccomp($afterPrice, '0', 2) < 0) {
+            $afterPrice = '0.00';
+        }
+
+        return [
+            'type' => $type,
+            'amount' => $amount,
+            'afterPrice' => $afterPrice,
+            'memberSave' => $memberSave,
+            'shopSave' => $shopSave,
+        ];
+    }
+
+    /**
+     * 会员折扣额:custom.discount ∈ (0,1) 时 base*(1-discount)。
+     * 折后若 ≤0 则按 createHdOrder 保底 0.01 反推优惠额,保证比较口径与落库一致。
+     */
+    public static function calcMemberSave($basePrice, $customDiscount)
+    {
+        $base = self::toMoney($basePrice);
+        if (!is_numeric($customDiscount) || $customDiscount <= 0 || $customDiscount >= 1.0) {
+            return '0.00';
+        }
+        if (bccomp($base, '0', 2) <= 0) {
+            return '0.00';
+        }
+        $after = bcmul($base, $customDiscount, 2);
+        if (bccomp($after, '0', 2) <= 0) {
+            $after = '0.01';
+        }
+        $save = bcsub($base, $after, 2);
+        return bccomp($save, '0', 2) > 0 ? $save : '0.00';
+    }
+
+    /**
+     * 门店满减额:与 OrderClass::addOrder 同一门槛(件数+金额双门槛,cutStyle 固定额或打折)。
+     * 满减额不得 ≥ 应付,否则放弃(与现网「订单金额不足满减金额」一致)。
+     */
+    public static function calcShopSave($basePrice, $bigNum, $shop)
+    {
+        $base = self::toMoney($basePrice);
+        $meetNum = self::shopField($shop, 'meetNum', 0);
+        $meetAmount = self::shopField($shop, 'meetAmount', 0);
+        $cutOption = self::shopField($shop, 'cutAmount', 0);
+        $cutStyle = intval(self::shopField($shop, 'cutStyle', 0));
+
+        if ($cutStyle == 0) {
+            $cutAmount = self::toMoney($cutOption);
+        } else {
+            $miniOption = bcsub('1', self::toMoney($cutOption), 2);
+            $cutAmount = bcmul($base, $miniOption, 2);
+        }
+
+        if (bccomp($cutAmount, '0', 2) <= 0 || $meetNum < 0 || bccomp(self::toMoney($meetAmount), '0', 2) <= 0) {
+            return '0.00';
+        }
+        if (bccomp(self::toMoney($bigNum), self::toMoney($meetNum), 2) < 0) {
+            return '0.00';
+        }
+        if (bccomp($base, self::toMoney($meetAmount), 2) < 0) {
+            return '0.00';
+        }
+        if (bccomp($cutAmount, $base, 2) >= 0) {
+            return '0.00';
+        }
+        return $cutAmount;
+    }
+
+    /**
+     * 从门店对象或数组取满减字段。
+     */
+    private static function shopField($shop, $key, $default)
+    {
+        if (is_array($shop)) {
+            return $shop[$key] ?? $default;
+        }
+        if (is_object($shop)) {
+            return $shop->$key ?? $default;
+        }
+        return $default;
+    }
+
+    /**
+     * 统一成两位小数字符串,避免浮点比较误差。
+     */
+    private static function toMoney($value)
+    {
+        if ($value === null || $value === '') {
+            return '0.00';
+        }
+        return number_format((float)$value, 2, '.', '');
+    }
+}