Skip to content

6.13 租户管理 ​

概述

租户管理是多租户体系的核心,支持租户的 CRUD、创建租户账号、查看租户用户。继承 BaseLogic(系统共享,租户表本身不隔离)。

模块特点 ​

特性说明
基类BaseLogic(系统共享,租户表不隔离)
特殊接口account(创建租户账号)、users(查看租户用户)
删除校验beforeDelete 检查用户数 + 保护默认租户
编码生成afterAdd 自动生成租户编码(T+4位ID)
状态校验TenantMiddleware 校验租户状态和过期时间

API 接口 ​

方法路径权限码说明
GET/api/tenant/pagesys:tenant:page分页列表
GET/api/tenant/listsys:tenant:list全量列表
GET/api/tenant/detail/:idsys:tenant:detail详情
GET/api/tenant/users/:tenantId-租户用户列表
POST/api/tenant/addsys:tenant:add新增租户
POST/api/tenant/accountsys:tenant:add创建租户账号
PUT/api/tenant/updatesys:tenant:update修改租户
DELETE/api/tenant/delete/:idsys:tenant:delete删除租户
DELETE/api/tenant/batchDeletesys:tenant:batchDelete批量删除

核心代码 ​

php
// app/logic/TenantLogic.php

class TenantLogic extends BaseLogic
{
    /**
     * 关联的模型类
     *
     * @var string
     */
    protected string $modelClass = Tenant::class;

    /**
     * LIKE 模糊匹配字段
     *
     * @var array
     */
    protected array $pageLikeFields = ['name', 'code', 'contact'];

    /**
     * 精确匹配字段
     *
     * @var array
     */
    protected array $pageEqFields = ['status'];

    /**
     * 默认排序规则
     *
     * @var array
     */
    protected array $pageOrderBy = ['field' => 'id', 'type' => 'desc'];

    /**
     * 枚举显示名映射
     *
     * @var array
     */
    protected array $serializeMaps = ['status' => 'tenant_status'];

    /**
     * 修改前处理:禁止修改编码
     *
     * @param int   $id   租户 ID
     * @param array $data 待更新的数据
     * @return array 过滤后的数据
     */
    protected function beforeUpdate(int $id, array $data): array
    {
        // 租户编码由系统自动生成(T+4位ID),不允许手动修改
        unset($data['code']);
        return $data;
    }

    /**
     * 新增后处理:自动生成租户编码
     *
     * @param int   $id   新增的租户 ID
     * @param array $data 新增时提交的原始数据
     * @return void
     */
    protected function afterAdd(int $id, array $data): void
    {
        // 编码格式:T + 4位ID左补零,如 T0001、T0002;因为依赖自增ID,所以在新增完成后回写
        $code = 'T' . str_pad((string)$id, 4, '0', STR_PAD_LEFT);
        Tenant::where('id', $id)->update(['code' => $code]);
    }

    /**
     * 删除前处理:保护默认租户 + 检查用户数
     *
     * @param int $id 待删除的租户 ID
     * @return void
     * @throws \Exception 当为默认租户或存在用户时抛出
     */
    protected function beforeDelete(int $id): void
    {
        // 默认租户不可删除
        if ($id == 1) {
            throw new \Exception('默认租户不可删除');
        }
        $userCount = User::where('tenant_id', $id)->where('is_delete', 0)->count();
        if ($userCount > 0) {
            throw new \Exception('该租户下存在用户,不可删除');
        }
    }

    /**
     * 列表查询后处理:批量补充租户用户数
     *
     * @param array &$records 记录列表(引用传递)
     * @param array $params   查询参数
     * @return void
     */
    protected function afterPageList(array &$records, array $params): void
    {
        if (empty($records)) return;

        // 一次性查询当前页所有租户的用户数量,避免 N+1
        $tenantIds = array_column($records, 'id');
        $counts = User::where('is_delete', 0)
            ->whereIn('tenant_id', $tenantIds)
            ->group('tenant_id')
            ->column('count(*)', 'tenant_id');

        foreach ($records as &$record) {
            $record['userCount'] = $counts[$record['id']] ?? 0;
        }
    }
}

列表返回 userCount ​

/api/tenant/page 和 /api/tenant/list 接口返回的每条记录中自动包含 userCount 字段,表示该租户下的用户数量。

实现方式

通过 BaseLogic::afterPageList 钩子实现,使用 whereIn + group 批量查询,避免 N+1 问题。

创建租户账号 ​

php
// TenantLogic::createAccount()
// 处理流程:
// 1. 校验租户存在 + 用户数限制
// 2. 注入租户ID,调用 UserLogic::add() 完成标准创建
//    (含编码自动生成、密码加密、头像处理、角色关联)

用户编码由 UserLogic 统一生成

创建租户账号时不再由 TenantLogic 直接生成用户编码, 而是通过 UserLogic::beforeAdd 统一生成,确保所有用户创建路径(直接添加、租户账号、导入)编码规则一致。

默认租户保护

默认租户(id=1)不可删除,编码不可修改。超级管理员(tenant_id=0)不受租户隔离限制,创建租户隔离数据时自动代入默认租户(tenant_id=1)。详见 多租户隔离。

套餐化角色预置 ​

系统内置三套租户角色模板,分配不同的菜单权限,用于按套餐等级快速授权:

角色名称定位菜单范围
租户-标准版基础功能核心业务模块
租户-专业版进阶功能核心 + 高级模块
租户-旗舰版全功能全部菜单(最全)

使用方式: 创建租户后,通过「角色管理 → 授权菜单」为对应角色分配菜单权限。创建租户管理员账号时,选择对应套餐角色即可完成授权。

设计意图

套餐角色解决了"每个租户都要手动配一遍菜单"的重复工作。管理员创建租户时只需选择套餐,角色权限即刻生效。

演示数据 ​

系统内置一组演示数据,可直接登录体验多租户效果:

平台级 ​

账号密码角色说明
admin123456超级管理员平台管理,可管理所有租户

租户级 ​

账号密码所属租户角色说明
tenant001123456客户租户A租户管理员可体验租户视角的功能与数据隔离

演示环境注意

以上账号仅用于开发和演示环境,生产环境部署后请立即修改密码或删除演示数据。

租户状态 ​

字段说明
status0=禁用 1=启用
expire_time过期时间,过期后 TenantMiddleware 返回 403
max_user_num最大用户数限制

小蚂蚁云团队 · 提供技术支持