You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exam/sys/depart/service/impl/SysDepartServiceImpl.java

369 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.yf.exam.modules.sys.depart.service.impl;
// 导入 MyBatis-Plus 的查询条件构造器,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于处理分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 的分页类,用于创建分页对象
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入 MyBatis-Plus 的服务实现基类,提供通用的服务层方法
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入自定义的分页请求数据传输对象类,用于封装分页查询请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入自定义的 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入系统部门数据传输对象类,用于在不同层之间传输部门信息
import com.yf.exam.modules.sys.depart.dto.SysDepartDTO;
// 导入系统部门树状数据传输对象类,用于返回部门的树状结构信息
import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO;
// 导入系统部门实体类,对应数据库中的部门表
import com.yf.exam.modules.sys.depart.entity.SysDepart;
// 导入系统部门映射器接口,用于与数据库进行交互
import com.yf.exam.modules.sys.depart.mapper.SysDepartMapper;
// 导入系统部门服务接口,定义部门相关的业务方法
import com.yf.exam.modules.sys.depart.service.SysDepartService;
// 导入 Apache Commons Lang3 工具类,用于字符串操作
import org.apache.commons.lang3.StringUtils;
// 导入 Spring 工具类,用于集合操作
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 部门信息业务实现类,实现了系统部门服务接口,处理部门相关的业务逻辑
* </p>
*
* @author 聪明笨狗
* @since 2020-09-02 17:25
*/
// 将该类标记为 Spring 服务组件,使其可以被 Spring 容器管理
@Service
public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart> implements SysDepartService {
/**
* 0标识为顶级分类作为根部门的标记
*/
private static final String ROOT_TAG = "0";
/**
* 保存或更新部门信息
* 如果传入的部门 ID 为空,则填充部门编码;否则,清空排序和部门编码信息
* @param reqDTO 部门信息数据传输对象,包含要保存或更新的部门信息
*/
@Override
public void save(SysDepartDTO reqDTO) {
// 判断传入的部门 ID 是否为空
if(StringUtils.isBlank(reqDTO.getId())) {
// 若为空,则填充部门编码
this.fillCode(reqDTO);
} else {
// 若不为空,则清空排序和部门编码信息
reqDTO.setSort(null);
reqDTO.setDeptCode(null);
}
// 创建一个新的部门实体对象
SysDepart entity = new SysDepart();
// 使用 BeanMapper 工具类将部门数据传输对象的属性复制到部门实体对象中
BeanMapper.copy(reqDTO, entity);
// 调用 MyBatis-Plus 的保存或更新方法,保存或更新部门信息
this.saveOrUpdate(entity);
}
/**
* 分页查询部门信息,并将结果转换为树状结构
* @param reqDTO 分页请求数据传输对象,包含分页查询的条件和参数
* @return 分页的部门树状结构数据传输对象列表
*/
@Override
public IPage<SysDepartTreeDTO> paging(PagingReqDTO<SysDepartDTO> reqDTO) {
// 创建分页对象,传入当前页码和每页记录数
Page query = new Page(reqDTO.getCurrent(), reqDTO.getSize());
// 获取请求参数中的部门信息
SysDepartDTO params = reqDTO.getParams();
// 调用映射器的分页查询方法,将结果转换为部门树状结构数据传输对象列表
IPage<SysDepartTreeDTO> pageData = baseMapper.paging(query, params);
return pageData;
}
/**
* 查找部门树状结构,调用重载方法,不传入部门 ID 列表
* @return 部门树状结构数据传输对象列表
*/
@Override
public List<SysDepartTreeDTO> findTree() {
return this.findTree(null);
}
/**
* 根据指定的部门 ID 列表查找部门树状结构
* @param ids 部门 ID 列表,若为空则查找所有部门的树状结构
* @return 部门树状结构数据传输对象列表
*/
@Override
public List<SysDepartTreeDTO> findTree(List<String> ids) {
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper();
// 按部门排序字段升序排序
wrapper.lambda().orderByAsc(SysDepart::getSort);
// 判断传入的部门 ID 列表是否不为空
if(!CollectionUtils.isEmpty(ids)) {
// 存储所有相关部门 ID 的列表
List<String> fullIds = new ArrayList<>();
// 遍历传入的部门 ID 列表
for(String id: ids) {
// 递归获取该部门及其所有父部门的 ID 并添加到 fullIds 列表中
this.cycleAllParent(fullIds, id);
}
// 判断 fullIds 列表是否不为空
if(!CollectionUtils.isEmpty(fullIds)) {
// 将查询条件限制为 fullIds 列表中的部门 ID
wrapper.lambda().in(SysDepart::getId, fullIds);
}
}
// 根据查询条件获取所有部门列表
List<SysDepart> list = this.list(wrapper);
// 使用 BeanMapper 工具类将部门实体列表转换为部门树状结构数据传输对象列表
List<SysDepartTreeDTO> dtoList = BeanMapper.mapList(list, SysDepartTreeDTO.class);
// 存储每个父部门 ID 对应的子部门列表的映射
Map<String,List<SysDepartTreeDTO>> map = new HashMap<>(16);
// 遍历部门树状结构数据传输对象列表
for(SysDepartTreeDTO item: dtoList) {
// 判断映射中是否已存在该父部门 ID
if(map.containsKey(item.getParentId())) {
// 若存在,则将该部门添加到对应的子部门列表中
map.get(item.getParentId()).add(item);
continue;
}
// 若不存在,则创建一个新的子部门列表,并将该部门添加到列表中
List<SysDepartTreeDTO> a = new ArrayList<>();
a.add(item);
// 将该父部门 ID 和对应的子部门列表添加到映射中
map.put(item.getParentId(), a);
}
// 获取根部门(父部门 ID 为 0的子部门列表
List<SysDepartTreeDTO> topList = map.get(ROOT_TAG);
// 判断根部门的子部门列表是否不为空
if(!CollectionUtils.isEmpty(topList)) {
// 遍历根部门的子部门列表
for(SysDepartTreeDTO item: topList) {
// 递归填充每个部门的子部门信息
this.fillChildren(map, item);
}
}
return topList;
}
/**
* 对部门进行排序操作,可实现部门的上升或下降排序
* @param id 要排序的部门 ID
* @param sort 排序方式0 表示上升1 表示下降
*/
@Override
public void sort(String id, Integer sort) {
// 根据部门 ID 获取要排序的部门实体对象
SysDepart depart = this.getById(id);
// 用于交换排序的部门实体对象
SysDepart exchange = null;
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 限制查询条件为与要排序的部门同级(父部门相同)
wrapper.lambda()
.eq(SysDepart::getParentId, depart.getParentId());
// 限制查询结果只返回一条记录
wrapper.last("LIMIT 1");
// 判断排序方式是否为上升
if(sort == 0) {
// 限制查询条件为排序值小于要排序部门的排序值,并按排序值降序排序
wrapper.lambda()
.lt(SysDepart::getSort, depart.getSort())
.orderByDesc(SysDepart::getSort);
// 获取满足条件的部门实体对象
exchange = this.getOne(wrapper, false);
}
// 判断排序方式是否为下降
if(sort == 1) {
// 限制查询条件为排序值大于要排序部门的排序值,并按排序值升序排序
wrapper.lambda()
.gt(SysDepart::getSort, depart.getSort())
.orderByAsc(SysDepart::getSort);
// 获取满足条件的部门实体对象
exchange = this.getOne(wrapper, false);
}
// 判断是否找到可交换排序的部门实体对象
if(exchange != null) {
// 创建一个新的部门实体对象,用于更新要排序的部门的排序值
SysDepart a = new SysDepart();
a.setId(id);
a.setSort(exchange.getSort());
// 创建一个新的部门实体对象,用于更新可交换排序的部门的排序值
SysDepart b = new SysDepart();
b.setId(exchange.getId());
b.setSort(depart.getSort());
// 调用 MyBatis-Plus 的更新方法,更新两个部门的排序值
this.updateById(a);
this.updateById(b);
}
}
/**
* 填充部门编码,根据父部门信息和同级部门排序生成新的部门编码
* @param reqDTO 部门信息数据传输对象,用于填充部门编码
*/
private void fillCode(SysDepartDTO reqDTO) {
// 部门编码的前缀
String code = "";
// 判断传入的父部门 ID 是否不为空且不为根部门 ID
if(StringUtils.isNotBlank(reqDTO.getParentId())
&& !ROOT_TAG.equals(reqDTO.getParentId())) {
// 根据父部门 ID 获取父部门实体对象
SysDepart parent = this.getById(reqDTO.getParentId());
// 将父部门的编码作为前缀
code = parent.getDeptCode();
}
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 限制查询条件为与要填充编码的部门同级(父部门相同),并按排序值降序排序
wrapper.lambda()
.eq(SysDepart::getParentId, reqDTO.getParentId())
.orderByDesc(SysDepart::getSort);
// 限制查询结果只返回一条记录
wrapper.last("LIMIT 1");
// 获取满足条件的部门实体对象
SysDepart depart = this.getOne(wrapper, false);
// 判断是否找到同级部门实体对象
if(depart != null) {
// 生成新的部门编码,将同级部门的排序值加 1 并格式化后添加到前缀后面
code += this.formatCode(depart.getSort() + 1);
// 设置要填充编码的部门的排序值为同级部门的排序值加 1
reqDTO.setSort(depart.getSort() + 1);
} else {
// 若未找到同级部门实体对象,则生成新的部门编码,排序值为 1 并格式化后添加到前缀后面
code += this.formatCode(1);
// 设置要填充编码的部门的排序值为 1
reqDTO.setSort(1);
}
// 设置要填充编码的部门的部门编码
reqDTO.setDeptCode(code);
}
/**
* 格式化排序值,根据排序值生成对应的部门编码前缀
* @param sort 排序值
* @return 格式化后的部门编码前缀
*/
private String formatCode(Integer sort) {
// 判断排序值是否小于 10
if(sort < 10) {
// 若小于 10则在排序值前添加 "A0"
return "A0" + sort;
}
// 若不小于 10则在排序值前添加 "A"
return "A" + sort;
}
/**
* 递归填充部门的子部门信息
* @param map 存储每个父部门 ID 对应的子部门列表的映射
* @param item 当前要填充子部门信息的部门树状结构数据传输对象
*/
private void fillChildren(Map<String,List<SysDepartTreeDTO>> map, SysDepartTreeDTO item) {
// 判断映射中是否存在该部门 ID 对应的子部门列表
if(map.containsKey(item.getId())) {
// 获取该部门 ID 对应的子部门列表
List<SysDepartTreeDTO> children = map.get(item.getId());
// 判断子部门列表是否不为空
if(!CollectionUtils.isEmpty(children)) {
// 遍历子部门列表
for(SysDepartTreeDTO sub: children) {
// 递归填充每个子部门的子部门信息
this.fillChildren(map, sub);
}
}
// 设置该部门的子部门列表
item.setChildren(children);
}
}
/**
* 获取指定部门及其所有子部门的 ID 列表
* @param id 要获取子部门 ID 列表的部门 ID
* @return 包含指定部门及其所有子部门 ID 的列表
*/
@Override
public List<String> listAllSubIds( String id) {
// 存储指定部门及其所有子部门 ID 的列表
List<String> ids = new ArrayList<>();
// 递归获取指定部门及其所有子部门的 ID 并添加到列表中
this.cycleAllSubs(ids, id);
return ids;
}
/**
* 递归获取指定部门及其所有子部门的 ID 并添加到列表中
* @param list 存储部门 ID 的列表
* @param id 当前要处理的部门 ID
*/
private void cycleAllSubs(List<String> list, String id) {
// 将当前部门 ID 添加到列表中
list.add(id);
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 限制查询条件为父部门 ID 等于当前部门 ID并按排序值降序排序
wrapper.lambda()
.eq(SysDepart::getParentId, id)
.orderByDesc(SysDepart::getSort);
// 获取满足条件的子部门列表
List<SysDepart> subList = this.list(wrapper);
// 判断子部门列表是否不为空
if(!CollectionUtils.isEmpty(subList)) {
// 遍历子部门列表
for(SysDepart item: subList) {
// 递归处理每个子部门
this.cycleAllSubs(list, item.getId());
}
}
}
/**
* 递归获取指定部门及其所有父部门的 ID 并添加到列表中
* @param list 存储部门 ID 的列表
* @param id 当前要处理的部门 ID
*/
private void cycleAllParent(List<String> list, String id) {
// 将当前部门 ID 添加到列表中
list.add(id);
// 根据部门 ID 获取部门实体对象
SysDepart depart = this.getById(id);
// 判断该部门的父部门 ID 是否不为空且不为根部门 ID
if(StringUtils.isNotBlank(depart.getParentId())
&& !ROOT_TAG.equals(depart.getParentId())) {
// 递归处理该部门的父部门
this.cycleAllParent(list, depart.getParentId());
}
}
}