You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yanzong/app/common/service/delivery/Express.php

285 lines
9.3 KiB

<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\service\delivery;
use app\common\library\helper;
use app\common\enum\Setting as SettingEnum;
use app\common\model\Delivery as DeliveryModel;
use app\common\model\store\Setting as SettingModel;
use app\common\service\BaseService;
/**
* 快递配送服务类
* Class Delivery
* @package app\common\service
*/
class Express extends BaseService
{
// 用户收货城市id
private int $cityId;
// 订单商品列表
private iterable $goodsList;
// 不在配送范围的商品ID
private ?int $notInRuleGoodsId = null;
// 运费模板数据集
private array $data = [];
/**
* 构造方法
* Express constructor.
* @param $cityId
* @param $goodsList
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function __construct($cityId, $goodsList)
{
parent::__construct();
// 赋值传参
$this->cityId = $cityId;
$this->goodsList = $goodsList;
// 整合运费模板
$this->initDeliveryTemplate();
}
/**
* 验证用户收货地址是否在配送范围
* @return bool
*/
public function isIntraRegion(): bool
{
if (!$this->cityId) {
return false;
}
foreach ($this->data as $item) {
$cityIds = [];
foreach ($item['delivery']['rule'] as $ruleItem) {
$cityIds = array_merge($cityIds, $ruleItem['region']);
}
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 string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getDeliveryFee(): string
{
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 float|int|mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function getFinalFreight()
{
// 运费合集
$expressPriceArr = helper::getArrayColumn($this->data, 'delivery_fee');
if (empty($expressPriceArr)) {
return 0.00;
}
// 最终运费金额
$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 void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function freeshipping(): void
{
// 订单商品总金额
$orderTotalPrice = helper::getArrayColumnSum($this->goodsList, 'total_price');
// 获取满额包邮设置
$options = SettingModel::getItem(SettingEnum::FULL_FREE);
foreach ($this->data as &$item) {
$item['free_goods_list'] = [];
foreach ($item['goodsList'] as $goodsItem) {
if (
$options['is_open']
&& $orderTotalPrice >= $options['money']
&& !in_array($goodsItem['goods_id'], $options['excludedGoodsIds'])
&& !in_array($this->cityId, $options['excludedRegions']['cityIds'])
) {
$item['free_goods_list'][] = $goodsItem['goods_id'];
}
}
}
}
/**
* 计算当前配送模板的运费
* @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['skuInfo']['goods_weight'], $goodsItem['total_num']);
$totalWeight = helper::bcadd($totalWeight, $goodsWeight);
$totalNum = helper::bcadd($totalNum, $goodsItem['total_num']);
}
}
return $item['delivery']['method'] == 10 ? $totalNum : $totalWeight;
}
/**
* 根据城市id获取规则信息
* @param
* @return array|false
*/
private function getCityDeliveryRule($delivery)
{
foreach ($delivery['rule'] as $item) {
if (in_array($this->cityId, $item['region'])) {
return $item;
}
}
return false;
}
/**
* 整合运费模板
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function initDeliveryTemplate(): void
{
// 运费模板ID集
$deliveryIds = $this->getDeliveryIds();
if (empty($deliveryIds)) {
return;
}
// 运费模板列表
$deliveryList = (new DeliveryModel)->getListByIds($deliveryIds);
// 整理数据集
foreach ($deliveryList as $item) {
$this->data[$item['delivery_id']]['delivery'] = $item;
$this->data[$item['delivery_id']]['goodsList'] = $this->getGoodsListByDeliveryId($item['delivery_id']);
}
}
/**
* 运费模板ID集
* @return array
*/
private function getDeliveryIds(): array
{
$deliveryIds = helper::getArrayColumn($this->goodsList, 'delivery_id');
return array_values(array_unique(array_filter($deliveryIds)));
}
/**
* 根据运费模板id整理商品集
* @param $deliveryId
* @return array
*/
private function getGoodsListByDeliveryId($deliveryId): array
{
$data = [];
foreach ($this->goodsList as $item) {
$item['delivery_id'] == $deliveryId && $data[] = $item;
}
return $data;
}
}