diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php
index 2e31dac..7823c99 100644
--- a/app/Http/Controllers/Admin/DashboardController.php
+++ b/app/Http/Controllers/Admin/DashboardController.php
@@ -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' => '总台运营方视角',
diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php
index d951d3f..3c44760 100644
--- a/resources/views/admin/dashboard.blade.php
+++ b/resources/views/admin/dashboard.blade.php
@@ -462,16 +462,75 @@
说明:这里先把收费主链的高频治理入口收敛到仪表盘;后续再补趋势/排行的真实聚合。
-
-
平台定位
-
- | 后台角色 | {{ $platformOverview['system_role'] }} |
- | 当前视角 | {{ $platformOverview['current_scope'] }} |
- | 商家模式 | {{ $platformOverview['merchant_mode'] }} |
- | 渠道数 | {{ $platformOverview['channel_count'] }} |
- | 活跃商家 | {{ $platformOverview['active_merchants'] }} |
- | 待处理订单 | {{ $platformOverview['pending_orders'] }} |
+
+
平台定位(运营版)
+
只保留“看完知道下一步做什么”的北极星指标与治理积压。
+
+ @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
+
+
+
+
+
收款漏斗(近7天)
+
用于快速判断卡点:催付 / 治理生效 / 同步订阅。
+
+
+
+
diff --git a/tests/Feature/AdminDashboardPlatformOpsOverviewShouldRenderTest.php b/tests/Feature/AdminDashboardPlatformOpsOverviewShouldRenderTest.php
new file mode 100644
index 0000000..65a170f
--- /dev/null
+++ b/tests/Feature/AdminDashboardPlatformOpsOverviewShouldRenderTest.php
@@ -0,0 +1,38 @@
+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);
+ }
+}