随着 SPC 系统中的 IQC、OQC 数据和使用人员不断增加,原有的“超级管理员与普通用户”两级权限已经无法准确表达实际岗位职责。同时,IQC 数据归属于具体供应商账号、OQC 数据只按机种展示,也逐渐暴露出账号间无法共享数据、出货去向不清晰等问题。

这次设计的重点不是简单增加几个角色,而是将登录身份、功能权限和业务数据归属拆开管理:角色与权限回答“用户能做什么”,公司数据域回答“用户能看哪些数据、报表属于谁”。

1. 业务问题

系统需要同时解决两类问题。

第一类是功能权限。OQC 质检员、IQC 质检员、查看员和 IQC 供应商承担不同工作,录入、查看、修改、删除、导出和系统配置等操作不能继续只依赖“是否为超级管理员”判断。

第二类是数据归属。IQC 供应商可能拥有多个登录账号,这些账号应共享同一家公司的 IQC 数据;OQC 则需要从原来的机种列表调整为“下游客户 → 机种”,让用户能够直接识别物料的出货去向。

2. 核心设计

整体模型拆分为账户、角色、权限、公司和报表五类对象:

flowchart LR
    U["用户账户"] -->|"配置一个业务角色"| R["角色"]
    R -->|"配置多个功能权限"| P["权限"]
    U -->|"供应商账号关联公司"| C["公司"]
    C -->|"业务归属"| IQC["IQC 报表"]
    C -->|"业务归属"| OQC["OQC 报表"]

账户只负责登录和识别当前用户;普通账户通过角色获得功能权限;公司作为 IQC、OQC 数据的业务归属主体。这样可以避免将岗位职责、登录账号和数据所有权混在同一个模型中。

3. RBAC 角色权限设计

系统保留 FastAPI Users 原有的超级管理员能力,并为普通用户设置四类初始业务角色。

用户类型

主要能力

数据范围

OQC 质检员

录入、查看 OQC 数据

全部 OQC 数据

IQC 质检员

录入、上传、查看 IQC 数据

全部 IQC 数据

查看员

只读查看 IQC、OQC 数据

全部 IQC、OQC 数据

IQC 供应商

上传、查看 IQC 数据

所属公司的 IQC 数据

超级管理员

全部业务和系统管理功能

全部数据

超级管理员继续通过 user.is_superuser 识别,不参与普通角色配置。普通用户通过 user_role 关联一个角色,角色再通过 role_permission 关联多个权限。角色与权限的关系保存在数据库中,可以由后台动态调整,不需要把角色判断散落在业务代码里。

权限由以下信息组成:

  • name:面向用户展示的权限名称。

  • code:系统内全局唯一的权限标识,供前后端鉴权使用。

  • group:权限所属分组,用于按照 IQC、OQC、账户与系统管理等模块展示权限。

group 只负责分类,code 只负责唯一标识。权限编码不使用 iqc.*、oqc.* 这样的模块前缀,避免同时在编码和分组字段中重复表达模块信息。

4. 公司数据域设计

公司统一分为上游供应商和下游客户两类,同一家公司不能同时属于两种类型。

  • 上游供应商用于 IQC 账号关联、数据分类和报表归属。

  • 下游客户用于 OQC 数据分类和出货去向展示,不需要创建系统账号。

4.1 用户与公司

用户是 FastAPI Users 提供的通用认证对象,公司归属只适用于 IQC 供应商账号。因此用户表不直接增加公司字段,而是通过 user_company 维护关系。

user_company.user_id 保持唯一,表示一个供应商账号最多属于一家公司;同一个 company_id 可以对应多个账号,从而支持同一家供应商的多个账号共享 IQC 数据。

4.2 报表与公司

IQC、OQC 报表直接保存 company_id。这是因为一家公司可以拥有多张报表,而一张报表只归属于一家公司,属于标准的一对多关系。

如果为这种关系额外增加报表公司关联表,不仅会增加查询关联,还需要对报表 ID 设置唯一约束才能维持“一张报表一个公司”的规则。只有未来出现一张报表同时归属于多家公司,或者关联关系需要记录有效期、来源等属性时,才需要单独的关联表。

