diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java index a07381ea4..aed5d4b00 100644 --- a/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java @@ -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 diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java index af43bea96..12dcb78ff 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java @@ -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 deptNames); + + /** + * 部门用户树 + * + * @param query 部门查询条件 + * @param sortQuery 排序条件 + * @param isSimple 是否只返回简单部门树 + * @return 部门数量 + */ + List> treeWithUsers(DeptQuery query, SortQuery sortQuery, boolean isSimple); } diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java index 5a17e1a66..5e1338658 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java @@ -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 roleIds, Long userId); + /** + * 关联用户 + * + * @param roleId 角色id + * @param userIds 用户id列表 + * @return 是否新增成功(true:成功;false:无变更/失败) + */ + boolean bindUserIds(Long roleId,List userIds); /** * 根据用户 ID 删除 * @@ -51,6 +60,8 @@ public interface UserRoleService { */ void saveBatch(List list); + + /** * 根据用户 ID 查询 * @@ -74,4 +85,5 @@ public interface UserRoleService { * @return 是否已关联(true:已关联;false:未关联) */ boolean isRoleIdExists(List roleIds); + } \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java index 2e3b610cc..ba9deaf7a 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java @@ -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.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 int countByNames(List deptNames) { return (int)this.count(Wrappers.lambdaQuery().in(DeptDO::getName, deptNames)); } + @Override + public List> treeWithUsers(DeptQuery query, SortQuery sortQuery, boolean isSimple) { + List 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> 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 userList = userMap.get(departmentId); + List> userTrees = userList.stream() + .map(user -> { + Tree 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 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 @@ protected void beforeUpdate(DeptReq req, Long id) { if (ObjectUtil.notEqual(newStatus, oldDept.getStatus())) { List 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 @@ protected void beforeUpdate(DeptReq req, Long id) { @Override protected void beforeDelete(List ids) { List list = baseMapper.lambdaQuery() - .select(DeptDO::getName, DeptDO::getIsSystem) - .in(DeptDO::getId, ids) - .list(); + .select(DeptDO::getName, DeptDO::getIsSystem) + .in(DeptDO::getId, ids) + .list(); Optional 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 @@ protected void beforeDelete(List 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(); } /** @@ -189,8 +264,8 @@ private Long countChildren(List 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(); } /** diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java index d13009c4a..36702592e 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java @@ -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; @@ -198,6 +199,8 @@ public int countByNames(List roleNames) { return (int)this.count(Wrappers.lambdaQuery().in(RoleDO::getName, roleNames)); } + + /** * 名称是否存在 * diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java index fb5b48f85..4f315a3c4 100644 --- a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java @@ -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; @@ -61,7 +63,28 @@ public boolean add(List roleIds, Long userId) { List userRoleList = roleIds.stream().map(roleId -> new UserRoleDO(userId, roleId)).toList(); return baseMapper.insertBatch(userRoleList); } - + @Override + public boolean bindUserIds(Long roleId, List userIds) { + // 检查是否有变更 + List 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 userRoleList = userIds.stream().map(userId -> new UserRoleDO(userId, roleId)).toList(); + return baseMapper.insertBatch(userRoleList); + } @Override public void deleteByUserIds(List userIds) { baseMapper.lambdaUpdate().in(UserRoleDO::getUserId, userIds).remove(); @@ -72,6 +95,8 @@ public void saveBatch(List list) { baseMapper.insert(list); } + + @Override @ContainerMethod(namespace = ContainerConstants.USER_ROLE_ID_LIST, type = MappingType.ORDER_OF_KEYS) public List listRoleIdByUserId(Long userId) { diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java index c4398fbf2..d122ac055 100644 --- a/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java @@ -80,6 +80,12 @@ public List> listDeptTree(DeptQuery query, SortQuery sortQuery) { return deptService.tree(query, sortQuery, true); } + @Operation(summary = "查询部门用户树", description = "查询树结构的部门列表") + @GetMapping("/tree/deptWithUsers") + public List> listDeptWithUsersTree(DeptQuery query, SortQuery sortQuery) { + return deptService.treeWithUsers(query, sortQuery, true); + } + @Operation(summary = "查询菜单树", description = "查询树结构的菜单列表") @GetMapping("/tree/menu") public List> listMenuTree(MenuQuery query, SortQuery sortQuery) { diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java index 8110d514f..1f0ea528b 100644 --- a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java @@ -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 @@ */ @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 { + + private final UserRoleService userRoleService; + + @Operation(summary = "查询角色关联用户", description = "查询角色组绑定的关联用户") + @GetMapping("/listRoleUsers/{id}") + public List 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 userIds) { + userRoleService.bindUserIds(roleId, userIds); + } }