library-picturebook-activity/docs/TENANT_IMPLEMENTATION.md
2025-12-09 11:10:36 +08:00

5.8 KiB
Raw Permalink Blame History

多租户系统实现方案

实现概述

已成功实现完整的多租户系统,包括以下核心功能:

  1. 租户管理模块:创建、查看、更新、删除租户
  2. 数据隔离:用户、角色、权限、菜单等数据按租户隔离
  3. 租户识别支持多种方式识别租户请求头、子域名、JWT Token
  4. 超级租户:可以创建租户并分配菜单
  5. 菜单分配:超级租户可以为租户分配菜单

核心变更

1. 数据库Schema变更

  • 新增 Tenant 表(租户表)
  • 新增 TenantMenu 表(租户菜单关联表)
  • 在以下表添加 tenantId 字段:
    • User
    • Role
    • Permission
    • Dict
    • Config
  • 调整唯一性约束:从全局唯一改为租户内唯一

2. 新增模块

  • TenantsModule: 租户管理模块
    • TenantsController: 租户CRUD接口
    • TenantsService: 租户业务逻辑
    • TenantGuard: 租户识别守卫(可选,当前未全局启用)
    • Tenant/TenantId 装饰器:获取当前租户信息

3. 修改的模块

  • AuthModule:

    • 登录时支持租户识别
    • JWT Token中包含租户ID
    • 用户验证时检查租户匹配
  • UsersModule:

    • 所有操作自动添加租户过滤
    • 创建用户时自动关联租户
  • RolesModule:

    • 所有操作自动添加租户过滤
    • 创建角色时自动关联租户
  • PermissionsModule:

    • 所有操作自动添加租户过滤
    • 创建权限时自动关联租户
  • MenusModule:

    • 用户菜单查询基于租户分配的菜单
    • 菜单管理仍为全局(超级租户管理)

使用步骤

1. 数据库迁移

cd backend
npm run prisma:migrate:dev -- --name add_tenant_support

2. 初始化超级租户

npm run init:super-tenant

这将创建:

  • 超级租户code: super
  • 超级管理员username: admin, password: admin123
  • 基础权限

3. 创建租户

使用超级管理员登录后通过API创建租户

POST /api/tenants
Headers:
  Authorization: Bearer <token>
  X-Tenant-Code: super
Body:
{
  "name": "租户A",
  "code": "tenant-a",
  "menuIds": [1, 2, 3]
}

4. 租户用户登录

POST /api/auth/login
Headers:
  X-Tenant-Code: tenant-a
Body:
{
  "username": "user1",
  "password": "password123"
}

租户识别方式

系统支持以下方式识别租户(按优先级):

  1. 请求头 X-Tenant-Id: 直接指定租户ID
  2. 请求头 X-Tenant-Code: 通过租户编码识别
  3. 子域名: 从Host头提取子域名匹配
  4. JWT Token: Token中包含的tenantId

数据隔离机制

所有数据查询都会自动添加租户过滤条件:

// 示例:查询用户
const where = tenantId ? { tenantId } : {};
const users = await prisma.user.findMany({ where });

确保:

  • 每个租户只能看到自己的数据
  • 不同租户的数据完全隔离
  • 超级租户可以管理所有租户

菜单分配机制

  • 菜单是全局的(由超级租户管理)
  • 通过 TenantMenu 表关联租户和菜单
  • 用户只能看到分配给其租户的菜单
  • 超级租户可以为租户分配/取消分配菜单

API接口

租户管理

  • POST /api/tenants - 创建租户
  • GET /api/tenants - 获取租户列表
  • GET /api/tenants/:id - 获取租户详情
  • PATCH /api/tenants/:id - 更新租户(包括菜单分配)
  • DELETE /api/tenants/:id - 删除租户
  • GET /api/tenants/:id/menus - 获取租户菜单树

其他接口

所有其他接口(用户、角色、权限等)都支持租户隔离,会自动根据请求中的租户信息过滤数据。

前端集成

1. 请求拦截器添加租户信息

service.interceptors.request.use((config) => {
  const tenantCode = localStorage.getItem('tenantCode');
  if (tenantCode) {
    config.headers['X-Tenant-Code'] = tenantCode;
  }
  return config;
});

2. 登录后保存租户信息

// 登录成功后
localStorage.setItem('tenantCode', response.data.user.tenantCode);
localStorage.setItem('tenantId', response.data.user.tenantId);

注意事项

  1. 数据迁移: 如果现有系统已有数据,需要将现有数据关联到超级租户
  2. 唯一性: 用户名、邮箱等在租户内唯一,不同租户可以有相同的用户名
  3. 超级租户: 超级租户不能被删除,且拥有所有权限
  4. 菜单管理: 菜单是全局的,但通过分配机制实现租户级别的菜单显示

文件清单

新增文件

  • backend/src/tenants/ - 租户模块
    • tenants.controller.ts
    • tenants.service.ts
    • tenants.module.ts
    • dto/create-tenant.dto.ts
    • dto/update-tenant.dto.ts
    • guards/tenant.guard.ts
    • decorators/tenant.decorator.ts
  • backend/scripts/init-super-tenant.ts - 初始化超级租户脚本
  • backend/docs/TENANT_GUIDE.md - 详细使用指南

修改文件

  • backend/prisma/schema.prisma - 数据库Schema
  • backend/src/app.module.ts - 添加TenantsModule
  • backend/src/auth/ - 认证相关修改
  • backend/src/users/ - 用户服务修改
  • backend/src/roles/ - 角色服务修改
  • backend/src/permissions/ - 权限服务修改
  • backend/src/menus/ - 菜单服务修改

后续优化建议

  1. 租户守卫: 可以全局启用TenantGuard自动识别租户
  2. 租户配置: 支持租户级别的系统配置
  3. 租户统计: 添加租户使用统计功能
  4. 数据导出: 支持租户数据导出和备份
  5. 租户主题: 支持租户级别的UI主题定制

测试建议

  1. 测试租户数据隔离:确保不同租户的数据不会互相访问
  2. 测试菜单分配:验证租户只能看到分配的菜单
  3. 测试超级租户权限:验证超级租户可以管理所有租户
  4. 测试租户识别:验证各种租户识别方式都能正常工作