跳转到内容

子系统管理设计文档

本期的“子系统”不是一个新的 NocoBase Application,也不是一个独立部署单元。它是本插件增加的一层业务配置和访问上下文,用来决定:

  • 当前用户看到哪个子系统的登录设置;
  • 当前子系统显示什么系统名称、Logo 和语言配置;
  • 当前子系统允许访问哪些已有路由;
  • 用户在登录后可以切换到哪些子系统。

现有用户、角色权限和业务数据关系保持不变。本插件只增加“子系统配置”和“子系统路由范围”这一层,不改变 NocoBase 原有的用户、角色、ACL、数据模型和路由实现。

同一客户可能由多个业务系统组合而成。例如,某客户可以同时使用:

  • 子系统 A;
  • 子系统 B;
  • 子系统 C。

这些系统仍然运行在同一个 NocoBase 平台中,但登录页展示内容、系统名称和可访问菜单可以不同。用户不需要维护多套账号,也不需要改变现有业务数据共享方式。

本期只解决以下问题:

  1. 管理员可以按子系统维护登录页设置。
  2. 管理员可以按子系统维护系统设置。
  3. 管理员可以按子系统选择可访问的已有路由。
  4. 登录后的用户可以在顶栏快速切换有权访问的子系统。
功能本期要求
登录设置 Tabs在现有“登录设置”页面增加子系统 Tabs,每个 Tab 对应一个子系统的登录配置
系统设置 Tabs在现有“系统设置”页面增加同一组子系统 Tabs,每个 Tab 对应一个子系统的系统配置
路由访问配置将“菜单”按路由理解,放入登录设置页面;展示系统全部已有路由,按子系统勾选可访问路由
子系统切换在现有顶栏右上角导航区域增加切换入口
域名识别主系统和每个子系统配置唯一访问域名,运行时只根据请求域名判断当前系统
用户与权限继续使用现有统一用户、角色和 ACL,不新建一套用户权限体系
数据共享保持现有数据共享方式,不在本期重新设计数据边界

以下内容不属于本期,也不应写入本期实现范围:

  • 不重构主界面,不重新设计门户首页;
  • 不做整体视觉主题、布局和样式改造;
  • 不创建独立的 NocoBase Application 或新的进程;
  • 不做独立数据库、独立数据库 Schema 或业务数据隔离;
  • 不做独立用户表、独立角色体系、独立 Token 或独立会话;
  • 不做独立认证体系、跨域 SSO 和多环境部署;域名只用于识别当前系统,不代表独立运行实例;
  • 不做子系统启动、停止、升级、监控等运行平台能力;
  • 不修改 NocoBase 核心源码;
  • 不修改 NocoBase 官方插件源码;
  • 不依赖已废弃的多应用插件或历史共享语义。

主系统继续使用平台当前的登录设置和系统设置,作为兼容已有用户和配置的默认对象。主系统也必须配置明确的访问域名,不能把未配置的未知域名自动当成主系统。

子系统是本插件登记的业务系统配置对象,至少需要有:

  • 子系统名称;
  • 访问域名;
  • 启用状态;
  • 登录设置;
  • 系统设置;
  • 可访问路由范围。

访问域名用于运行时识别子系统,必须全局唯一。子系统名称只用于 Tabs 和顶栏切换器展示,不能用于请求判定。

用户当前访问域名所映射的主系统或某个子系统称为当前上下文。登录设置、系统设置、路由展示和右上角切换器都根据当前上下文计算。

切换子系统的本质是跳转到目标子系统的域名。域名改变后,由目标域名重新确定上下文;用户身份体系保持不变,但目标域名是否已有有效登录状态需要单独判断。

当前子系统不能根据用户、角色或登录账号推断,因为同一个用户可能访问多个子系统。本插件只根据请求域名判定,规则如下:

  1. 主系统和每个子系统都配置唯一的访问域名,形成“域名 -> 系统”的映射关系。
  2. 用户打开登录页时,服务端根据该请求的域名查找对应系统,再返回该系统的登录设置和系统设置。
  3. 登录后的页面请求和接口请求继续使用当前域名,服务端每次都按域名解析当前系统,不依赖浏览器保存的子系统值。
  4. 用户从右上角切换器选择目标系统时,浏览器跳转到目标系统配置的域名;目标域名重新完成系统识别、登录状态检查和路由权限计算。
  5. 生产环境按规范化后的主机名精确匹配:统一转为小写,并去除端口和末尾的点。协议和路径不参与子系统判定。
  6. 经过反向代理时,只能在请求来自可信代理的前提下读取代理传递的原始域名;其他请求以实际 Host 为准,防止伪造域名获得错误配置。
  7. 未配置域名、重复域名、已停用系统的域名都不能进入业务页面,也不能自动回退到主系统。
  8. URL 查询参数、请求体或普通请求头中即使出现系统内部 ID,也不得改变域名判定结果。

