跳到主要内容

理解用户、租户与会话上下文

背景

在我们的系统中,一个用户可以拥有多个租户,这带来了几个关于“ID”的核心概念。为了消除混淆,本文档将清晰地定义以下三个关键概念:

  1. 用户的注册来源 (users.tenant_id)
  2. 租户的所有权关系 (tenants.user_id)
  3. 当前管理的租户 (owned_tenant_id - 运行时变量)

1. users.tenant_id:用户的注册来源 (数据库字段)

这是 users 表中的一个字段,用于标记用户的**“注册来源”。此字段在用户创建时被设定,之后永远不会改变**。

  • 官方平台注册:

    • 当用户在我们的官方主站注册时,他的 users.tenant_id 被设为固定值 1
  • 租户网站注册:

    • 当用户通过特定租户的网站注册时,他的 users.tenant_id 被设为该租户的ID。

核心用途: 仅用于追踪用户来源,进行数据分析。


2. tenants.user_id:租户的所有权 (数据库字段)

一个用户可以拥有并管理一个或多个租户。这个“所有权”是通过 tenants 表中的 user_id 字段来定义的。

  • 定义: tenants 表中的 user_id 是一个外键,指向 users 表的主键。
  • 一对多关系: 如果一个用户拥有多个租户,那么在 tenants 表中就会有多条记录拥有相同的 user_id
  • 查询: 要找到一个用户拥有的所有租户,可以执行以下查询:
    SELECT id, name FROM tenants WHERE user_id = '<user_id>';

3. owned_tenant_id:当前管理的租户 (运行时变量)

💡 重要提示: 这是最容易引起混淆、但也是最需要理解的概念:owned_tenant_id 不是数据库里的一个字段,而是一个在程序运行时(例如,在用户的登录会话、JWT Token 或 API 请求头中)使用的临时变量

  • 作用: 当一个拥有多个租户的用户登录系统时,他需要选择当前要管理哪一个租户。owned_tenant_id 这个变量就用来存放用户当前选择的租户ID
  • 生命周期: 它在用户选择要管理的租户后被赋值,并在用户退出登录或切换管理租户时被改变或销毁。

完整流程示例:登录与管理

让我们将这三个概念串联起来:

前提:

  • 用户 王五 已在平台注册 (users 表: { id: 123, tenant_id: 1 })。
  • 王五 已创建了两个租户 (tenants 表有两条记录: { id: "tenant-c", user_id: 123 }{ id: "tenant-d", user_id: 123 })。

流程:

  1. 用户登录: 王五 使用他的账号密码登录。

  2. 选择租户:

    • 后端通过查询 tenants 表 (SELECT * FROM tenants WHERE user_id = 123),发现 王五 拥有 "tenant-c" 和 "tenant-d" 两个租户。
    • 前端界面(UI)向 王五 显示一个选择列表:“请选择您要管理的租户:[公司C, 公司D]”。
  3. 设置管理上下文:

    • 王五 点击选择“公司D”。
    • 前端将用户选择的 tenant-d 作为参数,请求后端为此会话设置管理上下文。
    • 后端生成一个包含 owned_tenant_id: "tenant-d" 的JWT或会话记录。
  4. 执行管理操作:

    • 王五 点击“用户管理”页面,前端发起请求 GET /api/manage/users
    • 该请求的凭证(JWT)中包含了 { user_id: 123, owned_tenant_id: "tenant-d" }
    • 后端API接收到请求,从凭证中解析出 owned_tenant_id 是 "tenant-d"。
    • 后端的所有数据库操作都将基于 "tenant-d" 来执行(例如,SELECT * FROM users WHERE tenant_id = 'tenant-d'),从而确保他管理的是“公司D”而不是“公司C”的事务。

总结

概念位置/方式含义不变性/动态性
tenant_idusers 表的持久化字段用户的注册来源不变
所有权tenants 表的 user_id 字段定义了哪个用户拥有哪个或哪些租户关系固定
owned_tenant_id会话/JWT等上下文中的临时变量用户当前选择管理的租户动态