|
|
@@ -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, '.', '');
|
|
|
+ }
|
|
|
+}
|