249 lines
7.9 KiB
PHP
249 lines
7.9 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace app\common\service\delivery;
|
||
|
|
||
|
use app\common\library\helper;
|
||
|
use app\common\model\Setting as SettingModel;
|
||
|
use app\common\model\Delivery as DeliveryModel;
|
||
|
use app\common\enum\OrderType as OrderTypeEnum;
|
||
|
|
||
|
/**
|
||
|
* 快递配送服务类
|
||
|
* Class Delivery
|
||
|
* @package app\common\service
|
||
|
*/
|
||
|
class Express
|
||
|
{
|
||
|
private $cityId; // 用户收货城市id
|
||
|
private $goodsList; // 订单商品列表
|
||
|
private $orderType; // 订单类型 (主商城、拼团)
|
||
|
private $notInRuleGoodsId; // 不在配送范围的商品ID
|
||
|
|
||
|
// 运费模板数据集
|
||
|
private $data = [];
|
||
|
|
||
|
/**
|
||
|
* 构造方法
|
||
|
* Express constructor.
|
||
|
* @param $cityId
|
||
|
* @param $goodsList
|
||
|
* @param int $orderType
|
||
|
* @throws \think\db\exception\DataNotFoundException
|
||
|
* @throws \think\db\exception\ModelNotFoundException
|
||
|
* @throws \think\exception\DbException
|
||
|
*/
|
||
|
public function __construct($cityId, $goodsList, $orderType = OrderTypeEnum::MASTER)
|
||
|
{
|
||
|
// 赋值传参
|
||
|
$this->cityId = $cityId;
|
||
|
$this->goodsList = $goodsList;
|
||
|
$this->orderType = $orderType;
|
||
|
// 整合运费模板
|
||
|
$this->initDeliveryTemplate();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 验证用户收货地址是否在配送范围
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isIntraRegion()
|
||
|
{
|
||
|
if (!$this->cityId) return false;
|
||
|
foreach ($this->data as $item) {
|
||
|
$cityIds = [];
|
||
|
foreach ($item['delivery']['rule'] as $ruleItem) {
|
||
|
$cityIds = array_merge($cityIds, $ruleItem['region_data']);
|
||
|
}
|
||
|
if (!in_array($this->cityId, $cityIds)) {
|
||
|
$this->notInRuleGoodsId = current($item['goodsList'])['goods_id'];
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取不在配送范围的商品名称
|
||
|
* @return null
|
||
|
*/
|
||
|
public function getNotInRuleGoodsName()
|
||
|
{
|
||
|
$item = helper::getArrayItemByColumn($this->goodsList, 'goods_id', $this->notInRuleGoodsId);
|
||
|
return !empty($item) ? $item['goods_name'] : null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取订单的配送费用
|
||
|
* @return float|string
|
||
|
*/
|
||
|
public function getDeliveryFee()
|
||
|
{
|
||
|
if (empty($this->cityId) || empty($this->goodsList) || $this->notInRuleGoodsId > 0) {
|
||
|
return helper::number2(0.00);
|
||
|
}
|
||
|
// 处理商品包邮
|
||
|
$this->freeshipping();
|
||
|
// 计算配送金额
|
||
|
foreach ($this->data as &$item) {
|
||
|
// 计算当前配送模板的运费
|
||
|
$item['delivery_fee'] = $this->calcDeliveryAmount($item);
|
||
|
}
|
||
|
// 根据运费组合策略获取最终运费金额
|
||
|
return helper::number2($this->getFinalFreight());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 根据运费组合策略 计算最终运费
|
||
|
* @return double
|
||
|
*/
|
||
|
private function getFinalFreight()
|
||
|
{
|
||
|
// 运费合集
|
||
|
$expressPriceArr = helper::getArrayColumn($this->data, 'delivery_fee');
|
||
|
// 最终运费金额
|
||
|
$expressPrice = 0.00;
|
||
|
// 判断运费组合策略
|
||
|
switch (SettingModel::getItem('trade')['freight_rule']) {
|
||
|
case '10': // 策略1: 叠加
|
||
|
$expressPrice = array_sum($expressPriceArr);
|
||
|
break;
|
||
|
case '20': // 策略2: 以最低运费结算
|
||
|
$expressPrice = min($expressPriceArr);
|
||
|
break;
|
||
|
case '30': // 策略3: 以最高运费结算
|
||
|
$expressPrice = max($expressPriceArr);
|
||
|
break;
|
||
|
}
|
||
|
return $expressPrice;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 处理商品包邮
|
||
|
* @return bool
|
||
|
*/
|
||
|
private function freeshipping()
|
||
|
{
|
||
|
// 订单商品总金额
|
||
|
$orderTotalPrice = helper::getArrayColumnSum($this->goodsList, 'total_price');
|
||
|
// 获取满额包邮设置
|
||
|
$options = SettingModel::getItem('full_free');
|
||
|
foreach ($this->data as &$item) {
|
||
|
$item['free_goods_list'] = [];
|
||
|
foreach ($item['goodsList'] as $goodsItem) {
|
||
|
if (
|
||
|
$this->orderType === OrderTypeEnum::MASTER
|
||
|
&& $options['is_open'] == true
|
||
|
&& $orderTotalPrice >= $options['money']
|
||
|
&& !in_array($goodsItem['goods_id'], $options['notin_goods'])
|
||
|
&& !in_array($this->cityId, $options['notin_region']['citys'])
|
||
|
) {
|
||
|
$item['free_goods_list'][] = $goodsItem['goods_id'];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 计算当前配送模板的运费
|
||
|
* @param $item
|
||
|
* @return float|mixed|string
|
||
|
*/
|
||
|
private function calcDeliveryAmount($item)
|
||
|
{
|
||
|
// 获取运费模板下商品总数量or总重量
|
||
|
if (!$totality = $this->getItemGoodsTotal($item)) {
|
||
|
return 0.00;
|
||
|
}
|
||
|
// 当前收货城市配送规则
|
||
|
$deliveryRule = $this->getCityDeliveryRule($item['delivery']);
|
||
|
if ($totality <= $deliveryRule['first']) {
|
||
|
return $deliveryRule['first_fee'];
|
||
|
}
|
||
|
// 续件or续重 数量
|
||
|
$additional = $totality - $deliveryRule['first'];
|
||
|
if ($additional <= $deliveryRule['additional']) {
|
||
|
return helper::bcadd($deliveryRule['first_fee'], $deliveryRule['additional_fee']);
|
||
|
}
|
||
|
// 计算续重/件金额
|
||
|
if ($deliveryRule['additional'] < 1) {
|
||
|
// 配送规则中续件为0
|
||
|
$additionalFee = 0.00;
|
||
|
} else {
|
||
|
$additionalFee = helper::bcdiv($deliveryRule['additional_fee'], $deliveryRule['additional']) * $additional;
|
||
|
}
|
||
|
return helper::bcadd($deliveryRule['first_fee'], $additionalFee);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取运费模板下商品总数量or总重量
|
||
|
* @param $item
|
||
|
* @return int|string
|
||
|
*/
|
||
|
private function getItemGoodsTotal($item)
|
||
|
{
|
||
|
$totalWeight = 0; // 总重量
|
||
|
$totalNum = 0; // 总数量
|
||
|
foreach ($item['goodsList'] as $goodsItem) {
|
||
|
// 如果商品为包邮,则不计算总量中
|
||
|
if (!in_array($goodsItem['goods_id'], $item['free_goods_list'])) {
|
||
|
$goodsWeight = helper::bcmul($goodsItem['goods_sku']['goods_weight'], $goodsItem['total_num']);
|
||
|
$totalWeight = helper::bcadd($totalWeight, $goodsWeight);
|
||
|
$totalNum = helper::bcadd($totalNum, $goodsItem['total_num']);
|
||
|
}
|
||
|
}
|
||
|
return $item['delivery']['method']['value'] == 10 ? $totalNum : $totalWeight;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 根据城市id获取规则信息
|
||
|
* @param
|
||
|
* @return array|false
|
||
|
*/
|
||
|
private function getCityDeliveryRule($delivery)
|
||
|
{
|
||
|
foreach ($delivery['rule'] as $item) {
|
||
|
if (in_array($this->cityId, $item['region_data'])) {
|
||
|
return $item;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 整合运费模板
|
||
|
* @return bool
|
||
|
* @throws \think\db\exception\DataNotFoundException
|
||
|
* @throws \think\db\exception\ModelNotFoundException
|
||
|
* @throws \think\exception\DbException
|
||
|
*/
|
||
|
private function initDeliveryTemplate()
|
||
|
{
|
||
|
// 运费模板ID集
|
||
|
$deliveryIds = helper::getArrayColumn($this->goodsList, 'delivery_id');
|
||
|
// 运费模板列表
|
||
|
$deliveryList = (new DeliveryModel)->getListByIds(array_values(array_unique($deliveryIds)));
|
||
|
// 整理数据集
|
||
|
foreach ($deliveryList as $item) {
|
||
|
$this->data[$item['delivery_id']]['delivery'] = $item;
|
||
|
$this->data[$item['delivery_id']]['goodsList'] = $this->getGoodsListByDeliveryId($item['delivery_id']);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 根据运费模板id整理商品集
|
||
|
* @param $deliveryId
|
||
|
* @return array
|
||
|
*/
|
||
|
private function getGoodsListByDeliveryId($deliveryId)
|
||
|
{
|
||
|
$data = [];
|
||
|
foreach ($this->goodsList as $item) {
|
||
|
$item['delivery_id'] == $deliveryId && $data[] = $item;
|
||
|
}
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|