栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot多级分类源码

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot多级分类源码

 如何实现多级分类,方便维护,容易获取子分类、父级分类、某个子分类的顶级分类的所有子分类;

一、数据建表 

 

DROp TABLE IF EXISTS `oa_classification`;
CREATE TABLE `oa_classification`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类id',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '分类名称',
  `parent_id` bigint(20) NULL DEFAULT 0 COMMENT '父分类id',
  `sort` int(11) NOT NULL DEFAULT 0 COMMENT '显示顺序',
  `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '分类状态(0正常 1停用)',
  `deth` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '纵深',
  `deth_name` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '纵深名称',
  `deth_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '纵深ID',
  `create_by` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '项目分类表' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
 二、maven主要依赖


	mysql
	mysql-connector-java




	com.baomidou
	mybatis-plus-boot-starter
	3.5.1




	cn.hutool
	hutool-all
	5.8.0
三、代码 1、controller
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;


@Api(tags = "OA-系统设置-报销项目分类")
@RestController
@RequestMapping("/projectManagement")
@Validated
@Slf4j
public class ClassificationController extends BaseController {

    @Resource
    private OaClassificationService classificationService;


    @PostMapping("/classification/create")
    @ApiOperation("创建报销项目分类")
    public AjaxResult createClassification(@Valid @RequestBody ClassificationCreateReqVO reqVO) {
        Long classificationId = classificationService.createClassification(reqVO);
        return toAjax(true);
    }

    @PutMapping("update")
    @ApiOperation("更新报销项目分类")
    public AjaxResult updateClassification(@Valid @RequestBody ClassificationUpdateReqVO reqVO) {
        classificationService.updateClassification(reqVO);
        return toAjax(true);
    }

    @GetMapping("/get")
    @ApiOperation("获得报销项目分类信息")
    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
    public AjaxResult getClassification(@RequestParam("id") Long id) {
        OaClassification classification = classificationService.getClassification(id);
        return success(classification);
    }

    @DeleteMapping("delete")
    @ApiOperation("删除报销项目分类")
    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1", dataTypeClass = Long.class)
    public AjaxResult deleteClassification(@RequestParam("id") Long id) {
        classificationService.deleteClassification(id);
        return toAjax(true);
    }


    @GetMapping("/listChildren")
    @ApiOperation("获取报销项目分类列表-子分类")
    public AjaxResult listClassificationsChildren(ClassificationListReqVO reqVO) {
        List list = classificationService.getSimpleClassifications(reqVO);
        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        // 排序字段,这个字段不能是null,不然会报错,默认最好是数字
        treeNodeConfig.setWeightKey("sort");
        treeNodeConfig.setIdKey("id");
        // 父级id字段
        treeNodeConfig.setParentIdKey("parentId");
        // 最大递归深度
        //treeNodeConfig.setDeep(3);
        //转换器
        List> build = TreeUtil.build(list, 0, treeNodeConfig, ((object, treeNode) -> {
            treeNode.setId(object.getId().intValue());//id
            treeNode.setParentId(object.getParentId().intValue());//父id
            treeNode.putExtra("name", object.getName());
            treeNode.putExtra("sort", object.getSort());
            treeNode.putExtra("status", object.getStatus());
        }));
        log.info(JSON.toJSONString(build));
        return success(build);
    }

    @GetMapping("/list-all-simple-children")
    @ApiOperation(value = "获取报销项目分类精简信息列表-子分类", notes = "只包含被开启的报销项目分类,主要用于前端的下拉选项")
    public AjaxResult getSimpleClassificationsChildren() {
        List> simpleClassificationsChildren = classificationService.getSimpleClassificationsChildren();
        return success(simpleClassificationsChildren);
    }
}
2、service
import cn.hutool.core.lang.tree.Tree;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;


public interface OaClassificationService extends IService {
    
