1363 lines
44 KiB
PHP
1363 lines
44 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Http\Requests\OrderRequest;
|
||
use App\Models\Order;
|
||
use App\Models\OrderItem;
|
||
use App\Models\Stock;
|
||
use App\Models\StockLog;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Str;
|
||
|
||
class OrderController extends Controller
|
||
{
|
||
/**
|
||
* 订单列表
|
||
*/
|
||
public function index(Request $request)
|
||
{
|
||
$query = Order::with(['items', 'warehouse'])
|
||
->orderBy('created_at', 'desc');
|
||
|
||
// 筛选条件
|
||
if ($request->filled('platform')) {
|
||
$query->where('platform', $request->platform);
|
||
}
|
||
|
||
if ($request->filled('shop_id')) {
|
||
$query->where('shop_id', $request->shop_id);
|
||
}
|
||
|
||
if ($request->filled('order_status')) {
|
||
$query->where('order_status', $request->order_status);
|
||
}
|
||
|
||
if ($request->filled('audit_status')) {
|
||
$query->where('audit_status', $request->audit_status);
|
||
}
|
||
|
||
if ($request->filled('delivery_status')) {
|
||
$query->where('delivery_status', $request->delivery_status);
|
||
}
|
||
|
||
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}%");
|
||
});
|
||
}
|
||
|
||
if ($request->filled('date_range')) {
|
||
$dates = explode(',', $request->date_range);
|
||
if (count($dates) === 2) {
|
||
$query->whereBetween('order_time', [$dates[0], $dates[1]]);
|
||
}
|
||
}
|
||
|
||
$perPage = $request->input('limit', 10);
|
||
$orders = $query->paginate($perPage);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => [
|
||
'list' => $orders->items(),
|
||
'total' => $orders->total(),
|
||
'current_page' => $orders->currentPage(),
|
||
'last_page' => $orders->lastPage(),
|
||
],
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 订单详情
|
||
*/
|
||
public function show(string $id)
|
||
{
|
||
$order = Order::with(['items', 'warehouse'])->find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $order,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 手动拉取订单
|
||
*/
|
||
public function pullOrders(Request $request)
|
||
{
|
||
$request->validate([
|
||
'platform' => 'required|string',
|
||
'shop_id' => 'required|integer',
|
||
'pull_type' => 'required|in:all,increment,specify',
|
||
'order_ids' => 'required_if:pull_type,specify|string',
|
||
'start_time' => 'nullable|date',
|
||
'end_time' => 'nullable|date',
|
||
]);
|
||
|
||
// TODO: 这里应该调用平台API拉取订单
|
||
// 暂时返回模拟数据
|
||
$mockOrders = [
|
||
[
|
||
'platform_order_sn' => 'TEST' . Str::random(10),
|
||
'order_time' => now()->toDateTimeString(),
|
||
'buyer_nick' => '测试买家',
|
||
'receiver_name' => '张三',
|
||
'receiver_phone' => '13800138000',
|
||
'receiver_address' => '测试地址',
|
||
'goods_amount' => 199.00,
|
||
'discount_amount' => 0,
|
||
'freight' => 10.00,
|
||
'total_amount' => 209.00,
|
||
'platform_status' => 'WAIT_SELLER_SEND_GOODS',
|
||
'items' => [
|
||
[
|
||
'goods_name' => '测试商品',
|
||
'platform_sku' => 'SKU001',
|
||
'quantity' => 1,
|
||
'price' => 199.00,
|
||
'total_amount' => 199.00,
|
||
]
|
||
]
|
||
]
|
||
];
|
||
|
||
$createdCount = 0;
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
foreach ($mockOrders as $orderData) {
|
||
// 生成短ID
|
||
$shortId = 'O' . date('Ymd') . strtoupper(Str::random(6));
|
||
|
||
$order = Order::create([
|
||
'short_id' => $shortId,
|
||
'platform_order_sn' => $orderData['platform_order_sn'],
|
||
'platform' => $request->platform,
|
||
'shop_id' => $request->shop_id,
|
||
'shop_name' => '测试店铺',
|
||
'order_time' => $orderData['order_time'],
|
||
'buyer_nick' => $orderData['buyer_nick'],
|
||
'receiver_name' => $orderData['receiver_name'],
|
||
'receiver_phone' => $orderData['receiver_phone'],
|
||
'receiver_address' => $orderData['receiver_address'],
|
||
'goods_amount' => $orderData['goods_amount'],
|
||
'discount_amount' => $orderData['discount_amount'],
|
||
'freight' => $orderData['freight'],
|
||
'total_amount' => $orderData['total_amount'],
|
||
'order_status' => 'pending',
|
||
'platform_status' => $orderData['platform_status'],
|
||
'audit_status' => 'pending',
|
||
'delivery_status' => 'pending',
|
||
]);
|
||
|
||
// 创建订单项
|
||
foreach ($orderData['items'] as $itemData) {
|
||
OrderItem::create([
|
||
'order_id' => $order->id,
|
||
'goods_name' => $itemData['goods_name'],
|
||
'platform_sku' => $itemData['platform_sku'],
|
||
'quantity' => $itemData['quantity'],
|
||
'price' => $itemData['price'],
|
||
'total_amount' => $itemData['total_amount'],
|
||
]);
|
||
}
|
||
|
||
$createdCount++;
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => ['count' => $createdCount],
|
||
'message' => "成功拉取 {$createdCount} 个订单"
|
||
]);
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
return response()->json([
|
||
'code' => 500,
|
||
'message' => '拉取失败: ' . $e->getMessage()
|
||
], 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量审核订单
|
||
*/
|
||
public function batchAudit(Request $request)
|
||
{
|
||
$request->validate([
|
||
'order_ids' => 'required|array|min:1',
|
||
'order_ids.*' => 'integer|exists:orders,id',
|
||
'action' => 'required|in:approve,reject',
|
||
'comment' => 'required_if:action,reject|string|max:500',
|
||
]);
|
||
|
||
$successCount = 0;
|
||
$failedOrders = [];
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
foreach ($request->order_ids as $orderId) {
|
||
$order = Order::find($orderId);
|
||
|
||
if (!$order) {
|
||
$failedOrders[] = ['id' => $orderId, 'reason' => '订单不存在'];
|
||
continue;
|
||
}
|
||
|
||
// 只有待审核状态的订单可以审核
|
||
if ($order->audit_status !== 'pending') {
|
||
$failedOrders[] = ['id' => $orderId, 'reason' => '订单状态不可审核'];
|
||
continue;
|
||
}
|
||
|
||
$order->update([
|
||
'audit_status' => $request->action === 'approve' ? 'approved' : 'rejected',
|
||
'audit_comment' => $request->comment,
|
||
'order_status' => $request->action === 'approve' ? 'auditing' : 'pending',
|
||
]);
|
||
|
||
$successCount++;
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => [
|
||
'success_count' => $successCount,
|
||
'failed_orders' => $failedOrders,
|
||
],
|
||
'message' => "成功审核 {$successCount} 个订单"
|
||
]);
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
return response()->json([
|
||
'code' => 500,
|
||
'message' => '审核失败: ' . $e->getMessage()
|
||
], 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 单个订单审核
|
||
*/
|
||
public function auditOrder(Request $request, string $id)
|
||
{
|
||
$request->validate([
|
||
'action' => 'required|in:approve,reject',
|
||
'comment' => 'required_if:action,reject|string|max:500',
|
||
]);
|
||
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// 只有待审核状态的订单可以审核
|
||
if ($order->audit_status !== 'pending') {
|
||
return response()->json([
|
||
'code' => 400,
|
||
'message' => '订单状态不可审核'
|
||
], 400);
|
||
}
|
||
|
||
$order->update([
|
||
'audit_status' => $request->action === 'approve' ? 'approved' : 'rejected',
|
||
'audit_comment' => $request->comment,
|
||
'order_status' => $request->action === 'approve' ? 'auditing' : 'pending',
|
||
]);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'message' => $request->action === 'approve' ? '订单审核通过' : '订单已驳回'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 设置仓库和快递
|
||
*/
|
||
public function setWarehouseExpress(Request $request, string $id)
|
||
{
|
||
$request->validate([
|
||
'warehouse_id' => 'nullable|integer|exists:warehouses,id',
|
||
'express_company' => 'nullable|string|max:50',
|
||
'express_name' => 'nullable|string|max:50',
|
||
'express_template_id' => 'nullable|integer|exists:templates,id',
|
||
]);
|
||
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// 只有已审核的订单可以设置仓库和快递
|
||
if ($order->audit_status !== 'approved') {
|
||
return response()->json([
|
||
'code' => 400,
|
||
'message' => '只有已审核的订单可以设置仓库和快递'
|
||
], 400);
|
||
}
|
||
|
||
$order->update([
|
||
'warehouse_id' => $request->warehouse_id ?? $order->warehouse_id,
|
||
'express_company' => $request->express_company ?? $order->express_company,
|
||
'express_name' => $request->express_name ?? $order->express_name,
|
||
]);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'message' => '设置成功'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 发货
|
||
*/
|
||
public function shipOrder(Request $request, string $id)
|
||
{
|
||
$request->validate([
|
||
'express_company' => 'required|string|max:50',
|
||
'express_no' => 'required|string|max:50',
|
||
'is_print' => 'nullable|boolean',
|
||
]);
|
||
|
||
$order = Order::with('items')->find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// 检查订单状态
|
||
if ($order->order_status !== 'auditing') {
|
||
return response()->json([
|
||
'code' => 400,
|
||
'message' => '只有待发货的订单可以发货'
|
||
], 400);
|
||
}
|
||
|
||
// 检查是否设置了仓库
|
||
if (!$order->warehouse_id) {
|
||
return response()->json([
|
||
'code' => 400,
|
||
'message' => '请先设置发货仓库'
|
||
], 400);
|
||
}
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
// 更新订单状态
|
||
$order->update([
|
||
'express_company' => $request->express_company,
|
||
'express_no' => $request->express_no,
|
||
'delivery_status' => 'delivered',
|
||
'delivery_time' => now(),
|
||
'order_status' => 'shipped',
|
||
]);
|
||
|
||
// 扣减库存
|
||
foreach ($order->items as $item) {
|
||
if ($item->erp_sku) {
|
||
$this->deductStock(
|
||
$order->warehouse_id,
|
||
$item->erp_sku,
|
||
$item->quantity,
|
||
$order->short_id
|
||
);
|
||
}
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'message' => '发货成功'
|
||
]);
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
return response()->json([
|
||
'code' => 500,
|
||
'message' => '发货失败: ' . $e->getMessage()
|
||
], 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 扣减库存
|
||
*/
|
||
private function deductStock($warehouseId, $skuCode, $quantity, $relatedNo)
|
||
{
|
||
$stock = Stock::where('warehouse_id', $warehouseId)
|
||
->where('sku_code', $skuCode)
|
||
->first();
|
||
|
||
if (!$stock) {
|
||
throw new \Exception("商品 {$skuCode} 在仓库中不存在");
|
||
}
|
||
|
||
if ($stock->available_quantity < $quantity) {
|
||
throw new \Exception("商品 {$skuCode} 库存不足,可用库存: {$stock->available_quantity}");
|
||
}
|
||
|
||
$stock->decrement('quantity', $quantity);
|
||
|
||
// 记录库存流水
|
||
StockLog::create([
|
||
'sku_code' => $skuCode,
|
||
'warehouse_id' => $warehouseId,
|
||
'change_quantity' => -$quantity,
|
||
'type' => 'order_ship',
|
||
'related_no' => $relatedNo,
|
||
'remark' => '订单发货出库',
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 订单统计
|
||
*/
|
||
public function statistics(Request $request)
|
||
{
|
||
$query = Order::query();
|
||
|
||
// 时间范围筛选
|
||
if ($request->filled('start_date')) {
|
||
$query->where('order_time', '>=', $request->start_date);
|
||
}
|
||
if ($request->filled('end_date')) {
|
||
$query->where('order_time', '<=', $request->end_date);
|
||
}
|
||
|
||
// 平台筛选
|
||
if ($request->filled('platform')) {
|
||
$query->where('platform', $request->platform);
|
||
}
|
||
|
||
// 店铺筛选
|
||
if ($request->filled('shop_id')) {
|
||
$query->where('shop_id', $request->shop_id);
|
||
}
|
||
|
||
// 订单状态统计
|
||
$statusStats = [
|
||
'pending' => (clone $query)->where('order_status', 'pending')->count(),
|
||
'auditing' => (clone $query)->where('order_status', 'auditing')->count(),
|
||
'shipped' => (clone $query)->where('order_status', 'shipped')->count(),
|
||
'completed' => (clone $query)->where('order_status', 'completed')->count(),
|
||
'cancelled' => (clone $query)->where('order_status', 'cancelled')->count(),
|
||
];
|
||
|
||
// 审核状态统计
|
||
$auditStats = [
|
||
'pending' => (clone $query)->where('audit_status', 'pending')->count(),
|
||
'approved' => (clone $query)->where('audit_status', 'approved')->count(),
|
||
'rejected' => (clone $query)->where('audit_status', 'rejected')->count(),
|
||
];
|
||
|
||
// 发货状态统计
|
||
$deliveryStats = [
|
||
'pending' => (clone $query)->where('delivery_status', 'pending')->count(),
|
||
'delivered' => (clone $query)->where('delivery_status', 'delivered')->count(),
|
||
];
|
||
|
||
// 金额统计
|
||
$amountStats = [
|
||
'total_goods_amount' => (clone $query)->sum('goods_amount'),
|
||
'total_discount_amount' => (clone $query)->sum('discount_amount'),
|
||
'total_freight' => (clone $query)->sum('freight'),
|
||
'total_amount' => (clone $query)->sum('total_amount'),
|
||
'avg_order_amount' => (clone $query)->avg('total_amount') ?? 0,
|
||
];
|
||
|
||
// 平台分布统计
|
||
$platformStats = Order::select('platform', DB::raw('count(*) as count'), DB::raw('sum(total_amount) as amount'))
|
||
->when($request->filled('start_date'), function ($q) use ($request) {
|
||
$q->where('order_time', '>=', $request->start_date);
|
||
})
|
||
->when($request->filled('end_date'), function ($q) use ($request) {
|
||
$q->where('order_time', '<=', $request->end_date);
|
||
})
|
||
->groupBy('platform')
|
||
->get()
|
||
->map(function ($item) {
|
||
return [
|
||
'platform' => $item->platform,
|
||
'count' => $item->count,
|
||
'amount' => (float) $item->amount,
|
||
];
|
||
});
|
||
|
||
// 时间趋势统计(按天)
|
||
$dateFormat = $request->input('date_format', 'day'); // day, week, month
|
||
$format = $dateFormat === 'day' ? '%Y-%m-%d' : ($dateFormat === 'week' ? '%Y-%U' : '%Y-%m');
|
||
|
||
$trendStats = Order::select(
|
||
DB::raw("DATE_FORMAT(order_time, '{$format}') as date_group"),
|
||
DB::raw('count(*) as order_count'),
|
||
DB::raw('sum(total_amount) as total_amount')
|
||
)
|
||
->when($request->filled('start_date'), function ($q) use ($request) {
|
||
$q->where('order_time', '>=', $request->start_date);
|
||
})
|
||
->when($request->filled('end_date'), function ($q) use ($request) {
|
||
$q->where('order_time', '<=', $request->end_date);
|
||
})
|
||
->groupBy('date_group')
|
||
->orderBy('date_group')
|
||
->get();
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => [
|
||
'status_stats' => $statusStats,
|
||
'audit_stats' => $auditStats,
|
||
'delivery_stats' => $deliveryStats,
|
||
'amount_stats' => $amountStats,
|
||
'platform_stats' => $platformStats,
|
||
'trend_stats' => $trendStats,
|
||
'total_orders' => array_sum($statusStats),
|
||
'date_range' => [
|
||
'start_date' => $request->start_date,
|
||
'end_date' => $request->end_date,
|
||
],
|
||
],
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 订单导出
|
||
*/
|
||
public function export(Request $request)
|
||
{
|
||
$request->validate([
|
||
'export_type' => 'required|in:excel,csv',
|
||
'order_ids' => 'nullable|array',
|
||
'order_ids.*' => 'integer|exists:orders,id',
|
||
'start_date' => 'nullable|date',
|
||
'end_date' => 'nullable|date',
|
||
'platform' => 'nullable|string',
|
||
'order_status' => 'nullable|string',
|
||
'audit_status' => 'nullable|string',
|
||
'delivery_status' => 'nullable|string',
|
||
]);
|
||
|
||
$query = Order::with(['items', 'warehouse']);
|
||
|
||
// 筛选条件
|
||
if ($request->filled('order_ids')) {
|
||
$query->whereIn('id', $request->order_ids);
|
||
}
|
||
|
||
if ($request->filled('start_date')) {
|
||
$query->where('order_time', '>=', $request->start_date);
|
||
}
|
||
|
||
if ($request->filled('end_date')) {
|
||
$query->where('order_time', '<=', $request->end_date);
|
||
}
|
||
|
||
if ($request->filled('platform')) {
|
||
$query->where('platform', $request->platform);
|
||
}
|
||
|
||
if ($request->filled('order_status')) {
|
||
$query->where('order_status', $request->order_status);
|
||
}
|
||
|
||
if ($request->filled('audit_status')) {
|
||
$query->where('audit_status', $request->audit_status);
|
||
}
|
||
|
||
if ($request->filled('delivery_status')) {
|
||
$query->where('delivery_status', $request->delivery_status);
|
||
}
|
||
|
||
$orders = $query->orderBy('order_time', 'desc')->get();
|
||
|
||
// 准备导出数据
|
||
$exportData = $orders->map(function ($order) {
|
||
return [
|
||
'订单短ID' => $order->short_id,
|
||
'平台订单号' => $order->platform_order_sn,
|
||
'平台' => $order->platform,
|
||
'店铺名称' => $order->shop_name,
|
||
'下单时间' => $order->order_time,
|
||
'买家昵称' => $order->buyer_nick,
|
||
'收货人' => $order->receiver_name,
|
||
'收货电话' => $order->receiver_phone,
|
||
'收货地址' => $order->receiver_address,
|
||
'商品金额' => $order->goods_amount,
|
||
'优惠金额' => $order->discount_amount,
|
||
'运费' => $order->freight,
|
||
'总金额' => $order->total_amount,
|
||
'订单状态' => $this->getStatusText($order->order_status),
|
||
'平台状态' => $order->platform_status,
|
||
'审核状态' => $this->getAuditStatusText($order->audit_status),
|
||
'发货状态' => $this->getDeliveryStatusText($order->delivery_status),
|
||
'发货时间' => $order->delivery_time,
|
||
'快递公司' => $order->express_company,
|
||
'快递单号' => $order->express_no,
|
||
'仓库名称' => $order->warehouse_name,
|
||
'备注' => $order->remark,
|
||
'创建时间' => $order->created_at,
|
||
'更新时间' => $order->updated_at,
|
||
];
|
||
});
|
||
|
||
// 生成文件名
|
||
$filename = 'orders_export_' . date('Ymd_His') . '.' . $request->export_type;
|
||
|
||
// 在实际项目中,这里会生成Excel或CSV文件
|
||
// 暂时返回数据供前端处理
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => [
|
||
'filename' => $filename,
|
||
'count' => $orders->count(),
|
||
'data' => $exportData,
|
||
'headers' => array_keys($exportData->first() ?? []),
|
||
],
|
||
'message' => '导出数据准备完成'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取状态文本
|
||
*/
|
||
private function getStatusText($status): string
|
||
{
|
||
$statusMap = [
|
||
'pending' => '待处理',
|
||
'auditing' => '审核中',
|
||
'shipped' => '已发货',
|
||
'completed' => '已完成',
|
||
'cancelled' => '已取消',
|
||
];
|
||
|
||
return $statusMap[$status] ?? $status;
|
||
}
|
||
|
||
/**
|
||
* 获取审核状态文本
|
||
*/
|
||
private function getAuditStatusText($status): string
|
||
{
|
||
$statusMap = [
|
||
'pending' => '待审核',
|
||
'approved' => '已通过',
|
||
'rejected' => '已驳回',
|
||
];
|
||
|
||
return $statusMap[$status] ?? $status;
|
||
}
|
||
|
||
/**
|
||
* 获取发货状态文本
|
||
*/
|
||
private function getDeliveryStatusText($status): string
|
||
{
|
||
$statusMap = [
|
||
'pending' => '待发货',
|
||
'delivered' => '已发货',
|
||
];
|
||
|
||
return $statusMap[$status] ?? $status;
|
||
}
|
||
|
||
/**
|
||
* 订单操作日志
|
||
*/
|
||
public function getOrderLogs(string $id)
|
||
{
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// 在实际项目中,这里应该查询操作日志表
|
||
// 暂时返回模拟数据
|
||
$logs = $this->generateOrderLogs($order);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $logs,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 生成订单操作日志
|
||
*/
|
||
private function generateOrderLogs(Order $order): array
|
||
{
|
||
$logs = [];
|
||
|
||
// 订单创建日志
|
||
$logs[] = [
|
||
'id' => 1,
|
||
'operator' => '系统',
|
||
'operator_id' => 0,
|
||
'action' => 'create',
|
||
'action_text' => '创建订单',
|
||
'content' => "订单创建成功,订单号:{$order->short_id}",
|
||
'ip' => '127.0.0.1',
|
||
'user_agent' => '系统自动创建',
|
||
'created_at' => $order->created_at,
|
||
];
|
||
|
||
// 如果订单已审核,添加审核日志
|
||
if ($order->audit_status === 'approved') {
|
||
$logs[] = [
|
||
'id' => 2,
|
||
'operator' => '管理员',
|
||
'operator_id' => 1,
|
||
'action' => 'audit_approve',
|
||
'action_text' => '审核通过',
|
||
'content' => '订单审核通过,进入待发货状态',
|
||
'ip' => '192.168.1.100',
|
||
'user_agent' => 'Chrome/120.0.0.0',
|
||
'created_at' => $order->updated_at,
|
||
];
|
||
} elseif ($order->audit_status === 'rejected') {
|
||
$logs[] = [
|
||
'id' => 2,
|
||
'operator' => '管理员',
|
||
'operator_id' => 1,
|
||
'action' => 'audit_reject',
|
||
'action_text' => '审核驳回',
|
||
'content' => "订单审核驳回,原因:{$order->audit_comment}",
|
||
'ip' => '192.168.1.100',
|
||
'user_agent' => 'Chrome/120.0.0.0',
|
||
'created_at' => $order->updated_at,
|
||
];
|
||
}
|
||
|
||
// 如果设置了仓库,添加仓库设置日志
|
||
if ($order->warehouse_id) {
|
||
$logs[] = [
|
||
'id' => 3,
|
||
'operator' => '操作员',
|
||
'operator_id' => 2,
|
||
'action' => 'set_warehouse',
|
||
'action_text' => '设置仓库',
|
||
'content' => "设置发货仓库:{$order->warehouse_name}",
|
||
'ip' => '192.168.1.101',
|
||
'user_agent' => 'Firefox/121.0.0.0',
|
||
'created_at' => $order->updated_at,
|
||
];
|
||
}
|
||
|
||
// 如果已发货,添加发货日志
|
||
if ($order->delivery_status === 'delivered') {
|
||
$logs[] = [
|
||
'id' => 4,
|
||
'operator' => '发货员',
|
||
'operator_id' => 3,
|
||
'action' => 'ship',
|
||
'action_text' => '订单发货',
|
||
'content' => "订单已发货,快递公司:{$order->express_company},快递单号:{$order->express_no}",
|
||
'ip' => '192.168.1.102',
|
||
'user_agent' => 'Safari/17.0.0.0',
|
||
'created_at' => $order->delivery_time,
|
||
];
|
||
}
|
||
|
||
// 如果订单已完成,添加完成日志
|
||
if ($order->order_status === 'completed') {
|
||
$logs[] = [
|
||
'id' => 5,
|
||
'operator' => '系统',
|
||
'operator_id' => 0,
|
||
'action' => 'complete',
|
||
'action_text' => '订单完成',
|
||
'content' => '订单已完成交易',
|
||
'ip' => '127.0.0.1',
|
||
'user_agent' => '系统自动完成',
|
||
'created_at' => $order->end_time,
|
||
];
|
||
}
|
||
|
||
// 如果订单已取消,添加取消日志
|
||
if ($order->order_status === 'cancelled') {
|
||
$logs[] = [
|
||
'id' => 6,
|
||
'operator' => '管理员',
|
||
'operator_id' => 1,
|
||
'action' => 'cancel',
|
||
'action_text' => '订单取消',
|
||
'content' => "订单已取消,原因:{$order->audit_comment}",
|
||
'ip' => '192.168.1.100',
|
||
'user_agent' => 'Chrome/120.0.0.0',
|
||
'created_at' => $order->updated_at,
|
||
];
|
||
}
|
||
|
||
// 按时间倒序排序
|
||
usort($logs, function ($a, $b) {
|
||
return strtotime($b['created_at']) - strtotime($a['created_at']);
|
||
});
|
||
|
||
return $logs;
|
||
}
|
||
|
||
/**
|
||
* 记录订单操作日志
|
||
*/
|
||
private function logOrderOperation(Order $order, string $action, string $content, array $data = []): void
|
||
{
|
||
// 在实际项目中,这里应该将日志保存到数据库
|
||
// 暂时只记录到文件或控制台
|
||
$logData = [
|
||
'order_id' => $order->id,
|
||
'short_id' => $order->short_id,
|
||
'action' => $action,
|
||
'content' => $content,
|
||
'operator' => auth()->user() ? auth()->user()->name : '系统',
|
||
'operator_id' => auth()->id() ?? 0,
|
||
'data' => $data,
|
||
'created_at' => now()->toDateTimeString(),
|
||
];
|
||
|
||
// 记录到日志文件
|
||
$logPath = storage_path('logs/order_operations.log');
|
||
file_put_contents($logPath, json_encode($logData, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND);
|
||
}
|
||
|
||
/**
|
||
* 批量操作订单
|
||
*/
|
||
public function batchOperation(Request $request)
|
||
{
|
||
$request->validate([
|
||
'order_ids' => 'required|array|min:1',
|
||
'order_ids.*' => 'integer|exists:orders,id',
|
||
'operation' => 'required|in:audit_approve,audit_reject,set_warehouse,ship,cancel,delete',
|
||
'data' => 'nullable|array',
|
||
]);
|
||
|
||
$successCount = 0;
|
||
$failedOrders = [];
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
foreach ($request->order_ids as $orderId) {
|
||
$order = Order::find($orderId);
|
||
|
||
if (!$order) {
|
||
$failedOrders[] = ['id' => $orderId, 'reason' => '订单不存在'];
|
||
continue;
|
||
}
|
||
|
||
$result = $this->processBatchOperation($order, $request->operation, $request->data ?? []);
|
||
|
||
if ($result['success']) {
|
||
$successCount++;
|
||
|
||
// 记录操作日志
|
||
$this->logOrderOperation(
|
||
$order,
|
||
$request->operation,
|
||
$result['log_content'] ?? '批量操作',
|
||
$request->data ?? []
|
||
);
|
||
} else {
|
||
$failedOrders[] = ['id' => $orderId, 'reason' => $result['reason']];
|
||
}
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => [
|
||
'success_count' => $successCount,
|
||
'failed_orders' => $failedOrders,
|
||
],
|
||
'message' => "成功处理 {$successCount} 个订单"
|
||
]);
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
return response()->json([
|
||
'code' => 500,
|
||
'message' => '批量操作失败: ' . $e->getMessage()
|
||
], 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理批量操作
|
||
*/
|
||
private function processBatchOperation(Order $order, string $operation, array $data): array
|
||
{
|
||
$logContent = '';
|
||
|
||
switch ($operation) {
|
||
case 'audit_approve':
|
||
if ($order->audit_status !== 'pending') {
|
||
return ['success' => false, 'reason' => '订单状态不可审核'];
|
||
}
|
||
$order->update([
|
||
'audit_status' => 'approved',
|
||
'order_status' => 'auditing',
|
||
]);
|
||
$logContent = '批量审核通过';
|
||
break;
|
||
|
||
case 'audit_reject':
|
||
if ($order->audit_status !== 'pending') {
|
||
return ['success' => false, 'reason' => '订单状态不可审核'];
|
||
}
|
||
$order->update([
|
||
'audit_status' => 'rejected',
|
||
'audit_comment' => $data['comment'] ?? '',
|
||
]);
|
||
$logContent = '批量审核驳回,原因:' . ($data['comment'] ?? '无');
|
||
break;
|
||
|
||
case 'set_warehouse':
|
||
if ($order->audit_status !== 'approved') {
|
||
return ['success' => false, 'reason' => '只有已审核的订单可以设置仓库'];
|
||
}
|
||
$order->update([
|
||
'warehouse_id' => $data['warehouse_id'] ?? $order->warehouse_id,
|
||
'warehouse_name' => $data['warehouse_name'] ?? $order->warehouse_name,
|
||
]);
|
||
$logContent = '批量设置仓库:' . ($data['warehouse_name'] ?? '未知仓库');
|
||
break;
|
||
|
||
case 'ship':
|
||
if ($order->order_status !== 'auditing') {
|
||
return ['success' => false, 'reason' => '只有待发货的订单可以发货'];
|
||
}
|
||
if (!$order->warehouse_id) {
|
||
return ['success' => false, 'reason' => '请先设置发货仓库'];
|
||
}
|
||
$order->update([
|
||
'express_company' => $data['express_company'] ?? '',
|
||
'express_no' => $data['express_no'] ?? '',
|
||
'delivery_status' => 'delivered',
|
||
'delivery_time' => now(),
|
||
'order_status' => 'shipped',
|
||
]);
|
||
$logContent = '批量发货,快递:' . ($data['express_company'] ?? '未知') . ',单号:' . ($data['express_no'] ?? '无');
|
||
break;
|
||
|
||
case 'cancel':
|
||
if (!in_array($order->order_status, ['pending', 'auditing'])) {
|
||
return ['success' => false, 'reason' => '订单状态不可取消'];
|
||
}
|
||
$order->update([
|
||
'order_status' => 'cancelled',
|
||
'audit_status' => 'rejected',
|
||
'audit_comment' => '订单已取消',
|
||
]);
|
||
$logContent = '批量取消订单';
|
||
break;
|
||
|
||
case 'delete':
|
||
if (!in_array($order->order_status, ['cancelled', 'completed'])) {
|
||
return ['success' => false, 'reason' => '只有已取消或已完成的订单可以删除'];
|
||
}
|
||
$order->delete();
|
||
$logContent = '批量删除订单';
|
||
break;
|
||
|
||
default:
|
||
return ['success' => false, 'reason' => '不支持的操作类型'];
|
||
}
|
||
|
||
return ['success' => true, 'log_content' => $logContent];
|
||
}
|
||
|
||
/**
|
||
* 获取订单状态选项
|
||
*/
|
||
public function getStatusOptions()
|
||
{
|
||
$options = [
|
||
['value' => 'pending', 'label' => '待处理'],
|
||
['value' => 'auditing', 'label' => '审核中'],
|
||
['value' => 'shipped', 'label' => '已发货'],
|
||
['value' => 'completed', 'label' => '已完成'],
|
||
['value' => 'cancelled', 'label' => '已取消'],
|
||
];
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $options,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取审核状态选项
|
||
*/
|
||
public function getAuditStatusOptions()
|
||
{
|
||
$options = [
|
||
['value' => 'pending', 'label' => '待审核'],
|
||
['value' => 'approved', 'label' => '已通过'],
|
||
['value' => 'rejected', 'label' => '已驳回'],
|
||
];
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $options,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取发货状态选项
|
||
*/
|
||
public function getDeliveryStatusOptions()
|
||
{
|
||
$options = [
|
||
['value' => 'pending', 'label' => '待发货'],
|
||
['value' => 'delivered', 'label' => '已发货'],
|
||
];
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $options,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取平台选项
|
||
*/
|
||
public function getPlatformOptions()
|
||
{
|
||
$platforms = Order::select('platform')
|
||
->distinct()
|
||
->orderBy('platform')
|
||
->get()
|
||
->map(function ($item) {
|
||
return [
|
||
'value' => $item->platform,
|
||
'label' => $item->platform,
|
||
];
|
||
});
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $platforms,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取店铺选项
|
||
*/
|
||
public function getShopOptions()
|
||
{
|
||
$shops = Order::select('shop_id', 'shop_name')
|
||
->distinct()
|
||
->orderBy('shop_name')
|
||
->get()
|
||
->map(function ($item) {
|
||
return [
|
||
'value' => $item->shop_id,
|
||
'label' => $item->shop_name,
|
||
];
|
||
});
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $shops,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 更新订单备注
|
||
*/
|
||
public function updateRemark(Request $request, string $id)
|
||
{
|
||
$request->validate([
|
||
'remark' => 'required|string|max:1000',
|
||
]);
|
||
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
$oldRemark = $order->remark;
|
||
$order->update(['remark' => $request->remark]);
|
||
|
||
// 记录操作日志
|
||
$this->logOrderOperation(
|
||
$order,
|
||
'update_remark',
|
||
"更新订单备注,原备注:{$oldRemark},新备注:{$request->remark}"
|
||
);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'message' => '备注更新成功'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 完成订单
|
||
*/
|
||
public function completeOrder(string $id)
|
||
{
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// 只有已发货的订单可以完成
|
||
if ($order->order_status !== 'shipped') {
|
||
return response()->json([
|
||
'code' => 400,
|
||
'message' => '只有已发货的订单可以完成'
|
||
], 400);
|
||
}
|
||
|
||
$order->update([
|
||
'order_status' => 'completed',
|
||
'end_time' => now(),
|
||
]);
|
||
|
||
// 记录操作日志
|
||
$this->logOrderOperation(
|
||
$order,
|
||
'complete',
|
||
'订单已完成交易'
|
||
);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'message' => '订单已完成'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 同步订单到平台
|
||
*/
|
||
public function syncToPlatform(string $id)
|
||
{
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// TODO: 实现同步到平台API
|
||
// 这里应该调用对应平台的API更新订单状态
|
||
|
||
// 记录操作日志
|
||
$this->logOrderOperation(
|
||
$order,
|
||
'sync_to_platform',
|
||
"同步订单到平台:{$order->platform},平台订单号:{$order->platform_order_sn}"
|
||
);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'message' => '订单已同步到平台(模拟)',
|
||
'data' => [
|
||
'order_id' => $id,
|
||
'platform' => $order->platform,
|
||
'platform_order_sn' => $order->platform_order_sn,
|
||
]
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取订单操作历史
|
||
*/
|
||
public function getOperationHistory(string $id)
|
||
{
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
// 在实际项目中,这里应该查询操作日志表
|
||
// 暂时返回模拟数据
|
||
$history = $this->generateOrderLogs($order);
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $history,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取订单数量统计(用于仪表板)
|
||
*/
|
||
public function getDashboardStats()
|
||
{
|
||
$today = now()->startOfDay();
|
||
$yesterday = now()->subDay()->startOfDay();
|
||
$thisMonth = now()->startOfMonth();
|
||
|
||
$stats = [
|
||
'today' => [
|
||
'total' => Order::where('order_time', '>=', $today)->count(),
|
||
'pending' => Order::where('order_time', '>=', $today)->where('order_status', 'pending')->count(),
|
||
'auditing' => Order::where('order_time', '>=', $today)->where('order_status', 'auditing')->count(),
|
||
'shipped' => Order::where('order_time', '>=', $today)->where('order_status', 'shipped')->count(),
|
||
'amount' => Order::where('order_time', '>=', $today)->sum('total_amount'),
|
||
],
|
||
'yesterday' => [
|
||
'total' => Order::whereBetween('order_time', [$yesterday, $today])->count(),
|
||
'amount' => Order::whereBetween('order_time', [$yesterday, $today])->sum('total_amount'),
|
||
],
|
||
'this_month' => [
|
||
'total' => Order::where('order_time', '>=', $thisMonth)->count(),
|
||
'amount' => Order::where('order_time', '>=', $thisMonth)->sum('total_amount'),
|
||
],
|
||
'all' => [
|
||
'total' => Order::count(),
|
||
'pending' => Order::where('order_status', 'pending')->count(),
|
||
'auditing' => Order::where('order_status', 'auditing')->count(),
|
||
'shipped' => Order::where('order_status', 'shipped')->count(),
|
||
'completed' => Order::where('order_status', 'completed')->count(),
|
||
'cancelled' => Order::where('order_status', 'cancelled')->count(),
|
||
'amount' => Order::sum('total_amount'),
|
||
],
|
||
];
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $stats,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取订单操作类型选项
|
||
*/
|
||
public function getOperationTypeOptions()
|
||
{
|
||
$options = [
|
||
['value' => 'create', 'label' => '创建'],
|
||
['value' => 'audit_approve', 'label' => '审核通过'],
|
||
['value' => 'audit_reject', 'label' => '审核驳回'],
|
||
['value' => 'set_warehouse', 'label' => '设置仓库'],
|
||
['value' => 'ship', 'label' => '发货'],
|
||
['value' => 'complete', 'label' => '完成'],
|
||
['value' => 'cancel', 'label' => '取消'],
|
||
['value' => 'update_remark', 'label' => '更新备注'],
|
||
['value' => 'sync_to_platform', 'label' => '同步到平台'],
|
||
['value' => 'delete', 'label' => '删除'],
|
||
];
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => $options,
|
||
'message' => 'success'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 导出订单操作日志
|
||
*/
|
||
public function exportOperationLogs(Request $request, string $id)
|
||
{
|
||
$order = Order::find($id);
|
||
|
||
if (!$order) {
|
||
return response()->json([
|
||
'code' => 404,
|
||
'message' => '订单不存在'
|
||
], 404);
|
||
}
|
||
|
||
$request->validate([
|
||
'export_type' => 'required|in:excel,csv',
|
||
]);
|
||
|
||
// 获取操作日志
|
||
$logs = $this->generateOrderLogs($order);
|
||
|
||
// 准备导出数据
|
||
$exportData = array_map(function ($log) {
|
||
return [
|
||
'操作时间' => $log['created_at'],
|
||
'操作人' => $log['operator'],
|
||
'操作类型' => $log['action_text'],
|
||
'操作内容' => $log['content'],
|
||
'IP地址' => $log['ip'],
|
||
'用户代理' => $log['user_agent'],
|
||
];
|
||
}, $logs);
|
||
|
||
// 生成文件名
|
||
$filename = "order_{$order->short_id}_logs_" . date('Ymd_His') . '.' . $request->export_type;
|
||
|
||
return response()->json([
|
||
'code' => 200,
|
||
'data' => [
|
||
'filename' => $filename,
|
||
'count' => count($logs),
|
||
'data' => $exportData,
|
||
'headers' => array_keys($exportData[0] ?? []),
|
||
'order_info' => [
|
||
'short_id' => $order->short_id,
|
||
'platform_order_sn' => $order->platform_order_sn,
|
||
'platform' => $order->platform,
|
||
'shop_name' => $order->shop_name,
|
||
],
|
||
],
|
||
'message' => '操作日志导出数据准备完成'
|
||
]);
|
||
}
|
||
} |