随着 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 原有的超级管理员能力,并为普通用户设置四类初始业务角色。
超级管理员继续通过 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. 历史数据迁移
现有数据通过一个统一迁移脚本完成调整:
初始化业务角色和权限。
根据 IQC 供应商名称和 OQC 客户名称创建公司。
建立用户角色、用户公司以及报表公司关系。
校验现有账户和报表均已完成关联。
删除 IQC 报表原有的
owner_id、供应商名称字段和 OQC 报表原有的客户名称字段。
将创建、关联、校验和旧字段清理放在同一个迁移过程内,可以避免系统长期处于新旧归属关系并存的状态。
10. 总结
这套设计将登录账户、功能权限和业务数据归属拆分为相互独立的模型。RBAC 负责表达岗位能够执行的操作,公司数据域负责表达供应商账号能够访问的数据以及 IQC、OQC 报表的业务归属。
在此基础上,系统可以动态调整角色权限,支持同一供应商的多个账号共享 IQC 数据,并以“下游客户 → 机种”的方式组织 OQC 数据。后续新增角色、权限或公司类型时,也不需要重新改造现有认证模型。