Skip to content

Commit

Permalink
feat: 角色管理增加授权用户功能 (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
WhyGitC authored Oct 24, 2024
1 parent 2b47ed7 commit 73f880e
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ public class SysConstants {
* 管理员角色编码
*/
public static final String ADMIN_ROLE_CODE = "admin";
/**
* 超管角色组ID
*/
public static final Long SUPER_ROLE_ID = 1L;
/**
* 超管账号ID
*/
public static final Long SUPER_ADMIN_ID = 1L;

/**
* 顶级部门 ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

package top.continew.admin.system.service;

import cn.hutool.core.lang.tree.Tree;
import top.continew.admin.system.model.entity.DeptDO;
import top.continew.admin.system.model.query.DeptQuery;
import top.continew.admin.system.model.req.DeptReq;
import top.continew.admin.system.model.resp.DeptResp;
import top.continew.starter.data.mp.service.IService;
import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.service.BaseService;

import java.util.List;
Expand Down Expand Up @@ -56,4 +58,14 @@ public interface DeptService extends BaseService<DeptResp, DeptResp, DeptQuery,
* @return 部门数量
*/
int countByNames(List<String> deptNames);

/**
* 部门用户树
*
* @param query 部门查询条件
* @param sortQuery 排序条件
* @param isSimple 是否只返回简单部门树
* @return 部门数量
*/
List<Tree<String>> treeWithUsers(DeptQuery query, SortQuery sortQuery, boolean isSimple);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package top.continew.admin.system.service;

import top.continew.admin.system.model.entity.UserDO;
import top.continew.admin.system.model.entity.UserRoleDO;

import java.util.List;
Expand All @@ -37,6 +38,14 @@ public interface UserRoleService {
*/
boolean add(List<Long> roleIds, Long userId);

/**
* 关联用户
*
* @param roleId 角色id
* @param userIds 用户id列表
* @return 是否新增成功(true:成功;false:无变更/失败)
*/
boolean bindUserIds(Long roleId,List<Long> userIds);
/**
* 根据用户 ID 删除
*
Expand All @@ -51,6 +60,8 @@ public interface UserRoleService {
*/
void saveBatch(List<UserRoleDO> list);



/**
* 根据用户 ID 查询
*
Expand All @@ -74,4 +85,5 @@ public interface UserRoleService {
* @return 是否已关联(true:已关联;false:未关联)
*/
boolean isRoleIdExists(List<Long> roleIds);

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
package top.continew.admin.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
Expand All @@ -26,21 +31,26 @@
import top.continew.admin.system.mapper.DeptMapper;
import top.continew.admin.system.model.entity.DeptDO;
import top.continew.admin.system.model.query.DeptQuery;
import top.continew.admin.system.model.query.UserQuery;
import top.continew.admin.system.model.req.DeptReq;
import top.continew.admin.system.model.resp.DeptResp;
import top.continew.admin.system.model.resp.UserResp;
import top.continew.admin.system.service.DeptService;
import top.continew.admin.system.service.RoleDeptService;
import top.continew.admin.system.service.UserService;
import top.continew.starter.core.util.ReflectUtils;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.data.core.enums.DatabaseType;
import top.continew.starter.data.core.util.MetaUtils;
import top.continew.starter.extension.crud.annotation.TreeField;
import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
import top.continew.starter.extension.crud.util.TreeUtils;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
* 部门业务实现
Expand Down Expand Up @@ -80,6 +90,71 @@ public int countByNames(List<String> deptNames) {
return (int)this.count(Wrappers.<DeptDO>lambdaQuery().in(DeptDO::getName, deptNames));
}

@Override
public List<Tree<String>> treeWithUsers(DeptQuery query, SortQuery sortQuery, boolean isSimple) {
List<DeptResp> list = this.list(query, sortQuery);
if (CollUtil.isEmpty(list)) {
return new ArrayList<>(0);
} else {
TreeNodeConfig treeNodeConfig = TreeUtils.DEFAULT_CONFIG;
TreeField treeField = this.getListClass().getDeclaredAnnotation(TreeField.class);
if (!isSimple) {
treeNodeConfig = TreeUtils.genTreeNodeConfig(treeField);
}

// 创建一个部门ID到用户的映射
UserQuery userQuery = new UserQuery();
userQuery.setStatus(DisEnableStatusEnum.ENABLE);
Map<Long, List<UserResp>> userMap = userService.list(userQuery, null).stream()
.collect(Collectors.groupingBy(UserResp::getDeptId));

String rootId = "dept_0";

return TreeUtil.build(list, rootId, treeNodeConfig, (node, tree) -> {
Long departmentId = ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.value()), new Object[0]);
String uniqueDeptId = "dept_" + departmentId;
tree.setId(uniqueDeptId);
Long parentId = ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.parentIdKey()), new Object[0]);
tree.setParentId(parentId != null ? "dept_" + parentId : null);
tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()), new Object[0]));
tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()), new Object[0]));
tree.putExtra("origId", departmentId);
tree.putExtra("isUser", false);

