商品分类(递归子目录)接口开发笔记
开发如下接口:
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
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,可将查询子目录封装为方法,调用自身实现递归查询。