@extends('admin.layouts.app') @section('title', '总台仪表盘') @section('page_title', '总台仪表盘') @section('content') @php $incomingBack = (string) request()->query('back', ''); $safeBackForLinks = \App\Support\BackUrl::sanitizeForLinks($incomingBack); // 用于构建“从仪表盘跳到其它治理页后可返回仪表盘”的 back $selfWithoutBack = \App\Support\BackUrl::selfWithoutBack(); $billingEntryLinks = [ 'platform_orders' => \App\Support\BackUrl::withBack('/admin/platform-orders', $safeBackForLinks), 'site_subscriptions' => \App\Support\BackUrl::withBack('/admin/site-subscriptions', $safeBackForLinks), 'plans' => \App\Support\BackUrl::withBack('/admin/plans', $safeBackForLinks), ]; $platformOrdersQuickLinks = [ 'unpaid_pending' => \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=unpaid&status=pending', $safeBackForLinks), // 待生效:paid + pending,并显式锁定 sync_status=unsynced(排除同步失败等异常单) 'paid_pending' => \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=paid&status=pending&sync_status=unsynced', $safeBackForLinks), 'syncable_only' => \App\Support\BackUrl::withBack('/admin/platform-orders?syncable_only=1', $safeBackForLinks), 'sync_failed' => \App\Support\BackUrl::withBack('/admin/platform-orders?sync_status=failed', $safeBackForLinks), ]; @endphp
@php $kpiLinks = [ 'merchants' => \App\Support\BackUrl::withBack('/admin/merchants', $selfWithoutBack), 'plans' => \App\Support\BackUrl::withBack('/admin/plans', $selfWithoutBack), 'site_subscriptions' => \App\Support\BackUrl::withBack('/admin/site-subscriptions', $selfWithoutBack), 'platform_orders' => \App\Support\BackUrl::withBack('/admin/platform-orders', $selfWithoutBack), ]; @endphp
站点
全站点数量(总台视角)
套餐
可售套餐目录
订阅
站点订阅总量(收费主链)
平台订单
平台收费订单总量

趋势

近7天|平台订单(按天)
@php $trendRows = (array) ($platformOrderTrend7d ?? []); // 用于前端渐进增强渲染迷你图表(JS 读取 data-points) $trendPoints = []; foreach ($trendRows as $r) { $trendPoints[] = [ 'date' => (string) ($r['date'] ?? ''), 'count' => (int) ($r['count'] ?? 0), 'paid_sum' => (float) ($r['paid_sum'] ?? 0), ]; } @endphp
@php $trendPaidTotal = 0.0; $trendOrdersTotal = 0; $trendPaidMax = 0.0; foreach ($trendRows as $r) { $trendPaidTotal += (float) ($r['paid_sum'] ?? 0); $trendOrdersTotal += (int) ($r['count'] ?? 0); $trendPaidMax = max($trendPaidMax, (float) ($r['paid_sum'] ?? 0)); } @endphp
7天合计已付:¥{{ number_format($trendPaidTotal, 2) }} 7天订单数:{{ (int) $trendOrdersTotal }} 峰值:¥{{ number_format($trendPaidMax, 2) }}
@forelse($trendRows as $row) @php $d = (string) ($row['date'] ?? ''); $dayOrdersUrl = \App\Support\BackUrl::withBack( '/admin/platform-orders?' . \Illuminate\Support\Arr::query([ 'created_from' => $d, 'created_to' => $d, ]), $selfWithoutBack ); @endphp @empty @endforelse
日期 订单数 已付金额
{{ $d }} {{ (int) ($row['count'] ?? 0) }} ¥{{ number_format((float) ($row['paid_sum'] ?? 0), 2) }}
暂无数据
说明:先接入最小可用趋势数据;后续再补时间范围切换、维度切换与可视化图表。

排行(近7天站点收入 Top5)

