# 租户与用户权限体系梳理及纠偏流程 > 版本:v1.0 > 梳理日期:2026-08-20 > 适用范围:YG_FT 平台后端、前端、Compute Agent、MinIO、PostgreSQL 及离线部署目录 > 目的:明确租户、用户、角色、权限、资源和审批之间的层级关系,识别当前实现中的概念冲突,并为后续优化提供唯一设计口径。 ## 一、结论摘要 当前系统已经具备用户登录、租户字段、租户成员、资源 ACL、审批、GPU 分配和资源级动作校验,但概念来源还没有完全收敛,主要表现为: 1. **用户和租户没有混为一个数据库对象,但业务流程中边界不清**:用户表保存 `tenant_id`,同时又通过 `tenant_members` 表表达租户关系,导致“主租户”和“成员关系”的判定规则并存。 2. **平台角色、租户角色、页面权限码、资源动作权限同时存在,但职责没有彻底分离**:`users.role`、`users.permissions`、`tenant_members.role`、`roles.permissions` 可能产生不一致。 3. **资源归属字段不统一**:部分资源使用独立的 `created_by`,部分任务把创建者放在 `payload.created_by`,部分资源的 `tenant_id` 允许为空,历史数据因此出现“ACL 已授权但列表不可见”的问题。 4. **租户创建、用户创建、成员加入和资源授权是四条不同流程,目前在页面上有交叉**:创建用户时直接写入租户成员,租户的 `owner_user_id` 却没有同步建立 owner 成员关系。 5. **项目表和项目字段仍在数据库及部分接口中保留**,但当前产品已经取消项目作为用户可见的业务隔离层,继续把 `project_id` 当权限条件会造成新的歧义。 6. **正确的目标模型应当是**:平台管理员管理平台身份和租户;用户通过租户成员关系进入一个或多个租户;资源属于一个租户并有一个所有者;资源使用通过 ACL 动作权限;高风险动作通过审批;GPU 和配额是资源使用前的运行时约束。 本文件的纠偏原则是:**先统一口径,再做兼容迁移;先保证后端判定一致,再调整前端菜单和数据库约束;历史字段暂不删除,但不再新增新的业务依赖。** ## 二、目标层级模型 ```text 平台 Platform │ ├── 平台身份与平台角色 │ ├── platform_admin:平台管理员,可管理租户、用户、策略和全部资源 │ └── platform_user:普通平台用户,不因登录自动获得资源权限 │ ├── 租户 Tenant │ ├── 租户基本信息、状态、配额、留存策略 │ ├── 租户成员 TenantMember │ │ ├── owner:租户所有者 │ │ ├── admin:租户管理员 │ │ ├── member:普通成员 │ │ └── viewer:只读成员 │ └── 租户内资源 │ ├── 数据集 │ ├── 基座模型 │ ├── 训练任务与训练模型 │ ├── 评测任务 │ ├── 推理任务 │ └── 数据处理/转换任务 │ ├── 资源授权 Resource ACL │ ├── principal:user 或 tenant role │ ├── read:查看 │ ├── write:编辑 │ ├── execute:训练、推理、评测等使用 │ ├── download:下载或导出文件 │ ├── delete:删除 │ └── admin:资源授权和完整管理 │ ├── 运行时资源 │ ├── Compute Node │ ├── GPU │ ├── GPU assignment:用户获批可使用哪些 GPU │ ├── GPU reservation:任务当前占用哪些 GPU │ └── Tenant quota reservation:租户当前预留了多少运行资源 │ └── 审批与审计 ├── resource access:申请资源 ACL ├── gpu.assign:申请算力卡 ├── tenant.quota.update:申请租户配额变更 ├── 高风险删除/导出/合并操作 └── 全部结果写入审计日志 ``` ### 2.1 四个必须分开的概念 | 概念 | 正确含义 | 不应承担的职责 | |---|---|---| | 用户 User | 可登录平台的人或服务账号 | 不代表租户,也不自动代表资源权限 | | 租户 Tenant | 资源、成员、配额和审计的隔离边界 | 不直接作为登录账号 | | 角色 Role | 某个范围内的一组职责 | 不应直接等同于某个用户的全部资源权限 | | 权限 Permission | 对页面或资源的具体操作能力 | 不应只靠前端按钮控制 | ## 三、当前实现盘点 ### 3.1 用户身份层 当前主要实现位置: - `users` 表:保存账号、密码哈希、平台角色、状态、页面权限 JSON、主租户字段。 - `sessions` 表:保存登录会话、过期时间和注销时间。 - `backend/app/core/auth.py`:解析 Token、校验用户状态、校验页面权限和租户上下文。 - `/users` 接口:当前只有管理员可以创建、修改、禁用和删除用户。 当前用户对象包含: ```text id / username / display_name / status role / protected / permissions tenant_id ``` 存在的混淆: - `role=operator/viewer/user` 是平台用户字段,但 `tenant_members.role` 还有 `owner/admin/member/viewer`,两套角色名称不在同一层级。 - `role=user` 在创建接口中被接受,但设计文档主要定义的是 `admin/operator/viewer/guest`,前端又同时显示“普通用户”。 - 创建用户时直接生成 `tenant_members` 活跃成员记录;这相当于“创建用户”和“加入租户”一次完成,没有邀请、待确认或成员审批边界。 - `users.permissions` 是用户级 JSON,`roles` 表只提供查询接口,当前没有形成“角色变更自动计算权限”的唯一来源。 - 非管理员创建/更新用户时会被剥离 `compute` 和 `user-settings`,但初始化 SQL 中的 `u_operator` 种子数据仍包含 `compute`,代码默认值和 SQL 种子不一致。 ### 3.2 租户层 当前主要实现位置: - `tenants` 表:保存租户名称、状态、owner_user_id、配额和留存策略。 - `tenant_members` 表:保存租户与用户的多对多关系、租户角色、状态、邀请人和有效期。 - `tenant_membership()`、`user_tenant_ids()`:判定当前用户的租户范围。 - `/tenants/{tenant_id}/members`:管理员或租户 owner/admin 管理成员。 当前逻辑: ```text 当前用户租户范围 = users.tenant_id + tenant_members 中 status=active 的 tenant_id 当前请求租户 = X-Tenant-ID(仅在用户属于该租户时生效) 资源租户 = 资源 tenant_id,部分任务还会从 payload 中读取 tenant_id ``` 主要问题: 1. `users.tenant_id` 和 `tenant_members` 都能表达租户关系,但没有明确谁是主数据、谁是兼容字段。 2. `tenants.owner_user_id` 创建租户时写入,但创建租户流程没有自动写入对应的 `tenant_members(role='owner')`。 3. 平台管理员创建普通用户时直接加入租户,租户管理员也可以直接添加成员,邀请/加入申请流程虽已具备接口,但不是主流程。 4. `tenant_id` 在资源表中有的为 `NOT NULL`,有的允许 `NULL`;历史资源的 `NULL` 租户需要特殊兼容判断。 5. 租户删除是状态删除,但成员、资源、ACL、审批、MinIO 对象和本地缓存的后续处理没有统一的生命周期编排入口。 ### 3.3 页面权限层 页面权限由两部分组成: ```text 前端:路由 meta.permission + 侧边栏过滤 + 按钮显示 后端:MODULE_PERMISSIONS + get_current_user / require_admin ``` 页面权限码包括: ```text dashboard, fine-tune, model-eval, model-inference, model-manage, dataset, data-process, data-convert, compute, hardware, logs, user-settings ``` 问题: - 页面权限码是“能否进入模块”,不是“能否使用某一条数据或 GPU”,但历史实现中一度把 `compute` 页面权限阻断了普通用户读取自己获批 GPU 的接口,造成“有 GPU 分配但训练页面看不到”的问题。 - 普通用户的 `compute` 管理菜单应保持隐藏,但训练、推理、评测所需的个人节点/GPU查询应当是独立的自助只读接口。 - 前端权限控制和后端权限控制分别维护,按钮隐藏不能作为安全边界。 ### 3.4 资源权限层 当前资源权限主要由以下条件组合: ```text 平台管理员旁路 或 资源属于当前租户 且(资源所有者 == 当前用户 或 ACL 明确授权 或当前用户是租户 owner/admin) 且满足具体动作 read/write/execute/download/delete/admin ``` 已接入资源类型包括: ```text dataset / model / trained_model / fine-tune / eval / inference data_process / data_convert / compare / project(历史兼容) ``` 当前差异: | 资源 | 当前所有者来源 | 当前租户来源 | 主要风险 | |---|---|---|---| | datasets | `created_by` | `tenant_id`,部分历史为空 | 列表、详情、训练权限可能不一致 | | models | `created_by` | `tenant_id`,基座模型存在平台共享规则 | 基座模型共享与训练产物共享容易混淆 | | trained_models | `created_by` | `tenant_id` | 血缘来源、默认授权需统一 | | fine_tune_tasks | 多数在 `payload.created_by` | 多数在 payload 或派生字段 | 结构化查询和授权容易漏字段 | | eval_tasks | `created_by` | `tenant_id` | 与数据集/模型执行权限必须同时校验 | | compare_tasks | 表字段和 payload 并存 | 表字段和 payload 并存 | 推理任务与模型使用权限容易分裂 | | data_process_tasks | `created_by` | `tenant_id` | 处理结果派生数据集授权不明确 | | data_convert_tasks | `created_by` | `tenant_id` | 历史表结构和初始化脚本存在演进痕迹 | | projects | `create_by` 等历史字段 | `tenant_id` | 当前已取消项目业务层,但表和接口仍在 | ### 3.5 GPU 与运行时权限层 GPU 相关关系实际上有三层: ```text gpus └── gpu_assignments:管理员批准“用户可以使用哪张卡” └── gpu_allocations / gpu_reservations:任务运行时“当前占用了哪张卡” ``` 正确判定顺序应为: 1. 用户拥有租户成员资格且账号 active。 2. 用户已经获得该 GPU 的分配或管理员旁路。 3. 节点 online、enabled,GPU 存在且没有被其他任务占用。 4. 租户配额可以完成原子预留。 5. 训练、推理或评测任务提交到指定节点。 目前已经有分配申请、审批后写入 `gpu_assignments`、运行时预留和释放逻辑,但仍需把“审批结果、GPU 分配、配额预留、节点故障回收”统一成一个状态机。 ## 四、当前数据库关系与问题 ### 4.1 当前表的职责分组 ```text 身份 ├── users ├── sessions └── roles 租户 ├── tenants ├── tenant_members └── tenant_quota_reservations 资源权限 ├── acls ├── resource_access_requests ├── approval_templates ├── approval_instances └── approval_steps 业务资源 ├── datasets / dataset_files / dataset_file_versions / dataset_records ├── models / trained_models / model_lineage / model_artifacts ├── fine_tune_tasks / eval_tasks / compare_tasks ├── data_process_tasks / data_convert_tasks └── projects / project_members(历史兼容) 运行资源 ├── compute_nodes ├── gpus ├── gpu_assignments ├── gpu_allocations ├── gpu_reservations └── resource_replicas / storage_objects / storage_cache_jobs 审计 └── audit_logs ``` ### 4.2 数据库设计中的主要问题 #### 问题 A:租户归属字段可空且没有统一外键 `models`、`trained_models`、`datasets`、`eval_tasks`、`compare_tasks`、数据处理相关表的 `tenant_id` 演进过程不同,部分字段仍允许空值。多数资源表也没有对 `tenants(id)` 的外键约束。 后果: - 资源可能被创建但没有租户归属。 - 列表接口和详情接口的可见性不同。 - ACL 授权是否跨租户无法仅靠数据库判断。 - 历史数据需要特殊分支,增加权限代码复杂度。 #### 问题 B:所有者字段不统一 当前同时存在 `created_by`、`owner_id`、`create_by` 和 `payload.created_by`。这会导致: - 列表判断资源所有者时需要按资源类型写特殊逻辑。 - 用户删除时无法保证所有资源都能被一致地软删除。 - 审计目标和资源所有者可能不是同一个字段。 #### 问题 C:角色权限没有单一来源 当前存在以下可能来源: ```text users.role users.permissions(JSON) tenant_members.role roles.permissions(JSON) 前端 PermissionCode 后端 ALL_PERMISSIONS / MODULE_PERMISSIONS ``` 如果更新了角色但没有同步用户权限 JSON,页面和接口可能出现不同结果;如果只修改前端权限码,后端仍可能拒绝;如果只修改 `tenant_members.role`,并不会自动改变平台页面权限。 #### 问题 D:资源 ACL 不是所有资源的唯一授权来源 基座模型在当前设计中对租户用户默认共享 read/execute;资源所有者又有隐式权限;租户管理员又有管理旁路;ACL 只是其中一层。这个设计可以成立,但必须在文档和代码中严格区分: ```text 平台共享规则 ≠ ACL 授权 资源所有权 ≠ 租户管理员权限 页面权限 ≠ 资源动作权限 GPU 分配 ≠ 资源 ACL ``` #### 问题 E:项目字段与当前产品边界不一致 `projects`、`project_members`、`project_id` 仍在数据库中存在,但当前产品已经决定不把项目作为用户可见的隔离层。它们应被标记为 legacy,不应再参与新资源的权限判定,也不应在新建页面继续要求填写。 ## 五、目标唯一口径 ### 5.1 用户与租户的最终关系 ```text 一个用户可以属于多个租户 一个租户可以拥有多个用户 用户在一次请求中只能选择一个 active_tenant 资源必须归属于 active_tenant ``` 推荐保留: - `users.tenant_id`:暂时保留为兼容字段,表示用户的 primary tenant,不再作为多租户关系的唯一来源。 - `tenant_members`:升级为租户成员关系的唯一权威来源。 - `X-Tenant-ID`:明确表达当前请求租户,必须来自 active membership。 最终判定: ```text 有效租户 = tenant_members(status=active, 未过期) primary tenant = users.tenant_id(仅兼容和默认登录上下文) 当前租户 = 请求 X-Tenant-ID,否则 primary tenant ``` ### 5.2 平台角色与租户角色 推荐收敛为两层: | 层级 | 字段/表 | 允许的角色 | 作用 | |---|---|---|---| | 平台层 | `users.platform_role`,兼容当前 `users.role` | `platform_admin`、`platform_user` | 管理租户、平台配置、平台用户和全局策略 | | 租户层 | `tenant_members.role` | `owner`、`admin`、`member`、`viewer` | 管理本租户成员、资源和租户级审批 | 不建议继续把 `operator`、`user`、`member` 混在一个角色字段中。过渡期可以保留旧值,但新增代码只允许通过映射函数转换: ```text admin/protected -> platform_admin operator/user -> platform_user + tenant_members.member viewer -> platform_user + tenant_members.viewer ``` ### 5.3 页面权限与资源动作 页面权限只解决“能否进入模块”: ```text module permission: dataset / fine-tune / model-inference / ... ``` 资源动作解决“能否操作某个资源”: ```text read / write / execute / download / delete / admin ``` 统一授权函数输入应固定为: ```text authorize( actor_id, active_tenant_id, resource_type, resource_id, action, ) ``` 推荐判定顺序: 1. 会话有效且用户 active。 2. 当前租户 active,用户是该租户成员。 3. 平台管理员旁路,或资源属于当前租户。 4. 资源所有者拥有默认动作集合。 5. 租户 owner/admin 按租户范围拥有管理动作。 6. ACL 明确授予当前用户或租户角色对应动作。 7. 对 download、delete、admin 等高风险动作执行额外审批检查。 ### 5.4 资源标准字段 所有新资源应统一具备: ```text id tenant_id NOT NULL created_by NOT NULL created_at updated_at deleted_at deleted_by ``` 历史 `owner_id`、`create_by` 和 `payload.created_by` 只在迁移阶段读取,最终写入标准字段。任务配置 JSON 可以保留业务参数,但不再存放权限事实。 ## 六、标准业务流程 ### 6.1 创建租户流程 ```text 平台管理员 │ ├─ 创建租户基本信息 ├─ 设置租户配额和状态=active ├─ 指定或创建租户 owner ├─ 写入 tenant_members(tenant_id, owner_user_id, owner) ├─ 初始化审批策略 └─ 记录 tenant.create / tenant.member.add 审计 ``` 纠偏要求:`tenants.owner_user_id` 和 `tenant_members(role='owner')` 必须在同一事务中维护;不能只写一个。 ### 6.2 创建用户与加入租户流程 #### 平台管理员创建用户 ```text 平台管理员 │ ├─ 创建 users 记录 ├─ 选择初始平台角色:platform_user 或 platform_admin ├─ 选择 primary tenant ├─ 写入 tenant_members │ ├─ owner 仅平台管理员明确指定时允许 │ └─ 普通用户默认 member 或 viewer ├─ 生成页面权限快照(过渡期) └─ 记录 user.create / tenant.member.add 审计 ``` #### 已存在用户加入新租户 ```text 租户 owner/admin 或平台管理员 │ ├─ 发出邀请或创建加入申请 ├─ tenant_members.status = pending ├─ 被邀请用户接受 ├─ tenant_members.status = active └─ 用户选择 X-Tenant-ID 后访问该租户资源 ``` 纠偏要求:不能把“创建登录用户”和“拥有某租户全部资源”理解为同一件事;新用户默认没有任何资源 ACL,也没有 GPU 分配。 ### 6.3 创建业务资源流程 ```text 登录用户 │ ├─ 会话校验 ├─ 确定 active_tenant ├─ 校验租户 active 和用户成员状态 ├─ 写入资源 tenant_id + created_by ├─ 写入资源业务表 ├─ 写入 MinIO 对象元数据(如有文件) ├─ 生成资源血缘/版本快照 └─ 记录资源创建审计 ``` 数据处理结果生成数据集时,必须继承任务的租户和创建者;不能因为是系统生成就把 `tenant_id` 和 `created_by` 留空。 ### 6.4 资源查看和授权流程 ```text 用户请求资源列表/详情 │ ├─ 页面权限检查 ├─ active_tenant 检查 ├─ 资源 tenant_id 检查 ├─ 所有者 / 租户角色 / ACL 判定 ├─ 返回资源元数据 └─ 文件、版本、MinIO 对象再次按同一资源动作校验 ``` 资源授权: ```text 资源所有者或租户管理员/平台管理员 │ ├─ 选择用户或租户角色 ├─ 选择 read/write/execute/download 等动作 ├─ 设置有效期 ├─ 写入 ACL 或创建访问审批 └─ 记录授权来源、授权人和审计 ``` ### 6.5 训练、推理、评测使用资源流程 ```text 创建任务 │ ├─ 页面权限:fine-tune / model-inference / model-eval ├─ active_tenant 和任务资源归属校验 ├─ 基座模型:平台共享规则或 model.execute ├─ 训练模型:trained_model.execute ├─ 数据集:dataset.execute ├─ GPU assignment:用户是否获批使用所选卡 ├─ 节点:GPU 所属节点是否 online/enabled ├─ 原子预留 GPU 和租户配额 ├─ MinIO/节点缓存准备 ├─ 提交计算任务 └─ 成功、失败、取消、超时、节点故障统一释放预留 ``` ### 6.6 高风险操作审批流程 ```text 用户发起高风险操作 │ ├─ 检查当前用户是否有基础资源权限 ├─ 检查是否为管理员旁路 ├─ 匹配 tenant + action + resource_type 审批策略 ├─ 创建 pending 审批实例 ├─ 审批人从当前会话确定 ├─ approved -> 幂等执行具体效果 │ ├─ ACL 授权 │ ├─ GPU 分配 │ └─ 配额更新 ├─ rejected/cancelled/expired -> 不产生资源效果 └─ 全流程记录审计 ``` ## 七、纠偏开发计划 ### 阶段 0:冻结口径 1. 书面确认:不再把项目作为新业务隔离层。 2. 确认平台角色只有平台管理员和平台用户两类;租户职责由 `tenant_members.role` 表达。 3. 确认 `tenant_members` 是租户关系权威表,`users.tenant_id` 仅作为 primary tenant 兼容字段。 4. 确认所有资源必须有 `tenant_id`、`created_by`、生命周期字段。 5. 确认基座模型共享不等于训练模型和数据集共享。 ### 阶段 1:身份和租户纠偏 1. 为平台角色建立统一映射,停止新增 `role='user'` 的特殊逻辑。 2. 用户创建、租户创建、成员邀请、成员接受、成员移除统一走成员服务。 3. 创建租户时同步 owner 成员记录。 4. 租户成员变更、用户禁用、用户软删除时统一撤销会话、GPU 分配、ACL、待审批流程。 5. 新增当前租户查询和切换接口,前端显示“当前租户”,避免仅显示用户主租户。 ### 阶段 2:资源数据纠偏 1. 对所有资源表补齐并回填 `tenant_id`、`created_by`、`created_at`、`updated_at`。 2. 将任务 payload 中的创建者和租户迁移到结构化字段。 3. 对历史 `tenant_id IS NULL` 数据按以下顺序处理: - 能从创建者找到租户的,回填创建者所属租户。 - 系统生成数据集能从源任务找到租户的,继承源任务租户。 - 仍无法确定归属的,进入待治理清单,不自动公开。 - 管理员明确授权的历史资源可临时通过用户级 ACL 访问,直到完成归属迁移。 4. 为资源和租户建立必要索引;在数据清洗完成后再逐步收紧 `NOT NULL` 和外键。 ### 阶段 3:权限判定收敛 1. 建立统一 `authorize()` 服务,所有列表、详情、文件、版本、下载、MinIO、缓存和任务接口调用同一套判定。 2. 页面权限只用于模块入口;资源动作必须在后端独立判断。 3. 统一基座模型、训练模型、数据集和任务的血缘权限来源。 4. 统一 GPU assignment、GPU reservation 和租户配额 reservation 的状态流转。 5. 审批实例增加幂等键和过期处理任务,避免重复审批和重复执行。 ### 阶段 4:数据库和初始化脚本 1. 将完整初始化 SQL 与迁移 SQL 的职责分开:新库只执行一份完整脚本,旧库执行版本化迁移。 2. 初始化 SQL 中统一种子用户、种子角色、默认租户和租户成员关系,不能出现 operator 仍拥有普通用户不应有的 `compute` 权限而代码又剥离该权限的矛盾。 3. 给所有需要的关联增加外键或由应用层统一保证引用完整性。 4. 为 `tenant_id + status`、`created_by + deleted_at`、ACL 主体、审批状态和 GPU 状态建立索引。 5. 主工程与 `docker/offline/src` 的完整 SQL、迁移、代码和部署文档做 SHA-256 一致性校验。 ### 阶段 5:前端流程纠偏 1. 用户管理页面分别显示平台角色、所属租户、租户角色、页面权限和资源权限,不再把它们放在一个“权限”字段中。 2. 租户详情页显示成员、租户角色、配额使用、审批策略和资源统计。 3. 资源详情页显示资源所属租户、创建者、授权主体、动作、有效期和授权来源。 4. 训练/推理/评测页面只显示当前用户有 `execute` 权限且已获批 GPU 的资源。 5. 对“无权访问”“未授权”“审批中”“资源已删除”“租户已停用”分别展示原因,不统一显示为数据为空或 500。 ## 八、建议的验收矩阵 | 场景 | 预期结果 | |---|---| | 用户未加入租户访问租户资源 | 403 | | 用户属于租户但没有资源 ACL | 资源列表不可见,详情 403 | | 用户获得 dataset.read | 数据集可见,可预览,但不能训练或上传 | | 用户获得 dataset.execute | 数据集可用于训练/评测,但不能编辑或下载 | | 用户获得 dataset.download | 可以下载文件,但不能训练 | | 用户获得 trained_model.execute | 可以推理/评测,不能导出 | | 用户获得 trained_model.download | 可以导出或下载,不能执行推理 | | 用户获批 GPU 但没有数据集 execute | 不能创建训练任务 | | 用户有数据集 execute 但没有 GPU assignment | 不能创建训练任务 | | 管理员撤销 ACL | 列表、详情、执行、下载均立即重新校验 | | 用户被禁用 | 会话失效,成员和 ACL 不能继续生效 | | 租户被停用 | 租户资源不可创建和执行,运行任务进入待处理/失败策略 | | 历史资源 tenant_id 为空且无 ACL | 普通用户不可见 | | 历史资源 tenant_id 为空且有管理员直接用户 ACL | 仅指定用户在有效期内可见和按动作使用 | | 资源删除 | 资源不可见,ACL、审批、对象清理流程被触发 | | MinIO 不可用 | 资源元数据权限仍可判断,涉及对象的操作返回明确存储故障 | ## 九、纠偏完成判定 满足以下条件,才认为租户与用户权限完成统一,而不是“接口能够调用”: - `tenant_members` 成为租户关系唯一权威来源,`users.tenant_id` 只作为兼容主租户。 - 平台角色和租户角色职责分离,角色权限不再通过多个 JSON/字段重复维护。 - 新资源全部具备非空租户和创建者,历史资源有明确归属或进入治理清单。 - 所有资源接口、文件接口、MinIO 接口和任务接口使用统一动作授权。 - 数据集、基座模型、训练模型和任务之间的派生关系不会自动扩大访问范围。 - 用户创建、成员邀请、资源授权、GPU 申请、配额申请和审批都能追溯到用户、租户、资源、动作和请求 ID。 - 初始化 SQL、迁移 SQL、在线数据库和离线源码目录的表结构与权限逻辑一致。 - 双用户、跨租户、ACL 撤销、权限过期、用户禁用、租户停用、GPU 冲突和 MinIO 故障场景全部通过验证。 ## 十、2026-08-20 本轮改造结果 本轮已按目标层级模型完成一轮可运行的后端、数据库和前端收敛: 1. `users.platform_role` 已作为平台角色标准字段,值为 `platform_admin` 或 `platform_user`;历史 `users.role` 继续返回,供旧客户端兼容。 2. `tenant_members` 已作为租户成员关系的权威来源。创建租户时会在同一事务中写入 `owner` 成员;创建用户时单独使用 `tenant_role` 写入成员角色,不再因为平台角色是管理员就自动成为租户 owner。 3. 用户接口返回 `platform_role`、`tenant_memberships` 和 `tenant_count`,组织页面已区分平台身份与租户成员关系。 4. `fine_tune_tasks` 已增加结构化 `tenant_id`、`created_by`、`deleted_at`、`deleted_by`;训练列表、详情和 owner 判定优先使用结构化字段,历史 payload 仅作回退。 5. 新建数据集、训练、评测、推理任务会绑定当前认证租户;普通用户不能仅通过请求体伪造其他租户。平台管理员仍可为指定租户创建资源。 6. 普通用户的资源列表继续通过租户成员、所有者和 ACL 统一过滤;项目不再出现在前端路由、组织页面和新权限判断中。 7. 租户删除已补充 `deleted_at/deleted_by` 软删除,并同步禁用该租户的成员关系。 8. `000_full_init.sql` 已包含本轮标准字段、索引、owner 成员修复和无项目业务依赖的种子数据;`docker/offline/src/backend/app/db/sql` 仅保留这一份完整初始化脚本。 9. `docker/offline/src` 已同步当前 backend、frontend、compute 源码和构建后的 `frontend-dist`。 本轮已在线验证:数据库字段存在、普通用户身份返回、用户租户成员关系返回、租户 owner 自动建立、租户软删除、跨租户伪造创建被 403 拒绝;前端 `npm run build` 通过。项目表、项目字段及旧项目 API 仅保留历史兼容读取,不再作为新业务隔离条件。 ## 十一、下一步开发计划 1. 为资源详情和资源授权页面补充租户、创建者、授权来源、有效期和动作权限的可视化信息。 2. 将数据处理、数据转换等派生资源的租户和创建者继承改为统一服务,并清理历史 `tenant_id IS NULL` 待治理清单。 3. 将 GPU assignment、GPU reservation、租户配额 reservation 和审批实例整理为同一状态机,补充节点故障回收验收。 4. 执行双租户、双用户、ACL 过期/撤销、用户禁用、租户停用、MinIO 故障和多 GPU 冲突的完整回归测试。