diff --git a/app/Http/Controllers/Front/PlatformController.php b/app/Http/Controllers/Front/PlatformController.php
new file mode 100644
index 0000000..d7a3f2e
--- /dev/null
+++ b/app/Http/Controllers/Front/PlatformController.php
@@ -0,0 +1,31 @@
+where('status', 'active')
+ ->whereNotNull('published_at')
+ ->orderBy('sort')
+ ->orderByDesc('id')
+ ->get();
+
+ return view('platform.plans', [
+ 'plans' => $plans,
+ ]);
+ }
+}
diff --git a/resources/views/platform/index.blade.php b/resources/views/platform/index.blade.php
new file mode 100644
index 0000000..e67d719
--- /dev/null
+++ b/resources/views/platform/index.blade.php
@@ -0,0 +1,45 @@
+
+
+
+
+
+ SaaSShop|SaaS 电商系统
+
+
+
+
+
+
SaaSShop|SaaS 电商系统
+
这是 SaaSShop 的对外介绍与开通入口(前期先做 A:站点开通型)。B(license 授权码)后续在其它端能力更完整后再接入。
+
+
+
+
+
+
四端结构
+
+ 总台管理(平台治理/收费) → 站点后台(站点管理员) → 商家后台(商家运营) → 买家端(交易体验)。
+
+
+
+
当前优先级
+
+ 先把“套餐 → 订阅 → 平台订单 → 生效/同步/治理”跑成收费闭环;对外平台先做到可介绍、可看套餐、可引导开通。
+
+
+
+
+
+
diff --git a/resources/views/platform/plans.blade.php b/resources/views/platform/plans.blade.php
new file mode 100644
index 0000000..5ea90a3
--- /dev/null
+++ b/resources/views/platform/plans.blade.php
@@ -0,0 +1,59 @@
+
+
+
+
+
+ SaaSShop|套餐与功能
+
+
+
+
+
+
+
套餐与功能
+
仅展示「已发布 + 启用中」套餐。开通入口(A:站点开通型)将优先在此页面逐步接入。
+
+
+
+
+
+ @forelse($plans as $p)
+
+
+
{{ $p->name }}
+ {{ $p->billing_cycle }}
+
+
套餐编码:{{ $p->code }}
+
¥{{ number_format((float) $p->price, 2) }}
+ @if($p->description)
+
{{ $p->description }}
+ @endif
+
+
提示:前期先跑通收费闭环与治理;自助开通会在后续版本接入。
+
+ @empty
+
+
当前暂无可对外展示的套餐(需满足:启用中 + 已发布)。
+
+ @endforelse
+
+
+
+
diff --git a/routes/web.php b/routes/web.php
index 59e8f3d..09b3a7a 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -11,6 +11,7 @@ use App\Http\Controllers\Admin\ProductController as AdminProductController;
use App\Http\Controllers\Admin\MerchantController as AdminMerchantController;
use App\Http\Controllers\Admin\SiteSubscriptionController;
use App\Http\Controllers\Front\H5Controller;
+use App\Http\Controllers\Front\PlatformController as FrontPlatformController;
use App\Http\Controllers\MerchantAdmin\AuthController as MerchantAdminAuthController;
use App\Http\Controllers\MerchantAdmin\DashboardController as MerchantDashboardController;
use App\Http\Controllers\MerchantAdmin\OrderController as MerchantOrderController;
@@ -36,6 +37,12 @@ Route::get('/h5', [H5Controller::class, 'index']);
Route::get('/wechat/mp', [MpController::class, 'index']);
Route::get('/wechat/mini', [MiniProgramController::class, 'index']);
+// 对外平台(官网/开通入口)- 前期先承接介绍与套餐展示(A:站点开通型优先)
+Route::prefix('platform')->group(function () {
+ Route::get('/', [FrontPlatformController::class, 'index']);
+ Route::get('/plans', [FrontPlatformController::class, 'plans']);
+});
+
Route::get('/health', function () {
$dbOk = false;
$dbMsg = '未检测';
diff --git a/tests/Feature/PublicPlatformPlansPageTest.php b/tests/Feature/PublicPlatformPlansPageTest.php
new file mode 100644
index 0000000..5b0b49b
--- /dev/null
+++ b/tests/Feature/PublicPlatformPlansPageTest.php
@@ -0,0 +1,65 @@
+create([
+ 'code' => 'pub_plan_active_published',
+ 'name' => '对外展示套餐-启用已发布',
+ 'billing_cycle' => 'monthly',
+ 'price' => 99,
+ 'list_price' => 129,
+ 'status' => 'active',
+ 'sort' => 10,
+ 'description' => 'public ok',
+ 'published_at' => now(),
+ ]);
+
+ Plan::query()->create([
+ 'code' => 'pub_plan_active_unpublished',
+ 'name' => '不应展示-启用未发布',
+ 'billing_cycle' => 'monthly',
+ 'price' => 9,
+ 'list_price' => 9,
+ 'status' => 'active',
+ 'sort' => 20,
+ 'description' => 'no',
+ 'published_at' => null,
+ ]);
+
+ Plan::query()->create([
+ 'code' => 'pub_plan_inactive_published',
+ 'name' => '不应展示-停用已发布',
+ 'billing_cycle' => 'monthly',
+ 'price' => 199,
+ 'list_price' => 199,
+ 'status' => 'inactive',
+ 'sort' => 30,
+ 'description' => 'no',
+ 'published_at' => now(),
+ ]);
+
+ $res = $this->get('/platform/plans');
+ $res->assertOk();
+
+ $res->assertSee('对外展示套餐-启用已发布');
+ $res->assertDontSee('不应展示-启用未发布');
+ $res->assertDontSee('不应展示-停用已发布');
+ }
+
+ public function test_public_platform_index_page_should_be_accessible(): void
+ {
+ $this->get('/platform')
+ ->assertOk()
+ ->assertSee('SaaSShop');
+ }
+}