    Long createClassification(ClassificationCreateReqVO reqVO);

    
    void updateClassification(ClassificationUpdateReqVO reqVO);

    
    void deleteClassification(Long id);

    
    List getSimpleClassifications(ClassificationListReqVO reqVO);

    
    List> getSimpleClassificationsChildren();

    
    void getClassiByParentId( List list,Long parentId);


    
    OaClassification getClassification(Long id);

}


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.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


@Service
public class OaClassificationServiceImpl extends ServiceImpl
        implements OaClassificationService {
    String slash = "/";
    String comma = ",";
    @Resource
    private OaClassificationMapper classificationMapper;


    @Override
    public Long createClassification(ClassificationCreateReqVO reqVO) {
        // 校验正确性
        Long parentId = reqVO.getParentId();
        checkCreateOrUpdate(null, parentId, reqVO.getName());
        // 插入分类
        OaClassification classifiction = new OaClassification();
        BeanUtils.copyProperties(reqVO, classifiction);
        setLevelDept(parentId, classifiction);
        classificationMapper.insert(classifiction);
        Long id = classifiction.getId();
        if (Objects.equals(classifiction.getParentId(), 0L)) {
            OaClassification update = new OaClassification();
            update.setId(id);
            update.setDethId(String.valueOf(id));
            classificationMapper.updateById(update);
        } else {
            OaClassification parentClass = classificationMapper.selectById(classifiction.getParentId());
            OaClassification update = new OaClassification();
            update.setId(id);
            update.setDethId(parentClass.getDethId().concat(slash).concat(String.valueOf(id)));
            classificationMapper.updateById(update);
        }
        return id;
    }


    @Override
    public void updateClassification(ClassificationUpdateReqVO reqVO) {
        Long id = reqVO.getId();
        Long parentId = reqVO.getParentId();
        // 校验正确性
        checkCreateOrUpdate(id, parentId, reqVO.getName());
        // 更新分类
        OaClassification updateObj = new OaClassification();
        BeanUtils.copyProperties(reqVO, updateObj);
        setLevelDept(parentId, updateObj);
        classificationMapper.updateById(updateObj);
        if (!Objects.equals(parentId, 0L)) {
            OaClassification parentClass = classificationMapper.selectById(parentId);
            OaClassification update = new OaClassification();
            update.setId(id);
            update.setDethId(parentClass.getDethId().concat(slash).concat(String.valueOf(id)));
            classificationMapper.updateById(update);
        }
        //修改是修改子分类名称
        reName(updateObj.getId());
    }

    
    private void setLevelDept(Long parentId, OaClassification classifiction) {
        //如果不是一级分类插入层级、纵深
        if (parentId == null || Objects.equals(parentId, 0L)) {
            //设置层级

            classifiction.setParentId(0L);
            classifiction.setDethName(classifiction.getName());
            return;
        } else {
            OaClassification dbOaClassification = classificationMapper.selectById(parentId);

            //设置纵深
            String deth = dbOaClassification.getDeth();
            if (StrUtil.isEmpty(deth)) {
                classifiction.setDeth(String.valueOf(parentId));
            } else {
                classifiction.setDeth(deth.concat(comma).concat(String.valueOf(parentId)));
            }
            classifiction.setDethName(dbOaClassification.getDethName().concat("/").concat(classifiction.getName()));
        }
    }

    @Async
    public void reName(Long id) {
        List classificationDOs = classificationMapper.selectByParentId(id);
        if (classificationDOs == null) {
            return;
        }
        //遍历子类
        classificationDOs.forEach((tem) -> {
            Long parentId = tem.getParentId();
            OaClassification dbOaClassification = classificationMapper.selectById(parentId);
            //设置层级

            //设置纵深
            String deth = dbOaClassification.getDeth();
            if (StrUtil.isEmpty(deth)) {
                tem.setDeth(String.valueOf(parentId));
            } else {
                tem.setDeth(deth.concat(comma).concat(String.valueOf(parentId)));
            }
            tem.setDethName(dbOaClassification.getDethName().concat(slash).concat(tem.getName()));
            tem.setDethId(dbOaClassification.getDethId().concat(slash).concat(tem.getId().toString()));
            classificationMapper.updateById(tem);
            reName(tem.getId());
        });
    }

    @Override
    public void deleteClassification(Long id) {
        // 校验是否存在
        checkClassificationExists(id);
        // 校验是否有子分类
        List oaClassifications = classificationMapper.selectByParentId(id);
        if (!CollUtil.isEmpty(oaClassifications)) {
            throw new ServiceException("存在子分类,无法删除");
        }

        classificationMapper.deleteById(id);
    }

    @Override
    public List getSimpleClassifications(ClassificationListReqVO reqVO) {
        return classificationMapper.selectPageList(reqVO);
    }

    @Override
    public List> getSimpleClassificationsChildren() {
        // 获得项目分类列表,只要开启状态的
        ClassificationListReqVO reqVO = new ClassificationListReqVO();
        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
        List list = getSimpleClassifications(reqVO);

        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        // 排序字段,这个字段不能是null,不然会报错,默认最好是数字
        treeNodeConfig.setWeightKey("sort");
        treeNodeConfig.setIdKey("id");
        // 父级id字段
        treeNodeConfig.setParentIdKey("parentId");
        // 最大递归深度
        //treeNodeConfig.setDeep(3);
        //转换器
        List> trees = TreeUtil.build(list, 0, treeNodeConfig, ((object, treeNode) -> {
            treeNode.setId(object.getId().intValue());//id
            treeNode.setParentId(object.getParentId().intValue());//父id
            treeNode.putExtra("name", object.getName());
        }));
        return trees;
    }

    @Override
    public void getClassiByParentId(List list, Long id) {
        if (id == null) {
            return;
        }
        List classificationDOs = classificationMapper.selectByParentId(id);
        if (CollUtil.isEmpty(classificationDOs)) {
            return;
        }
        list.addAll(classificationDOs);
        // 递归,简单粗暴
        classificationDOs.forEach((tem) -> {
            this.getClassiByParentId(list, tem.getId());
        });

        return;
    }


    private void checkCreateOrUpdate(Long id, Long parentId, String name) {
        // 校验自己存在
        checkClassificationExists(id);
        // 校验父分类的有效性
        checkParentClassificationEnable(id, parentId);
        // 校验分类名的唯一性
        checkClassificationNameUnique(id, parentId, name);
    }

    private void checkParentClassificationEnable(Long id, Long parentId) {
        if (parentId == null || Objects.equals(parentId, 0L)) {
            return;
        }
        // 不能设置自己为父分类
        if (parentId.equals(id)) {
            throw new ServiceException("不能设置自己为父分类");
        }
        // 父不存在
        OaClassification classifiction = classificationMapper.selectById(parentId);
        if (classifiction == null) {
            throw new ServiceException("父级分类不存在");
        }
        // 父分类被禁用
        if (!CommonStatusEnum.ENABLE.getStatus().equals(classifiction.getStatus())) {
            throw new ServiceException("分类不处于开启状态,不允许选择");
        }
        // 父分类不能是原来的子分类
        List children = new ArrayList<>();
        this.getClassiByParentId(children, id);
        if (children.stream().anyMatch(classifiction1 -> classifiction1.getId().equals(parentId))) {
            throw new ServiceException("不能设置自己的子分类为父分类");
        }
    }

    private void checkClassificationExists(Long id) {
        if (id == null) {
            return;
        }
        OaClassification classifiction = classificationMapper.selectById(id);
        if (classifiction == null) {
            throw new ServiceException("分类不存在");
        }
    }


    private void checkClassificationNameUnique(Long id, Long parentId, String name) {
        OaClassification menu = classificationMapper.selectByParentIdAndName(parentId, name);
        if (menu == null) {
            return;
        }
        // 如果 id 为空,说明不用比较是否为相同 id 的
        if (id == null) {
            throw new ServiceException("已经存在该名字的分类");
        }
        if (!menu.getId().equals(id)) {
            throw new ServiceException("已经存在该名字的分类");
        }
    }


    @Override
    public OaClassification getClassification(Long id) {
        return classificationMapper.selectById(id);
    }


}




