---
title: 角色与权限设计说明书
description: 基于当前代码实现，总结 Xpert 系统的角色、权限、租户和组织范围控制架构
---

# 角色与权限设计说明书

本文档不是通用模板，而是基于当前 Xpert 仓库实现整理出的现状说明。目标读者是产品、研发、实施、运营和审计同学，用来回答 4 个核心问题：

1. 系统里有哪些角色，它们大致代表什么业务身份？
2. 权限是如何组织的，哪些功能由权限控制，哪些由功能开关控制？
3. 租户、组织、资源可见范围之间是什么关系？
4. 一个用户最终为什么能看到某个页面、访问某个接口、操作某条数据？

## 一句话结论

当前系统采用的是一个典型的多租户 RBAC 架构，但不是单一维度的 RBAC。

实际生效逻辑更接近下面这个公式：

`可访问 = 已登录 + 租户匹配 + 功能已启用 + 角色/权限满足 + 组织或资源范围满足`

也就是说，页面或接口是否可用，通常由以下 5 层共同决定：

1. 身份是否已认证
2. 请求是否落在当前租户
3. 该功能是否在当前租户/组织启用
4. 当前角色是否拥有对应权限
5. 该资源是否允许在当前组织或用户范围内访问

## 1. 产品视角下的权限对象模型

当前系统的权限设计围绕 6 个核心对象展开：

| 对象             | 产品含义                          | 当前作用                                        |
| ---------------- | --------------------------------- | ----------------------------------------------- |
| Tenant           | 企业或工作区顶层边界              | 负责数据隔离、默认角色、默认权限、默认功能开关  |
| Organization     | Tenant 下的业务组织/部门/业务单元 | 负责用户实际工作的上下文和部分功能范围          |
| User             | 登录主体                          | 挂载一个租户级角色，可加入多个组织              |
| Role             | 角色模板                          | 决定一个用户默认拥有哪一组能力                  |
| RolePermission   | 权限点开关                        | 以“角色 + 权限名 + enabled”形式存储实际授权结果 |
| UserOrganization | 用户与组织关系                    | 表示用户属于哪些组织，以及默认组织是谁          |

从产品上看，这意味着：

- 用户的“身份”主要由 `Role` 决定。
- 用户的“工作范围”主要由 `Tenant + Organization` 决定。
- 用户的“页面可见性”和“操作能力”主要由 `RolePermission` 决定。
- 用户能不能看到某类功能，还会再经过 `Feature` 功能开关过滤。

## 2. 当前系统的角色体系

代码里当前定义了 8 个角色枚举：

- `SUPER_ADMIN`
- `ADMIN`
- `DATA_ENTRY`
- `EMPLOYEE`
- `CANDIDATE`
- `MANAGER`
- `VIEWER`
- `TRIAL`

其中，当前默认创建和默认授权里实际有明确配置的主要是以下 7 个：

| 角色          | 产品定位              | 当前默认能力概括                                       |
| ------------- | --------------------- | ------------------------------------------------------ |
| `SUPER_ADMIN` | 租户最高管理员        | 拥有几乎全部后台管理权限、AI 权限和高危操作权限        |
| `ADMIN`       | 租户管理员/业务管理员 | 拥有大部分管理能力，但不包含 `SUPER_ADMIN_EDIT`        |
| `DATA_ENTRY`  | 轻量运营/录入角色     | 仅有少量基础权限，但默认可编辑知识库、Xpert 和使用聊天 |
| `EMPLOYEE`    | 普通内部成员          | 主要是基础看板和组织联系人查看能力                     |
| `CANDIDATE`   | 候选或低权限成员      | 只有极少量基础能力                                     |
| `VIEWER`      | 浏览型角色            | 几乎没有传统后台权限，但默认可使用 AI 查看和聊天能力   |
| `TRIAL`       | 试用租户管理员        | 拥有试用阶段较完整的 AI 和基础后台能力                 |

### 2.1 角色设计的产品特点

当前角色体系有 3 个明显特征：

1. 角色是“租户级角色”，不是“组织级角色”。
2. 同一个用户当前只挂载一个 `roleId`，不会随组织切换而自动切换角色。
3. 组织切换改变的是工作上下文和数据范围，不改变角色本身。

这意味着产品上要把它理解为：

- 用户在一个 Tenant 里是“一个身份，多组织工作”。
- 系统当前不是“每个组织一套独立角色”的模型。
- 如果后续需要做“同一用户在不同组织扮演不同角色”，现有模型需要扩展。

### 2.2 当前实现里的角色现状说明

有两点需要特别说明：

1. `RolesEnum` 中包含 `MANAGER`，但当前默认权限清单中没有为它配置默认权限。
2. 种子脚本里被标记为 `isSystem` 的默认角色只有 `SUPER_ADMIN`、`ADMIN`、`EMPLOYEE`、`CANDIDATE`、`VIEWER`，而 `DATA_ENTRY` 和 `TRIAL` 虽然会被创建，但不一定被标记为系统角色。

从产品治理角度看，后续如果要在后台明确区分“系统预置角色”和“客户自定义角色”，这里建议统一定义。

## 3. 权限点如何组织

当前权限并不是按菜单树来定义，而是按“能力点”定义。大体分成 4 层：

1. 平台与组织管理权限
2. AI 产品权限
3. 数据分析与 BI 权限
4. 功能开关与套餐能力

### 3.1 平台与组织管理权限

这类权限主要来自 `PermissionsEnum`，典型包括：

| 权限域         | 代表权限                                                         | 产品含义                             |
| -------------- | ---------------------------------------------------------------- | ------------------------------------ |
| 用户管理       | `ORG_USERS_VIEW` `ORG_USERS_EDIT`                                | 查看和管理租户用户                   |
| 员工管理       | `ORG_EMPLOYEES_VIEW` `ORG_EMPLOYEES_EDIT`                        | 查看和维护员工信息                   |
| 邀请管理       | `ORG_INVITE_VIEW` `ORG_INVITE_EDIT`                              | 查看和发起邀请                       |
| 组织范围管理   | `ALL_ORG_VIEW` `ALL_ORG_EDIT` `CHANGE_SELECTED_ORGANIZATION`     | 查看全部组织、编辑全部组织、切换组织 |
| 角色权限管理   | `CHANGE_ROLES_PERMISSIONS`                                       | 修改角色权限                         |
| 超级管理员能力 | `SUPER_ADMIN_EDIT`                                               | 进入更高等级的租户级操作             |
| 系统配置       | `INTEGRATION_EDIT` `CUSTOM_SMTP_VIEW` `VIEW_ALL_EMAIL_TEMPLATES` | 集成、SMTP、邮件模板等设置能力       |
| 高危操作       | `ACCESS_DELETE_ACCOUNT` `ACCESS_DELETE_ALL_DATA`                 | 删除账号、删除全部数据               |

前端角色权限管理页会把这些权限分成两组：

- `GENERAL`
- `ADMINISTRATION`

产品上可以理解为：

- `GENERAL` 偏通用业务能力
- `ADMINISTRATION` 偏后台治理和高权限能力

### 3.2 AI 权限

AI 侧权限来自 `AIPermissionsEnum`：

| 权限                 | 产品含义              |
| -------------------- | --------------------- |
| `KNOWLEDGEBASE_EDIT` | 创建、编辑知识库      |
| `COPILOT_VIEW`       | 查看 Copilot 相关能力 |
| `COPILOT_EDIT`       | 配置 Copilot          |
| `XPERT_EDIT`         | 创建和编辑 Xpert      |
| `CHAT_VIEW`          | 使用聊天能力          |

这部分权限已经被纳入角色默认权限矩阵，而不是另起一套用户授权体系。

### 3.3 数据分析与 BI 权限

分析侧权限来自 `AnalyticsPermissionsEnum`，主要包括：

| 权限域    | 代表权限                                                   | 产品含义             |
| --------- | ---------------------------------------------------------- | -------------------- |
| 数据源    | `DATA_SOURCE_VIEW` `DATA_SOURCE_EDIT`                      | 数据连接与数据源维护 |
| 语义模型  | `MODELS_VIEW` `MODELS_EDIT`                                | 语义模型查看与编辑   |
| 故事/项目 | `STORIES_VIEW` `STORIES_EDIT`                              | Story/Project 能力   |
| 业务域    | `BUSINESS_AREA_VIEW` `BUSINESS_AREA_EDIT`                  | 业务域配置           |
| 认证      | `CERTIFICATION_EDIT`                                       | 指标或对象认证       |
| 指标      | `INDICATOR_VIEW` `INDICATOR_MARTKET_VIEW` `INDICATOR_EDIT` | 指标与指标市场       |
| 数据工厂  | `DATA_FACTORY_VIEW` `DATA_FACTORY_EDIT`                    | 数据加工相关能力     |

这说明当前系统不是只有“管理后台权限”，而是已经把 AI、BI、内容配置等都纳入同一套角色权限体系。

## 4. 功能开关与权限是两层控制

这是当前产品架构里非常关键的一点。

系统里有一套独立的 `Feature` / `FeatureOrganization` 机制，用来决定某个功能是否在租户或组织范围内启用。典型能力包括：

- `FEATURE_USER`
- `FEATURE_ROLES_PERMISSION`
- `FEATURE_EMAIL_TEMPLATE`
- `FEATURE_SMTP`
- `FEATURE_INTEGRATION`
- AI/Analytics 相关 feature key

因此产品上应把“能否进入某个功能”理解为双重校验：

1. 功能是否启用
2. 用户是否有权限

举例：

- 即使用户有 `ORG_USERS_EDIT`，如果 `FEATURE_USER` 没开，前端入口仍可能不显示。
- 即使组织里打开了 AI Copilot，如果用户没有 `COPILOT_EDIT`，设置页仍不能进入。

这也是为什么当前菜单、设置页和部分路由的判断逻辑，都会同时看 `featureKey` 和 `permissionKeys`。

## 5. 范围控制：Tenant、Organization、资源可见性三层边界

### 5.1 Tenant 是第一层硬边界

绝大多数核心实体继承自 `TenantBaseEntity` 或 `TenantOrganizationBaseEntity`，这意味着数据天然带有 `tenantId`，请求也会带 `Tenant-Id` 头。

后端的 `TenantPermissionGuard` / `TenantBaseGuard` 会优先检查：

- 当前登录用户的 `tenantId`
- 请求里的 `Tenant-Id`
- 或查询/提交数据中的 `tenantId`

如果租户不匹配，请求会被拦截。

从产品上看，Tenant 是“企业边界”或“工作区边界”，跨租户默认拒绝。

### 5.2 Organization 是第二层工作边界

`UserOrganization` 表示“一个用户加入多个组织”的关系。产品上它承担 3 个作用：

1. 决定用户可切换到哪些组织
2. 记录默认组织
3. 在切换组织后为页面和接口提供当前组织上下文

前端会在请求头里附带 `Organization-Id`，并在组织切换器中维护当前选中的组织。

但要注意：

- 当前组织主要用于“数据范围”和“功能上下文”。
- 当前系统的大部分角色权限仍然是 tenant 级，而不是 organization 级。

### 5.3 资源本身还可以有第三层可见范围

AI 知识库已经实现了资源级可见性：

- `private`: 仅自己可见
- `organization`: 组织内可见
- `public`: 租户内可见

这说明当前系统已经不是简单的“有权限就能看全部数据”，而是开始引入“资源所有权 + 可见范围”的细粒度控制。

产品上可以理解为：

- `KNOWLEDGEBASE_EDIT` 决定你能不能管理知识库这个功能
- `KnowledgebasePermission` 决定你能看见哪些知识库条目

### 5.4 Xpert Agent 与 Assistant API 的访问判断

