# 租户与角色权限关系真实案例说明

> 适用范围：`leaudit-platform` 当前 `角色权限 + 租户配置 + 数据范围` 模型  
> 更新日期：2026-05-20  
> 文档定位：不用抽象术语，直接用真实业务例子说明“租户”和“角色权限配置”到底是什么关系。

---

## 1. 先记一句最核心的话

这套系统里有三层控制：

1. `角色权限`
   - 管“这个人能不能做这件事”
2. `租户配置`
   - 管“这件事能不能落到这个租户上”
3. `数据范围`
   - 管“这个人最终能看到哪些租户的数据”

它们是三层，不是一层。

---

## 2. 例子 1：入口模块

假设现在有两个租户：

- `MZ` = 梅州
- `PUBLIC` = 公共资源域

再假设有一个用户 `张三`：

- 人属于 `MZ`
- 角色有：
  - `entry_module:create`
  - `entry_module:update`

这时候只能说明一件事：

- 张三“有权新建和编辑入口模块”

但这还不能直接推出：

- 张三可以把入口模块挂到任何租户上

还要再看租户配置。

如果租户配置是：

- `MZ.feature_keys` 包含 `home.entry_module`
- `MZ.can_host_entry_module = true`
- `PUBLIC.feature_keys` 包含 `home.entry_module`
- `PUBLIC.can_host_entry_module = false`

那么结果就是：

1. 张三可以进入入口模块管理页
2. 张三可以新建入口模块
3. 张三可以把模块挂到 `MZ`
4. 张三不能把模块挂到 `PUBLIC`

为什么？

- 因为权限页只给了他“操作资格”
- 但 `PUBLIC` 没开放“可承载入口模块”

所以这里的判断链路是：

1. 先看角色权限：能不能新建
2. 再看租户承载能力：这个租户能不能被选
3. 最后才允许落库

---

## 3. 例子 2：文档上传

假设用户 `李四`：

- 属于 `HZ`
- 角色里有 `documents:upload:create`

这说明：

- 李四“可以上传文档”

但如果 `HZ` 这个租户配置是：

- `feature_keys` 没有 `documents.upload`
- 或者 `can_host_documents = false`

那么正确行为应该是：

1. 李四即使有上传权限
2. `HZ` 这个租户也不应该作为文档归属租户被使用
3. 页面可能不显示上传入口，或者后端直接拒绝

也就是说：

- `有权限` 不等于 `这个租户允许承载这类业务`

---

## 4. 例子 3：知识库

假设用户 `王五`：

- 属于 `MZ`
- 角色里有 `rag:dataset:manage`

这说明：

- 王五可以管理知识库

但是否能给 `SZ` 租户建知识库，还要看：

- `SZ.feature_keys` 是否开启了 `rag.dataset`
- `SZ.can_host_rag` 是否为 `true`

如果：

- `SZ` 没开 `rag.dataset`

那就算王五权限再高，也不应该把知识库业务落到 `SZ`

这里要理解：

- `rag:dataset:manage` 是“人有能力操作”
- `feature_keys / can_host_rag` 是“租户有没有资格承载这个业务”

---

## 5. 例子 4：公共资源域 `PUBLIC`

这个最容易混。

假设 `PUBLIC` 用来放：

- 公共模板
- 公共规则
- 公共知识库底座
- 公共入口模块

那 `PUBLIC` 的意义是：

- 这是一个“共享资源归属地”
- 不是谁都能随便改的地方

比如用户 `赵六`：

- 属于 `MZ`
- 角色只有 `document:read`

那他可能可以看到 `PUBLIC` 下的一些共享资源，  
但如果没有 `document:update` 或更高权限：

- 他也不能改 `PUBLIC` 的资源

所以：

- `PUBLIC` 决定资源归属偏共享
- `角色权限` 决定你能不能动这份共享资源

---

## 6. 例子 5：为什么角色权限页和租户页看起来都在管“入口模块/文档/知识库”

因为它们问的不是同一个问题。

角色权限页在问：

- 这个用户能不能进入这个模块？
- 能不能新增、编辑、删除？

租户页在问：

- 这个租户开不开这个业务？
- 这个租户能不能承载这类数据？

可以把它理解成：

- `角色权限 = 驾照`
- `租户能力 = 哪些路允许走`
- `数据范围 = 你今天实际能开到哪些区域`

只有驾照，没有开通道路，不行。  
只有道路开放，没有驾照，也不行。

---

## 7. 一个完整串联例子

假设你要“给梅州上线一个新的首页入口模块”。

要同时满足下面 6 件事：

1. 当前操作人有权限
   - 例如有 `entry_module:create`
2. `MZ` 租户开放了入口模块业务
   - `feature_keys` 包含 `home.entry_module`
3. `MZ` 允许承载入口模块
   - `can_host_entry_module = true`
4. 当前人数据范围允许操作这个租户
   - 不是只能管自己部门，却跑去配别的租户
5. 首页用户属于 `MZ`
   - 登录后解析出 `tenant_code = MZ`
6. 首页读取时，模块租户关系里包含 `MZ`

最后结果才是：

- 后台能配置
- 前台能看到
- 数据不会挂错租户

---

## 8. 一句话收口

比如“入口模块”这个词，在系统里其实对应 3 个不同问题：

1. `角色权限`
   - 你能不能配入口模块
2. `租户配置`
   - 这个租户开不开入口模块
   - 这个租户能不能承载入口模块
3. `数据范围`
   - 你能不能看到或操作这个租户下的入口模块

这三件事少一件都不成立。

---

## 9. 最终理解公式

后续看所有业务模块，都可以套下面这条公式：

`最终是否允许 = 角色权限通过`

并且

`目标租户开放该业务`

并且

`目标租户允许承载该类数据`

并且

`当前用户数据范围允许访问该租户/该资源`

只要按这个公式看，就不会再把“角色权限”和“租户配置”混成一件事。