3、mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import org.apache.ibatis.annotations.Param;

import java.util.List;


public interface OaClassificationMapper extends BaseMapper {

    List selectPageList(@Param("reqVO") ClassificationListReqVO reqVO);


    OaClassification selectByParentIdAndName(@Param("parentId") Long parentId, @Param("name") String name);


    List selectByParentId(Long parentId);


}




    
        
        
        
        
        
        
        
        
        
        
        
        
    

    
        id
        ,name,parent_id,
        sort,status,deth,
        deth_name,deth_id,create_by,
        create_time,update_by,update_time
    
    
        select * from oa_classification
        
            
                parent_id= #{parentId}
            
            
                and name =#{name}
            
        
    
    


4、基础类
@Data
@TableName(value = "oa_classification", autoResultMap = true)
@EqualsAndHashCode(callSuper = true)
public class OaClassification extends BaseEntityTwo {

    
    @ApiModelProperty("分类id")
    private Long id;
    
    @ApiModelProperty("分类名称")
    //@Length(max= 100,message="编码长度不能超过100")
    private String name;
    
    @ApiModelProperty("父分类id")
    private Long parentId;
    
    @ApiModelProperty("显示顺序")
    private Integer sort;
    
    @ApiModelProperty("分类状态(0正常 1停用)")
    private Integer status;
    