Xpert Agent 的访问判断属于资源级权限，不应只看用户是否能访问某个 Workspace。

对外接口分为两类：

- `GET /api/xpert/accessible/:id`: 轻量访问判断接口，用于菜单、目录、第三方系统集成等只需要判断可见性的场景。
- `/api/ai/*`: 对外 Assistant 能力接口，对齐 LangGraph SDK 的 assistant / run 调用模型，用于获取 assistant 描述、统计、运行能力和发起运行。

如果外部系统只需要判断“当前用户是否可以访问某个已发布 Xpert Agent”，应调用：

```http
GET /api/xpert/accessible/:id
```

其中 `:id` 是 Xpert Agent 的 `xpert.id`。如果外部系统中字段名仍叫 `assistantId`，但实际保存的是 Xpert 的 id，也应该传这个 id。

接口位置：

- `packages/server-ai/src/xpert/xpert-access.controller.ts`
- 由 `packages/server-ai/src/xpert/xpert.module.ts` 注册到 `/api/xpert` 路由前缀下
- 内部只调用 `PublishedXpertAccessService.getAccessiblePublishedXpert(id)`

这个接口的产品语义是“当前请求主体是否有权访问这个已发布 Xpert”，而不是“这个 Xpert 是否存在”或“这个 Workspace 是否可访问”。它返回最小化结果，避免外部系统依赖 LangGraph assistant payload。

返回语义：

| 状态  | 含义                                              |
| ----- | ------------------------------------------------- |
| `200` | 当前用户或 API principal 可以访问该已发布 Xpert   |
| `403` | Xpert 存在，但当前主体没有访问权限                |
| `404` | 当前租户下找不到该已发布 Xpert，或该 Xpert 未发布 |

当前已发布 Xpert 的访问规则由 `PublishedXpertAccessService` 统一判断，核心条件包括：

1. Xpert 必须属于当前 Tenant，且已经发布。
2. 如果 Xpert 绑定 Organization，则必须匹配当前 Organization。
3. 当前用户满足以下任一关系即可访问：
   - Xpert 创建者
   - Xpert 所在 Workspace owner
   - Xpert 所在 Workspace member
   - Xpert 绑定的 User Group 成员
   - 符合租户共享 Workspace 规则的租户级 Xpert

`/api/ai/*` 也是对外 API，但它们必须使用同一套资源级访问边界：

```http
GET /api/ai/assistants/:id
POST /api/ai/assistants/count
POST /api/ai/assistants/search
GET /api/ai/assistants/:id/runtime-capabilities
POST /api/ai/threads/:thread_id/runs
POST /api/ai/threads/:thread_id/runs/stream
POST /api/ai/threads/:thread_id/runs/wait
```

这些接口面向外部 assistant 调用方，不能因为 xpert-pro 系统内部存在 `assistant_binding` 而放宽授权。也就是说，组织级或租户级的 `chat_common`、`xpert_shared`、`chatbi` 等系统助理绑定，只能用于 xpert-pro 自身的内部 Assistant Binding 能力，不能在 `/api/ai/*` 中被解释为“当前用户已被授权访问该 Xpert Agent”。

因此：

1. `/api/xpert/accessible/:id` 使用 `PublishedXpertAccessService.getAccessiblePublishedXpert(id)`。
2. `/api/ai/assistants/search` 和 `/api/ai/assistants/count` 使用 `findAccessiblePublishedXperts` / `countAccessiblePublishedXperts`。
3. `/api/ai/assistants/:id`、`/api/ai/assistants/:id/runtime-capabilities` 和 run 创建链路在解析 `assistant_id` 时，也必须使用 `getAccessiblePublishedXpert`。
4. 内部系统助理绑定的访问和偏好设置，应走 Assistant Binding 的内部 API 或服务，不应复用 `/api/ai/*` 作为内部授权捷径。

推荐实践：