// 添加用户信息到树节点
if (userMap.containsKey(departmentId)) {
List<UserResp> userList = userMap.get(departmentId);
List<Tree<String>> userTrees = userList.stream()
.map(user -> {
Tree<String> userTree = new Tree<>();
String uniqueUserId = "user_" + user.getId();
String userAliasName = user.getUsername() + "(" + user.getNickname() + ")";
userTree.setId(uniqueUserId);
userTree.setParentId(uniqueDeptId);
userTree.setName(userAliasName);
userTree.setWeight(0);
userTree.putExtra("origId", user.getId()); // 添加原始用户ID
userTree.putExtra("isUser", true); // 添加原始用户ID
return userTree;
})
.collect(Collectors.toList());
tree.setChildren(userTrees);
}

if (!isSimple) {
List<Field> fieldList = ReflectUtils.getNonStaticFields(this.getListClass());
fieldList.removeIf((f) -> {
return CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), new CharSequence[]{treeField.value(), treeField.parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey()});
});
fieldList.forEach((f) -> {
tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f.getName()), new Object[0]));
});
}
});
}
}


@Override
protected void beforeAdd(DeptReq req) {
String name = req.getName();
Expand All @@ -105,13 +180,13 @@ protected void beforeUpdate(DeptReq req, Long id) {
if (ObjectUtil.notEqual(newStatus, oldDept.getStatus())) {
List<DeptDO> children = this.listChildren(id);
long enabledChildrenCount = children.stream()
.filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus()))
.count();
.filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus()))
.count();
CheckUtils.throwIf(DisEnableStatusEnum.DISABLE
.equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName);
.equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName);
DeptDO oldParentDept = this.getByParentId(oldParentId);
CheckUtils.throwIf(DisEnableStatusEnum.ENABLE.equals(newStatus) && DisEnableStatusEnum.DISABLE
.equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName);
.equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName);
}
// 变更上级部门
if (ObjectUtil.notEqual(req.getParentId(), oldParentId)) {
Expand All @@ -126,12 +201,12 @@ protected void beforeUpdate(DeptReq req, Long id) {
@Override
protected void beforeDelete(List<Long> ids) {
List<DeptDO> list = baseMapper.lambdaQuery()
.select(DeptDO::getName, DeptDO::getIsSystem)
.in(DeptDO::getId, ids)
.list();
.select(DeptDO::getName, DeptDO::getIsSystem)
.in(DeptDO::getId, ids)
.list();
Optional<DeptDO> isSystemData = list.stream().filter(DeptDO::getIsSystem).findFirst();
CheckUtils.throwIf(isSystemData::isPresent, "所选部门 [{}] 是系统内置部门,不允许删除", isSystemData.orElseGet(DeptDO::new)
.getName());
.getName());
CheckUtils.throwIf(this.countChildren(ids) > 0, "所选部门存在下级部门,不允许删除");
CheckUtils.throwIf(userService.countByDeptIds(ids) > 0, "所选部门存在用户关联,请解除关联后重试");
// 删除角色和部门关联
Expand All @@ -148,10 +223,10 @@ protected void beforeDelete(List<Long> ids) {
*/
private boolean isNameExists(String name, Long parentId, Long id) {
return baseMapper.lambdaQuery()
.eq(DeptDO::getName, name)
.eq(DeptDO::getParentId, parentId)
.ne(null != id, DeptDO::getId, id)
.exists();
.eq(DeptDO::getName, name)
.eq(DeptDO::getParentId, parentId)
.ne(null != id, DeptDO::getId, id)
.exists();
}

/**
Expand Down Expand Up @@ -189,8 +264,8 @@ private Long countChildren(List<Long> ids) {
}
DatabaseType databaseType = MetaUtils.getDatabaseTypeOrDefault(dataSource, DatabaseType.MYSQL);
return ids.stream()
.mapToLong(id -> baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count())
.sum();
.mapToLong(id -> baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count())
.sum();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import top.continew.admin.common.enums.DataScopeEnum;
import top.continew.admin.system.mapper.RoleMapper;
import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.entity.UserDO;
import top.continew.admin.system.model.query.RoleQuery;
import top.continew.admin.system.model.req.RoleReq;
import top.continew.admin.system.model.resp.MenuResp;
Expand Down Expand Up @@ -198,6 +199,8 @@ public int countByNames(List<String> roleNames) {
return (int)this.count(Wrappers.<RoleDO>lambdaQuery().in(RoleDO::getName, roleNames));
}



/**
* 名称是否存在
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
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.service.UserRoleService;
import top.continew.starter.core.util.validate.CheckUtils;

import java.util.List;

Expand Down Expand Up @@ -61,7 +63,28 @@ public boolean add(List<Long> roleIds, Long userId) {
List<UserRoleDO> userRoleList = roleIds.stream().map(roleId -> new UserRoleDO(userId, roleId)).toList();
return baseMapper.insertBatch(userRoleList);
}

@Override
public boolean bindUserIds(Long roleId, List<Long> userIds) {
// 检查是否有变更
List<Long> oldRoleIdList = baseMapper.lambdaQuery()
.select(UserRoleDO::getUserId)
.eq(UserRoleDO::getRoleId, roleId)
.list()
.stream()
.map(UserRoleDO::getRoleId)
.toList();
if (CollUtil.isEmpty(CollUtil.disjunction(userIds, oldRoleIdList))) {
return false;
}
if (SysConstants.SUPER_ROLE_ID.equals(roleId) && !userIds.contains(SysConstants.SUPER_ADMIN_ID)){
CheckUtils.throwIf(true,"不能移除管理员默认超管角色组");
}
// 删除原有关联
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 deleteByUserIds(List<Long> userIds) {
baseMapper.lambdaUpdate().in(UserRoleDO::getUserId, userIds).remove();
Expand All @@ -72,6 +95,8 @@ public void saveBatch(List<UserRoleDO> list) {
baseMapper.insert(list);
}



@Override
@ContainerMethod(namespace = ContainerConstants.USER_ROLE_ID_LIST, type = MappingType.ORDER_OF_KEYS)
public List<Long> listRoleIdByUserId(Long userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ public List<Tree<Long>> listDeptTree(DeptQuery query, SortQuery sortQuery) {
return deptService.tree(query, sortQuery, true);
}

@Operation(summary = "查询部门用户树", description = "查询树结构的部门列表")
@GetMapping("/tree/deptWithUsers")
public List<Tree<String>> listDeptWithUsersTree(DeptQuery query, SortQuery sortQuery) {
return deptService.treeWithUsers(query, sortQuery, true);
}

@Operation(summary = "查询菜单树", description = "查询树结构的菜单列表")
@GetMapping("/tree/menu")
public List<Tree<Long>> listMenuTree(MenuQuery query, SortQuery sortQuery) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,27 @@

package top.continew.admin.controller.system;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import top.continew.admin.system.model.query.DeptQuery;
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.service.RoleService;
import top.continew.admin.system.service.UserRoleService;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import top.continew.starter.extension.crud.controller.BaseController;
import top.continew.starter.extension.crud.enums.Api;
import top.continew.starter.extension.crud.model.query.SortQuery;

import java.util.List;

/**
* 角色管理 API
Expand All @@ -37,6 +46,22 @@
*/
@Tag(name = "角色管理 API")
@RestController
@RequiredArgsConstructor
@CrudRequestMapping(value = "/system/role", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE})
public class RoleController extends BaseController<RoleService, RoleResp, RoleDetailResp, RoleQuery, RoleReq> {

private final UserRoleService userRoleService;

@Operation(summary = "查询角色关联用户", description = "查询角色组绑定的关联用户")
@GetMapping("/listRoleUsers/{id}")
public List<Long> listUsers(@PathVariable("id") Long roleId) {
return userRoleService.listUserIdByRoleId(roleId);
}

@Operation(summary = "关联用户", description = "批量关联用户")
@SaCheckPermission("system:role:bindUsers")
@PostMapping("/bindUsers/{id}")
public void bindUsers(@PathVariable("id") Long roleId,@RequestBody List<Long> userIds) {
userRoleService.bindUserIds(roleId, userIds);
}
}

0 comments on commit 73f880e

Please sign in to comment.