merchantId($request); $page = max((int) $request->integer('page', 1), 1); $filters = $this->filters($request); $statusStatsFilters = $filters; $statusStatsFilters['status'] = ''; if ($filters['has_validation_error'] ?? false) { return view('merchant_admin.orders.index', [ 'orders' => Order::query()->whereRaw('1 = 0')->paginate(10)->withQueryString(), 'statusStats' => $this->emptyStatusStats(), 'summaryStats' => $this->emptySummaryStats(), 'trendStats' => $this->emptyTrendStats(), 'operationsFocus' => $this->buildOperationsFocus($merchantId, $this->emptySummaryStats(), $filters), 'workbenchLinks' => $this->workbenchLinks(), 'filters' => $filters, 'filterOptions' => [ 'statuses' => $this->statuses, 'paymentStatuses' => ['unpaid', 'paid', 'refunded', 'failed'], 'platforms' => ['pc', 'h5', 'wechat_mp', 'wechat_mini', 'app'], 'deviceTypes' => ['desktop', 'mobile', 'mini-program', 'mobile-webview', 'app-api'], 'paymentChannels' => ['wechat_pay', 'alipay'], 'timeRanges' => [ 'all' => '全部时间', 'today' => '今天', 'last_7_days' => '近7天', ], 'sortOptions' => [ 'latest' => '创建时间倒序', 'oldest' => '创建时间正序', 'pay_amount_desc' => '实付金额从高到低', 'pay_amount_asc' => '实付金额从低到高', 'product_amount_desc' => '商品金额从高到低', 'product_amount_asc' => '商品金额从低到高', ], ], 'cacheMeta' => [ 'store' => config('cache.default'), 'ttl' => '10m', ], 'activeFilterSummary' => $this->buildActiveFilterSummary($filters), 'statusLabels' => $this->statusLabels(), 'paymentStatusLabels' => $this->paymentStatusLabels(), 'platformLabels' => $this->platformLabels(), 'deviceTypeLabels' => $this->deviceTypeLabels(), 'paymentChannelLabels' => $this->paymentChannelLabels(), ]); } $summaryStats = Cache::remember( CacheKeys::merchantOrdersSummary($merchantId, $statusStatsFilters), now()->addMinutes(10), fn () => $this->buildSummaryStats($this->applyFilters(Order::query()->forMerchant($merchantId), $statusStatsFilters)) ); return view('merchant_admin.orders.index', [ 'orders' => Cache::remember( CacheKeys::merchantOrdersList($merchantId, $page, $filters), now()->addMinutes(10), fn () => $this->applySorting($this->applyFilters(Order::query()->forMerchant($merchantId), $filters), $filters) ->paginate(10) ->withQueryString() ), 'statusStats' => Cache::remember( CacheKeys::merchantOrdersStatusStats($merchantId, $statusStatsFilters), now()->addMinutes(10), fn () => $this->buildStatusStats($this->applyFilters(Order::query()->forMerchant($merchantId), $statusStatsFilters)) ), 'summaryStats' => $summaryStats, 'operationsFocus' => $this->buildOperationsFocus($merchantId, $summaryStats, $filters), 'workbenchLinks' => $this->workbenchLinks(), 'trendStats' => Cache::remember( CacheKeys::merchantOrdersTrendSummary($merchantId, $statusStatsFilters), now()->addMinutes(10), fn () => $this->buildTrendStats($this->applyFilters(Order::query()->forMerchant($merchantId), $statusStatsFilters)) ), 'filters' => $filters, 'filterOptions' => [ 'statuses' => $this->statuses, 'paymentStatuses' => ['unpaid', 'paid', 'refunded', 'failed'], 'platforms' => ['pc', 'h5', 'wechat_mp', 'wechat_mini', 'app'], 'deviceTypes' => ['desktop', 'mobile', 'mini-program', 'mobile-webview', 'app-api'], 'paymentChannels' => ['wechat_pay', 'alipay'], 'timeRanges' => [ 'all' => '全部时间', 'today' => '今天', 'last_7_days' => '近7天', ], 'sortOptions' => [ 'latest' => '创建时间倒序', 'oldest' => '创建时间正序', 'pay_amount_desc' => '实付金额从高到低', 'pay_amount_asc' => '实付金额从低到高', 'product_amount_desc' => '商品金额从高到低', 'product_amount_asc' => '商品金额从低到高', ], ], 'cacheMeta' => [ 'store' => config('cache.default'), 'ttl' => '10m', ], 'activeFilterSummary' => $this->buildActiveFilterSummary($filters), 'statusLabels' => $this->statusLabels(), 'paymentStatusLabels' => $this->paymentStatusLabels(), 'platformLabels' => $this->platformLabels(), 'deviceTypeLabels' => $this->deviceTypeLabels(), 'paymentChannelLabels' => $this->paymentChannelLabels(), ]); } public function show(Request $request, int $id): View { $merchantId = $this->merchantId($request); return view('merchant_admin.orders.show', [ 'order' => Order::query()->with(['items.product', 'user'])->forMerchant($merchantId)->findOrFail($id), ]); } public function export(Request $request): StreamedResponse|RedirectResponse { $merchantId = $this->merchantId($request); $filters = $this->filters($request); if ($filters['has_validation_error'] ?? false) { return redirect('/merchant-admin/orders?' . http_build_query($this->exportableFilters($filters))) ->withErrors($filters['validation_errors'] ?? ['订单筛选条件不合法,请先修正后再导出。']); } $fileName = 'merchant_' . $merchantId . '_orders_' . now()->format('Ymd_His') . '.csv'; $exportSummary = $this->buildSummaryStats( $this->applyFilters(Order::query()->forMerchant($merchantId), $filters) ); return response()->streamDownload(function () use ($merchantId, $filters, $exportSummary) { $handle = fopen('php://output', 'w'); fwrite($handle, "\xEF\xBB\xBF"); foreach ($this->exportSummaryRows($filters, 'merchant', $merchantId) as $summaryRow) { fputcsv($handle, $summaryRow); } fputcsv($handle, ['导出订单数', $exportSummary['total_orders'] ?? 0]); fputcsv($handle, ['导出实付总额', number_format((float) ($exportSummary['total_pay_amount'] ?? 0), 2, '.', '')]); fputcsv($handle, ['导出平均客单价', number_format((float) ($exportSummary['average_order_amount'] ?? 0), 2, '.', '')]); fputcsv($handle, ['导出已支付订单数', $exportSummary['paid_orders'] ?? 0]); fputcsv($handle, ['导出支付失败订单', $exportSummary['failed_payment_orders'] ?? 0]); fputcsv($handle, []); fputcsv($handle, [ 'ID', '用户ID', '订单号', '订单状态', '支付状态', '平台', '设备类型', '支付渠道', '买家姓名', '买家手机', '买家邮箱', '商品金额', '优惠金额', '运费', '实付金额', '商品行数', '商品件数', '商品摘要', '创建时间', '支付时间', '发货时间', '完成时间', '备注', ]); foreach ($this->applySorting($this->applyFilters(Order::query()->with('items')->forMerchant($merchantId), $filters), $filters)->cursor() as $order) { $itemCount = $order->items->count(); $totalQuantity = (int) $order->items->sum('quantity'); $itemSummary = $order->items ->map(fn ($item) => trim(($item->product_title ?? '商品') . ' x' . ((int) $item->quantity))) ->implode(' | '); fputcsv($handle, [ $order->id, $order->user_id, $order->order_no, $this->statusLabel((string) $order->status), $this->paymentStatusLabel((string) $order->payment_status), $this->platformLabel((string) $order->platform), $this->deviceTypeLabel((string) $order->device_type), $this->paymentChannelLabel((string) $order->payment_channel), $order->buyer_name, $order->buyer_phone, $order->buyer_email, number_format((float) $order->product_amount, 2, '.', ''), number_format((float) $order->discount_amount, 2, '.', ''), number_format((float) $order->shipping_amount, 2, '.', ''), number_format((float) $order->pay_amount, 2, '.', ''), $itemCount, $totalQuantity, $itemSummary, optional($order->created_at)?->format('Y-m-d H:i:s'), optional($order->paid_at)?->format('Y-m-d H:i:s'), optional($order->shipped_at)?->format('Y-m-d H:i:s'), optional($order->completed_at)?->format('Y-m-d H:i:s'), $order->remark, ]); } fclose($handle); }, $fileName, [ 'Content-Type' => 'text/csv; charset=UTF-8', ]); } public function updateStatus(Request $request, int $id): RedirectResponse { $merchantId = $this->merchantId($request); $data = $request->validate([ 'status' => ['required', 'string'], ]); $order = Order::query()->forMerchant($merchantId)->findOrFail($id); $order->update(['status' => $data['status']]); Cache::add(CacheKeys::merchantOrdersVersion($merchantId), 1, now()->addDays(30)); Cache::increment(CacheKeys::merchantOrdersVersion($merchantId)); Cache::forget(CacheKeys::merchantDashboardStats($merchantId)); return redirect('/merchant-admin/orders')->with('success', '订单状态更新成功'); } protected function filters(Request $request): array { $timeRange = trim((string) $request->string('time_range', 'all')); $rawStartDate = trim((string) $request->string('start_date')); $rawEndDate = trim((string) $request->string('end_date')); $minPayAmount = trim((string) $request->string('min_pay_amount')); $maxPayAmount = trim((string) $request->string('max_pay_amount')); $validationErrors = []; if ($timeRange === 'today') { $startDate = now()->toDateString(); $endDate = now()->toDateString(); } elseif ($timeRange === 'last_7_days') { $startDate = now()->subDays(6)->toDateString(); $endDate = now()->toDateString(); } else { $timeRange = 'all'; $startDate = $rawStartDate; $endDate = $rawEndDate; } if ($rawStartDate !== '' && ! $this->isValidDate($rawStartDate)) { $validationErrors[] = '开始日期格式不正确,请使用 YYYY-MM-DD。'; } if ($rawEndDate !== '' && ! $this->isValidDate($rawEndDate)) { $validationErrors[] = '结束日期格式不正确,请使用 YYYY-MM-DD。'; } if ($rawStartDate !== '' && $rawEndDate !== '' && $this->isValidDate($rawStartDate) && $this->isValidDate($rawEndDate) && $rawStartDate > $rawEndDate) { $validationErrors[] = '开始日期不能晚于结束日期。'; } if ($minPayAmount !== '' && ! is_numeric($minPayAmount)) { $validationErrors[] = '最低实付金额必须为数字。'; } if ($maxPayAmount !== '' && ! is_numeric($maxPayAmount)) { $validationErrors[] = '最高实付金额必须为数字。'; } if ($minPayAmount !== '' && $maxPayAmount !== '' && is_numeric($minPayAmount) && is_numeric($maxPayAmount) && (float) $minPayAmount > (float) $maxPayAmount) { $validationErrors[] = '最低实付金额不能大于最高实付金额。'; } return [ 'status' => trim((string) $request->string('status')), 'payment_status' => trim((string) $request->string('payment_status')), 'platform' => trim((string) $request->string('platform')), 'device_type' => trim((string) $request->string('device_type')), 'payment_channel' => trim((string) $request->string('payment_channel')), 'keyword' => trim((string) $request->string('keyword')), 'start_date' => $startDate, 'end_date' => $endDate, 'min_pay_amount' => $minPayAmount, 'max_pay_amount' => $maxPayAmount, 'time_range' => $timeRange, 'sort' => trim((string) $request->string('sort', 'latest')), 'validation_errors' => $validationErrors, 'has_validation_error' => ! empty($validationErrors), ]; } protected function applyFilters(Builder $query, array $filters): Builder { return $query ->when(($filters['status'] ?? '') !== '', fn ($builder) => $builder->where('status', $filters['status'])) ->when(($filters['payment_status'] ?? '') !== '', fn ($builder) => $builder->where('payment_status', $filters['payment_status'])) ->when(($filters['platform'] ?? '') !== '', fn ($builder) => $builder->where('platform', $filters['platform'])) ->when(($filters['device_type'] ?? '') !== '', fn ($builder) => $builder->where('device_type', $filters['device_type'])) ->when(($filters['payment_channel'] ?? '') !== '', fn ($builder) => $builder->where('payment_channel', $filters['payment_channel'])) ->when(($filters['keyword'] ?? '') !== '', fn ($builder) => $builder->where(function ($subQuery) use ($filters) { $subQuery->where('order_no', 'like', '%' . $filters['keyword'] . '%') ->orWhere('buyer_name', 'like', '%' . $filters['keyword'] . '%') ->orWhere('buyer_phone', 'like', '%' . $filters['keyword'] . '%') ->orWhere('buyer_email', 'like', '%' . $filters['keyword'] . '%'); })) ->when(($filters['start_date'] ?? '') !== '', fn ($builder) => $builder->whereDate('created_at', '>=', $filters['start_date'])) ->when(($filters['end_date'] ?? '') !== '', fn ($builder) => $builder->whereDate('created_at', '<=', $filters['end_date'])) ->when(($filters['min_pay_amount'] ?? '') !== '' && is_numeric($filters['min_pay_amount']), fn ($builder) => $builder->where('pay_amount', '>=', $filters['min_pay_amount'])) ->when(($filters['max_pay_amount'] ?? '') !== '' && is_numeric($filters['max_pay_amount']), fn ($builder) => $builder->where('pay_amount', '<=', $filters['max_pay_amount'])); } protected function buildStatusStats(Builder $query): array { $counts = (clone $query) ->selectRaw('status, COUNT(*) as aggregate') ->groupBy('status') ->pluck('aggregate', 'status'); $stats = ['all' => (int) $counts->sum()]; foreach ($this->statuses as $status) { $stats[$status] = (int) ($counts[$status] ?? 0); } return $stats; } protected function applySorting(Builder $query, array $filters): Builder { return match ($filters['sort'] ?? 'latest') { 'oldest' => $query->orderBy('created_at')->orderBy('id'), 'pay_amount_desc' => $query->orderByDesc('pay_amount')->orderByDesc('id'), 'pay_amount_asc' => $query->orderBy('pay_amount')->orderByDesc('id'), 'product_amount_desc' => $query->orderByDesc('product_amount')->orderByDesc('id'), 'product_amount_asc' => $query->orderBy('product_amount')->orderByDesc('id'), default => $query->latest(), }; } protected function buildSummaryStats(Builder $query): array { $summary = (clone $query) ->selectRaw('COUNT(*) as total_orders') ->selectRaw('COALESCE(SUM(pay_amount), 0) as total_pay_amount') ->selectRaw("SUM(CASE WHEN payment_status = 'unpaid' THEN pay_amount ELSE 0 END) as unpaid_pay_amount") ->selectRaw("SUM(CASE WHEN payment_status = 'paid' THEN pay_amount ELSE 0 END) as paid_pay_amount") ->selectRaw("SUM(CASE WHEN payment_status = 'paid' THEN 1 ELSE 0 END) as paid_orders") ->selectRaw("SUM(CASE WHEN payment_status = 'refunded' THEN 1 ELSE 0 END) as refunded_orders") ->selectRaw("SUM(CASE WHEN payment_status = 'failed' THEN 1 ELSE 0 END) as failed_payment_orders") ->selectRaw("SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) as pending_shipment_orders") ->selectRaw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_orders") ->selectRaw("SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_orders") ->first(); $totalOrders = (int) ($summary->total_orders ?? 0); $totalPayAmount = (float) ($summary->total_pay_amount ?? 0); $paidOrders = (int) ($summary->paid_orders ?? 0); $refundedOrders = (int) ($summary->refunded_orders ?? 0); $completedOrders = (int) ($summary->completed_orders ?? 0); $cancelledOrders = (int) ($summary->cancelled_orders ?? 0); return [ 'total_orders' => $totalOrders, 'total_pay_amount' => $totalPayAmount, 'unpaid_pay_amount' => (float) ($summary->unpaid_pay_amount ?? 0), 'paid_pay_amount' => (float) ($summary->paid_pay_amount ?? 0), 'paid_orders' => $paidOrders, 'refunded_orders' => $refundedOrders, 'failed_payment_orders' => (int) ($summary->failed_payment_orders ?? 0), 'pending_shipment_orders' => (int) ($summary->pending_shipment_orders ?? 0), 'completed_orders' => $completedOrders, 'cancelled_orders' => $cancelledOrders, 'average_order_amount' => $totalOrders > 0 ? round($totalPayAmount / $totalOrders, 2) : 0, 'payment_rate' => $totalOrders > 0 ? round(($paidOrders / $totalOrders) * 100, 2) : 0, 'refund_rate' => $paidOrders > 0 ? round(($refundedOrders / $paidOrders) * 100, 2) : 0, 'completion_rate' => $totalOrders > 0 ? round(($completedOrders / $totalOrders) * 100, 2) : 0, 'cancellation_rate' => $totalOrders > 0 ? round(($cancelledOrders / $totalOrders) * 100, 2) : 0, ]; } protected function buildTrendStats(Builder $query): array { $todayStart = Carbon::today(); $tomorrowStart = (clone $todayStart)->copy()->addDay(); $last7DaysStart = (clone $todayStart)->copy()->subDays(6)->startOfDay(); $today = (clone $query) ->where('created_at', '>=', $todayStart) ->where('created_at', '<', $tomorrowStart) ->selectRaw('COUNT(*) as total_orders') ->selectRaw('COALESCE(SUM(CASE WHEN payment_status = \'paid\' THEN pay_amount ELSE 0 END), 0) as total_pay_amount') ->first(); $last7Days = (clone $query) ->where('created_at', '>=', $last7DaysStart) ->where('created_at', '<', $tomorrowStart) ->selectRaw('COUNT(*) as total_orders') ->selectRaw('COALESCE(SUM(CASE WHEN payment_status = \'paid\' THEN pay_amount ELSE 0 END), 0) as total_pay_amount') ->first(); return [ 'today_orders' => (int) ($today->total_orders ?? 0), 'today_pay_amount' => (float) ($today->total_pay_amount ?? 0), 'last_7_days_orders' => (int) ($last7Days->total_orders ?? 0), 'last_7_days_pay_amount' => (float) ($last7Days->total_pay_amount ?? 0), ]; } protected function emptyStatusStats(): array { $stats = ['all' => 0]; foreach ($this->statuses as $status) { $stats[$status] = 0; } return $stats; } protected function emptySummaryStats(): array { return [ 'total_orders' => 0, 'total_pay_amount' => 0, 'unpaid_pay_amount' => 0, 'paid_pay_amount' => 0, 'paid_orders' => 0, 'refunded_orders' => 0, 'failed_payment_orders' => 0, 'pending_shipment_orders' => 0, 'completed_orders' => 0, 'cancelled_orders' => 0, 'average_order_amount' => 0, 'payment_rate' => 0, 'refund_rate' => 0, 'completion_rate' => 0, 'cancellation_rate' => 0, ]; } protected function emptyTrendStats(): array { return [ 'today_orders' => 0, 'today_pay_amount' => 0, 'last_7_days_orders' => 0, 'last_7_days_pay_amount' => 0, ]; } protected function isValidDate(string $value): bool { try { $date = Carbon::createFromFormat('Y-m-d', $value); } catch (\Throwable $exception) { return false; } return $date && $date->format('Y-m-d') === $value; } protected function exportableFilters(array $filters): array { return array_filter([ 'status' => $filters['status'] ?? '', 'payment_status' => $filters['payment_status'] ?? '', 'platform' => $filters['platform'] ?? '', 'device_type' => $filters['device_type'] ?? '', 'payment_channel' => $filters['payment_channel'] ?? '', 'keyword' => $filters['keyword'] ?? '', 'start_date' => $filters['start_date'] ?? '', 'end_date' => $filters['end_date'] ?? '', 'min_pay_amount' => $filters['min_pay_amount'] ?? '', 'max_pay_amount' => $filters['max_pay_amount'] ?? '', 'time_range' => $filters['time_range'] ?? '', 'sort' => $filters['sort'] ?? '', ], fn ($value) => $value !== null && $value !== '' && $value !== 'all' && $value !== 'latest'); } protected function exportSummaryRows(array $filters, string $scope, ?int $merchantId = null): array { return [ ['导出信息', $scope === 'platform' ? '平台订单导出' : '商家订单导出'], ['导出时间', now()->format('Y-m-d H:i:s')], ['商家ID', $merchantId ? (string) $merchantId : '全部商家'], ['订单状态', $this->statusLabel($filters['status'] ?? '')], ['支付状态', $this->paymentStatusLabel($filters['payment_status'] ?? '')], ['平台', $this->platformLabel($filters['platform'] ?? '')], ['设备类型', $this->displayFilterValue((string) ($filters['device_type'] ?? ''), $this->deviceTypeLabels())], ['支付渠道', $this->displayFilterValue((string) ($filters['payment_channel'] ?? ''), $this->paymentChannelLabels())], ['关键词', $this->displayTextValue($filters['keyword'] ?? '')], ['快捷时间范围', $this->displayFilterValue($filters['time_range'] ?? 'all', [ 'all' => '全部时间', 'today' => '今天', 'last_7_days' => '近7天', ])], ['开始日期', $this->displayTextValue($filters['start_date'] ?? '')], ['结束日期', $this->displayTextValue($filters['end_date'] ?? '')], ['最低实付金额', $this->displayMoneyValue($filters['min_pay_amount'] ?? '')], ['最高实付金额', $this->displayMoneyValue($filters['max_pay_amount'] ?? '')], ['排序', $this->sortLabel($filters['sort'] ?? 'latest')], ]; } protected function buildActiveFilterSummary(array $filters): array { return [ '关键词' => $this->displayTextValue($filters['keyword'] ?? '', '全部'), '订单状态' => $this->statusLabel($filters['status'] ?? ''), '支付状态' => $this->paymentStatusLabel($filters['payment_status'] ?? ''), '平台' => $this->platformLabel($filters['platform'] ?? ''), '设备类型' => $this->displayFilterValue((string) ($filters['device_type'] ?? ''), $this->deviceTypeLabels()), '支付渠道' => $this->displayFilterValue((string) ($filters['payment_channel'] ?? ''), $this->paymentChannelLabels()), '实付金额区间' => $this->formatMoneyRange($filters['min_pay_amount'] ?? '', $filters['max_pay_amount'] ?? ''), '排序' => $this->sortLabel($filters['sort'] ?? 'latest'), ]; } protected function statusLabels(): array { return [ 'pending' => '待处理', 'paid' => '已支付', 'shipped' => '已发货', 'completed' => '已完成', 'cancelled' => '已取消', ]; } protected function statusLabel(string $status): string { return $this->statusLabels()[$status] ?? '全部'; } protected function paymentStatusLabels(): array { return [ 'unpaid' => '未支付', 'paid' => '已支付', 'refunded' => '已退款', 'failed' => '支付失败', ]; } protected function paymentStatusLabel(string $status): string { return $this->paymentStatusLabels()[$status] ?? '全部'; } protected function platformLabels(): array { return [ 'pc' => 'PC 端', 'h5' => 'H5', 'wechat_mp' => '微信公众号', 'wechat_mini' => '微信小程序', 'app' => 'APP 接口预留', ]; } protected function platformLabel(string $platform): string { return $this->platformLabels()[$platform] ?? '全部'; } protected function deviceTypeLabels(): array { return [ 'desktop' => '桌面浏览器', 'mobile' => '移动浏览器', 'mini-program' => '小程序环境', 'mobile-webview' => '微信内网页', 'app-api' => 'APP 接口', ]; } protected function deviceTypeLabel(string $deviceType): string { return $this->deviceTypeLabels()[$deviceType] ?? ($deviceType === '' ? '未设置' : $deviceType); } protected function paymentChannelLabels(): array { return [ 'wechat_pay' => '微信支付', 'alipay' => '支付宝', ]; } protected function paymentChannelLabel(string $paymentChannel): string { return $this->paymentChannelLabels()[$paymentChannel] ?? ($paymentChannel === '' ? '未设置' : $paymentChannel); } protected function sortLabel(string $sort): string { return match ($sort) { 'oldest' => '创建时间正序', 'pay_amount_desc' => '实付金额从高到低', 'pay_amount_asc' => '实付金额从低到高', 'product_amount_desc' => '商品金额从高到低', 'product_amount_asc' => '商品金额从低到高', default => '创建时间倒序', }; } protected function formatMoneyRange(string $min, string $max): string { if ($min === '' && $max === '') { return '全部'; } $minLabel = $min !== '' && is_numeric($min) ? ('¥' . number_format((float) $min, 2, '.', '')) : '不限'; $maxLabel = $max !== '' && is_numeric($max) ? ('¥' . number_format((float) $max, 2, '.', '')) : '不限'; return $minLabel . ' ~ ' . $maxLabel; } protected function displayFilterValue(string $value, array $options): string { if ($value === '') { return '全部'; } return (string) ($options[$value] ?? $value); } protected function displayTextValue(string $value, string $default = '未设置'): string { return $value === '' ? $default : $value; } protected function displayMoneyValue(string $value): string { if ($value === '') { return '全部'; } return is_numeric($value) ? ('¥' . number_format((float) $value, 2, '.', '')) : $value; } protected function workbenchLinks(): array { return [ 'paid_high_amount' => '/merchant-admin/orders?sort=pay_amount_desc&payment_status=paid', 'pending_latest' => '/merchant-admin/orders?sort=latest&payment_status=unpaid', 'failed_latest' => '/merchant-admin/orders?sort=latest&payment_status=failed', 'completed_latest' => '/merchant-admin/orders?sort=latest&status=completed', 'current' => '/merchant-admin/orders', ]; } protected function buildOperationsFocus(int $merchantId, array $summaryStats, array $filters): array { $pendingCount = (int) Order::query()->forMerchant($merchantId)->where('payment_status', 'unpaid')->count(); $failedCount = (int) Order::query()->forMerchant($merchantId)->where('payment_status', 'failed')->count(); $completedCount = (int) Order::query()->forMerchant($merchantId)->where('status', 'completed')->count(); $links = $this->workbenchLinks(); $currentQuery = http_build_query(array_filter($this->exportableFilters($filters), fn ($value) => $value !== null && $value !== '' && $value !== 'all' && $value !== 'latest')); $currentUrl = $links['current'] . ($currentQuery !== '' ? ('?' . $currentQuery) : ''); $workbench = [ '高金额已支付' => $links['paid_high_amount'], '待支付跟进' => $links['pending_latest'], '支付失败排查' => $links['failed_latest'], '最近完成订单' => $links['completed_latest'], '返回当前筛选视图' => $currentUrl, ]; $signals = [ '待支付订单' => $pendingCount, '支付失败订单' => $failedCount, '已完成订单' => $completedCount, ]; if (($filters['platform'] ?? '') === 'wechat_mini') { return [ 'headline' => '当前筛选已聚焦微信小程序订单,建议优先关注下单到支付转化是否顺畅,并同步排查小程序端支付回流体验。', 'actions' => [ ['label' => '继续查看微信小程序订单', 'url' => $currentUrl], ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['payment_channel'] ?? '') === 'wechat_pay') { return [ 'headline' => '当前筛选已聚焦微信支付订单,建议优先核对支付成功率、回调稳定性与失败重试转化。', 'actions' => [ ['label' => '继续查看微信支付订单', 'url' => $currentUrl], ['label' => '去看支付失败订单', 'url' => $links['failed_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['device_type'] ?? '') === 'mini-program') { return [ 'headline' => '当前筛选已聚焦小程序环境订单,建议优先核对授权链路、支付唤起表现与下单回流是否顺畅。', 'actions' => [ ['label' => '继续查看小程序环境订单', 'url' => $currentUrl], ['label' => '去看微信支付订单', 'url' => $links['failed_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['device_type'] ?? '') === 'mobile-webview') { return [ 'headline' => '当前筛选已聚焦微信内网页订单,建议优先关注授权静默登录、页面跳转稳定性与支付拉起后的回流体验。', 'actions' => [ ['label' => '继续查看微信内网页订单', 'url' => $currentUrl], ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['device_type'] ?? '') === 'mobile') { return [ 'headline' => '当前筛选已聚焦移动浏览器订单,建议优先关注 H5 下单链路、页面加载稳定性与支付转化流失点。', 'actions' => [ ['label' => '继续查看移动浏览器订单', 'url' => $currentUrl], ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['device_type'] ?? '') === 'desktop') { return [ 'headline' => '当前筛选已聚焦桌面浏览器订单,建议优先关注 PC 端下单流程、页面首屏稳定性与高客单转化表现。', 'actions' => [ ['label' => '继续查看桌面浏览器订单', 'url' => $currentUrl], ['label' => '去看高金额已支付订单', 'url' => $links['paid_high_amount']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['payment_status'] ?? '') === 'failed') { return [ 'headline' => '当前筛选已聚焦支付失败订单,建议优先排查支付渠道、回调结果与用户重试情况。', 'actions' => [ ['label' => '继续查看支付失败订单', 'url' => $currentUrl], ['label' => '去看高金额已支付订单', 'url' => $links['paid_high_amount']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['payment_status'] ?? '') === 'unpaid') { return [ 'headline' => '当前筛选已聚焦待支付订单,建议优先跟进下单未支付用户,并观察支付转化时效。', 'actions' => [ ['label' => '继续查看待支付订单', 'url' => $currentUrl], ['label' => '去看高金额已支付订单', 'url' => $links['paid_high_amount']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['payment_status'] ?? '') === 'paid') { return [ 'headline' => '当前正在查看已支付订单,建议优先关注高金额订单履约进度与异常售后风险。', 'actions' => [ ['label' => '继续查看已支付订单', 'url' => $currentUrl], ['label' => '去看最近完成订单', 'url' => $links['completed_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($filters['status'] ?? '') === 'completed') { return [ 'headline' => '当前正在查看已完成订单,建议复盘高客单成交与复购来源,沉淀更稳定的转化路径。', 'actions' => [ ['label' => '继续查看已完成订单', 'url' => $currentUrl], ['label' => '去看高金额已支付订单', 'url' => $links['paid_high_amount']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($summaryStats['total_orders'] ?? 0) <= 0) { return [ 'headline' => '当前商家暂无订单,建议先确认交易链路、支付链路与回写链路是否都已打通。', 'actions' => [ ['label' => '先看订单整体情况', 'url' => $links['paid_high_amount']], ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } if (($summaryStats['total_orders'] ?? 0) < 5) { return [ 'headline' => '当前商家已有少量订单沉淀,建议优先关注待支付订单,并同步查看已支付订单质量。', 'actions' => [ ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ['label' => '去看已支付订单', 'url' => $links['paid_high_amount']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } return [ 'headline' => $failedCount > 0 ? '当前商家订单已形成基础规模,建议优先关注待支付、支付失败与高金额已支付订单。' : '当前商家订单已形成基础规模,建议优先关注待支付与高金额已支付订单,保持交易闭环稳定。', 'actions' => $failedCount > 0 ? [ ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ['label' => '去看支付失败订单', 'url' => $links['failed_latest']], ] : [ ['label' => '去看待支付订单', 'url' => $links['pending_latest']], ['label' => '去看高金额已支付订单', 'url' => $links['paid_high_amount']], ], 'workbench' => $workbench, 'signals' => $signals, ]; } }