1. 菜单、助理目录、第三方系统集成等只做可见性判断的场景，优先使用 `GET /api/xpert/accessible/:id`。
2. LangGraph SDK 风格的 assistant 客户端使用 `/api/ai/*`；这些接口返回成功时，表示已经通过同源的 Xpert 访问判断。
3. 不要通过 Workspace 访问权限、User Group 列表或 `assistant_binding` 自行推断 Xpert Agent 授权。

## 6. 一次权限判断是如何发生的

下面是当前系统的一次典型授权链路：

```mermaid
flowchart LR
    A[用户登录] --> B[加载用户/角色/角色权限]
    B --> C[前端载入权限与功能开关]
    C --> D[页面和菜单先做可见性过滤]
    D --> E[请求带 Tenant-Id 和 Organization-Id]
    E --> F[后端校验租户边界]
    F --> G[后端校验角色或权限]
    G --> H[业务层按组织/资源范围过滤数据]
```

更细一点看，顺序通常是：

1. 登录成功后，系统会把 `role`、`role.rolePermissions`、`tenant.featureOrganizations` 一起加载到当前用户上下文。
2. 前端把启用中的 `rolePermissions` 注入 `NgxPermissionsService`，据此决定菜单、设置项和路由可见性。
3. 每次请求自动附带 `Tenant-Id`，有组织上下文时再附带 `Organization-Id`。
4. 后端先做租户匹配校验，再根据接口上的 `@Permissions(...)` 或 `@Roles(...)` 元数据进行授权判断。
5. 某些业务对象还会再根据“创建者、组织可见、租户公开”等资源属性做二次过滤。

## 7. 前后端分别承担什么责任

### 7.1 前端负责“可见性”和“操作引导”

前端当前主要通过以下方式限制用户：

- 菜单显隐
- 设置页侧栏显隐
- 路由守卫 `NgxPermissionsGuard`
- 按权限加载不同模块入口

典型例子：

- `/settings/users` 依赖 `ORG_USERS_VIEW`
- `/settings/roles` 依赖 `CHANGE_ROLES_PERMISSIONS`
- `/settings/email-templates` 依赖 `VIEW_ALL_EMAIL_TEMPLATES`
- `/xpert/knowledge` 依赖 `KNOWLEDGEBASE_EDIT`

### 7.2 后端负责“最终裁决”

后端通过以下守卫和装饰器落实最终授权：

| 机制                                    | 作用                         |
| --------------------------------------- | ---------------------------- |
| `@Permissions(...)` + `PermissionGuard` | 校验当前角色是否拥有所需权限 |
| `@Roles(...)` + `RoleGuard`             | 校验当前用户是否属于指定角色 |
| `TenantPermissionGuard`                 | 校验请求是否落在当前租户     |
| `OrganizationPermissionGuard`           | 用于少量组织属性权限场景     |

这意味着：

- 前端隐藏按钮并不等于真正安全。
- 真正的授权边界仍然在后端接口。

## 8. 当前系统里的典型访问规则

### 场景 1：进入“用户管理”

要看到并使用“用户管理”，通常至少需要满足：

1. `FEATURE_USER` 已启用
2. 拥有 `ORG_USERS_VIEW` 或 `ORG_USERS_EDIT`
3. 请求落在当前租户

### 场景 2：进入“角色与权限”

要进入角色权限设置页，通常需要：

1. `FEATURE_ROLES_PERMISSION` 已启用
2. 拥有 `CHANGE_ROLES_PERMISSIONS`
3. 请求落在当前租户

另外，后端对 `SUPER_ADMIN` 角色有额外保护：

- 不允许直接修改 `SUPER_ADMIN` 的权限
- 不允许直接删除 `SUPER_ADMIN` 的权限记录

### 场景 3：进入 AI 知识库

要管理知识库，通常需要：

1. 相关 AI feature 已启用
2. 拥有 `KNOWLEDGEBASE_EDIT`
3. 访问的知识库记录对当前用户可见

知识库列表当前遵循以下产品规则：

