创建订单时,如果有赠品需扣赠品
parent
8f4866ff05
commit
07256db41b
|
|
@ -10,10 +10,9 @@ use App\Exceptions\BizException;
|
|||
use App\Helpers\Paginator as PaginatorHelper;
|
||||
use App\Models\KuaidiLog;
|
||||
use App\Services\OrderService;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
|
|
@ -94,13 +93,13 @@ class OrderController extends Controller
|
|||
$request->input('coupon_id'),
|
||||
$request->input('note'),
|
||||
);
|
||||
}, 3);
|
||||
} catch (ModelNotFoundException | BizException $e) {
|
||||
throw $e;
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
});
|
||||
} catch (QueryException $e) {
|
||||
if (strpos($e->getMessage(), 'Numeric value out of range') !== false) {
|
||||
$e = new BizException('商品库存不足');
|
||||
}
|
||||
|
||||
throw new BizException('系统繁忙,请稍后再试');
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return OrderResource::make($order);
|
||||
|
|
@ -133,19 +132,11 @@ class OrderController extends Controller
|
|||
{
|
||||
$user = $request->user();
|
||||
|
||||
try {
|
||||
return DB::transaction(function () use ($id, $user) {
|
||||
$order = $user->orders()->lockForUpdate()->findOrFail($id);
|
||||
DB::transaction(function () use ($id, $user) {
|
||||
$order = $user->orders()->lockForUpdate()->findOrFail($id);
|
||||
|
||||
(new OrderService())->confirm($order);
|
||||
});
|
||||
} catch (ModelNotFoundException | BizException $e) {
|
||||
throw $e;
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
throw new BizException('确认失败,请重试');
|
||||
}
|
||||
(new OrderService())->confirm($order);
|
||||
});
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
|
@ -161,19 +152,11 @@ class OrderController extends Controller
|
|||
{
|
||||
$user = $request->user();
|
||||
|
||||
try {
|
||||
return DB::transaction(function () use ($id, $user) {
|
||||
$order = $user->orders()->lockForUpdate()->findOrFail($id);
|
||||
DB::transaction(function () use ($id, $user) {
|
||||
$order = $user->orders()->lockForUpdate()->findOrFail($id);
|
||||
|
||||
(new OrderService())->cancel($order);
|
||||
});
|
||||
} catch (ModelNotFoundException | BizException $e) {
|
||||
throw $e;
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
throw new BizException('取消失败,请重试');
|
||||
}
|
||||
(new OrderService())->cancel($order);
|
||||
});
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
|
@ -193,19 +176,11 @@ class OrderController extends Controller
|
|||
|
||||
$user = $request->user();
|
||||
|
||||
try {
|
||||
return DB::transaction(function () use ($id, $user, $input) {
|
||||
$order = $user->orders()->findOrFail($id);
|
||||
return DB::transaction(function () use ($id, $user, $input) {
|
||||
$order = $user->orders()->findOrFail($id);
|
||||
|
||||
return (new OrderService())->pay($order, $input['pay_way']);
|
||||
});
|
||||
} catch (ModelNotFoundException | BizException $e) {
|
||||
throw $e;
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
throw new BizException('支付失败,请重试');
|
||||
}
|
||||
return (new OrderService())->pay($order, $input['pay_way']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class OrderProduct extends Model
|
|||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'gift_for_sku_id',
|
||||
'user_id',
|
||||
'order_id',
|
||||
'spu_id',
|
||||
|
|
@ -76,6 +77,16 @@ class OrderProduct extends Model
|
|||
return $this->belongsTo(ProductSku::class, 'sku_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认此订单商品是否是赠品
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isGift()
|
||||
{
|
||||
return $this->gift_for_sku_id !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单商品是否能发起售后
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ProductGift extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
|
@ -17,5 +14,14 @@ class ProductGift extends Model
|
|||
'gift_sku_id',
|
||||
'num',
|
||||
'sent',
|
||||
'remaining',
|
||||
];
|
||||
|
||||
/**
|
||||
* 赠送的商品 SKU
|
||||
*/
|
||||
public function giftSku()
|
||||
{
|
||||
return $this->belongsTo(ProductSku::class, 'gift_sku_id');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use App\Models\ProductSku;
|
|||
use App\Models\ShippingAddress;
|
||||
use App\Models\User;
|
||||
use App\Models\UserCoupon;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderService
|
||||
|
|
@ -170,11 +171,13 @@ class OrderService
|
|||
|
||||
foreach ($mapProducts as $product) {
|
||||
$sku = $product['sku'];
|
||||
$qty = $product['quantity'];
|
||||
|
||||
// 支付金额 = 商品总额 - 优惠券折扣金额- 会员折扣金额
|
||||
$totalAmount = $product['total_amount'] - $product['coupon_discount_amount'] - $product['vip_discount_amount'];
|
||||
|
||||
$orderProducts[] = [
|
||||
'gift_for_sku_id' => null,
|
||||
'user_id' => $order->user_id,
|
||||
'order_id' => $order->id,
|
||||
'spu_id' => $sku->spu_id,
|
||||
|
|
@ -186,8 +189,8 @@ class OrderService
|
|||
'weight' => $sku->weight,
|
||||
'sell_price' => $sku->sell_price,
|
||||
'vip_price' => $sku->vip_price,
|
||||
'quantity' => $product['quantity'],
|
||||
'remain_quantity' => $product['quantity'],
|
||||
'quantity' => $qty,
|
||||
'remain_quantity' => $qty, // 剩余发货数量
|
||||
'coupon_discount_amount' => $product['coupon_discount_amount'],
|
||||
'vip_discount_amount' => $product['vip_discount_amount'],
|
||||
'total_amount' => $totalAmount,
|
||||
|
|
@ -195,9 +198,34 @@ class OrderService
|
|||
'updated_at' => $order->updated_at,
|
||||
];
|
||||
|
||||
$sku->update([
|
||||
'stock' => DB::raw("stock - {$product['quantity']}"), // 库存
|
||||
]);
|
||||
// 将赠品加入订单中
|
||||
$gifts = $this->deductProduct($sku, $qty);
|
||||
|
||||
foreach ($gifts as $gift) {
|
||||
$giftSku = $gift['sku'];
|
||||
|
||||
$orderProducts[] = [
|
||||
'gift_for_sku_id' => $sku->id,
|
||||
'user_id' => $order->user_id,
|
||||
'order_id' => $order->id,
|
||||
'spu_id' => $giftSku->spu_id,
|
||||
'sku_id' => $giftSku->id,
|
||||
'category_id' => $giftSku->category_id,
|
||||
'name' => $giftSku->name,
|
||||
'specs' => json_encode($giftSku->specs),
|
||||
'cover' => $giftSku->cover,
|
||||
'weight' => $giftSku->weight,
|
||||
'sell_price' => $giftSku->sell_price,
|
||||
'vip_price' => $giftSku->vip_price,
|
||||
'quantity' => $gift['num'],
|
||||
'remain_quantity' => $gift['num'], // 剩余发货数量
|
||||
'coupon_discount_amount' => 0,
|
||||
'vip_discount_amount' => 0,
|
||||
'total_amount' => 0,
|
||||
'created_at' => $order->created_at,
|
||||
'updated_at' => $order->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 处理赠品
|
||||
|
|
@ -209,6 +237,90 @@ class OrderService
|
|||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣商品的库存和赠品数量
|
||||
*
|
||||
* @param \App\Models\ProductSku $sku
|
||||
* @param int $qty
|
||||
* @return array
|
||||
*/
|
||||
protected function deductProduct(ProductSku $sku, int $qty)
|
||||
{
|
||||
// 扣商品库存
|
||||
$sku->update([
|
||||
'stock' => DB::raw("stock - {$qty}"), // 库存
|
||||
]);
|
||||
|
||||
try {
|
||||
return retry(5, function () use ($sku, $qty) {
|
||||
return $this->deductGifts($sku, $qty);
|
||||
}, 0, function ($e) {
|
||||
// 如果赠品库存不足时,需重试
|
||||
return $e instanceof QueryException
|
||||
&& strpos($e->getMessage(), 'Numeric value out of range') !== false;
|
||||
});
|
||||
} catch (QueryException $e) {
|
||||
// 赠品库存不足
|
||||
if (strpos($e->getMessage(), 'Numeric value out of range') !== false) {
|
||||
$e = new BizException('下单人数过多,请稍后再试!');
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣出商品的赠品
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
// 如果赠品有限,且剩余数量不足时,直接赠送剩余赠品
|
||||
if ($gift->limit !== 0 && $gift->remaining < $qty) {
|
||||
$qty = $gift->remaining;
|
||||
}
|
||||
|
||||
// 如果赠送的份数小于1,则不赠送
|
||||
if ($qty < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($gift->limit === 0) {
|
||||
$gift->increment('sent', $qty);
|
||||
} else {
|
||||
$gift->update([
|
||||
'remaining' => DB::raw("remaining-{$qty}"),
|
||||
'sent' => DB::raw("sent+{$qty}"),
|
||||
]);
|
||||
}
|
||||
|
||||
$gifts[] = [
|
||||
'sku' => $gift->giftSku,
|
||||
'num' => $qty*$gift->num, // 赠送商品总数
|
||||
];
|
||||
}
|
||||
|
||||
return $gifts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认快速下单
|
||||
*
|
||||
|
|
@ -678,7 +790,10 @@ class OrderService
|
|||
$products = $order->products()->get();
|
||||
|
||||
foreach ($products->load('sku') as $product) {
|
||||
$product->sku?->increment('stock', $product->quantity);
|
||||
// 取消订单时,赠品不退回
|
||||
if (! $product->isGift()) {
|
||||
$product->sku?->increment('stock', $product->quantity);
|
||||
}
|
||||
}
|
||||
|
||||
$order->update([
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ class CreateProductGiftsTable extends Migration
|
|||
$table->id();
|
||||
$table->unsignedBigInteger('sku_id')->comment('主SKU商品ID');
|
||||
$table->unsignedBigInteger('gift_sku_id')->comment('赠品SKU商品ID');
|
||||
$table->unsignedInteger('num')->default(0)->comment('赠送数量');
|
||||
$table->unsignedInteger('limit')->default(0)->comment('上限数量');
|
||||
$table->unsignedInteger('sent')->default(0)->comment('已送数量');
|
||||
$table->unsignedInteger('num')->default(0)->comment('每份赠送数量');
|
||||
$table->unsignedInteger('limit')->default(0)->comment('上限(份)');
|
||||
$table->unsignedInteger('sent')->default(0)->comment('已送(份)');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddRemainingToProductGiftsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('product_gifts', function (Blueprint $table) {
|
||||
$table->unsignedInteger('remaining')->default(0)->comment('剩余(份)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('product_gifts', function (Blueprint $table) {
|
||||
$table->dropColumn(['remaining']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddGiftForSkuIdToOrderProductsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('order_products', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('gift_for_sku_id')->nullable()->comment('礼品所属的商品 SKU');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('order_products', function (Blueprint $table) {
|
||||
$table->dropColumn(['gift_for_sku_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue