feat(dashboard): 平台定位改为运营版北极星指标+治理Top3

This commit is contained in:
萝卜
2026-03-17 07:05:03 +08:00
parent df99fb00ed
commit 8bd838c0b6
3 changed files with 212 additions and 11 deletions

View File

@@ -159,10 +159,13 @@ class DashboardController extends Controller
]
);
// 统一基准时间:避免本方法内多次 now() 调用在跨天瞬间造成口径漂移
$baseNow = now();
// 趋势卡(最小可用):近 7 天平台订单按天统计(订单数 + 已付金额)
$trendDays = 7;
$trendStart = now()->startOfDay()->subDays($trendDays - 1);
$trendEnd = now()->endOfDay();
$trendStart = $baseNow->copy()->startOfDay()->subDays($trendDays - 1);
$trendEnd = $baseNow->copy()->endOfDay();
// 统一提供给视图做“趋势/排行/占比”跳转的日期范围口径,避免 Blade 内重复 now() 计算导致漂移。
$dashboardRangeFrom7d = (string) $trendStart->format('Y-m-d');
@@ -264,6 +267,86 @@ class DashboardController extends Controller
$merchantIdToName = Merchant::query()->pluck('name', 'id')->all();
// 平台定位(运营版):北极星指标 + 明确治理入口(不暴露组合维度)
$range30Start = $baseNow->copy()->subDays(29)->startOfDay();
$range30End = $baseNow->copy()->endOfDay();
$range30From = (string) $range30Start->format('Y-m-d');
$range30To = (string) $range30End->format('Y-m-d');
$paidRevenue30d = (float) PlatformOrder::query()
->where('payment_status', 'paid')
->whereBetween('created_at', [$range30Start, $range30End])
->sum('paid_amount');
$activePaidMerchants = (int) SiteSubscription::query()
->whereNotNull('merchant_id')
->where('status', 'activated')
->whereNotNull('ends_at')
->where('ends_at', '>=', $baseNow)
->distinct()
->count('merchant_id');
$renewalCreated30d = (int) PlatformOrder::query()
->where('order_type', 'renewal')
->whereBetween('created_at', [$range30Start, $range30End])
->count();
$renewalSuccess30d = (int) PlatformOrder::query()
->where('order_type', 'renewal')
->where('payment_status', 'paid')
->where('status', 'activated')
->whereBetween('created_at', [$range30Start, $range30End])
->count();
$renewalSuccessRate30d = $renewalCreated30d > 0
? min(100, max(0, round(($renewalSuccess30d / $renewalCreated30d) * 100, 1)))
: 0;
$funnelUnpaidPending7d = (int) PlatformOrder::query()
->whereBetween('created_at', [$trendStart, $trendEnd])
->where('payment_status', 'unpaid')
->where('status', 'pending')
->count();
$funnelPaid7d = (int) PlatformOrder::query()
->whereBetween('created_at', [$trendStart, $trendEnd])
->where('payment_status', 'paid')
->count();
$funnelPaidActivated7d = (int) PlatformOrder::query()
->whereBetween('created_at', [$trendStart, $trendEnd])
->where('payment_status', 'paid')
->where('status', 'activated')
->count();
// 将平台定位的关键指标与“可执行动作入口”绑定(回到仪表盘自身)
$opsLinks = [
'revenue_30d_paid_orders' => \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'payment_status' => 'paid',
'created_from' => $range30From,
'created_to' => $range30To,
]), \App\Support\BackUrl::selfWithoutBack()),
'active_paid_merchants_subscriptions' => \App\Support\BackUrl::withBack('/admin/site-subscriptions?status=activated', \App\Support\BackUrl::selfWithoutBack()),
'renewal_orders_30d' => \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'order_type' => 'renewal',
'created_from' => $range30From,
'created_to' => $range30To,
]), \App\Support\BackUrl::selfWithoutBack()),
'funnel_unpaid_pending_7d' => \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'payment_status' => 'unpaid',
'status' => 'pending',
'created_from' => $dashboardRangeFrom7d,
'created_to' => $dashboardRangeTo7d,
]), \App\Support\BackUrl::selfWithoutBack()),
'funnel_paid_7d' => \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'payment_status' => 'paid',
'created_from' => $dashboardRangeFrom7d,
'created_to' => $dashboardRangeTo7d,
]), \App\Support\BackUrl::selfWithoutBack()),
'funnel_paid_activated_7d' => \App\Support\BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'payment_status' => 'paid',
'status' => 'activated',
'created_from' => $dashboardRangeFrom7d,
'created_to' => $dashboardRangeTo7d,
]), \App\Support\BackUrl::selfWithoutBack()),
];
return view('admin.dashboard', [
'adminName' => $admin->name,
'stats' => $stats,
@@ -283,6 +366,27 @@ class DashboardController extends Controller
'store' => config('cache.default'),
'ttl' => '10m',
],
'platformOpsOverview' => [
// 北极星指标
'paid_revenue_30d' => $paidRevenue30d,
'active_paid_merchants' => $activePaidMerchants,
'renewal_success_rate_30d' => $renewalSuccessRate30d,
'renewal_success_30d' => $renewalSuccess30d,
'renewal_created_30d' => $renewalCreated30d,
// 漏斗近7天
'funnel_unpaid_pending_7d' => $funnelUnpaidPending7d,
'funnel_paid_7d' => $funnelPaid7d,
'funnel_paid_activated_7d' => $funnelPaidActivated7d,
// 待处理治理(积压口径,全量)
'govern_bmpa_processable' => (int) ($stats['platform_orders_unpaid_pending'] ?? 0),
'govern_syncable' => (int) ($stats['platform_orders_syncable'] ?? 0),
'govern_sync_failed' => (int) ($stats['platform_orders_sync_failed'] ?? 0),
'links' => $opsLinks,
],
'platformOverview' => [
'system_role' => '总台管理',
'current_scope' => '总台运营方视角',

View File

@@ -462,16 +462,75 @@
<div class="muted muted-xs mt-10">说明:这里先把收费主链的高频治理入口收敛到仪表盘;后续再补趋势/排行的真实聚合。</div>
</div>
<div class="card">
<h3 class="mt-0">平台定位</h3>
<table>
<tr><th>后台角色</th><td>{{ $platformOverview['system_role'] }}</td></tr>
<tr><th>当前视角</th><td>{{ $platformOverview['current_scope'] }}</td></tr>
<tr><th>商家模式</th><td>{{ $platformOverview['merchant_mode'] }}</td></tr>
<tr><th>渠道数</th><td>{{ $platformOverview['channel_count'] }}</td></tr>
<tr><th>活跃商家</th><td>{{ $platformOverview['active_merchants'] }}</td></tr>
<tr><th>待处理订单</th><td>{{ $platformOverview['pending_orders'] }}</td></tr>
<div class="card" data-role="dashboard-platform-ops-overview">
<h3 class="mt-0">平台定位(运营版)</h3>
<div class="muted">只保留“看完知道下一步做什么”的北极星指标与治理积压。</div>
@php
$ops = $platformOpsOverview ?? [];
$opsLinks = (array) ($ops['links'] ?? []);
$paidRevenue30d = (float) ($ops['paid_revenue_30d'] ?? 0);
$activePaidMerchants = (int) ($ops['active_paid_merchants'] ?? 0);
$renewalRate30d = (float) ($ops['renewal_success_rate_30d'] ?? 0);
$renewalSuccess30d = (int) ($ops['renewal_success_30d'] ?? 0);
$renewalCreated30d = (int) ($ops['renewal_created_30d'] ?? 0);
$funnelUnpaidPending7d = (int) ($ops['funnel_unpaid_pending_7d'] ?? 0);
$funnelPaid7d = (int) ($ops['funnel_paid_7d'] ?? 0);
$funnelPaidActivated7d = (int) ($ops['funnel_paid_activated_7d'] ?? 0);
$goBmpa = (int) ($ops['govern_bmpa_processable'] ?? 0);
$goSyncable = (int) ($ops['govern_syncable'] ?? 0);
$goSyncFailed = (int) ($ops['govern_sync_failed'] ?? 0);
@endphp
<table class="mt-10">
<tr>
<th>近30天已收款</th>
<td>
<a class="link" href="{!! (string) ($opsLinks['revenue_30d_paid_orders'] ?? $billingEntryLinks['platform_orders']) !!}">
{{ number_format($paidRevenue30d, 2) }}
</a>
</td>
</tr>
<tr>
<th>活跃付费站点</th>
<td>
<a class="link" href="{!! (string) ($opsLinks['active_paid_merchants_subscriptions'] ?? $billingEntryLinks['site_subscriptions']) !!}">
{{ $activePaidMerchants }}
</a>
<span class="muted muted-xs">(以“已生效且未到期订阅”估算)</span>
</td>
</tr>
<tr>
<th>续费成功率30天</th>
<td>
<a class="link" href="{!! (string) ($opsLinks['renewal_orders_30d'] ?? $billingEntryLinks['platform_orders']) !!}">
{{ $renewalRate30d }}%
</a>
<span class="muted muted-xs">{{ $renewalSuccess30d }} / {{ $renewalCreated30d }}</span>
</td>
</tr>
</table>
<div class="mt-10">
<div class="muted"><strong>收款漏斗近7天</strong></div>
<div class="muted muted-xs mt-6">用于快速判断卡点:催付 / 治理生效 / 同步订阅。</div>
<div class="actions gap-10 mt-6">
<a class="btn btn-secondary btn-sm" href="{!! (string) ($opsLinks['funnel_unpaid_pending_7d'] ?? $platformOrdersQuickLinks['unpaid_pending']) !!}">待支付 {{ $funnelUnpaidPending7d }}</a>
<a class="btn btn-secondary btn-sm" href="{!! (string) ($opsLinks['funnel_paid_7d'] ?? $platformOrdersQuickLinks['platform_orders']) !!}">已支付 {{ $funnelPaid7d }}</a>
<a class="btn btn-secondary btn-sm" href="{!! (string) ($opsLinks['funnel_paid_activated_7d'] ?? $platformOrdersQuickLinks['platform_orders']) !!}">已生效 {{ $funnelPaidActivated7d }}</a>
</div>
</div>
<div class="mt-10">
<div class="muted"><strong>待处理治理Top3</strong></div>
<div class="actions gap-10 mt-6">
<a class="btn btn-sm" href="{!! $platformOrdersQuickLinks['paid_pending'] !!}">可BMPA {{ $goBmpa }}</a>
<a class="btn btn-sm" href="{!! $platformOrdersQuickLinks['syncable_only'] !!}">可同步 {{ $goSyncable }}</a>
<a class="btn btn-sm" href="{!! $platformOrdersQuickLinks['sync_failed'] !!}">同步失败 {{ $goSyncFailed }}</a>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,38 @@
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AdminDashboardPlatformOpsOverviewShouldRenderTest extends TestCase
{
use RefreshDatabase;
protected function loginAsPlatformAdmin(): void
{
$this->seed();
$this->post('/admin/login', [
'email' => 'platform.admin@demo.local',
'password' => 'Platform@123456',
])->assertRedirect('/admin');
}
public function test_dashboard_should_render_platform_ops_overview_block(): void
{
$this->loginAsPlatformAdmin();
$res = $this->get('/admin');
$res->assertOk();
$html = (string) $res->getContent();
$this->assertStringContainsString('data-role="dashboard-platform-ops-overview"', $html);
$this->assertStringContainsString('平台定位(运营版)', $html);
$this->assertStringContainsString('待处理治理Top3', $html);
$this->assertStringContainsString('可BMPA', $html);
$this->assertStringContainsString('可同步', $html);
$this->assertStringContainsString('同步失败', $html);
}
}