Skip to content

租户配置

配置文件发布到 config/tenancy.php。项目通常只需要调整识别模式、中央域名、迁移范围、日志通道和队列监控。

识别模式

同一套部署全局选择一种模式:

模式租户识别方式部署要求
domain根据请求 Host 查询租户域名为每个租户准备 Domain、DNS、TLS 和反向代理
shared_domain根据固定请求头 X-Tenant 查询租户 UUID中央后台与租户后台共用一个域名

独立域名模式

项目根目录 .env

dotenv
TENANCY_IDENTIFICATION_MODE=domain
TENANT_CENTRAL_DOMAIN=admin.example.com

web/.envweb/.env.production 或当前构建环境文件:

dotenv
VITE_TENANCY_IDENTIFICATION_MODE=domain

TENANT_CENTRAL_DOMAIN 应填写主机名,例如 admin.example.com,不包含协议和端口。后台租户资料中的 Domain 同样只填写主机名,例如 tenant-a.example.com

生产环境创建租户时会检查租户域名的 A 或 CNAME 记录。本地与测试环境可以使用 nip.io 等开发域名:

text
tenant-a.127.0.0.1.nip.io

请求进入租户域名后,asset() 会根据 tenancy.filesystem.asset_helper_tenancy 使用租户资产根地址;tenant_asset($path) 会显式生成命名路由 stancl.tenancy.asset 的地址。保持以下配置可注册 Stancl 租户资产路由:

php
'routes' => true,

共享域名模式

项目根目录 .env

dotenv
TENANCY_IDENTIFICATION_MODE=shared_domain
TENANT_CENTRAL_DOMAIN=admin.example.com

web/.envweb/.env.production 或当前构建环境文件:

dotenv
VITE_TENANCY_IDENTIFICATION_MODE=shared_domain

TENANCY_IDENTIFICATION_MODE 控制后端租户识别中间件,VITE_TENANCY_IDENTIFICATION_MODE 控制管理端租户创建、域名字段和入口展示。两个值必须保持一致。

租户入口使用租户 UUID:

text
https://admin.example.com/#/login?tenant=<tenant-uuid>

管理端在当前标签页的 sessionStorage 中保存租户 UUID,并为普通租户 API 注入:

http
X-Tenant: <tenant-uuid>
Authorization: Bearer <tenant-token>

缺少 X-Tenant 的请求进入中央上下文。访问以下入口会清除当前标签页的租户 UUID:

text
https://admin.example.com/#/login?tenant=central

租户 UUID、中央 Token 和各租户 Token 相互隔离:

  • 当前标签页的租户 UUID 和注入 Header 保存在 sessionStorage
  • Token 保存在 localStorage,键名按 central 或租户 UUID 区分。
  • 切换租户时会清除当前标签页的菜单标签,再读取目标上下文对应的 Token。

前端或其他客户端必须同时发送与当前租户匹配的 X-Tenant 和租户 Token。Header 已提供但租户 UUID 格式错误、租户不存在、停用或到期时,请求会返回租户不可用;Header 缺失时进入中央上下文。

管理端在构建时读取 VITE_TENANCY_IDENTIFICATION_MODE。修改该值后,重新启动开发服务或重新构建前端。共享域名模式创建租户时可留空 Domain,数据库名称按 UUID 生成,例如:

text
tenant_0190d8d7_1234_7abc_8def_1234567890ab

前端配置

前端使用两个租户环境变量:

dotenv
VITE_TENANCY_MODE=true
VITE_TENANCY_IDENTIFICATION_MODE=shared_domain

VITE_TENANCY_MODE=true 让管理端 API 使用当前页面源站的 /api 地址,安装命令会维护该值。VITE_TENANCY_IDENTIFICATION_MODE 只接受 domainshared_domain,由部署人员在对应的前端构建环境文件中维护。

数据库与迁移

当前包只启用 MySQL 租户数据库管理器。中央连接沿用 DB_CONNECTION,租户上下文初始化后 Laravel 默认数据库连接会切换到对应租户库。

migration_parameters 用于手动执行 Stancl 租户迁移命令,例如 tenants:migrate

