* @created 2022-07-22 17:58:14 * @modified 2022-07-22 17:58:14 */ namespace Beike\Shop\Services\TotalServices; use Beike\Libraries\Weight; use Beike\Models\Logistics; use Beike\Models\Product; use Beike\Shop\Services\CheckoutService; use Illuminate\Support\Str; class ShippingService{ // 运费计算 public static function getTotal(CheckoutService $checkout): ?array{ $totalService = $checkout->totalService; $shippingMethod = $totalService->getShippingMethod(); $amount = 0; if($shippingMethod && !is_int($checkout->cart->shipping_method_code)){ // 通过插件(固定运费) 进行计算 if(empty($shippingMethod)) return NULL; $shippingPluginCode = self::parseShippingPluginCode($shippingMethod); $pluginCode = Str::studly($shippingPluginCode); if(!app('plugin')->checkActive($shippingPluginCode)){ $cart = $checkout->cart; $cart->shipping_method_code = ''; $cart->saveOrFail(); return []; } $className = "Plugin\\{$pluginCode}\\Bootstrap"; if(!method_exists($className,'getShippingFee')){ throw new \Exception("请在插件 {$className} 实现方法: public function getShippingFee(CheckoutService \$checkout)"); } $amount = (float)(new $className)->getShippingFee($checkout); } else{ // 通过物流进行计算 $logisticsId = (int)$checkout->cart->shipping_method_code; $logisticsInfo = self::getLogistics($logisticsId,$checkout->cart); if($logisticsInfo) { // 商品信息处理 获取同一个商品的数量 $products = $totalService->getCartProducts(); // $products = $checkout->selectedProducts->toArray() ?? []; $productsList = collect($products)->groupBy('product_id')->map(function($group){ $firstInfo = $group->first(); $productWeightInfo = Product::query() ->select(['id','weight','weight_class']) ->find($firstInfo['product_id']); if($productWeightInfo) $productWeightInfo = $productWeightInfo->toArray(); // 重量转换 - 物流重量单位为 千克;商品重量需要进行转换 $weight = $productWeightInfo['weight'] ?? 0; $sumQuantity = $group->sum('quantity'); $sumWeight = (float)sprintf("%.2f",($weight * $sumQuantity)); $weightClass = $productWeightInfo['weight_class']; $sumWeight = Weight::convert($sumWeight,$weightClass);// 总重量 单位:克 return [ 'product_id' => $firstInfo['product_id'], 'sum_quantity' => $sumQuantity, 'sum_weight' => $sumWeight, 'weight' => $weight, 'weight_class' => $weightClass, ]; }); // weight:按重量计费,num:按数量计费,free:卖家包邮 $logisticsHandleFun = 'compute_'.$logisticsInfo['type']; $amount = self::$logisticsHandleFun($logisticsInfo,$productsList); } } $totalData = [ 'code' => 'shipping', 'title' => trans('shop/carts.shipping_fee'), 'amount' => $amount, 'amount_format' => currency_format($amount), ]; $totalService->amount += $totalData['amount']; $totalService->totals[] = $totalData; return $totalData; } /** * Common: 运费计算 - 物流运费 - 获取使用的物流 * Author: wu-hui * Time: 2023/08/22 11:55 * @param $logisticsId * @param $cart * @return array|mixed|mixed[] */ private static function getLogistics($logisticsId,$cart){ if($logisticsId <= 0){ // 获取当前收货地址国家ID $cartArr = $cart->toArray(); $address = $cart->guest_shipping_address ?? $cart->guest_payment_address ?? $cartArr['payment_address'] ?? $cartArr['shipping_address']; if(!$address) return []; $countryId = $address['country_id']; // 获取全部物流 返回第一个物流信息ID $logisticsList = Logistics::getAll($countryId,['id','type','first_weight','first_weight_fee','continuation_weight_max','add_weight','continuation_weight_fee','num_fee']); return $logisticsList[0] ?? []; }else{ // 获取物流信息 $logisticsInfo = Logistics::query() ->select(['type','first_weight','first_weight_fee','continuation_weight_max','add_weight','continuation_weight_fee','num_fee']) ->find($logisticsId) ->toArray(); return $logisticsInfo; } } /** * Common: 运费计算 - 物流运费 - 运费计算(按重量计费) * Author: wu-hui * Time: 2023/08/22 15:34 * @param $logisticsInfo * @param $productsList * @return float|int */ private static function compute_weight($logisticsInfo,$productsList){ /** * 计算规则: * 总重量 - 低于首重;则运费 = 【首重运费】 * 总重量 - 高于首重;则运费 = 首重按照【首重运费】计算;(剩余重量 / 每增加重量 ) * 续重运费 */ $amount = (int)0; $firstWeight = (int)$logisticsInfo['first_weight'];// 首重 $firstWeightFee = (float)$logisticsInfo['first_weight_fee'];// 首重运费 $addWeight = (int)$logisticsInfo['add_weight'];// 每增加重量 $continuationWeightFee = (float)$logisticsInfo['continuation_weight_fee'];// 续重运费 // 循环处理商品 foreach($productsList as $productInfo){ // 首重运费 $amount += (float)sprintf("%.2f",$firstWeightFee); // 判断:如果总重量超过首重 则计算续重运费 $surplus = (int)$productInfo['sum_weight'] - $firstWeight; if($surplus > 0) $amount += (float)sprintf("%.2f",($surplus / $addWeight) * $continuationWeightFee); } return $amount; } /** * Common: 运费计算 - 物流运费 - 运费计算(按数量计费) * Author: wu-hui * Time: 2023/08/22 14:47 * @param $logisticsInfo * @param $productsList * @return float|int */ private static function compute_num($logisticsInfo,$productsList){ /** * 计算规则: * 购买数量 - 低于首件数量;则运费 = 【首件运费】 * 购买数量 - 高于首件数量;则运费 = 首件数量按照【首件运费】计算;(剩余购买数量 / 每增加件数量 ) * 续件运费 */ $amount = (int)0; $firstWeight = (int)$logisticsInfo['first_weight'];// 首件数量 $firstWeightFee = (float)$logisticsInfo['first_weight_fee'];// 首件运费 $addWeight = (int)$logisticsInfo['add_weight'];// 每增加件 $continuationWeightFee = (float)$logisticsInfo['continuation_weight_fee'];// 续件运费 // 循环处理商品 foreach($productsList as $productInfo){ // 首件运费 $amount += (float)sprintf("%.2f",$firstWeightFee); // 判断:如果数量超过首件数量 则计算续件运费 $surplus = (int)$productInfo['sum_quantity'] - $firstWeight; if($surplus > 0) $amount += (float)sprintf("%.2f",($surplus / $addWeight) * $continuationWeightFee); } return $amount; } /** * Common: 运费计算 - 物流运费 - 运费计算(卖家包邮) * Author: wu-hui * Time: 2023/08/22 11:59 * @param $logisticsInfo * @param $productsList * @return int */ private static function compute_free($logisticsInfo,$productsList){ return 0; } /** * 通过配送方式获取插件编码 * * @param $shippingMethod * @return string */ public static function parseShippingPluginCode($shippingMethod): string { $methodArray = explode('.', $shippingMethod); return $methodArray[0]; } }