这里的“登录的是哪个子系统”准确含义是“用户当前访问的域名属于哪个系统”,而不是为每个子系统重新创建一套账号或认证服务。本期仍使用同一套 NocoBase 用户和认证能力。

域名判定的部署前提是:各系统域名的 DNS、HTTPS 证书和反向代理已经配置,并且这些域名都能转发到同一个 NocoBase 实例。反向代理必须保留并可信地传递原始主机名。本插件负责域名与系统配置的匹配,不负责创建 DNS 记录、申请证书或搭建反向代理。

保留现有登录设置表单和字段,在页面顶部增加子系统 Tabs:

登录设置
[主系统] [子系统 A] [子系统 B] [子系统 C]
当前 Tab 的登录页配置
- 布局
- 背景图
- 标题字体大小
- 技术支持信息
- 其他现有登录设置字段
当前 Tab 的路由访问范围
- 全部路由树
- 路由勾选
[提交]

Tabs 只负责切换配置对象,不改变现有表单的使用方式。用户在某个 Tab 中提交时,只保存该子系统的数据,不能覆盖其他子系统或主系统的配置。

  • 主系统 Tab 展示和保存现有主系统登录设置;
  • 子系统 Tab 展示本插件为该子系统保存的登录设置;
  • 新建子系统时,可以用主系统当前值作为初始值,但之后不再实时继承;
  • 子系统未配置某个可选字段时,使用本插件定义的默认值,不隐式修改主系统配置;
  • 登录页面实际展示内容由当前访问域名映射的系统决定;
  • 本期不新增独立登录认证流程,仍使用现有 NocoBase 登录能力。

提交成功后应明确提示当前保存的子系统名称。切换 Tabs 或刷新页面后,已保存的数据必须保持一致,不能因为切换 Tab 而丢失表单内容。

“菜单”在本设计中统一理解为“桌面路由访问范围”。路由配置直接读取系统中已有的桌面路由目录,不复制一套新的菜单数据。

现有路由可能包含以下类型:

  • 分组;
  • 页面;
  • Flow 页面;
  • 链接;
  • 标签页。

管理界面应展示全部路由及其层级关系。隐藏状态、路由类型和父子关系作为路由信息显示,不应在读取时静默丢弃。

路由按树形结构展示,每个子系统单独保存一份勾选结果:

  • 勾选页面,表示该子系统允许进入该页面;
  • 勾选分组,表示允许使用该分组下已授权的子路由;
  • 勾选子路由时,必要的父级分组自动作为导航路径保留;
  • 取消父级分组时,其下级路由全部取消;
  • 保存后显示已选择路由数量;
  • 不同子系统的勾选结果互不覆盖。

父级分组只是展示和导航结构,不应被当作绕过页面权限的特殊权限。

子系统路由范围是现有权限体系之外的附加限制,最终访问结果为:

最终可访问路由 = 现有角色/ACL允许的路由 ∩ 当前子系统允许的路由

因此:

  • 本插件不能因为勾选路由而扩大用户原有 ACL 权限;
  • 用户没有现有 ACL 权限的路由,即使在子系统中被勾选,也不能访问;
  • 未被当前子系统勾选的路由,不出现在该子系统的菜单中;
  • 直接输入未授权路由地址时,服务端仍需拒绝访问或按平台规则返回无权限结果,不能只依靠前端隐藏菜单;
  • 路由权限配置只针对路由入口,不改变数据行级、字段级和操作级 ACL。

不再单独设计“菜单管理”页面。路由访问配置放在登录设置页面的当前子系统区域内,与登录页配置一起维护。这样管理员选择子系统后,可以在同一个上下文中完成“登录页展示什么”和“该子系统能进入哪里”两类配置。

在现有系统设置页面顶部增加与登录设置完全一致的子系统 Tabs:

