744 lines
22 KiB
PHP
744 lines
22 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Constants\PayWay;
|
||
use App\Endpoint\Api\Http\Resources\ProductSkuSimpleResource;
|
||
use App\Endpoint\Api\Http\Resources\ShippingAddressResource;
|
||
use App\Endpoint\Api\Http\Resources\UserCouponResource;
|
||
use App\Exceptions\BizException;
|
||
use App\Exceptions\ShippingNotSupportedException;
|
||
use App\Helpers\Numeric;
|
||
use App\Helpers\Order as OrderHelper;
|
||
use App\Models\Order;
|
||
use App\Models\OrderPackage;
|
||
use App\Models\OrderProduct;
|
||
use App\Models\ProductSku;
|
||
use App\Models\ShippingAddress;
|
||
use App\Models\User;
|
||
use App\Models\UserCoupon;
|
||
use Carbon\Carbon;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
class OrderService
|
||
{
|
||
/**
|
||
* @var array
|
||
*/
|
||
protected $wxpayWays = [
|
||
PayWay::WXPAY_APP,
|
||
PayWay::WXPAY_JSAPI,
|
||
PayWay::WXPAY_MINI,
|
||
PayWay::WXPAY_H5,
|
||
];
|
||
|
||
/**
|
||
* 快速下单
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param int $skuId
|
||
* @param int $quantity
|
||
* @param int $shippingAddressId
|
||
* @param int|null $couponId
|
||
* @param string|null $note
|
||
* @return \App\Models\Order
|
||
*/
|
||
public function createQuickOrder(
|
||
User $user,
|
||
int $skuId,
|
||
int $quantity,
|
||
int $shippingAddressId,
|
||
?int $couponId = null,
|
||
?string $note = null
|
||
): Order {
|
||
$sku = ProductSku::online()->findOrFail($skuId);
|
||
|
||
$product = [
|
||
'sku' => $sku,
|
||
'quantity' => $quantity,
|
||
];
|
||
|
||
return $this->createOrder($user, [$product], $shippingAddressId, $couponId, $note);
|
||
}
|
||
|
||
/**
|
||
* 购物车下单
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param int $skuId
|
||
* @param int $quantity
|
||
* @param int $shippingAddressId
|
||
* @param int|null $couponId
|
||
* @param string|null $note
|
||
* @return \App\Models\Order
|
||
*/
|
||
public function createShoppingCartOrder(
|
||
User $user,
|
||
array $shoppingCartItemIds,
|
||
int $shippingAddressId,
|
||
?int $couponId = null,
|
||
?string $note = null,
|
||
): Order {
|
||
$order = $this->createOrder(
|
||
$user,
|
||
$this->getProductsByShoppingCart($user, $shoppingCartItemIds),
|
||
$shippingAddressId,
|
||
$couponId,
|
||
$note
|
||
);
|
||
|
||
$user->shoppingCartItems()->whereIn('id', $shoppingCartItemIds)->delete();
|
||
|
||
return $order;
|
||
}
|
||
|
||
/**
|
||
* 确认快速下单
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param int $skuId
|
||
* @param int $quantity
|
||
* @param int|null $shippingAddressId
|
||
* @param int|null $couponId
|
||
* @return array
|
||
*/
|
||
public function verifyQuickOrder(User $user, int $skuId, int $quantity, ?int $shippingAddressId = null, ?int $couponId = null)
|
||
{
|
||
$sku = ProductSku::online()->findOrFail($skuId);
|
||
|
||
$product = [
|
||
'sku' => $sku,
|
||
'quantity' => $quantity,
|
||
];
|
||
|
||
return $this->verifyOrder($user, [$product], $shippingAddressId, $couponId);
|
||
}
|
||
|
||
/**
|
||
* 确认购物车订单
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param array $shoppingCartItemIds
|
||
* @param int|null $shippingAddressId
|
||
* @param int|null $couponId
|
||
* @return array
|
||
*/
|
||
public function verifyShoppingCartOrder(User $user, array $shoppingCartItemIds, ?int $shippingAddressId = null, ?int $couponId = null)
|
||
{
|
||
// 获取购买商品
|
||
$products = $this->getProductsByShoppingCart($user, $shoppingCartItemIds);
|
||
|
||
return $this->verifyOrder($user, $products, $shippingAddressId, $couponId);
|
||
}
|
||
|
||
/**
|
||
* 创建订单
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param array $products
|
||
* @param int $shippingAddressId
|
||
* @param int|null $couponId
|
||
* @param string|null $note
|
||
* @return \App\Models\Order
|
||
*/
|
||
protected function createOrder(
|
||
User $user,
|
||
array $products,
|
||
int $shippingAddressId,
|
||
?int $couponId = null,
|
||
?string $note = null,
|
||
): Order {
|
||
foreach ($products as $product) {
|
||
$sku = $product['sku'];
|
||
|
||
if ($product['quantity'] > $sku->saleable_stock) {
|
||
throw new BizException('商品库存不足');
|
||
}
|
||
}
|
||
|
||
$shippingAddress = $this->getShippingAddress($user, $shippingAddressId);
|
||
|
||
// 优惠券
|
||
$coupon = null;
|
||
|
||
if ($couponId) {
|
||
$coupon = $user->coupons()->onlyAvailable()->lockForUpdate()->findOrFail($couponId);
|
||
}
|
||
|
||
$mapProducts = $this->mapProducts($user, $products, $coupon);
|
||
|
||
// 计算运费
|
||
$shippingFee = $this->calculateShippingFee($mapProducts, $shippingAddress);
|
||
|
||
list(
|
||
$productsTotalAmount,
|
||
$vipDiscountAmount,
|
||
$couponDiscountAmount,
|
||
$totalAmount,
|
||
) = $this->calculateFees($mapProducts);
|
||
|
||
// 订单总费用
|
||
$totalAmount += $shippingFee;
|
||
|
||
$order = $user->orders()->create([
|
||
'sn' => OrderHelper::serialNumber(),
|
||
'user_coupon_id' => $coupon?->id,
|
||
'coupon_discount_amount' => $couponDiscountAmount,
|
||
'vip_discount_amount' => $vipDiscountAmount,
|
||
'shipping_fee' => $shippingFee,
|
||
'products_total_amount' => $productsTotalAmount,
|
||
'total_amount' => $totalAmount,
|
||
'status' => $totalAmount > 0 ? Order::STATUS_PENDING : Order::STATUS_PAID,
|
||
'note' => $note,
|
||
// 收货地址
|
||
'consignee_name' => $shippingAddress->consignee,
|
||
'consignee_telephone' => $shippingAddress->telephone,
|
||
'consignee_zone' => $shippingAddress->zone,
|
||
'consignee_address' => $shippingAddress->address,
|
||
]);
|
||
|
||
$data = [];
|
||
|
||
foreach ($mapProducts as $product) {
|
||
$sku = $product['sku'];
|
||
|
||
// 支付金额 = 商品总额 - 优惠券折扣金额- 会员折扣金额
|
||
$totalAmount = $product['total_amount'] - $product['coupon_discount_amount'] - $product['vip_discount_amount'];
|
||
|
||
$data[] = [
|
||
'user_id' => $order->user_id,
|
||
'order_id' => $order->id,
|
||
'spu_id' => $sku->spu_id,
|
||
'sku_id' => $sku->id,
|
||
'category_id' => $sku->category_id,
|
||
'name' => $sku->name,
|
||
'specs' => json_encode($sku->specs),
|
||
'cover' => $sku->cover,
|
||
'weight' => $sku->weight,
|
||
'sell_price' => $sku->sell_price,
|
||
'vip_price' => $sku->vip_price,
|
||
'quantity' => $product['quantity'],
|
||
'remain_quantity' => $product['quantity'],
|
||
'coupon_discount_amount' => $product['coupon_discount_amount'],
|
||
'vip_discount_amount' => $product['vip_discount_amount'],
|
||
'total_amount' => $totalAmount,
|
||
'created_at' => $order->created_at,
|
||
'updated_at' => $order->updated_at,
|
||
];
|
||
|
||
$this->deductSkuStock($sku, $product['quantity']);
|
||
}
|
||
|
||
OrderProduct::insert($data);
|
||
|
||
// 将优惠券标记为已使用
|
||
$coupon?->markAsUse();
|
||
|
||
return $order;
|
||
}
|
||
|
||
/**
|
||
* 扣除商品库存
|
||
*
|
||
* @param \App\Models\ProductSku $sku
|
||
* @param int $quantity
|
||
* @return void
|
||
*/
|
||
protected function deductSkuStock(ProductSku $sku, int $quantity): void
|
||
{
|
||
$sku->update([
|
||
'stock' => DB::raw("stock - {$quantity}"), // 库存
|
||
'sales' => DB::raw("sales + {$quantity}"), // 销量
|
||
]);
|
||
|
||
$sku->spu->update([
|
||
'sales' => DB::raw("sales + {$quantity}"), // 销量
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 确认订单
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param array $products
|
||
* @param int|null $shippingAddressId
|
||
* @param int|null $couponId
|
||
* @return array
|
||
*/
|
||
protected function verifyOrder(User $user, array $products, ?int $shippingAddressId = null, ?int $couponId = null): array
|
||
{
|
||
// 获取收货地址
|
||
$shippingAddress = $this->getShippingAddress($user, $shippingAddressId);
|
||
|
||
// 优惠券
|
||
$coupon = null;
|
||
|
||
if ($couponId) {
|
||
$coupon = $user->coupons()->onlyAvailable()->findOrFail($couponId);
|
||
}
|
||
|
||
$mapProducts = $this->mapProducts($user, $products, $coupon);
|
||
|
||
// 是否支持配送
|
||
$shippingSupported = true;
|
||
// 运费
|
||
$shippingFee = 0;
|
||
|
||
if ($shippingAddress) {
|
||
try {
|
||
$shippingFee = $this->calculateShippingFee($mapProducts, $shippingAddress);
|
||
} catch (ShippingNotSupportedException $e) {
|
||
$shippingFee = 0;
|
||
$shippingSupported = false;
|
||
}
|
||
}
|
||
|
||
list(
|
||
$productsTotalAmount,
|
||
$vipDiscountAmount,
|
||
$couponDiscountAmount,
|
||
$totalAmount,
|
||
) = $this->calculateFees($mapProducts);
|
||
|
||
// 订单总费用
|
||
$totalAmount += $shippingFee;
|
||
|
||
return [
|
||
'products' => collect($mapProducts)->map(function ($item) {
|
||
return [
|
||
'sku' => ProductSkuSimpleResource::make($item['sku']),
|
||
'quantity' => $item['quantity'],
|
||
];
|
||
}),
|
||
'coupons' => UserCouponResource::collection((new CouponService())->getAvailableCoupons($user, $products)),
|
||
'shipping_address' => $shippingAddress ? ShippingAddressResource::make($shippingAddress) : null, // 收货地址
|
||
'shipping_supported' => $shippingSupported,
|
||
'shipping_fee' => Numeric::trimTrailingZero(bcdiv($shippingFee, 100, 2)), // 运费
|
||
'products_total_amount' => Numeric::trimTrailingZero(bcdiv($productsTotalAmount, 100, 2)), // 商品总额
|
||
'vip_discount_amount' => Numeric::trimTrailingZero(bcdiv($vipDiscountAmount, 100, 2)), // 会员折扣金额
|
||
'coupon_discount_amount' => Numeric::trimTrailingZero(bcdiv($couponDiscountAmount, 100, 2)), // 优惠券折扣金额
|
||
'total_amount' => Numeric::trimTrailingZero(bcdiv($totalAmount, 100, 2)), // 实付金额
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 计算费用
|
||
*
|
||
* @param array $products
|
||
* @return array
|
||
*/
|
||
protected function calculateFees(array $products)
|
||
{
|
||
$productsTotalAmount = 0;
|
||
$vipDiscountAmount = 0;
|
||
$couponDiscountAmount = 0;
|
||
|
||
|
||
foreach ($products as $product) {
|
||
$productsTotalAmount += $product['total_amount'];
|
||
$vipDiscountAmount += $product['vip_discount_amount'];
|
||
$couponDiscountAmount += $product['coupon_discount_amount'];
|
||
}
|
||
|
||
// 订单总额
|
||
$totalAmount = $productsTotalAmount - $vipDiscountAmount - $couponDiscountAmount;
|
||
|
||
return [
|
||
$productsTotalAmount,
|
||
$vipDiscountAmount,
|
||
$couponDiscountAmount,
|
||
$totalAmount,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 计算运费
|
||
*
|
||
* @param array $products
|
||
* @param \App\Models\ShippingAddress $shippingAddress
|
||
* @return int
|
||
*/
|
||
protected function calculateShippingFee(array $products, ShippingAddress $shippingAddress): int
|
||
{
|
||
// 运费
|
||
$shippingFee = 0;
|
||
|
||
$shippings = [];
|
||
|
||
foreach ($products as $product) {
|
||
$sku = $product['sku'];
|
||
|
||
if (is_null($sku->shipping_template_id)) {
|
||
continue;
|
||
}
|
||
|
||
$shipping = $shippings[$sku->shipping_template_id] ?? [
|
||
'total_weight' => 0,
|
||
'total_amount' => 0,
|
||
];
|
||
$shipping['total_weight'] += $sku->weight * $product['quantity'];
|
||
$shipping['total_amount'] += $product['total_amount'] - $product['vip_discount_amount'] - $product['coupon_discount_amount'];
|
||
|
||
$shippings[$sku->shipping_template_id] = $shipping;
|
||
}
|
||
|
||
$shippingService = new ShippingService();
|
||
|
||
foreach ($shippings as $templateId => $shipping) {
|
||
$shippingFee += $shippingService->countShippingAmount(
|
||
$shipping['total_weight'],
|
||
$shipping['total_amount'],
|
||
$templateId,
|
||
$shippingAddress->zone_id
|
||
);
|
||
}
|
||
|
||
return $shippingFee;
|
||
}
|
||
|
||
/**
|
||
* 准备商品信息
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param array $products
|
||
* @param \App\Models\UserCoupon|null $coupon
|
||
* @return array
|
||
*
|
||
* @throws \App\Exceptions\BizException
|
||
*/
|
||
protected function mapProducts(User $user, array $products, ?UserCoupon $coupon): array
|
||
{
|
||
$_products = collect($products)->map(function ($item) use ($user) {
|
||
$sku = $item['sku'];
|
||
|
||
return array_merge($item, [
|
||
// 优惠券折扣金额
|
||
'coupon_discount_amount' => 0,
|
||
// 会员折扣金额
|
||
'vip_discount_amount' => $this->calculateVipDiscountAmount(
|
||
$user, $sku, $item['quantity']
|
||
),
|
||
// 总金额
|
||
'total_amount' => $sku->sell_price * $item['quantity'],
|
||
]);
|
||
});
|
||
|
||
if ($coupon === null) {
|
||
return $_products->all();
|
||
}
|
||
|
||
$couponDiscountAmounts = $this->getCouponDiscountAmounts($coupon, $_products->all());
|
||
|
||
return $_products->map(function ($item) use ($couponDiscountAmounts) {
|
||
$item['coupon_discount_amount'] = $couponDiscountAmounts[$item['sku']->id] ?? 0;
|
||
|
||
return $item;
|
||
})->all();
|
||
}
|
||
|
||
/**
|
||
* 计算会员折扣金额
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param \App\Models\ProductSku $sku
|
||
* @param int $quantity
|
||
* @return int
|
||
*/
|
||
protected function calculateVipDiscountAmount(User $user, ProductSku $sku, int $quantity)
|
||
{
|
||
$price = $sku->getRealPrice($user);
|
||
|
||
if ($price > $sku->sell_price) {
|
||
return 0;
|
||
}
|
||
|
||
return ($sku->sell_price - $price) * $quantity;
|
||
}
|
||
|
||
/**
|
||
* 获取商品的优惠券折扣金额
|
||
*
|
||
* @param \App\Models\UserCoupon $coupon
|
||
* @param array $products
|
||
* @return array
|
||
*
|
||
* @throws \App\Exceptions\BizException
|
||
*/
|
||
protected function getCouponDiscountAmounts(UserCoupon $coupon, array $products): array
|
||
{
|
||
// 启用的券的使用规则
|
||
$coupon->loadMissing(['ranges' => function ($query) {
|
||
$query->isEnable();
|
||
}]);
|
||
|
||
// 可使用优惠券的商品总额
|
||
$amounts = [];
|
||
|
||
foreach ($products as $item) {
|
||
$sku = $item['sku'];
|
||
|
||
if (! $coupon->isSupport($sku)) {
|
||
throw new BizException('优惠券不满足使用条件');
|
||
}
|
||
|
||
$amount = $item['total_amount'] - $item['vip_discount_amount'];
|
||
|
||
// 仅保留商品真实总额大于0的商品
|
||
if ($amount > 0) {
|
||
$amounts[$sku->id] = $amount;
|
||
}
|
||
}
|
||
|
||
// 全部商品总额
|
||
$totalAmount = array_sum($amounts);
|
||
|
||
if ($coupon->coupon_threshold > $totalAmount) {
|
||
throw new BizException('优惠券不满足使用条件');
|
||
}
|
||
|
||
// 优惠券折扣金额
|
||
$couponAmount = value(function ($coupon, $totalAmount) {
|
||
// 如果优惠券是折扣券,则需算出优惠总额
|
||
if ($coupon->isDiscountCoupon()) {
|
||
return (int) bcmul($totalAmount, bcdiv(100 - $coupon->coupon_amount, 100, 2));
|
||
}
|
||
|
||
// 如果优惠券的折扣金额超过商品总额,则优惠金额为商品总额
|
||
if ($coupon->coupon_amount > $totalAmount) {
|
||
return $totalAmount;
|
||
}
|
||
|
||
return $coupon->coupon_amount;
|
||
}, $coupon, $totalAmount);
|
||
|
||
// 待计算优惠的商品总数
|
||
$i = count($amounts);
|
||
|
||
foreach ($amounts as &$amount) {
|
||
$i--;
|
||
|
||
if ($i > 0) {
|
||
$amount = (int) bcdiv(bcmul($couponAmount, $amount, 0), $totalAmount, 2);
|
||
$couponAmount -= $amount;
|
||
} else {
|
||
$amount = $couponAmount;
|
||
}
|
||
|
||
unset($amount);
|
||
}
|
||
|
||
return $amounts;
|
||
}
|
||
|
||
/**
|
||
* 根据购物车获取商品
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param array $shoppingCartItemIds
|
||
* @return array
|
||
*
|
||
* @throws \App\Exceptions\BizException
|
||
*/
|
||
protected function getProductsByShoppingCart(User $user, array $shoppingCartItemIds): array
|
||
{
|
||
$shoppingCartItems = $user->shoppingCartItems()->findMany($shoppingCartItemIds);
|
||
|
||
if ($shoppingCartItems->count() !== count($shoppingCartItemIds)) {
|
||
throw new BizException('购物车商品已丢失');
|
||
}
|
||
|
||
$shoppingCartItems->load('sku');
|
||
|
||
$lostShoppingCartItems = $shoppingCartItems->filter(function ($item) {
|
||
return $item->sku === null;
|
||
});
|
||
|
||
if ($lostShoppingCartItems->count() > 0) {
|
||
throw new BizException('购物车商品已失效');
|
||
}
|
||
|
||
return $shoppingCartItems->map(function ($item) {
|
||
return [
|
||
'sku' => $item->sku,
|
||
'quantity' => $item->quantity,
|
||
];
|
||
})->all();
|
||
}
|
||
|
||
/**
|
||
* 获取收货地址
|
||
*
|
||
* @param \App\Models\User $user
|
||
* @param int|null $shippingAddressId
|
||
* @return \App\Models\ShippingAddress|null
|
||
*
|
||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||
*/
|
||
protected function getShippingAddress(User $user, ?int $shippingAddressId = null): ?ShippingAddress
|
||
{
|
||
if ($shippingAddressId) {
|
||
return $user->shippingAddresses()->findOrFail($shippingAddressId);
|
||
}
|
||
|
||
return $user->shippingAddresses()->where('is_default', true)->first();
|
||
}
|
||
|
||
/**
|
||
* 订单付款
|
||
*
|
||
* @param \App\Models\Order $order
|
||
* @param string $payWay
|
||
* @return mixed
|
||
*
|
||
* @throws \App\Exceptions\WeChatPayException
|
||
*/
|
||
public function pay(Order $order, string $payWay)
|
||
{
|
||
if (! $order->isPending()) {
|
||
throw new BizException('订单状态不是待付款');
|
||
}
|
||
|
||
if (in_array($payWay, $this->wxpayWays)) {
|
||
return (new WeChatPayService())->pay([
|
||
'attach' => json_encode([
|
||
'pay_target' => 'order',
|
||
'pay_way' => $payWay,
|
||
]),
|
||
'body' => config('settings.app_name').'-商城订单',
|
||
'out_trade_no' => $order->sn,
|
||
'total_fee' => $order->total_amount,
|
||
'notify_url' => url(route('wxpay.paid_notify', [], false), [], true),
|
||
'trade_type' => PayWay::$wxpayTradeTypes[$payWay],
|
||
]);
|
||
}
|
||
|
||
throw new BizException('支付方式不支持');
|
||
}
|
||
|
||
/**
|
||
* 支付成功
|
||
*
|
||
* @param string $sn
|
||
* @param array $params
|
||
* @return void
|
||
*/
|
||
public function paySuccess(string $sn, array $params = []): void
|
||
{
|
||
$order = Order::where('sn', $sn)->firstOrFail();
|
||
|
||
if (! $order->isPending()) {
|
||
throw new BizException('订单状态不是待支付');
|
||
}
|
||
|
||
$order->update([
|
||
'pay_sn' => $params['pay_sn'],
|
||
'pay_way' => $params['pay_way'],
|
||
'pay_at' => $params['pay_at'],
|
||
'status' => Order::STATUS_PAID,
|
||
]);
|
||
|
||
// todo 处理预收益
|
||
}
|
||
|
||
/**
|
||
* 更新包裹状态
|
||
*
|
||
* @param Collection|null $packages
|
||
* @param integer $status
|
||
* @return void
|
||
*/
|
||
public function updatePackageStatus(OrderPackage $package, int $status)
|
||
{
|
||
//如果包裹已签收则不再更新包裹状态
|
||
if ($package->status == OrderPackage::STATUS_CHECK || $package->status == OrderPackage::STATUS_AUTOCHECK) {
|
||
return;
|
||
}
|
||
//如果签收状态
|
||
if ($status == OrderPackage::STATUS_CHECK || $status == OrderPackage::STATUS_AUTOCHECK) {
|
||
$this->checkOrderPackage($package, $status);
|
||
} else {
|
||
$package->update([
|
||
'status'=>$status,
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 订单确认收货
|
||
*
|
||
* @return void
|
||
*/
|
||
public function checkOrder(Order $order)
|
||
{
|
||
//获取订单的未作废未签收包裹
|
||
$packages = $order->packages()->unchecked()->where('is_failed', false)->get();
|
||
foreach ($packages as $package) {
|
||
$this->checkPackage($package, OrderPackage::STATUS_AUTOCHECK);
|
||
}
|
||
if ($order->isShipped()
|
||
&& OrderPackage::where('order_id', $order->id)->unchecked()->doesntExist()
|
||
) {
|
||
//更新订单完成
|
||
$order->update([
|
||
'status' => Order::STATUS_COMPLETED,
|
||
'completed_at' => now(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 签收订单包裹:会更新订单状态
|
||
*
|
||
* @param OrderPackage $package
|
||
* @param integer $status
|
||
* @param Carbon|null $time
|
||
* @return void
|
||
*/
|
||
public function checkOrderPackage(OrderPackage $package, int $status, ?Carbon $time = null)
|
||
{
|
||
$this->checkPackage($package, $status);
|
||
//如果订单是发货完成状态,且所有包裹都签收了
|
||
if ($package->order->isShipped()
|
||
&& OrderPackage::where('order_id', $package->order_id)->unchecked()->doesntExist()
|
||
) {
|
||
//更新订单完成
|
||
$package->order->update([
|
||
'status' => Order::STATUS_COMPLETED,
|
||
'completed_at' => now(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 签收包裹
|
||
*
|
||
* status 区分3签收(物流推送签收)和11自动签收(属于用户手动点,或者时间到了)
|
||
* @return void
|
||
*/
|
||
protected function checkPackage(OrderPackage $package, int $status, ?Carbon $time = null)
|
||
{
|
||
//如果不是签收状态,则直接退出
|
||
if (!in_array($status, [OrderPackage::STATUS_CHECK, OrderPackage::STATUS_AUTOCHECK])) {
|
||
return;
|
||
}
|
||
|
||
$checkTime = $time ?? now();//没指定签收时间,则默认当前时间
|
||
|
||
//物流签收,更新包裹状态。
|
||
// if ($status == OrderPackage::STATUS_CHECK) { //自动签收不更新包裹状态
|
||
$package->update([
|
||
'status'=> $status,
|
||
'checked_at' => $checkTime,
|
||
]);
|
||
// }
|
||
//如果这个包裹的订单状态是发货中,发货完成
|
||
if ($package->order->isShipped() || $package->order->isShipping()) {
|
||
//处理这个包裹的商品的售后过期时间
|
||
$package->orderProducts()->update([
|
||
'after_expire_at'=>$checkTime->addDays(config('settings.sale_after_expire_days', 7)),
|
||
]);
|
||
}
|
||
}
|
||
}
|