    @ApiModelProperty("纵深")
    //@Length(max= 255,message="编码长度不能超过255")
    private String deth;
    
    @ApiModelProperty("纵深名称")
    //@Length(max= 300,message="编码长度不能超过300")
    private String dethName;
    
    @ApiModelProperty("纵深ID")
    //@Length(max= 255,message="编码长度不能超过255")
    private String dethId;

}

@Data
public class BaseEntityTwo implements Serializable {
    private static final long serialVersionUID = 1L;


    
    @TableField(fill = FieldFill.INSERT)
    @Excel(name = "创建者",sort = 101)
    private String createBy;

    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    @Excel(name = "创建时间", sort = 102,width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
    private Date createTime;

    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @Excel(name = "更新者",sort = 103)
    private String updateBy;

    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @Excel(name = "更新时间", sort = 104,width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
    private Date updateTime;

}
@Data
public class ClassificationBaseVO {

    @ApiModelProperty(value = "分类名称", required = true, example = "xxx")
    @NotBlank(message = "分类名称不能为空")
    @Size(max = 30, message = "分类名称长度不能超过30个字符")
    private String name;

    @ApiModelProperty(value = "父菜单ID,一级分类", example = "1024")
    private Long parentId;

    private Integer sort;


}
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ClassificationCreateReqVO extends ClassificationBaseVO {
}
@Data
public class ClassificationListReqVO {

    @ApiModelProperty(value = "分类名称", example = "王二", notes = "模糊匹配")
    private String name;

    @ApiModelProperty(value = "分类ID", example = "1")
    private Long id;

    @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 CommonStatusEnum 枚举类")
    private Integer status;

}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClassificationSimpleRespVO {

    @ApiModelProperty(value = "分类编号", required = true, example = "1024")
    private Long id;

    @ApiModelProperty(value = "分类名称", required = true, example = "王二")
    private String name;

    @ApiModelProperty(value = "父分类 ID", required = true, example = "1024")
    private Long parentId;

}
@Data
@EqualsAndHashCode(callSuper = true)
public class ClassificationUpdateReqVO extends ClassificationBaseVO {

    @ApiModelProperty(value = "分类编号", required = true, example = "1024")
    @NotNull(message = "分类编号不能为空")
    private Long id;

    @ApiModelProperty(value = "状态 0开启 1关闭", required = true, example = "1")
    @NotNull(message = "状态不能为空")
    private Integer status;

}
四、不足

1、纵深名称和纵深ID使用分隔符+字符存储。可以使用json存储,配合mybatis的typeHandler属性,方便业务使用时结构化;

2、可以增加缓存提升获取分类的效率,如果项目分类比较多,不建议一次取出;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1014281.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号