orderBy('created_at', 'desc'); // 筛选条件 if ($request->filled('status')) { $query->where('status', $request->status); } if ($request->filled('type')) { $query->where('type', $request->type); } if ($request->filled('keyword')) { $keyword = $request->keyword; $query->where(function ($q) use ($keyword) { $q->where('short_id', 'like', "%{$keyword}%") ->orWhere('order_short_id', 'like', "%{$keyword}%") ->orWhere('reason', 'like', "%{$keyword}%"); }); } if ($request->filled('date_range')) { $dates = explode(',', $request->date_range); if (count($dates) === 2) { $query->whereBetween('created_at', [$dates[0], $dates[1]]); } } $perPage = $request->input('limit', 20); $afterSales = $query->paginate($perPage); // 格式化数据 $list = $afterSales->map(function ($as) { return [ 'id' => $as->id, 'short_id' => $as->short_id, 'order_id' => $as->order_id, 'order_short_id' => $as->order_short_id, 'type' => $as->type, 'type_text' => AfterSale::getTypeText($as->type), 'status' => $as->status, 'status_text' => AfterSale::getStatusText($as->status), 'reason' => $as->reason, 'description' => $as->description, 'refund_amount' => $as->refund_amount, 'return_express_company' => $as->return_express_company, 'return_express_no' => $as->return_express_no, 'reject_reason' => $as->reject_reason, 'processor_name' => $as->processor?->name, 'processed_at' => $as->processed_at?->format('Y-m-d H:i:s'), 'creator_name' => $as->creator?->name, 'created_at' => $as->created_at?->format('Y-m-d H:i:s'), 'completed_at' => $as->completed_at?->format('Y-m-d H:i:s'), 'items' => $as->items->map(fn($item) => [ 'id' => $item->id, 'goods_name' => $item->goods_name, 'erp_sku' => $item->erp_sku, 'platform_sku' => $item->platform_sku, 'quantity' => $item->quantity, 'price' => $item->price, 'total_amount' => $item->total_amount, ]), 'order' => $as->order ? [ 'id' => $as->order->id, 'short_id' => $as->order->short_id, 'platform' => $as->order->platform, 'shop_name' => $as->order->shop_name, 'receiver_name' => $as->order->receiver_name, 'receiver_phone' => $as->order->receiver_phone, 'receiver_address' => $as->order->receiver_address, 'total_amount' => $as->order->total_amount, 'order_status' => $as->order->order_status, 'delivery_status' => $as->order->delivery_status, ] : null, ]; }); return response()->json([ 'code' => 200, 'data' => [ 'list' => $list, 'total' => $afterSales->total(), 'current_page' => $afterSales->currentPage(), 'last_page' => $afterSales->lastPage(), ], 'message' => 'success', ]); } /** * 获取可售后的订单(仅已发货的订单) */ public function availableOrders(Request $request) { $query = Order::where('order_status', 'shipped') ->orderBy('created_at', 'desc'); if ($request->filled('keyword')) { $keyword = $request->keyword; $query->where(function ($q) use ($keyword) { $q->where('short_id', 'like', "%{$keyword}%") ->orWhere('platform_order_sn', 'like', "%{$keyword}%") ->orWhere('receiver_name', 'like', "%{$keyword}%") ->orWhere('receiver_phone', 'like', "%{$keyword}%"); }); } $perPage = $request->input('limit', 10); $orders = $query->paginate($perPage); $list = $orders->map(function ($order) { return [ 'id' => $order->id, 'short_id' => $order->short_id, 'platform' => $order->platform, 'shop_name' => $order->shop_name, 'receiver_name' => $order->receiver_name, 'receiver_phone' => $order->receiver_phone, 'receiver_address' => $order->receiver_address, 'total_amount' => $order->total_amount, 'goods_amount' => $order->goods_amount, 'freight' => $order->freight, 'express_company' => $order->express_company, 'express_no' => $order->express_no, 'order_status' => $order->order_status, 'delivery_status' => $order->delivery_status, 'delivery_time' => $order->delivery_time, 'items' => $order->items->map(fn($item) => [ 'id' => $item->id, 'goods_name' => $item->goods_name, 'erp_sku' => $item->erp_sku, 'platform_sku' => $item->platform_sku, 'quantity' => $item->quantity, 'price' => $item->price, 'total_amount' => $item->total_amount, ]), ]; }); return response()->json([ 'code' => 200, 'data' => [ 'list' => $list, 'total' => $orders->total(), 'current_page' => $orders->currentPage(), 'last_page' => $orders->lastPage(), ], 'message' => 'success', ]); } /** * 获取可售后的订单(包含未发货的仅退款订单) */ public function allAvailableOrders(Request $request) { // 已发货订单:退货、换货 // 未发货订单:仅退款 $query = Order::whereIn('order_status', ['shipped', 'pending', 'auditing']) ->where('delivery_status', 'delivered') ->orderBy('created_at', 'desc'); // 仅退款:未发货的订单(delivery_status = pending) $queryOnlyRefund = Order::whereIn('order_status', ['pending', 'auditing']) ->where('delivery_status', 'pending') ->orderBy('created_at', 'desc'); if ($request->filled('keyword')) { $keyword = $request->keyword; $query->where(function ($q) use ($keyword) { $q->where('short_id', 'like', "%{$keyword}%") ->orWhere('platform_order_sn', 'like', "%{$keyword}%") ->orWhere('receiver_name', 'like', "%{$keyword}%"); }); $queryOnlyRefund->where(function ($q) use ($keyword) { $q->where('short_id', 'like', "%{$keyword}%") ->orWhere('platform_order_sn', 'like', "%{$keyword}%") ->orWhere('receiver_name', 'like', "%{$keyword}%"); }); } $perPage = $request->input('limit', 10); $ordersShipped = $query->paginate($perPage); $ordersNotShipped = $queryOnlyRefund->paginate($perPage); $formatOrders = function ($orders) { return $orders->map(function ($order) { return [ 'id' => $order->id, 'short_id' => $order->short_id, 'platform' => $order->platform, 'shop_name' => $order->shop_name, 'receiver_name' => $order->receiver_name, 'receiver_phone' => $order->receiver_phone, 'receiver_address' => $order->receiver_address, 'total_amount' => $order->total_amount, 'goods_amount' => $order->goods_amount, 'freight' => $order->freight, 'express_company' => $order->express_company, 'express_no' => $order->express_no, 'order_status' => $order->order_status, 'delivery_status' => $order->delivery_status, 'delivery_time' => $order->delivery_time, 'can_refund_only' => $order->delivery_status === 'pending', // 未发货可仅退款 'items' => $order->items->map(fn($item) => [ 'id' => $item->id, 'goods_name' => $item->goods_name, 'erp_sku' => $item->erp_sku, 'platform_sku' => $item->platform_sku, 'quantity' => $item->quantity, 'price' => $item->price, 'total_amount' => $item->total_amount, ]), ]; }); }; return response()->json([ 'code' => 200, 'data' => [ 'shipped_orders' => [ 'list' => $formatOrders($ordersShipped), 'total' => $ordersShipped->total(), 'current_page' => $ordersShipped->currentPage(), 'last_page' => $ordersShipped->lastPage(), ], 'not_shipped_orders' => [ 'list' => $formatOrders($ordersNotShipped), 'total' => $ordersNotShipped->total(), 'current_page' => $ordersNotShipped->currentPage(), 'last_page' => $ordersNotShipped->lastPage(), ], ], 'message' => 'success', ]); } /** * 售后详情 */ public function show(string $id) { $afterSale = AfterSale::with(['order', 'processor', 'creator', 'items'])->find($id); if (!$afterSale) { return response()->json(['code' => 404, 'message' => '售后单不存在'], 404); } return response()->json([ 'code' => 200, 'data' => [ 'id' => $afterSale->id, 'short_id' => $afterSale->short_id, 'order_id' => $afterSale->order_id, 'order_short_id' => $afterSale->order_short_id, 'type' => $afterSale->type, 'type_text' => AfterSale::getTypeText($afterSale->type), 'status' => $afterSale->status, 'status_text' => AfterSale::getStatusText($afterSale->status), 'reason' => $afterSale->reason, 'description' => $afterSale->description, 'refund_amount' => $afterSale->refund_amount, 'return_express_company' => $afterSale->return_express_company, 'return_express_no' => $afterSale->return_express_no, 'reject_reason' => $afterSale->reject_reason, 'processor_name' => $afterSale->processor?->name, 'processed_at' => $afterSale->processed_at?->format('Y-m-d H:i:s'), 'creator_name' => $afterSale->creator?->name, 'created_at' => $afterSale->created_at?->format('Y-m-d H:i:s'), 'completed_at' => $afterSale->completed_at?->format('Y-m-d H:i:s'), 'items' => $afterSale->items->map(fn($item) => [ 'id' => $item->id, 'order_item_id' => $item->order_item_id, 'goods_name' => $item->goods_name, 'erp_sku' => $item->erp_sku, 'platform_sku' => $item->platform_sku, 'quantity' => $item->quantity, 'price' => $item->price, 'total_amount' => $item->total_amount, ]), 'order' => $afterSale->order ? [ 'id' => $afterSale->order->id, 'short_id' => $afterSale->order->short_id, 'platform' => $afterSale->order->platform, 'shop_name' => $afterSale->order->shop_name, 'receiver_name' => $afterSale->order->receiver_name, 'receiver_phone' => $afterSale->order->receiver_phone, 'receiver_address' => $afterSale->order->receiver_address, 'total_amount' => $afterSale->order->total_amount, 'goods_amount' => $afterSale->order->goods_amount, 'freight' => $afterSale->order->freight, 'express_company' => $afterSale->order->express_company, 'express_no' => $afterSale->order->express_no, 'order_status' => $afterSale->order->order_status, 'delivery_status' => $afterSale->order->delivery_status, 'items' => $afterSale->order->items->map(fn($item) => [ 'id' => $item->id, 'goods_name' => $item->goods_name, 'erp_sku' => $item->erp_sku, 'platform_sku' => $item->platform_sku, 'quantity' => $item->quantity, 'price' => $item->price, 'total_amount' => $item->total_amount, ]), ] : null, ], 'message' => 'success', ]); } /** * 创建售后单 */ public function store(Request $request) { $validator = Validator::make($request->all(), [ 'order_id' => 'required|integer|exists:orders,id', 'type' => 'required|in:refund_only,return,exchange', 'reason' => 'required|string|max:255', 'description' => 'nullable|string', 'items' => 'required|array|min:1', 'items.*.order_item_id' => 'required|integer|exists:order_items,id', 'items.*.quantity' => 'required|integer|min:1', 'refund_amount' => 'nullable|numeric|min:0', ]); if ($validator->fails()) { return response()->json(['code' => 422, 'message' => '验证失败', 'errors' => $validator->errors()], 422); } $order = Order::find($request->order_id); // 仅退款必须未发货 if ($request->type === 'refund_only' && $order->delivery_status !== 'pending') { return response()->json(['code' => 400, 'message' => '仅退款只能针对未发货的订单'], 400); } // 退货/换货必须已发货 if (in_array($request->type, ['return', 'exchange']) && $order->delivery_status !== 'delivered') { return response()->json(['code' => 400, 'message' => '退货/换货只能针对已发货的订单'], 400); } try { $afterSale = DB::transaction(function () use ($request, $order) { // 计算退款金额 $refundAmount = 0; if ($request->filled('refund_amount')) { $refundAmount = $request->refund_amount; } else { // 自动计算:选中的商品小计之和 foreach ($request->items as $item) { $orderItem = OrderItem::find($item['order_item_id']); $refundAmount += $orderItem->price * $item['quantity']; } // 如果是退货/换货,退款不含运费 if (in_array($request->type, ['return', 'exchange'])) { $refundAmount = min($refundAmount, $order->goods_amount); } } $afterSale = AfterSale::create([ 'short_id' => AfterSale::generateShortId(), 'order_id' => $order->id, 'order_short_id' => $order->short_id, 'type' => $request->type, 'status' => AfterSale::STATUS_PENDING, 'reason' => $request->reason, 'description' => $request->description, 'refund_amount' => $refundAmount, 'created_by' => auth()->id(), ]); // 创建明细 foreach ($request->items as $item) { $orderItem = OrderItem::find($item['order_item_id']); AfterSaleItem::create([ 'after_sale_id' => $afterSale->id, 'order_item_id' => $item['order_item_id'], 'goods_id' => $orderItem->goods_id, 'goods_name' => $orderItem->goods_name, 'erp_sku' => $orderItem->erp_sku, 'platform_sku' => $orderItem->platform_sku, 'quantity' => $item['quantity'], 'price' => $orderItem->price, 'total_amount' => $orderItem->price * $item['quantity'], ]); } return $afterSale; }); return response()->json([ 'code' => 200, 'data' => $afterSale, 'message' => '售后单创建成功', ]); } catch (\Exception $e) { return response()->json(['code' => 500, 'message' => '创建失败:' . $e->getMessage()], 500); } } /** * 更新售后单状态 */ public function updateStatus(Request $request, string $id) { $validator = Validator::make($request->all(), [ 'status' => 'required|in:pending,processing,completed,rejected', 'reject_reason' => 'required_if:status,rejected|nullable|string|max:255', 'return_express_company' => 'nullable|string|max:100', 'return_express_no' => 'nullable|string|max:100', ]); if ($validator->fails()) { return response()->json(['code' => 422, 'message' => '验证失败', 'errors' => $validator->errors()], 422); } $afterSale = AfterSale::find($id); if (!$afterSale) { return response()->json(['code' => 404, 'message' => '售后单不存在'], 404); } // 状态流转校验 $allowedTransitions = [ 'pending' => ['processing', 'rejected'], 'processing' => ['completed', 'rejected'], 'processing' => ['completed', 'rejected'], 'completed' => [], 'rejected' => [], ]; if (!in_array($request->status, $allowedTransitions[$afterSale->status] ?? [])) { return response()->json(['code' => 400, 'message' => '当前状态不允许此操作'], 400); } $updateData = [ 'status' => $request->status, 'processed_by' => auth()->id(), 'processed_at' => now(), ]; if ($request->status === 'rejected') { $updateData['reject_reason'] = $request->reject_reason; } if ($request->status === 'completed') { $updateData['completed_at'] = now(); } if ($request->filled('return_express_company')) { $updateData['return_express_company'] = $request->return_express_company; } if ($request->filled('return_express_no')) { $updateData['return_express_no'] = $request->return_express_no; } $afterSale->update($updateData); return response()->json([ 'code' => 200, 'data' => $afterSale, 'message' => '状态更新成功', ]); } /** * 删除售后单(仅待处理状态可删除) */ public function destroy(string $id) { $afterSale = AfterSale::find($id); if (!$afterSale) { return response()->json(['code' => 404, 'message' => '售后单不存在'], 404); } if ($afterSale->status !== 'pending') { return response()->json(['code' => 400, 'message' => '仅待处理状态的售后单可删除'], 400); } // 删除明细 $afterSale->items()->delete(); $afterSale->delete(); return response()->json(['code' => 200, 'message' => '删除成功']); } /** * 统计待处理数量 */ public function stats(Request $request) { $pending = AfterSale::where('status', 'pending')->count(); $processing = AfterSale::where('status', 'processing')->count(); $completedToday = AfterSale::where('status', 'completed') ->whereDate('completed_at', today()) ->count(); return response()->json([ 'code' => 200, 'data' => [ 'pending' => $pending, 'processing' => $processing, 'completed_today' => $completedToday, ], 'message' => 'success', ]); } }