4、角色管理模块:角色创建、角色编辑、角色删除、角色继承关系
角色管理,说白了就是给「权限」找个容器。你想想看,如果每个用户都要单独配权限,那配方系统里有几百号人,光配权限就能配到崩溃。所以我们需要角色——把一堆权限打包成一个角色,再把角色分配给用户。
我在好几个配方管理项目里都踩过角色的坑。嗯,今天就把这些经验掰开揉碎了讲给你听。
4.1 角色创建:别小看这一步
角色创建看起来简单——填个名字,点个保存。但我在项目中遇到过一个问题:角色名称没做唯一性校验,结果生产环境里出现了两个「质检员」角色,权限还不一样,排查了整整两天。
我曾经踩过的坑:角色名称必须全局唯一,且建议用英文标识+中文描述的组合。比如 role_code = "qc_inspector",role_name = "质检员"。这样代码里引用时不会乱。
创建角色时,我建议至少包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| role_code | 字符串 | 角色编码,全局唯一,用于代码判断 |
| role_name | 字符串 | 角色名称,显示给用户看 |
| description | 文本 | 角色描述,说明这个角色是干嘛的 |
| parent_role_id | 整数 | 父角色ID,用于继承关系 |
| status | 枚举 | 启用/禁用,禁用时该角色下所有用户都失效 |
代码实现上,我个人习惯用这样的结构:
// 角色创建接口示例
POST /api/roles
{
"role_code": "qc_inspector",
"role_name": "质检员",
"description": "负责配方质检流程的操作员",
"parent_role_id": null,
"status": "enabled"
}
小技巧:创建角色时,建议默认勾选「启用」状态。但如果是继承自父角色的子角色,最好默认「禁用」,等权限配置完再手动启用。防止权限还没配好就被用户用上了。
4.2 角色编辑:权限变更的「安全阀」
角色编辑比创建复杂得多。为什么?因为角色一旦被分配给了用户,你改角色权限就会影响到所有关联用户。我在一个项目中遇到过:编辑角色时不小心去掉了「查看配方」权限,结果整个质检组都看不到配方了,生产线直接停摆。
所以我建议角色编辑时,必须做两件事:
- 变更记录:每次编辑都要记录谁改了、改了啥、什么时候改的
- 影响范围提示:保存前弹窗提示「此角色已分配给 23 个用户,确认修改?」
编辑界面的核心逻辑:
// 角色编辑时,权限变更的对比逻辑
const originalPermissions = getRolePermissions(roleId);
const newPermissions = getFormPermissions();
// 找出差异
const added = newPermissions.filter(p => !originalPermissions.includes(p));
const removed = originalPermissions.filter(p => !newPermissions.includes(p));
// 如果有差异,记录变更并提示用户
if (added.length > 0 || removed.length > 0) {
showConfirmDialog({
title: '权限变更确认',
message: `将新增 ${added.length} 项权限,移除 ${removed.length} 项权限`,
onConfirm: () => saveRoleChanges(roleId, newPermissions)
});
}
核心原则:角色编辑是「批量操作」,一次修改影响多人。所以编辑操作必须比创建操作多一层确认、多一层记录、多一层回滚机制。
4.3 角色删除:最危险的操作
角色删除,嗯,这个我得好好说说。我曾经在生产环境里直接删了一个角色,结果那个角色下的 50 多个用户瞬间变成了「无角色」状态,所有权限都丢了。虽然数据没丢,但用户那边炸了锅。
所以我现在的做法是:角色删除必须走「软删除 + 转移」流程。
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 检查关联用户 | 统计该角色下有多少用户 |
| 2 | 选择转移角色 | 这些用户要转移到哪个角色下 |
| 3 | 确认删除 | 二次确认,输入角色名称才能删除 |
| 4 | 软删除 | 标记为 deleted,保留历史数据 |
代码实现上,我建议这样:
// 角色删除接口
DELETE /api/roles/{roleId}
// 请求体
{
"transfer_to_role_id": 123, // 用户转移到哪个角色
"confirm_role_name": "质检员" // 输入角色名称确认
}
// 后端处理逻辑
function deleteRole(roleId, transferToRoleId, confirmName) {
const role = getRoleById(roleId);
if (role.role_name !== confirmName) {
throw new Error('角色名称确认失败');
}
// 转移用户
const users = getUsersByRoleId(roleId);
users.forEach(user => {
assignRoleToUser(user.id, transferToRoleId);
});
// 软删除角色
softDeleteRole(roleId);
}
绝对不要做的事:不要直接物理删除角色!你永远不知道哪天需要回溯历史权限数据。软删除 + 保留操作日志,这是底线。
4.4 角色继承关系:权限复用的精髓
角色继承,说白了就是「子角色自动拥有父角色的权限」。这个设计在配方管理系统里特别实用。比如你有一个「配方管理员」角色,下面可以继承出「高级配方管理员」「初级配方管理员」等子角色。
我建议的继承规则:
- 单向继承:子角色继承父角色的所有权限,但不能反向继承
- 权限叠加:子角色可以额外添加权限,但不能移除父角色的权限
- 层级限制:最多 3 层继承,太深了容易混乱
下面这张图展示了角色继承的核心逻辑:
实现继承关系时,我建议用这样的数据结构:
// 角色权限合并逻辑
function getEffectivePermissions(roleId) {
const role = getRoleById(roleId);
let permissions = [];
// 递归获取父角色权限
if (role.parent_role_id) {
const parentPermissions = getEffectivePermissions(role.parent_role_id);
permissions = permissions.concat(parentPermissions);
}
// 叠加当前角色自己的权限
const selfPermissions = getRoleSelfPermissions(roleId);
permissions = permissions.concat(selfPermissions);
// 去重
return [...new Set(permissions)];
}
我的经验:继承关系不要搞太深。3 层以内是清晰的,超过 3 层,你根本搞不清某个权限是从哪一层继承来的。我见过一个项目搞了 7 层继承,最后没人敢改角色权限,因为不知道会影响到谁。
4.5 角色管理的避坑总结
嗯,说了这么多,最后给你总结几个我踩过的坑:
- 角色名称别用中文做唯一标识——我见过「质检员」和「质检员(带空格)」被当成两个角色
- 删除角色前必须检查关联用户——不然用户权限会「凭空消失」
- 继承关系要显示在界面上——用户需要一眼看出某个角色是从哪继承来的
- 角色变更要有审批流程——特别是生产环境,改角色权限最好走审批
一句话总结:角色管理不是简单的增删改查,它是权限体系的骨架。骨架歪了,整个权限系统都会出问题。创建要规范、编辑要留痕、删除要转移、继承要清晰——这四件事做好了,角色管理就稳了。