erp-backend/app/Http/Controllers/DeliveryController.php
2026-04-01 17:07:04 +08:00

391 lines
13 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\Http\Controllers;
use App\Models\ErpSku;
use App\Models\Order;
use App\Models\DeliveryRecord;
use App\Models\PrintLog;
use App\Models\BatchPrint;
use App\Jobs\SyncDeliveryToPlatform;
use App\Services\StockService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class DeliveryController extends Controller
{
protected $stockService;
public function __construct(StockService $stockService)
{
$this->stockService = $stockService;
// 已删除 $this->middleware('auth:api');
}
/**
* 待发货订单列表(批量打印)
* 仅显示当前用户仓库的订单(若用户有仓库)
*/
public function pendingDelivery(Request $request)
{
$user = auth()->user();
$query = Order::with(['platformOrder', 'items.erpSku'])
->where('audit_status', 'approved')
->where('delivery_status', 'pending')
->where('print_status', 0)
->orderBy('audit_time', 'asc');
if ($user->warehouse_id) {
$query->where('warehouse_id', $user->warehouse_id);
} else {
// 无仓库权限的用户直接返回空结果
return response()->json(Order::paginate(0));
}
$orders = $query->paginate($request->get('per_page', 20));
return response()->json($orders);
}
/**
* 标记打印(支持批量)
*/
public function markPrinted(Request $request)
{
$request->validate([
'order_ids' => 'required|array',
'order_ids.*' => 'exists:orders,id',
'batch_no' => 'nullable|string|max:64',
'print_type' => 'in:normal,reprint',
'status' => 'in:success,failed',
'reason' => 'nullable|string|max:255'
]);
$orderIds = $request->order_ids;
$printType = $request->input('print_type', 'normal');
$status = $request->input('status', 'success'); // 默认成功
$reason = $request->input('reason');
$user = auth()->user();
// 校验订单是否属于当前用户仓库(如果用户有仓库限制)
if ($user->warehouse_id) {
$invalidOrders = Order::whereIn('id', $orderIds)
->where('warehouse_id', '!=', $user->warehouse_id)
->pluck('id')
->toArray();
if (!empty($invalidOrders)) {
return response()->json([
'error' => '部分订单不属于您的仓库,无法打印',
'invalid_order_ids' => $invalidOrders
], 403);
}
}
// 校验订单状态是否允许打印(已审核、待发货)
$invalidStateOrders = Order::whereIn('id', $orderIds)
->where(function ($q) {
$q->where('audit_status', '!=', 'approved')
->orWhere('delivery_status', '!=', 'pending');
})
->pluck('id')
->toArray();
if (!empty($invalidStateOrders)) {
return response()->json([
'error' => '部分订单状态不允许打印',
'invalid_order_ids' => $invalidStateOrders
], 422);
}
// 生成或使用批次号
$batchNo = $request->input('batch_no', 'BATCH_' . Str::random(12));
// 打印状态0=未打印, 1=打印成功, 2=打印失败
$printStatus = $status === 'failed' ? 2 : 1;
DB::beginTransaction();
try {
// 批量更新订单打印状态(失败时保持待发货,不改变订单状态)
Order::whereIn('id', $orderIds)->update([
'print_status' => $printStatus,
'print_time' => $status === 'success' ? now() : null
]);
// 批量插入打印日志(记录成功或失败原因)
$printLogs = [];
foreach ($orderIds as $orderId) {
$printLogs[] = [
'order_id' => $orderId,
'batch_no' => $batchNo,
'operator_id' => $user->id,
'print_time' => now(),
'print_type' => $printType,
'reason' => $status === 'failed' ? $reason : null,
];
}
PrintLog::insert($printLogs);
// 批量打印记录(批次统计)
if (count($orderIds) > 1) {
BatchPrint::updateOrCreate(
['batch_no' => $batchNo],
[
'operator_id' => $user->id,
'order_count' => count($orderIds),
'print_time' => now()
]
);
}
DB::commit();
if ($status === 'failed') {
return response()->json([
'code' => 200,
'message' => '已记录打印失败',
'data' => ['batch_no' => $batchNo, 'count' => count($orderIds), 'reason' => $reason]
]);
}
return response()->json([
'code' => 200,
'message' => '标记打印成功',
'data' => ['batch_no' => $batchNo, 'count' => count($orderIds)]
]);
} catch (\Exception $e) {
DB::rollBack();
Log::error('标记打印失败', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json(['error' => '标记失败:' . $e->getMessage()], 500);
}
}
/**
* 重打订单(单个)
*/
public function reprint(Request $request, $id)
{
$request->validate([
'reason' => 'required|string|max:255',
]);
$order = Order::findOrFail($id);
$user = auth()->user();
// 仓库权限校验
if ($user->warehouse_id && $user->warehouse_id != $order->warehouse_id) {
return response()->json(['error' => '无权操作该订单'], 403);
}
if ($order->delivery_status !== 'pending') {
return response()->json(['error' => '只有待发货的订单可以重打'], 400);
}
DB::beginTransaction();
try {
$order->print_time = now();
$order->print_status = 1;
$order->save();
PrintLog::create([
'order_id' => $id,
'batch_no' => null,
'operator_id' => $user->id,
'print_time' => now(),
'print_type' => 'reprint',
'reason' => $request->reason,
]);
DB::commit();
return response()->json(['message' => '重打成功']);
} catch (\Exception $e) {
DB::rollBack();
Log::error('重打失败', ['order_id' => $id, 'error' => $e->getMessage()]);
return response()->json(['error' => '重打失败:' . $e->getMessage()], 500);
}
}
/**
* 获取打印日志列表
*/
public function printLogs(Request $request)
{
$query = PrintLog::with(['order', 'operator'])->orderBy('id', 'desc');
if ($request->filled('batch_no')) {
$query->where('batch_no', $request->batch_no);
}
if ($request->filled('order_id')) {
$query->where('order_id', $request->order_id);
}
if ($request->filled('print_type')) {
$query->where('print_type', $request->print_type);
}
$logs = $query->paginate($request->get('per_page', 15));
return response()->json($logs);
}
/**
* 获取批量打印记录(批次统计)
*/
public function batchPrintLogs(Request $request)
{
$batches = BatchPrint::orderBy('id', 'desc')
->paginate($request->get('per_page', 15));
return response()->json($batches);
}
/**
* 发货并回传(集成库存扣减)
*/
public function ship(Request $request)
{
$request->validate([
'order_id' => 'required|exists:orders,id',
'tracking_no' => 'required|string|max:100',
'logistics_company' => 'required|string|max:100'
]);
Log::info('发货请求开始', ['order_id' => $request->order_id]);
// 用户认证检查(假设路由已加 auth 中间件,但为了安全仍显式判断)
$user = auth()->user();
if (!$user) {
return response()->json(['error' => '未认证'], 401);
}
$order = Order::with(['items.erpSku', 'warehouse'])->find($request->order_id);
if (!$order) {
Log::error('订单不存在', ['order_id' => $request->order_id]);
return response()->json(['error' => '订单不存在'], 404);
}
// 仓库权限校验(如果用户有仓库限制)
if ($user->warehouse_id && $user->warehouse_id != $order->warehouse_id) {
return response()->json(['error' => '无权操作该订单'], 403);
}
// 订单状态校验
if ($order->delivery_status !== 'pending') {
return response()->json(['error' => '订单状态不允许发货(非待发货)'], 400);
}
if (!$order->warehouse_id) {
return response()->json(['error' => '订单未设置发货仓库'], 400);
}
// 前置检查商品是否都有ERP SKU且库存充足
foreach ($order->items as $item) {
if (!$item->erp_sku_id) {
return response()->json([
'error' => '订单商品未绑定ERP SKUSKU: ' . ($item->platform_sku ?? '未知')
], 422);
}
$erpSku = ErpSku::find($item->erp_sku_id);
if (!$erpSku) {
return response()->json([
'error' => 'ERP SKU不存在ID: ' . $item->erp_sku_id
], 422);
}
// 检查可用库存
try {
$available = $this->stockService->checkStock($erpSku->sku_code, $order->warehouse_id);
} catch (\Exception $e) {
Log::error('库存检查失败', ['sku' => $erpSku->sku_code, 'error' => $e->getMessage()]);
return response()->json(['error' => '库存检查失败:' . $e->getMessage()], 500);
}
if ($available < $item->quantity) {
return response()->json([
'error' => '库存不足SKU: ' . $erpSku->sku_code . ' 可用库存: ' . $available . ',需扣减: ' . $item->quantity
], 422);
}
}
DB::beginTransaction();
try {
// 扣减库存(事务内再次检查并扣减,依赖 StockService 内部的原子性)
foreach ($order->items as $item) {
$erpSku = ErpSku::find($item->erp_sku_id); // 已在循环内查过,可复用变量,但简单起见再查一次
$this->stockService->ship(
$erpSku->sku_code,
$order->warehouse_id,
$item->quantity,
$order->id,
$request->tracking_no
);
}
// 更新订单状态
$order->update([
'express_company' => $request->logistics_company,
'express_no' => $request->tracking_no,
'delivery_status' => 'delivered',
'delivery_time' => now(),
'order_status' => 'shipped',
'sync_status' => 0,
]);
// 保存发货记录
DeliveryRecord::create([
'order_id' => $order->id,
'tracking_no' => $request->tracking_no,
'logistics_company'=> $request->logistics_company,
'sync_status' => 0
]);
DB::commit();
// 异步回传平台
SyncDeliveryToPlatform::dispatch($order->id);
Log::info('发货成功', ['order_id' => $order->id]);
return response()->json(['message' => '发货成功']);
} catch (\Exception $e) {
DB::rollBack();
Log::error('发货失败', [
'order_id' => $order->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json(['error' => '发货失败:' . $e->getMessage()], 500);
}
}
/**
* 回传失败列表
*/
public function syncFailed(Request $request)
{
$orders = Order::with('platformOrder')
->where('sync_status', 2)
->paginate($request->get('per_page', 15));
return response()->json($orders);
}
/**
* 重试回传
*/
public function retrySync($id)
{
$order = Order::findOrFail($id);
if ($order->sync_status != 2) {
return response()->json(['error' => '订单不是回传失败状态'], 422);
}
$order->sync_status = 0;
$order->save();
SyncDeliveryToPlatform::dispatch($order->id);
Log::info('重试回传已加入队列', ['order_id' => $order->id]);
return response()->json(['message' => '已加入重试队列']);
}
}