1、利用validator进行参数校验
public class DeptParam {
private Integer id;
@NotBlank(message = "部门名称不可以为空")
@Length(max = 15, min = 2, message = "部门名称长度需要在2-15个字之间")
private String name;
//指定parentId初始值,避免空值异常
private Integer parentId = 0;
@NotNull(message = "展示顺序不可以为空")
private Integer seq;
@Length(max = 150, message = "备注的长度需要在150个字以内")
private String remark;
}
2、因为部门下面可能会有子部门,需要构成部门树。解决方法:适配一个类来继承原来的类,增加属性 。
通过一个List集合来包含部门。使用到了org.springframework.beans.BeanUtils中的copyProperties()
可以对两个字段相同的对象进行复制
public class DeptLevelDto extends SysDept {
//继承:一个部门,又有一个部门属性,构成部门包含部门,即构成树形结构
private List deptList = Lists.newArrayList();
public static DeptLevelDto adapt(SysDept dept) {
DeptLevelDto dto = new DeptLevelDto();
//字段相同的对象进行复制
BeanUtils.copyProperties(dept, dto);
return dto;
}
}
3、在dao层中使用逆向工程生成的代码,然后我们通过情况进行修改和增删 ,简化开发。
例如:给参数id加上 @Param("id") Integer id : 保证了参数不一致问题
4、services层: 对部门的增删改查
在设计数据库的时候有个level值,我们可以通过这个level来构建部门的树形结构,我们规定了level的计算方式:
根部门的level为 0 。如果一个部门存在父部门,那么它的level值为 父部门level值 . 父部门id
例如:父部门level 为 0 ,id 为 1 ,则它同一级的的部门level值为 0.1
package com.mmall.util;
import org.apache.commons.lang3.StringUtils;
public class LevelUtil {
public final static String SEPARATOR = ".";
public final static String ROOT = "0";
// 0
// 0.1
// 0.1.2
// 0.1.3
// 0.4
public static String calculateLevel(String parentLevel, int parentId) {
if (StringUtils.isBlank(parentLevel)) {
return ROOT;
} else {
return StringUtils.join(parentLevel, SEPARATOR, parentId);
}
}
}
增加部门
public void save(DeptParam param) {
//首先对参数进行校验
BeanValidator.check(param);
//如果插入的部门参数已经存在,即同一层级下存在相同名称的部门,抛出参数异常
if(checkExist(param.getParentId(), param.getName(), param.getId())) {
throw new ParamException("同一层级下存在相同名称的部门");
}
//Builder模式简化get set方法,构造一个SysDept对象
SysDept dept = SysDept.builder().id(param.getId()).name(param.getName()).parentId(param.getParentId())
.seq(param.getSeq()).remark(param.getRemark()).build();
//通过id查到该部门的级别,再通过自定义的level计算工具计算对应的level
dept.setLevel(LevelUtil.calculateLevel(getLevel(param.getParentId()), param.getParentId()));
//通过RequestHolder拿到当前用户的用户名
dept.setOperator(RequestHolder.getCurrentUser().getUsername());
//ip计算工具,可以不理
dept.setOperateIp(IpUtil.getRemoteIp(RequestHolder.getCurrentRequest()));
dept.setOperateTime(new Date());
sysDeptMapper.insertSelective(dept);
//日志记录
sysLogService.saveDeptLog(null, dept);
}
当我们保存了一个/多个部门后,这仅仅是保存了它的数据(具有能构成树的数据),接下来需要把这些数据用来构成部门树:
1、拿到所有部门列表适配为我们的dto结构
2、通过一个高级的数据结构Multimap:他可以构造像Map
恰好我们需要构造:level -> [dept1, dept2, ...],以level为key,部门列表为值的map结构。
最终形成:levelDeptMap:{{level0,{dept1,..}},{level1,{dept1,..}},{...}}
3、生成一个递归树:不断去levelDeptMap取元素,设置部门的父子结构
//把部门列表查出来,适配为我们的dto
public List deptTree() {
List deptList = sysDeptMapper.getAllDept();
List dtoList = Lists.newArrayList();
for (SysDept dept : deptList) {
DeptLevelDto dto = DeptLevelDto.adapt(dept);
dtoList.add(dto);
}
//把所有的部门列表转化为树形结构,把每个dto中的list属性列出来
return deptListToTree(dtoList);
}
private List deptListToTree(List deptLevelList) {
//如果列表是空的,直接返回一个空列表
if (CollectionUtils.isEmpty(deptLevelList)) {
return Lists.newArrayList();
}
//需要构造像Map>或者Map>这样比较复杂的集合类型的数据结构,Multimap在合适不过
// level -> [dept1, dept2, ...] 形如Map>
//这样就可以实现我们以level为key,部门列表为值的map结构
Multimap levelDeptMap = ArrayListMultimap.create();
//建立一个存放根的列表,即level值为0的列表
List rootList = Lists.newArrayList();
//接下来只需要我们把level和列表put进去就好
for (DeptLevelDto dto : deptLevelList) {
levelDeptMap.put(dto.getLevel(), dto);
//存放根列表
if (LevelUtil.ROOT.equals(dto.getLevel())) {
rootList.add(dto);
}
}
// 把根列表按照seq从小到大排序
Collections.sort(rootList, new Comparator() {
public int compare(DeptLevelDto o1, DeptLevelDto o2) {
return o1.getSeq() - o2.getSeq();
}
});
// 递归生成树
transformDeptTree(rootList, LevelUtil.ROOT, levelDeptMap);
return rootList;
}
// 首先传进来level:0, 0, all 0->0.1,0.2(根部门id为1的下属部门为了level为.0.1)
// level:0.1
// level:0.2
// levelDeptMap:{{level0,{dept1,..}},{level1,{dept1,..}},{...}}
// deptLevelList: rootList
private void transformDeptTree(List deptLevelList, String level, Multimap levelDeptMap) {
//如果根列表还有值,就一直取
for (int i = 0; i < deptLevelList.size(); i++) {
// 遍历该层的每个元素
DeptLevelDto deptLevelDto = deptLevelList.get(i);
// 处理当前层级的数据,通过根部门id计算出它下层部门
String nextLevel = LevelUtil.calculateLevel(level, deptLevelDto.getId());
// 处理下一层 {level1,{dept1,..}
List tempDeptList = (List) levelDeptMap.get(nextLevel);
//如果下一层不为空
if (CollectionUtils.isNotEmpty(tempDeptList)) {
// 排序
Collections.sort(tempDeptList, deptSeqComparator);
// 设置下一层部门 :把tempDeptList设置进dto的列表属性中
deptLevelDto.setDeptList(tempDeptList);
// 进入到下一层处理
transformDeptTree(tempDeptList, nextLevel, levelDeptMap);
}
}
}
public Comparator deptSeqComparator = new Comparator() {
public int compare(DeptLevelDto o1, DeptLevelDto o2) {
return o1.getSeq() - o2.getSeq();
}
};
更新部门
1、首先进行参数的检验:同一层级下不能存在相同名称的部门,通过传进来部门id查看部门存不存在,不存在抛异常
2、检验当前部门有没有子部门,有的话父部门的level值更新了,相应子部门的level值也要更新(注意:更新操作必须的事务性的,不能父部门更新成功了,子部门没有更新)
2.1:首先要把当前部门的所有子部门查出来,更新子部门level的前缀:记录新老父部门level , 通过字符串的查找截取拼接构成新level
public void update(DeptParam param) {
BeanValidator.check(param);
if(checkExist(param.getParentId(), param.getName(), param.getId())) {
throw new ParamException("同一层级下存在相同名称的部门");
}
SysDept before = sysDeptMapper.selectByPrimaryKey(param.getId());
Preconditions.checkNotNull(before, "待更新的部门不存在");
SysDept after = SysDept.builder().id(param.getId()).name(param.getName()).parentId(param.getParentId())
.seq(param.getSeq()).remark(param.getRemark()).build();
after.setLevel(LevelUtil.calculateLevel(getLevel(param.getParentId()), param.getParentId()));
after.setOperator(RequestHolder.getCurrentUser().getUsername());
after.setOperateIp(IpUtil.getRemoteIp(RequestHolder.getCurrentRequest()));
after.setOperateTime(new Date());
updateWithChild(before, after);
sysLogService.saveDeptLog(before, after);
}
@Transactional
void updateWithChild(SysDept before, SysDept after) {
//更新后的level
String newLevelPrefix = after.getLevel();
//更新前的level
String oldLevelPrefix = before.getLevel();
//如果两者不相等,说明子部门的level要更新
if (!newLevelPrefix.equals(oldLevelPrefix)) {
//先查出来之前的子部门
List deptList = sysDeptMapper.getChildDeptListByLevel(oldLevelPrefix);
if (CollectionUtils.isNotEmpty(deptList)) {
//子部门不为空,则遍历其列表
for (SysDept dept : deptList) {
String level = dept.getLevel();
//查找指定字符或字符串在字符串中第一次出现地方的索引,未找到的情况返回 -1
if (level.indexOf(oldLevelPrefix) == 0) {
//新level = 新前缀加时之前的后缀
level = newLevelPrefix + level.substring(oldLevelPrefix.length());
dept.setLevel(level);
}
}
sysDeptMapper.batchUpdateLevel(deptList);
}
}
sysDeptMapper.updateByPrimaryKey(after);
}
删除部门:注意判断三个点
1、待删除的部门不存在,无法删除
2、当前部门下面有子部门,无法删除
3、当前部门下面有用户,无法删除
public void delete(int deptId) {
//删除之前判断当前部门是否存在
SysDept dept = sysDeptMapper.selectByPrimaryKey(deptId);
Preconditions.checkNotNull(dept, "待删除的部门不存在,无法删除");
//查是否有子部门和用户
if (sysDeptMapper.countByParentId(dept.getId()) > 0) {
throw new ParamException("当前部门下面有子部门,无法删除");
}
if(sysUserMapper.countByDeptId(dept.getId()) > 0) {
throw new ParamException("当前部门下面有用户,无法删除");
}
//根据id删除
sysDeptMapper.deleteByPrimaryKey(deptId);
}
两个辅助函数
private boolean checkExist(Integer parentId, String deptName, Integer deptId) {
//计算一共有多少符合条件(name,parentId)的行,大于0说明该部门已经存在
return sysDeptMapper.countByNameAndParentId(parentId, deptName, deptId) > 0;
}
private String getLevel(Integer deptId) {
SysDept dept = sysDeptMapper.selectByPrimaryKey(deptId);
if (dept == null) {
return null;
}
return dept.getLevel();
}
Controller层比较简单:要遵从之前约定的url规范
package com.mmall.controller;
import com.mmall.common.JsonData;
import com.mmall.dto.DeptLevelDto;
import com.mmall.param.DeptParam;
import com.mmall.service.SysDeptService;
import com.mmall.service.SysTreeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/sys/dept")
public class SysDeptController {
private final static Logger log = LoggerFactory.getLogger(SysDeptController.class);
@Autowired
private SysDeptService sysDeptService;
@Autowired
private SysTreeService sysTreeService;
@RequestMapping("/save.json")
@ResponseBody
public JsonData saveDept(DeptParam param) {
sysDeptService.save(param);
return JsonData.success();
}
@RequestMapping("/tree.json")
@ResponseBody
public JsonData tree() {
List dtoList = sysTreeService.deptTree();
return JsonData.success(dtoList);
}
@RequestMapping("/update.json")
@ResponseBody
public JsonData updateDept(DeptParam param) {
sysDeptService.update(param);
return JsonData.success();
}
@RequestMapping("/dept.page")//回去views中查找dept.jsp
public ModelAndView page() {
return new ModelAndView("dept");
}
@RequestMapping("/delete.json")
@ResponseBody
public JsonData delete(@RequestParam("id") int id) {
sysDeptService.delete(id);
return JsonData.success();
}
}