Ver Fonte

优化:订单结算使用优惠券时仅折扣适用商品

Yahoo há 4 anos atrás
pai
commit
c70498a885

+ 12 - 26
app/api/model/UserCoupon.php

@@ -13,11 +13,12 @@ declare (strict_types=1);
 namespace app\api\model;
 
 use app\api\service\User as UserService;
-use app\common\exception\BaseException;
 use app\common\model\UserCoupon as UserCouponModel;
+use app\common\enum\coupon\CouponType as CouponTypeEnum;
 use app\common\enum\coupon\ExpireType as ExpireTypeEnum;
 use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
 use app\common\library\helper;
+use cores\exception\BaseException;
 
 /**
  * 用户优惠券模型
@@ -206,33 +207,18 @@ class UserCoupon extends UserCouponModel
     {
         // 获取用户可用的优惠券列表
         $list = (new static)->getList($userId, ['dataType' => 'isUsable', 'amount' => $orderPayPrice]);
-        $data = [];
-        foreach ($list as $coupon) {
-            // 有效期范围内
-            if ($coupon['start_time'] > time()) continue;
-            $key = $coupon['user_coupon_id'];
-            $data[$key] = [
-                'user_coupon_id' => $coupon['user_coupon_id'],
-                'name' => $coupon['name'],
-                'coupon_type' => $coupon['coupon_type'],
-                'reduce_price' => $coupon['reduce_price'],
-                'discount' => $coupon['discount'],
-                'min_price' => $coupon['min_price'],
-                'expire_type' => $coupon['expire_type'],
-                'start_time' => $coupon['start_time'],
-                'end_time' => $coupon['end_time'],
-                'apply_range' => $coupon['apply_range'],
-                'apply_range_config' => $coupon['apply_range_config']
-            ];
-            // 计算打折金额
-            if ($coupon['coupon_type'] == 20) {
-                $reducePrice = helper::bcmul($orderPayPrice, $coupon['discount'] / 10);
-                $data[$key]['reduced_price'] = helper::bcsub($orderPayPrice, $reducePrice);
-            } else
-                $data[$key]['reduced_price'] = $coupon['reduce_price'];
+        $data = $list->isEmpty() ? [] : $list->toArray()['data'];
+        foreach ($data as &$item) {
+            // 计算最大能折扣的金额
+            if ($item['coupon_type'] == CouponTypeEnum::DISCOUNT) {
+                $reducePrice = helper::bcmul($orderPayPrice, $item['discount'] / 10);
+                $item['reduced_price'] = helper::bcsub($orderPayPrice, $reducePrice);
+            } else {
+                $item['reduced_price'] = $item['reduce_price'];
+            }
         }
         // 根据折扣金额排序并返回
-        return array_sort($data, 'reduced_price', true);
+        return !empty($data) ? array_sort($data, 'reduced_price', true) : [];
     }
 
     /**

+ 141 - 37
app/api/service/coupon/GoodsDeduct.php

@@ -12,8 +12,11 @@ declare (strict_types=1);
 
 namespace app\api\service\coupon;
 
+use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
+use app\common\enum\coupon\CouponType as CouponTypeEnum;
 use app\common\service\BaseService;
 use app\common\library\helper;
+use cores\exception\BaseException;
 
 /**
  * 订单优惠券折扣服务类
@@ -22,30 +25,104 @@ use app\common\library\helper;
  */
 class GoodsDeduct extends BaseService
 {
+    // 实际抵扣金额
     private $actualReducedMoney;
 
-    public function setGoodsCouponMoney($goodsList, $reducedMoney)
+    // 订单商品列表
+    private $goodsList = [];
+
+    // 优惠券信息
+    private $couponInfo = [];
+
+    // 实际参与优惠券折扣的商品记录
+    private $rangeGoodsList = [];
+
+    // 设置订单商品列表
+    public function setGoodsList(iterable $goodsList): GoodsDeduct
     {
-        // 统计订单商品总金额,(单位分)
-        $orderTotalPrice = 0;
-        foreach ($goodsList as &$goods) {
-            $goods['total_price'] *= 100;
-            $orderTotalPrice += $goods['total_price'];
-        }
-        // 计算实际抵扣金额
-        $this->setActualReducedMoney($reducedMoney, $orderTotalPrice);
-        // 实际抵扣金额为0,
+        $this->goodsList = $goodsList;
+        return $this;
+    }
+
+    // 设置优惠券信息
+    public function setCouponInfo(array $couponInfo): GoodsDeduct
+    {
+        $this->couponInfo = $couponInfo;
+        return $this;
+    }
+
+    /**
+     * 计算优惠券抵扣金额
+     * @return GoodsDeduct
+     * @throws BaseException
+     */
+    public function setGoodsCouponMoney(): GoodsDeduct
+    {
+        // 验证当前类属性
+        $this->checkAttribute();
+        // 设置实际参与优惠券折扣的商品记录
+        $this->setRangeGoodsList();
+        // 计算实际抵扣的金额
+        $this->setActualReducedMoney();
+        // 实际抵扣金额为0
         if ($this->actualReducedMoney > 0) {
             // 计算商品的价格权重
-            $goodsList = $this->getGoodsListWeight($goodsList, $orderTotalPrice);
+            $this->setGoodsListWeight();
             // 计算商品优惠券抵扣金额
-            $this->setGoodsListCouponMoney($goodsList);
-            // 总抵扣金额
-            $totalCouponMoney = helper::getArrayColumnSum($goodsList, 'coupon_money');
-            $this->setGoodsListCouponMoneyFill($goodsList, $totalCouponMoney);
-            $this->setGoodsListCouponMoneyDiff($goodsList, $totalCouponMoney);
+            $this->setGoodsListCouponMoney();
+            // 总抵扣金额 (已分配的)
+            $assignedCouponMoney = helper::getArrayColumnSum($this->rangeGoodsList, 'coupon_money');
+            // 分配剩余的抵扣金额
+            $this->setGoodsListCouponMoneyFill($assignedCouponMoney);
+            $this->setGoodsListCouponMoneyDiff($assignedCouponMoney);
+        }
+        return $this;
+    }
+
+    // 获取实际参与优惠券折扣的商品记录
+    public function getRangeGoodsList(): array
+    {
+        return $this->rangeGoodsList;
+    }
+
+    /**
+     * 设置实际参与优惠券折扣的商品记录
+     * @return void
+     */
+    private function setRangeGoodsList()
+    {
+        $this->rangeGoodsList = [];
+        foreach ($this->goodsList as $goods) {
+            $goods['total_price'] *= 100;
+            switch ($this->couponInfo['apply_range']) {
+                case ApplyRangeEnum::ALL:
+                    $this->rangeGoodsList[$goods['goods_id']] = $goods;
+                    break;
+                case ApplyRangeEnum::SOME:
+                    if (in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['applyGoodsIds'])) {
+                        $this->rangeGoodsList[$goods['goods_id']] = $goods;
+                    }
+                    break;
+                case ApplyRangeEnum::EXCLUDE:
+                    if (!in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['excludedGoodsIds'])) {
+                        $this->rangeGoodsList[$goods['goods_id']] = $goods;
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * 验证当前类属性
+     * @throws BaseException
+     */
+    private function checkAttribute()
+    {
+        if (empty($this->goodsList) || empty($this->couponInfo)) {
+            throwError('goodsList or couponInfo not found.');
         }
-        return $goodsList;
     }
 
     public function getActualReducedMoney()
@@ -53,54 +130,81 @@ class GoodsDeduct extends BaseService
         return $this->actualReducedMoney;
     }
 
-    private function setActualReducedMoney($reducedMoney, $orderTotalPrice)
+    /**
+     * 计算实际抵扣的金额
+     */
+    private function setActualReducedMoney()
     {
-        $reducedMoney *= 100;
-        $this->actualReducedMoney = ($reducedMoney >= $orderTotalPrice) ? $orderTotalPrice - 1 : $reducedMoney;
+        // 获取当前订单商品总额
+        $orderTotalPrice = $this->getOrderTotalPrice();
+        // 计算最大抵扣金额
+        $reducedMoney = 0;
+        foreach ($this->rangeGoodsList as $goods) {
+            $reducedMoney += $goods['total_price'];
+        }
+        // 计算打折金额
+        if ($this->couponInfo['coupon_type'] == CouponTypeEnum::DISCOUNT) {
+            $reducePrice = $reducedMoney - ($reducedMoney * ($this->couponInfo['discount'] / 10));
+        } else {
+            $reducePrice = $this->couponInfo['reduce_price'];
+        }
+        // 优惠券最大允许抵扣到一分钱,所以此处判断抵扣金额大于等于订单金额时,减去一分钱
+        $this->actualReducedMoney = ($reducePrice >= $orderTotalPrice) ? $orderTotalPrice - 1 : $reducePrice;
     }
 
-    private function arraySortByWeight($goodsList)
+    /**
+     * 获取当前订单商品总额
+     * @return float|int
+     */
+    private function getOrderTotalPrice()
     {
-        return array_sort($goodsList, 'weight', true);
+        $orderTotalPrice = 0;
+        foreach ($this->goodsList as $goods) {
+            $orderTotalPrice += ($goods['total_price'] * 100);
+        }
+        return $orderTotalPrice;
     }
 
-    private function getGoodsListWeight($goodsList, $orderTotalPrice)
+    /**
+     * 计算商品抵扣的权重(占比)
+     */
+    private function setGoodsListWeight()
     {
-        foreach ($goodsList as &$goods) {
+        $orderTotalPrice = helper::getArrayColumnSum($this->rangeGoodsList, 'total_price');
+        foreach ($this->rangeGoodsList as &$goods) {
             $goods['weight'] = $goods['total_price'] / $orderTotalPrice;
         }
-        return $this->arraySortByWeight($goodsList);
+        array_sort($this->rangeGoodsList, 'weight', true);
     }
 
-    private function setGoodsListCouponMoney(&$goodsList)
+    /**
+     * 计算商品抵扣的金额
+     */
+    private function setGoodsListCouponMoney(): void
     {
-        foreach ($goodsList as &$goods) {
+        foreach ($this->rangeGoodsList as &$goods) {
             $goods['coupon_money'] = helper::bcmul($this->actualReducedMoney, $goods['weight'], 0);
         }
-        return true;
     }
 
-    private function setGoodsListCouponMoneyFill(&$goodsList, $totalCouponMoney)
+    private function setGoodsListCouponMoneyFill($assignedCouponMoney): void
     {
-        if ($totalCouponMoney === 0) {
+        if ($assignedCouponMoney === 0) {
             $temReducedMoney = $this->actualReducedMoney;
-            foreach ($goodsList as &$goods) {
+            foreach ($this->rangeGoodsList as &$goods) {
                 if ($temReducedMoney === 0) break;
                 $goods['coupon_money'] = 1;
                 $temReducedMoney--;
             }
         }
-        return true;
     }
 
-    private function setGoodsListCouponMoneyDiff(&$goodsList, $totalCouponMoney)
+    private function setGoodsListCouponMoneyDiff($assignedCouponMoney): void
     {
-        $tempDiff = $this->actualReducedMoney - $totalCouponMoney;
-        foreach ($goodsList as &$goods) {
+        $tempDiff = $this->actualReducedMoney - $assignedCouponMoney;
+        foreach ($this->rangeGoodsList as &$goods) {
             if ($tempDiff < 1) break;
             $goods['coupon_money']++ && $tempDiff--;
         }
-        return true;
     }
-
 }

+ 15 - 10
app/api/service/order/Checkout.php

@@ -34,7 +34,7 @@ use app\common\service\BaseService;
 use app\common\service\delivery\Express as ExpressService;
 use app\common\service\goods\source\Factory as StockFactory;
 use app\common\library\helper;
-use app\common\exception\BaseException;
+use cores\exception\BaseException;
 
 /**
  * 订单结算台服务类
@@ -152,7 +152,6 @@ class Checkout extends BaseService
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
-     * @throws \cores\exception\BaseException
      */
     public function onCheckout($goodsList): array
     {
@@ -168,7 +167,6 @@ class Checkout extends BaseService
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
-     * @throws \cores\exception\BaseException
      */
     private function checkout(): array
     {
@@ -518,7 +516,7 @@ class Checkout extends BaseService
      * @param array $couponList 当前用户可用的优惠券列表
      * @param int $couponId 当前选择的优惠券id
      * @return void
-     * @throws \cores\exception\BaseException
+     * @throws BaseException
      */
     private function setOrderCouponMoney(array $couponList, int $couponId): void
     {
@@ -538,12 +536,17 @@ class Checkout extends BaseService
         // 获取优惠券信息
         $couponInfo = $this->getCouponInfo($couponId, $couponList);
         // 计算订单商品优惠券抵扣金额
-        $goodsListTemp = helper::getArrayColumns($this->goodsList, ['total_price']);
+        $goodsListTemp = helper::getArrayColumns($this->goodsList, ['goods_id', 'total_price']);
         $CouponMoney = new GoodsDeductService;
-        $completed = $CouponMoney->setGoodsCouponMoney($goodsListTemp, $couponInfo['reduced_price']);
+        $rangeGoodsList = $CouponMoney->setGoodsList($goodsListTemp)
+            ->setCouponInfo($couponInfo)
+            ->setGoodsCouponMoney($goodsListTemp, $couponInfo)
+            ->getRangeGoodsList();
         // 分配订单商品优惠券抵扣金额
-        foreach ($this->goodsList as $key => &$goods) {
-            $goods['coupon_money'] = $completed[$key]['coupon_money'] / 100;
+        foreach ($this->goodsList as &$goods) {
+            if (isset($rangeGoodsList[$goods['goods_id']])) {
+                $goods['coupon_money'] = $rangeGoodsList[$goods['goods_id']]['coupon_money'] / 100;
+            }
         }
         // 记录订单优惠券信息
         $this->orderData['couponId'] = $couponId;
@@ -555,7 +558,7 @@ class Checkout extends BaseService
      * @param int $couponId
      * @param $couponList
      * @return bool
-     * @throws \cores\exception\BaseException
+     * @throws BaseException
      */
     private function verifyOrderCouponId(int $couponId, $couponList): bool
     {
@@ -646,7 +649,9 @@ class Checkout extends BaseService
      * @param $order
      * @return bool
      * @throws BaseException
-     * @throws \Exception
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
      */
     private function createOrderEvent($order): bool
     {