SaaS架构概述与ThinkPHP优势
在云计算时代,多租户SaaS(Software as a Service)平台已成为企业软件交付的主流模式。本文将深入探讨如何使用ThinkPHP 6.1构建一个高性能、可扩展的多租户SaaS平台,实现数据隔离、租户管理和系统扩展的全套解决方案。
系统架构设计
我们的SaaS平台采用分层架构设计,包含以下核心模块:
- 多租户数据隔离引擎
- 动态数据库路由系统
- 租户生命周期管理
- 统一认证授权中心
- 模块化业务组件
环境配置与项目初始化
Composer项目创建
# 创建ThinkPHP 6.1项目
composer create-project topthink/think saas-platform
# 安装多租户扩展
composer require hhxsv5/laravel-s
# 项目目录结构
app/
├── controller/
├── model/
├── middleware/
├── service/
├── common.php
config/
├── database.php
├── tenant.php
public/
routes/
tenant/
├── database/
└── migrations/
多租户配置文件
// config/tenant.php
<?php
return [
// 租户识别方式
'identification' => [
'type' => 'subdomain', // subdomain, header, query
'field' => 'tenant_id',
],
// 数据库连接策略
'database' => [
'strategy' => 'database_per_tenant', // shared, database_per_tenant
'prefix' => 'tenant_',
],
// 租户生命周期
'lifecycle' => [
'trial_days' => 30,
'grace_period' => 7,
],
];
核心中间件实现
租户识别中间件
<?php
namespace appmiddleware;
class TenantIdentification
{
public function handle($request, Closure $next)
{
// 从子域名识别租户
$host = $request->host();
$subdomain = $this->extractSubdomain($host);
if ($subdomain) {
$tenant = appmodelTenant::where('subdomain', $subdomain)->find();
if (!$tenant) {
return json(['error' => '租户不存在'], 404);
}
if (!$tenant->is_active) {
return json(['error' => '租户已被停用'], 403);
}
// 设置当前租户上下文
app()->bind('tenant', $tenant);
$request->tenant = $tenant;
}
return $next($request);
}
private function extractSubdomain($host)
{
$parts = explode('.', $host);
if (count($parts) > 2) {
return $parts[0];
}
return null;
}
}
数据库路由中间件
<?php
namespace appmiddleware;
class DatabaseRouter
{
public function handle($request, Closure $next)
{
$tenant = $request->tenant;
if ($tenant) {
// 动态切换数据库连接
$this->switchDatabase($tenant);
}
return $next($request);
}
private function switchDatabase($tenant)
{
$config = [
// 默认数据库配置
'type' => 'mysql',
'hostname' => env('database.hostname', '127.0.0.1'),
'database' => 'tenant_' . $tenant->id,
'username' => env('database.username', 'root'),
'password' => env('database.password', ''),
'hostport' => env('database.hostport', '3306'),
'charset' => 'utf8mb4',
'prefix' => '',
];
// 设置租户数据库连接
thinkfacadeDb::connect($config, 'tenant');
}
}
数据模型设计
租户主模型
<?php
namespace appmodel;
use thinkModel;
class Tenant extends Model
{
protected $autoWriteTimestamp = true;
// 定义租户状态
const STATUS_ACTIVE = 1;
const STATUS_SUSPENDED = 0;
const STATUS_TRIAL = 2;
public function users()
{
return $this->hasMany(User::class);
}
public function subscriptions()
{
return $this->hasMany(Subscription::class);
}
public function getCurrentSubscriptionAttribute()
{
return $this->subscriptions()
->where('status', Subscription::STATUS_ACTIVE)
->order('id', 'desc')
->find();
}
public function scopeActive($query)
{
return $query->where('is_active', self::STATUS_ACTIVE);
}
public function isOnTrial()
{
return $this->trial_ends_at && $this->trial_ends_at > time();
}
}
租户感知的基础模型
<?php
namespace appmodel;
use thinkModel;
class TenantAwareModel extends Model
{
protected $tenantField = 'tenant_id';
protected static function onBeforeInsert(Model $model)
{
$tenant = app('tenant');
if ($tenant) {
$model->setAttr('tenant_id', $tenant->id);
}
}
public function scopeForTenant($query, $tenantId = null)
{
$tenantId = $tenantId ?: (app('tenant')->id ?? null);
if ($tenantId) {
return $query->where('tenant_id', $tenantId);
}
return $query;
}
public function tenant()
{
return $this->belongsTo(Tenant::class);
}
}
服务层架构
租户管理服务
<?php
namespace appservice;
use appmodelTenant;
use thinkfacadeDb;
class TenantService
{
public function createTenant($data)
{
Db::startTrans();
try {
// 创建租户记录
$tenant = Tenant::create([
'name' => $data['name'],
'subdomain' => $data['subdomain'],
'email' => $data['email'],
'trial_ends_at' => strtotime('+30 days'),
]);
// 创建租户数据库
$this->createTenantDatabase($tenant);
// 执行租户数据库迁移
$this->runTenantMigrations($tenant);
// 创建默认管理员
$this->createDefaultAdmin($tenant, $data['admin_password']);
Db::commit();
return $tenant;
} catch (Exception $e) {
Db::rollback();
throw $e;
}
}
private function createTenantDatabase(Tenant $tenant)
{
$databaseName = 'tenant_' . $tenant->id;
$sql = "CREATE DATABASE IF NOT EXISTS `{$databaseName}`
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
Db::execute($sql);
}
private function runTenantMigrations(Tenant $tenant)
{
// 切换到租户数据库执行迁移
$originalConfig = Db::getConfig();
try {
$this->switchToTenantDatabase($tenant);
// 执行租户级别的数据表创建
$migrations = [
'create_users_table',
'create_products_table',
'create_orders_table',
// ... 其他业务表
];
foreach ($migrations as $migration) {
$this->runMigration($migration);
}
} finally {
// 恢复默认数据库连接
Db::connect($originalConfig);
}
}
}
控制器实现
租户注册控制器
<?php
namespace appcontroller;
use appserviceTenantService;
use thinkfacadeValidate;
class TenantController
{
protected $tenantService;
public function __construct(TenantService $tenantService)
{
$this->tenantService = $tenantService;
}
public function register()
{
$data = request()->post();
// 验证输入
$validate = Validate::rule([
'name' => 'require|max:100',
'subdomain' => 'require|alphaDash|unique:tenant,subdomain',
'email' => 'require|email',
'admin_password' => 'require|min:6',
]);
if (!$validate->check($data)) {
return json(['error' => $validate->getError()], 422);
}
try {
$tenant = $this->tenantService->createTenant($data);
return json([
'message' => '租户注册成功',
'tenant' => $tenant->toArray(),
'login_url' => 'http://' . $tenant->subdomain . '.' . request()->rootDomain()
]);
} catch (Exception $e) {
return json(['error' => '注册失败: ' . $e->getMessage()], 500);
}
}
public function updateSubscription()
{
$tenant = app('tenant');
$plan = request()->post('plan');
try {
$subscription = $this->tenantService
->updateSubscription($tenant, $plan);
return json([
'message' => '订阅更新成功',
'subscription' => $subscription
]);
} catch (Exception $e) {
return json(['error' => $e->getMessage()], 500);
}
}
}
业务数据控制器
<?php
namespace appcontroller;
use appmodelProduct;
class ProductController
{
public function index()
{
$page = request()->param('page', 1);
$limit = request()->param('limit', 15);
$products = Product::forTenant()
->with(['category'])
->order('id', 'desc')
->paginate([
'list_rows' => $limit,
'page' => $page,
]);
return json([
'data' => $products->items(),
'total' => $products->total(),
'current_page' => $products->currentPage(),
]);
}
public function create()
{
$data = request()->post();
$product = Product::create($data);
return json([
'message' => '产品创建成功',
'product' => $product
]);
}
}
数据库迁移与种子数据
系统表迁移
<?php
// database/migrations/20240101000000_create_tenants_table.php
use thinkmigrationMigrator;
use thinkmigrationdbColumn;
class CreateTenantsTable extends Migrator
{
public function change()
{
$table = $this->table('tenants');
$table->addColumn('name', 'string', ['limit' => 100])
->addColumn('subdomain', 'string', ['limit' => 50])
->addColumn('email', 'string', ['limit' => 255])
->addColumn('is_active', 'boolean', ['default' => true])
->addColumn('trial_ends_at', 'integer', ['null' => true])
->addColumn('created_at', 'integer')
->addColumn('updated_at', 'integer')
->addIndex(['subdomain'], ['unique' => true])
->create();
}
}
租户业务表迁移
<?php
// tenant/migrations/20240101000001_create_products_table.php
class CreateProductsTable extends Migrator
{
public function change()
{
$table = $this->table('products');
$table->addColumn('tenant_id', 'integer')
->addColumn('name', 'string', ['limit' => 100])
->addColumn('description', 'text', ['null' => true])
->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])
->addColumn('stock', 'integer', ['default' => 0])
->addColumn('is_active', 'boolean', ['default' => true])
->addColumn('created_at', 'integer')
->addColumn('updated_at', 'integer')
->addIndex(['tenant_id'])
->create();
}
}
路由配置
多租户路由定义
<?php
// route/tenant.php
use thinkfacadeRoute;
// 租户子域名路由组
Route::domain(':subdomain', function () {
// 租户认证路由
Route::group('auth', function () {
Route::post('login', 'tenant/Auth/login');
Route::post('logout', 'tenant/Auth/logout');
Route::post('refresh', 'tenant/Auth/refresh');
});
// 租户业务路由
Route::group('api', function () {
Route::get('products', 'tenant/Product/index');
Route::post('products', 'tenant/Product/create');
Route::put('products/:id', 'tenant/Product/update');
Route::delete('products/:id', 'tenant/Product/delete');
Route::get('orders', 'tenant/Order/index');
Route::post('orders', 'tenant/Order/create');
})->middleware(['appmiddlewareTenantJwtAuth']);
})->middleware(['appmiddlewareTenantIdentification']);
// 系统管理路由
Route::group('system', function () {
Route::post('tenants/register', 'Tenant/register');
Route::put('tenants/:id', 'Tenant/update');
Route::get('tenants', 'Tenant/index');
})->middleware(['appmiddlewareSystemAuth']);
事件与监听器
租户事件定义
<?php
namespace appevent;
class TenantCreated
{
public $tenant;
public function __construct($tenant)
{
$this->tenant = $tenant;
}
}
class TenantSuspended
{
public $tenant;
public function __construct($tenant)
{
$this->tenant = $tenant;
}
}
// 事件监听器
class SendTenantWelcomeNotification
{
public function handle(TenantCreated $event)
{
$tenant = $event->tenant;
// 发送欢迎邮件
$mailer = new appserviceMailService();
$mailer->sendWelcomeEmail($tenant);
// 创建初始数据
$seeder = new appserviceTenantSeeder();
$seeder->seedInitialData($tenant);
}
}
// 事件注册
// app/event.php
return [
'listen' => [
'TenantCreated' => [
'applistenerSendTenantWelcomeNotification',
'applistenerCreateInitialData',
],
'TenantSuspended' => [
'applistenerNotifyTenantSuspension',
],
],
];
性能优化策略
- 数据库连接池:使用连接池管理租户数据库连接
- Redis缓存:缓存租户配置和频繁访问的数据
- 查询优化:为租户数据表添加合适索引
- 懒加载:按需加载租户数据库连接
- CDN加速:静态资源使用CDN分发
安全防护措施
- 数据隔离:确保租户间数据完全隔离
- SQL注入防护:使用参数绑定和ORM
- XSS防护:输出数据自动转义
- CSRF保护:表单请求令牌验证
- 速率限制:API访问频率控制
部署与监控
Docker部署配置
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "80:80"
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=saas_platform
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
mysql_data:
总结与最佳实践
通过本教程,我们构建了一个基于ThinkPHP 6.1的完整多租户SaaS平台。这个架构展示了如何实现数据隔离、租户管理和系统扩展的核心功能,为构建企业级SaaS应用提供了可靠的技术基础。
核心技术创新:
- 动态数据库路由实现数据隔离
- 中间件链完成租户识别和路由
- 服务层封装复杂业务逻辑
- 事件系统处理租户生命周期
- 模块化设计支持功能扩展
这种架构设计不仅保证了系统的安全性和性能,还为未来的功能扩展和维护提供了良好的基础,是构建现代SaaS平台的理想技术选择。

