页面与工作区
三个登录身份入口保留。列表的新建、复制、删除、发布按各类内容权限显示;没有修改权限的三维工作区仍可查看,编辑、导入、自动保存和恢复写入受到控制。
管理员授权界面展示固定清单,不能在普通角色编辑中改动固定模板。勾选动作自动补齐所属页面;取消页面先确认依赖动作。
按资料、产品原型、设计、开发、测试梳理本轮调整。页面与操作使用真实权限;管理员采用固定授权;内部维护与普通业务隔离;工作身份切换保护未保存内容。
真实接口、浏览器操作与构建/测试记录分别列示;测试覆盖范围和 OFD 本地入口边界仍需结合下文阅读。
旧环境采用 SINGLE_ACTIVE。管理员曾动态获得全部叶子权限,但内容写又被教员身份门槛阻止;本轮把这种不一致改为明确的页面权限与操作权限。
| 旧角色 | 旧授权 | 数据范围 | 迁移依据 |
|---|---|---|---|
| 管理员 · admin | 动态全部叶子权限 | ALL | 旧权限集合全选,但内容写仍被教员身份门槛阻止。 |
| 教员 · teacher | 33 项显式权限 | SYSTEM | 保留原有制作、教学能力;未获授予的资源管理不自动新增。 |
| 学员 · student | 14 项显式权限 | SELF | 保留本人或本人队组的学习与执行能力。 |
动作合同记录 87 个新增动作和 14 个停用共用动作码。旧角色仅按原已持有权限和所属页面展开,自定义角色不自动补齐其他业务能力。
三个登录身份入口保留。列表的新建、复制、删除、发布按各类内容权限显示;没有修改权限的三维工作区仍可查看,编辑、导入、自动保存和恢复写入受到控制。
管理员授权界面展示固定清单,不能在普通角色编辑中改动固定模板。勾选动作自动补齐所属页面;取消页面先确认依赖动作。
只列出当前账号已分配且有效的工作身份。单角色生效模式下,切换前确认各工作区未保存内容;成功后重新加载身份、菜单和页签。
切换请求失败保留原页面。切换已提交但新身份加载失败时,不继续使用旧身份业务缓存。
| 内容类型 | 新建 / 复制 | 编辑 / 上传 / 封面 | 删除工程 | 发布 / 撤回发布 | 批准 / 驳回 |
|---|---|---|---|---|---|
| 模型 | content.model.create | content.model.update | content.model.delete | content.model.publish | 不新增审批功能 |
| 场景 | content.scene.create | content.scene.update | content.scene.delete | content.scene.publish | 不新增审批功能 |
| 训练编排 | content.training.create | content.training.update | content.training.delete | content.training.publish | 不新增审批功能 |
| 作业指导书 | content.ofd.create | content.ofd.update | content.ofd.delete | content.ofd.publish | content.ofd.review |
保存后发布需要修改与发布两项权限。只有发布权限时,提交已保存的服务端版本;OFD 还须复用与当前内容指纹一致的已保存交付物,缺失或过期时由编辑者处理。
| 迁移后角色 | 默认保留能力 | 明确边界 |
|---|---|---|
| 管理员 | 固定普通管理授权、系统配置与既有教学/资源管理 | 不自动拥有内容写、学员执行/提交或内部维护权限。 |
| 教员 | 原有四类内容制作、教学管理与运行能力 | 原无 manage 的资源/知识管理不因拆码获得 CRUD。 |
| 学员 | 本人/本人队组的领取、执行、提交、指导书学习与反馈 | SELF 范围、参与人、队伍、岗位和正式考试投影继续生效。 |
| 内部维护 | 受保护账号与有效内部角色共同确认后获得维护能力 | 普通人员/角色目录隐藏;内部审计与业务审计分别读取。 |
任务中的教员、学员、观察员成员身份与平台角色名称解耦;成员身份不授予平台动作权限。业务参与范围、固定版本、乐观锁、发布前置条件继续由服务端校验。
默认 OFD 原型仍使用本地存储。本轮接入真实会话和本地写权限保护,未将整个原型业务迁移至 API;不能将前端限制描述成完整服务端隔离、跨设备协作或新增审批功能。
通过 32 / 32 项通过 · JSON 证据
| 检查项目 | 结果 | 补充证据 |
|---|---|---|
| root 使用管理员登录入口进入内部维护角色 | 通过 | |
| 普通角色目录不显示内部维护角色 | 通过 | |
| 管理员角色采用固定的受限授权 | 通过 | |
| 管理员授权已持久化,不动态获得全权限 | 通过 | |
| admin 无法通过接口修改固定管理员授权 | 通过 | |
| root 无法通过接口修改固定管理员授权 | 通过 | |
| admin 账号已取消 is_protected | 通过 | |
| 用户目录不显示 root 维护账号 | 通过 | |
| 普通账号按 ID 查询 root 受保护 | 通过 | |
| 普通账号按 ID 停用 root 受保护 | 通过 | |
| 普通账号按 ID 重置密码 root 受保护 | 通过 | |
| 普通账号按 ID 删除 root 受保护 | 通过 | |
| 管理员无法访问维护审计 | 通过 | |
| 普通审计不混入 root 登录与操作记录 | 通过 | |
| root 活动保留在可追溯的内部审计 | 通过 | |
| 目录提供模型独立动作与查看依赖 | 通过 | |
| 已移除旧的共用内容写权限 | 通过 | |
| 创建可配置的reader测试角色 | 通过 | |
| 保存reader细分授权 | 通过 | |
| 创建可配置的editor测试角色 | 通过 | |
| 保存editor细分授权 | 通过 | |
| 建立隔离的多角色测试账号 | 通过 | |
| 多角色测试账号正常登录 | 通过 | |
| 接口切换到reader角色 | 通过 | |
| reader当前身份独立生效 | 通过 | |
| reader拥有模型查看权限 | 通过 | |
| 只有查看权限时,直接新建接口返回403 | 通过 | |
| 接口切换到editor角色 | 通过 | |
| editor当前身份独立生效 | 通过 | |
| editor拥有模型查看权限 | 通过 | |
| 自定义非教员角色获得新增权限即可创建模型 | 通过 | |
| 普通管理员模型查看与创建权限相互独立 | 通过 |
通过 39 / 39 项通过 · JSON 证据
| 检查项目 | 结果 | 补充证据 |
|---|---|---|
| 维护账号使用已分配的内部角色,普通管理员不带维护标记 | 通过 | |
| admin 的普通角色目录和分页都隐藏内部角色 | 通过 | |
| admin 不能按已知内部角色 ID 读取/详情 | 通过 | HTTP 404 |
| admin 不能按已知内部角色 ID 读取/users | 通过 | HTTP 404 |
| admin 不能按已知内部角色 ID 读取/permissions | 通过 | HTTP 404 |
| root 的普通角色目录和分页都隐藏内部角色 | 通过 | |
| root 不能按已知内部角色 ID 读取/详情 | 通过 | HTTP 404 |
| root 不能按已知内部角色 ID 读取/users | 通过 | HTTP 404 |
| root 不能按已知内部角色 ID 读取/permissions | 通过 | HTTP 404 |
| 角色 admin 的关联用户排除维护账号 | 通过 | |
| 角色 administrative 的关联用户排除维护账号 | 通过 | |
| 角色 teacher 的关联用户排除维护账号 | 通过 | |
| 角色 student 的关联用户排除维护账号 | 通过 | |
| 角色 group_leader 的关联用户排除维护账号 | 通过 | |
| 角色 rv_reader_1e43e04e 的关联用户排除维护账号 | 通过 | |
| 角色 rv_editor_1e43e04e 的关联用户排除维护账号 | 通过 | |
| 教学人员搜索不暴露维护账号 | 通过 | |
| 教学人员按 ID 解析拒绝维护账号 | 通过 | HTTP 400 |
| 公开登录候选列表不暴露维护账号 | 通过 | |
| admin 无法通过普通用户接口给测试账号授予内部角色 | 通过 | HTTP 400 |
| root 无法通过普通用户接口给测试账号授予内部角色 | 通过 | HTTP 400 |
| 拒绝内部角色分配后测试账号原角色和版本保持不变 | 通过 | |
| admin 无法通过普通编辑更改固定管理员数据范围 | 通过 | HTTP 403 |
| admin 无法停用固定管理员角色 | 通过 | HTTP 403 |
| root 无法通过普通编辑更改固定管理员数据范围 | 通过 | HTTP 403 |
| root 无法停用固定管理员角色 | 通过 | HTTP 403 |
| 固定管理员角色状态、范围、版本未被边界请求改变 | 通过 | |
| 普通管理员不能读取维护审计/列表 | 通过 | HTTP 403 |
| 普通管理员不能读取维护审计/stats | 通过 | HTTP 403 |
| 普通管理员不能读取维护审计/export | 通过 | HTTP 403 |
| 已知维护账号错误密码和未知账号均返回统一认证失败 | 通过 | |
| 维护登录失败在普通列表不可见且内部审计保留真实 USER 目标 | 通过 | |
| 未知身份失败仍可在普通安全审计中查到 | 通过 | |
| 普通审计导出不包含维护记录,维护导出保留该记录 | 通过 | |
| 普通审计统计只计算 BUSINESS,维护统计只计算 INTERNAL | 通过 | |
| 普通用户和角色统计排除内部账号及内部角色 | 通过 | |
| 首页人数不计维护账号,最近动态不显示维护失败日志 | 通过 | |
| 数据库只读核验:已知维护失败 INTERNAL,未知失败 BUSINESS | 通过 | |
| 维护再次登录后普通 admin 仍未被重新保护 | 通过 |
通过 9 / 9 项通过,0 项失败,0 项待完成/跳过/需复核 · 完整 JSON · 开始于 2026-09-06T04:01:43.840Z
| 浏览器用例 | 结果 | 耗时 | 失败证据 |
|---|---|---|---|
| 管理员授权固定,权限树细分到页面操作 | 通过 | 4.9 秒 | — |
| 权限依赖:勾选操作自动补齐查看,取消查看需要确认 | 通过 | 11.4 秒 | — |
| 头像弹窗切换自定义角色,清理旧缓存并改变操作能力 | 通过 | 6.2 秒 | — |
| 角色切换接口失败保留原身份与页面 | 通过 | 3.7 秒 | — |
| 切换已提交但身份加载失败,不展示旧角色业务页面 | 通过 | 5.1 秒 | — |
| 训练编排未保存时,取消切换保留当前内容与身份 | 通过 | 7.9 秒 | — |
| 未保存训练的保存请求失败时,阻止提交角色切换 | 通过 | 6.6 秒 | — |
| 权限加载乱序:关闭 A 后打开 B,A 的迟到响应不能覆盖 B | 通过 | 6.2 秒 | — |
| 保存授权期间禁止恢复,PUT 成功但列表刷新失败仍保留提交快照 | 通过 | 6.5 秒 | — |
角色切换/身份获取/保存失败等异常路径使用浏览器定向响应模拟。它们验证前端保留状态与阻断行为,不表示真实服务发生过这些故障。
完整验证记录。下方按实际执行的命令与结果展示,不把单测数量并入 E2E 或真实 API 数量。
{
"time": "2026-09-06T04:08:03.624366+00:00",
"overallStatus": "PASS",
"e2eComplete": true,
"build": {
"web": "PASS: pnpm build; Vite production build and postbuild 10 iframe payload checks",
"auth": "PASS: Maven test/package",
"tran": "PASS: Maven test/package"
},
"backendTests": {
"ut_auth": {
"tests": 112,
"failures": 0,
"errors": 0,
"skipped": 0
},
"ut_tran": {
"tests": 340,
"failures": 0,
"errors": 0,
"skipped": 0
}
},
"nodeTests": {
"contentCoverPermissions": {
"tests": 57,
"failed": 0,
"command": "node --test tools/content-permissions.test.mjs tools/training-cover.test.mjs tools/scene-cover-api.test.mjs"
},
"uiPermissionContracts": {
"tests": 12,
"failed": 0,
"command": "node --test scripts/permission-selection.contract.test.mjs scripts/teaching-permissions.test.mjs"
}
},
"realApi": {
"permissions": 32,
"maintenanceBoundaries": 39,
"failed": 0
},
"e2e": {
"complete": true,
"passed": 9,
"failed": 0,
"flaky": 0,
"skipped": 0,
"screenshots": 12,
"command": "pnpm exec playwright test --config role-permissions-live.config.ts"
},
"ddl": {
"status": "PASS",
"file": "20260906_036_role_permissions_maintenance.sql",
"sha256": "66d6c32edef315c8fcd9264352dda3444ddcdf1ca2d50d2af6f138fc7f4d4bd1",
"environment": "Development: unreal_tran / public, KingBase MySQL compatibility",
"execution": "200 statements committed; same SHA rerun committed successfully",
"catalogLeafCount": 138,
"adminFixedLeafCount": 106,
"fullDdlUpdated": true,
"executionRecord": "unreal_tran_api/database/迁移执行记录.md"
},
"runtime": {
"gateway": "http://127.0.0.1:6180",
"authPort": 6101,
"tranPort": 6102,
"deployedArtifacts": {
"auth": {
"sha256": "98aaeedfe29c8e75e99ef9421a3982ae52ab357eb8592c68a30dfc54e633ff85",
"runningCopyMatches": true
},
"tran": {
"sha256": "d66a38602e32c0db0c2680905f6a6461d9cd09ab12f4ba1481c87f99f583e828",
"runningCopyMatches": true
}
},
"rootBootstrap": "Initialized from the local account file; one-time bootstrap disabled on restart; root login verified afterward",
"adminProtected": false
},
"cleanup": {
"time": "2026-09-06T04:07:09.247Z",
"projects": [
"165"
],
"users": [
"100"
],
"roles": [
"20",
"21"
],
"status": "PASS",
"postDeleteReads": "All 4 isolated fixture records return HTTP 404"
},
"artifactChecks": {
"status": "PASS",
"file": "artifact-checks.json",
"diffWhitespace": {
"unreal_tran_api": "PASS",
"unreal_tran_web": "PASS",
"ute2e": "PASS"
}
},
"scopeNotes": [
"Root login and operation audits are retained as INTERNAL; ordinary lists, statistics and exports exclude them.",
"Admin has fixed persisted grants; both admin and root cannot edit that built-in role permission set through the application.",
"OFD legacy editor continues to store its existing local browser data; session permission checks were added, not a new server persistence workflow.",
"Permission and role-switch tests are the focus; this run does not claim complete regression of every teaching or 3D business scenario.",
"Temporary test fixtures were deleted; existing training used for dirty-state checks was not saved.",
"Development environment only; no production deployment, Git commit or push performed."
]
}清理证据。业务模型、场景与训练的保留/恢复情况以记录为准。
{
"time": "2026-09-06T04:07:09.247Z",
"projects": [
"165"
],
"users": [
"100"
],
"roles": [
"20",
"21"
],
"status": "PASS",
"postDeleteReads": "All 4 isolated fixture records return HTTP 404"
}截图只证明相应画面。标记为“过程截图”的文件未关联当前结果,不计入当前通过数量。

