部门管理
部门管理
业务用途
部门管理维护企业的组织架构树。用户通过 deptId 归属部门,部门数据是(本部门、本部门及以下)的判定依据。
平台部门表不使用 ancestors 祖先链字段(与若依 RuoYi 不同),而是仅靠 parent_id 维护父子关系,树形结构在服务层内存中递归构建。删除部门前会校验是否存在子部门或部门下是否有用户。

涉及文件
| 层 | 文件路径 |
|---|---|
| 控制器 | backend/src/main/java/com/lowcode/controller/system/SysDeptController.java |
| 服务实现 | backend/src/main/java/com/lowcode/service/impl/SysDeptServiceImpl.java |
| 实体 | backend/src/main/java/com/lowcode/entity/SysDept.java |
| 前端 API | frontend/src/api/(部门接口内联于各业务页面调用 /system/dept/*) |
| 前端页面 | frontend/src/views/system/dept/index.vue |
数据库表
sys_dept(部门表)
| 字段 | 类型 | 说明 |
|---|---|---|
parent_id | Long | 父部门 ID(根部门为 0) |
dept_name | String | 部门名称 |
sort | Integer | 显示顺序 |
leader_user_id | Long | 负责人用户 ID |
leader_user_name | String | 负责人姓名(@TableField(exist = false),不入库,查询时回填) |
phone | String | 联系电话 |
email | String | 邮箱 |
status | String | 状态(0正常 1禁用) |
没有 ancestors 字段
部门表只用 parent_id 表达层级,没有 ancestors(如 0,100,101)这样的祖先链。这意味着查询「某部门及所有子孙」需要递归或在应用层遍历,而非一条 SQL FIND_IN_SET。如对性能有要求,可考虑自行加 ancestors 字段优化。
后端实现
端点一览
SysDeptController 基础路径:/api/system/dept(注意此控制器采用 /create /update /delete /get 风格,非 RESTful)
| HTTP 方法 | 路径 | 方法名 | 说明 |
|---|---|---|---|
| GET | /api/system/dept/list | list | 获取部门列表(支持 deptName/status 过滤) |
| GET | /api/system/dept/tree | tree | 获取部门树形结构 |
| GET | /api/system/dept/get?id= | get | 获取部门详情 |
| POST | /api/system/dept/create | create | 创建部门 |
| PUT | /api/system/dept/update | update | 更新部门 |
| DELETE | /api/system/dept/delete?id= | delete | 删除部门 |
list 与 tree 端点带 @PreAuthorize("hasAuthority('system:dept:list')") 权限校验。
关键代码
树形构建在内存中递归完成,按 sort 升序排列:
@Override
public List<SysDept> getDeptTree() {
List<SysDept> allDepts = getDeptList(null, null);
return buildDeptTree(allDepts, 0L);
}
private List<SysDept> buildDeptTree(List<SysDept> depts, Long parentId) {
List<SysDept> tree = new ArrayList<>();
for (SysDept dept : depts) {
if (parentId.equals(dept.getParentId())) {
List<SysDept> children = buildDeptTree(depts, dept.getId());
tree.add(dept);
}
}
return tree;
}查询列表时会回填负责人姓名(leaderUserName)--先收集所有 leaderUserId,批量查 sys_user,再映射回填,避免 N+1 查询:
private void fillLeaderNames(List<SysDept> depts) {
List<Long> userIds = depts.stream()
.map(SysDept::getLeaderUserId)
.filter(id -> id != null).distinct().collect(Collectors.toList());
if (userIds.isEmpty()) return;
List<SysUser> users = userMapper.selectList(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getTenantId, currentTenantId()).in(SysUser::getId, userIds));
Map<Long, String> userNameMap = new HashMap<>();
for (SysUser user : users) {
userNameMap.put(user.getId(),
StringUtils.hasText(user.getNickname()) ? user.getNickname() : user.getUsername());
}
for (SysDept dept : depts) {
if (dept.getLeaderUserId() != null) {
dept.setLeaderUserName(userNameMap.get(dept.getLeaderUserId()));
}
}
}删除部门有双重前置校验--有子部门或有用户都不允许删除,直接抛异常:
@Override
public void deleteDept(Long id) {
if (hasChildDept(id)) {
throw new RuntimeException("存在子部门,不允许删除");
}
if (hasUser(id)) {
throw new RuntimeException("部门下存在用户,不允许删除");
}
remove(new LambdaQueryWrapper<SysDept>()
.eq(SysDept::getTenantId, currentTenantId()).eq(SysDept::getId, id));
}前端实现
- 页面
frontend/src/views/system/dept/index.vue:el-tree展示部门树,弹窗表单做新增/编辑,选择父部门、填写名称、排序、负责人、联系方式。

- 部门接口在多个业务页面(如用户管理的部门筛选下拉)直接调用
/system/dept/tree或/system/dept/list。
操作步骤
- 进入「系统管理 -> 部门管理」。
- 新增根部门:父部门选「根」,填名称、排序、负责人、状态。
- 在某部门下新增子部门:父部门选该部门,依次建立多级组织。

- 删除部门前,先确保该部门无子部门、无关联用户。
常见问题
删除部门报「存在子部门,不允许删除」?
部门树不允许带子删除。请先删除或迁移所有子部门后再删父部门。
删除部门报「部门下存在用户,不允许删除」?
该部门下仍有用户(sys_user.dept_id 指向它)。请先把用户迁移到其他部门,或删除/禁用这些用户。
负责人姓名是怎么显示的?
leader_user_name 是非持久化字段,后端在返回前通过 fillLeaderNames 批量查 sys_user 回填(优先取 nickname,无则取 username)。前端无需额外请求。
