前言
大家好,我是 elk!在上篇文章中,我们实现了菜单管理和动态路由权限控制。今天,我们将深入 RBAC 权限模型的核心环节------角色管理。角色作为连接用户与权限的桥梁,其管理功能直接决定了系统的权限分配灵活性。
🔑 核心功能亮点:
- 角色与菜单权限的绑定机制
- 事务处理保证数据一致性
- 批量操作用户角色关系
模块创建与结构优化
标准化模块生成
bash
nest g res role --no-spec # 生成角色管理基础结构
DTO 定义(数据处理对象)
typescript
// create-role.dto.ts
export class CreateRoleDto {
@ApiProperty({ description: '角色名称' })
@IsNotEmpty()
roleName: string;
@ApiProperty({ description: '菜单权限ID集合' })
@IsNumber({}, { each: true })
menuIds: number[]; // 更清晰的字段命名
// 移除非必要字段(如delFlag)
}
实体映射
typescript
// role.entity.ts
import { sys_role as Role } from '@prisma/client';
export class RoleEntity implements Role {
role_id: number;
role_name: string;
role_label: string;
status: string;
remark: string;
order_num: number;
del_flag: string;
created_by: string;
created_at: Date;
updated_by: string;
updated_at: Date;
}
🔐 角色管理核心实现
接口设计全景
功能 | 方法 | 路径 | 参数 | 技术点 |
---|---|---|---|---|
创建角色 | POST | /system/role | CreateRoleDto | 事务处理、批量权限关联 |
角色列表 | GET | /system/role/list | pageNum, pageSize | 驼峰转换(humps)、分页参数处理 |
角色详情 | GET | /system/role/:id | id | 关联查询(include) |
更新角色 | PUT | /system/role | UpdateRoleDto | 事务删除-重建权限 |
删除角色 | DELETE | /system/role/:id | id | 级联删除 |
关键业务逻辑优化
1. 角色创建(事务处理)
typescript
async create(createRoleDto: CreateRoleDto) {
// 角色权限-接收处理
const roleKeys = createRoleDto.roleKey;
// 新增角色
const role = this.prisma.sys_role.create({
data: {
role_name: createRoleDto.roleName,
role_label: createRoleDto.roleLabel,
status: createRoleDto.status,
remark: createRoleDto.remark,
order_num: createRoleDto.orderNum,
del_flag: createRoleDto.delFlag,
},
});
// 角色与菜单进行绑定
const roleMenu = this.prisma.sys_role_menu.createMany({
data: roleKeys.map((roleKey) => {
return {
role_id: createRoleDto.roleId,
menu_id: roleKey,
};
}),
});
// 事务-保证角色和角色菜单表数据一致性
const transaction = await this.prisma.$transaction([role, roleMenu]);
if (!transaction) {
return '新增失败';
} else {
return '新增成功';
}
}
2. 权限更新(原子操作)
typescript
async update(dto: UpdateRoleDto) {
return this.prisma.$transaction([
// 删除原有权限
this.prisma.sys_role_menu.deleteMany({
where: { role_id: dto.roleId }
}),
// 添加新权限
this.prisma.sys_role_menu.createMany({
data: dto.menuIds.map(menuId => ({
role_id: dto.roleId,
menu_id: menuId
}))
}),
// 更新角色信息
this.prisma.sys_role.update({
where: { role_id: dto.roleId },
data: {
role_name: dto.roleName,
updated_at: new Date()
}
})
]);
}
3. 角色详情查询(关联数据优化)
typescript
async findOne(id: number): Promise<RoleEntity> {
const role = await this.prisma.sys_role.findUnique({
where: {
role_id: id,
},
include: {
menus: {
select: {
menu_id: true
}
}
}
});
// 判断是否存在无数据的情况
if (!role) {
throw new NotFoundException(`角色ID ${id} 不存在`);
}
// 空值以排除,安然取值
return {
...camelizeKeys(role),
roleKey: role.menus.map(menu => menu.menu_id)
};
}
4. 批量删除支持
typescript
@Delete('batch')
async batchRemove(@Body() ids: number[]) {
return this.prisma.$transaction([
// 删除关联权限
this.prisma.sys_role_menu.deleteMany({
where: { role_id: { in: ids } }
}),
// 删除角色本体
this.prisma.sys_role.deleteMany({
where: { role_id: { in: ids } }
})
]);
}
💡 深度实践技巧
1. 事务处理最佳实践
typescript
// 错误处理增强
try {
await this.prisma.$transaction([...operations], {
maxWait: 5000, // 最大等待时间
timeout: 10000 // 超时时间
});
} catch (e) {
this.logger.error('角色更新失败', e.stack);
throw new BadRequestException('数据更新失败');
}
2. 数据权限扩展实现
typescript
// 在角色实体中添加数据权限字段
@ApiProperty({ description: '数据权限范围' })
dataScope: DataScopeEnum; // ENUM: ALL, DEPT, CUSTOM
// 创建时存储部门权限
if (dto.dataScope === DataScopeEnum.CUSTOM) {
await prisma.sys_role_dept.createMany({
data: dto.deptIds.map(deptId => ({
role_id: role.role_id,
dept_id: deptId
}))
});
}
3. 缓存优化策略
typescript
// 角色权限缓存
@Cacheable({ key: 'role:${roleId}:menus' })
async getRoleMenus(roleId: number) {
return this.prisma.sys_role_menu.findMany({
where: { role_id: roleId }
});
}
// 更新时清除缓存
@CacheEvict({ key: 'role:${dto.roleId}:menus' })
async update(dto: UpdateRoleDto) { ... }
🔜 下篇预告:用户管理模块
在下一篇文章中,我们将深入:
1️⃣ 用户-角色绑定关系管理
2️⃣ 部门树形结构实现
3️⃣ 用户信息加密存储方案
4️⃣ 批量导入导出实战
💡 思考题:如何实现用户离职时的权限自动回收?欢迎在评论区分享你的方案!