系统设置
[主系统] [子系统 A] [子系统 B] [子系统 C]
当前 Tab 的系统配置
- 系统名称
- Logo
- 启用的语言
- 其他现有系统设置字段
[提交]
  • 主系统 Tab 继续使用当前 NocoBase 系统设置;
  • 子系统 Tab 的名称、Logo、语言等数据由本插件按子系统独立保存;
  • 在某个子系统 Tab 中保存,只影响该子系统上下文;
  • 子系统之间不共享可变的系统名称和 Logo;
  • 本期不因为系统设置 Tabs 而改造主界面布局或全局主题;
  • 现有依赖系统设置的 NocoBase 功能仍以平台公开能力为准,本插件只为当前子系统提供对应配置值。

登录设置和系统设置使用同一组子系统 Tabs、同一套子系统记录、域名映射和启用状态。两处页面切换到同一个子系统时,应读取同一个配置对象,避免出现“登录设置属于 A,系统设置却属于 B”的情况。

在现有顶栏右上角导航区域增加子系统切换入口。控件应嵌入现有顶栏,不新增独立门户页,不改变现有顶栏其他按钮的含义和排列规则。

切换器展示:

  • 当前子系统名称;
  • 当前子系统域名;
  • 当前用户可以访问且处于启用状态的子系统列表;
  • 主系统及其已配置域名。

无权访问或已停用的子系统不能出现在普通用户的切换列表中。管理人员可以在配置页面看到停用对象并进行维护,但停用对象不应成为普通用户的可用入口。

用户选择子系统后:

  1. 取得目标子系统配置的访问域名;
  2. 浏览器完整跳转到目标域名,不在 URL 中附加系统内部 ID;
  3. 目标域名根据自身域名映射重新确定当前子系统;
  4. 检查目标域名的登录状态,并重新加载该子系统允许的路由树;
  5. 当前路径在目标子系统中仍可访问时保留该路径,否则进入该子系统的第一个可访问页面;
  6. 顶栏显示新的当前子系统名称。

当前 NocoBase 客户端认证令牌默认保存在浏览器 localStorage,不同域名之间不会天然共享。目标域名已经存在有效登录状态时可以直接进入;否则应显示目标域名对应的登录页并要求正常登录。跨域切换后自动免登录属于 SSO 范围,不在本期实现。无论是否需要重新登录,都不创建新的用户账号,也不改变现有角色和数据权限。

对象本期原则
用户继续使用 NocoBase 现有统一用户
登录认证继续使用现有认证能力,本期只切换登录页配置,不新建认证体系
角色继续使用现有角色
ACL继续使用现有资源、操作、字段和数据权限
路由在现有 ACL 之外增加子系统路由范围,最终权限取交集
业务数据保持当前数据共享关系,不新增跨子系统数据隔离规则
文件、工作流和其他插件数据保持现状,不因子系统 Tabs 自动复制或隔离

本期的“统一权限不变”是指现有权限体系不被拆分或替换;子系统路由范围只是让同一套权限在不同业务系统入口上呈现不同的可见范围。

11. 本插件与 NocoBase 原有能力的边界

Section titled “11. 本插件与 NocoBase 原有能力的边界”
  • 子系统的登记、名称和启用状态;
  • 主系统及子系统访问域名的登记、唯一性校验和域名解析;
  • 按子系统保存登录设置;
  • 按子系统保存系统设置;
  • 读取已有路由并维护子系统路由范围;
  • 在顶栏提供子系统切换;
  • 根据当前子系统过滤路由并执行服务端访问校验;
  • 提供必要的配置权限和操作提示。
  • 原有登录认证和用户身份;
  • 原有系统设置能力;
  • 桌面路由目录及其树形关系;
  • 角色、资源、操作、字段和数据 ACL;
  • 业务数据、文件、工作流及其他插件的现有行为。

本插件可以调用 NocoBase 当前公开且未废弃的能力,例如读取桌面路由和使用现有路由权限操作,但不得修改官方插件源码、替换核心实现或依赖历史废弃插件。

本设计以当前已有的登录设置插件能力作为承载基础:保留现有登录设置字段、页面入口和认证页面接入方式,在其上增加子系统维度和路由范围配置。系统设置 Tabs 通过本插件接入现有设置中心,不修改 @nocobase/plugin-system-settings 的源码。

因此,设计中新增的配置、接口、权限和 UI 均属于本插件;NocoBase 原有插件只是被调用的能力提供方,不是本次改造对象。

以下是业务概念,不限定具体表名或代码结构:

子系统
├─ 名称、访问域名、启用状态
├─ 登录设置(关联子系统记录)
├─ 系统设置(关联子系统记录)
└─ 路由范围(子系统记录 + desktopRouteId)

约束如下:

  • 主系统和每个启用的子系统都必须配置访问域名;
  • 一个域名只能映射一个系统,不能重复配置;
  • 配置数据可以在本插件内部使用记录主键关联子系统,但运行时不能通过外部传入的 ID 判定;
  • 主系统域名必须显式登记,未知域名不能被误当成主系统;
  • 路由范围保存路由标识,不复制路由标题、页面 Schema 或菜单结构;
  • 路由被删除或失效时,相关范围记录应被忽略并允许清理;
  • 子系统名称用于展示,内部记录主键用于配置关联,二者不能混用;
  • 停用子系统时保留其配置,重新启用后可恢复原设置。
  1. 本插件维护主系统和子系统清单、访问域名及启用状态。
  2. 管理员进入“登录设置”,选择一个子系统 Tab。
  3. 修改该子系统的登录页字段,并在同一 Tab 勾选路由范围。
  4. 提交后只保存当前子系统配置。
  5. 管理员进入“系统设置”,选择相同的子系统 Tab。
  6. 修改系统名称、Logo、语言等字段并提交。
  1. 用户访问一个已配置的系统域名。
  2. 服务端根据请求域名确定当前系统,登录页读取该系统对应的登录设置。
  3. 登录成功后,路由列表按“现有 ACL ∩ 子系统路由范围”展示。
  4. 用户在顶栏打开子系统切换器。
  5. 选择有权访问的子系统后,浏览器跳转到目标域名,由目标域名重新确定上下文。
  • 子系统没有配置任何可访问页面时,不允许将其作为普通用户可用子系统启用;
  • 主系统或子系统未配置域名、域名格式错误或域名冲突时,不允许启用;
  • 请求域名没有匹配系统时返回未配置结果,不能回退到主系统;
  • 子系统只勾选了父级分组而没有可进入的子页面时,提示管理员补充路由;
  • 当前页面在切换后无权访问时,自动转到目标子系统的可访问起始页面;
  • 子系统被停用后,从普通用户切换列表中移除,已有上下文在下一次请求时按平台无权限规则处理;
  • 路由已从系统路由目录删除时,不应继续显示为可访问入口;
  • 用户角色发生变化后,切换器和路由列表应按最新 ACL 重新计算;
  • 子系统配置保存失败时,不更新当前 Tab 的本地展示结果,并保留可重试状态。
  • 登录设置页面出现主系统和全部已登记子系统 Tabs;
  • 切换 Tab 能读取对应的登录设置;
  • 修改子系统登录设置不会改变主系统或其他子系统;
  • 访问不同已配置域名时,登录页能自动展示对应系统的登录设置;
  • 刷新页面后,已保存值仍与对应子系统一致。
  • 系统设置页面出现与登录设置相同的子系统 Tabs;
  • 每个子系统可以独立保存系统名称、Logo 和语言配置;
  • 修改子系统系统设置不会覆盖主系统设置;
  • 页面不因本期功能改变现有主界面结构和整体样式。
  • 管理员可以看到系统当前全部桌面路由及层级;
  • 每个子系统可以独立勾选和保存路由;
  • 父子勾选关系符合树形路由导航规则;
  • 未授权路由不出现在子系统菜单中,直接访问也不能绕过服务端校验;
  • 现有角色/ACL 权限继续生效,子系统路由范围不能扩大原有权限。
  • 普通用户只能看到启用且有权访问的子系统;
  • 切换时跳转到目标子系统域名,目标域名重新解析当前系统;
  • 目标域名已有有效登录状态时直接进入,否则显示该域名对应的登录页;
  • 切换后当前子系统名称、路由列表和配置上下文同步更新;
  • 切换不要求重新创建用户或改变现有数据权限。
  • 主系统和每个启用的子系统均有唯一访问域名;
  • 同一路径通过不同已配置域名访问时,能得到对应系统的设置和路由范围;
  • 未配置、冲突或停用系统的域名不能进入业务页面;
  • URL 参数、请求体或普通请求头中的系统内部 ID 不能改变域名判定结果;
  • 未知域名不会自动使用主系统配置。
  • 没有修改 NocoBase 核心源码和官方插件源码;
  • 没有引入已废弃的多应用、共享集合或历史菜单方案;
  • 没有新增独立数据库、独立 Application、SSO 或部署生命周期能力。