CRMEB/crmeb/app/services/order/StoreOrderComputedServices.php
2021-07-13 16:49:21 +08:00

416 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\services\BaseServices;
use app\dao\order\StoreOrderDao;
use app\services\pay\PayServices;
use app\services\product\product\StoreCategoryServices;
use app\services\product\product\StoreProductServices;
use app\services\user\MemberCardServices;
use app\services\user\UserServices;
use think\exception\ValidateException;
use app\services\user\UserAddressServices;
use app\services\coupon\StoreCouponUserServices;
use app\services\shipping\ShippingTemplatesFreeServices;
use app\services\shipping\ShippingTemplatesRegionServices;
use app\services\shipping\ShippingTemplatesServices;
/**
* 订单计算金额
* Class StoreOrderComputedServices
* @package app\services\order
*/
class StoreOrderComputedServices extends BaseServices
{
/**
* 支付类型
* @var string[]
*/
public $payType = ['weixin' => '微信支付', 'yue' => '余额支付', 'offline' => '线下支付', 'pc' => 'pc'];
/**
* 额外参数
* @var array
*/
protected $paramData = [];
/**
* StoreOrderComputedServices constructor.
* @param StoreOrderDao $dao
*/
public function __construct(StoreOrderDao $dao)
{
$this->dao = $dao;
}
/**
* 设置额外参数
* @param array $paramData
* @return $this
*/
public function setParamData(array $paramData)
{
$this->paramData = $paramData;
return $this;
}
/**
* 计算订单金额
* @param int $uid
* @param string $key
* @param array $cartGroup
* @param int $addressId
* @param string $payType
* @param bool $useIntegral
* @param int $couponId
* @param bool $is_create
* @param int $shipping_type
* @return array
*/
public function computedOrder(int $uid, string $key, array $cartGroup, int $addressId, string $payType, bool $useIntegral = false, int $couponId = 0, bool $isCreate = false, int $shippingType = 1)
{
$offlinePayStatus = (int)sys_config('offline_pay_status') ?? (int)2;
$systemPayType = PayServices::PAY_TYPE;
if ($offlinePayStatus == 2) unset($systemPayType['offline']);
if (strtolower($payType) != 'pc') {
if (!array_key_exists($payType, $systemPayType)) {
throw new ValidateException('选择支付方式有误');
}
}
if ($this->dao->count(['unique' => $key, 'uid' => $uid])) {
throw new ValidateException('请勿重复提交订单');
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->get($uid);
if (!$userInfo) {
throw new ValidateException('用户不存在!');
}
$cartInfo = $cartGroup['cartInfo'];
$priceGroup = $cartGroup['priceGroup'];
$other = $cartGroup['other'];
$payPrice = (float)$priceGroup['totalPrice'];
/** @var UserAddressServices $addressServices */
$addressServices = app()->make(UserAddressServices::class);
$addr = $addressServices->getAddress($addressId);
if ($addr) {
$addr = $addr->toArray();
} else {
$addr = [];
}
$combinationId = $this->paramData['combinationId'] ?? 0;
$seckillId = $this->paramData['seckill_id'] ?? 0;
$bargainId = $this->paramData['bargainId'] ?? 0;
$isActivity = $combinationId || $seckillId || $bargainId;
if (!$isActivity) {
//使用优惠劵
[$payPrice, $couponPrice] = $this->useCouponId($couponId, $uid, $cartInfo, $payPrice, $isCreate);
//使用积分
[$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral] = $this->useIntegral($useIntegral, $userInfo, $payPrice, $other);
}
//计算邮费
[$payPrice, $payPostage, $storePostageDiscount] = $this->computedPayPostage($shippingType, $payType, $cartInfo, $addr, $payPrice, $other);
$result = [
'total_price' => $priceGroup['totalPrice'],
'pay_price' => $payPrice,
'pay_postage' => $payPostage,
'coupon_price' => $couponPrice ?? 0,
'deduction_price' => $deductionPrice ?? 0,
'usedIntegral' => $usedIntegral ?? 0,
'SurplusIntegral' => $SurplusIntegral ?? 0,
'storePostageDiscount' => $storePostageDiscount ?? 0,
];
$this->paramData = [];
return $result;
}
/**
* 使用优惠卷
* @param int $couponId
* @param int $uid
* @param $cartInfo
* @param $payPrice
* @param bool $is_create
*/
public function useCouponId(int $couponId, int $uid, $cartInfo, $payPrice, bool $isCreate)
{
//使用优惠劵
$res1 = true;
if ($couponId) {
/** @var StoreCouponUserServices $couponServices */
$couponServices = app()->make(StoreCouponUserServices::class);
$couponInfo = $couponServices->getOne([['id', '=', $couponId], ['uid', '=', $uid], ['is_fail', '=', 0], ['status', '=', 0], ['start_time', '<', time()], ['end_time', '>', time()]], '*', ['issue']);
if (!$couponInfo) {
throw new ValidateException('选择的优惠劵无效!');
}
$type = $couponInfo['applicable_type'] ?? 0;
$flag = false;
$price = 0;
$count = 0;
switch ($type) {
case 0:
case 3:
foreach ($cartInfo as $cart) {
$price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
break;
case 1://品类券
/** @var StoreCategoryServices $storeCategoryServices */
$storeCategoryServices = app()->make(StoreCategoryServices::class);
$cateGorys = $storeCategoryServices->getAllById((int)$couponInfo['category_id']);
if ($cateGorys) {
$cateIds = array_column($cateGorys, 'id');
foreach ($cartInfo as $cart) {
if (isset($cart['productInfo']['cate_id']) && array_intersect(explode(',', $cart['productInfo']['cate_id']), $cateIds)) {
$price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
}
}
break;
case 2:
foreach ($cartInfo as $cart) {
if (isset($cart['product_id']) && in_array($cart['product_id'], explode(',', $couponInfo['product_id']))) {
$price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
}
break;
}
if ($count && $couponInfo['use_min_price'] <= $price) {
$flag = true;
}
if (!$flag) {
throw new ValidateException('不满足优惠劵的使用条件!');
}
$payPrice = (float)bcsub((string)$payPrice, (string)$couponInfo['coupon_price'], 2);
if ($isCreate) {
$res1 = $couponServices->useCoupon($couponId);
}
$couponPrice = $couponInfo['coupon_price'];
} else {
$couponPrice = 0;
}
if (!$res1) {
throw new ValidateException('使用优惠劵失败!');
}
return [$payPrice, $couponPrice];
}
/**
* 使用积分
* @param $useIntegral
* @param $userInfo
* @param $payPrice
* @param $other
* @return array
*/
public function useIntegral(bool $useIntegral, $userInfo, string $payPrice, array $other)
{
$SurplusIntegral = 0;
if ($useIntegral && $userInfo['integral'] > 0) {
//积分抵扣上限
$integralMaxNum = sys_config('integral_max_num', 200);
if ($integralMaxNum > 0 && $userInfo['integral'] > $integralMaxNum) {
$integral = $integralMaxNum;
} else {
$integral = $userInfo['integral'];
}
$deductionPrice = (float)bcmul((string)$integral, (string)$other['integralRatio'], 2);
if ($deductionPrice < $payPrice) {
$payPrice = bcsub((string)$payPrice, (string)$deductionPrice, 2);
$usedIntegral = $integral;
} else {
$deductionPrice = $payPrice;
$usedIntegral = (int)ceil(bcdiv((string)$payPrice, (string)$other['integralRatio'], 2));
$payPrice = 0;
}
$SurplusIntegral = (int)bcsub((string)$userInfo['integral'], (string)$usedIntegral, 0);
} else {
$deductionPrice = 0;
$usedIntegral = 0;
}
if ($payPrice <= 0) $payPrice = 0;
return [$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral];
}
/**
* 计算邮费
* @param int $shipping_type
* @param string $payType
* @param array $cartInfo
* @param array $addr
* @param string $payPrice
* @param array $other
* @return array
*/
public function computedPayPostage(int $shipping_type, string $payType, array $cartInfo, array $addr, string $payPrice, array $other)
{
$storePostageDiscount = 0;
if (!isset($addr['id'])) {
$payPostage = 0;
} else {
//$shipping_type = 1 快递发货 $shipping_type = 2 门店自提
if ($payType == 'offline' && sys_config('offline_postage') == 1) {
$payPostage = 0;
} else {
$postage = $this->getOrderPriceGroup($cartInfo, $addr);
$payPostage = $postage['storePostage'];
$storePostageDiscount = $postage['storePostageDiscount'];
}
$store_self_mention = sys_config('store_self_mention') ?? 0;
if (!$store_self_mention) $shipping_type = 1;
if ($shipping_type === 1) {
//是否包邮
if ((isset($other['offlinePostage']) && $other['offlinePostage'] && $payType == 'offline')) $payPostage = 0;
$payPrice = (float)bcadd((string)$payPrice, (string)$payPostage, 2);
} else if ($shipping_type === 2) {
//门店自提没有邮费支付
$priceGroup['storePostage'] = 0;
$payPostage = 0;
$storePostageDiscount = 0;
}
}
return [$payPrice, $payPostage, $storePostageDiscount];
}
/**
* 运费计算,总金额计算
* @param $cartInfo
* @return array
*/
public function getOrderPriceGroup($cartInfo, $addr)
{
$storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮
$totalPrice = $this->getOrderSumPrice($cartInfo, 'truePrice');//获取订单总金额
$costPrice = $this->getOrderSumPrice($cartInfo, 'costPrice');//获取订单成本价
$vipPrice = $this->getOrderSumPrice($cartInfo, 'vip_truePrice');//获取订单会员优惠金额
//如果满额包邮等于0
if (!$storeFreePostage) {
$storePostage = 0;
} else {
if ($addr) {
//按照运费模板计算每个运费模板下商品的件数/重量/体积以及总金额 按照首重倒序排列
$cityId = $addr['city_id'] ?? 0;
$tempIds[] = 1;
foreach ($cartInfo as $key_c => $item_c) {
$tempIds[] = $item_c['productInfo']['temp_id'];
}
$tempIds = array_unique($tempIds);
/** @var ShippingTemplatesServices $shippServices */
$shippServices = app()->make(ShippingTemplatesServices::class);
$temp = $shippServices->getShippingColumn(['id' => $tempIds], 'type,appoint', 'id');
/** @var ShippingTemplatesRegionServices $regionServices */
$regionServices = app()->make(ShippingTemplatesRegionServices::class);
$regionList = $regionServices->getTempRegionList($tempIds, [$cityId, 0]);
foreach ($regionList as $key_r => $item_r) {
$regions[$item_r['temp_id']] = $item_r;
}
$temp_num = [];
foreach ($cartInfo as $cart) {
$tempId = $cart['productInfo']['temp_id'] ?? 1;
$type = isset($temp[$tempId]['type']) ? $temp[$tempId]['type'] : $temp[1]['type'];
if ($type == 1) {
$num = $cart['cart_num'];
} elseif ($type == 2) {
$num = $cart['cart_num'] * $cart['productInfo']['attrInfo']['weight'];
} else {
$num = $cart['cart_num'] * $cart['productInfo']['attrInfo']['volume'];
}
$region = isset($regions[$tempId]) ? $regions[$tempId] : $regions[1];
if (!isset($temp_num[$cart['productInfo']['temp_id']])) {
$temp_num[$cart['productInfo']['temp_id']]['number'] = $num;
$temp_num[$cart['productInfo']['temp_id']]['price'] = bcmul($cart['cart_num'], $cart['truePrice'], 2);
$temp_num[$cart['productInfo']['temp_id']]['first'] = $region['first'];
$temp_num[$cart['productInfo']['temp_id']]['first_price'] = $region['first_price'];
$temp_num[$cart['productInfo']['temp_id']]['continue'] = $region['continue'];
$temp_num[$cart['productInfo']['temp_id']]['continue_price'] = $region['continue_price'];
$temp_num[$cart['productInfo']['temp_id']]['temp_id'] = $cart['productInfo']['temp_id'];
$temp_num[$cart['productInfo']['temp_id']]['city_id'] = $addr['city_id'];
} else {
$temp_num[$cart['productInfo']['temp_id']]['number'] += $num;
$temp_num[$cart['productInfo']['temp_id']]['price'] += bcmul($cart['cart_num'], $cart['truePrice'], 2);
}
}
/** @var ShippingTemplatesFreeServices $freeServices */
$freeServices = app()->make(ShippingTemplatesFreeServices::class);
foreach ($temp_num as $k => $v) {
if (isset($temp[$v['temp_id']]['appoint']) && $temp[$v['temp_id']]['appoint']) {
if ($freeServices->isFree($v['temp_id'], $v['city_id'], $v['number'], $v['price'])) {
unset($temp_num[$k]);
}
}
}
//首件运费最大值
$maxFirstPrice = $temp_num ? max(array_column($temp_num, 'first_price')) : 0;
//初始运费为0
$storePostage = 0;
//循环运费数组
foreach ($temp_num as $fk => $fv) {
//找到首件运费等于最大值
if ($fv['first_price'] == $maxFirstPrice) {
//每次循环设置初始值
$tempArr = $temp_num;
$Postage = 0;
//计算首件运费
if ($fv['number'] <= $fv['first']) {
$Postage = bcadd($Postage, $fv['first_price'], 2);
} else {
if ($fv['continue'] <= 0) {
$Postage = $Postage;
} else {
$Postage = bcadd(bcadd($Postage, $fv['first_price'], 2), bcmul(ceil(bcdiv(bcsub($fv['number'], $fv['first'], 2), $fv['continue'] ?? 0, 2)), $fv['continue_price'], 4), 2);
}
}
//删除计算过的首件数据
unset($tempArr[$fk]);
//循环计算剩余运费
foreach ($tempArr as $cv) {
if ($cv['continue'] <= 0) {
$Postage = $Postage;
} else {
$Postage = bcadd($Postage, bcmul(ceil(bcdiv($cv['number'], $cv['continue'] ?? 0, 2)), $cv['continue_price'], 2), 2);
}
}
//获取运费计算中的最大值
if ($Postage > $storePostage) $storePostage = $Postage;
}
}
} else {
$storePostage = 0;
}
if (bcadd((string)$totalPrice, (string)$vipPrice, 2) >= $storeFreePostage) $storePostage = 0;//如果总价大于等于满额包邮 邮费等于0
}
$storePostageDiscount = 0;
return compact('storePostage', 'storeFreePostage', 'totalPrice', 'costPrice', 'vipPrice', 'storePostageDiscount');
}
/**获取某个字段总金额
* @param $cartInfo
* @param $key 键名
* @return int|string
*/
public function getOrderSumPrice($cartInfo, $key = 'truePrice')
{
$SumPrice = 0;
foreach ($cartInfo as $cart) {
$SumPrice = bcadd($SumPrice, bcmul($cart['cart_num'], $cart[$key], 2), 2);
}
return $SumPrice;
}
}