feat(platform): 新增对外平台入口 /platform(首页+套餐展示)
This commit is contained in:
31
app/Http/Controllers/Front/PlatformController.php
Normal file
31
app/Http/Controllers/Front/PlatformController.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Front;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Plan;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class PlatformController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request): View
|
||||||
|
{
|
||||||
|
return view('platform.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function plans(Request $request): View
|
||||||
|
{
|
||||||
|
// 对外展示口径:仅展示“已发布 + 启用中”的套餐,避免未准备好的套餐被外部看到。
|
||||||
|
$plans = Plan::query()
|
||||||
|
->where('status', 'active')
|
||||||
|
->whereNotNull('published_at')
|
||||||
|
->orderBy('sort')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('platform.plans', [
|
||||||
|
'plans' => $plans,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
resources/views/platform/index.blade.php
Normal file
45
resources/views/platform/index.blade.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SaaSShop|SaaS 电商系统</title>
|
||||||
|
<style>
|
||||||
|
body{font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif; margin:0; background:#f6f7fb; color:#111827;}
|
||||||
|
.wrap{max-width: 980px; margin:0 auto; padding:24px;}
|
||||||
|
.card{background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:20px;}
|
||||||
|
.muted{color:#6b7280; font-size:14px; line-height:1.6;}
|
||||||
|
.grid{display:grid; grid-template-columns: 1fr 1fr; gap:16px; margin-top:16px;}
|
||||||
|
.btn{display:inline-block; padding:10px 14px; border-radius:8px; border:1px solid #111827; text-decoration:none; color:#111827; font-weight:600;}
|
||||||
|
.btn-primary{background:#111827; color:#fff;}
|
||||||
|
@media (max-width: 720px){.grid{grid-template-columns:1fr}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="card">
|
||||||
|
<h1 style="margin:0 0 8px 0;">SaaSShop|SaaS 电商系统</h1>
|
||||||
|
<div class="muted">这是 SaaSShop 的对外介绍与开通入口(前期先做 A:站点开通型)。B(license 授权码)后续在其它端能力更完整后再接入。</div>
|
||||||
|
<div style="margin-top:14px; display:flex; gap:10px; flex-wrap:wrap;">
|
||||||
|
<a class="btn btn-primary" href="/platform/plans">查看套餐与功能</a>
|
||||||
|
<a class="btn" href="/admin/login">进入总台管理(运营/治理)</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card">
|
||||||
|
<h3 style="margin:0 0 8px 0;">四端结构</h3>
|
||||||
|
<div class="muted">
|
||||||
|
总台管理(平台治理/收费) → 站点后台(站点管理员) → 商家后台(商家运营) → 买家端(交易体验)。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3 style="margin:0 0 8px 0;">当前优先级</h3>
|
||||||
|
<div class="muted">
|
||||||
|
先把“套餐 → 订阅 → 平台订单 → 生效/同步/治理”跑成收费闭环;对外平台先做到可介绍、可看套餐、可引导开通。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
59
resources/views/platform/plans.blade.php
Normal file
59
resources/views/platform/plans.blade.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SaaSShop|套餐与功能</title>
|
||||||
|
<style>
|
||||||
|
body{font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif; margin:0; background:#f6f7fb; color:#111827;}
|
||||||
|
.wrap{max-width: 980px; margin:0 auto; padding:24px;}
|
||||||
|
.top{display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;}
|
||||||
|
.muted{color:#6b7280; font-size:14px; line-height:1.6;}
|
||||||
|
.grid{display:grid; grid-template-columns: repeat(3, 1fr); gap:14px; margin-top:16px;}
|
||||||
|
.card{background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:18px;}
|
||||||
|
.btn{display:inline-block; padding:8px 12px; border-radius:8px; border:1px solid #111827; text-decoration:none; color:#111827; font-weight:600;}
|
||||||
|
.btn-primary{background:#111827; color:#fff;}
|
||||||
|
.badge{display:inline-block; font-size:12px; padding:2px 8px; border-radius:999px; border:1px solid #e5e7eb; color:#374151;}
|
||||||
|
@media (max-width: 980px){.grid{grid-template-columns:1fr 1fr}}
|
||||||
|
@media (max-width: 720px){.grid{grid-template-columns:1fr}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="top">
|
||||||
|
<div>
|
||||||
|
<h1 style="margin:0 0 6px 0;">套餐与功能</h1>
|
||||||
|
<div class="muted">仅展示「已发布 + 启用中」套餐。开通入口(A:站点开通型)将优先在此页面逐步接入。</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; gap:10px;">
|
||||||
|
<a class="btn" href="/platform">返回首页</a>
|
||||||
|
<a class="btn" href="/admin/plans">运营管理套餐</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
@forelse($plans as $p)
|
||||||
|
<div class="card">
|
||||||
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:10px;">
|
||||||
|
<h3 style="margin:0;">{{ $p->name }}</h3>
|
||||||
|
<span class="badge">{{ $p->billing_cycle }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="muted" style="margin-top:6px;">套餐编码:{{ $p->code }}</div>
|
||||||
|
<div style="margin-top:10px; font-size:22px; font-weight:700;">¥{{ number_format((float) $p->price, 2) }}</div>
|
||||||
|
@if($p->description)
|
||||||
|
<div class="muted" style="margin-top:8px;">{{ $p->description }}</div>
|
||||||
|
@endif
|
||||||
|
<div style="margin-top:12px;">
|
||||||
|
<a class="btn btn-primary" href="/admin/platform-orders/create?plan_id={{ $p->id }}">我要开通/下单(暂由运营处理)</a>
|
||||||
|
</div>
|
||||||
|
<div class="muted" style="margin-top:8px;">提示:前期先跑通收费闭环与治理;自助开通会在后续版本接入。</div>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div class="card" style="grid-column: 1 / -1;">
|
||||||
|
<div class="muted">当前暂无可对外展示的套餐(需满足:启用中 + 已发布)。</div>
|
||||||
|
</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -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\MerchantController as AdminMerchantController;
|
||||||
use App\Http\Controllers\Admin\SiteSubscriptionController;
|
use App\Http\Controllers\Admin\SiteSubscriptionController;
|
||||||
use App\Http\Controllers\Front\H5Controller;
|
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\AuthController as MerchantAdminAuthController;
|
||||||
use App\Http\Controllers\MerchantAdmin\DashboardController as MerchantDashboardController;
|
use App\Http\Controllers\MerchantAdmin\DashboardController as MerchantDashboardController;
|
||||||
use App\Http\Controllers\MerchantAdmin\OrderController as MerchantOrderController;
|
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/mp', [MpController::class, 'index']);
|
||||||
Route::get('/wechat/mini', [MiniProgramController::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 () {
|
Route::get('/health', function () {
|
||||||
$dbOk = false;
|
$dbOk = false;
|
||||||
$dbMsg = '未检测';
|
$dbMsg = '未检测';
|
||||||
|
|||||||
65
tests/Feature/PublicPlatformPlansPageTest.php
Normal file
65
tests/Feature/PublicPlatformPlansPageTest.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Plan;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class PublicPlatformPlansPageTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_public_platform_plans_page_should_only_show_active_and_published_plans(): void
|
||||||
|
{
|
||||||
|
Plan::query()->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');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user