113 lines
3.4 KiB
PHP
113 lines
3.4 KiB
PHP
<?php
|
||
namespace App\Services;
|
||
|
||
use App\Models\Stock;
|
||
use App\Models\StockLog;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
class StockService
|
||
{
|
||
/**
|
||
* 锁定库存(审核订单时调用)
|
||
*/
|
||
public function lock($skuCode, $warehouseId, $quantity, $orderId)
|
||
{
|
||
if ($quantity <= 0) {
|
||
throw new \InvalidArgumentException('占用数量必须为正数');
|
||
}
|
||
|
||
return DB::transaction(function () use ($skuCode, $warehouseId, $quantity, $orderId) {
|
||
$stock = $this->getStock($skuCode, $warehouseId);
|
||
$available = $this->getAvailableQuantity($stock);
|
||
if ($available < $quantity) {
|
||
throw new \Exception("商品 {$skuCode} 可用库存不足,当前可用{$available},需占用{$quantity}");
|
||
}
|
||
|
||
$stock->increment('locked_quantity', $quantity);
|
||
|
||
StockLog::create([
|
||
'sku_code' => $skuCode,
|
||
'warehouse_id' => $warehouseId,
|
||
'change_quantity' => $quantity,
|
||
'type' => 'lock',
|
||
'order_id' => $orderId,
|
||
'remark' => "锁定库存,订单ID {$orderId}",
|
||
]);
|
||
|
||
return $stock;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取指定SKU在仓库的可用库存
|
||
*/
|
||
public function checkStock($skuCode, $warehouseId)
|
||
{
|
||
$stock = Stock::where('sku_code', $skuCode)
|
||
->where('warehouse_id', $warehouseId)
|
||
->first();
|
||
|
||
if (!$stock) {
|
||
return 0; // 无记录表示库存为0
|
||
}
|
||
|
||
return $this->getAvailableQuantity($stock);
|
||
}
|
||
|
||
/**
|
||
* 发货出库(扣减锁定库存)
|
||
*/
|
||
public function ship($skuCode, $warehouseId, $quantity, $orderId, $trackingNo = null)
|
||
{
|
||
if ($quantity <= 0) {
|
||
throw new \InvalidArgumentException('出库数量必须为正数');
|
||
}
|
||
|
||
return DB::transaction(function () use ($skuCode, $warehouseId, $quantity, $orderId, $trackingNo) {
|
||
$stock = $this->getStock($skuCode, $warehouseId);
|
||
|
||
// 检查锁定库存是否足够
|
||
if ($stock->locked_quantity < $quantity) {
|
||
throw new \Exception("商品 {$skuCode} 锁定库存不足,当前锁定{$stock->locked_quantity},需出库{$quantity}");
|
||
}
|
||
|
||
// 扣减总库存和锁定库存
|
||
$stock->decrement('quantity', $quantity);
|
||
$stock->decrement('locked_quantity', $quantity);
|
||
|
||
StockLog::create([
|
||
'sku_code' => $skuCode,
|
||
'warehouse_id' => $warehouseId,
|
||
'change_quantity' => -$quantity,
|
||
'type' => 'ship',
|
||
'order_id' => $orderId,
|
||
'remark' => "发货出库,订单ID {$orderId},运单号 {$trackingNo}",
|
||
]);
|
||
|
||
return $stock;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取或创建库存记录
|
||
*/
|
||
protected function getStock($skuCode, $warehouseId)
|
||
{
|
||
return Stock::firstOrCreate(
|
||
['sku_code' => $skuCode, 'warehouse_id' => $warehouseId],
|
||
[
|
||
'quantity' => 0,
|
||
'locked_quantity' => 0,
|
||
'defective_quantity' => 0,
|
||
]
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 计算可用库存
|
||
*/
|
||
protected function getAvailableQuantity($stock)
|
||
{
|
||
return $stock->quantity - $stock->locked_quantity - $stock->defective_quantity;
|
||
}
|
||
} |