diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php
index 6f5e09c..003723c 100644
--- a/app/Http/Controllers/Admin/DashboardController.php
+++ b/app/Http/Controllers/Admin/DashboardController.php
@@ -300,6 +300,10 @@ class DashboardController extends Controller
? min(100, max(0, round(($renewalSuccess30d / $renewalCreated30d) * 100, 1)))
: 0;
+ $ordersTotal7d = (int) PlatformOrder::query()
+ ->whereBetween('created_at', [$trendStart, $trendEnd])
+ ->count();
+
$funnelUnpaidPending7d = (int) PlatformOrder::query()
->whereBetween('created_at', [$trendStart, $trendEnd])
->where('payment_status', 'unpaid')
@@ -377,6 +381,7 @@ class DashboardController extends Controller
'renewal_created_30d' => $renewalCreated30d,
// 漏斗(近7天)
+ 'orders_total_7d' => $ordersTotal7d,
'funnel_unpaid_pending_7d' => $funnelUnpaidPending7d,
'funnel_paid_7d' => $funnelPaid7d,
'funnel_paid_activated_7d' => $funnelPaidActivated7d,
diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php
index f90ff2f..b3a6e66 100644
--- a/resources/views/admin/dashboard.blade.php
+++ b/resources/views/admin/dashboard.blade.php
@@ -481,6 +481,8 @@
$renewalSuccess30d = (int) ($ops['renewal_success_30d'] ?? 0);
$renewalCreated30d = (int) ($ops['renewal_created_30d'] ?? 0);
+ $ordersTotal7d = (int) ($ops['orders_total_7d'] ?? 0);
+
$funnelUnpaidPending7d = (int) ($ops['funnel_unpaid_pending_7d'] ?? 0);
$funnelPaid7d = (int) ($ops['funnel_paid_7d'] ?? 0);
$funnelPaidActivated7d = (int) ($ops['funnel_paid_activated_7d'] ?? 0);
@@ -519,23 +521,80 @@
-
+ @php
+ $den = max(1, $ordersTotal7d);
+ $pctUnpaidPending = $den > 0 ? min(100, max(0, round(($funnelUnpaidPending7d / $den) * 100, 1))) : 0;
+ $pctPaid = $den > 0 ? min(100, max(0, round(($funnelPaid7d / $den) * 100, 1))) : 0;
+ $pctPaidActivated = $den > 0 ? min(100, max(0, round(($funnelPaidActivated7d / $den) * 100, 1))) : 0;
+
+ // 待处理治理:以平台订单总量作为分母,给一个“规模感”(不要求精确经营含义)。
+ $poTotalForOps = (int) ($stats['platform_orders'] ?? 0);
+ $denOps = max(1, $poTotalForOps);
+ $pctGoBmpa = $poTotalForOps > 0 ? min(100, max(0, round(($goBmpa / $denOps) * 100, 1))) : 0;
+ $pctGoSyncable = $poTotalForOps > 0 ? min(100, max(0, round(($goSyncable / $denOps) * 100, 1))) : 0;
+ $pctGoSyncFailed = $poTotalForOps > 0 ? min(100, max(0, round(($goSyncFailed / $denOps) * 100, 1))) : 0;
+ @endphp
+
+
-
diff --git a/tests/Feature/AdminDashboardPlatformOpsOverviewMiniBarsShouldRenderTest.php b/tests/Feature/AdminDashboardPlatformOpsOverviewMiniBarsShouldRenderTest.php
new file mode 100644
index 0000000..1eddc75
--- /dev/null
+++ b/tests/Feature/AdminDashboardPlatformOpsOverviewMiniBarsShouldRenderTest.php
@@ -0,0 +1,43 @@
+seed();
+
+ $this->post('/admin/login', [
+ 'email' => 'platform.admin@demo.local',
+ 'password' => 'Platform@123456',
+ ])->assertRedirect('/admin');
+ }
+
+ public function test_dashboard_platform_ops_overview_should_render_mini_bars(): void
+ {
+ $this->loginAsPlatformAdmin();
+
+ $res = $this->get('/admin');
+ $res->assertOk();
+
+ $html = (string) $res->getContent();
+
+ // 收款漏斗 mini bars
+ $this->assertStringContainsString('data-role="platform-ops-funnel-bars"', $html);
+ $this->assertStringContainsString('data-role="ops-funnel-unpaid-pending-bar"', $html);
+ $this->assertStringContainsString('data-role="ops-funnel-paid-bar"', $html);
+ $this->assertStringContainsString('data-role="ops-funnel-paid-activated-bar"', $html);
+
+ // 待处理治理 Top3 mini bars
+ $this->assertStringContainsString('data-role="platform-ops-governance-bars"', $html);
+ $this->assertStringContainsString('data-role="ops-govern-bmpa-bar"', $html);
+ $this->assertStringContainsString('data-role="ops-govern-syncable-bar"', $html);
+ $this->assertStringContainsString('data-role="ops-govern-sync-failed-bar"', $html);
+ }
+}