@php $rankRows = (array) ($merchantRevenueRank7d ?? []); $rankTotal = 0.0; foreach ($rankRows as $r) { $rankTotal += (float) ($r['paid_sum'] ?? 0); } @endphp
近7天|按已付金额统计
@php // 用于前端渐进增强渲染迷你排行(JS 读取 data-points) $rankPoints = []; foreach ($rankRows as $r) { $mid = (int) ($r['merchant_id'] ?? 0); $rankPoints[] = [ 'merchant_id' => $mid, 'name' => (string) (($merchantIdToName[$mid] ?? '') ?: ('#' . $mid)), 'count' => (int) ($r['count'] ?? 0), 'paid_sum' => (float) ($r['paid_sum'] ?? 0), ]; } @endphp
@php $rankPaidMax = 0.0; $rankOrdersTotal = 0; foreach ($rankRows as $r) { $rankPaidMax = max($rankPaidMax, (float) ($r['paid_sum'] ?? 0)); $rankOrdersTotal += (int) ($r['count'] ?? 0); } @endphp
Top5合计已付:¥{{ number_format($rankTotal, 2) }} Top5订单数:{{ (int) $rankOrdersTotal }} Top1金额:¥{{ number_format($rankPaidMax, 2) }}
@forelse($rankRows as $row) @php $mid = (int) ($row['merchant_id'] ?? 0); $mname = (string) (($merchantIdToName[$mid] ?? '') ?: ('#' . $mid)); $merchantOrdersUrl = \App\Support\BackUrl::withBack( '/admin/platform-orders?' . \Illuminate\Support\Arr::query([ 'merchant_id' => $mid, 'created_from' => now()->subDays(6)->format('Y-m-d'), 'created_to' => now()->format('Y-m-d'), ]), $selfWithoutBack ); @endphp @empty @endforelse
站点 订单数 已付金额
{{ $mname }} {{ (int) ($row['count'] ?? 0) }} ¥{{ number_format((float) ($row['paid_sum'] ?? 0), 2) }}
暂无数据
说明:先落最小可用 Top5 排行;后续补时间范围切换、维度切换与异常排行。

平台定位

后台角色{{ $platformOverview['system_role'] }}
当前视角{{ $platformOverview['current_scope'] }}
商家模式{{ $platformOverview['merchant_mode'] }}
渠道数{{ $platformOverview['channel_count'] }}
活跃商家{{ $platformOverview['active_merchants'] }}
待处理订单{{ $platformOverview['pending_orders'] }}

最近平台订单

