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 SKU,SKU: ' . ($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' => '已加入重试队列']); } }