DataBuilder(百度胜算)权限控制体系完整文档
基于 console-databuilder 前端代码深度分析 | 最后更新:2026-05-19
目录
一、产品概述
DataBuilder(百度胜算) 是百度智能云旗下的数据智能平台,面向模型训练、AI应用及数据飞轮等场景,提供多模态数据管理、高性能计算、智能开发平台和丰富算子能力。支持一站式数据治理、数据加工和数据应用。
技术栈:React + TypeScript + Redux Toolkit + React Router 6 + @baidu/qianfan-antd-kit (antd)
部署模式:支持公有云 / 私有化双模式部署(通过 flags.DatabuilderPrivateSwitch 开关控制)
产品版本(通过 VERSION_NAME 枚举区分):
| 版本 | 枚举值 | 说明 |
|---|---|---|
| 免费版 | TRIAL |
基础功能,是产品默认开通的体验版本 |
| 标准版 | STANDARD |
提供完整的数据管理和开发能力 |
| 专业版 | PROFESSIONAL |
面向企业级场景,功能更丰富 |
| 企业版 | ENTERPRISE |
最高规格,支持高级定制和更大规模 |
版本信息的获取通过 getEditionInfo() API 调用,返回的数据包含版本名称、状态(RUNNING 正常运行 / STOPPED 欠费停服)、过期时间和资源ID。前端将版本信息存入 Redux Store 的 editionInfo 字段,业务代码可根据版本差异做功能开关。
权限架构:三层权限体系(路由级 + 组件级 + 数据级)
二、项目角色体系
2.1 角色类型
| 角色类型 | 枚举值 | 说明 |
|---|---|---|
| 用户自定义角色 | RoleType.User = 'USER' |
空间管理员在空间内创建的自定义角色,可自由分配权限 |
| 系统预设角色 | RoleType.System = 'SYSTEM' |
系统内置角色(如管理员、开发者等),在代码中硬编码,不可删除 |
2.2 主体类型(授权对象)
PrincipalType 定义了权限可以被授予的三类对象:
| 主体类型 | 枚举值 | 说明 |
|---|---|---|
| 用户 | PrincipalType.User = 'USER' |
直接对单个用户授予权限 |
| 用户组 | PrincipalType.Group = 'GROUP' |
对一组用户(用户组)批量授予权限 |
| 角色 | PrincipalType.Role = 'ROLE' |
将权限授予角色,用户通过获得角色来间接获得权限 |
在权限管理弹窗中,管理员可以选择任意主体类型进行授权,系统会通过 getWorkspaceUserList API 列出所有可选的主体供选择。
2.3 系统内置角色
| 角色 | 说明 | 权限级别 |
|---|---|---|
| 系统管理员 (systemAdmin) | 由后端 verifySystemAdmin 接口判断。管理员可以访问空间外的元存储菜单,通常拥有所有空间的管理权限 |
最高 |
| DataBuilderFullControl | 专门用于公有云 Playground(体验/演示环境) 的完全控制权限。Playground 是一种无需真实购买产品即可体验的沙盒环境,系统预设了演示账户和演示工作空间。通过 checkFullControl() API 验证 |
完全控制 |
| 全部用户 (ALL) | 特殊主体,名称为"全部用户",对应 Privilege.All 权限组,表示该权限对所有用户生效 |
可配置任意权限 |
| 元存储管理员 (metastoreAdmin) | 元存储级别的管理员,后端返回值 ["admin"],用于判断用户是否有元存储管理能力 |
元存储管理 |
2.4 权限级别(PermissionType)
PermissionType 用于描述对某个具体资源的读写权限等级,
由后端 getWorkspacePermission(workspaceId) 接口自动计算返回,
不是在创建角色时手动设定的。它和 ResourceType 不在同一个层次:
ResourceType 定义了"有哪些资源可被管理",PermissionType 定义了
"当前用户对这些资源有多大的操作权限"。,定义在 src/api/auth.ts 中:
// PermissionType 表示对资源的操作级别
export type PermissionType = 'readWrite' | 'readOnly' | undefined;
| 值 | 含义 | 使用场景 |
|---|---|---|
'readWrite' |
可读写 | 用户对该资源(如表、卷、工作流)可以进行读取和修改操作 |
'readOnly' |
只读 | 用户只能查看该资源,不能修改 |
undefined |
无权限 | 用户完全无法访问该资源 |
在后端返回的 GetWorkspacePermissionResult 中,每个资源类型(catalog、table、volume 等)的值都是一个 PermissionType[] 数组,前端取第一项 [0] 存入 WorkspaceAuth Redux Store。
PermissionType 的使用位置:
- src/store/WorkspaceAuth.ts:存储每个资源类型的读写权限
- src/api/auth.ts:定义 PermissionType 类型和 GetWorkspacePermissionResult 接口
- App.tsx 和 pages/index.tsx:进入工作空间时调用 getWorkspacePermission() 获取并 dispatch 到 store
- 各业务页面:通过 useSelector(state => state.workspaceAuthSlice.xxx) 读取,决定编辑按钮是否可用
PermissionType 和 Privilege 权限点是否重复?
不是重复——它们是两个不同层次的设计:
| 维度 | Privilege 权限点 | PermissionType |
|---|---|---|
| 粒度 | 细粒度,75个具体操作(如 WRITE_TABLE) | 粗粒度,3档(readWrite/readOnly/undefined) |
| 作用 | 功能级——能不能看到按钮、能不能执行某操作 | 资源级——对某类资源整体是只读还是可读写 |
| 判定方式 | 在角色创建时由管理员勾选分配 | 由后端根据用户身份直接计算返回 |
| 存储位置 | workspacePermission: { PRIV: boolean } | workspaceAuth: { table: 'readWrite' } |
| 当前使用 | 广泛用于路由HOC、AuthButton、useWorkspaceAuth | 当前业务组件中使用较少,更偏向后端保底机制 |
打个比方:Privilege.WRITE_TABLE 是"你有没有资格去写表这个操作",是个开关;
PermissionType = 'readWrite' 是"后端根据你的整体身份,判定你对表类资源可以进行读写"。
两者来源不同——权限点是角色配置的结果,PermissionType 是后端综合判断的结果。
2.5 页面状态
| 状态 | 枚举值 | 说明 |
|---|---|---|
| 有效 | PageStatus.Valid |
当前页面有权限,正常渲染 |
| 页面无权限 | PageStatus.NoPagePermission |
用户通过直接输入 URL 硬访问了一个没有分配权限的页面,HOC 拦截后显示"页面无权限"提示 |
| 空间无权限 | PageStatus.NoWorkspacePermission |
用户在空间内没有任何菜单权限,显示"空间无权限"提示 |
PageStatus 的触发时机和 UI 表现:当用户在地址栏直接输入 URL 硬访问时,
WithPermissionHoc 先从 Redux 读取 workspacePermission 映射表。
如果连任一空间菜单权限都没有,dispatch NoWorkspacePermission,
此时 AppLayout(src/components/AppLayout/index.tsx:570-576)
完全隐藏侧边导航栏(因为没有可访问的菜单项);
如果用户有空间菜单权限但不含当前页面权限,dispatch NoPagePermission,
此时 AppLayout 仍然展示侧边导航栏(让用户可以导航到其他有权限的页面)。
这套机制确保即使用户绕过前端菜单直接输入 URL,权限系统仍能有效拦截并给出恰当提示。
三、项目权限体系(完整多级表格)
3.1 通用权限点
通用权限点定义了对资源的基本操作能力,它们之间有层级包含关系:
Manage(管理) > Modify(修改) > Execute(执行) > View(查看)
例如,如果用户拥有 MANAGE 权限,则自动拥有 MODIFY、EXECUTE、VIEW 权限。这个层级判断逻辑在 checkSimplePrivilege() 工具函数中实现(位于 src/utils/auth.ts:142)。
通用权限点在使用上与功能权限点不同:功能权限点(如 PIPELINE_DESIGNER_CREATE)用于空间级别的菜单和按钮控制;
通用权限点(如 MANAGE、VIEW)用于单个资源对象的 CRUD 级别控制。
典型使用场景在 数据管道模块(PipelineBuilderList、ConfigPanelBase)和 API Key 组件中:
每条管道记录有自己的 privileges 数组,用 checkSimplePrivilege(record.privileges, Privilege.View) 判断用户对该条管道的查看/执行/修改/管理权限。
这使得同一个用户对不同管道可以有不同的操作级别,比空间级的统一权限更精细。
| 权限枚举值 | 中文名称 | 包含的权限 | 说明 |
|---|---|---|---|
ALL |
全部权限 | 所有权限 | 权限集合,通常用于角色配置时一键赋予所有权限 |
FULL_CONTROL |
完全控制 | 完全控制 | 最高权限,用于 Playground 管理员 |
MANAGE |
管理 | Modify + Execute + View | 包含资源的全部管理权限 |
MODIFY |
修改 | Execute + View | 可以修改资源内容 |
EXECUTE |
执行 | View | 可以运行/执行操作 |
USE |
使用 | - | 可以使用资源(如使用数据源) |
VIEW |
查看 | - | 最基础的查看权限 |
3.2 空间管理模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 空间管理 | - | WORKSPACES_MENU | 空间管理菜单 | 菜单 | 可见左侧菜单"工作空间" | 菜单隐藏 |
| 空间管理 | - | WORKSPACE_CREATE | 新建空间 | 按钮 | 可点击"新建空间"按钮 | 按钮禁用+提示 |
3.3 元存储(空间外)
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 元存储 | - | METASTORE_MENU | 元数据菜单 | 菜单 | 可见左侧菜单"元数据"(空间外) | 菜单隐藏 |
| 元存储 | - | METASTORE_CREATE | 创建元存储 | 按钮 | 可创建元存储 | 按钮禁用+提示 |
3.4 工作区模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 工作区 | - | WORKSPACE_MENU | 工作区菜单 | 菜单 | 可见左侧菜单"工作台" | 菜单隐藏 |
| 工作区 | - | PROJECT_CREATE | 项目创建 | 按钮 | 可创建工作台项目 | 按钮禁用+提示 |
| 工作区 | - | DIRECTORY_CREATE | 新建文件夹 | 按钮 | 可在工作区内新建文件夹 | 按钮禁用+提示 |
| 工作区 | - | FILE_IMPORT | 导入文件 | 按钮 | 可在工作区内导入文件 | 按钮禁用+提示 |
| 工作区 | - | NOTEBOOK_CREATE | 新建Notebook | 按钮 | 可新建Jupyter Notebook | 按钮禁用+提示 |
3.5 元数据目录(空间内)
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 元数据 | - | CATALOG_MENU | 元数据菜单 | 菜单 | 可见左侧菜单"元数据" | 菜单隐藏 |
| 元数据 | 目录 | CREATE_CATALOG | 创建目录 | 操作 | 可创建Catalog目录 | 按钮禁用 |
| 元数据 | 数据源 | CREATE_CONNECTION | 创建数据源 | 操作 | 可创建外部数据源连接 | 按钮禁用 |
| 元数据 | 数据源 | USE_CONNECTION | 使用数据源 | 操作 | 可使用已有数据源 | 不可使用 |
| 元数据 | 浏览 | BROWSE | 查看元数据 | 操作 | 可浏览元数据目录结构 | 不可浏览 |
| 元数据 | Schema | CREATE_SCHEMA | 创建模式 | 操作 | 可在Catalog下创建Schema | 按钮禁用 |
| 元数据 | Volume | CREATE_VOLUME | 创建卷 | 操作 | 可创建数据卷 | 按钮禁用 |
| 元数据 | Volume | READ_VOLUME | 读卷 | 操作 | 可读取数据卷内容 | 不可读取 |
| 元数据 | Volume | WRITE_VOLUME | 写卷 | 操作 | 可写入数据卷 | 不可写入 |
| 元数据 | Table | CREATE_TABLE | 创建表 | 操作 | 可创建数据表 | 按钮禁用 |
| 元数据 | Table | READ_TABLE | 读表 | 操作 | 可读取表数据 | 不可读取 |
| 元数据 | Table | WRITE_TABLE | 写表 | 操作 | 可写入表数据 | 不可写入 |
| 元数据 | Dataset | CREATE_DATASET | 创建数据集 | 操作 | 可创建数据集 | 按钮禁用 |
| 元数据 | Dataset | READ_DATASET | 读数据集 | 操作 | 可读取数据集 | 不可读取 |
| 元数据 | Dataset | WRITE_DATASET | 写数据集 | 操作 | 可写入数据集 | 不可写入 |
| 元数据 | Dataset | CREATE_DATASET_VERSION | 创建数据集版本 | 操作 | 可为数据集创建新版本 | 按钮禁用 |
| 元数据 | Model | CREATE_MODEL | 创建模型 | 操作 | 可创建模型 | 按钮禁用 |
| 元数据 | Model | CREATE_MODEL_VERSION | 创建模型版本 | 操作 | 可为模型创建新版本 | 按钮禁用 |
| 元数据 | Operator | CREATE_OPERATOR | 创建算子 | 操作 | 可创建算子 | 按钮禁用 |
| 元数据 | Operator | CREATE_OPERATOR_VERSION | 创建算子版本 | 操作 | 可为算子创建新版本 | 按钮禁用 |
| 元数据 | 表列 | (TableColumn资源) | 列权限 | 数据级 | 可访问特定列,通过列级鉴权实现 | 列被屏蔽 |
| 元数据 | 表行 | (TableRow资源) | 行权限 | 数据级 | 可访问特定行,通过SQL过滤规则实现 | 行被过滤 |
3.6 计算资源模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 计算资源 | - | COMPUTE_MENU | 计算资源菜单 | 菜单 | 可见左侧菜单"计算资源" | 菜单隐藏 |
| 计算资源 | 源连接与集成 | INTEGRATION_COMPUTE_CREATE | 新建集成实例 | 按钮 | 可创建集成计算资源 | 按钮禁用+提示 |
| 计算资源 | 常驻实例 | ETL_COMPUTE_CREATE | 新建常驻实例 | 按钮 | 可创建常驻实例 | 按钮禁用+提示 |
| 计算资源 | 任务模板 | ETL_JOB_TEMPLATE_CREATE | 新建任务模板 | 按钮 | 可创建任务实例模板 | 按钮禁用+提示 |
| 计算资源 | 分析/AI搜索 | ANALYSIS_COMPUTE_CREATE | 新建分析实例 | 按钮 | 可创建分析计算资源 | 按钮禁用+提示 |
| 计算资源 | 通用常驻实例 | LOGIC_COMPUTE_CREATE | 新建通用实例 | 按钮 | 可创建通用常驻实例 | 按钮禁用+提示 |
| 计算资源 | 资源池 | RESOURCE_POOL_CREATE | 新建资源池 | 按钮 | 可创建资源池 | 按钮禁用+提示 |
| 计算资源 | 资源组 | RESOURCE_GROUP_CREATE | 新建资源组 | 按钮 | 可创建资源组 | 按钮禁用+提示 |
3.7 数据集成模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 数据集成 | - | INTEGRATION_MENU | 数据集成菜单 | 菜单 | 可见"数据集成" | 菜单隐藏 |
| 数据集成 | 非结构化 | UNSTRUCTURED_INTEGRATION_CREATE | 新建 | 按钮 | 可创建非结构化集成任务 | 按钮禁用+提示 |
| 数据集成 | 非结构化 | UNSTRUCTURED_INTEGRATION_EXECUTE | 批量运行 | 按钮 | 可批量运行 | 按钮禁用+提示 |
| 数据集成 | 非结构化 | UNSTRUCTURED_INTEGRATION_STOP | 批量停止 | 按钮 | 可批量停止 | 按钮禁用+提示 |
| 数据集成 | 非结构化 | UNSTRUCTURED_INTEGRATION_DELETE | 批量删除 | 按钮 | 可批量删除 | 按钮禁用+提示 |
| 数据集成 | 结构化 | STRUCTURED_INTEGRATION_CREATE | 新建 | 按钮 | 可创建结构化集成任务 | 按钮禁用+提示 |
| 数据集成 | 结构化 | STRUCTURED_INTEGRATION_EXECUTE | 批量运行 | 按钮 | 可批量运行 | 按钮禁用+提示 |
| 数据集成 | 结构化 | STRUCTURED_INTEGRATION_PUBLISH | 批量发布 | 按钮 | 可发布任务 | 按钮禁用+提示 |
| 数据集成 | 结构化 | STRUCTURED_INTEGRATION_DELETE | 批量删除 | 按钮 | 可删除任务 | 按钮禁用+提示 |
| 数据集成 | 结构化 | STRUCTURED_INTEGRATION_MODIFY | 批量编辑 | 按钮 | 可编辑任务 | 按钮禁用+提示 |
3.8 工作流模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 工作流 | - | WORKFLOW_MENU | 工作流菜单 | 菜单 | 可见"工作流"菜单 | 菜单隐藏 |
| 工作流 | - | WORKFLOW_CREATE | 新建工作流 | 按钮 | 可创建DAG工作流 | 按钮禁用+提示 |
| 工作流 | - | WORKFLOW_IMPORT | 导入工作流 | 按钮 | 可导入工作流JSON | 按钮禁用+提示 |
| 工作流 | 运行记录 | WORKFLOW_INSTANCE_MENU | 运行记录菜单 | 菜单 | 可查看运行记录Tab | Tab隐藏 |
3.9 数据质量模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 数据质量 | - | QUALITY_MENU | 数据质量菜单 | 菜单 | 可见"数据质量"菜单 | 菜单隐藏 |
| 数据质量 | 规则模板 | QUALITY_TEMPLATE_CREATE | 创建自定义模板 | 按钮 | 可创建自定义质量模板 | 按钮禁用+提示 |
| 数据质量 | 质量规则 | QUALITY_OBJECT_ADD | 添加监控对象 | 按钮 | 可添加质量监控对象 | 按钮禁用+提示 |
3.10 其他功能模块(含白名单控制)
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 白名单 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|---|
| 模型服务 | - | MODEL_SERVICE_MENU | 模型服务菜单 | 菜单 | 否 | 可见菜单 | 菜单隐藏 |
| 模型服务 | - | MODEL_SERVICE_CREATE | 创建服务 | 按钮 | 否 | 可创建模型服务 | 按钮禁用+提示 |
| 内容理解 | - | CONTENT_UNDERSTANDING_MENU | 内容理解菜单 | 菜单 | 是 | 可见菜单 | 菜单隐藏 |
| 数据管道 | - | PIPELINE_DESIGNER_MENU | 数据管道菜单 | 菜单 | 是 | 可见菜单 | 菜单隐藏 |
| 数据管道 | - | PIPELINE_DESIGNER_CREATE | 新建管道 | 按钮 | 是 | 可创建Pipeline | 按钮禁用+提示 |
| 数据血缘 | - | LINEAGE_MENU | 数据血缘菜单 | 菜单 | 否 | 可见菜单 | 菜单隐藏 |
| 本体管理 | - | ONTOLOGY_MENU | 本体管理菜单 | 菜单 | 是 | 可见菜单 | 菜单隐藏 |
| 本体管理 | 对象类型 | ONTOLOGY_OBJECT_TYPE_CREATE | 创建对象类型 | 按钮 | 是 | 可创建ObjectType | 按钮禁用+提示 |
| 本体管理 | 关系类型 | ONTOLOGY_LINK_TYPE_CREATE | 创建关系类型 | 按钮 | 是 | 可创建LinkType | 按钮禁用+提示 |
| 逻辑建模 | - | LOGIC_MENU | 逻辑建模菜单 | 菜单 | 是 | 可见菜单 | 菜单隐藏 |
| 逻辑建模 | - | LOGIC_CREATE | 新建逻辑 | 按钮 | 是 | 可创建逻辑模型 | 按钮禁用+提示 |
| 数据搜索 | - | DATASEARCH_MENU | 数据搜索菜单 | 菜单 | 是 | 可见菜单 | 菜单隐藏 |
| 数据搜索 | - | DATASEARCH_CREATE | 新建搜索 | 按钮 | 是 | 可创建搜索任务 | 按钮禁用+提示 |
3.11 数据架构模块
| 模块 | 子模块 | 权限枚举值 | 权限名称 | 类型 | 有权限时 | 无权限时 |
|---|---|---|---|---|---|---|
| 数据架构 | 全部 | DATAFRAME_MENU | 数据架构菜单 | 菜单 | 可见"数据架构"及所有子菜单(主题域/标准/模型/指标) | 菜单隐藏 |
| 数据架构 | 全部 | DATAFRAME_EDIT | 数据架构编辑 | 按钮 | 可编辑主题域/标准/模型/指标 | 只读查看,编辑按钮禁用 |
四、角色与权限对应关系
4.1 系统管理员 (systemAdmin)
系统管理员由后端 verifySystemAdmin() API 判断,该接口是一个 POST 请求,返回布尔值。管理员拥有系统最高权限:
| 权限场景 | 是否拥有 |
|---|---|
| 访问所有页面 | 是 |
| 所有元数据操作 | 是 |
| 所有计算资源创建 | 是 |
| 所有数据集成操作 | 是 |
| 所有工作流操作 | 是 |
| 所有数据质量操作 | 是 |
| 所有数据架构编辑 | 是 |
| 查看元数据菜单(空间外) | 是(系统管理员或拥有 metastore VIEW 权限的用户) |
4.2 DataBuilderFullControl(Playground 管理员)
Playground 是百度智能云提供的产品体验/演示环境,用户无需真实购买产品即可体验 DataBuilder 功能。系统预先配置了演示账户和演示工作空间数据。
DataBuilderFullControl 权限用于区分 Playground 中的管理员和普通体验用户:
| 用户类型 | 权限场景 | 是否拥有 |
|---|---|---|
| Playground 管理员 | 访问空间外页面(主路由) | 是 |
| Playground 管理员 | 所有空间内功能菜单 | 是 |
| Playground 管理员 | 所有按钮级别操作 | 是 |
| Playground 普通用户 | 访问空间外页面(主路由) | 否(所有主路由被重定向到工作台固定页面) |
| Playground 普通用户 | 空间内功能菜单 | 取决于被分配的角色权限 |
FullControl 的判断通过 checkFullControl() API 调用实现,该接口在内部调用了 /api/auth/fullControl 端点,使用静默模式(silent=true),接口正常返回即表示用户有此权限。
4.3 白名单机制详解
什么是白名单?
白名单是百度智能云平台的一种灰度发布和功能管控机制。部分新功能模块在正式全量上线前,会先通过白名单的方式向特定用户/企业开放,用以收集反馈和控制风险。用户如果不在白名单中,则看不到对应功能模块的入口。
白名单如何工作?
- 后端接口
whitelistVerifyAll()在应用启动时自动调用,返回用户在所有功能上的白名单状态 - 返回结果是一个
featureTypes数组,每项包含featureType(白名单类型名称)和inWhitelist(是否在白名单中) - 前端将结果处理后存入 Redux Store 的
whiteListSlice - 在路由权限 HOC 中,如果检测到某个功能模块的菜单权限被禁用,且对应白名单为 false,则显示"白名单功能,开通请提工单"而不是普通无权限提示
白名单映射表
| 白名单 Key | 中文名称 | 影响的功能模块 | 未开通时的用户提示 |
|---|---|---|---|
| UnderstandingAndPipeline | 理解与管道 | 内容理解 + 数据管道 | "白名单功能,开通请提工单" |
| Ontology | 本体管理 | 本体管理 | 同上 |
| Logic | 逻辑建模 | 逻辑建模 | 同上 |
| DataSearch | 数据搜索 | 数据搜索 | 同上 |
| DataBuilder | 产品白名单 | 产品访问权限 | 无 |
| hideGpu | 隐藏GPU | 隐藏GPU相关按钮 | 按钮消失(理想汽车用户专用) |
| configModeDiJob | 可配置化模式 | 集成-可配置化模式 | 功能不可用 |
| computeLimit | 计算资源限制 | 最多2个Ray+2个Doris实例 | 超过限制时不可创建 |
白名单与产品功能发布的关系:白名单是百度智能云平台的一种 灰度发布机制。新功能开发完成后并非直接全量上线,而是先通过白名单 向特定用户或企业开放。用户如果在白名单中,可以看到新功能菜单入口并正常使用; 如果不在白名单中,菜单入口不显示,硬输入 URL 访问时显示"白名单功能,开通请提工单"。 白名单独立于角色权限——即使角色给了所有权限,不在白名单中仍然看不到对应功能。 随着产品迭代,白名单范围逐渐扩大,最终全量开放时移除白名单限制。
4.4 空间内角色-权限参考映射表
以下展示三种典型角色的权限配置(实际权限由空间管理员通过角色灵活配置):
| 权限分类 | 权限枚举值 | 管理员 | 开发者 | 访客 |
|---|---|---|---|---|
| 工作台菜单 | WORKSPACE_MENU | 是 | 是 | 是 |
| 项目创建 | PROJECT_CREATE | 是 | 是 | 否 |
| 新建文件夹 | DIRECTORY_CREATE | 是 | 是 | 否 |
| 导入文件 | FILE_IMPORT | 是 | 是 | 否 |
| 新建Notebook | NOTEBOOK_CREATE | 是 | 是 | 否 |
| 元数据菜单 | CATALOG_MENU | 是 | 是 | 是 |
| 创建目录 | CREATE_CATALOG | 是 | 否 | 否 |
| 创建数据源 | CREATE_CONNECTION | 是 | 否 | 否 |
| 读表 | READ_TABLE | 是 | 是 | 是 |
| 写表 | WRITE_TABLE | 是 | 是 | 否 |
| 创建表 | CREATE_TABLE | 是 | 否 | 否 |
| 计算资源菜单 | COMPUTE_MENU | 是 | 是 | 否 |
| 创建各类计算资源 | *_CREATE | 是 | 否 | 否 |
| 数据集成菜单 | INTEGRATION_MENU | 是 | 是 | 否 |
| 数据集成CRUD | *_CREATE/DELETE/... | 是 | 否 | 否 |
| 工作流菜单 | WORKFLOW_MENU | 是 | 是 | 否 |
| 新建工作流 | WORKFLOW_CREATE | 是 | 是 | 否 |
| 导入工作流 | WORKFLOW_IMPORT | 是 | 否 | 否 |
| 数据质量菜单 | QUALITY_MENU | 是 | 是 | 否 |
| 创建质量模板 | QUALITY_TEMPLATE_CREATE | 是 | 否 | 否 |
| 模型服务菜单 | MODEL_SERVICE_MENU | 是 | 是 | 否 |
| 创建模型服务 | MODEL_SERVICE_CREATE | 是 | 否 | 否 |
| 数据血缘菜单 | LINEAGE_MENU | 是 | 是 | 是 |
| 数据架构菜单 | DATAFRAME_MENU | 是 | 是 | 是 |
| 数据架构编辑 | DATAFRAME_EDIT | 是 | 是 | 否 |
| 本体管理菜单 | ONTOLOGY_MENU | 是 | 是* | 否 |
| 逻辑建模菜单 | LOGIC_MENU | 是 | 是* | 否 |
| 数据搜索菜单 | DATASEARCH_MENU | 是 | 是* | 否 |
| 内容理解菜单 | CONTENT_UNDERSTANDING_MENU | 是 | 是* | 否 |
| 数据管道菜单 | PIPELINE_DESIGNER_MENU | 是 | 是* | 否 |
是*表示除了角色权限外,还需要用户在该功能的白名单中。
五、前端权限管理实现详解(含注释)
本章节对权限系统的每一层实现代码提供详细的中文注释,帮助理解每一段代码的作用和在整个权限体系中的位置。
5.1 权限枚举定义
文件: src/api/permission/type.ts
以下是权限系统的核心枚举和类型定义,是整个权限体系的"字典"。
// ========== 角色类型 ==========
// 用于区分角色是用户创建的还是系统内置的
export enum RoleType {
User = 'USER', // 用户自定义角色,空间管理员创建,可编辑可删除
System = 'SYSTEM' // 系统预设角色,系统内置,不可删除
}
// ========== 资源类型(共39种) ==========
// 定义了系统中所有可以被权限管理的资源对象类型
export enum ResourceType {
Workspace = 'WORKSPACE', // 工作空间(顶层资源)
Metastore = 'METASTORE', // 元存储(空间外的数据源注册中心)
ResourcePool = 'RESOURCE_POOL', // 资源池(通用资源队列)
Workflow = 'WORKFLOW', // 工作流 DAG
Quality = 'QUALITY', // 数据质量检查
Directory = 'DIRECTORY', // 工作区文件夹
File = 'FILE', // 工作区文件
Notebook = 'NOTEBOOK', // Jupyter Notebook
Connection = 'CONNECTION', // 外部数据源连接
Catalog = 'CATALOG', // 元数据目录(Schema 的上级)
Schema = 'SCHEMA', // 数据模式/数据库
Table = 'TABLE', // 数据表
Volume = 'VOLUME', // 数据卷(非结构化数据容器)
Dataset = 'DATASET', // 数据集
Model = 'MODEL', // 模型(ML 模型)
Operator = 'OPERATOR', // 算子(数据处理单元)
UnstructuredIntegration = 'UNSTRUCTURED_INTEGRATION', // 非结构化数据集成任务
StructuredIntegration = 'STRUCTURED_INTEGRATION', // 结构化数据集成任务
IntegrationCompute = 'INTEGRATION_COMPUTE', // 集成专用计算资源
EtlCompute = 'ETL_COMPUTE', // ETL 常驻计算资源
EtlJobTemplate = 'ETL_JOB_TEMPLATE', // ETL 任务模板
AnalysisCompute = 'ANALYSIS_COMPUTE', // 分析计算资源
LogicCompute = 'LOGIC_COMPUTE', // 通用常驻计算资源
TableColumn = 'TABLE_COLUMN', // 表列(列级权限)
TableRow = 'TABLE_ROW', // 表行(行级权限)
ModelService = 'MODEL_SERVICE', // 模型在线服务
ResourceGroup = 'RESOURCE_GROUP', // 资源组
StructuredData = 'STRUCTURED_DATA', // 结构化数据(工作台右键创建)
MediaSet = 'MEDIA_SET', // 媒体集(工作台非结构化数据)
DataIntegration = 'DATA_INTEGRATION', // 数据集成总类型
Pipeline = 'PIPELINE', // 数据管道(已废弃?使用 PipelineDesigner)
PipelineDesigner = 'PIPELINE_DESIGNER', // 数据管道设计器
ContentUnderstanding = 'CONTENT_UNDERSTANDING', // 内容理解/分析
Ontology = 'ONTOLOGY', // 本体管理
Logic = 'LOGIC', // 逻辑设计
DataSearch = 'DATASEARCH' // 数据搜索
}
// ========== 权限点枚举(共75个) ==========
// 定义了系统中所有可操作的功能权限点
export enum Privilege {
// --- 通用权限(用于资源级别的权限分配) ---
All = 'ALL', // 全部权限的集合,在角色创建时使用
Manage = 'MANAGE', // 管理权限,层级最高,包含 Modify + Execute + View
Execute = 'EXECUTE', // 执行权限,包含 View
View = 'VIEW', // 查看权限,最基本的读权限
Modify = 'MODIFY', // 修改权限,包含 Execute + View
Use = 'USE', // 使用权限(如"使用数据源")
FullControl = 'FULL_CONTROL', // 完全控制(Playground 管理员专用)
// --- 空间管理相关 ---
WorkspacesMenu = 'WORKSPACES_MENU', // 左侧导航"工作空间"菜单是否显示
WorkspaceCreate = 'WORKSPACE_CREATE', // 是否允许创建新工作空间
// --- 元存储(空间外) ---
MetastoreMenu = 'METASTORE_MENU', // 左侧导航"元数据"菜单(空间外)是否显示
MetastoreCreate = 'METASTORE_CREATE', // 是否允许创建元存储
// --- 工作区(空间内) ---
WorkspaceMenu = 'WORKSPACE_MENU', // 工作台菜单是否显示
ProjectCreate = 'PROJECT_CREATE', // 是否允许创建工作台项目
DirCreate = 'DIRECTORY_CREATE', // 是否允许新建文件夹
FileImport = 'FILE_IMPORT', // 是否允许导入文件
NotebookCreate = 'NOTEBOOK_CREATE', // 是否允许新建 Jupyter Notebook
// --- 元数据目录(空间内) ---
CatalogMenu = 'CATALOG_MENU', // 元数据菜单(空间内)是否显示
CreateCatalog = 'CREATE_CATALOG', // 是否允许创建 Catalog
CreateConnection = 'CREATE_CONNECTION', // 是否允许创建外部数据源连接
Browse = 'BROWSE', // 是否允许浏览元数据目录
ReadVolume = 'READ_VOLUME', // 是否允许读取卷内容
ReadTable = 'READ_TABLE', // 是否允许读取表数据
ReadDataset = 'READ_DATASET', // 是否允许读取数据集
WriteVolume = 'WRITE_VOLUME', // 是否允许写入卷
WriteTable = 'WRITE_TABLE', // 是否允许写入表数据
WriteDataset = 'WRITE_DATASET', // 是否允许写入数据集
CreateSchema = 'CREATE_SCHEMA', // 是否允许创建 Schema
CreateVolume = 'CREATE_VOLUME', // 是否允许创建卷
CreateTable = 'CREATE_TABLE', // 是否允许创建表
CreateDataset = 'CREATE_DATASET', // 是否允许创建数据集
CreateDatasetVersion = 'CREATE_DATASET_VERSION', // 是否允许创建数据集新版本
CreateModel = 'CREATE_MODEL', // 是否允许创建模型
CreateModelVersion = 'CREATE_MODEL_VERSION', // 是否允许创建模型新版本
CreateOperator = 'CREATE_OPERATOR', // 是否允许创建算子
CreateOperatorVersion = 'CREATE_OPERATOR_VERSION', // 是否允许创建算子新版本
UseConnection = 'USE_CONNECTION', // 是否允许使用已有数据源
// --- 计算资源 ---
ComputeMenu = 'COMPUTE_MENU', // 计算资源菜单是否显示
IntegrationComputeCreate = 'INTEGRATION_COMPUTE_CREATE', // 是否允许创建集成计算资源
EtlComputeCreate = 'ETL_COMPUTE_CREATE', // 是否允许创建 ETL 常驻实例
EtlJobTemplateCreate = 'ETL_JOB_TEMPLATE_CREATE', // 是否允许创建任务模板
AnalysisComputeCreate = 'ANALYSIS_COMPUTE_CREATE', // 是否允许创建分析实例
LogicComputeCreate = 'LOGIC_COMPUTE_CREATE', // 是否允许创建通用常驻实例
ResourcePoolCreate = 'RESOURCE_POOL_CREATE', // 是否允许创建资源池
ResourceGroupCreate = 'RESOURCE_GROUP_CREATE', // 是否允许创建资源组
// --- 数据集成 ---
IntegrationMenu = 'INTEGRATION_MENU', // 数据集成菜单是否显示
UnstructuredIntegrationCreate = 'UNSTRUCTURED_INTEGRATION_CREATE', // 创建非结构化集成任务
UnstructuredIntegrationStop = 'UNSTRUCTURED_INTEGRATION_STOP', // 停止非结构化集成任务
UnstructuredIntegrationDelete = 'UNSTRUCTURED_INTEGRATION_DELETE', // 删除非结构化集成任务
UnstructuredIntegrationExecute = 'UNSTRUCTURED_INTEGRATION_EXECUTE', // 运行非结构化集成任务
StructuredIntegrationCreate = 'STRUCTURED_INTEGRATION_CREATE', // 创建结构化集成任务
StructuredIntegrationExecute = 'STRUCTURED_INTEGRATION_EXECUTE', // 运行结构化集成任务
StructuredIntegrationPublish = 'STRUCTURED_INTEGRATION_PUBLISH', // 发布结构化集成任务
StructuredIntegrationDelete = 'STRUCTURED_INTEGRATION_DELETE', // 删除结构化集成任务
StructuredIntegrationModify = 'STRUCTURED_INTEGRATION_MODIFY', // 编辑结构化集成任务
// --- 工作流 ---
WorkflowMenu = 'WORKFLOW_MENU', // 工作流菜单是否显示
WorkflowCreate = 'WORKFLOW_CREATE', // 是否允许创建工作流
WorkflowImport = 'WORKFLOW_IMPORT', // 是否允许导入工作流 JSON
WorkflowInstanceMenu = 'WORKFLOW_INSTANCE_MENU', // 运行记录 Tab 是否显示
// --- 数据质量 ---
DataQualityMenu = 'QUALITY_MENU', // 数据质量菜单是否显示
QualityTemplateCreate = 'QUALITY_TEMPLATE_CREATE', // 是否允许创建自定义质量模板
QualityObjectAdd = 'QUALITY_OBJECT_ADD', // 是否允许添加监控对象
// --- 模型服务 ---
ModelServiceMenu = 'MODEL_SERVICE_MENU', // 模型服务菜单是否显示
ModelServiceCreate = 'MODEL_SERVICE_CREATE', // 是否允许创建模型在线服务
// --- 内容理解(需白名单) ---
ContentUnderstandingMenu = 'CONTENT_UNDERSTANDING_MENU', // 内容理解菜单是否显示
// --- 数据管道(需白名单) ---
PipelineDesignerMenu = 'PIPELINE_DESIGNER_MENU', // 数据管道菜单是否显示
PipelineDesignerCreate = 'PIPELINE_DESIGNER_CREATE', // 是否允许创建 Pipeline
// --- 数据血缘 ---
LineageMenu = 'LINEAGE_MENU', // 数据血缘菜单是否显示
// --- 本体管理(需白名单) ---
OntologyMenu = 'ONTOLOGY_MENU', // 本体管理菜单是否显示
OntologyObjectTypeCreate = 'ONTOLOGY_OBJECT_TYPE_CREATE', // 是否允许创建对象类型
OntologyLinkTypeCreate = 'ONTOLOGY_LINK_TYPE_CREATE', // 是否允许创建关系类型
// --- 逻辑建模(需白名单) ---
LogicMenu = 'LOGIC_MENU', // 逻辑建模菜单是否显示
LogicCreate = 'LOGIC_CREATE', // 是否允许创建逻辑设计
// --- 数据搜索(需白名单) ---
DataSearchMenu = 'DATASEARCH_MENU', // 数据搜索菜单是否显示
DataSearchCreate = 'DATASEARCH_CREATE', // 是否允许创建搜索
// --- 数据架构 ---
DataArchitectureMenu = 'DATAFRAME_MENU', // 数据架构菜单是否显示(含主题域/标准/模型/指标)
DataArchitectureEdit = 'DATAFRAME_EDIT' // 数据架构编辑权限(是否能编辑主题域/标准/模型/指标)
}
// ========== 权限类型 ==========
// 区分单个权限点还是权限分组
export enum PrivilegeType {
Group = 'GROUP', // 权限分组,如 ALL 表示一组权限的集合
Single = 'SINGLE' // 单个权限点,一般传此类型
}
// ========== 主体类型(权限授予对象) ==========
// 定义权限可以被授予给用户、用户组还是角色
export enum PrincipalType {
User = 'USER', // 直接授权给单个用户
Group = 'GROUP', // 授权给用户组(组内所有成员继承权限)
Role = 'ROLE' // 授权给角色(拥有该角色的用户继承权限)
}
5.2 Redux 状态管理
权限数据在 Redux Store 中分为三个 Slice 来管理。
5.2.1 全局权限 Store(GlobalAuth)
文件: src/store/GlobalAuth.ts
这个 Slice 管理应用级别的权限状态,包括全局功能权限、空间内功能权限、页面状态等。
import {PermissionType, EditionInfo} from '@api/auth';
import {Privilege} from '@api/permission/type';
import {createSlice} from '@reduxjs/toolkit';
/**
* PageStatus 页面状态枚举
* 用于决定当前页面应该展示正常内容还是无权限提示
*/
export enum PageStatus {
Valid = 'valid', // 页面正常,渲染业务内容
NoPagePermission = 'noPagePermission', // 当前页面无权限,显示提示
NoWorkspacePermission = 'noWorkspacePermission' // 空间内无任何菜单权限
}
/**
* IGlobalAuthState 全局权限状态结构
* 这个接口定义了整个应用层面与权限相关的所有状态字段
*/
export interface IGlobalAuthState {
workspace: PermissionType;
// 工作空间级别的读写权限,来自 getWorkspacePermission 接口
// 值为 'readWrite' | 'readOnly' | undefined
metastore: PermissionType;
// 元存储级别的读写权限
editionInfo: EditionInfo;
// 产品版本信息,包含版本名称(免费版/标准版/专业版/企业版)、
// 运行状态(RUNNING/STOPPED)、过期时间、资源ID
workspacePermission: Partial<Record<Privilege, boolean>>;
// 【核心】空间内功能权限映射表
// Key 是 Privilege 枚举值(如 WORKSPACE_MENU),Value 是布尔值(true=有权限)
// 这个映射表由 getWorkspacePrivilege() 在进入工作空间时构建
// 所有 UI 层的权限判断都查询这个映射表
globalPermission: Partial<Record<Privilege, boolean>>;
// 全局(空间外)功能权限映射表
// 由 getGlobalPrivilege() 在应用启动时构建
// 包含空间外菜单的权限(如 WORKSPACES_MENU、METASTORE_MENU)
systemAdmin: boolean;
// 是否是系统管理员,由 verifySystemAdmin() API 返回
pageStatus: PageStatus;
// 当前页面状态,用于决定是否显示无权限页面
hasFullControl: boolean;
// 是否有 DataBuilderFullControl 权限(Playground 管理员专用)
}
/**
* 初始状态:所有权限默认为 null 或 false
* 在权限数据加载完成之前,UI 展示 Loading 状态
*/
const initialState: IGlobalAuthState = {
workspace: undefined,
metastore: undefined,
editionInfo: undefined,
workspacePermission: null,
globalPermission: null,
systemAdmin: false,
pageStatus: PageStatus.Valid,
hasFullControl: false
};
/**
* 创建 Redux Slice
* 定义了 6 个 reducer 用于更新权限状态
*/
const globalAuthSlice = createSlice({
name: 'globalAuth',
initialState,
reducers: {
// 更新全局(空间外)权限映射表
updateGlobalPermission: (state, action) => {
state.globalPermission = action.payload;
},
// 更新空间内功能权限映射表
// action.payload 格式: { WORKSPACE_MENU: true, CATALOG_MENU: false, ... }
updateWorkspacePermission: (state, action) => {
state.workspacePermission = action.payload;
},
// 更新系统管理员状态
updateSystemAdmin: (state, action) => {
state.systemAdmin = action.payload;
},
// 更新页面状态(Valid / NoPagePermission / NoWorkspacePermission)
updatePageStatus: (state, action) => {
state.pageStatus = action.payload;
},
// 更新产品版本信息(免费版/标准版/专业版/企业版)
updateEditionInfo: (state, action) => {
state.editionInfo = action.payload;
},
// 更新 FullControl 权限(Playground 管理员标记)
updateHasFullControl: (state, action) => {
state.hasFullControl = action.payload;
}
}
});
// 导出 actions 供 dispatch 使用
export const {
updateGlobalPermission,
updateWorkspacePermission,
updateEditionInfo,
updateHasFullControl
} = globalAuthSlice.actions;
export default globalAuthSlice.reducer;
5.2.2 工作空间资源权限 Store(WorkspaceAuth)
文件: src/store/WorkspaceAuth.ts
这个 Slice 管理与资源级别的读写权限,描述用户对每个资源类型的操作级别。
import {PermissionType} from '@api/auth';
import {createSlice} from '@reduxjs/toolkit';
/**
* IWorkspaceAuthState 工作空间资源权限状态
* 存储每种资源类型的读写权限等级
* 值来源:getWorkspacePermission() API 返回的 GetWorkspacePermissionResult
* 后端返回格式:{ catalog: ['readWrite'], table: ['readOnly'], ... }
* 前端取 [0] 后存储
*/
export interface IWorkspaceAuthState {
catalog: PermissionType; // 目录的读写权限
compute: PermissionType; // 计算资源的读写权限
file: PermissionType; // 文件的读写权限
job: PermissionType; // 任务的读写权限
schema: PermissionType; // Schema 的读写权限
table: PermissionType; // 表的读写权限
volume: PermissionType; // 卷的读写权限
workflow: PermissionType; // 工作流的读写权限
metastore: PermissionType; // 元存储的读写权限
workspace: PermissionType; // 工作空间的读写权限
metastoreAdmin: 'admin' | undefined;
// 是否是元存储管理员
// 后端返回 ["admin"],前端取 [0] 得到 'admin'
}
/**
* 初始状态:所有资源权限未加载时为 undefined
*/
const initialState: IWorkspaceAuthState = {
catalog: undefined,
compute: undefined,
file: undefined,
job: undefined,
schema: undefined,
table: undefined,
volume: undefined,
workflow: undefined,
metastore: undefined,
workspace: undefined,
metastoreAdmin: undefined
};
/**
* 创建 Slice
* updateWorkspaceAuth reducer 从后端返回的权限数组中取第一项
*/
const workspaceAuthSlice = createSlice({
name: 'workspaceAuth',
initialState,
reducers: {
updateWorkspaceAuth: (state, action) => {
// 遍历 11 个资源类型字段
['catalog', 'compute', 'file', 'job', 'schema',
'table', 'volume', 'workflow', 'metastore',
'workspace', 'metastoreAdmin'].forEach((key) => {
// 后端返回格式如 action.payload.table = ['readWrite']
// 取数组第一项 [0] 存入状态
state[key] = action.payload[key]?.[0];
});
}
}
});
export const {updateWorkspaceAuth} = workspaceAuthSlice.actions;
export default workspaceAuthSlice.reducer;
5.2.3 白名单 Store(WhiteList)
文件: src/store/whiteList.ts
白名单状态管理灰度功能是否对当前用户开放。
import {createSlice, PayloadAction} from '@reduxjs/toolkit';
import {IWhiteListResult, WhitelistName} from '@api/auth';
/**
* 理想汽车用户白名单结果(用于隐藏 GPU 按钮)
*/
export interface LiAutoWhiteListResult {
inWhitelist: boolean;
}
/**
* 后端白名单字段名 -> 前端 Store 字段名的映射
* 后端使用大写,前端使用 camelCase
*/
export const WebWhiteListName = {
[WhitelistName.UnderstandingAndPipeline]: 'understandingAndPipeline',
[WhitelistName.Ontology]: 'ontology',
[WhitelistName.Logic]: 'logic',
[WhitelistName.DataSearch]: 'dataSearch'
};
/**
* WhiteListState 白名单状态结构
*/
export interface WhiteListState {
liAutoWhiteList?: LiAutoWhiteListResult; // 理想汽车用户专用
understandingAndPipeline: boolean; // 理解与管道白名单(控制内容理解+数据管道)
ontology: boolean; // 本体管理白名单
logic: boolean; // 逻辑建模白名单
dataSearch: boolean; // 数据搜索白名单
allWhitelistStatus?: IWhiteListResult[]; // 后端返回的所有原始白名单数据
}
/**
* 初始状态:所有白名单默认为 true(乐观策略,先展示功能)
* 接口加载后会用真实值覆盖
*/
const initialState: WhiteListState = {
liAutoWhiteList: {inWhitelist: false},
understandingAndPipeline: true,
ontology: true,
logic: true,
dataSearch: true,
allWhitelistStatus: undefined
};
const whiteListSlice = createSlice({
name: 'whiteList',
initialState,
reducers: {
// 更新理想汽车白名单
updateLiAutoWhiteList: (state, action: PayloadAction<LiAutoWhiteListResult | undefined>) => {
state.liAutoWhiteList = action.payload;
},
// 更新所有白名单状态(核心方法)
// 在 whitelistVerifyAll() 接口返回后被调用
updateAllWhitelistStatus: (state, action: PayloadAction<IWhiteListResult[] | undefined>) => {
state.allWhitelistStatus = action.payload;
// 遍历后端返回的每条白名单状态
action.payload?.forEach((item) => {
// 通过映射表找到对应的前端字段名
if (WebWhiteListName[item.featureType]) {
// 将白名单状态写入对应字段
state[WebWhiteListName[item.featureType]] = item.inWhitelist;
}
});
}
}
});
export const {updateLiAutoWhiteList, updateAllWhitelistStatus} = whiteListSlice.actions;
export default whiteListSlice.reducer;
5.2.4 Store 组合入口
文件: src/store/index.ts
import {configureStore} from '@reduxjs/toolkit';
import sumSlice from './sumSlice';
import globalAuthSlice from './GlobalAuth'; // 全局权限
import workspaceAuthSlice from './WorkspaceAuth'; // 空间资源权限
import notebookSlice from './notebookSlice';
import cdcIntegrationSlice from './cdcIntegrationSlice';
import workflowSlice from './workflow';
import workflowNewSlice from './workflowSlice';
import workEditorSlice from './workEditorSlice';
import whiteListSlice from './whiteList'; // 白名单
import pipelineSlice from './pipelineSlice';
import pipelinePreviewSlice from './pipelinePreviewSlice';
// 使用 configureStore 合并所有 Slice
const store = configureStore({
reducer: {
sumSlice,
globalAuthSlice, // ← 全局权限(包含 workspacePermission 映射表)
workspaceAuthSlice, // ← 空间资源权限(包含各资源类型读写级别)
whiteListSlice, // ← 白名单(灰度功能开关)
notebookSlice,
workflowNewSlice,
workflowSlice,
cdcIntegrationSlice,
workEditorSlice,
pipelineSlice,
pipelinePreviewSlice
}
});
// 导出类型供 useSelector 和 useDispatch 使用
export type IAppState = ReturnType<typeof store.getState>;
export type IAppDispatch = typeof store.dispatch;
export default store;
5.3 权限 API 接口
文件: src/api/permission/index.ts
所有权限相关的 API 接口都定义在此文件中,按功能分为以下几类。
// ========== 角色管理 ==========
// 获取工作空间下的所有角色列表
getRolesList(workspaceId)
// 请求: GET /authorization/roles?workspaceId=xxx
// 返回: { success: true, result: RoleItem[] }
// RoleItem 结构: { name, description, privileges: Privilege[], type: RoleType, createdAt, createdBy }
// 获取指定角色的详细信息
getRoleDetail(workspaceId, roleName)
// 请求: GET /authorization/role?workspaceId=xxx&roleName=yyy
// 返回: { success: true, result: RoleDetail }
// 创建新角色
createRole(workspaceId, params)
// 请求: PUT /authorization/roles?workspaceId=xxx
// Body: { name: "自定义角色", description: "...", privileges: ["WORKSPACE_MENU", ...] }
// 返回: { success: true, result: RoleDetail }
// 删除角色
deleteRole(workspaceId, roleName)
// 请求: DELETE /authorization/roles?workspaceId=xxx&roleName=yyy
// 返回: { success: true, result: boolean }
// ========== 主体管理 ==========
// 获取工作空间下的用户/用户组/角色列表
getWorkspaceUserList({
workspaceId, // 空间 ID
principalType, // 可选,筛选主体类型:USER/GROUP/ROLE
principalName, // 可选,按名称搜索
order, // 可选,排序方式
pageNo, // 分页页码
pageSize // 每页条数
})
// 返回: {
// success: true,
// result: {
// principalInfos: [{
// principal: { id: "xxx", name: "张三", type: "USER" },
// roles: [{ grantor: "admin", name: "开发者" }],
// createdAt: "2024-01-01T00:00:00Z"
// }],
// totalCount: 10
// }
// }
// 更新工作空间用户的角色(添加或移除角色)
updateWorkspaceUser(workspaceId, [
{
principal: { id: "xxx", name: "张三", type: "USER" },
addRoleNames: ["开发者"], // 要添加的角色名
removeRoleNames: [] // 要移除的角色名
}
])
// 请求: PATCH /authorization/principals?workspaceId=xxx
// 返回: { success: true, result: boolean }
// ========== 资源权限查询与修改 ==========
// 获取某个资源上的所有权限授权记录
getResourceList(resourceType, resourceId, workspaceId)
// 请求: GET /authorization/acl?resourceType=TABLE&resourceId=xxx&workspaceId=yyy
// 返回: { success: true, result: ResourcePermissionInfo[] }
// ResourcePermissionInfo: {
// principal: { id, name, type }, // 授权主体
// privilege: { type: "SINGLE", privilege: "READ_TABLE" }, // 权限类型和权限点
// inheritedFrom: { id, name, type }, // 继承来源(如果权限来自父级资源)
// grantor: "admin" // 授权者
// }
// 更新资源权限(批量添加或移除权限)
updateResourcePermission([
{
principal: { id: "xxx", name: "张三", type: "USER" },
addPrivileges: [{ privilege: "READ_TABLE", type: "SINGLE" }],
removePrivileges: [],
resource: { type: "TABLE", id: "table-id" }
}
], workspaceId)
// 请求: PATCH /authorization/acl?workspaceId=xxx
// 返回: { success: true, result: boolean }
// ========== 鉴权(验证是否有权限) ==========
// 单个资源鉴权
verifyAuth({
privileges: [{ privilege: Privilege.VIEW, type: PrivilegeType.Single }],
resource: { id: "resource-id", type: ResourceType.Table }
})
// 请求: POST /authorization/verify
// 说明: privileges 数组中多个权限是"或"的关系,只要满足一个即返回 true
// 返回: { success: true, result: boolean }
// 批量资源鉴权
batchVerifyAuth([
{ privileges: [...], resource: { id: "id1", type: ResourceType.Table } },
{ privileges: [...], resource: { id: "id2", type: ResourceType.Volume } }
])
// 请求: POST /authorization/verifyBatch
// 返回: { success: true, result: [true, false, ...] }
// 校验当前用户是否是系统管理员
verifySystemAdmin()
// 请求: POST /authorization/verifySystemAdmin
// 返回: { success: true, result: boolean }
// ========== 行/列权限(表级别的细粒度权限) ==========
// 查询表的行过滤规则
getTableRowFilterRule(workspaceId, tableName)
// 返回: [{ name: "rule1", expression: "region = 'cn'", principal: {...} }]
// 设置行权限过滤规则
updateTableRowFilterRule(workspaceId, tableName, {
principals: [{ id: "...", type: "USER" }],
expression: "region = 'cn'", // SQL 过滤表达式
extraAcl: [...]
})
// 删除行权限过滤规则
deleteTableRowFilterRule(workspaceId, [
{ tableName: "...", rowFilterName: "rule1" }
])
// 检查用户对表的列是否有权限
checkTablePermission(workspaceId, tableName, [
{ principal: { id: "...", type: "USER" } }
])
// 返回: [true, false, ...] 对应每个 principal 是否有权限
5.4 权限工具函数
文件: src/utils/auth.ts
这些工具函数负责将后端返回的权限数据转换为前端 Redux Store 需要的格式。
import {batchVerifyAuth, verifyAuth, verifySystemAdmin} from '@api/permission';
import {Privilege, PrivilegeType, ResourceType} from '@api/permission/type';
import {queryMetastore} from '@api/metastore';
import {queryWorkspaceDetail} from '@api/workspace';
/**
* 空间内所有可分配的功能权限列表(共约 50 个)
* 这个列表定义了 workspacePermission 映射表中包含的所有权限 key
* 在 getWorkspacePrivilege() 中用于从后端返回的 privileges 数组构建布尔映射
*/
export const WorkspacePrivileges = [
// --- 菜单权限 ---
Privilege.WorkspaceMenu, // 工作台菜单
Privilege.CatalogMenu, // 元数据菜单
Privilege.ComputeMenu, // 计算资源菜单
Privilege.IntegrationMenu, // 数据集成菜单
Privilege.WorkflowMenu, // 工作流菜单
Privilege.WorkflowInstanceMenu, // 运行记录菜单
Privilege.DataQualityMenu, // 数据质量菜单
Privilege.ContentUnderstandingMenu, // 内容理解菜单
Privilege.PipelineDesignerMenu, // 数据管道菜单
Privilege.PipelineDesignerCreate, // 数据管道创建
Privilege.ModelServiceMenu, // 模型服务菜单
Privilege.LineageMenu, // 数据血缘菜单
Privilege.OntologyMenu, // 本体管理菜单
Privilege.DataArchitectureMenu, // 数据架构菜单
Privilege.LogicMenu, // 逻辑建模菜单
Privilege.DataSearchMenu, // 数据搜索菜单
// --- 按钮/操作权限 ---
Privilege.ResourceGroupCreate,
Privilege.IntegrationComputeCreate,
Privilege.EtlComputeCreate,
Privilege.EtlJobTemplateCreate,
Privilege.AnalysisComputeCreate,
Privilege.LogicComputeCreate,
Privilege.ResourcePoolCreate,
Privilege.UnstructuredIntegrationCreate,
Privilege.UnstructuredIntegrationStop,
Privilege.UnstructuredIntegrationDelete,
Privilege.UnstructuredIntegrationExecute,
Privilege.StructuredIntegrationCreate,
Privilege.StructuredIntegrationExecute,
Privilege.StructuredIntegrationPublish,
Privilege.StructuredIntegrationDelete,
Privilege.StructuredIntegrationModify,
Privilege.WorkflowCreate,
Privilege.WorkflowImport,
Privilege.DataArchitectureEdit,
Privilege.Modify,
Privilege.QualityObjectAdd,
Privilege.QualityTemplateCreate,
Privilege.ProjectCreate,
Privilege.ModelServiceCreate,
Privilege.OntologyObjectTypeCreate,
Privilege.OntologyLinkTypeCreate,
Privilege.LogicCreate,
Privilege.DataSearchCreate
];
/**
* getGlobalPrivilege - 获取全局(空间外)权限
* 在 App.tsx 应用启动时调用
* 返回全局权限映射表,包含空间外菜单是否可见
*
* 实现细节:
* 1. 同时调用 verifySystemAdmin() 和 queryMetastore() 两个接口
* 2. 空间列表菜单始终可见(workspacesMenu = true)
* 3. 元存储菜单仅对系统管理员或有 VIEW 权限的用户可见
*
* 返回格式示例:
* {
* WORKSPACES_MENU: true,
* METASTORE_MENU: false // 非管理员,不含 VIEW 权限
* }
*/
export const getGlobalPrivilege = async (): Promise<
Partial<Record<Privilege, boolean>>
> => {
const [systemAdminRes, metastoreRes] = await Promise.all([
verifySystemAdmin(), // 检查是否为系统管理员
queryMetastore(true) // 静默模式查询元存储信息
]);
const isSystemAdmin = systemAdminRes.result;
const privileges = metastoreRes.result?.privileges;
return {
[Privilege.WorkspacesMenu]: true, // 空间列表始终可见
[Privilege.MetastoreMenu]: // 元存储菜单条件可见
isSystemAdmin || privileges?.includes(Privilege.View)
};
};
/**
* getWorkspacePrivilege - 获取空间内所有功能权限
* 在 pages/index.tsx 的 Main 组件中进入空间时调用
* 返回完整的空间内权限映射表
*
* 实现细节:
* 1. 同时查询工作空间详情和元存储信息
* 2. 将 workspaceDetail 返回的 privileges 数组与 WorkspacePrivileges
* 列表交叉,构建 { Privilege: boolean } 的键值对映射
* 3. 额外从 metastore 查询 CreateCatalog 和 CreateConnection 权限
*
* 输入:workspaceId - 当前空间 ID
*
* 返回格式示例:
* {
* WORKSPACE_MENU: true,
* CATALOG_MENU: false,
* COMPUTE_MENU: true,
* CREATE_TABLE: false,
* ...共约50个键值对
* }
*/
export const getWorkspacePrivilege = async (workspaceId) => {
const [workspaceRes, metastoreRes] = await Promise.all([
queryWorkspaceDetail({id: workspaceId}), // 查询空间详情
queryMetastore(true, workspaceId) // 查询空间内元存储
]);
const metaPrivileges = metastoreRes.result?.privileges;
// 从后端返回的 privileges 字符串数组构建布尔映射
// workspaceRes.result.privileges 示例: ["WORKSPACE_MENU", "COMPUTE_MENU", ...]
// Object.fromEntries 将其转换为: { WORKSPACE_MENU: true, COMPUTE_MENU: true, ... }
return {
...Object.fromEntries(
WorkspacePrivileges.map((key) => [
key,
// 后端 privileges 数组中包含该 key 则表示用户有此权限
workspaceRes.result?.privileges.includes(key)
])
),
// 以下两个权限不来自空间详情,而来自元存储接口
[Privilege.CreateCatalog]:
metaPrivileges?.includes(Privilege.CreateCatalog),
[Privilege.CreateConnection]:
metaPrivileges?.includes(Privilege.CreateConnection)
};
};
/**
* checkSimplePrivilege - 简单权限判断(层级包含关系)
* 用于在权限管理弹窗中判断权限选项的启用/禁用
*
* 权限层级:Manage > Modify > Execute > View
* 如果用户拥有 Manage 权限,则自动拥有 Modify、Execute、View 权限
*
* 参数:
* privileges: 用户拥有的权限列表(如 ["MANAGE", "VIEW"])
* auth: 需要检查的目标权限(如 Privilege.View)
*
* 返回:用户是否有权限执行该操作
*
* 示例:
* checkSimplePrivilege(["MANAGE"], Privilege.View) → true
* checkSimplePrivilege(["VIEW"], Privilege.Modify) → false
*/
export const checkSimplePrivilege = (
privileges: string[],
auth: Privilege
): boolean => {
const privilegesMap = {
[Privilege.Manage]: [
Privilege.Manage, Privilege.Modify,
Privilege.Execute, Privilege.View
],
[Privilege.Modify]: [
Privilege.Modify, Privilege.Execute, Privilege.View
],
[Privilege.Execute]: [Privilege.Execute, Privilege.View],
[Privilege.View]: [Privilege.View]
};
// 遍历用户拥有的权限,检查是否包含了目标权限
return privileges.some((item) => {
const allowed = privilegesMap[item as Privilege];
return allowed?.includes(auth) ?? false;
});
};
5.5 应用初始化 - 权限加载全流程
文件: src/App.tsx
应用启动时按以下顺序加载权限数据:
第一步:产品激活和版本检查(init 函数内)
公有云用户需要先验证产品是否激活和购买。如果是 Playground 体验用户,则检查 FullControl 权限。
async function init() {
setInitLoading(true);
if (!isPrivate && !isPlayGroundUser) {
// ===== 公有云普通用户 =====
// 1. 检查 IAM STS 角色是否创建(产品开通的前置条件)
const iamRoleInfo = await queryIamStsRole();
// 2. 检查产品是否已激活
const isEdapActive = await queryEdapActive();
if (isEdapActive.result?.isActivated) {
// 已激活,设置产品激活状态
appDispatch({ type: AppContextActionType.ACTIVATE_PRODUCT });
} else {
// 未激活,跳转到产品激活页面
window.location.href = `${baseUrl}/edap/#/active`;
}
// 3. 检查产品版本信息(免费版/标准版/专业版/企业版)
const editionRes = await getEditionInfo();
if (editionRes.result.name) {
// 有版本信息 → 存储到 Redux
store.dispatch({
type: 'globalAuth/updateEditionInfo',
payload: editionRes.result
});
// result 结构: { name: "STANDARD", status: "RUNNING", expiredTime: "...", resourceId: "..." }
} else {
// 无版本 → 尚未购买,跳转到购买页面
window.location.href = `${baseUrl}/edap/#/billing?type=OPEN`;
}
} else if (isPlayGroundUser) {
// ===== Playground 体验用户 =====
// 检查是否拥有 DataBuilderFullControl 权限
const res = await checkFullControl(true); // silent=true 静默模式
if (res.success) {
store.dispatch({ type: 'globalAuth/updateHasFullControl', payload: true });
}
}
setInitLoading(false);
}
第二步:获取全局权限(useEffect)
useEffect(() => {
// 获取空间外的菜单权限
getGlobalPrivilege().then((res) => {
// res 格式: { WORKSPACES_MENU: true, METASTORE_MENU: false }
store.dispatch({
type: 'globalAuth/updateGlobalPermission',
payload: res
});
});
}, []);
第三步:获取白名单状态(useEffect)
useEffect(() => {
// 理想汽车用户专用:检查是否需要隐藏 GPU 按钮
checkLiAutoWhiteList().then((res) => {
store.dispatch({ type: 'whiteList/updateLiAutoWhiteList', payload: res.result });
});
// 获取所有功能的用户白名单状态
whitelistVerifyAll().then((res) => {
// res.result.featureTypes 示例:
// [{ featureType: "UnderstandingAndPipeline", inWhitelist: true },
// { featureType: "Ontology", inWhitelist: false }, ...]
store.dispatch(updateAllWhitelistStatus(res.result.featureTypes));
});
}, []);
第四步:进入工作空间时获取空间内权限(在 pages/index.tsx Main 组件中)
// 每当 workspaceId 变化时重新加载空间内权限
useEffect(() => {
if (workspaceId) {
// 1. 获取空间内功能权限(菜单+按钮级别的)
getWorkspacePrivilege(workspaceId).then((res) => {
store.dispatch({
type: 'globalAuth/updateWorkspacePermission',
payload: res // { WORKSPACE_MENU: true, CATALOG_MENU: false, ... }
});
});
// 2. 获取空间内资源级别权限(读/写级别的)
getWorkspacePermission(workspaceId).then((res) => {
store.dispatch({
type: 'workspaceAuth/updateWorkspaceAuth',
payload: res.result
// res.result: { table: ['readWrite'], volume: ['readOnly'], ... }
});
});
}
}, [workspaceId]);
5.6 路由级权限控制
5.6.1 菜单配置中绑定权限
文件: src/pages/index.tsx(约 1847 行)
每一个菜单项都通过 privilege 字段绑定对应的权限枚举值。
// ========== 空间外菜单 ==========
// 这些菜单在工作空间选择页面(未进入具体空间时)的左侧导航显示
export const menus: MenuItem[] = [
{
menuName: '工作空间',
key: urls.manageWorkspace, // 路由路径
isNavMenu: true, // 是否显示在左侧导航
Component: WorkspaceList, // 对应页面组件
privilege: Privilege.WorkspacesMenu // ← 绑定的权限 key
},
{
menuName: '元数据',
key: urls.metastore,
isNavMenu: true,
Component: Metastore,
privilege: Privilege.MetastoreMenu // ← 绑定的权限 key
}
];
// ========== 空间内菜单 ==========
// 这些菜单在进入具体工作空间后的左侧导航显示
export const workspaceMenus: MenuItem[] = [
{
menuName: '工作台',
key: urls.workArea,
isNavMenu: true,
Component: WorkArea,
privilege: Privilege.WorkspaceMenu // ← 绑定的权限 key
},
{
menuName: '元数据',
key: urls.metaData,
isNavMenu: true,
Component: MetaData,
privilege: Privilege.CatalogMenu // ← 绑定的权限 key
},
// ... 更多菜单配置(共约 80 个菜单项)
];
// 空间内所有菜单权限列表
// 用于在 WithPermissionHoc 中判断用户是否有任一空间菜单权限
export const WorkspaceMenuPrivileges = [
Privilege.WorkspaceMenu,
Privilege.CatalogMenu,
Privilege.ComputeMenu,
Privilege.IntegrationMenu,
Privilege.WorkflowMenu,
Privilege.WorkflowInstanceMenu,
Privilege.DataQualityMenu,
Privilege.ContentUnderstandingMenu,
Privilege.PipelineDesignerMenu,
Privilege.ModelServiceMenu,
Privilege.LineageMenu,
Privilege.OntologyMenu,
Privilege.DataArchitectureMenu,
Privilege.LogicMenu,
Privilege.DataSearchMenu
];
5.6.2 路由生成
文件: src/router/router.tsx
// 主路由(空间外):每个菜单项生成一条路由
// privilege 仅作为元数据标记,主路由不通过 HOC 做权限拦截
// 权限过滤是在 Main 组件内部的 filteredMenus 中完成的
const mainRouter = flattenedMenuList.map((menu) => ({
path: menu.key,
element: <Main menus={menus} component={menu.Component} />,
privilege: menu.privilege
}));
// 工作空间路由(空间内):通过 withPermissionHoc 包裹每个子页面
// 每个子页面的权限由 HOC 在渲染时实时检查
const workspaceRouter = [{
path: '/workspace',
element: <Main menus={workspaceMenus} />,
children: workspaceFlattenedMenuList.map((menu) => ({
path: menu.key,
// withPermissionHoc 的第二个参数是菜单的 privilege 值
// HOC 内部会从 Redux 读取 workspacePermission 映射表来判断权限
element: withPermissionHoc(
<LazyComponent component={menu.Component} />,
menu.privilege // ← 传递权限 key 给 HOC
),
privilege: menu.privilege
}))
}];
// Playground 用户路由处理
// 无 FullControl 的体验用户 → 所有主路由重定向到预设的工作台页面
const router = useMemo(() => {
const list = [
...(isPlayGroundUser && !hasFullControl
? playgroundMainRouter // 全部重定向路由
: mainRouter), // 正常路由
...workspaceRouter,
{ path: '*', element: <Navigate to={urls.manageWorkspace} replace /> }
];
return createHashRouter(list);
}, [appState.isActivated, isPlayGroundUser, hasFullControl]);
5.6.3 权限 HOC 核心逻辑
文件: src/router/WithPermissionHoc.tsx
withPermissionHoc 是整个路由级权限控制的核心。它在每个子页面渲染前执行权限检查。
import store, {IAppState} from '@store/index';
import {useSelector} from 'react-redux';
import {WorkspaceMenuPrivileges} from '../pages/index';
import {WorkspaceNoPermission} from '@components/WithoutPermissionPage/WorkspaceNoPermission';
import {PageNoPermission, PageNoPermissionIcon} from '@components/WithoutPermissionPage/PageNoPermission';
import {Loading} from 'acud';
import {PageStatus} from '@store/GlobalAuth';
import {Privilege} from '@api/permission/type';
/**
* withPermissionHoc - 路由级权限高阶组件
*
* 工作原理:
* 1. 接收要渲染的页面元素和该页面对应的权限 key
* 2. 从 Redux Store 的 workspacePermission 映射表中查找用户是否有对应权限
* 3. 根据查找结果决定渲染正常页面还是无权限提示
*
* 参数:
* element: 要渲染的页面组件
* needPermission: 该页面对应的 Privilege 枚举值(如 Privilege.CatalogMenu)
*/
export const withPermissionHoc = (element, needPermission) => {
function WithPermissionComponents() {
// ===== 步骤1:从 Redux 读取当前空间的功能权限映射表 =====
// workspacePermission 格式:
// { WORKSPACE_MENU: true, CATALOG_MENU: false, COMPUTE_MENU: true, ... }
const permission = useSelector(
(state: IAppState) => state.globalAuthSlice.workspacePermission
);
// ===== 步骤2:从 Redux 读取白名单状态 =====
// 这四个白名单用于判断部分功能模块是否对当前用户开放
const understandingAndPipeline = useSelector(
(state: IAppState) => state.whiteListSlice.understandingAndPipeline
);
const ontology = useSelector(
(state: IAppState) => state.whiteListSlice.ontology
);
const logic = useSelector(
(state: IAppState) => state.whiteListSlice.logic
);
const dataSearch = useSelector(
(state: IAppState) => state.whiteListSlice.dataSearch
);
// ===== 步骤3:权限未加载 → 显示 Loading =====
// 首次进入空间时 workspacePermission 为 null,等接口返回后再判断
if (!permission) {
return <Loading />;
}
// ===== 步骤4:检查用户是否有任一空间内菜单权限 =====
// 遍历 WorkspaceMenuPrivileges 列表,只要有一个为 true 就表示有权限
// 如果用户连任一菜单权限都没有,直接显示"空间无权限",不再检查具体页面
const hasWorkspaceMenu = WorkspaceMenuPrivileges.some(
(item) => permission?.[item]
);
if (!hasWorkspaceMenu) {
store.dispatch({
type: 'globalAuth/updatePageStatus',
payload: PageStatus.NoWorkspacePermission
});
return <WorkspaceNoPermission />;
}
// ===== 步骤5:检查当前页面是否有权限 =====
// needPermission 为空(未设置 privilege 字段)或映射表中为 true → 有权限
if (!needPermission || permission?.[needPermission]) {
store.dispatch({
type: 'globalAuth/updatePageStatus',
payload: PageStatus.Valid
});
return element; // ← 正常渲染页面
}
// ===== 步骤6:无权限 → 区分是白名单功能还是常规无权限 =====
store.dispatch({
type: 'globalAuth/updatePageStatus',
payload: PageStatus.NoPagePermission
});
// 白名单功能判断:如果某个功能模块的菜单权限被禁用,且用户不在对应白名单中
// 则提示"白名单功能,开通请提工单",否则提示常规的无权限文案
if (!understandingAndPipeline &&
(needPermission === Privilege.ContentUnderstandingMenu ||
needPermission === Privilege.PipelineDesignerMenu)) {
return <PageNoPermission
content="白名单功能,开通请提工单"
icon={PageNoPermissionIcon.whiteList}
/>;
}
if (!ontology && needPermission === Privilege.OntologyMenu) {
return <PageNoPermission
content="白名单功能,开通请提工单"
icon={PageNoPermissionIcon.whiteList}
/>;
}
if (!logic && needPermission === Privilege.LogicMenu) {
return <PageNoPermission
content="白名单功能,开通请提工单"
icon={PageNoPermissionIcon.whiteList}
/>;
}
if (!dataSearch && needPermission === Privilege.DataSearchMenu) {
return <PageNoPermission
content="白名单功能,开通请提工单"
icon={PageNoPermissionIcon.whiteList}
/>;
}
// 常规无权限(用户角色中没有分配该页面的菜单权限)
return <PageNoPermission
content="您当前暂无该页面权限,请联系空间管理员进行授权"
/>;
}
return <WithPermissionComponents />;
};
5.7 组件级权限控制
组件级权限控制通过三个 React 组件实现:AuthButton(按钮)、AuthComponents(通用组件)、AuthMenuItem(菜单项)。它们的核心机制是:无权限时不是隐藏组件,而是将其置为 disabled 状态并用 Tooltip 包裹显示提示信息。
5.7.1 AuthButton(按钮权限控制)
文件: src/components/AuthComponentsAntd/AuthButton.tsx
import {Button, Tooltip} from '@baidu/qianfan-antd-kit';
import type {ButtonProps} from '@baidu/qianfan-antd-kit';
import {isNil} from 'lodash';
import React, {memo, useMemo} from 'react';
import {TooltipConfig, TooltipType} from './constants';
/**
* AuthButton Props 定义
* 继承 Button 的所有属性,额外增加权限控制相关属性
*/
interface AuthButtonProps extends ButtonProps {
isAuth: boolean; // 是否有权限(核心:从 Redux 传入)
isDisabled?: boolean; // 额外的禁用条件(如业务逻辑判断)
tooltipType?: TooltipType; // 无权限时 Tooltip 的提示类型
disabledTooltip?: string; // 自定义 Tooltip 提示文案(优先级最高)
}
/**
* AuthButton 权限控制按钮组件
*
* 行为:
* - 有权限(isAuth=true):正常渲染按钮,可点击
* - 无权限(isAuth=false):按钮变为 disabled 状态,hover 显示权限不足提示
* - 按钮始终可见(不隐藏),让用户知道此功能存在
*/
const AuthButton: React.FC<AuthButtonProps> = ({
isAuth,
tooltipType = TooltipType.Resource, // 默认为资源权限不足
disabledTooltip,
children,
disabled: propsDisabled, // 外部传入的 disabled 属性
...props
}) => {
// 计算最终 disabled 状态
// 1. 外部没传 disabled → 纯粹由 isAuth 决定
// 2. 外部传了 disabled → 取"外部禁用 OR 无权限"的并集
const disabled = useMemo(() => {
if (isNil(propsDisabled)) return !isAuth; // 无权限即禁用
return propsDisabled || !isAuth; // 合并禁用条件
}, [isAuth, propsDisabled]);
// 构建按钮元素
const button = (
<Button {...props} disabled={disabled}>
{children}
</Button>
);
// 确定 Tooltip 文案:自定义文案 > 类型预设文案
const tooltipTitle = disabledTooltip || TooltipConfig[tooltipType];
// 有权限直接返回按钮;无权限用 Tooltip 包裹后返回
return <>
{isAuth
? button
: <Tooltip title={tooltipTitle}>{button}</Tooltip>
}
</>;
};
// 使用 memo 避免不必要的重渲染
export default memo(AuthButton);
使用示例:
import {AuthButton} from '@components/AuthComponentsAntd';
import {Privilege} from '@api/permission/type';
import {useSelector} from 'react-redux';
function MyPage() {
// 从 Redux 读取权限映射表
const permission = useSelector(
state => state.globalAuthSlice.workspacePermission
);
return (
<AuthButton
// isAuth: 从权限映射表中查询 DATAFRAME_EDIT 权限
isAuth={permission?.[Privilege.DataArchitectureEdit]}
// tooltipType: 功能权限不足时提示"请向空间管理员申请"
tooltipType={TooltipType.Function}
>
编辑
</AuthButton>
);
}
5.7.2 AuthComponents(通用组件权限包裹)
文件: src/components/AuthComponentsAntd/AuthComponents.tsx
import {Tooltip} from '@baidu/qianfan-antd-kit';
import {TooltipPlacement} from 'antd/lib/tooltip';
import React, {cloneElement, memo, ReactElement} from 'react';
import {TooltipConfig, TooltipType} from './constants';
/**
* AuthComponents Props
* 适用于任意 ReactElement(不限于 Button,也包括 Select、Input 等)
*/
interface AuthComponentsProps {
isAuth: boolean; // 是否有权限
children: ReactElement; // 要包裹的任意 React 组件
tooltipType?: TooltipType; // 提示类型
disabledTooltip?: string; // 自定义提示
placement?: TooltipPlacement; // Tooltip 弹出方向
}
/**
* AuthComponents 通用权限包裹组件
*
* 与 AuthButton 不同,此组件通过 cloneElement 方法
* 动态向子组件注入 disabled: true 属性,适用于任何表单组件
*
* 使用场景:需要对 Select、Input、Switch 等非按钮组件进行权限控制时
*/
const AuthComponents: React.FC<AuthComponentsProps> = ({
isAuth,
tooltipType = TooltipType.Resource,
disabledTooltip,
children,
placement = 'top'
}) => {
// 有权限:原样返回子组件,不做任何修改
if (isAuth) return children;
// 无权限:克隆子元素并强制注入 disabled: true
const controlledChild = cloneElement(children, {
disabled: !isAuth
});
return (
<Tooltip
title={disabledTooltip || TooltipConfig[tooltipType]}
placement={placement}
>
{controlledChild}
</Tooltip>
);
};
export default memo(AuthComponents);
5.7.3 AuthMenuItem(菜单项权限控制)
文件: src/components/AuthComponentsAntd/AuthMenuItem.tsx
import {Menu, Tooltip} from '@baidu/qianfan-antd-kit';
import {MenuItemProps} from 'antd/lib/menu/MenuItem';
import React, {memo} from 'react';
import {TooltipConfig, TooltipType} from './constants';
/**
* AuthMenuItem Props
* 继承 MenuItem 属性,增加权限控制
*/
interface AuthMenuItemProps extends MenuItemProps {
isAuth: boolean; // 是否有权限
tooltipType?: TooltipType; // 提示类型
disabledTooltip?: string; // 自定义提示
}
/**
* AuthMenuItem 菜单项权限控制
*
* 用于下拉菜单中的菜单项权限控制
* 无权限时菜单项变为 disabled 状态并显示提示
*/
const AuthMenuItem: React.FC<AuthMenuItemProps> = ({
isAuth,
tooltipType = TooltipType.Resource,
disabledTooltip,
children,
disabled,
...props
}) => {
const isNotAuthOrDisabled = !isAuth || disabled;
const tooltipTitle = disabledTooltip || TooltipConfig[tooltipType];
return isNotAuthOrDisabled ? (
<Menu.Item {...props} disabled={isNotAuthOrDisabled}>
<Tooltip title={tooltipTitle}>
{children}
</Tooltip>
</Menu.Item>
) : (
<Menu.Item {...props}>{children}</Menu.Item>
);
};
export default memo(AuthMenuItem);
5.7.4 Tooltip 类型定义
文件: src/components/AuthComponentsAntd/constants.ts
// Tooltip 提示类型:区分权限不足的原因
export enum TooltipType {
Function = 'function', // 功能权限不足(按钮级),提示"请向空间管理员申请"
Resource = 'resource', // 资源权限不足(数据级),提示"无权限执行该操作"
NotSupport = 'notSupport' // 功能暂不支持
}
// 各类型对应的中文提示文案
export const TooltipConfig = {
[TooltipType.Function]: '您暂无该按钮权限,请向空间管理员申请',
[TooltipType.Resource]: '您无权限执行该操作',
[TooltipType.NotSupport]: '暂不支持'
};
5.8 数据级权限控制 Hooks
5.8.1 useWorkspaceAuth - 同步权限查询
文件: src/hooks/useWorkspaceAuth.ts
不需要 API 调用,直接从 Redux Store 读取权限映射表。
import {useSelector} from 'react-redux';
import {IAppState} from '@store/index';
import {Privilege} from '@api/permission/type';
/**
* useWorkspaceAuth - 同步查询空间内功能权限
*
* 纯同步操作,从 Redux 中读取已加载的权限映射表
* 适用于 UI 组件的条件渲染(如表单按钮是否可点击)
*
* 参数:
* privileges: 需要查询的权限列表
* (如 [Privilege.ReadTable, Privilege.WriteTable])
*
* 返回:
* 权限键值对,key 为权限枚举值,value 为布尔值
* 例:{ ReadTable: true, WriteTable: false }
*
* 注意:如果权限数据尚未加载(null),所有权限默认为 false
*/
export default function useWorkspaceAuth(
privileges: Privilege[]
): Partial<Record<Privilege, boolean>> {
const permission = useSelector(
(state: IAppState) => state.globalAuthSlice.workspacePermission
);
// 遍历请求的权限列表,从映射表中取值,未加载则返回 false
return privileges.reduce(
(pre, cur) => ({
...pre,
[cur]: permission ? permission?.[cur] : false
}),
{}
);
}
5.8.2 useVerifyAuth - 异步实时鉴权
文件: src/hooks/useVerifyAuth.ts
需要调用后端 API 进行实时鉴权,适用于资源级权限验证。
import {batchVerifyAuth, verifyAuth} from '@api/permission';
import {VerifyAuthParams} from '@api/permission/type';
import {useCallback, useEffect, useState} from 'react';
/**
* useVerifyAuth - 异步实时鉴权 Hook
*
* 通过调用后端 /authorization/verify 接口进行实时的权限验证
* 适用于需要对特定资源进行鉴权的场景(如检查用户是否可读取某张表)
*
* 参数:
* resourceAuth: 鉴权参数数组,每项包含权限列表和资源信息
* 示例:[{ privileges: [{ privilege: VIEW, type: SINGLE }],
* resource: { id: "table-123", type: ResourceType.Table } }]
*
* 返回:
* [auth]: boolean 数组,每个元素对应输入数组中该资源的鉴权结果
*
* 鉴权逻辑:
* - 单个资源:调用 verifyAuth()
* - 多个资源:调用 batchVerifyAuth() 批量鉴权
* - privileges 数组中多个权限是"或"关系,只要满足一个即返回 true
*/
export function useVerifyAuth(resourceAuth: VerifyAuthParams[]) {
const [auth, setAuth] = useState<boolean[]>([]);
const getVerifyAuth = useCallback(async () => {
if (resourceAuth?.length > 1) {
// 批量鉴权
const res = await batchVerifyAuth(resourceAuth);
setAuth(res.result); // result 是 boolean[],如 [true, false, true]
} else {
// 单个鉴权
const res = await verifyAuth(resourceAuth[0]);
setAuth([res.result]); // 包装成数组 [boolean]
}
}, [resourceAuth]);
// resourceAuth 变化时自动重新鉴权
useEffect(() => {
getVerifyAuth();
}, [getVerifyAuth]);
return [auth];
}
5.8.3 useHasFullControl - FullControl 检查
文件: src/hooks/useHasFullControl.ts
import {useSelector} from 'react-redux';
import {IAppState} from '@store/index';
/**
* useHasFullControl - 检查用户是否有 DataBuilderFullControl 权限
*
* 专门用于 Playground 场景:区分管理员和普通体验用户
* Playground 管理员拥有 FullControl,可以访问所有功能
*
* 返回:true 表示是 Playground 管理员
*/
export default function useHasFullControl(): boolean {
const hasFullControl = useSelector(
(state: IAppState) => state.globalAuthSlice.hasFullControl
);
return hasFullControl;
}
5.9 权限管理弹窗组件
文件: src/components/PermissionManage/index.tsx
权限管理弹窗允许空间管理员对特定资源(如表、卷、工作流等)进行权限分配。
主要功能:
- 权限类型切换:对于表资源,支持切换"整表权限 / 列权限 / 行权限"三种授权维度
- 授权操作:新增用户/用户组/角色对该资源的权限
- 取消授权:移除已授权的权限记录
- 权限继承显示:显示权限是从哪个父级资源继承来的
- 系统角色保护:系统预设角色不可撤销
- 继承权限保护:从父级继承来的权限不可在当前层级撤销
5.10 无权限页面组件
文件: src/components/WithoutPermissionPage/
三种无权限状态的展示:
| 状态 | 组件 | 展示内容 | 触发场景 |
|---|---|---|---|
| 页面无权限 | PageNoPermission |
"页面无权限" + "请联系空间管理员" | 用户角色没有分配当前页面的菜单权限 |
| 白名单未开通 | PageNoPermission(icon=whiteList) |
"页面无权限" + "白名单功能,开通请提工单" | 功能模块需要白名单但用户不在白名单中 |
| 空间无权限 | WorkspaceNoPermission |
用户没有空间内任何菜单的权限 | 用户被添加到空间但未分配任何角色 |
| 路由错误 | ErrorBoundary |
"无法找到页面" | 404 或路由匹配失败 |
六、完整权限工作流
6.1 应用启动阶段的权限加载流程
当用户打开 DataBuilder 应用时,权限系统按照以下顺序逐步完成初始化:
第一阶段:产品状态检查。 应用首先判断当前是公有云环境还是私有化环境。如果是公有云环境,需要依次检查 IAM STS 角色是否已创建(这是产品开通的前置条件),然后检查产品是否已激活。如果尚未激活,用户会被重定向到产品激活页面。激活检查通过后,还会查询当前产品的版本信息(免费版 TRIAL、标准版 STANDARD、专业版 PROFESSIONAL 还是企业版 ENTERPRISE),以及版本状态是否正常。如果是 Playground 体验环境,则跳过这些步骤,直接检查体验用户是否拥有 DataBuilderFullControl 管理员权限。
第二阶段:全局权限加载。 在初始化完成后,应用会并行发起两个关键请求:一是调用 verifySystemAdmin() 判断当前用户是否为系统管理员;二是调用 queryMetastore() 查询元存储的权限信息。这两个请求的结果被 getGlobalPrivilege() 函数合并处理,生成全局权限映射表并 dispatch 到 Redux Store。此时,空间列表菜单始终可见,而元存储菜单仅在用户是系统管理员或拥有 VIEW 权限时才显示。
第三阶段:白名单状态获取。 同时,应用还会发起白名单查询请求。whitelistVerifyAll() 接口返回用户在所有灰度功能上的白名单状态,前端将结果存入 Redux 的 whiteListSlice。白名单状态将影响后续路由 HOC 中无权限提示的文案(区分"白名单功能,开通请提工单"和常规的"请联系空间管理员")。
第四阶段:进入工作空间。 当用户选择一个工作空间进入后,Main 组件检测到 URL 中的 workspaceId 参数变化,触发空间内权限的加载。这个过程包含两个并行请求:getWorkspacePrivilege() 从工作空间详情的 privileges 字段中提取所有功能权限,构建 { Privilege: boolean } 映射表,存入 workspacePermission;getWorkspacePermission() 获取资源级别的读写权限,如 { table: ['readWrite'], volume: ['readOnly'] },存入 workspaceAuth。
以上四个阶段完成之后,所有权限数据均已就位,路由开始渲染。
6.2 页面路由匹配与权限拦截
当用户导航到某个具体页面时,React Router 匹配到对应的路由,然后 withPermissionHoc 高阶组件开始执行权限拦截。
首先,HOC 从 Redux Store 读取 workspacePermission 权限映射表。如果映射表为 null(权限数据尚未加载),则显示 Loading 加载动画,等待权限数据就绪。
权限数据就绪后,HOC 首先检查用户是否拥有空间内任一菜单的权限。这是通过遍历 WorkspaceMenuPrivileges 列表(包含所有菜单权限如 WORKSPACE_MENU、CATALOG_MENU 等)来完成的。如果用户连一个菜单权限都没有,说明用户虽然被添加到了工作空间,但没有被分配任何功能角色,此时显示"空间无权限"页面。
如果用户至少有一个菜单权限,HOC 继续检查当前访问的具体页面是否有权限。检查方式很简单:查看传入的 needPermission 参数(即该页面在菜单配置中绑定的 privilege 值)是否在权限映射表中为 true。如果为 true 或者 needPermission 本身为空(表示该页面不需要权限检查),则正常渲染页面。
如果权限检查失败,说明用户角色没有被分配该页面的权限。此时 HOC 进一步区分失败原因:检查该页面对应的功能模块是否需要白名单(内容理解、数据管道需要 UnderstandingAndPipeline 白名单;本体管理需要 Ontology 白名单;逻辑建模需要 Logic 白名单;数据搜索需要 DataSearch 白名单)。如果功能需要白名单且用户不在白名单中,显示"白名单功能,开通请提工单";如果是常规的角色权限不足,显示"您当前暂无该页面权限,请联系空间管理员进行授权"。
6.3 页面内部的组件级权限控制
当页面正常渲染后,页面内部的按钮、表单、菜单等 UI 组件也受到权限控制。
按钮权限: 使用 AuthButton 组件包裹需要权限控制的按钮。AuthButton 接收 isAuth 参数,该参数通常从 Redux 的 workspacePermission 中查询对应的 Privilege 值获得。如果 isAuth=true,按钮正常可点击;如果 isAuth=false,按钮变为 disabled 状态,鼠标悬停时显示 Tooltip 提示。按钮不会隐藏,让用户知道这个功能存在,只是当前没有权限使用。
通用组件权限: 使用 AuthComponents 包裹任意 React 元素(如 Select 下拉框、Input 输入框等)。通过 cloneElement 技术动态向子组件注入 disabled 属性,实现与 AuthButton 相同的禁用效果。
菜单项权限: 使用 AuthMenuItem 包裹下拉菜单中的菜单项,无权限时菜单项变为灰色不可点击状态。
6.4 数据级别的细粒度权限控制
除了路由和组件级别的权限控制,DataBuilder 还支持对具体数据资源的细粒度权限管理。
功能权限查询(同步): 使用 useWorkspaceAuth Hook 可以从 Redux 中同步读取已加载的权限映射表。例如,在数据表的操作栏中,需要判断用户是否有 READ_TABLE 和 WRITE_TABLE 权限,来决定是否显示"查看数据"和"编辑表"按钮。
资源鉴权(异步): 使用 useVerifyAuth Hook 可以向后端发起实时的权限验证请求。这通常用于一些需要动态判断的场景,比如用户尝试打开某张表时,需要调用 verifyAuth 接口实时检查用户对该表的访问权限。批量鉴权 batchVerifyAuth 支持一次请求验证多个资源的权限。
行级和列级权限: 对于表资源,DataBuilder 支持更精细的行权限和列权限。行权限通过 SQL 过滤表达式实现(如 region = 'cn' 限制只能看特定区域的数据),列权限则可以限制用户只能访问特定的列。这些在权限管理弹窗中通过切换"整表权限 / 列权限 / 行权限"来配置。
6.5 权限判断的总优先级
当用户尝试访问某个功能或资源时,系统按照以下优先级依次判断:
- 白名单检查(最高优先级):功能模块是否在用户的白名单中?不在白名单中则直接拒绝访问,提示提工单开通。
- 系统管理员检查:用户是否是系统管理员?管理员拥有所有功能权限,跳过后续检查。
- FullControl 检查(Playground 专用):Playground 体验用户是否拥有 FullControl 权限?管理员可访问全部功能。
- 路由级权限检查(菜单权限
XXX_MENU):用户角色是否有该页面的菜单访问权限? - 组件级权限检查(操作权限
XXX_CREATE/XXX_DELETE等):用户角色是否有执行该操作的权限? - 数据级权限检查(读写权限
READ_TABLE/WRITE_TABLE等):用户对具体资源是否有读或写的权限? - 资源读写级别检查(
PermissionType):对资源是 readWrite、readOnly 还是无权限?
6.6 权限加载的三个批次与时机详解
权限数据并非一次性全部加载,而是按照三个批次在不同时间点逐步获取。需要注意区分两类概念:权限配置的加载(管理员在弹窗里看到谁有什么权限)和权限的生效(普通用户访问数据时后端自动过滤)。下面以用户从打开空间到操作元数据模块的完整路径为例说明。
批次一:进入空间时 — 模块级功能权限(一次性加载,存入 Redux)
用户点击某个工作空间进入时,Main 组件检测到 workspaceId 变化,并行发起两个请求:
getWorkspacePrivilege():从工作空间详情的privileges数组构建功能权限映射表,包含CATALOG_MENU(元数据菜单是否可见)、CREATE_CATALOG(能否创建目录)、CREATE_SCHEMA(能否创建模式)等约 50 个功能开关,存入globalAuthSlice.workspacePermission。这些是 Privilege 枚举值 → boolean 的映射。getWorkspacePermission():从后端获取资源类型级别的粗粒度权限(注意:是资源类型,不是具体资源实例)。返回格式如{ catalog: ['readWrite'], schema: ['readOnly'], table: ['readWrite'] },取[0]后存入workspaceAuthSlice。这是 PermissionType(三档粗粒度),仅作为后端保底机制,前端业务组件中实际较少使用。
此时已确定:① 元数据左侧菜单是否可见;② 创建 Catalog、创建 Schema 等按钮是否可用。尚未加载:具体某个 Catalog 下有哪些 Schema、某张表谁有读写权限。
批次二:进入元数据模块浏览目录树 — 不发权限请求(后端 SQL 层过滤)
用户点击左侧"元数据"菜单后,浏览 Catalog → Schema → Table 的目录树。此过程的前端行为:
- 控制按钮显隐:左侧树渲染时调用
useWorkspaceAuth([Privilege.CreateCatalog]),从 Redux 同步读取批次一已加载的权限映射表,决定"创建 Catalog"等按钮是否显示。不发起任何网络请求。 - 目录树的内容:前端调用普通的
listSchemas、listTables等数据 API,后端在查询时自动过滤——用户没有读权限的资源根本不会出现在列表中。前端完全不知道哪些资源被过滤掉了,也无需知道。
批次三:进入具体资源详情页 — 随数据 API 返回该资源的操作权限
当用户点击某个 Schema(如 catalog.default)进入详情页时,PanelSchema 组件调用 getSchemaDetail(workspaceId, 'catalog.default')。这个数据查询接口的返回结果中直接包含了当前用户对此 Schema 的权限列表:
// getSchemaDetail 返回结构
res.result.schema.privileges = ["MANAGE", "VIEW"]
// 存入组件 state: const [authList, setAuthList] = useState<Privilege[]>([]);
此 authList 数组随后用于:
- "权限管理"Tab 是否可见:通过
checkSimplePrivilege(authList, MANAGE)判断。若不含MANAGE,Tab 完全不渲染。 - "重命名 Schema"是否可用:同样判断
MANAGE。 - VIEW 在此处未被前端实际使用:后端返回它只是因为
MANAGE ⊃ VIEW的层级关系,前端代码中PanelSchema.tsx没有任何地方读取VIEW。
同样的模式适用于 Table 详情页(PanelTable.tsx)、Catalog 详情页等——进入页面时随数据 API 返回该资源上的 privileges 数组,无需额外权限请求。
批次补充:权限管理 Tab — 管理员查看/修改授权配置(完全独立于数据访问)
用户若拥有 MANAGE 权限,可以看到"权限管理"Tab。点击后触发 getResourceList(ResourceType.Schema, 'catalog.default'),查询该资源上所有主体(用户/用户组/角色)被授予的权限记录:
- 张三 — 管理 (MANAGE)
- 李四 — 全部权限 (ALL, GROUP)
- 开发组 — 读表 (READ_TABLE)
这些数据存入 PermissionManage 组件本地 useState,用于管理员增删授权。切换"列权限/行权限"Radio 时分别调 getResourceList(TABLE_COLUMN, ...) 和 getTableRowFilterRule()。
关键区分:此处的权限配置加载与普通用户的数据访问完全独立。普通用户访问表数据时,行列权限已经在后端 SQL 查询层自动生效(SELECT 只返回有权限的列、WHERE 自动拼接行过滤条件)。前端全程不感知权限过滤逻辑——不调 verifyAuth,不调 checkTablePermission,只负责渲染后端返回的已过滤数据。
完整时间线(以用户操作元数据为例)
- 进入空间 →
getWorkspacePrivilege()加载 CATALOG_MENU、CREATE_CATALOG 等功能开关 → ReduxworkspacePermission - 点击元数据菜单 →
useWorkspaceAuth同步读 Redux 控制按钮,后端listCatalogs自动过滤无权限资源 - 点击某个 Catalog → Schema →
listSchemas/listTables后端过滤,前端不加载权限 - 点击某个 Schema 进入详情 →
getSchemaDetail()随数据返回privileges: ["MANAGE"]→ 决定"权限管理"Tab 可见 + "重命名"可用 - 点击权限管理 Tab(仅管理员) →
getResourceList(SCHEMA, name)拉取所有主体授权记录 → 组件本地 state - 切换列/行权限 Radio(仅管理员) →
getResourceList(TABLE_COLUMN, ...)/getTableRowFilterRule()→ 组件本地 state - 普通用户预览表数据 → 普通数据 API,后端 SQL 层自动应用行列权限过滤,前端不感知
文档说明:本文档基于
console-databuilder前端代码深度分析生成,涵盖文件包括:src/api/permission/type.ts、src/api/permission/index.ts、src/api/auth.ts、src/store/GlobalAuth.ts、src/store/WorkspaceAuth.ts、src/store/whiteList.ts、src/store/index.ts、src/utils/auth.ts、src/router/router.tsx、src/router/WithPermissionHoc.tsx、src/pages/index.tsx、src/App.tsx、src/components/AuthComponentsAntd/全部文件、src/components/PermissionManage/、src/components/WithoutPermissionPage/、src/hooks/useVerifyAuth.ts、src/hooks/useWorkspaceAuth.ts、src/hooks/useHasFullControl.ts等。