mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-10-26 06:57:08 +08:00
feat: 角色管理增加授权用户功能 (#93)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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 删除
|
||||
*
|
||||
@@ -51,6 +60,8 @@ public interface UserRoleService {
|
||||
*/
|
||||
void saveBatch(List<UserRoleDO> list);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户 ID 查询
|
||||
*
|
||||
@@ -74,4 +85,5 @@ public interface UserRoleService {
|
||||
* @return 是否已关联(true:已关联;false:未关联)
|
||||
*/
|
||||
boolean isRoleIdExists(List<Long> roleIds);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -26,21 +31,26 @@ import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 部门业务实现
|
||||
@@ -80,6 +90,71 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
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();
|
||||
@@ -105,13 +180,13 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
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)) {
|
||||
@@ -126,12 +201,12 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
@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, "所选部门存在用户关联,请解除关联后重试");
|
||||
// 删除角色和部门关联
|
||||
@@ -148,10 +223,10 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,8 +264,8 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,7 @@ import top.continew.admin.common.context.UserContextHolder;
|
||||
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;
|
||||
@@ -198,6 +199,8 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
|
||||
return (int)this.count(Wrappers.<RoleDO>lambdaQuery().in(RoleDO::getName, roleNames));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 名称是否存在
|
||||
*
|
||||
|
||||
@@ -23,9 +23,11 @@ import lombok.RequiredArgsConstructor;
|
||||
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;
|
||||
|
||||
@@ -61,7 +63,28 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
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();
|
||||
@@ -72,6 +95,8 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
baseMapper.insert(list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@ContainerMethod(namespace = ContainerConstants.USER_ROLE_ID_LIST, type = MappingType.ORDER_OF_KEYS)
|
||||
public List<Long> listRoleIdByUserId(Long userId) {
|
||||
|
||||
@@ -80,6 +80,12 @@ public class CommonController {
|
||||
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) {
|
||||
|
||||
@@ -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
|
||||
@@ -37,6 +46,22 @@ import top.continew.starter.extension.crud.enums.Api;
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user