From 18546a254c7e563c3cf3d37c57798944b11a11ab Mon Sep 17 00:00:00 2001 From: wuhui_zzw <1760308791@qq.com> Date: Tue, 22 Aug 2023 16:00:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=EF=BC=9A=E7=89=A9=E6=B5=81?= =?UTF-8?q?=E8=BF=90=E8=B4=B9=E8=AE=A1=E7=AE=97=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beike/Models/Logistics.php | 26 ++- beike/Repositories/CountryRepo.php | 4 +- beike/Services/ShippingMethodService.php | 15 ++ .../Http/Controllers/ProductController.php | 4 +- beike/Shop/Services/CheckoutService.php | 7 +- .../TotalServices/ShippingService.php | 180 +++++++++++++++--- resources/lang/en/admin/logistics.php | 1 + resources/lang/zh_cn/admin/logistics.php | 1 + themes/default/checkout.blade.php | 74 +++++-- 9 files changed, 259 insertions(+), 53 deletions(-) diff --git a/beike/Models/Logistics.php b/beike/Models/Logistics.php index 9df21ee2..6994b056 100644 --- a/beike/Models/Logistics.php +++ b/beike/Models/Logistics.php @@ -5,8 +5,7 @@ namespace Beike\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes; -class Logistics extends Base -{ +class Logistics extends Base{ use HasFactory; use SoftDeletes; @@ -27,9 +26,26 @@ class Logistics extends Base 'day_max', 'position' ]; + /** + * Common: 根据国家ID 获取所有关联的物流列表 + * Author: wu-hui + * Time: 2023/08/22 10:31 + * @param int $countryId + * @param string[] $field + * @return array + */ + public static function getAll(int $countryId,$field = ['id','name','warehouse_name','type','day_min','day_max']){ + $list = self::select($field) + ->whereRaw(\DB::raw('FIND_IN_SET('.$countryId.',country_ids)')) + ->orderBy('position','ASC') + ->orderBy('id','ASC') + ->get(); - public function country() - { - return $this->belongsTo(Country::class, 'country_id', 'id'); + + return $list ? $list->toArray() : []; + } + + public function country(){ + return $this->belongsTo(Country::class,'country_id','id'); } } diff --git a/beike/Repositories/CountryRepo.php b/beike/Repositories/CountryRepo.php index a32540de..abbe02da 100644 --- a/beike/Repositories/CountryRepo.php +++ b/beike/Repositories/CountryRepo.php @@ -137,7 +137,7 @@ class CountryRepo $builder = Country::query() ->where('name', 'like', "$name%") ->select('id', 'name', 'status', 'code') - ->orderBy('sort_order','DESC') + ->orderBy('sort_order','ASC') ->orderBy('id','ASC'); // if ($onlyActive) { // $builder->where('status', 1); @@ -156,7 +156,7 @@ class CountryRepo $list = Country::query() ->whereIn('id', $ids) ->select('id', 'name', 'code') - ->orderBy('sort_order','DESC') + ->orderBy('sort_order','ASC') ->orderBy('id','ASC') ->get(); if($list) { diff --git a/beike/Services/ShippingMethodService.php b/beike/Services/ShippingMethodService.php index 52776df9..37902630 100644 --- a/beike/Services/ShippingMethodService.php +++ b/beike/Services/ShippingMethodService.php @@ -12,6 +12,7 @@ namespace Beike\Services; use Beike\Admin\Http\Resources\PluginResource; +use Beike\Models\Logistics; use Beike\Repositories\PluginRepo; use Beike\Shop\Services\CheckoutService; use Illuminate\Support\Str; @@ -43,12 +44,26 @@ class ShippingMethodService if ($quotes) { $pluginResource = (new PluginResource($plugin))->jsonSerialize(); $shippingMethods[] = [ + 'is_logistics' => 0, 'code' => $pluginCode, 'name' => $pluginResource['name'], 'quotes' => $quotes, ]; } } + // 获取根据收货地址 获取物流信息 + if($checkout->cart->guest_shipping_address || $checkout->cart->guest_payment_address){ + $address = $checkout->cart->guest_shipping_address ?? $checkout->cart->guest_payment_address; + $logisticsList = Logistics::getAll($address['country_id']); + foreach($logisticsList as $logisticsItem){ + $shippingMethods[] = [ + 'is_logistics' => 1, + 'code' => $logisticsItem['id'], + 'name' => $logisticsItem['name'], + 'quotes' => $logisticsItem, + ]; + } + } return $shippingMethods; } diff --git a/beike/Shop/Http/Controllers/ProductController.php b/beike/Shop/Http/Controllers/ProductController.php index d4004cc8..9af55380 100644 --- a/beike/Shop/Http/Controllers/ProductController.php +++ b/beike/Shop/Http/Controllers/ProductController.php @@ -89,8 +89,8 @@ class ProductController extends Controller 'product_id' => $item['product_id'], 'product_sku_id' => $item['product_sku_id'], 'quantity' => $item['quantity'], - 'product' => Product::where('id',40)->first()->toArray(), - 'sku' => ProductSku::where('id',641)->first()->toArray(), + 'product' => Product::where('id',$item['product_id'])->first()->toArray(), + 'sku' => ProductSku::where('id',$item['product_sku_id'])->first()->toArray(), ]; } $cartItems = collect($diyData)->mapInto(CartProduct::class); diff --git a/beike/Shop/Services/CheckoutService.php b/beike/Shop/Services/CheckoutService.php index 530bb045..9abbe15f 100644 --- a/beike/Shop/Services/CheckoutService.php +++ b/beike/Shop/Services/CheckoutService.php @@ -252,16 +252,19 @@ class CheckoutService foreach ($shipments as $shipment) { $shipmentCodes = array_merge($shipmentCodes, array_column($shipment['quotes'], 'code')); } - if (! in_array($currentCart->shipping_method_code, $shipmentCodes)) { + // 判断:如果运费方式不存在 并且不是物流则设置第一个物流信息 + if (!in_array($currentCart->shipping_method_code, $shipmentCodes) && !is_int($currentCart->shipping_method_code)) { $this->updateShippingMethod($shipmentCodes[0] ?? ''); $this->totalService->setShippingMethod($shipmentCodes[0] ?? ''); } + $shippingMethodCode = !empty($currentCart->shipping_method_code) ? $currentCart->shipping_method_code : ($shipments[0]['code'] ?? ''); + $data = [ 'current' => [ 'shipping_address_id' => $currentCart->shipping_address_id, 'guest_shipping_address' => $currentCart->guest_shipping_address, - 'shipping_method_code' => $currentCart->shipping_method_code, + 'shipping_method_code' => $shippingMethodCode, 'payment_address_id' => $currentCart->payment_address_id, 'guest_payment_address' => $currentCart->guest_payment_address, 'payment_method_code' => $currentCart->payment_method_code, diff --git a/beike/Shop/Services/TotalServices/ShippingService.php b/beike/Shop/Services/TotalServices/ShippingService.php index ead0dbc9..1df0b4a3 100644 --- a/beike/Shop/Services/TotalServices/ShippingService.php +++ b/beike/Shop/Services/TotalServices/ShippingService.php @@ -12,52 +12,176 @@ namespace Beike\Shop\Services\TotalServices; +use Beike\Libraries\Weight; +use Beike\Models\Logistics; use Beike\Shop\Services\CheckoutService; use Illuminate\Support\Str; -class ShippingService -{ - /** - * @param CheckoutService $checkout - * @return array|null - * @throws \Exception|\Throwable - */ - public static function getTotal(CheckoutService $checkout): ?array - { +class ShippingService{ + // 运费计算 + public static function getTotal(CheckoutService $checkout): ?array{ $totalService = $checkout->totalService; $shippingMethod = $totalService->getShippingMethod(); - if (empty($shippingMethod)) { - return null; + $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 = $checkout->selectedProducts->toArray() ?? []; + $productsList = collect($products)->groupBy('product_id')->map(function($group){ + $firstInfo = $group->first(); + // 重量转换 - 物流重量单位为 千克;商品重量需要进行转换 + $weight = $firstInfo['product']['weight'] ?? 0; + $sumQuantity = $group->sum('quantity'); + $sumWeight = (float)sprintf("%.2f",($weight * $sumQuantity)); + $weightClass = $firstInfo['product']['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); + } } - $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); $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 + $address = $cart->guest_shipping_address ?? $cart->guest_payment_address; + $countryId = $address['country_id']; + // 获取全部物流 返回第一个物流信息ID + $logisticsList = Logistics::getAll($countryId,['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; + } + + /** * 通过配送方式获取插件编码 diff --git a/resources/lang/en/admin/logistics.php b/resources/lang/en/admin/logistics.php index c90e8fe7..8dc7027f 100644 --- a/resources/lang/en/admin/logistics.php +++ b/resources/lang/en/admin/logistics.php @@ -77,4 +77,5 @@ return [ 'add_num'=>'Add num', 'continuation_num_fee'=>'Continuation num fee', 'freight_calculation_method'=>'Freight calculation method', + 'logistics_duration' => 'Delivery within :day working days after shipment', ]; diff --git a/resources/lang/zh_cn/admin/logistics.php b/resources/lang/zh_cn/admin/logistics.php index d2464b26..dde7232c 100644 --- a/resources/lang/zh_cn/admin/logistics.php +++ b/resources/lang/zh_cn/admin/logistics.php @@ -76,4 +76,5 @@ return [ 'add_num' => '每增加件', 'continuation_num_fee' => '续件运费', 'freight_calculation_method' => '运费计算方式', + 'logistics_duration' => '发货后:day个工作日内交货', ]; diff --git a/themes/default/checkout.blade.php b/themes/default/checkout.blade.php index 1dcc9106..1051a45d 100644 --- a/themes/default/checkout.blade.php +++ b/themes/default/checkout.blade.php @@ -47,19 +47,42 @@
{{ __('shop/checkout.delivery_method') }}
@foreach ($shipping_methods as $methods) - @foreach ($methods['quotes'] as $shipping) -
-
- - + @if(!$methods['is_logistics']) + @foreach ($methods['quotes'] as $shipping) +
+
+ + +
+
+
{{ $shipping['name'] }}
+
{!! $shipping['description'] !!}
+
{!! $shipping['html'] ?? '' !!}
+
-
-
{{ $shipping['name'] }}
-
{!! $shipping['description'] !!}
-
{!! $shipping['html'] ?? '' !!}
+ @endforeach + @else +
+
+ +
+
+
{{ $methods['name'] }}({{ $methods['quotes']['warehouse_name'] }})
+
+ @if($methods['quotes']['type'] == 'weight') + {{ __('admin/logistics.type_weight') }} + @elseif ($methods['quotes']['type'] == 'num') + {{ __('admin/logistics.type_num') }} + @elseif ($methods['quotes']['type'] == 'free') + {{ __('admin/logistics.type_free') }} + @endif + + {{ __('admin/logistics.logistics_duration',['day'=> $methods['quotes']['day_min'].'-'.$methods['quotes']['day_max'] ]) }} + +
+
-
- @endforeach + @endif @endforeach
@@ -194,8 +217,9 @@ let html = ''; data.forEach((methods) => { - methods.quotes.forEach((quote) => { - html += `
+ if(!methods.is_logistics){ + methods.quotes.forEach((quote) => { + html += `
@@ -206,7 +230,29 @@
${quote.html || ''}
`; - }) + }) + } + else{ + // 获取计费方式 + let typeText = '{{ __('admin/logistics.type_weight') }}'; + if(methods.quotes.type === 'num') typeText = '{{ __('admin/logistics.type_num') }}'; + else if(methods.quotes.type === 'free') typeText = '{{ __('admin/logistics.type_free') }}'; + // 获取配送时间 + let logistics_duration = '{{ __('admin/logistics.logistics_duration') }}' + logistics_duration = logistics_duration.replace(":day", methods.quotes.day_min + '-' + methods.quotes.day_max); + html += `
+
+ +
+
+
${methods.name}(${methods.quotes.warehouse_name} }})
+
+ ${typeText} + ${logistics_duration} +
+
+
`; + } }) $('#shipping-methods-wrap').html(html);