From b27fd67347d992b2e89eeca547968914a0981d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=9D=E5=8D=9C?= Date: Tue, 10 Mar 2026 21:56:14 +0000 Subject: [PATCH] =?UTF-8?q?feat(plans):=20=E7=A9=BA=E5=BA=93=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E5=88=9D=E5=A7=8B=E5=8C=96=E9=BB=98=E8=AE=A4=E5=A5=97?= =?UTF-8?q?=E9=A4=90=EF=BC=88=E5=B8=A6=E6=8A=A4=E6=A0=8F=E4=B8=8E=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Admin/PlanController.php | 43 ++++++++++++++ resources/views/admin/plans/index.blade.php | 22 ++++--- routes/web.php | 2 + tests/Feature/AdminPlanSeedDefaultsTest.php | 58 +++++++++++++++++++ 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 tests/Feature/AdminPlanSeedDefaultsTest.php diff --git a/app/Http/Controllers/Admin/PlanController.php b/app/Http/Controllers/Admin/PlanController.php index 6497714..0f59fde 100644 --- a/app/Http/Controllers/Admin/PlanController.php +++ b/app/Http/Controllers/Admin/PlanController.php @@ -228,6 +228,49 @@ class PlanController extends Controller }); } + public function seedDefaults(Request $request): RedirectResponse + { + $this->ensurePlatformAdmin($request); + + // 安全护栏:仅当当前库没有任何套餐时才允许一键初始化 + $existingCount = Plan::query()->count(); + if ($existingCount > 0) { + return redirect()->back()->with('error', '当前已有套餐(' . $existingCount . ' 条),为避免污染运营数据,已阻止一键初始化。'); + } + + $now = now(); + $defaults = [ + [ + 'code' => 'starter_monthly', + 'name' => '基础版(月付)', + 'billing_cycle' => 'monthly', + 'price' => 99, + 'list_price' => 129, + 'status' => 'active', + 'sort' => 10, + 'description' => '适合刚开站的试运营阶段,可用于 Demo 场景。', + 'published_at' => $now, + ], + [ + 'code' => 'pro_yearly', + 'name' => '专业版(年付)', + 'billing_cycle' => 'yearly', + 'price' => 1999, + 'list_price' => 2599, + 'status' => 'active', + 'sort' => 20, + 'description' => '面向成长型站点,后续搭配授权项配置。', + 'published_at' => $now, + ], + ]; + + foreach ($defaults as $row) { + Plan::query()->create($row); + } + + return redirect('/admin/plans')->with('success', '已初始化默认套餐:' . count($defaults) . ' 条。'); + } + protected function statusLabels(): array { return [ diff --git a/resources/views/admin/plans/index.blade.php b/resources/views/admin/plans/index.blade.php index 7f882cb..365c08b 100644 --- a/resources/views/admin/plans/index.blade.php +++ b/resources/views/admin/plans/index.blade.php @@ -11,13 +11,21 @@

工具

-
- - - - - -
+
+
+ + + + + +
+ +
+ @csrf + +
安全护栏:当库中已存在套餐时会自动阻止,避免污染运营数据。
+
+
diff --git a/routes/web.php b/routes/web.php index a963b83..3c9e725 100644 --- a/routes/web.php +++ b/routes/web.php @@ -121,6 +121,8 @@ Route::prefix('admin')->group(function () { Route::get('/plans/export', [PlanController::class, 'export']); Route::get('/plans/create', [PlanController::class, 'create']); Route::post('/plans', [PlanController::class, 'store']); + // 注意:必须放在 /plans/{plan} 之前,避免被参数路由吞掉导致 404 + Route::post('/plans/seed-defaults', [PlanController::class, 'seedDefaults']); Route::get('/plans/{plan}/edit', [PlanController::class, 'edit']); Route::post('/plans/{plan}', [PlanController::class, 'update']); Route::post('/plans/{plan}/set-status', [PlanController::class, 'setStatus']); diff --git a/tests/Feature/AdminPlanSeedDefaultsTest.php b/tests/Feature/AdminPlanSeedDefaultsTest.php new file mode 100644 index 0000000..3cc05a6 --- /dev/null +++ b/tests/Feature/AdminPlanSeedDefaultsTest.php @@ -0,0 +1,58 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_platform_admin_can_seed_default_plans_when_empty(): void + { + $this->loginAsPlatformAdmin(); + + // 清空 seed 带来的默认套餐(测试口径:模拟空库) + Plan::query()->delete(); + $this->assertSame(0, Plan::query()->count()); + + $this->post('/admin/plans/seed-defaults') + ->assertRedirect('/admin/plans'); + + $this->assertSame(2, Plan::query()->count()); + $this->assertNotNull(Plan::query()->where('code', 'starter_monthly')->first()); + $this->assertNotNull(Plan::query()->where('code', 'pro_yearly')->first()); + + $this->get('/admin/plans') + ->assertOk() + ->assertSee('基础版(月付)') + ->assertSee('专业版(年付)'); + } + + public function test_seed_default_plans_is_blocked_when_plans_exist(): void + { + $this->loginAsPlatformAdmin(); + + $this->assertTrue(Plan::query()->count() > 0); + + $this->post('/admin/plans/seed-defaults') + ->assertRedirect(); + + // 不应新增到 2 条以上;且仍能打开列表 + $this->get('/admin/plans') + ->assertOk() + ->assertSee('套餐管理'); + } +}