Vben 5 前端与 API 合同
# Vben 5 前端与 API 合同
版本 v1.5,2026-10-22。返回总览。下面的路径和 JSON 是本项目新合同提案,不是既有管理端或 Vben 示例后端的现成 API。
# 1. Vben 使用方式
采用 Vben 5 的 web-antd 应用,保留其表单、表格、弹窗、布局、主题、国际化和权限工具。工程基线见 10-sources.md。默认中文,业务页面放在 apps/web-antd/src/views,下述路径均相对该应用。
| 位置 | 本项目改动 |
|---|---|
| src/main.ts | 初始化 Vben 偏好并清理旧持久化身份键 |
| src/api/security-transport.ts | 同源策略发现、生产 JWE、响应完整性/请求号校验与失败封闭 |
| src/api/admin.ts | 独立 realm 客户端、Cookie 客户端、一次刷新、错误与版本处理 |
| src/auth/{realm,session,coordination,legacy}.ts | URL 身份区域、内存会话、Web Locks/BroadcastChannel、旧存储清理 |
| src/router/{access,manifest}.ts | 授权快照、版本一致性、组件/路径白名单与动态路由 |
| src/{app.vue,layouts/basic.vue,preferences.ts} | 官方 vue-vben-admin 登录/后台布局、原生多标签/主题偏好与 Fago 身份桥接 |
| src/router/{menu-groups,vben-menus}.ts | 授权叶节点分组、官方菜单树与分组重定向适配 |
| access store | 移除 token 和权限的持久化 |
| src/views/ | 登录、固定改密、工作台、个人中心与受控权限目录 |
M1 实际实现与验收见 实施记录;系统管理、基础设施和 SaaS 编辑页面按后续里程碑交付。优先在应用 adapters 中扩展,仅当共享 store 配置确实需要调整时小范围修改工作区包,记录上游差异。原始 Vben UserInfo 中有示例字段,应用应定义 AdminUserInfo 或映射成真正需要的字段,不在用户资料里重复放 token。
Vben 的后端模式通过接口返回路由,再映射到本地组件;本项目不允许服务端下发远程 JS。只接受本地组件 manifest、已知布局和白名单 meta。官方权限说明 (opens new window)
# 2. 导航与页面
租户端包含:工作台、系统管理(用户、部门、岗位、角色、菜单查看/分配)、个人中心、会话管理。R2 增加基础设施(字典、参数、文件、日志、任务、通知、代码生成)。
平台端使用 /platform 前端区域,提供租户、套餐/能力和平台审计。平台与租户状态、API 客户端、动态路由和 cookie 分开;登出时清理对应身份的路由和缓存。R1 默认不支持同一标签页同时扮演两种身份。
当前侧栏采用两级导航:租户按系统管理、身份管理、基础设施、消息通知、开发示例归组,平台按租户与套餐、身份与登录、审计与权限归组;工作台和个人中心保持一级入口。分组仅消费已校验的授权叶节点,不新增授权合同;空组隐藏,当前路由的父组在恢复和导航时自动展开。完整结构与扩展位置见 菜单与路由。
2026-10-28 按用户确认恢复官方 vue-vben-admin 完整布局:后台使用 @vben/layouts 的 BasicLayout/UserDropdown,登录使用 AuthPageLayout,复用原生侧栏、顶栏、面包屑、多标签、主题/布局偏好与搜索。保留既有业务组件、API 和身份流程;Pinia 只驻留内存,搜索历史不写 localStorage。授权或账号改变时清理布局缓存,普通 access 轮换不改变标签;分组导航仅重定向到本轮已授权叶节点。物理目录沿用 fago/fago-vben5,来源与固定版本仍为 官方上游快照。
菜单目录由平台种子发布;租户管理员分配已开通菜单与权限,不输入任意页面组件路径。若提供目录编辑器,componentKey 必须从前端发布 manifest 下拉选择。
列表统一采用“筛选 → 分页表格 → 详情/编辑抽屉”,危险操作二次确认,异步提交防重复。用户管理使用部门树与列表,角色管理使用权限树与数据范围,未实现选项不显示。权限不足显示 403;资源不存在/不可见显示 404;按钮权限使用实际 permission code,前后端一致。
# 3. 请求与响应
租户 API 前缀 /api/admin/v1,平台 API 前缀 /api/platform/v1。采用 REST 资源路径,JSON 字段 camelCase。旧 /admin-api/system/... 不作为兼容目标。
开发/测试默认明文;生产请求响应 JSON 内容强制使用 JWE,前端先验证/解密再处理业务码。HTTPS、Origin、Cookie 与权限校验仍保留,详见 配置与数据安全。下列 JSON 为开发环境及解密后的业务合同。
成功统一 HTTP 200/201,并返回:
{
"code": 0,
"data": { "list": [], "total": 0, "pageNo": 1, "pageSize": 20 },
"message": "ok",
"requestId": "req_example"
}
删除成功返回 200 + data:null,不返回空的 204,保证请求封装一致。列表空集合为 [],树叶 children 可省略;可空字段在 OpenAPI 明确,不随意混用空字符串与 null。未知字段在写 DTO 中拒绝,响应禁止包含 passwordHash、token 摘要和内部错误栈。
失败同时使用正确 HTTP 状态与稳定业务 code:
{
"code": 409001,
"data": null,
"message": "记录已变更,请重试",
"requestId": "req_example"
}
| HTTP | 业务 code 示例 | 前端行为 |
|---|---|---|
| 400 | 400001 参数格式,400002 字段校验 | 表单定位错误,必要时返回 fieldErrors |
| 401 | 401001 access 到期,401002 会话失效,401003 凭证无效 | 仅受保护 API 的 access 到期允许单次刷新;会话失效直接重新登录 |
| 403 | 403001 权限不足,403002 租户不可用,403003 必须改密,403004 Origin/CSRF 拒绝,403005 操作者再鉴别失败 | 不触发 token 刷新,显示对应页面/操作引导 |
| 404 | 404001 资源不存在或不可见 | 页面 404/列表移除记录 |
| 409 | 409001 版本冲突,409002 唯一键冲突,409003 业务状态冲突,409004 部门树结构/深度冲突,409005 租户用户配额已满 | 保留表单并引导刷新,不覆盖;配额已满时不要原样重试创建 |
| 429 | 429001 频率限制 | 使用 Retry-After,不自动无限重试 |
| 500/503 | 500001 内部错误,503001 依赖不可用 | 展示 requestId,服务端记录脱敏原因 |
Vben request client 的成功 code 配置为 0;错误提示读 message。原有 refresh 示例的响应解包和本项目不同,必须改成读取标准 envelope 内的 accessToken,不能直接复制示例的 resp.data 假设。
# 4. 通用参数
分页使用 pageNo ≥ 1,pageSize 默认 20、最大 100,返回 list/total;R1 采用 offset 分页。时间范围采用 from/to,左闭右开,并要求显式时区。sortBy 只能取端点白名单,sortOrder 为 asc/desc,默认按 id 倒序,不能把用户输入直接拼进 SQL。
ID 是字符串;时间为 RFC3339 UTC;状态统一为字符串 enum,例如 enabled/disabled,避免沿用不同来源的 0/1 含义。通用树字段为 id、parentId(根为 null)、children。更新请求必须包含整数 version,资源归属字段由服务端确定或专门授权,禁止 mass assignment。
X-Request-ID 可由可信代理传入,否则服务端生成;响应返回 requestId。日志对 query 脱敏,密码和 refresh token 不放 URL。R1 写请求不自动重试;发生网络不确定时通过查询确认结果,不能仅靠前端禁用按钮假定请求幂等。R2 对支付、通知和批任务定义独立持久化幂等键。
# 5. 登录与启动合同
{
"tenantCode": "example",
"clientId": "admin-web",
"username": "admin",
"password": "<user-input>"
}
POST /auth/login data 返回 accessToken、expiresIn(秒)、mustChangePassword;refresh token 只在 cookie。POST /auth/refresh 请求体 {},返回相同 token DTO。二者需检查用户、会话和租户状态。
GET /auth/me 的 data 样例:
{
"id": "1001",
"username": "admin",
"realName": "租户管理员",
"avatar": null,
"roles": ["tenant_admin"],
"tenant": { "id": "101", "code": "example", "name": "示例企业" },
"homePath": "/workspace",
"mustChangePassword": false
}
GET /auth/codes 返回当前有效权限字符串数组;GET /auth/menus 返回动态路由树。三个接口返回 X-Authz-Version,前端仅在版本一致时完成权限初始化;不同则清空并重取,最多一次,持续变动显示可重试提示,不无限循环。正常受保护响应带版本,新版本触发清空旧动态路由和权限后重新加载;未通过身份验证的拒绝响应不泄露授权版本。
[
{
"name": "System",
"path": "/system",
"component": "BasicLayout",
"meta": { "title": "系统管理", "order": 10 },
"children": [
{
"name": "SystemUsers",
"path": "/system/users",
"component": "/system/user/index",
"meta": { "title": "用户管理", "icon": "lucide:users" }
}
]
}
]
此例为本项目数据样例,真实 component key 必须与选定快照的 pageMap 验证。name 全局唯一,path 唯一,父目录按需补齐但不附带额外操作权限。无可访问业务页的用户仍可进入固定基础工作台和个人中心。
# 受限会话与启动分支
登录成功和页面恢复共用同一分支:有登录结果时直接读取 token DTO;没有内存 token 时先用独立客户端刷新一次。取得 DTO 后,在任何 me/codes/menus 请求之前检查 mustChangePassword:
| 结果 | 前端动作 | 服务端允许的受保护入口 |
|---|---|---|
| true | 清空旧权限/动态路由,进入固定 /auth/change-password 页;该页不依赖用户信息或菜单初始化 | 刷新、登出、本人改密;其余返回 403003 |
| false | 并行加载 me/codes → menus → 校验版本/组件 → 注册路由 → 已授权 homePath | 按正常权限校验 |
| 登录/刷新失败 | 清空 token、权限和半初始化路由,回登录页 | 不保留部分登录状态 |
改密页与登录页均为静态基础路由,不需要后台菜单授权;受限会话若进入其他页面,路由守卫返回改密页。重载改密页时刷新恢复受限会话并再次走 true 分支,不把 403003 当成 token 过期触发刷新循环。受限刷新不会延长其 30 分钟绝对有效期。
PUT /profile/password 接收 currentPassword/newPassword,目标用户只取当前 Actor。改密成功后服务端清除标记、递增密码版本、撤销全部会话并清除 cookie;前端清空内存状态、广播登出并回登录页,不继续用旧 token 加载菜单。普通个人中心改密也遵循该撤销规则。
# 6. R1 租户 API 清单
下表省略公共前缀。read/create/update/delete 为具体资源权限;只有本人接口不要求后台管理权限。
| 资源 | 方法与路径 | 规则 |
|---|---|---|
| 身份 | POST /auth/login、/auth/refresh、/auth/logout | 公开身份入口但有 Origin/限流/会话校验 |
| 本人权限 | GET /auth/me、/auth/codes、/auth/menus | 正常有效会话;强制改密受限会话拒绝,不用于改密页初始化 |
| 个人资料 | PATCH /profile、PUT /profile/password | 仅本人,字段白名单 |
| 本人会话 | GET /auth/sessions;DELETE /auth/sessions/{id} | 只允许撤销本人会话 |
| 用户 | GET/POST /system/users;GET/PUT/DELETE /system/users/{id} | system:user:read/create/update/delete |
| 用户状态 | PUT /system/users/{id}/status | system:user:update + 最后管理员保护 |
| 用户密码 | POST /system/users/{id}/reset-password | tenant_admin + system:user:reset-password + operatorPassword;触发全部会话撤销和首次改密 |
| 用户角色 | GET/PUT /system/users/{id}/roles | tenant_admin + system:role:grant;GET 返回 items/version,PUT 全量替换并带 version/operatorPassword |
| 部门 | GET /system/departments/tree;POST /system/departments;PUT/DELETE /system/departments/{id} | system:dept:read/create/update/delete,防环和非空删除校验 |
| 岗位 | GET/POST /system/posts;PUT/DELETE /system/posts/{id} | system:post:read/create/update/delete |
| 用户岗位 | GET/PUT /system/users/{id}/posts | system:user:update;GET 返回 items/version,PUT 同租户全量替换并带 version |
| 角色 | GET/POST /system/roles;GET/PUT/DELETE /system/roles/{id} | 元数据读取为 system:role:read;写入还须 tenant_admin,保留角色只读 |
| 授权 | GET/PUT /system/roles/{id}/grants | tenant_admin + system:role:grant;菜单、权限码和 dataScope,带 version/operatorPassword,同事务替换 |
| 可分配目录 | GET /system/menu-options、/system/permission-options | tenant_admin;仅已开通目录 |
| 安全策略 | GET/PUT /system/security-policy | 独立 security-policy:read/update;继承/完整覆盖,version 与再鉴别 |
| 账号解锁 | GET /system/security-users;POST /system/users/{id}/unlock | system:user:unlock 的目录与数据范围;version 与再鉴别,旧会话失效 |
| 租户审计 | GET /system/audit-logs、/system/login-logs | 独立读取权限,敏感字段脱敏 |
角色和用户下拉选项提供独立 /options 端点,按权限限制并返回 id/label,支持 keyword 检索,最多 100 项,不默认返回全部用户详细信息。关联编辑器将远程搜索与已选标签合并,不把首 100 项误当完整目录。菜单管理页面在 R1 是目录查看和授权,不承诺租户自定义动态页面。
# M2 管理合同细节
用户编辑遗漏 departmentId 表示保留归属,显式 null 表示清空;部门变更受同租户及数据范围检查。DELETE 请求带 query version 和空对象正文。状态、重置口令、用户关联与角色授权写成功的 data 为 null,编辑用户/角色则返回更新 DTO。角色菜单选择的 menuIds 使用已发布目录稳定键,菜单选项增加所需的 permissionCode,不能以数据库自增 ID 或任意组件字符串代替。
特权接口的 operatorPassword 只用于同一业务事务中的本人再鉴别,错误码 403005 对外固定为“操作者身份验证失败”。错误计数和锁定可独立提交,但不得提交目标业务修改。前端在取消、提交结束和身份切换时清空口令,不存储或自动重试。用户/角色/安全策略页面的 409001 提示关闭后刷新核对版本,不覆盖获胜请求。
# 7. R1 平台 API 清单
平台身份入口 /auth/login/refresh/logout/me/codes/menus 使用平台前缀和独立 DTO,登录不接收 tenantCode。
| 资源 | 方法与路径 | 规则 |
|---|---|---|
| 租户 | GET/POST /tenants;GET/PUT /tenants/{id} | platform:tenant:read/create/update |
| 初始化 | POST /tenants/{id}/initialize | PENDING 租户,锁定并幂等初始化 |
| 生命周期 | PUT /tenants/{id}/status、/package、/expiry | 版本检查、审计、更新 authz_version |
| 套餐 | GET/POST /packages;GET/PUT /packages/{id} | platform📦*,生效范围显式预览 |
| 能力目录 | GET /capabilities、/permissions、/menus、/tenant-permission-options | 只读发布目录;后者返回 admin 权限与 capabilityCode;/permissions 保持平台本人目录 |
| 平台审计 | GET /audit-logs | platform:audit:read |
当前 api/openapi.yaml 为 77 个操作,含请求/响应 schema、分页上限、错误、权限码及 627 个可校验示例,并生成前端类型。health、M1 身份/会话/目录、M2 系统管理和 M3 平台租户/套餐/生命周期/配额/权限上限/平台审计均已有实际实现。M3 在两库合计校验 306 条真实明文、RSA JWE 与 SM2-SM4 响应;合同和文档样例本身不替代实际验证,详见 M3 实施记录。
# 8. 扩展合同:多终端、SSO、租户权限、工作流
登录请求从 M0 增加 clientId;realm 由 API audience/客户端注册决定,不接受任意字符串借此切换到平台身份。会员 API 使用 /api/member/v1 和独立客户端,服务身份使用 scopes,不能沿用后台 tenant_admin。
SSO 新增 POST /auth/sso/start、GET /auth/sso/callback、POST /auth/sso/logout;外部 IdP 的 back-channel logout 端点独立验证签名及 issuer/audience。客户端只跳转后端返回的白名单授权地址,不直接处理 IdP client secret。流程见身份方案。
M3 已实现 GET/PUT /tenants/{id}/permissions,维护 inherit/custom 模式及允许权限码,限制在套餐开通能力内;变更检查 version 并递增 authz_version。租户管理员只能在该租户有效权限集合内继续分配角色权限。
通用工作流页面新增流程/表单设计器、流程中心、我的流程、待办/已办和审批详情;按钮权限和任务参与人资格双重校验。工作流命令可返回 202 + operationId,前端轮询操作状态或订阅经过认证的状态通道;不因为统一 HTTP 示例只写 200/201 而把异步命令伪装为完成。
数据库与消息切换属于服务端部署配置,不在普通业务页面展示驱动参数或凭证。租户界面只展示对用户有意义的能力、配额和可访问功能。
# 9. 文件和业务流程共用入口
文件/文档页面按 resourceId + resourceVersion 提交,通用发起 API 为 POST /workflow/instances,包含 definitionVersion、resourceType、resourceId、resourceVersion、purposeKey、formData、commandId。后端从受控 adapter 加载与授权资源,拒绝前端传入任意 storageKey/URL 作为资源真值。
独立行政表单先通过 POST /workflow/form-instances 创建草稿,再以 PUT /workflow/form-instances/{id} + version 修改;响应返回 resourceId/resourceVersion 和引用的 formVersion。在线表单不需要新建业务模块,发起统一使用 resourceType: form-instance,后端在发起事务内冻结提交版本并创建实例,不能由前端任意拼接他人的表单引用。节点数据编辑按服务端字段权限另存可追溯记录,不修改提交快照。
实例详情返回只读 engineVersion,客户端不能用请求字段切换执行语义。API/worker 缺少精确匹配运行时则不执行任务,返回可重试的依赖不可用响应;界面显示待恢复状态,不把任务渲染成已完成。定义发布和应用升级的兼容规则见 运行时版本与滚动升级。
定义发起准入停用或所需运行时进入 draining/retired 时,首次发起返回 HTTP 409 + code 409003,不自动重试,也不改用默认运行时;界面刷新可发起定义列表并提示选择已启用版本。定时/消息触发也走同一服务端准入检查。此前已受理的 commandId 查询或重投按原命令状态返回,不被当成一次新发起;身份、租户和载荷检查仍然生效。
工作流详情返回任务、流程轨迹、固定版本和 adapter 提供的受控查看入口。当前页面所在业务模块不决定能否查看附件;下载端点重新检查资源 ACL/临时任务 grant。用户上传新版后,界面同时标明“当前版本”和“正在审批版本”,批准不会自动应用到新版。
资源类型不同只改变表单、查看器和状态映射,审批命令统一为 /workflow/tasks/{id}/actions;文件审批不单开一套角色、任务和历史。异步动作通过 /workflow/commands/{commandId} 查询,不用成功 toast 隐藏待确认状态。
# M3 平台页面与合同细节
/platform/tenants、/platform/packages、/platform/audit-logs 只在 platform 组件/路径白名单登记。API 模块在发送请求前拒绝租户区域调用;服务端继续校验 audience、会话、权限及来源。租户详情增加 initialized、userCount、userQuota、authzVersion,套餐详情增加 tenantCount。能力目录 ID 是稳定代码,使用 CatalogOption 而非数字 ID 选择项。初始化、到期、套餐、自定义权限更新分别使用独立白名单 DTO;expiresAt 缺失与显式 null 不混淆。页面每次打开编辑器重读版本,失败不换版本覆盖、不自动重发,并丢弃旧身份或已卸载页面的迟到结果。