商品分类(递归子目录)接口开发笔记

商品分类(递归子目录)接口开发笔记

开发如下接口:

1.所有类目

GET /categories

request

无需登录

response

success

{
    "status": 0,
    "data": [{
        "id": 100001,
        "parentId": 0,
        "name": "家用电器",
        "sortOrder": 1,
        "subCategories": [{
            "id": 100006,
            "parentId": 100001,
            "name": "冰箱",
            "sortOrder": 2,
            "subCategories": [{
                "id": 100040,
                "parentId": 100006,
                "name": "进口冰箱",
                "sortOrder": 1,
                "subCategories": []
            }]
        },  {
        "id": 100005,
        "parentId": 0,
        "name": "酒水饮料",
        "sortOrder": 1,
        "subCategories": [{
            "id": 100026,
            "parentId": 100005,
            "name": "白酒",
            "sortOrder": 1,
            "subCategories": []
        }, {
            "id": 100027,
            "parentId": 100005,
            "name": "红酒",
            "sortOrder": 1,
            "subCategories": []
        }]
    }]
}

数据库表:

/*
 Navicat Premium Data Transfer

 Source Server         : 本地数据库
 Source Server Type    : MySQL
 Source Server Version : 50726
 Source Host           : localhost:3306
 Source Schema         : mimall

 Target Server Type    : MySQL
 Target Server Version : 50726
 File Encoding         : 65001

 Date: 27/03/2020 22:10:31
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for mall_category
-- ----------------------------
DROP TABLE IF EXISTS `mall_category`;
CREATE TABLE `mall_category`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '类别Id',
  `parent_id` int(11) NULL DEFAULT NULL COMMENT '父类别id当id=0时说明是根节点,一级类别',
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '类别名称',
  `status` tinyint(1) NULL DEFAULT 1 COMMENT '类别状态1-正常,2-已废弃',
  `sort_order` int(4) NOT NULL DEFAULT 1 COMMENT '数字越大,排序优先级越高',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 100052 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of mall_category
-- ----------------------------
INSERT INTO `mall_category` VALUES (100001, 0, '家用电器', 1, 1, '2000-03-25 16:46:00', '2000-03-25 16:46:00');
INSERT INTO `mall_category` VALUES (100002, 0, '数码3C', 1, 2, '2000-03-25 16:46:21', '2000-03-25 16:46:21');
INSERT INTO `mall_category` VALUES (100003, 0, '服装箱包', 1, 1, '2000-03-25 16:49:53', '2000-03-25 16:49:53');
INSERT INTO `mall_category` VALUES (100004, 0, '食品生鲜', 1, 1, '2000-03-25 16:50:19', '2000-03-25 16:50:19');
INSERT INTO `mall_category` VALUES (100005, 0, '酒水饮料', 1, 1, '2000-03-25 16:50:29', '2000-03-25 16:50:29');
INSERT INTO `mall_category` VALUES (100006, 100001, '冰箱', 1, 1, '2000-03-25 16:52:15', '2000-03-25 16:52:15');
INSERT INTO `mall_category` VALUES (100007, 100001, '电视', 1, 2, '2000-03-25 16:52:26', '2000-03-25 16:52:26');
INSERT INTO `mall_category` VALUES (100008, 100001, '洗衣机', 1, 1, '2000-03-25 16:52:39', '2000-03-25 16:52:39');
INSERT INTO `mall_category` VALUES (100009, 100001, '空调', 1, 1, '2000-03-25 16:52:45', '2000-03-25 16:52:45');
INSERT INTO `mall_category` VALUES (100010, 100001, '电热水器', 1, 1, '2000-03-25 16:52:54', '2000-03-25 16:52:54');
INSERT INTO `mall_category` VALUES (100011, 100002, '电脑', 1, 1, '2000-03-25 16:53:18', '2000-03-25 16:53:18');
INSERT INTO `mall_category` VALUES (100012, 100002, '手机', 1, 1, '2000-03-25 16:53:27', '2000-03-25 16:53:27');
INSERT INTO `mall_category` VALUES (100013, 100002, '平板电脑', 1, 1, '2000-03-25 16:53:35', '2000-03-25 16:53:35');
INSERT INTO `mall_category` VALUES (100014, 100002, '数码相机', 1, 1, '2000-03-25 16:53:56', '2000-03-25 16:53:56');
INSERT INTO `mall_category` VALUES (100015, 100002, '3C配件', 1, 1, '2000-03-25 16:54:07', '2000-03-25 16:54:07');
INSERT INTO `mall_category` VALUES (100016, 100003, '女装', 1, 1, '2000-03-25 16:54:44', '2000-03-25 16:54:44');
INSERT INTO `mall_category` VALUES (100017, 100003, '帽子', 1, 1, '2000-03-25 16:54:51', '2000-03-25 16:54:51');
INSERT INTO `mall_category` VALUES (100018, 100003, '旅行箱', 1, 1, '2000-03-25 16:55:02', '2000-03-25 16:55:02');
INSERT INTO `mall_category` VALUES (100019, 100003, '手提包', 1, 1, '2000-03-25 16:55:09', '2000-03-25 16:55:09');
INSERT INTO `mall_category` VALUES (100020, 100003, '保暖内衣', 1, 1, '2000-03-25 16:55:18', '2000-03-25 16:55:18');
INSERT INTO `mall_category` VALUES (100021, 100004, '零食', 1, 1, '2000-03-25 16:55:30', '2000-03-25 16:55:30');
INSERT INTO `mall_category` VALUES (100022, 100004, '生鲜', 1, 1, '2000-03-25 16:55:37', '2000-03-25 16:55:37');
INSERT INTO `mall_category` VALUES (100023, 100004, '半成品菜', 1, 1, '2000-03-25 16:55:47', '2000-03-25 16:55:47');
INSERT INTO `mall_category` VALUES (100024, 100004, '速冻食品', 1, 1, '2000-03-25 16:55:56', '2000-03-25 16:55:56');
INSERT INTO `mall_category` VALUES (100025, 100004, '进口食品', 1, 1, '2000-03-25 16:56:06', '2000-03-25 16:56:06');
INSERT INTO `mall_category` VALUES (100026, 100005, '白酒', 1, 1, '2000-03-25 16:56:22', '2000-03-25 16:56:22');
INSERT INTO `mall_category` VALUES (100027, 100005, '红酒', 1, 1, '2000-03-25 16:56:30', '2000-03-25 16:56:30');
INSERT INTO `mall_category` VALUES (100028, 100005, '饮料', 1, 1, '2000-03-25 16:56:37', '2000-03-25 16:56:37');
INSERT INTO `mall_category` VALUES (100029, 100005, '调制鸡尾酒', 1, 1, '2000-03-25 16:56:45', '2000-03-25 16:56:45');
INSERT INTO `mall_category` VALUES (100030, 100005, '进口洋酒', 1, 1, '2000-03-25 16:57:05', '2000-03-25 16:57:05');
INSERT INTO `mall_category` VALUES (100040, 100006, '进口冰箱', 1, 1, '2020-03-27 20:02:27', '2020-03-27 20:02:29');
INSERT INTO `mall_category` VALUES (100050, 100040, '进口德国冰箱', 1, 1, '2020-03-27 21:27:19', '2020-03-27 21:27:22');

SET FOREIGN_KEY_CHECKS = 1;

开发思路分析:
1、先查出一级目录 —> 查其子目录,一直查到的是null (从外到内)

2、查出目录 - >查父目录,一直查到parent_id=0(从内到外)

本次笔记使用思路一,先查询一级目录,在便利查询子目录,没有子目录为止

分析数据可得最外层使用ResponseVo即可,data 为Category类的部分成员变量,故可新建一个CategoryVo类设置相关变量,由于子Category中有Category,所以需要添加List subCategories 以实现递归嵌套。

CategoryVo类:

package cn.blogsx.mimall.vo;

import lombok.Data;

import java.util.List;

/**
 * @author Alex
 * @create 2020-03-27 20:09
 **/
@Data
public class CategoryVo {
    private Integer id;

    private Integer parentId;

    private String name;

    private Integer sortOrder;

    private List<CategoryVo> subCategories; //如此设计即可实现递归

}

service层:

package cn.blogsx.mimall.service;

import cn.blogsx.mimall.vo.CategoryVo;
import cn.blogsx.mimall.vo.ResponseVo;

import java.util.List;

public interface ICategoryService {


    ResponseVo<List<CategoryVo>> selectAll();
}

实现类:

package cn.blogsx.mimall.service.impl;

import cn.blogsx.mimall.dao.CategoryMapper;
import cn.blogsx.mimall.pojo.Category;
import cn.blogsx.mimall.service.ICategoryService;
import cn.blogsx.mimall.vo.CategoryVo;
import cn.blogsx.mimall.vo.ResponseVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static cn.blogsx.mimall.consts.MiMallConst.ROOT_PARENT_ID;

/**
 * @author Alex
 * @create 2020-03-27 20:16
 **/
@Service
public class CategoryServiceImpl implements ICategoryService {
    @Autowired
    private CategoryMapper categoryMapper;

    /**
     * 耗时:http(请求api) > 磁盘 > 内存
     * mysql(内网 + 磁盘)
     * @return
     */

    @Override
    public ResponseVo<List<CategoryVo>> selectAll() {
//        List<CategoryVo> categoryVoList = new ArrayList<>();

        List<Category> categories = categoryMapper.selectAll();
        //查出parent_id=0
//        for (Category category : categories) {
//            if(category.getParentId().equals(ROOT_PARENT_ID)) {
//                CategoryVo categoryVo= new CategoryVo();
//                BeanUtils.copyProperties(category,categoryVo);
//                categoryVoList.add(categoryVo);
//            }
//        } 等价于下方写法
        //lambda + stream
        List<CategoryVo> categoryVoList = categories.stream()
                .filter(e -> e.getParentId().equals(ROOT_PARENT_ID))
                .map(this::categoryToCategoryVo)
                .sorted(Comparator.comparing(CategoryVo::getSortOrder).reversed())
                .collect(Collectors.toList());

        //查询子目录
        findSubCategory(categoryVoList,categories);

        return ResponseVo.success(categoryVoList);

    }
    private void findSubCategory(List<CategoryVo> categoryVoList, List<Category> categories) {
        for(CategoryVo categoryVo : categoryVoList) {
            List<CategoryVo> subCategoryVoList = new ArrayList<>();
            for (Category category : categories) {
                //如果查到内容,设置subCategory,继续往下查
                if(categoryVo.getId().equals(category.getParentId())) {
                    CategoryVo subCategoryVo = categoryToCategoryVo(category);
                    subCategoryVoList.add(subCategoryVo);
                }
                subCategoryVoList.sort(Comparator.comparing(CategoryVo::getSortOrder).reversed());//降序排序
                categoryVo.setSubCategories(subCategoryVoList);
                //继续往下查
                findSubCategory(subCategoryVoList,categories);
            }
        }
    }
    private CategoryVo  categoryToCategoryVo(Category category) {
        CategoryVo categoryVo= new CategoryVo();
        BeanUtils.copyProperties(category,categoryVo);
        return categoryVo;
    }
}

查询思路:由于数据库表时分类表,故数据量不会很大,应一次性将数据全部查询出来,再根据id == parentId判断是否为子分类,并递归查询,直到子分类为null,可将查询子目录封装为方法,调用自身实现递归查询。

posted @ 2020-03-28 09:31  sxblog  阅读(370)  评论(0编辑  收藏  举报