UG Admin 架构
产品与后端边界
UG Admin 提供类似 RuoYi 的前端管理工作流:登录、导航、权限、用户、部门、角色和字典。React 与 Vue 是可独立选用的原生实现;UG UI 提供公共组件。模板不捆绑后端,不预建行业业务、多租户或插件平台。
Go、Java、Python、Rust、Node、PHP、Next.js 等后端通过公开 API 接入。区别在协议、字段和业务语义,不在语言:不按语言复制七套适配器,也不要求已有后端改成模板的默认 URL。
src/modules/<业务>/页面、查询与操作
↓
模块 api / 系统管理 helper(业务模型与动作)
↓
Auth / Data / Access 合同 ← src/backend 项目适配
↓
后端公开 API(身份、最终授权、数据范围、事务与存储)
DataProvider 是列表和常见 CRUD 的复用工具,不是所有业务必须实现的框架。模块可以通过 custom 承载审批、导出等动作,也可以使用 HTTP 客户端封装自己的类型化 API;不要为每个行业动作给核心增加方法。具体 DTO、状态码、路径和凭据流程留在项目适配或模块 API 中。后端最终授权不能由前端菜单和按钮显隐替代。
维护仓库与新项目
维护仓库同时保存双端,以相同行为合同维护:
apps/react/src/ React 原生实现
apps/vue/src/ Vue 原生实现
apps/docs/ 独立开发者文档站,读取 docs Markdown
packages/admin-core/ 框架无关合同及系统管理模型
packages/admin-react/ React 页面壳与组合模式
packages/admin-vue/ Vue 页面壳与组合模式
scripts/scaffold/ 单端创建器、业务生成器、固定配置模板
tests/e2e/ 双端浏览器场景
创建器按明确目录边界复制所选端、系统模块及共享源码;不解析 import 图、不正则重写应用源码、不维护第二份内置页面 loader 映射。Vite 和项目初始化采用 scripts/scaffold/templates/ 的固定模板。输出是独立项目:
src/
app/ 框架装配、路由、认证、查询缓存、主题
backend/ 项目协议与认证适配
modules/
system/ 用户、部门、角色、字典、首页、个人资料、设置
orders/ 新业务自己的 index.ts、api.ts、页面
layout/ 壳层组合
patterns/ 原生页面组合模式
pages/ 登录、加载、404 与错误页
project.ts 品牌和提供者注入
shared/ 对应维护仓库的 admin-shared
public/
scripts/ 业务生成器和架构检查
vendor/ 固定版本的 UI 分发包
package.json 所选端依赖与 ugAdmin 模板版本
消费项目没有 apps/ 或另一框架的依赖,不带仓库测试和学习页面。当前兼容 UI 使用已安装构建包的分发快照,保留许可与锁文件;不依赖相邻仓库源码链接。升级采用源码差异合并,保留项目配置、适配和业务模块,不重新生成覆盖项目。
资源生成器默认只输出列表,--crud 可生成原生列表、详情和新增/编辑源码;所有输出位于所选模块,不新增核心注册表或运行时配置引擎。创建器携带接口、部署说明和配置示例;前端接口位置见 接入合同,部署见 部署指南。
开发者文档是独立的第一方站点,用 Astro 生成静态页面、UG UI 与 React 仅用于交互孤岛,展示两端共用的使用说明;不需要把同一文档站重复实现为 Vue。正文以 docs/*.md 为事实源,构建时用 marked 转换并改写站内链接;它不依赖后台认证、业务模块或真实服务,不参与 React/Vue 后台生产包,也不会被创建器复制到新项目。端口从 3400 起排:文档站 3400,React 3401,Vue 3402;新增框架应用依次往后追加,文档站始终是第一个入口。静态输出在 apps/docs/dist。
运行时状态
框架配置(品牌、模块开关、页面表、语言、表格形态)是进程内单实例状态,用就地更新的方式保存:
- 不再导出可重新赋值的绑定,引用方拿到的对象始终同一实例;
resetAdminRuntime()复位到默认值,测试与嵌入式场景用它隔离;- 同一时刻只能有一份配置,因此不支持同页面并发两个后台实例;需要并行实例时改成显式传入的运行时上下文。
开发期应用代码一律引用子路径(解析到源码);包根入口只给按版本安装的外部消费方。
业务模块内部职责
src/modules/<模块>/ 不再平铺,按职责分目录(system/management、system/dictionaries 已按此整理):
| 目录 | 放什么 |
|---|---|
pages/ |
路由页组件,一个文件一个页面 |
editors/ |
新增/编辑表单容器 |
fields/ |
字段级组件与校验展示 |
actions/ |
工具栏、行操作、状态开关、列设置 |
dialogs/ |
详情抽屉、确认与批量弹窗 |
filters/ |
查询摘要、搜索表单、范围筛选 |
hooks/ |
数据与视图状态 Hook |
lib/ |
纯函数、契约映射、校验规则 |
模块对外只经 index.ts(装配)与 api.ts(稳定能力)暴露,跨模块不直接引用内部文件。
app 目录职责
apps/<框架>/src/app 按职责分目录,不再平铺:
| 目录 | 放什么 |
|---|---|
providers/ |
access、auth、data、theme 四类 Provider 装配 |
routing/ |
router、route-components、navigation、RouteBreadcrumbs、modules(模块注册与发现)、module-types |
guards/ |
RequireAuth、PageActionGate、PermissionGate |
layout/ |
页签与主题配置等壳层组件 |
shared/ |
跨模块共用的对话框与卡片列表 |
i18n/ |
语言与文案 hook |
routing/modules.ts 用 import.meta.glob("../../modules/*/index.ts") 发现模块;移动该文件时 glob 相对路径必须同步,否则模块不会加载。
模块注册
页面表只有一个来源:各端 src/modules/*/index.ts 的 AdminModule 由 app/routing/modules.ts 统一发现并组装。
configureAdminModules / configureAdminPages / configureAdminPermissions只允许出现在apps/*/src/app/routing/modules.ts,由check:architecture强制;测试可例外。- 新增页面 = 新增或修改模块入口,不改应用装配;删除页面 = 同时删路由、权限键、测试与文档(
remove:module有体检)。
模块入口与依赖方向
系统模块、示例模块、新业务使用同一 AdminModule 合同,没有内置页面枚举和单独的业务路由旁路:
id是唯一模块名,enabled是启动开关。pages登记一个或多个页面的元数据、原生懒加载函数、可选图标。permissions提供可分配权限目录;登记不授予当前账户权限。decorateData用于模块自己的写后行为;系统管理的身份复核由 system 提供。createMockData仅用于开发装配,生产入口不登记模拟数据。
src/app/routing/modules.ts 自动收集 modules/*/index.ts。路由、导航、搜索、面包屑和页签使用组合后的同一目录;ID、路径、别名冲突和父级循环在启动时报错。关闭模块同时退出菜单与路由,旧页签不能恢复。没有插件生命周期、事件总线或服务容器。
依赖以职责为准,不能靠移动目录名称绕过边界:
| 层 | 可以依赖 | 不可以依赖 |
|---|---|---|
共享核心 auth/config/data/routing |
核心合同与平台无关公共依赖 | 任意系统或行业业务、演示数据、应用与框架运行时 |
应用基础层 app/layout/patterns/pages |
共享核心、UG UI、框架适配;开发装配可用通用内存 Provider | 具体模块页面、系统或行业模型、业务写后规则 |
modules/<名称> |
基础层服务、共享合同、本模块实现、其他模块显式公开 API | 其他模块内部页面、组件和查询实现 |
backend 与模块 API |
HTTP 客户端、Provider 合同、对应业务模型 | 将项目协议和服务端职责反写入框架核心或 UG UI |
模块发现是装配入口:app/routing/modules.ts 通过 glob 收集模块入口,不手写具体业务 import。模块之间确有复用需求时,仅通过目标模块的 api.ts 显式导出稳定能力,内部可以重组;不要导入目标 index.ts 装配入口,也不为尚无消费者的模块预建出口。API 依赖不代表自动开启目标模块,调用方仍需处理权限与可用性。
check:architecture 检查静态 import/export、类型引用和字面量动态 import 的上述核心/基础层/模块边界,并检查跨端依赖、失效引用和循环依赖;模板与生成的单端项目使用同一检查器。测试可以直接覆盖内部实现;glob、动态拼接路径和 CSS 是否滥用公开 API 仍需按实际调用人工核对,不能用检查通过代替设计审查。
系统页面元数据在 management/pages.ts,双端 loader 和实现留在各自 modules/system/;系统管理的模型、权限、查询 helper、身份复核在 management/,字典合同在 dictionaries/。核心首页仅展示有权查看的少量统计,不增加复杂分析。个人中心从头像菜单进入。可选学习页面属于 modules/examples/,示例概览、账户、设置分别在 /examples/overview、/examples/profile、/examples/settings,不替换系统页面。
后端适配与状态
project.ts 在挂载前设置品牌并注入 backend/index.ts 的 auth/data/access。未指定部分使用默认实现;生产默认 HTTP,本地默认演示,生产禁止 demo/mock 模式。默认 REST 请求可用 mapRequest 转换方法、URL、查询、载荷,responseMappers 转换返回模型,mapError 转换包括 HTTP 200 在内的业务失败。HTTP Auth 支持端点、登录字段、会话和错误映射,复杂流程可替换 Provider。完整合同见 后端接入。
请求保留取消信号。401 清理会话与查询缓存,403 保持真实拒绝,422 保留字段错误,写操作不自动重试。切换账户清缓存;同一身份权限更新重置活跃查询。系统管理写入影响当前身份时由 management/session.ts 复核会话,失败清身份但不把已成功写入伪装成失败。外部撤权、定制业务动作和实时推送由实际项目接入。
React/Vue 各自使用 TanStack Query 原生适配,组件和 Hook/composable 不跨框架共享。品牌和本地存储使用 storageNamespace 隔离。真实后端仍需实现资源关系约束与授权;演示数据不等同于服务端实现。
UI 与验证
UGAdminShell 负责全局壳层,secondaryBar 承载页签;页面通过 Patterns 和 UG UI 组合。Admin CSS 只负责业务布局,不穿透公共组件内部结构。稳定身份、权限与字典规则分别见 身份体系、管理合同、字典合同。
按实际影响运行类型检查、相关现有测试、架构检查和构建。新增业务不要求新增测试文件或修改双端核心;公共合同变化才同步双端。构建、模拟 API 验证、真实服务联调和用户界面验收分别报告,不能互相替代。
新增业务先留在所选端模块;只有真实复用且语义稳定时才提取。纯模型和算法进入对应共享职责目录,框架组件与查询状态留在原生端,公共 UI 能力回 ug-ui。目录数量、代码行数和双端外形相似本身都不是增加抽象层的理由。
架构不变量
只保留“改了就一定出事”的硬约束,其余属于维护规范,见下一节。
| 不变量 | 含义 | 现状 |
|---|---|---|
| 依赖单向 | ug-ui ← admin-core ← admin-react / admin-vue ← 应用与消费方;核心不依赖框架,两个渲染层互不引用 |
已由 check:architecture 守住 |
| 单一事实源 | 业务页面实现只在一处;消费方通过依赖引用,不复制源码 | 已由 check:copies(消费方)与模块契约守住 |
| 双端原生 | React 与 Vue 各自原生实现,同名页面 + 同一份核心逻辑 | 已由 check:parity 守住文件层 |
维护规范(开发期与发布期分开)
开发期与发布期用不同的解析方式,二者不混用:
| 场景 | 依赖写法 | 产物解析 |
|---|---|---|
| 本地开发(模板仓库、消费方联调) | file: 直连源码,改完刷新即生效 |
Vite/框架直接编译 TS 与 Vue 文件 |
| 发布(对外版本) | 版本号依赖 | 发布流程先构建 dist,再把入口指向产物 |
因此“子路径当前指向源码”是开发期的正确状态,不是缺陷。发布时必须执行构建与入口切换,这条写进 docs/RELEASE.md。
架构债务处理状态
原表中的八项已全部收口,门禁固化在 bun run check 里,防止回流:
| 编号 | 问题 | 处理 |
|---|---|---|
| A1 | 页面描述声明了动作却没人消费 | 动作改为 resource/action 合同并由双端工具栏消费,描述缺动作即不渲染;角色/部门/字典描述先移除未接线的动作 |
| A2 | 包被按文件子路径随意引用 | exports 改为按目录白名单,check:exports 拦截未声明子路径 |
| A3 | 双端能力写死在脚本里 | configs/frameworks.json 登记各端,检查、脚手架与构建统一读取 |
| B1 | 核心用模块级可变单例 | 改为就地更新的单实例状态,新增 resetAdminRuntime();单实例限制写进文档 |
| C1 | 模块注册双通道 | 页面表只由模块入口发现产生,configure* 仅允许出现在 app/routing/modules.ts(check:architecture 强制) |
| D1 | 样式无作用域、直连 UG token | 类名限定 admin- 前缀,UG token 只在 app.css 映射为 --admin-* 语义变量(check:styles 强制) |
| E1 | 权限码是裸字符串 | check:permissions 校验页面引用的权限码都在目录中,拼错即失败 |
| F1 | 文档站与效果预览耦合 | /demo/ 为独立页面并自带错误边界,UG_DOCS_PREVIEW=false 可整体关闭预览 |
新增触发条件:任一包新增导出目录、新增渲染层、改动权限目录或样式变量层时,先看本节对应门禁,不要绕过检查改实现。