- 自己创建的知识库可见
- 他人创建但标记为 `organization` 的知识库可见
- 标记为 `public` 的知识库可在更大范围内可见

### 场景 4：插件调用系统能力

插件体系不是默认拿到全部系统能力，而是要求插件在声明权限后才能调用对应服务。当前已实现的受控能力包括：

- 文件系统
- 集成信息读取
- 用户读取/写入
- handoff 队列能力

产品上可以把它理解成：

- 平台用户通过角色权限访问产品
- 平台插件通过声明式 service permission 访问平台能力

## 9. 当前架构的产品价值

从产品设计角度，这套架构已经具备以下优点：

1. 有清晰的多租户隔离边界。
2. 角色和权限解耦，支持预置角色和自定义角色并存。
3. 功能开关和权限分离，支持套餐化、灰度化和分组织启用。
4. 前端和后端双重校验，既有体验控制，也有真正的安全控制。
5. AI、BI、系统设置已经纳入同一套权限骨架，便于统一治理。
6. 知识库等资源已经开始支持资源级可见性，而不只是粗粒度角色控制。

## 10. 当前实现里需要产品和研发共同注意的地方

以下不是“理论上的最佳实践”，而是基于当前代码现状提炼出来的注意事项：

### 10.1 当前是租户级角色，不是组织级角色

如果业务未来要支持：

- 同一用户在组织 A 是管理员，在组织 B 只是查看者
- 按组织分别配置角色

那么现有数据模型需要扩展，因为当前 `User` 只直接绑定了一个 `roleId`。

### 10.2 功能开关和权限需要一起设计

产品出 PRD 时不能只写“给某角色加权限”，还要同时确认：

- 对应 feature 是否已经启用
- 是租户级启用还是组织级启用

否则会出现“有权限但无入口”或“有入口但进不去”的体验差异。

### 10.3 少量能力仍然是角色白名单，而不是权限点

部分接口和页面直接使用 `@Roles(...)` 或角色名路由守卫，例如：

- `SUPER_ADMIN`
- `ADMIN`
- `TRIAL`

这类设计更适合“平台级身份”判断，但不适合后期做细粒度授权。

产品如果预计某个能力未来会被更多角色复用，建议优先设计成独立权限点，而不是继续堆叠角色白名单。

### 10.4 前端的角色型守卫需要单独验证

当前前端大量依赖权限点做显隐，角色名也被用于少量页面控制。但从仓库现状看，前端更明确地加载了 `permissions`，角色型路由是否都已完成统一注入，建议在新增“仅角色可见”页面前再做一次联调验证。

### 10.5 系统预置角色定义建议进一步收口

当前“角色枚举”“默认权限清单”“是否系统角色”三处定义并不完全一致。产品如果要对外提供“系统角色不可删除/不可重命名”的治理规则，建议先统一这三处口径。

## 11. 产品团队可以怎样使用这套模型

在写需求或评审新功能时，建议按下面的顺序思考：

1. 这个功能属于哪个 Tenant/Organization 范围？
2. 这是一个新功能开关，还是现有功能下的新权限点？
3. 它是“页面可见控制”还是“数据操作控制”，还是两者都要？
4. 它应该继承给哪些默认角色？
5. 它是否需要资源级可见性，例如“仅自己/组织内/全租户”？

如果这 5 个问题能被提前回答，新的角色与权限设计通常就能比较平滑地接入现有架构。

## 12. 总结

当前 Xpert 的角色与权限体系，本质上是：

- 以 Tenant 为硬隔离边界
- 以 Organization 为工作上下文
- 以 Role 为身份模板
- 以 RolePermission 为能力开关
- 以 Feature 为产品能力开关
- 以资源可见性补足更细粒度的数据范围控制

从产品成熟度看，这已经是一套可以支撑多模块、多租户、多产品线协同演进的权限骨架。后续如果继续演进，最值得优先明确的是两件事：

1. 是否要把“组织级角色”纳入模型
2. 是否要把剩余的角色白名单能力逐步收敛到统一权限点
