erp-backend/app/Services/StockService.php
Yatu e80e57cdcf feat: B-001 驳回库存释放 + B-002 缺失路由 + B-003 delivery路径统一
- StockService: 添加unlockStock()方法
- OrderController: 驳回时自动解锁库存
- routes/api.php: 添加warehouse/bindings、operation-logs、delivery统一路由
2026-04-01 19:05:21 +08:00

144 lines
4.4 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
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;
});
}
/**
* 解锁库存(驳回订单时调用)
*/
public function unlockStock($skuCode, $warehouseId, $quantity, $orderId)
{
if ($quantity <= 0) {
throw new \InvalidArgumentException('解锁数量必须为正数');
}
return DB::transaction(function () use ($skuCode, $warehouseId, $quantity, $orderId) {
$stock = $this->getStock($skuCode, $warehouseId);
if ($stock->locked_quantity < $quantity) {
throw new \Exception("商品 {$skuCode} 锁定库存不足,当前锁定{$stock->locked_quantity},需解锁{$quantity}");
}
$stock->decrement('locked_quantity', $quantity);
StockLog::create([
'sku_code' => $skuCode,
'warehouse_id' => $warehouseId,
'change_quantity' => -$quantity,
'type' => 'unlock',
'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;
}
}