招聘通是我们平台的企业端招聘管理产品,最早是单账号模式——一个企业一个账号,所有人共用。随着客户规模变大,企业要求支持多账号、分权限管理:HR 专员只能看自己负责的职位,HR 经理能看全部,管理员能配置权限。2021年初我们做了分账号系统,今天把架构设计和落地经验分享出来。
一、需求分析
核心需求:
- 多账号:一个企业可以创建多个子账号,每个子账号独立登录
- 角色权限:预设角色(管理员、HR经理、HR专员、面试官),也支持自定义角色
- 数据隔离:子账号只能看到有权限的数据(职位、简历、面试等)
- 操作审计:记录子账号的关键操作,支持审计追溯
- 账号生命周期:创建、启用、禁用、删除,离职员工账号及时回收
二、数据隔离方案选型
多租户数据隔离有三种常见方案:
| 方案 | 隔离程度 | 成本 | 适用场景 |
|---|---|---|---|
| 独立数据库 | 最高 | 高 | 大客户、对数据隔离要求极高 |
| 共享数据库独立 Schema | 中 | 中 | 中等规模租户 |
| 共享数据库共享表(tenant_id) | 低 | 低 | 大量小租户 |
我们的场景是企业内部多账号,不是真正的多租户 SaaS,数据隔离要求没那么高。选了最简单的共享表 + 企业ID隔离方案,所有表带 company_id 字段,查询时自动加上企业过滤。
子账号级别的数据隔离(比如 HR 专员只能看自己的简历)通过权限系统在应用层控制,不是数据库层面隔离。
三、数据库设计
3.1 核心表结构
| |
3.2 权限节点设计
权限用编码标识,按模块分层:
权限节点存在数据库里,系统初始化时导入。新增权限节点时通过数据库迁移添加。
四、权限校验实现
4.1 登录态与企业隔离
用户登录后,Session 里保存 account_id 和 company_id。所有查询自动加上 company_id 过滤,确保跨企业数据隔离。
用 Yii2 的 Behavior 实现自动加企业过滤:
| |
模型里挂上这个 Behavior,查询和插入时自动处理 company_id,业务代码不用关心。
4.2 权限校验中间件
用 Yii2 的 ActionFilter 做权限校验:
| |
4.3 权限服务
权限检查用 Redis 缓存,避免每次查数据库:
| |
角色或账号权限变更时,调用 clearCache() 清缓存。
4.4 数据范围控制
数据范围控制账号能看到哪些数据。比如 HR 专员只能看到自己创建的职位,HR 经理能看到全公司的。
| |
五、操作审计
关键操作记录审计日志:
| |
用 Behavior 自动记录审计日志:
| |
六、踩过的坑
1. 权限缓存不一致
角色权限变更后只清了角色关联的账号缓存,漏了一些账号,导致权限变更后部分账号还是旧权限。后来改成角色变更时,查所有关联这个角色的账号,全部清缓存。
2. 企业隔离 Behavior 漏掉关联查询
CompanyScopeBehavior 只处理了主表查询,关联查询(JOIN)时没加企业过滤,导致跨企业数据泄露。后来在所有关联查询里手动加了企业条件,或者用子查询替代 JOIN。
3. 主账号权限绕过
主账号(is_owner=1)应该拥有所有权限,但有些地方直接查角色权限表,主账号没关联角色,返回空权限。在权限服务里加了主账号短路判断,主账号直接返回 true。
4. 数据范围与权限的关系没理清
数据范围和功能权限是两个维度:功能权限控制能不能操作(如能不能删除职位),数据范围控制能看到哪些数据(如能看到哪些职位)。一开始混在一起,后来拆成两个独立的服务,逻辑清晰了很多。
5. 审计日志表膨胀
审计日志增长很快,一年就到了几千万条,查询变慢。后来按月份分区,超过一年的日志归档到冷存储,只保留近一年的在线查询。
七、总结
分账号系统的核心设计:
- 数据隔离:共享表 + company_id,Behavior 自动加过滤,业务代码无感知
- 权限模型:RBAC(账号 → 角色 → 权限),支持预设角色和自定义角色
- 数据范围:独立于功能权限,控制能看到的数据范围(全部/部门/自己)
- 权限缓存:Redis 缓存权限,变更时主动清缓存
- 操作审计:Behavior 自动记录关键操作,支持审计追溯
- 账号生命周期:创建/启用/禁用/删除,离职及时回收权限
这套系统上线后,支撑了几千家企业的多账号管理,稳定性和性能都没问题。架构上留了扩展空间,后续加了部门管理、审批流程等功能,都是在这个基础上扩展的。
多账号系统的设计难点不在技术,而在业务逻辑的梳理——权限和数据范围的边界要划清楚,否则很容易出现越权或数据泄露。设计阶段多花时间梳理需求,比上线后补漏洞成本低得多。