验收后于 12:10:50(北京时间)等待完整加载后补拍:管理员权限面板展示固定授权,模型操作节点与保存授权被禁用。补拍仅执行读取,原始 Playwright 附件保持原记录。

勾选模型新建时,同步勾选模型页面,避免产生不可达的操作权限。

移除模型查看时提示其依赖操作会一起移除,取消后保留原勾选。

截图捕获了只读状态,但列表仍有加载遮罩,不能单独证明完整列表已加载。模型查看返回 200、新建返回 403 的结论见真实 API 断言。

仅列出当前账号已分配的有效工作身份,并标识当前身份。

切换至获得模型新增权限的自定义角色后,新建入口可见。

注入角色接口 503 响应后显示短消息,原身份和当前页面保留。

角色切换已提交但身份接口失败时回到登录入口,不继续显示旧身份业务页面。

修改训练名称后切换角色触发离开确认;取消时不提交角色切换请求,修改仍保留。

模拟训练保存接口 503 后保留原身份与输入,角色切换请求未提交。

权限编辑切换上下文后的延迟响应不应覆盖当前角色。此截图的通过状态以关联测试记录为准。

权限写入成功与随后列表刷新失败分别处理,避免误报授权未保存。此截图的通过状态以关联测试记录为准。
data/ 仅包含脱敏角色/权限快照与动作合同。本生成器不读取或复制账号配置、私有测试 fixtures、数据库备份、服务日志或会话凭据。