diff --git a/resources/views/admin/platform_orders/index.blade.php b/resources/views/admin/platform_orders/index.blade.php index 0e92827..f56c3fb 100644 --- a/resources/views/admin/platform_orders/index.blade.php +++ b/resources/views/admin/platform_orders/index.blade.php @@ -77,10 +77,20 @@ // 快捷筛选:尽量保留当前筛选(站点/套餐/订阅ID/back 等),仅覆盖目标筛选字段,并清空 page。 $buildQuickFilterUrl = function (array $overrides) { $path = '/' . ltrim(request()->path(), '/'); - $q = request()->query(); - // 强制清页码,避免“切筛选但还停留在老 page=xxx”导致空页 - unset($q['page']); + // 快捷筛选的设计原则: + // - 保留“上下文”字段(站点/套餐/订阅/back/关键词) + // - 清理其它可能互斥/叠加导致空结果的筛选字段(例如 syncable_only/reconcile_mismatch 等) + // - 并且强制清空 page,避免落到空页 + $contextKeys = [ + 'merchant_id' => 1, + 'plan_id' => 1, + 'site_subscription_id' => 1, + 'back' => 1, + 'keyword' => 1, + ]; + + $q = array_intersect_key(request()->query(), $contextKeys); foreach ($overrides as $k => $v) { if ($v === null) { diff --git a/tests/Feature/AdminPlatformOrderIndexQuickFilterLinksKeepContextTest.php b/tests/Feature/AdminPlatformOrderIndexQuickFilterLinksKeepContextTest.php index 9ef39d3..b759cd6 100644 --- a/tests/Feature/AdminPlatformOrderIndexQuickFilterLinksKeepContextTest.php +++ b/tests/Feature/AdminPlatformOrderIndexQuickFilterLinksKeepContextTest.php @@ -19,19 +19,57 @@ class AdminPlatformOrderIndexQuickFilterLinksKeepContextTest extends TestCase ])->assertRedirect('/admin'); } - public function test_quick_filter_links_should_keep_context_and_clear_page(): void + public function test_quick_filter_links_should_keep_context_and_drop_other_filters(): void { $this->loginAsPlatformAdmin(); - // 模拟:当前已筛选站点/套餐/订阅ID,并且带 page/back - $res = $this->get('/admin/platform-orders?merchant_id=2&plan_id=3&site_subscription_id=4&page=9&back=%2Fadmin%2Fplans'); + // 模拟:当前已筛选站点/套餐/订阅ID,并且带一堆其它筛选(可能互斥),以及 page/back + $res = $this->get('/admin/platform-orders?merchant_id=2&plan_id=3&site_subscription_id=4&page=9&back=%2Fadmin%2Fplans&syncable_only=1&reconcile_mismatch=1&refund_inconsistent=1&receipt_status=has'); $res->assertOk(); - // 以“待支付”为例:应保留 merchant_id/plan_id/site_subscription_id/back,同时覆盖 payment_status=unpaid,并清除 page - $res->assertSee('/admin/platform-orders?merchant_id=2&plan_id=3&site_subscription_id=4&back=%2Fadmin%2Fplans&payment_status=unpaid', false); - $res->assertDontSee('page=9', false); + $html = (string) $res->getContent(); + + preg_match_all('/href="([^"]+)"/', $html, $m); + $hrefs = $m[1] ?? []; + + // 找到“待支付”的快捷筛选链接:payment_status=unpaid 且不包含 status=pending(避免匹配到“可BMPA处理”) + $unpaidLinks = array_values(array_filter($hrefs, function ($u) { + return str_contains($u, '/admin/platform-orders') + && str_contains($u, 'payment_status=unpaid') + && !str_contains($u, 'status=pending'); + })); + + $this->assertGreaterThanOrEqual(1, count($unpaidLinks)); + + $parts = parse_url($unpaidLinks[0]); + parse_str($parts['query'] ?? '', $q); + + // 应保留上下文:merchant/plan/subscription/back,并覆盖 payment_status + $this->assertSame('2', (string) ($q['merchant_id'] ?? '')); + $this->assertSame('3', (string) ($q['plan_id'] ?? '')); + $this->assertSame('4', (string) ($q['site_subscription_id'] ?? '')); + $this->assertSame('/admin/plans', (string) ($q['back'] ?? '')); + $this->assertSame('unpaid', (string) ($q['payment_status'] ?? '')); + + // 并且:不应携带其它互斥筛选字段,避免叠加导致空结果 + $this->assertArrayNotHasKey('page', $q); + $this->assertArrayNotHasKey('syncable_only', $q); + $this->assertArrayNotHasKey('reconcile_mismatch', $q); + $this->assertArrayNotHasKey('refund_inconsistent', $q); + $this->assertArrayNotHasKey('receipt_status', $q); // “全部”应清空筛选,仅保留 back - $res->assertSee('/admin/platform-orders?back=%2Fadmin%2Fplans', false); + $allLinks = array_values(array_filter($hrefs, function ($u) { + if (!str_starts_with($u, '/admin/platform-orders')) { + return false; + } + + $p = parse_url($u); + parse_str($p['query'] ?? '', $q2); + + return (($q2['back'] ?? null) === '/admin/plans') && (count($q2) === 1); + })); + + $this->assertGreaterThanOrEqual(1, count($allLinks)); } }