From 485a8a639df8b4003c5324f64e5bc351ba09e89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=9D=E5=8D=9C?= Date: Mon, 16 Mar 2026 23:33:10 +0800 Subject: [PATCH] chore(governance): block batch BMPA when refund_status=has --- .../Admin/PlatformOrderController.php | 5 +++ app/Support/PlatformOrderToolsGuard.php | 4 ++ ...vateShouldBlockWhenRefundStatusHasTest.php | 37 ++++++++++++++++++ ...onShouldDisableWhenRefundStatusHasTest.php | 38 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 tests/Feature/AdminPlatformOrderBatchMarkPaidAndActivateShouldBlockWhenRefundStatusHasTest.php create mode 100644 tests/Feature/AdminPlatformOrderIndexBatchBmpaButtonShouldDisableWhenRefundStatusHasTest.php diff --git a/app/Http/Controllers/Admin/PlatformOrderController.php b/app/Http/Controllers/Admin/PlatformOrderController.php index 18a377a..f071585 100644 --- a/app/Http/Controllers/Admin/PlatformOrderController.php +++ b/app/Http/Controllers/Admin/PlatformOrderController.php @@ -1653,6 +1653,11 @@ class PlatformOrderController extends Controller return redirect()->back()->with('warning', '当前筛选包含「订阅同步治理」相关条件(同步状态/同步失败/失败原因/只看可同步等)。本动作仅用于推进待处理未支付订单,请先清空同步治理筛选后再执行。'); } + // 治理优先:若当前筛选包含「有退款」集合,则不允许直接批量推进 + if ((string) ($filters['refund_status'] ?? '') === 'has') { + return redirect()->back()->with('warning', '当前筛选为「有退款」订单集合。为避免带退款订单被批量推进,请先完成退款治理(核对退款回执/修正状态)后再执行批量标记支付并生效。'); + } + // 治理优先:若当前筛选已包含“对账不一致/退款不一致”等治理集合,则先治理再执行批量推进 if ((string) ($filters['reconcile_mismatch'] ?? '') === '1' || (string) ($filters['refund_inconsistent'] ?? '') === '1') { return redirect()->back()->with('warning', '当前筛选包含「对账不一致/退款不一致」治理集合,请先完成回执/退款治理后,再执行批量标记支付并生效。'); diff --git a/app/Support/PlatformOrderToolsGuard.php b/app/Support/PlatformOrderToolsGuard.php index 1f9f71d..13a033b 100644 --- a/app/Support/PlatformOrderToolsGuard.php +++ b/app/Support/PlatformOrderToolsGuard.php @@ -101,6 +101,10 @@ class PlatformOrderToolsGuard return '当前筛选包含「订阅同步治理」相关条件(同步状态/同步失败/失败原因等)。BMPA 动作仅用于推进待处理未支付订单,请先清空同步治理筛选后再执行。'; } + if ((string) ($filters['refund_status'] ?? '') === 'has') { + return '当前集合为「有退款」:为避免带退款订单被批量推进,请先完成退款治理(核对退款回执/修正状态)后再执行 BMPA。'; + } + if (((string) ($filters['reconcile_mismatch'] ?? '') === '1') || ((string) ($filters['refund_inconsistent'] ?? '') === '1')) { return '当前集合包含「对账不一致/退款不一致」治理集合:建议先完成回执/退款治理后再批量推进。'; } diff --git a/tests/Feature/AdminPlatformOrderBatchMarkPaidAndActivateShouldBlockWhenRefundStatusHasTest.php b/tests/Feature/AdminPlatformOrderBatchMarkPaidAndActivateShouldBlockWhenRefundStatusHasTest.php new file mode 100644 index 0000000..db342c3 --- /dev/null +++ b/tests/Feature/AdminPlatformOrderBatchMarkPaidAndActivateShouldBlockWhenRefundStatusHasTest.php @@ -0,0 +1,37 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_batch_mark_paid_and_activate_should_block_when_refund_status_has_present(): void + { + $this->loginAsPlatformAdmin(); + + $res = $this->post('/admin/platform-orders/batch-mark-paid-and-activate', [ + 'scope' => 'filtered', + 'status' => 'pending', + 'payment_status' => 'unpaid', + 'refund_status' => 'has', + 'limit' => 50, + ]); + + $res->assertRedirect(); + $res->assertSessionHas('warning'); + } +} diff --git a/tests/Feature/AdminPlatformOrderIndexBatchBmpaButtonShouldDisableWhenRefundStatusHasTest.php b/tests/Feature/AdminPlatformOrderIndexBatchBmpaButtonShouldDisableWhenRefundStatusHasTest.php new file mode 100644 index 0000000..457728f --- /dev/null +++ b/tests/Feature/AdminPlatformOrderIndexBatchBmpaButtonShouldDisableWhenRefundStatusHasTest.php @@ -0,0 +1,38 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_batch_bmpa_button_should_disable_when_refund_status_has_even_if_pending_unpaid(): void + { + $this->loginAsPlatformAdmin(); + + // pending+unpaid 是 BMPA 的基本口径,但 refund_status=has 表示存在退款轨迹,应先治理再推进。 + $res = $this->get('/admin/platform-orders?status=pending&payment_status=unpaid&refund_status=has'); + $res->assertOk(); + + $html = (string) $res->getContent(); + + $this->assertStringContainsString('批量标记支付并生效(含订阅同步)(当前筛选范围)', $html); + $this->assertStringContainsString('data-role="batch-bmpa-blocked-hint"', $html); + $this->assertStringContainsString('有退款', $html); + + $this->assertTrue(str_contains($html, 'type="submit" disabled') || str_contains($html, 'disabled="disabled"')); + } +}