findOrFail($skuId); $product = [ 'sku' => $sku, 'quantity' => $quantity, ]; return $this->createOrder($user, [$product], $shippingAddressId, $couponId, $note, $bargainOrder); } /** * 购物车下单 * * @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 array $products * @param int $shippingAddressId * @param int|null $couponId * @param string|null $note * @param BargainOrder|null $bargainOrder * @return \App\Models\Order */ public function createOrder( User $user, array $products, ?int $shippingAddressId, ?int $couponId = null, ?string $note = null, ?BargainOrder $bargainOrder = 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, $bargainOrder); // 计算运费 $shippingFee = $this->calculateShippingFee($mapProducts, $shippingAddress); list( $productsTotalAmount, $vipDiscountAmount, $couponDiscountAmount, $salesValue ) = $this->calculateFees($mapProducts); $order = $this->storeOrder( $user, $productsTotalAmount, $couponDiscountAmount, $vipDiscountAmount, $shippingFee, $salesValue, $shippingAddress, $note, $coupon, $bargainOrder,//添加砍价订单逻辑 ); $this->storeOrderProducts($order, $mapProducts); // 将优惠券标记为已使用 $coupon?->markAsUse(); if ($order->total_amount === 0) { $this->pay($order, PayWay::Balance); $order->refresh(); } return $order; } /** * 添加店铺订单 * */ public function createOrderByPre(User $user, OrderPre $order_pre) { $products = []; foreach($order_pre->products as $item) { array_push($products, [ 'sku' => ProductSku::findOrFail($item['sku_id']), 'quantity' => $item['quantity'] ]); } $coupon_id = data_get($order_pre, 'others.coupon_id'); $note = data_get($order_pre, 'others.note'); // 优惠券 $coupon = null; if ($coupon_id) { $coupon = $user->coupons()->onlyAvailable()->lockForUpdate()->findOrFail($coupon_id); } $mapProducts = $this->mapProducts($user, $products, $coupon); list( $productsTotalAmount, $vipDiscountAmount, $couponDiscountAmount, $salesValue ) = $this->calculateFees($mapProducts); $order = $this->storeOrder( $user, $productsTotalAmount, $couponDiscountAmount, $vipDiscountAmount, 0, $salesValue, null, $note, $coupon ); $order->update([ 'store_id' => $order_pre->store_id, 'inviter_id' => $order_pre->user_id, 'source_type' => OrderPre::class, 'source_id' => $order_pre->id, ]); $this->storeOrderProducts($order, $mapProducts); // 将优惠券标记为已使用 $coupon?->markAsUse(); if ($order->total_amount === 0) { $this->pay($order, PayWay::Balance); $order->refresh(); } return $order; } /** * 保存订单 * * @param \App\Models\User $user * @param \App\Models\ShippingAddress $shippingAddress * @param int $productsTotalAmount * @param int $couponDiscountAmount * @param int $vipDiscountAmount * @param int $shippingFee * @param float $salesValue * @param string|null $note * @param \App\Models\UserCoupon|null $coupon * @return \App\Models\Order */ protected function storeOrder( User $user, int $productsTotalAmount, int $couponDiscountAmount, int $vipDiscountAmount, int $shippingFee, $salesValue, ?ShippingAddress $shippingAddress, ?string $note = null, ?UserCoupon $coupon = null, ?BargainOrder $bargainOrder = null, ): Order { // 订单支付金额=商品总额-券折扣金额-会员折扣金额+邮费-砍价金额 $totalAmount = $productsTotalAmount - $couponDiscountAmount - $vipDiscountAmount; //如果有砍价优惠 if ($bargainOrder) { $totalAmount -= $bargainOrder->bargain_price; } if ($totalAmount < 0) { $totalAmount = 0; } $totalAmount += $shippingFee; do { // 如果订单号重复,则直接重试 try { $attrs = [ 'sn' => serial_number(), 'user_coupon_id' => $coupon?->id, 'coupon_discount_amount' => $couponDiscountAmount, 'vip_discount_amount' => $vipDiscountAmount, 'shipping_fee' => $shippingFee, 'products_total_amount' => $productsTotalAmount, 'total_amount' => $totalAmount, // 商品总额-券折扣金额-会员折扣金额+邮费 'sales_value' => $salesValue, // 订单总销售值 'note' => $note, // 收货地址 'consignee_name' => $shippingAddress->consignee??'', 'consignee_telephone' => $shippingAddress->telephone??'', 'consignee_zone' => $shippingAddress->zone??'', 'consignee_address' => $shippingAddress->address??'', //砍价订单金额 'bargain_amount'=>$bargainOrder?->bargain_price ?? 0, ]; return $user->orders()->create($attrs); } catch (QueryException $e) { if (strpos($e->getMessage(), 'Duplicate entry') === false) { throw $e; } } } while (true); } /** * 保存订单商品 * * @param \App\Models\Order $order * @param array $mapProducts * @return void */ public function storeOrderProducts(Order $order, array $mapProducts) { $orderProducts = []; foreach ($mapProducts as $product) { $sku = $product['sku']; $qty = $product['quantity']; // 支付金额 = 商品总额 - 优惠券折扣金额- 会员折扣金额 - 砍价金额 $totalAmount = $product['total_amount'] - $product['coupon_discount_amount'] - $product['vip_discount_amount'] - $product['bargain_amount']; $orderProducts[] = [ 'gift_for_sku_id' => null, 'activity_id'=>null, 'is_gift'=> false, '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, 'sales_value' => $sku->sales_value, 'quantity' => $qty, 'remain_quantity' => $qty, // 剩余发货数量 'coupon_discount_amount' => $product['coupon_discount_amount'], 'vip_discount_amount' => $product['vip_discount_amount'], 'total_amount' => $totalAmount, 'bargain_amount'=> $product['bargain_amount'], 'created_at' => $order->created_at, 'updated_at' => $order->updated_at, ]; // 扣除商品库存 if (!$order->store_id) { $this->deductProduct($sku, $qty); } } //根据订单参加的活动添加赠品; $gifts = $this->activityGifts($order, $orderProducts); $orderProducts = array_merge($orderProducts, $gifts); OrderProduct::insert($orderProducts); } /** * 扣商品的库存 * 03-04取消根据商品赠送赠品 * * @param \App\Models\ProductSku $sku * @param int $qty * @return array */ protected function deductProduct(ProductSku $sku, int $qty) { // 扣商品库存 $sku->update([ 'sales' => DB::Raw("sales + {$qty}"), 'stock' => DB::raw("stock - {$qty}"), // 库存 ]); $sku->spu?->increment('sales', $qty); // // 如果是因为赠品库存不足引起的异常,则需重试 // do { // try { // return $this->deductGifts($sku, $qty); // } catch (QueryException $e) { // if (strpos($e->getMessage(), 'Numeric value out of range') === false) { // throw $e; // } // } // } while (true); } /** * 从门店中扣除商品库存 */ protected function deductProductFromStore(Store $store, ProductSku $sku, int $amount) { $sku = $store->productSkus()->findOrFail($sku->id); // 添加出库记录 $tag = Tag::firstOrCreate([ 'type' => Tag::TYPE_STORE_STOCK, 'name' => '下单出库' ]); $store->stockLogs()->create([ 'amount' => 0-$amount, 'product_sku_id' => $sku->id, 'remarks' => '购买', 'tag_id' => $tag->id ]); $store->productSkus()->updateExistingPivot($sku->id, [ 'amount' => $sku->pivot->amount - $amount ]); } /** * 扣出商品的赠品 * * @param \App\Models\ProductSku $sku * @param int $qty * @return array */ protected function deductGifts(ProductSku $sku, int $qty) { // 赠品 $gifts = []; if ($qty < 1) { return $gifts; } $sku->gifts->loadMissing('giftSku'); foreach ($sku->gifts as $gift) { // 如果未找到赠品,则不赠送 if ($gift->giftSku === null) { continue; } // 需赠送礼品的总数 $num = $gift->num * $qty; if ($gift->isLimit()) { if ($gift->remaining === 0) { continue; } if ($num > $gift->remaining) { // 计算剩余可赠送的份数 $remainingQty = (int) ($gift->remaining / $gift->num); $num = $gift->num * $remainingQty; } } if ($gift->isLimit()) { $gift->update([ 'remaining' => DB::raw("remaining-{$num}"), 'sent' => DB::raw("sent+{$num}"), ]); } else { $gift->increment('sent', $num); } $gifts[] = [ 'sku' => $gift->giftSku, 'num' => $num, // 赠送商品总数 ]; } return $gifts; } /** * Undocumented function * * @param Order $order * @param [type] $products * @return array $gifts */ protected function activityGifts(Order $order, $products) { $_products = array_column($products, 'total_amount', 'sku_id'); $inValidParts = []; // 整理订单商品的分区 $partSkus = ProductPartSku::with('part')->whereIn('sku_id', array_keys($_products))->get(); foreach ($partSkus as $partSku) { if ($partSku->part?->is_show) { $inValidParts[$partSku->part_id][$partSku->sku_id] = $_products[$partSku->sku_id] ?? 0; // if (isset($inValidParts[$partSku->part_id])) { // $inValidParts[$partSku->part_id] += $_products[$partSku->sku_id] ?? 0; // } else { // $inValidParts[$partSku->part_id] = $_products[$partSku->sku_id] ?? 0; // } } } // dd($inValidParts); //根据分区获取活动 $partActivities = ActivityProductPart::with(['activity', 'activity.gifts'])->whereHas('activity', function (Builder $query) { return $query->where('is_use', true)->where('started_at', '<', now())->where('ended_at', '>=', now()); })->whereIn('part_id', array_keys($inValidParts))->get(); $activityArr = []; $partActivities->each(function ($item) use (&$activityArr) { $activityArr[$item->activity_id][] = $item->part_id; }); $giveGifts = []; $sendedActivities = []; foreach ($partActivities as $partActivity) { //判断该活动是否已处理 if (in_array($partActivity->activity_id, $sendedActivities)) { continue; } $sendedActivities[] = $partActivity->activity_id; //获取活动的赠品赠送规则 $_giftsRule = $partActivity->activity?->gifts_rule; //判断是否首单:times=0为仅首单赠送, 1为不限 if ($_giftsRule['times'] == 0 && OrderProduct::where([ 'activity_id' => $partActivity->activity_id, 'user_id' => $order->user_id, ])->whereHas('order', function ($query) {//除取消外的订单 return $query->where('status', '<>', Order::STATUS_CANCELLED); })->exists()) { continue;//提前结束本次循环 } //判断是否满足门槛 $inValidGoods = []; foreach ($inValidParts as $key => $part) { if (in_array($key, $activityArr[$partActivity->activity_id])) { $inValidGoods = array_merge($inValidGoods, $part); } } if (bcmul($_giftsRule['value'], 100) > array_sum($inValidGoods)) { continue;//提前结束本次循环 } //返回赠品 $_gifts = $partActivity->activity->gifts; $_num = 0; foreach ($_gifts as $_gift) { if (($_gift->stock - $_gift->pivot->qty) >= 0) { $_num += $_gift->pivot->qty; $giveGifts[] = [ 'gift_for_sku_id'=> null, 'user_id' => $order->user_id, 'order_id' => $order->id, 'spu_id' => $_gift->spu_id, 'sku_id' => $_gift->id, 'category_id' => $_gift->category_id, 'name' => $_gift->name, 'specs' => json_encode($_gift->specs), 'cover' => $_gift->cover, 'weight' => $_gift->weight, 'sell_price' => $_gift->sell_price, 'vip_price' => $_gift->vip_price, 'sales_value' => 0, // 赠品不算销售值 'quantity' => $_gift->pivot->qty, 'remain_quantity' => $_gift->pivot->qty, // 剩余发货数量 'coupon_discount_amount' => 0, 'vip_discount_amount' => 0, 'total_amount' => 0, 'bargain_amount'=> 0, 'created_at' => $order->created_at, 'updated_at' => $order->updated_at, 'is_gift'=> true, 'activity_id'=>$partActivity->activity_id, ]; // 扣除商品库存 $this->deductProduct($_gift, $_gift->pivot->qty); } } if ($_num > 0) { //记录订单参与活动信息 OrderActivity::firstOrCreate([ 'order_id'=>$order->id, 'activity_id'=>$partActivity->activity_id, ]); } } return $giveGifts; } /** * 确认快速下单 * * @param \App\Models\User $user * @param int $skuId * @param int $quantity * @param int|null $shippingAddressId * @param int|null $couponId * @param int|null $bargainOrderId * @return array */ public function verifyQuickOrder(User $user, int $skuId, int $quantity, ?int $shippingAddressId = null, ?int $couponId = null, ?int $bargainOrderId = null) { $sku = ProductSku::online()->findOrFail($skuId); $product = [ 'sku' => $sku, 'quantity' => $quantity, ]; return $this->verifyOrder($user, [$product], $shippingAddressId, $couponId, $bargainOrderId); } /** * 确认购物车订单 * * @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|null $shippingAddressId * @param int|null $couponId * @param int|null $bargainOrderId * @return array */ protected function verifyOrder(User $user, array $products, ?int $shippingAddressId = null, ?int $couponId = null, ?int $bargainOrderId = null): array { // 获取收货地址 $shippingAddress = $this->getShippingAddress($user, $shippingAddressId); // 优惠券 $coupon = null; if ($couponId) { $coupon = $user->coupons()->onlyAvailable()->findOrFail($couponId); } $bargainOrder = null; if ($bargainOrderId) { $bargainOrder = $user->bargainOrders()->findOrFail($bargainOrderId); } $mapProducts = $this->mapProducts($user, $products, $coupon, $bargainOrder); // 是否支持配送 $shippingSupported = true; // 运费 $shippingFee = 0; if ($shippingAddress) { try { $shippingFee = $this->calculateShippingFee($mapProducts, $shippingAddress); } catch (ShippingNotSupportedException $e) { $shippingFee = 0; $shippingSupported = false; } } list( $productsTotalAmount, $vipDiscountAmount, $couponDiscountAmount, $salesValue, $bargainAmount, ) = $this->calculateFees($mapProducts); $totalAmount = $productsTotalAmount - $couponDiscountAmount - $vipDiscountAmount - $bargainAmount; if ($totalAmount < 0) { $totalAmount = 0; } $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' => trim_trailing_zeros(bcdiv($shippingFee, 100, 2)), // 运费 'products_total_amount' => trim_trailing_zeros(bcdiv($productsTotalAmount, 100, 2)), // 商品总额 'vip_discount_amount' => trim_trailing_zeros(bcdiv($vipDiscountAmount, 100, 2)), // 会员折扣金额 'coupon_discount_amount' => trim_trailing_zeros(bcdiv($couponDiscountAmount, 100, 2)), // 优惠券折扣金额 'total_amount' => trim_trailing_zeros(bcdiv($totalAmount, 100, 2)), // 实付金额 'bargain_amount' => trim_trailing_zeros(bcdiv($bargainAmount, 100, 2)), //砍价金额 ]; } /** * 计算费用 * * @param array $products * @return array */ protected function calculateFees(array $products) { $productsTotalAmount = 0; $vipDiscountAmount = 0; $couponDiscountAmount = 0; $salesValue = 0; $bargainAmount = 0; foreach ($products as $product) { $productsTotalAmount += $product['total_amount']; $vipDiscountAmount += $product['vip_discount_amount']; $couponDiscountAmount += $product['coupon_discount_amount']; $salesValue = bcadd($salesValue, $product['total_sales_value']); $bargainAmount += $product['bargain_amount']; } return [ $productsTotalAmount, $vipDiscountAmount, $couponDiscountAmount, $salesValue, $bargainAmount, ]; } /** * 计算运费 * * @param array $products * @param \App\Models\ShippingAddress $shippingAddress * @return int */ protected function calculateShippingFee(array $products, ?ShippingAddress $shippingAddress): int { // 运费 $shippingFee = 0; if (!$shippingAddress) { return $shippingFee; } $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 {sku: App\Models\ProductSku, quantity: 数量} * @param \App\Models\UserCoupon|null $coupon * @return array * * @throws \App\Exceptions\BizException */ protected function mapProducts(User $user, array $products, ?UserCoupon $coupon, ?BargainOrder $bargainOrder = null): 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'], // 总销售值 'total_sales_value' => bcmul($sku->sales_value, $item['quantity'], 2), //砍价金额 'bargain_amount' => 0, ]); }); //处理砍价金额 if ($bargainOrder) { $bargainDiscountAmount = $this->getBargainDiscountAmount($bargainOrder, $_products->all()); $_products = $_products->map(function ($item) use ($bargainDiscountAmount) { $item['bargain_amount'] = $bargainDiscountAmount[$item['sku']->id] ?? 0; return $item; }); } 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--; $originalAmount = $amount; if ($i > 0) { $amount = (int) bcdiv(bcmul($couponAmount, $amount, 0), $totalAmount, 2); $couponAmount -= $amount; $totalAmount -= $originalAmount; } else { $amount = $couponAmount; } unset($amount); } return $amounts; } /** * */ protected function getBargainDiscountAmount(BargainOrder $bargainOrder, array $products): array { // 砍价的商品 $amounts = []; foreach ($products as $item) { $sku = $item['sku']; $amount = $item['total_amount'] - $item['vip_discount_amount']; // 仅保留商品真实总额大于0的商品 if ($amount > 0) { $amounts[$sku->id] = $amount; } } // 全部商品总额 $totalAmount = array_sum($amounts); $bargainAmount = $bargainOrder->bargain_price ?? 0; // 待计算优惠的商品总数 $i = count($amounts); foreach ($amounts as &$amount) { $i--; $originalAmount = $amount; if ($i > 0) { $amount = (int) bcdiv(bcmul($bargainAmount, $amount, 0), $totalAmount, 2); $bargainAmount -= $amount; $totalAmount -= $originalAmount; } else { $amount = $bargainAmount; } 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.spu'); $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 \App\Enums\PayWay $payWay * @return mixed * * @throws \App\Exceptions\WeChatPayException */ public function pay(Order $order, PayWay $payWay) { if (! $order->isPending()) { throw new BizException('订单状态不是待付款'); } do { $payLog = null; try { $payLog = $order->payLogs()->create([ 'pay_sn' => serial_number(), 'pay_way' => $payWay, ]); } catch (QueryException $e) { if (strpos($e->getMessage(), 'Duplicate entry') === false) { throw $e; } } } while ($payLog === null); $data = [ 'pay_sn' => $payLog->pay_sn, ]; // 如果支付方式为线下支付,或支付金额为 0,则按支付完成处理 if ($order->total_amount === 0 || $payLog->isOffline()) { (new PayService())->handleSuccess($payLog, [ 'pay_at' => now(), ]); $data = null; } elseif ($payLog->isWxpay()) { if (is_null($tradeType = WxpayTradeType::tryFromPayWay($payLog->pay_way))) { throw new BizException('支付方式 非法'); } $params = [ 'body' => app_settings('app.app_name').'-商城订单', 'out_trade_no' => $payLog->pay_sn, 'total_fee' => $order->total_amount, 'trade_type' => $tradeType->value, ]; if ($tradeType === WxpayTradeType::JSAPI) { $socialite = match ($payLog->pay_way) { PayWay::WxpayMiniProgram => SocialiteUser::where([ 'user_id' => $order->user_id, 'socialite_type' => SocialiteType::WechatMiniProgram, ])->first(), }; if ($socialite === null) { throw new BizException('未绑定微信小程序'); } $params['openid'] = $socialite->socialite_id; } $data = (new WxpayService())->pay($params, match ($payLog->pay_way) { PayWay::WxpayMiniProgram => 'mini_program', default => 'default', }); } elseif ($payLog->isAlipay()) { $params = [ 'subject' => app_settings('app.app_name').'-商城订单', 'out_trade_no' => $payLog->pay_sn, 'total_amount' => bcdiv($order->total_amount, 100, 2), ]; $data = app(AlipayService::class)->pay($params); } return [ 'pay_way' => $payLog->pay_way, 'data' => $data, ]; } /** * 确认订单 * * @param \App\Models\Order $order * @param bool $isSettlable * @return void */ public function confirm(Order $order, $isSettlable = false) { if (! $order->isShipped()) { throw new BizException('订单包裹未发完'); } if ($isSettlable && $order->afterSales()->processing()->count() > 0) { throw new BizException('订单商品售后中,不能完成此订单'); } $orderPackageService = new OrderPackageService(); $order->loadMissing('packages'); $packages = $order->packages->filter(function ($item) { return ! $item->is_failed && ! $item->isChecked(); }); foreach ($packages as $package) { $orderPackageService->checkPackage($package, true); } $order->update([ 'status' => Order::STATUS_COMPLETED, 'completed_at' => now(), ]); } /** * 取消订单 * * @param \App\Models\Order $order * @return void */ public function cancel(Order $order) { if (! $order->isPending() && ! $order->isWaitShipping()) { throw new BizException('订单状态不是待付款或待发货'); } if ($order->isWaitShipping()) { $order->refundLogs()->create([ 'sn' => serial_number(), 'amount' => $order->total_amount, 'reason' => '取消订单', ]); } // 门店订单 if ($order->store_id) { // 返还店铺库存 } else { $products = $order->products()->get(); foreach ($products->load(['sku', 'spu']) as $product) { if ($product->sku === null) { continue; } // 如果商品不是赠品,则直接增加商品库存 if (! $product->isGift()) { $product->spu?->increment('sales', -$product->quantity); $product->sku?->update([ 'stock' => DB::Raw("stock + {$product->quantity}"), 'sales' => DB::Raw("sales - {$product->quantity}"), ]); continue; } //原赠品 if ($product->gift_for_sku_id) { $gift = ProductGift::where('sku_id', $product->gift_for_sku_id) ->where('gift_sku_id', $product->sku_id) ->first(); if ($gift === null) { continue; } if ($gift->isLimit()) { $gift->update([ 'remaining' => DB::raw("remaining+{$product->quantity}"), 'sent' => DB::raw("sent-{$product->quantity}"), ]); } else { $gift->decrement('sent', $product->quantity); } continue; } else {//新赠品, 则直接增加商品库存 $product->spu?->increment('sales', -$product->quantity); $product->sku?->update([ 'stock' => DB::Raw("stock + {$product->quantity}"), 'sales' => DB::Raw("sales - {$product->quantity}"), ]); continue; } } } // 取消订单,退券 if ($order->user_coupon_id) { UserCoupon::where('user_id', $order->user_id)->where('id', $order->user_coupon_id)->update([ 'is_use'=>false, ]); } $order->update([ 'status' => Order::STATUS_CANCELLED, ]); } }