diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php index fa95b19..b14711b 100644 --- a/app/Http/Controllers/Admin/DashboardController.php +++ b/app/Http/Controllers/Admin/DashboardController.php @@ -48,10 +48,31 @@ class DashboardController extends Controller ->where('ends_at', '<', now()->addDays(7)) ->count(), 'platform_orders' => PlatformOrder::count(), - 'platform_orders_unpaid_pending' => PlatformOrder::query() - ->where('payment_status', 'unpaid') - ->where('status', 'pending') - ->count(), + // 可BMPA处理(尽量贴近后端治理安全阀): + // - pending + unpaid + // - 排除:存在退款轨迹(refund_total > 0) + // - 排除:续费缺订阅(order_type=renewal 且 site_subscription_id 为空) + 'platform_orders_unpaid_pending' => (function () { + $q = PlatformOrder::query() + ->where('payment_status', 'unpaid') + ->where('status', 'pending') + ->where(function ($b) { + $b->where('order_type', '!=', 'renewal') + ->orWhereNotNull('site_subscription_id'); + }); + + $driver = $q->getQuery()->getConnection()->getDriverName(); + + if ($driver === 'sqlite') { + $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_EXTRACT(meta, '$.refund_summary.total_amount') AS REAL) ELSE (SELECT IFNULL(SUM(CAST(JSON_EXTRACT(value, '$.amount') AS REAL)), 0) FROM json_each(COALESCE(JSON_EXTRACT(meta, '$.refund_receipts'), '[]'))) END)"; + $q->whereRaw("ROUND(($refundTotalExpr) * 100) <= 0"); + } else { + $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.refund_summary.total_amount')) AS DECIMAL(12,2)) ELSE (SELECT IFNULL(SUM(j.amount), 0) FROM JSON_TABLE(meta, '$.refund_receipts[*]' COLUMNS(amount DECIMAL(12,2) PATH '$.amount')) j) END)"; + $q->whereRaw("ROUND(($refundTotalExpr) * 100) <= 0"); + } + + return $q->count(); + })(), 'platform_orders_paid_pending' => PlatformOrder::query() ->where('payment_status', 'paid') ->where('status', 'pending') diff --git a/tests/Feature/AdminDashboardBillingWorkbenchBmpaProcessableCountShouldExcludeRefundTraceAndRenewalMissingSubscriptionTest.php b/tests/Feature/AdminDashboardBillingWorkbenchBmpaProcessableCountShouldExcludeRefundTraceAndRenewalMissingSubscriptionTest.php new file mode 100644 index 0000000..5a30f69 --- /dev/null +++ b/tests/Feature/AdminDashboardBillingWorkbenchBmpaProcessableCountShouldExcludeRefundTraceAndRenewalMissingSubscriptionTest.php @@ -0,0 +1,98 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_dashboard_bmpa_processable_count_should_exclude_refund_trace_and_renewal_missing_subscription(): void + { + Cache::flush(); + + $this->loginAsPlatformAdmin(); + + // 清理 seed 订单,避免统计受 seed 影响。 + PlatformOrder::query()->delete(); + + $merchantId = (int) Merchant::query()->value('id'); + $platformAdminId = (int) Admin::query()->where('email', 'platform.admin@demo.local')->value('id'); + + // 1) 可处理:pending+unpaid 且无退款轨迹 + PlatformOrder::query()->create([ + 'merchant_id' => $merchantId, + 'plan_id' => null, + 'site_subscription_id' => null, + 'created_by_admin_id' => $platformAdminId ?: null, + 'order_no' => 'PO_DASH_BMPA_PROCESSABLE_0001', + 'order_type' => 'new_purchase', + 'status' => 'pending', + 'payment_status' => 'unpaid', + 'payable_amount' => 9, + 'paid_amount' => 0, + 'meta' => [], + ]); + + // 2) 不可处理:pending+unpaid 但有退款轨迹(refund_total>0) + PlatformOrder::query()->create([ + 'merchant_id' => $merchantId, + 'plan_id' => null, + 'site_subscription_id' => null, + 'created_by_admin_id' => $platformAdminId ?: null, + 'order_no' => 'PO_DASH_BMPA_BLOCKED_REFUND_TRACE_0002', + 'order_type' => 'new_purchase', + 'status' => 'pending', + 'payment_status' => 'unpaid', + 'payable_amount' => 9, + 'paid_amount' => 0, + 'meta' => [ + 'refund_summary' => [ + 'total_amount' => 1, + 'count' => 1, + ], + ], + ]); + + // 3) 不可处理:pending+unpaid 且续费缺订阅 + PlatformOrder::query()->create([ + 'merchant_id' => $merchantId, + 'plan_id' => null, + 'site_subscription_id' => null, + 'created_by_admin_id' => $platformAdminId ?: null, + 'order_no' => 'PO_DASH_BMPA_BLOCKED_RENEWAL_MISSING_SUB_0003', + 'order_type' => 'renewal', + 'status' => 'pending', + 'payment_status' => 'unpaid', + 'payable_amount' => 9, + 'paid_amount' => 0, + 'meta' => [], + ]); + + Cache::flush(); + + $res = $this->get('/admin'); + $res->assertOk(); + + // 只应统计 1 条真正可 BMPA 处理的订单 + $res->assertSee('可BMPA处理(1)'); + $res->assertDontSee('可BMPA处理(3)'); + $res->assertDontSee('可BMPA处理(2)'); + } +}