php
'migration_parameters' => [
    '--force' => true,
    '--path' => [database_path('migrations/tenant')],
    '--realpath' => true,
],

global_migration_parameters 用于创建租户时,在租户上下文中执行 Laravel 全局迁移。默认配置会执行项目默认迁移路径:

php
'global_migration_parameters' => [
    '--force' => true,
],

如果项目根迁移中包含只属于中央库的表,应把所有租户公共迁移放到独立目录,并明确限制路径:

php
'global_migration_parameters' => [
    '--force' => true,
    '--path' => [database_path('migrations/tenant')],
    '--realpath' => true,
],

创建租户时还会按默认模块和套餐权限模块执行各模块迁移。

需要为某个租户使用已有 MySQL 实例时,先在 config/database.php 定义连接,再在租户管理页面选择该连接并填写对应数据库配置。

缓存与日志

租户缓存键使用 tenant:<uuid>: 前缀。缓存型 Session 会跟随租户缓存范围隔离。

默认隔离以下日志通道:

php
'logging' => [
    'channels' => ['daily', 'single', 'query'],
    'suffix_base' => 'tenant-',
],

日志隔离只处理 logging.channels 中带 path 的文件通道,租户日志目录为:

text
storage/logs/tenant-{uuid}

stackstderrsyslog 等没有文件路径的通道仍按 Laravel 原配置工作。

队列监控

包配置默认关闭队列监控:

dotenv
TENANCY_QUEUE_MONITOR=false

catch:tenant:install 会在根目录 .env 缺少该变量时追加 TENANCY_QUEUE_MONITOR=true;已有配置保持原值。修改后需要重启队列 worker,使监听器按新配置重新注册。

队列连接和 worker 用法参见租户队列

文件系统与资产

默认隔离三个本地磁盘:

磁盘租户目录访问边界
uploadsstorage/uploads/tenant-{uuid}公开业务上传,可通过 /uploads/tenant-{uuid}/... 访问
staticstorage/static/tenant-{uuid}私有文件,不提供浏览器直连地址
certsstorage/certs/tenant-{uuid}私有证书,不提供浏览器直连地址

相关最小配置为:

php
'filesystem' => [
    'suffix_base' => 'tenant-',
    'disks' => ['uploads', 'static', 'certs'],
    'root_override' => [
        'uploads' => '%storage_path%',
        'static' => '%storage_path%',
        'certs' => '%storage_path%',
    ],
    'suffix_storage_path' => true,
    'asset_helper_tenancy' => true,
],

'routes' => true,

执行以下命令创建公开上传软链接:

shell
php artisan storage:link

软链接关系应为:

text
public/uploads -> storage/uploads

上传接口保存并返回的相对路径类似:

text
uploads/tenant-{uuid}/2026-07-18/attachments/example.jpg

页面统一使用 /uploads/tenant-{uuid}/... 地址。共享域名模式下,浏览器直接加载图片时不会自动携带 X-Tenant,因此公开业务上传应使用这类公开路径。uploads 目录不应保存密钥、证书或其他需要鉴权的文件。

独立域名模式可以继续使用 asset()tenant_asset() 生成租户资产地址;后者依赖 stancl.tenancy.asset 命名路由。

资产排障

  1. 检查文件是否真实存在于 storage/uploads/tenant-{uuid}/...

  2. 检查 public/uploads 是否正确指向 storage/uploads

  3. 检查 tenancy.filesystem.disksroot_override 是否包含目标磁盘。

  4. 独立域名资产使用命名路由时,检查 tenancy.routes=true,并执行:

    shell
    php artisan route:list --name=stancl.tenancy.asset
  5. 修改配置或路由后重建缓存:

    shell
    php artisan optimize:clear
    php artisan config:cache
    php artisan route:cache

租户日志中的 Route [stancl.tenancy.asset] not defined 可能来自路由注册失败,也可能是缓存更新前留下的历史记录。确认命名路由存在后,继续观察是否产生新的同类日志。

PHP-FPM 请求生命周期

PHP-FPM 下,租户上下文会保持到响应发送、流式响应回调和 Kernel::terminate() 完成。租户初始化失败时,系统会恢复已经切换的中央数据库、缓存、文件系统和日志状态,再抛出原始初始化异常。