@php $platformOrdersIndexUrl = \App\Support\BackUrl::withBack('/admin/platform-orders', $selfWithoutBack); @endphp 查看全部
@forelse(($recentPlatformOrders ?? []) as $po) @php $poShowUrl = \App\Support\BackUrl::withBack('/admin/platform-orders/' . $po->id, $selfWithoutBack); $hasReceiptEvidence = (data_get($po->meta, 'payment_summary.total_amount') !== null) || (data_get($po->meta, 'payment_receipts.0.amount') !== null); $fixReceiptUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'add-payment-receipt'); $noReceiptListUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?payment_status=paid&receipt_status=none', $selfWithoutBack); $syncErrMsg = (string) (data_get($po->meta, 'subscription_activation_error.message') ?? ''); $bmpaErrMsg = (string) (data_get($po->meta, 'batch_mark_paid_and_activate_error.message') ?? ''); $syncFailedListUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?sync_status=failed', $selfWithoutBack); $bmpaFailedListUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?bmpa_failed_only=1', $selfWithoutBack); // 扫描行:直达治理锚点(与下方提示块的链接口径保持一致) $scanGoReconcileUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'payment-receipts'); $scanGoRefundUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'refund-receipts'); $scanGoSyncFailedUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'sync-failed'); $scanGoBmpaFailedUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'bmpa-failed'); $scanGoRelationUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders/' . $po->id, $selfWithoutBack, 'relation-subscription'); // 运营扫描用的“治理状态摘要”(不替代下方的治理提示入口,只用于快速判断) // 注意:为避免对“未支付订单”造成误导,回执/对账/退款在非 paid/refunded 时显示 "-"。 $isPaid = ((string) $po->payment_status === 'paid'); $isRefunded = ((string) $po->payment_status === 'refunded'); $receiptStatusText = $isPaid ? ($hasReceiptEvidence ? '有' : '无') : '-'; $reconcileStatusText = ($isPaid && $hasReceiptEvidence) ? ($po->isReconcileMismatch() ? '不一致' : '一致') : '-'; $refundStatusText = ($isPaid || $isRefunded) ? ($po->isRefundInconsistent() ? '异常' : '正常') : '-'; $syncStatusText = $syncErrMsg !== '' ? '失败' : '正常'; $bmpaStatusText = $bmpaErrMsg !== '' ? '失败' : '正常'; $subscriptionStatusText = ((string) $po->order_type === 'renewal') ? (empty($po->site_subscription_id) ? '缺' : '有') : '-'; // 运营提效:失败原因较短时,提供“一键进入同原因集合”链接(避免复制粘贴)。 $FAILED_REASON_KEYWORD_MAX_LEN = 80; $syncReasonUrl = ''; if ($syncErrMsg !== '' && mb_strlen($syncErrMsg) <= $FAILED_REASON_KEYWORD_MAX_LEN) { $syncReasonUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([ 'sync_status' => 'failed', 'sync_error_keyword' => $syncErrMsg, ]), $selfWithoutBack); } $bmpaReasonUrl = ''; if ($bmpaErrMsg !== '' && mb_strlen($bmpaErrMsg) <= $FAILED_REASON_KEYWORD_MAX_LEN) { $bmpaReasonUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([ 'bmpa_failed_only' => '1', 'bmpa_error_keyword' => $bmpaErrMsg, ]), $selfWithoutBack); } @endphp @empty @endforelse
订单号 类型 金额 支付 状态
{{ $po->order_no }} {{ $po->orderTypeLabel() }}
{{ (string) (optional($po->merchant)->name ?: ('站点#' . (int) ($po->merchant_id ?? 0))) }} {{ (string) (optional($po->plan)->name ?: ((int) ($po->plan_id ?? 0) > 0 ? ('套餐#' . (int) $po->plan_id) : '-')) }}
回执: @if($receiptStatusText === '无') {{ $receiptStatusText }} @else {{ $receiptStatusText }} @endif 对账: @if($reconcileStatusText !== '-' && $reconcileStatusText !== '一致') {{ $reconcileStatusText }} @else {{ $reconcileStatusText }} @endif 退款: @if($refundStatusText !== '-' && $refundStatusText !== '正常') {{ $refundStatusText }} @else {{ $refundStatusText }} @endif 同步: @if($syncStatusText === '失败') {{ $syncStatusText }} @else {{ $syncStatusText }} @endif BMPA: @if($bmpaStatusText === '失败') {{ $bmpaStatusText }} @else {{ $bmpaStatusText }} @endif 订阅: @if($subscriptionStatusText === '缺') {{ $subscriptionStatusText }} @else {{ $subscriptionStatusText }} @endif
¥{{ number_format((float) $po->payable_amount, 2) }} {{ $po->payment_status }} @if((string) $po->payment_status === 'paid' && ! $hasReceiptEvidence)
无回执 去补回执 进入集合
@endif
{{ $po->status }} @if($syncErrMsg !== '')
同步失败 进入集合 @if($syncReasonUrl !== '') 同原因集合 @elseif($syncErrMsg !== '') 原因过长 @endif 查看失败详情
@endif @if($bmpaErrMsg !== '')
BMPA失败 进入集合 @if($bmpaReasonUrl !== '') 同原因集合 @elseif($bmpaErrMsg !== '') 原因过长 @endif 查看失败详情
@endif @if((string) $po->status === 'pending' && (string) $po->payment_status === 'paid' && $po->isReconcileMismatch()) @php $reconcileMismatchUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?reconcile_mismatch=1', $selfWithoutBack); @endphp
对账不一致 进入集合 去对账 去补回执
@endif @if($po->isRefundInconsistent()) @php $refundInconsistentUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?refund_inconsistent=1', $selfWithoutBack); @endphp
退款不一致 进入集合 去核对退款 去补退款记录
@endif @if((string) $po->order_type === 'renewal' && empty($po->site_subscription_id)) @php $renewalMissingSubscriptionUrl = \App\Support\BackUrl::withBack('/admin/platform-orders?renewal_missing_subscription=1', $selfWithoutBack); @endphp
续费缺订阅 进入集合 去关联订阅
@endif
暂无数据
说明:当前先接入最近订单列表;后续补“同步状态/站点/套餐/治理入口”。

