Sfoglia il codice sorgente

fix(order): 修正混合订单红包与跑腿费优惠口径

- 限制红包仅抵扣商品金额并回写实际抵扣额
- 从会员折扣和门店满减基数中剔除跑腿费

API: 调整 create-mix-order 计价规则,不变更请求与响应结构
shizhongqi 12 ore fa
parent
commit
d1169164ac

+ 53 - 37
app-mall/controllers/OrderController.php

@@ -897,6 +897,8 @@ class OrderController extends BaseController
      * 复用 OrderService::createHdOrder,不改动 buy-item / create-order
      * 整单优惠:会员折扣与门店满减互斥,只落地优惠金额更大的一项(相等用会员折扣)
      * 秒杀商品已是活动价:seckillGoodsPrice 不计入会员折、门店满减金额基数,seckillGoodsNum 不计入满减件数门槛,仍计入应付和最低消费数量
+     * 跑腿费(runnerSendCost)不参与会员折/门店满减基数,折后原额计入应付
+     * 红包只抵商品金额:在加跑腿费/运费/包装费之前抵扣,抵扣额不超过商品合计,不抵运费
      */
     public function actionCreateMixOrder()
     {
@@ -1255,6 +1257,50 @@ class OrderController extends BaseController
             // 含花材(含混合)按花材报价 goodsType=1;纯花束按 0
             $quoteGoodsType = $hasItem ? 1 : 0;
 
+            // 红包(归属花店客户,与 buy-item 一致)
+            // 红包只抵商品金额:在加跑腿费/运费/包装费之前抵扣,抵扣额不超过商品合计,避免红包抵用运费
+            $hbId = $post['hbId'] ?? 0;
+            $hb = null;
+            if ($hbId > 0) {
+                $hb = HbClass::getById($hbId, true);
+                if (!empty($hb)) {
+                    if ($hb->customId != $hdCustomId) {
+                        util::fail('不是你的红包');
+                    }
+                    if ($hb->amount != $post['hbAmount']) {
+                        util::fail('红包金额不相等');
+                    }
+                    if ($hb->status == 1) {
+                        util::fail('红包已使用');
+                    }
+                    if ($hb->status == -1) {
+                        util::fail('红包已作废');
+                    }
+                    if ($hb->endTime < time()) {
+                        $hb->status = -1;
+                        $hb->save();
+                        util::fail('红包已失效');
+                    }
+                    if ($hb->beginTime > time()) {
+                        util::fail('红包可使用期未到');
+                    }
+                    // 混合结算:按适用范围用商品明细校验最低消费(不含运费包装费)
+                    HbScopeClass::assertMinConsume($hb, $resolvedProduct ?? [], $goodsOnlyPrice ?? $modifyPrice);
+                    // 此时 $modifyPrice 仍为纯商品合计,红包抵扣额以其为上限
+                    $hbDeduct = $hb->amount;
+                    if (bccomp($hbDeduct, $modifyPrice, 2) > 0) {
+                        $hbDeduct = $modifyPrice;
+                    }
+                    $modifyPrice = bcsub($modifyPrice, $hbDeduct, 2);
+                    // 回写实际抵扣额:createHdOrder 以此核算原价 prePrice 与优惠额,保持口径一致
+                    $post['hbAmount'] = $hbDeduct;
+                } else {
+                    util::fail('红包无效');
+                }
+            }
+
+            // 跑腿费(sendType=2 实际收取部分):不参与会员折/门店满减基数,随单传给折扣落地层剔除
+            $runnerSendCost = 0;
             // 跑腿 style=2:按包运费花束 / 不包运费花束 / 纯花材满减 + 跑腿或自定义(异常兜底)
             if (isset($post['sendType']) && (int)$post['sendType'] === 2) {
                 if ((int)$openIntraCity === 2) {
@@ -1368,6 +1414,7 @@ class OrderController extends BaseController
                 $post['sendDistance'] = $distanceMeters;
                 $post['freightSource'] = $freightSource;
                 $modifyPrice = bcadd($modifyPrice, $sendCost, 2);
+                $runnerSendCost = $sendCost;
             }
 
             if ($unMeetSendCost > 0) {
@@ -1379,40 +1426,6 @@ class OrderController extends BaseController
                 $modifyPrice = bcadd($modifyPrice, $packingFee, 2);
             }
 
-            // 红包(归属花店客户,与 buy-item 一致)
-            $hbId = $post['hbId'] ?? 0;
-            $hb = null;
-            if ($hbId > 0) {
-                $hb = HbClass::getById($hbId, true);
-                if (!empty($hb)) {
-                    if ($hb->customId != $hdCustomId) {
-                        util::fail('不是你的红包');
-                    }
-                    if ($hb->amount != $post['hbAmount']) {
-                        util::fail('红包金额不相等');
-                    }
-                    if ($hb->status == 1) {
-                        util::fail('红包已使用');
-                    }
-                    if ($hb->status == -1) {
-                        util::fail('红包已作废');
-                    }
-                    if ($hb->endTime < time()) {
-                        $hb->status = -1;
-                        $hb->save();
-                        util::fail('红包已失效');
-                    }
-                    if ($hb->beginTime > time()) {
-                        util::fail('红包可使用期未到');
-                    }
-                    // 混合结算:按适用范围用商品明细校验最低消费(不含运费包装费)
-                    HbScopeClass::assertMinConsume($hb, $resolvedProduct ?? [], $goodsOnlyPrice ?? $modifyPrice);
-                    $modifyPrice = bcsub($modifyPrice, $hb->amount, 2);
-                } else {
-                    util::fail('红包无效');
-                }
-            }
-
             $psMethodError = \bizHd\order\classes\PsMethodClass::checkLimit(
                 $this->shopId,
                 $this->mainId,
@@ -1444,10 +1457,13 @@ class OrderController extends BaseController
             $post['hasSeckill'] = $hasSeckillGoods ? 1 : 0;
             $post['seckillGoodsPrice'] = $seckillGoodsPrice;
             $post['seckillGoodsNum'] = $seckillGoodsNum;
-            // 互斥比较与落单使用同一口径:秒杀活动价不参与会员折和门店满减
+            // 跑腿费随单传给折扣落地层(createHdOrder 会员折 / addOrder 门店满减),从可折扣基数剔除
+            $post['runnerSendCost'] = $runnerSendCost;
+            // 互斥比较与落单使用同一口径:秒杀活动价、跑腿费不参与会员折和门店满减
+            $wholeDiscountPrice = bcsub($modifyPrice, $runnerSendCost, 2);
             $wholeDiscountPrice = bcsub(
-                $modifyPrice,
-                HdOrderClass::capSeckillExcludePrice($seckillGoodsPrice, $modifyPrice),
+                $wholeDiscountPrice,
+                HdOrderClass::capSeckillExcludePrice($seckillGoodsPrice, $wholeDiscountPrice),
                 2
             );
             $wholeDiscountNum = bcsub($unMeetBigNum, $seckillGoodsNum, 2);

+ 5 - 3
biz-hd/order/classes/OrderClass.php

@@ -195,15 +195,17 @@ class OrderClass extends BaseClass
         $skipShopMeetCut = !empty($data['skipShopMeetCut']) || intval($data['groupBuyId'] ?? 0) > 0;
         $seckillGoodsPrice = self::capSeckillExcludePrice($data['seckillGoodsPrice'] ?? 0, $data['actPrice'] ?? 0);
         $seckillGoodsNum = isset($data['seckillGoodsNum']) && is_numeric($data['seckillGoodsNum'] ?? 0) ? $data['seckillGoodsNum'] : 0;
-        unset($data['skipShopMeetCut'], $data['skipMemberDiscount']); //$data['seckillGoodsPrice'], $data['seckillGoodsNum']
+        // 跑腿费不参与门店满减:从满减基数剔除(mall 混合单传入,其他单为 0);非表字段,落库前一并移除
+        $runnerSendCost = isset($data['runnerSendCost']) && $data['runnerSendCost'] > 0 ? $data['runnerSendCost'] : 0;
+        unset($data['skipShopMeetCut'], $data['skipMemberDiscount'], $data['runnerSendCost']); //$data['seckillGoodsPrice'], $data['seckillGoodsNum']
         if ($book == 0 && !$skipShopMeetCut) {
             $actPrice = $data['actPrice'];
             $meetNum = $shop->meetNum ?? 0;
             $meetAmount = $shop->meetAmount ?? 0;
             $cutOption = $shop->cutAmount ?? 0;
             $cutStyle = $shop->cutStyle ?? 0;
-            // 满减金额门槛和折扣额按剔除秒杀后的应付算,避免秒杀价凑满减或被打折
-            $discountablePrice = bcsub($actPrice, $seckillGoodsPrice, 2);
+            // 满减金额门槛和折扣额按剔除秒杀、跑腿费后的应付算,避免秒杀价凑满减或被打折、跑腿费被减免
+            $discountablePrice = bcsub(bcsub($actPrice, $seckillGoodsPrice, 2), $runnerSendCost, 2);
             // 满减件数门槛不含秒杀份数,最低消费数量仍用订单 bigNum
             $meetBigNum = isset($data['bigNum']) ? bcsub($data['bigNum'], $seckillGoodsNum, 2) : 0;
             if (bccomp($meetBigNum, '0', 2) < 0) {

+ 1 - 1
biz-hd/order/classes/WholeOrderDiscountClass.php

@@ -20,7 +20,7 @@ class WholeOrderDiscountClass
      * 在红包后的应付上比较会员折扣额与门店满减额,只选较大者。
      * 相等时用会员折扣,避免两层同时落地。
      *
-     * @param float|string $basePrice 商品+运费/包装-红包
+     * @param float|string $basePrice 商品+运费/包装-红包(不含跑腿费,跑腿费不参与整单优惠)
      * @param float|string $bigNum 花材扎数 + 花束份数(与订单 bigNum 一致)
      * @param float|string $customDiscount 客户折扣 0~1,1 表示无折扣
      * @param object|array $shop 门店 meetNum/meetAmount/cutAmount/cutStyle

+ 4 - 2
biz-hd/order/services/OrderService.php

@@ -246,14 +246,16 @@ class OrderService extends BaseService
         $memberDiscount = $custom->discount ?? 1;
         // 秒杀已是活动价,会员折只打在非秒杀应付上,折后再加回秒杀额
         $seckillGoodsPrice = OrderClass::capSeckillExcludePrice($data['seckillGoodsPrice'] ?? 0, $modifyPrice);
+        // 跑腿费不参与会员折扣:从可折扣基数剔除,折后原额加回(mall 混合单传入,其他单为 0)
+        $runnerSendCost = isset($data['runnerSendCost']) && $data['runnerSendCost'] > 0 ? $data['runnerSendCost'] : 0;
         if (!$skipMemberDiscount && is_numeric($memberDiscount) && $memberDiscount > 0 && $memberDiscount < 1.0 && $modifyPrice > 0) {
-            $discountablePrice = bcsub($modifyPrice, $seckillGoodsPrice, 2);
+            $discountablePrice = bcsub(bcsub($modifyPrice, $seckillGoodsPrice, 2), $runnerSendCost, 2);
             if (bccomp($discountablePrice, '0', 2) > 0) {
                 $discountedPrice = bcmul($discountablePrice, $memberDiscount, 2);
                 if (bccomp($discountedPrice, '0', 2) <= 0) {
                     $discountedPrice = 0.01;
                 }
-                $modifyPrice = bcadd($discountedPrice, $seckillGoodsPrice, 2);
+                $modifyPrice = bcadd(bcadd($discountedPrice, $seckillGoodsPrice, 2), $runnerSendCost, 2);
             }
         }
         //优惠