refactor(system/role): 重构角色管理

This commit is contained in:
2025-02-06 20:36:18 +08:00
parent 4caada8c64
commit f6535ef7a3
20 changed files with 455 additions and 77 deletions

View File

@@ -16,7 +16,12 @@
package top.continew.admin.system.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import top.continew.admin.system.model.entity.UserRoleDO;
import top.continew.admin.system.model.resp.role.RoleUserResp;
import top.continew.starter.data.mp.base.BaseMapper;
/**
@@ -26,4 +31,15 @@ import top.continew.starter.data.mp.base.BaseMapper;
* @since 2023/2/13 23:13
*/
public interface UserRoleMapper extends BaseMapper<UserRoleDO> {
/**
* 分页查询列表
*
* @param page 分页条件
* @param queryWrapper 查询条件
* @return 分页列表信息
*/
IPage<RoleUserResp> selectUserPage(@Param("page") IPage<UserRoleDO> page,
@Param(Constants.WRAPPER) QueryWrapper<UserRoleDO> queryWrapper);
}

View File

@@ -16,6 +16,7 @@
package top.continew.admin.system.model.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -37,6 +38,12 @@ public class UserRoleDO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId
private Long id;
/**
* 用户 ID
*/

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.admin.system.model.query;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 角色关联用户查询条件
*
* @author Charles7c
* @since 2025/2/5 22:01
*/
@Data
@Schema(description = "角色关联用户查询条件")
public class RoleUserQuery implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 角色 ID
*/
@Schema(description = "角色 ID", example = "1")
private Long roleId;
/**
* 关键词
*/
@Schema(description = "关键词", example = "zhangsan")
private String description;
}

View File

@@ -72,4 +72,10 @@ public class UserQuery implements Serializable {
*/
@Schema(description = "用户 ID 列表", example = "[1,2,3]")
private List<Long> userIds;
/**
* 不包含的用户 ID 列表
*/
@Schema(description = "不包含的用户 ID 列表", example = "[1,2]")
private List<Long> excludeUserIds;
}

View File