套餐订单占比(Top5)

@php $plansIndexUrl = \App\Support\BackUrl::withBack('/admin/plans', $selfWithoutBack); @endphp 查看套餐
近7天|按订单数统计
@php $shareRows = (array) ($planOrderShare ?? []); $top5Orders = 0; foreach ($shareRows as $r) { $top5Orders += (int) ($r['count'] ?? 0); } $totalOrders = (int) ($planOrderShareTotal ?? 0); if ($totalOrders <= 0) { // 兜底:兼容旧数据(未传 total 时,至少不影响渲染) $totalOrders = (int) $top5Orders; } @endphp @php // 用于前端渐进增强渲染占比条形图(JS 读取 data-points) $sharePoints = []; foreach ($shareRows as $r) { $pid = (int) ($r['plan_id'] ?? 0); $sharePoints[] = [ 'plan_id' => $pid, 'name' => (string) (($planIdToName[$pid] ?? '') ?: ('#' . $pid)), 'count' => (int) ($r['count'] ?? 0), ]; } @endphp
@php $shareTop1Count = 0; foreach ($shareRows as $r) { $shareTop1Count = max($shareTop1Count, (int) ($r['count'] ?? 0)); } $shareTop1Pct = $totalOrders > 0 ? round(($shareTop1Count / $totalOrders) * 100, 1) : 0; $shareCoveragePct = $totalOrders > 0 ? round(($top5Orders / $totalOrders) * 100, 1) : 0; $shareOtherPct = max(0, round(100 - $shareCoveragePct, 1)); $shareOtherCount = max(0, (int) $totalOrders - (int) $top5Orders); @endphp
全量订单:{{ (int) $totalOrders }} Top5合计:{{ (int) $top5Orders }} 覆盖率:{{ $shareCoveragePct }}% 其它:{{ $shareOtherPct }}%({{ (int) $shareOtherCount }}单) Top1占比:{{ $shareTop1Pct }}%
@forelse($shareRows as $row) @php $planId = (int) ($row['plan_id'] ?? 0); $count = (int) ($row['count'] ?? 0); $pct = $totalOrders > 0 ? round(($count / $totalOrders) * 100, 1) : 0; $planName = (string) (($planIdToName[$planId] ?? '') ?: ('#' . $planId)); @endphp @php $planOrdersUrl = \App\Support\BackUrl::withBack( '/admin/platform-orders?' . \Illuminate\Support\Arr::query([ 'plan_id' => $planId, 'created_from' => now()->subDays(6)->format('Y-m-d'), 'created_to' => now()->format('Y-m-d'), ]), $selfWithoutBack ); @endphp @empty @endforelse
套餐 订单数 占比
{{ $planName }} {{ $count }} {{ $pct }}%
暂无数据
说明:当前口径为“平台订单按 plan_id 的数量占比(Top5)”;后续扩展到金额占比、渠道占比与时间范围切换。
@endsection