hyperf 多租户 SaaS 基础框架 开源完整流程(从 0 到持续维护)==写开源项目全流程
一套 Hyperf 多租户 SaaS 基础框架的开源落地方案覆盖 从0搭建到持续维护并给出可直接改造的核心代码骨架。 ---1)先定多租户策略强烈建议这样起步 先用 共享库 行级隔离tenant_id后续再支持“独立库租户” esc to interrupt - 起步快、成本低、运维简单 - 业务模型统一适合开源模板 - 后续可按租户等级切换到独立库VIP 租户 ---2)项目结构建议 saas-hyperf/ ├─ app/ │ ├─ Context/ │ │ └─ TenantContext.php │ ├─ Middleware/ │ │ └─ TenantResolveMiddleware.php │ ├─ Model/ │ │ ├─ Tenant.php │ │ ├─ User.php │ │ └─ BaseTenantModel.php │ ├─ Service/ │ │ └─ TenantService.php │ ├─ Controller/ │ │ ├─ AuthController.php │ │ └─ ProjectController.php │ └─ Exception/Handler/ ├─ config/autoload/ │ ├─ databases.php │ ├─ middlewares.php │ └─ routes.php ├─ migrations/ ├─ tests/ ├─ .github/workflows/ci.yml ├─ docker-compose.yml ├─ CLAUDE.md ├─ LICENSE └─ README.md ---3)从0初始化命令composercreate-project hyperf/hyperf-skeleton saas-hyperfcdsaas-hyperf# 常用依赖按你项目版本调整composerrequire hyperf/db-connection hyperf/databasecomposerrequire hyperf/redis hyperf/validationcomposerrequire firebase/php-jwtcomposerrequire--devphpunit/phpunit friendsofphp/php-cs-fixer ---4)核心代码租户上下文 自动隔离4.1TenantContext协程安全 app/Context/TenantContext.php?php declare(strict_types1);namespace App\Context;use Hyperf\Context\Context;class TenantContext{private const KEY_IDtenant.id;private const KEY_CODEtenant.code;public staticfunctionset(int$tenantId, string$tenantCode): void{Context::set(self::KEY_ID,$tenantId);Context::set(self::KEY_CODE,$tenantCode);}public staticfunctionid(): ?int{returnContext::get(self::KEY_ID);}public staticfunctioncode(): ?string{returnContext::get(self::KEY_CODE);}public staticfunctionclear(): void{Context::set(self::KEY_ID, null);Context::set(self::KEY_CODE, null);}}---4.2租户解析中间件子域名 / Header app/Middleware/TenantResolveMiddleware.php?php declare(strict_types1);namespace App\Middleware;use App\Context\TenantContext;use App\Model\Tenant;use Hyperf\Di\Annotation\Inject;use Hyperf\HttpServer\Contract\RequestInterface;use Psr\Http\Message\ResponseInterface;use Psr\Http\Server\MiddlewareInterface;use Psr\Http\Server\RequestHandlerInterface;use Hyperf\HttpServer\Response;class TenantResolveMiddleware implements MiddlewareInterface{#[Inject]protected RequestInterface$request;#[Inject]protected Response$response;publicfunctionprocess($request, RequestHandlerInterface$handler): ResponseInterface{$host$this-request-getUri()-getHost();$tenantCode$this-request-header(X-Tenant-Code)??$this-parseSubdomain($host);if(!$tenantCode){return$this-response-json([code40001,messagetenant required])-withStatus(400);}/** var Tenant|null$tenant*/$tenantTenant::query()-where(code,$tenantCode)-where(status,1)-first();if(!$tenant){return$this-response-json([code40004,messagetenant not found])-withStatus(404);}TenantContext::set((int)$tenant-id,(string)$tenant-code);try{return$handler-handle($request);}finally{TenantContext::clear();}}privatefunctionparseSubdomain(string$host): ?string{// 例如 tenant1.example.com$partsexplode(.,$host);returncount($parts)3?$parts[0]:null;}}---4.3基础租户模型自动 tenant_id 过滤 app/Model/BaseTenantModel.php?php declare(strict_types1);namespace App\Model;use App\Context\TenantContext;use Hyperf\DbConnection\Model\Model;use Hyperf\Database\Model\Builder;abstract class BaseTenantModel extends Model{protected staticfunctionbooted(): void{static::addGlobalScope(tenant_scope,function(Builder$builder){$tenantIdTenantContext::id();if($tenantId!null){$builder-where($builder-getModel()-getTable()..tenant_id,$tenantId);}});static::creating(function(Model$model){if(empty($model-tenant_id)TenantContext::id()!null){$model-tenant_idTenantContext::id();}});}}业务模型示例 app/Model/Project.php?php declare(strict_types1);namespace App\Model;class Project extends BaseTenantModel{protected ?string$tableprojects;protected array$fillable[tenant_id,name,owner_id,status,];}---5)关键数据表迁移 migrations/2026_01_01_000001_create_core_tables.php?php use Hyperf\Database\Schema\Schema;use Hyperf\Database\Schema\Blueprint;use Hyperf\Database\Migrations\Migration;returnnew class extends Migration{publicfunctionup(): void{Schema::create(tenants,function(Blueprint$table){$table-bigIncrements(id);$table-string(name,100);$table-string(code,64)-unique();// 子域名/租户唯一编码$table-tinyInteger(status)-default(1);$table-timestamps();});Schema::create(users,function(Blueprint$table){$table-bigIncrements(id);$table-unsignedBigInteger(tenant_id);$table-string(email,191);$table-string(password,255);$table-string(name,100);$table-timestamps();$table-unique([tenant_id,email]);$table-index([tenant_id]);});Schema::create(projects,function(Blueprint$table){$table-bigIncrements(id);$table-unsignedBigInteger(tenant_id);$table-unsignedBigInteger(owner_id);$table-string(name,191);$table-tinyInteger(status)-default(1);$table-timestamps();$table-index([tenant_id,owner_id]);});}publicfunctiondown(): void{Schema::dropIfExists(projects);Schema::dropIfExists(users);Schema::dropIfExists(tenants);}};---6)路由与中间件注册 config/autoload/middlewares.php?phpreturn[http[\App\Middleware\TenantResolveMiddleware::class,],];config/autoload/routes.php?php use Hyperf\HttpServer\Router\Router;Router::addGroup(/api,function(){Router::post(/auth/login,[App\Controller\AuthController::class,login]);Router::get(/projects,[App\Controller\ProjectController::class,index]);Router::post(/projects,[App\Controller\ProjectController::class,store]);});---7)控制器示例天然按 tenant 隔离 app/Controller/ProjectController.php?php declare(strict_types1);namespace App\Controller;use App\Model\Project;use Hyperf\HttpServer\Contract\RequestInterface;use Hyperf\HttpServer\Contract\ResponseInterface;class ProjectController{publicfunctionindex(ResponseInterface$response){$listProject::query()-orderByDesc(id)-limit(20)-get();return$response-json([code0,data$list]);}publicfunctionstore(RequestInterface$request, ResponseInterface$response){$data$request-all();$projectProject::query()-create([name$data[name]??,owner_id(int)($data[owner_id]??0),status1,]);return$response-json([code0,data$project]);}}---8)开源必须补齐的工程化8.1Docker本地一键跑 docker-compose.yml 至少包含 - apphyperf - mysql - redis8.2CIGitHub Actions .github/workflows/ci.yml - PHP lint - 单元测试 - 静态分析phpstan/psalm 任选 - 格式检查php-cs-fixer8.3开源治理 - LICENSE推荐 MIT / Apache-2.0 - README.md安装、配置、租户接入流程、API 示例 - issue templates / PR template - SECURITY.md漏洞提交通道 - 版本规范SemVerv0.x -v1.0 ---9)持续维护路线你可以直接按这个迭代 v0.1MVP - 租户识别 - 行级隔离 - 用户/项目基础 CRUD - JWT 登录 v0.2 - RBACtenant_admin / member - 审计日志谁在何时改了什么 - 限流按租户 v0.3 - 计费模型套餐、成员上限、资源配额 - Webhook / OpenAPI v1.0 - 独立库租户能力 - 数据迁移工具共享库 -独立库 - 多环境发布标准化灰度/回滚 ---10)你这个开源框架最容易踩坑的5点1. 忘记给查询加租户过滤上面全局作用域已处理大部分2. 超级管理员接口误穿透租户边界必须显式 withoutGlobalScope 且审计3. 唯一索引没带 tenant_id导致跨租户冲突4. 缓存 key 没加租户前缀数据串租户5. 异步任务没带 tenant 上下文队列 payload 必须含 tenant_id --- 如果你要直接发布模板仓库这套骨架已经够做一个可运行的开源起点先把上面4个核心文件 迁移 CI 放进仓库第一版就能被社区用起来并持续迭代。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2555204.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!