5. 后端权限控制

后端继续使用 FastAPI Users 完成登录认证。超级管理员直接拥有全部权限,普通用户则通过 FastAPI 的 Depends 依赖统一校验接口所需的权限编码。

@router.post(
    "/import",
    dependencies=[Depends(require_permission("import_iqc_data"))],
)
async def import_iqc_data():
    ...

Depends 会在接口执行前完成当前用户和权限检查。校验失败时直接返回 403,校验成功后才进入业务接口。这样可以把权限规则集中管理,避免在每个接口中重复编写角色判断。

功能权限与数据范围需要分别处理。Depends 判断用户是否具有查看或上传 IQC 数据的能力;当用户是 IQC 供应商时,查询层或 Service 层还要通过 user_company 取得其 company_id,只返回该公司的 IQC 数据。

6. 前端权限控制

用户登录后,用户信息接口返回当前角色和权限列表,前端在统一用户状态中保存权限编码。

前端根据权限完成以下控制:

  • 路由守卫阻止用户进入无权访问的页面。

  • 导航菜单和业务 Tab 根据权限显示或隐藏。

  • 新增、上传、修改、删除、导出和配置按钮根据权限显示或隐藏。

  • 权限查看页面根据 group 对权限进行分组展示。

前端不直接判断“OQC 质检员”或“IQC 供应商”等角色名称,而是判断具体权限编码。这样后台调整角色权限后,前端功能可以随权限结果变化,不需要同步修改角色判断代码。

前端控制主要用于界面展示和操作引导,不能作为安全边界。即使按钮被隐藏,后端接口仍必须执行完整权限校验。

7. OQC 按下游客户分类

OQC 页面由原来的机种列表调整为以下层级:

OQC
├─ 下游客户 A
│  ├─ 机种 A01
│  └─ 机种 A02
└─ 下游客户 B
   └─ 机种 B01

新建或导入 OQC 数据时,系统根据客户名称匹配已经维护的下游客户,并将机种报表归入对应分类。未匹配到客户时不自动创建公司,而是提示先维护公司资料。

同一机种涉及多个下游客户时,按照该机种第一次进入系统时确定的客户进行归类,后续导入继续沿用首次确定的分类。

8. 数据库关系

核心表关系可以简化为两条链路:

flowchart LR
    U["用户"] -->|"最多一条"| UR["用户角色关联"]
    R["角色"] -->|"可分配给多个用户"| UR
    R -->|"可配置多个权限"| RP["角色权限关联"]
    P["权限"] -->|"可配置给多个角色"| RP

    U -->|"最多一条"| UC["用户公司关联"]
    C["公司"] -->|"可包含多个账号"| UC
    C -->|"可拥有多张"| IR["IQC 报表"]
    C -->|"可拥有多张"| OR["OQC 报表"]

用户与角色、公司使用关联表解耦;角色与权限使用关联表实现动态多对多配置;IQC、OQC 报表则直接通过 company_id 表达业务归属。

9. 历史数据迁移

现有数据通过一个统一迁移脚本完成调整:

  1. 初始化业务角色和权限。

  2. 根据 IQC 供应商名称和 OQC 客户名称创建公司。

  3. 建立用户角色、用户公司以及报表公司关系。

  4. 校验现有账户和报表均已完成关联。

  5. 删除 IQC 报表原有的 owner_id、供应商名称字段和 OQC 报表原有的客户名称字段。

将创建、关联、校验和旧字段清理放在同一个迁移过程内,可以避免系统长期处于新旧归属关系并存的状态。

10. 总结

这套设计将登录账户、功能权限和业务数据归属拆分为相互独立的模型。RBAC 负责表达岗位能够执行的操作,公司数据域负责表达供应商账号能够访问的数据以及 IQC、OQC 报表的业务归属。

在此基础上,系统可以动态调整角色权限,支持同一供应商的多个账号共享 IQC 数据,并以“下游客户 → 机种”的方式组织 OQC 数据。后续新增角色、权限或公司类型时,也不需要重新改造现有认证模型。