@@ -73,12 +73,6 @@ public class RoleReq implements Serializable {
@Length(max = 200, message = "描述长度不能超过 {max} 个字符")
private String description;
/**
* 功能权限:菜单 ID 列表
*/
@Schema(description = "功能权限:菜单 ID 列表", example = "1000,1010,1011,1012,1013,1014")
private List<Long> menuIds = new ArrayList<>();
/**
* 数据权限
*/
@@ -91,12 +85,6 @@ public class RoleReq implements Serializable {
@Schema(description = "权限范围:部门 ID 列表", example = "5")
private List<Long> deptIds = new ArrayList<>();
/**
* 菜单选择是否父子节点关联
*/
@Schema(description = "菜单选择是否父子节点关联", example = "false")
private Boolean menuCheckStrictly;
/**
* 部门选择是否父子节点关联
*/

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.admin.system.model.req;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 修改角色功能权限参数
*
* @author Charles7c
* @since 2025/2/5 21:00
*/
@Data
@Schema(description = "修改角色功能权限参数")
public class RoleUpdatePermissionReq implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 角色 ID
*/
@Schema(description = "角色 ID", example = "1")
private Long roleId;
/**
* 功能权限:菜单 ID 列表
*/
@Schema(description = "功能权限:菜单 ID 列表", example = "1000,1010,1011,1012,1013,1014")
private List<Long> menuIds = new ArrayList<>();
/**
* 菜单选择是否父子节点关联
*/
@Schema(description = "菜单选择是否父子节点关联", example = "false")
private Boolean menuCheckStrictly;
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.admin.system.model.resp;
package top.continew.admin.system.model.resp.role;
import cn.crane4j.annotation.AssembleMethod;
import cn.crane4j.annotation.ContainerMethod;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.admin.system.model.resp;
package top.continew.admin.system.model.resp.role;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.admin.system.model.resp.role;
import cn.crane4j.annotation.Assemble;
import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import top.continew.admin.common.constant.ContainerConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.common.enums.GenderEnum;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* 角色关联用户信息
*
* @author Charles7c
* @since 2025/2/5 22:01
*/
@Data
@Schema(description = "角色关联用户信息")
public class RoleUserResp implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@Schema(description = "ID", example = "1")
private Long id;
/**
* 角色 ID
*/
@Schema(description = "角色 ID", example = "1")
private Long roleId;
/**
* 用户 ID
*/
@Schema(description = "用户 ID", example = "1")
@Assemble(prop = ":roleIds", sort = 0, container = ContainerConstants.USER_ROLE_ID_LIST)
private Long userId;
/**
* 用户名
*/
@Schema(description = "用户名", example = "zhangsan")
private String username;
/**
* 昵称
*/
@Schema(description = "昵称", example = "张三")
private String nickname;
/**
* 性别
*/
@Schema(description = "性别", example = "1")
private GenderEnum gender;
/**
* 状态
*/
@Schema(description = "状态", example = "1")
private DisEnableStatusEnum status;
/**
* 是否为系统内置数据
*/
@Schema(description = "是否为系统内置数据", example = "false")
private Boolean isSystem;
/**
* 描述
*/
@Schema(description = "描述", example = "测试人员描述信息")
private String description;
/**
* 部门 ID
*/
@Schema(description = "部门 ID", example = "5")
private Long deptId;
/**
* 所属部门
*/
@Schema(description = "所属部门", example = "测试部")
private String deptName;
/**
* 角色 ID 列表
*/
@Schema(description = "角色 ID 列表", example = "2")
@Assemble(prop = ":roleNames", container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class)
private List<Long> roleIds;
/**
* 角色名称列表
*/
@Schema(description = "角色名称列表", example = "测试人员")
private List<String> roleNames;
public Boolean getDisabled() {
return this.getIsSystem() && Objects.equals(roleId, SysConstants.SUPER_ROLE_ID);
}
}

View File

@@ -20,8 +20,9 @@ import top.continew.admin.common.context.RoleContext;
import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.query.RoleQuery;
import top.continew.admin.system.model.req.RoleReq;
import top.continew.admin.system.model.resp.RoleDetailResp;
import top.continew.admin.system.model.resp.RoleResp;
import top.continew.admin.system.model.req.RoleUpdatePermissionReq;
import top.continew.admin.system.model.resp.role.RoleDetailResp;
import top.continew.admin.system.model.resp.role.RoleResp;
import top.continew.starter.data.mp.service.IService;
import top.continew.starter.extension.crud.service.BaseService;
@@ -36,6 +37,14 @@ import java.util.Set;
*/
public interface RoleService extends BaseService<RoleResp, RoleDetailResp, RoleQuery, RoleReq>, IService<RoleDO> {
/**
* 修改角色权限
*
* @param id 角色 ID
* @param req 参数
*/
void updatePermission(Long id, RoleUpdatePermissionReq req);
/**
* 分配角色给用户
*

View File

@@ -17,6 +17,10 @@
package top.continew.admin.system.service;
import top.continew.admin.system.model.entity.UserRoleDO;
import top.continew.admin.system.model.query.RoleUserQuery;
import top.continew.admin.system.model.resp.role.RoleUserResp;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.resp.PageResp;
import java.util.List;
@@ -28,6 +32,15 @@ import java.util.List;
*/
public interface UserRoleService {
/**
* 分页查询角色关联用户列表
*
* @param query 查询条件
* @param pageQuery 分页查询条件
* @return 分页信息
*/
PageResp<RoleUserResp> pageUser(RoleUserQuery query, PageQuery pageQuery);
/**
* 批量分配角色给指定用户
*
@@ -46,6 +59,13 @@ public interface UserRoleService {
*/
boolean assignRoleToUsers(Long roleId, List<Long> userIds);
/**
* 根据 ID 删除
*
* @param ids ID 列表
*/
void deleteByIds(List<Long> ids);
/**
* 根据用户 ID 删除
*

View File

@@ -37,9 +37,10 @@ import top.continew.admin.system.mapper.RoleMapper;
import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.query.RoleQuery;
import top.continew.admin.system.model.req.RoleReq;
import top.continew.admin.system.model.req.RoleUpdatePermissionReq;
import top.continew.admin.system.model.resp.MenuResp;
import top.continew.admin.system.model.resp.RoleDetailResp;
import top.continew.admin.system.model.resp.RoleResp;
import top.continew.admin.system.model.resp.role.RoleDetailResp;
import top.continew.admin.system.model.resp.role.RoleResp;
import top.continew.admin.system.service.*;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.service.BaseServiceImpl;
@@ -71,8 +72,6 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
// 新增信息
Long roleId = super.add(req);
// 保存角色和菜单关联
roleMenuService.add(req.getMenuIds(), roleId);
// 保存角色和部门关联
roleDeptService.add(req.getDeptIds(), roleId);
return roleId;
@@ -95,12 +94,10 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
if (SysConstants.SUPER_ROLE_CODE.equals(req.getCode())) {
return;
}
// 保存角色和菜单关联
boolean isSaveMenuSuccess = roleMenuService.add(req.getMenuIds(), id);
// 保存角色和部门关联
boolean isSaveDeptSuccess = roleDeptService.add(req.getDeptIds(), id);
// 如果功能权限或数据权限有变更,则更新在线用户权限信息
if (isSaveMenuSuccess || isSaveDeptSuccess || ObjectUtil.notEqual(req.getDataScope(), oldDataScope)) {
// 如果数据权限有变更,则更新在线用户权限信息
if (isSaveDeptSuccess || ObjectUtil.notEqual(req.getDataScope(), oldDataScope)) {
this.updateUserContext(id);
}
}
@@ -121,6 +118,22 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
roleDeptService.deleteByRoleIds(ids);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updatePermission(Long id, RoleUpdatePermissionReq req) {
super.getById(id);
// 保存角色和菜单关联
boolean isSaveMenuSuccess = roleMenuService.add(req.getMenuIds(), id);
// 如果功能权限有变更,则更新在线用户权限信息
if (isSaveMenuSuccess) {
this.updateUserContext(id);
}
baseMapper.lambdaUpdate()
.set(RoleDO::getMenuCheckStrictly, req.getMenuCheckStrictly())
.eq(RoleDO::getId, id)
.update();
}
@Override
public void assignToUsers(Long id, List<Long> userIds) {
super.getById(id);

View File

@@ -16,18 +16,29 @@
package top.continew.admin.system.service.impl;
import cn.crane4j.annotation.AutoOperate;
import cn.crane4j.annotation.ContainerMethod;
import cn.crane4j.annotation.MappingType;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.common.constant.ContainerConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.system.mapper.UserRoleMapper;
import top.continew.admin.system.model.entity.UserRoleDO;
import top.continew.admin.system.model.query.RoleUserQuery;
import top.continew.admin.system.model.resp.role.RoleUserResp;
import top.continew.admin.system.service.UserRoleService;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.resp.PageResp;
import java.util.List;
@@ -43,6 +54,28 @@ public class UserRoleServiceImpl implements UserRoleService {
private final UserRoleMapper baseMapper;
@Override
@AutoOperate(type = RoleUserResp.class, on = "list")
public PageResp<RoleUserResp> pageUser(RoleUserQuery query, PageQuery pageQuery) {
String description = query.getDescription();
QueryWrapper<UserRoleDO> queryWrapper = new QueryWrapper<UserRoleDO>().eq("t1.role_id", query.getRoleId())
.and(StrUtil.isNotBlank(description), q -> q.like("t2.username", description)
.or()
.like("t2.nickname", description)
.or()
.like("t2.description", description));
// 排序
if (!pageQuery.getSort().isUnsorted()) {
for (Sort.Order order : pageQuery.getSort()) {
String property = order.getProperty();
queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property));
}
}
IPage<RoleUserResp> page = baseMapper.selectUserPage(new Page<>(pageQuery.getPage(), pageQuery
.getSize()), queryWrapper);
return PageResp.build(page);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean assignRolesToUser(List<Long> roleIds, Long userId) {
@@ -67,28 +100,16 @@ public class UserRoleServiceImpl implements UserRoleService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean assignRoleToUsers(Long roleId, List<Long> userIds) {
// 检查是否有变更
List<Long> oldUserIdList = baseMapper.lambdaQuery()
.select(UserRoleDO::getUserId)
.eq(UserRoleDO::getRoleId, roleId)
.list()
.stream()
.map(UserRoleDO::getUserId)
.toList();
if (CollUtil.isEmpty(CollUtil.disjunction(userIds, oldUserIdList))) {
return false;
}
CheckUtils.throwIf(SysConstants.SUPER_ROLE_ID.equals(roleId) && !userIds
.contains(SysConstants.SUPER_USER_ID), "不允许变更超管用户角色");
// 删除原有关联
baseMapper.lambdaUpdate().eq(UserRoleDO::getRoleId, roleId).remove();
// 保存最新关联
List<UserRoleDO> userRoleList = userIds.stream().map(userId -> new UserRoleDO(userId, roleId)).toList();
return baseMapper.insertBatch(userRoleList);
}
@Override
public void deleteByIds(List<Long> ids) {
baseMapper.deleteByIds(ids);
}
@Override
public void deleteByUserIds(List<Long> userIds) {
if (CollUtil.isEmpty(userIds)) {

View File

@@ -488,6 +488,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
List<Date> createTimeList = query.getCreateTime();
Long deptId = query.getDeptId();
List<Long> userIdList = query.getUserIds();
List<Long> excludeUserIdList = query.getExcludeUserIds();
return new QueryWrapper<UserDO>().and(StrUtil.isNotBlank(description), q -> q.like("t1.username", description)
.or()
.like("t1.nickname", description)
@@ -504,7 +505,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
deptIdList.add(deptId);
q.in("t1.dept_id", deptIdList);
})
.in(CollUtil.isNotEmpty(userIdList), "t1.id", userIdList);
.in(CollUtil.isNotEmpty(userIdList), "t1.id", userIdList)
.notIn(CollUtil.isNotEmpty(excludeUserIdList), "t1.id", excludeUserIdList);
}
/**

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="top.continew.admin.system.mapper.UserRoleMapper">
<select id="selectUserPage" resultType="top.continew.admin.system.model.resp.role.RoleUserResp">
SELECT
t1.*,
t2.nickname,
t2.username,
t2.status,
t2.gender,
t2.dept_id,
t2.description,
t2.is_system,
t3.name AS deptName
FROM sys_user_role AS t1
LEFT JOIN sys_user AS t2 ON t2.id = t1.user_id
LEFT JOIN sys_dept AS t3 ON t3.id = t2.dept_id
${ew.customSqlSegment}
</select>
</mapper>