Java-springboot生鲜电商项目(二)商品分类模块

  • 主要功能
  • 主要会使用的新技术和重难点:
  • 涉及到的接口
  • (一)开发添加商品分类目录的接口
  • 1.在MallExceptionEnum加入处理异常的相关代码
  • 2.在dao层CategoryMapper中添加通过商品类目名查询的接口
  • 3.在categoryMapper.xml中添加SQL语句
  • 4.另外添加目录请求类,不用pojo中Category,是因为保持每个类都有自己的职责,也是保证程序的安全性
  • 5.在Service下创建目录分类的Service接口,并创建CategoryImpl实现接口
  • 6.在controller层中创建CategoryController
  • 使用postman进行接口的测试,添加新的商品类别,如下图:
  • categoryController中在// 1.判断商品类别这个判断语句中,过于冗长,阅读代码不好,需要进行优化重构
  • 参数校验
  • 1.在CategoryController/addCategory方法中添加`@Valid`并注释掉判断语句
  • 在AddCategoryReq类中的属性添加上参数校验注解
  • 使用postman先进行name长度的校验,可以看出长度大于6发生异常
  • 控制台打印的信息如下
  • 但是我们可以发现,在postman打印出来的信息是系统异常,用户并不知道是什么原因,所以还要就行修改:
  • 在/exception/GlobalExceptionHandler.java中,添加下面的代码来去捕捉错误信息
  • 在postman中我设置多type>4的异常,进行接口的测试,提示的错误由系统的错误改变成了更加直观易懂的错误提示,如果想在进行校验,或者更改校验信息,直接可以在实体类的属性中添加参数注解。
  • (二)使用swagger自动生成API文档
  • 在pom.xml添加相关依赖
  • 在MallApplication启动方法中加入@EnableSwagger2
  • 创建config包,编写两个swagger2的相关类
  • 在categoryController的addCategory方法上添加`@ApiOperation("后台添加商品分类目录")`的注解,然后启动springboot,在网页中输入`localhost:8080/swagger-ui.html`,显示swagger就成功了
  • (三)更新商品分类目录接口
  • 1.在request下新建一个UpdateCategory类进行商品分类目录来为后续的更新进行的操作
  • 在categorySeviceImpl中添加update方法,用于更新商品目录的业务操作
  • 在categoryController编写更新商品分类目录操作
  • 在postman进行测试,将id为29的鸭货修改type为3以及修改同id的名字来检查是否能进行同名操作
  • (四)统一校验管理员身份
  • 经过在商品的分类目录中的添加方法和更新方法中,重复出现了校验管理员身份的代码,造成代码复用冗余,不易阅读的修改的情况,所以还需要对校验管理员身份再次进行迭代优化。
  • 在filter/AdminFilter创建一个过滤器来进行拦截
  • 在config创建管理员过滤器的配置类
  • 在categoryController中将在删除商品分类目录的接口的实现管理员校验登录的过滤器,不用实现具体逻辑的情况下看看是否会对管理员进行拦截
  • 我使用具有管理员身份的账号在postman中进行登录后,测试delete接口,返回的接口为空,则测试成功,用普通管理员身份登录,测报错,提示预期错误,则测试成功
  • (五)对商品分类目录进行删除接口的编写
  • 在MallExceptionEnum添加
  • 在categoryServiceImpl中添加删除商品目录的业务方法
  • 在categoryController中添加删除接口
  • 使用postman进行删除商品目录接口测试
  • (六)查询后台商品分类的接口(难点)
  • 在pom.xml中引入相关依赖
  • 在model下创建vo,vo是存储经过一定转化之后返回给前端的一个类
  • 在dao层中categoryMapper创建查询商品列表的接口
  • 在CategoryMapper.xml中编写查询列表的SQL语句
  • 在categoryServiceImpl中实现查询商品目录列表并分类的业务逻辑
  • 在controller中编写给管理员看的商品目录列表
  • 使用postman进行接口测试,和预期一样,老铁没毛病
  • (七)给前台用户看的分类列表
  • 在dao层定义接口
  • 在categoryMapper.xml中编写SQL语句
  • 在categoryServiceImpl中编写前台目录展示的业务代码
  • 在categoryController中编写前台目录展示的接口
  • 使用postman进行测试,和预期一样
  • (八)使用springboot集成Redis,将目录存放在Redis中
  • 在pom.xml中引入Redis依赖
  • 在application.properties中配置Redis
  • 在MallApplication启动方法中加入`@EnableCaching`注解
  • 在categoryServiceImpl中的listCategoryForCustomer方法添加`@Cacheable(value = "listCategoryForCustomer")`注解,//是spring所提供的
  • 在config中创建Redis的配置类
  • 使用postman进行测试,这个步骤比较恶心,我还踩了下坑,因为原来我的注解不是spring的
  • 先在categoryServiceImpl的方法中打个断点
  • 进入postman,访问`http://localhost:8080/category/list`,第一次访问会比较慢,会进入断点,30秒后,或者取消这个断点,再次进行访问就会发现只用10几毫秒就能访问(我这个是38毫秒,直接访问数据库是100多毫秒),说明,存入到Redis成功
  • 还有一种方法就是打开Redis,输入`keys *`查看是否有数据输入,上面的注解value就显示到了Redis中。


主要功能

  1. 商品的增删改查
  2. 分类的父一级目录、递归

主要会使用的新技术和重难点:

  1. swagger2的使用
  2. 以注解的方式进行参数校验
  3. 管理员的后天登录校验重构
  4. Redis的使用
  5. 统一鉴权
  6. 调试功能

涉及到的接口

  1. 增加目录分类(后台)
  2. 更新目录分类(后台)
  3. 删除分类(后台)
  4. 分类列表(后台)
  5. 分类列表(前台)

(一)开发添加商品分类目录的接口

1.在MallExceptionEnum加入处理异常的相关代码

PARA_NOT_NULL(10010,"参数不能为空"),
CREATE_FAILE(10011,"新增失败"),
REQUEST_PARAM_EROOR(10012,"参数错误"),

2.在dao层CategoryMapper中添加通过商品类目名查询的接口

Category selectByName(String name);

3.在categoryMapper.xml中添加SQL语句

<select id="selectByName" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from imooc_mall_category
    where name=#{name,jdbcType=VARCHAR}
  </select>

4.另外添加目录请求类,不用pojo中Category,是因为保持每个类都有自己的职责,也是保证程序的安全性

package com.hyb.mall.model.request;

/**
 * 描述:AddCategoryReq添加目录请求类
 * 不用pojo中Category,是因为保持每个类都有自己的职责,也是保证程序的安全性
 */
public class AddCategoryReq {
    private String name;
    private Integer type;
    private Integer parentId;
    private Integer orderNum;

    public AddCategoryReq() {
    }

    public AddCategoryReq(String name, Integer type, Integer parentId, Integer orderNum) {
        this.name = name;
        this.type = type;
        this.parentId = parentId;
        this.orderNum = orderNum;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    public Integer getOrderNum() {
        return orderNum;
    }

    public void setOrderNum(Integer orderNum) {
        this.orderNum = orderNum;
    }
}

5.在Service下创建目录分类的Service接口,并创建CategoryImpl实现接口

package com.hyb.mall.service.impl;

import com.fasterxml.jackson.databind.util.BeanUtil;
import com.hyb.mall.exception.MallException;
import com.hyb.mall.exception.MallExceptionEnum;
import com.hyb.mall.model.dao.CategoryMapper;
import com.hyb.mall.model.pojo.Category;
import com.hyb.mall.model.request.AddCategoryReq;
import com.hyb.mall.service.CategoryService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 描述:目录分类实现类
 */
@Service
public class CategoryServiceImpl implements CategoryService {
    @Autowired
    CategoryMapper categoryMapper;
    //写上override,让他直接实现categoryService接口的方法
    @Override
    public void add(AddCategoryReq addCategoryReq){
        Category category = new Category();
        //小技巧:将拷贝的源,拷贝到目标类中
        BeanUtils.copyProperties(addCategoryReq,category);
        //获取到商品在数据库的名字,以便判断商品的重名
        Category categoryOld = categoryMapper.selectByName(addCategoryReq.getName());
        //如果数据库有重名,则抛出异常不允许创建
        if (categoryOld != null){
            throw new MallException(MallExceptionEnum.NAME_EXISTED);
        }
        int count = categoryMapper.insertSelective(category);
        //判断是否插入成功
        if (count==0){
            throw new MallException(MallExceptionEnum.INSERT_FAILED);
        }
    }
}

6.在controller层中创建CategoryController

package com.hyb.mall.controller;

import com.hyb.mall.common.ApiRestResponse;
import com.hyb.mall.common.Constant;
import com.hyb.mall.exception.MallException;
import com.hyb.mall.exception.MallExceptionEnum;
import com.hyb.mall.model.pojo.User;
import com.hyb.mall.model.request.AddCategoryReq;
import com.hyb.mall.service.CategoryService;
import com.hyb.mall.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;

/**
 * 描述:目录controller
 */
@Controller
public class CategoryController {
    @Autowired
    UserService userService;
    @Autowired
    CategoryService categoryService;

    @PostMapping("admin/category/add")
    @ResponseBody
    public ApiRestResponse addCategory(HttpSession session, @RequestBody AddCategoryReq addCategoryReq) {
        //1.判断商品分类目录名称,级别父id,时序是否为空,必须按照顺序进行书写,不然会报错
        if (addCategoryReq.getName() == null ||
                addCategoryReq.getType() == null ||
                addCategoryReq.getParentId() == null ||
                addCategoryReq.getOrderNum() == null
        ) {
            return ApiRestResponse.error(MallExceptionEnum.PARA_NOT_NULL);
        }
        //2.获取session中的用户
        User currentUser = (User) session.getAttribute(Constant.HYB_MALL_USER);
        //判断当前用户是否为空,为空则需要登录
        if (currentUser == null) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_LOGIN);
        }
        //3.登录校验是否为管理员
        boolean adminRole = userService.checkAdminRole(currentUser);
        if (adminRole) {
            //是管理员,则执行添加商品类别操作
            categoryService.add(addCategoryReq);
            return ApiRestResponse.success();
        } else {
            return ApiRestResponse.error(MallExceptionEnum.NEED_ADMIN);
        }
    }
}

使用postman进行接口的测试,添加新的商品类别,如下图:

java 电商源码 java电商项目模块介绍_spring

categoryController中在// 1.判断商品类别这个判断语句中,过于冗长,阅读代码不好,需要进行优化重构

参数校验

  1. @Valid 需要校验
  2. NotNull 非空
  3. Max(value) 最大值
  4. Size(max,min) 字符串长度范围限制

1.在CategoryController/addCategory方法中添加@Valid并注释掉判断语句

@PostMapping("admin/category/add")
    @ResponseBody
    public ApiRestResponse addCategory(HttpSession session, @Valid @RequestBody AddCategoryReq addCategoryReq) {
//        //1.判断商品分类目录名称,级别父id,时序是否为空,必须按照顺序进行书写,不然会报错
//        if (addCategoryReq.getName() == null ||
//                addCategoryReq.getType() == null ||
//                addCategoryReq.getParentId() == null ||
//                addCategoryReq.getOrderNum() == null
//        ) {
//            return ApiRestResponse.error(MallExceptionEnum.PARA_NOT_NULL);
//        }

在AddCategoryReq类中的属性添加上参数校验注解

@Size(min = 2, max = 5)
    @NotNull(message = "分类目录名称不能为空")
    private String name;

    @Max(3)
    @NotNull(message = "分类目录类型不能为空")
    private Integer type;

    @NotNull(message = "分类目录上级目录不能为空")
    private Integer parentId;

    @NotNull(message = "分类目录展示的排序不能为空")
    private Integer orderNum;

使用postman先进行name长度的校验,可以看出长度大于6发生异常

java 电商源码 java电商项目模块介绍_xml_02

控制台打印的信息如下

java 电商源码 java电商项目模块介绍_spring_03

但是我们可以发现,在postman打印出来的信息是系统异常,用户并不知道是什么原因,所以还要就行修改:

在/exception/GlobalExceptionHandler.java中,添加下面的代码来去捕捉错误信息

@ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("MethodArgumentNotValidException: ", e);
        return handleBindingResult(e.getBindingResult());
    }
    private ApiRestResponse handleBindingResult(BindingResult result){
        //把异常处理为对外暴露的提示
        List<String> list = new ArrayList<>();
        //是不是包含错误
        if (result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            for (ObjectError objectError: allErrors) {
                String message = objectError.getDefaultMessage();
                list.add(message);
            }
        }
        if(list.size() == 0){
            return ApiRestResponse.error(MallExceptionEnum.REQUEST_PARAM_EROOR);
        }
        return ApiRestResponse.error(MallExceptionEnum.REQUEST_PARAM_EROOR.getCode(),list.toString());
    }

在postman中我设置多type>4的异常,进行接口的测试,提示的错误由系统的错误改变成了更加直观易懂的错误提示,如果想在进行校验,或者更改校验信息,直接可以在实体类的属性中添加参数注解。

java 电商源码 java电商项目模块介绍_Redis_04

(二)使用swagger自动生成API文档

在pom.xml添加相关依赖

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
                <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

在MallApplication启动方法中加入@EnableSwagger2

@SpringBootApplication
@MapperScan(basePackages = "com.hyb.mall.model.dao")
@EnableSwagger2
public class MallApplication {

    public static void main(String[] args) {
        SpringApplication.run(MallApplication.class, args);
    }

}

创建config包,编写两个swagger2的相关类

package com.hyb.mall.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
public class SpringFoxConfig {

    //访问http://localhost:8083/swagger-ui.html可以看到API文档
    @Bean
    public Docket api() {
    //固定格式
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("慕慕生鲜")
                .description("")
                .termsOfServiceUrl("")
                .build();
    }
}
package com.hyb.mall.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 描述:配置地址映射
 */
@Configuration
public class MallWebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations(
                "classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations(
                "classpath:/META-INF/resources/webjars/");
    }
}

在categoryController的addCategory方法上添加@ApiOperation("后台添加商品分类目录")的注解,然后启动springboot,在网页中输入localhost:8080/swagger-ui.html,显示swagger就成功了

java 电商源码 java电商项目模块介绍_java_05

(三)更新商品分类目录接口

1.在request下新建一个UpdateCategory类进行商品分类目录来为后续的更新进行的操作

package com.hyb.mall.model.request;

import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * 描述:updateCategoryReq更新修改目录请求类
 * 不用pojo中Category,是因为保持每个类都有自己的职责,也是保证程序的安全性
 */
public class UpdateCategoryReq {

    @NotNull(message = "分类目录id不能为空")
    private Integer id;

    @Size(min = 2, max = 5)
    private String name;

    @Max(3)
    private Integer type;

    private Integer parentId;

    private Integer orderNum;

    public UpdateCategoryReq() {
    }

    public UpdateCategoryReq(String name, Integer type, Integer parentId, Integer orderNum) {
        this.name = name;
        this.type = type;
        this.parentId = parentId;
        this.orderNum = orderNum;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    public Integer getOrderNum() {
        return orderNum;
    }

    public void setOrderNum(Integer orderNum) {
        this.orderNum = orderNum;
    }
}

在categorySeviceImpl中添加update方法,用于更新商品目录的业务操作

@Override
    public void update(Category updateCategoryReq){
        //1.查找有没有重复的商品分类名字
        if (updateCategoryReq.getName() != null) {
            //获取需要更新商品的名字
            Category categoryOld = categoryMapper.selectByName(updateCategoryReq.getName());
            if (categoryOld !=null && ! categoryOld.getId().equals(updateCategoryReq.getId())) {
                throw new MallException(MallExceptionEnum.NAME_EXISTED);
            }
        }
        int count = categoryMapper.updateByPrimaryKeySelective(updateCategoryReq);
        if (count == 0) {
            throw new MallException(MallExceptionEnum.UPDATE_FAILED);
        }
    }

在categoryController编写更新商品分类目录操作

@ApiOperation("后台更新商品分类目录")
    @PostMapping("admin/category/update")
    @ResponseBody
    public ApiRestResponse updateCategory(@Valid @RequestBody UpdateCategoryReq updateCategoryReq,HttpSession session){
        //1.获取session中的用户
        User currentUser = (User) session.getAttribute(Constant.HYB_MALL_USER);
        //2.判断当前用户是否为空,为空则需要登录
        if (currentUser == null) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_LOGIN);
        }
        //3.登录校验是否为管理员
        boolean adminRole = userService.checkAdminRole(currentUser);
        if (adminRole) {
            //是管理员,则执行添加商品类别操作
            Category category =new Category();
            BeanUtils.copyProperties(updateCategoryReq,category);
            categoryService.update(category);
            return ApiRestResponse.success();
        } else {
            return ApiRestResponse.error(MallExceptionEnum.NEED_ADMIN);
        }
    }

在postman进行测试,将id为29的鸭货修改type为3以及修改同id的名字来检查是否能进行同名操作

java 电商源码 java电商项目模块介绍_spring_06


java 电商源码 java电商项目模块介绍_xml_07

(四)统一校验管理员身份

经过在商品的分类目录中的添加方法和更新方法中,重复出现了校验管理员身份的代码,造成代码复用冗余,不易阅读的修改的情况,所以还需要对校验管理员身份再次进行迭代优化。

在filter/AdminFilter创建一个过滤器来进行拦截

package com.hyb.mall.filter;
import com.hyb.mall.common.Constant;
import com.hyb.mall.model.pojo.User;
import com.hyb.mall.service.CategoryService;
import com.hyb.mall.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 描述:管理员校验过滤器
 */
public class AdminFilter implements Filter {
    @Autowired
    private UserService userService;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest servletRequest, 
                         ServletResponse servletResponse, 
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpSession session = request.getSession();
        //1.获取session中的用户
        User currentUser = (User) session.getAttribute(Constant.HYB_MALL_USER);
        //2.判断当前用户是否为空,为空则需要登录
        if (currentUser == null) {
            PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse)servletResponse).getWriter();
            out.write("{\n" +
                    "    \"status\": 10007,\n" +
                    "    \"msg\": \"NEED_LOGIN\",\n" +
                    "    \"data\": null\n" +
                    "}");
            out.flush();
            out.close();
            return;
        }
        //3.登录校验是否为管理员
        boolean adminRole = userService.checkAdminRole(currentUser);
        if (adminRole) {
            //是管理员,则执行放行的操作
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
            out.write("{\n" +
                    "    \"status\": 10009,\n" +
                    "    \"msg\": \"NEED_ADMIN\",\n" +
                    "    \"data\": null\n" +
                    "}");
            out.flush();
            out.close();
            return;
        }
    }

    @Override
    public void destroy() {}
}

在config创建管理员过滤器的配置类

package com.hyb.mall.config;

import com.hyb.mall.filter.AdminFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 描述:管理员过滤器的配置
 */
@Configuration
public class AdminFilterConfig {
    @Bean
    public AdminFilter adminFilter(){
        return new AdminFilter();
    }

    //将整个filter放到整个链路中去
    @Bean(name = "adminFilterConf") //设置的名字不能和类名一样不然会有冲突
    public FilterRegistrationBean adminFilterConfig(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(adminFilter());
        //设置拦截的URL
        filterRegistrationBean.addUrlPatterns("/admin/category/*");
        filterRegistrationBean.addUrlPatterns("/admin/product/*");
        filterRegistrationBean.addUrlPatterns("/admin/order/*");
        //给过滤器配置设置名字,以便于区分不同的名字
        filterRegistrationBean.setName("adminFilterConfig");
        return filterRegistrationBean;
    }
}

在categoryController中将在删除商品分类目录的接口的实现管理员校验登录的过滤器,不用实现具体逻辑的情况下看看是否会对管理员进行拦截

@ApiOperation("后台删除商品分类目录")
    @PostMapping("admin/category/delete")
    @ResponseBody
    public ApiRestResponse deleteCategory(){
        return null;
    }

我使用具有管理员身份的账号在postman中进行登录后,测试delete接口,返回的接口为空,则测试成功,用普通管理员身份登录,测报错,提示预期错误,则测试成功

java 电商源码 java电商项目模块介绍_xml_08

java 电商源码 java电商项目模块介绍_java_09

(五)对商品分类目录进行删除接口的编写

在MallExceptionEnum添加

DELETE_FAILE(10013,"删除失败"),

在categoryServiceImpl中添加删除商品目录的业务方法

@Override
    public void delete (Integer id){
        //1.先进行查找,判断是否能在数据库中找到要删除的id
        Category categoryOld = categoryMapper.selectByPrimaryKey(id);
        //2.查找不到id记录,无法删除,删除失败
        if (categoryOld == null) {
          throw new MallException(MallExceptionEnum.DELETE_FAILE);
        }
        //3.进行删除操作
        int count = categoryMapper.deleteByPrimaryKey(id);
        //4.记录为0,则删除失败
        if (count == 0) {
            throw new MallException(MallExceptionEnum.DELETE_FAILE);
        }
    }

在categoryController中添加删除接口

@ApiOperation("后台删除商品分类目录")
    @PostMapping("admin/category/delete")
    @ResponseBody
    public ApiRestResponse deleteCategory(@RequestParam Integer id) {
        categoryService.delete(id);
        return ApiRestResponse.success();
    }

使用postman进行删除商品目录接口测试

java 电商源码 java电商项目模块介绍_spring_10

(六)查询后台商品分类的接口(难点)

在pom.xml中引入相关依赖

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>

在model下创建vo,vo是存储经过一定转化之后返回给前端的一个类

package com.hyb.mall.model.vo;
import com.hyb.mall.model.pojo.Category;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CategoryVO {
    private Integer id;
    private String name;
    private Integer type;
    private Integer parentId;
    private Integer orderNum;
    private Date createTime;
    private Date updateTime;
    private List<CategoryVO> childCategory = new ArrayList<>();

    public List<CategoryVO> getChildCategory() {
        return childCategory;
    }

    public void setChildCategory(List<CategoryVO> childCategory) {
        this.childCategory = childCategory;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    public Integer getOrderNum() {
        return orderNum;
    }

    public void setOrderNum(Integer orderNum) {
        this.orderNum = orderNum;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}

在dao层中categoryMapper创建查询商品列表的接口

List<Category> selectList();

在CategoryMapper.xml中编写查询列表的SQL语句

<select id="selectList" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from imooc_mall_category
  </select>

在categoryServiceImpl中实现查询商品目录列表并分类的业务逻辑

@Override
    public PageInfo listForAdmin(Integer pageNum, Integer pageSize) {
        //type是第一优先级,order_num是第二优先级
        PageHelper.startPage(pageNum, pageSize, "type,order_num");
        //做查询工作
        List<Category> categoryList = categoryMapper.selectList();
        //返回前端的类型
        PageInfo pageInfo = new PageInfo(categoryList);
        return pageInfo;
    }

在controller中编写给管理员看的商品目录列表

@ApiOperation("后台商品分类列表")
    @PostMapping("admin/category/list")
    @ResponseBody
    public ApiRestResponse listCategoryForAdmin(@RequestParam Integer pageNum,@RequestParam Integer pageSize){
        PageInfo pageInfo = categoryService.listForAdmin(pageNum, pageSize);
        return ApiRestResponse.success(pageInfo);
    }

使用postman进行接口测试,和预期一样,老铁没毛病

java 电商源码 java电商项目模块介绍_java_11

(七)给前台用户看的分类列表

在dao层定义接口

List<Category> selectCategoriesByParentId(Integer parentId);

在categoryMapper.xml中编写SQL语句

<select id="selectCategoriesByParentId" parameterType="int" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from imooc_mall_category
    where parent_id = #{parentId}
  </select>

在categoryServiceImpl中编写前台目录展示的业务代码

@Override
    public List<CategoryVO> listCategoryForCustomer(){
        ArrayList<CategoryVO> categoryVOList = new ArrayList<>();
        recursivelyFindCategories(categoryVOList,0);
        return categoryVOList;
    }
    private void recursivelyFindCategories(List<CategoryVO> categoryVOList,Integer parentId){
        //递归获取所有子类别,并组合成为一个目录树
        List<Category> categoryList = categoryMapper.selectCategoriesByParentId(parentId);
        if (!CollectionUtils.isEmpty(categoryList)) {
            for (int i = 0; i < categoryList.size(); i++) {
                Category category = categoryList.get(i);
                CategoryVO categoryVO = new CategoryVO();
                BeanUtils.copyProperties(category,categoryVO);
                categoryVOList.add(categoryVO);
                recursivelyFindCategories(categoryVO.getChildCategory(),categoryVO.getId());
            }
        }
    }

在categoryController中编写前台目录展示的接口

@ApiOperation("前台商品分类列表")
    @PostMapping("category/list")
    @ResponseBody
    public ApiRestResponse listCategoryForCustomer(){
        List<CategoryVO> categoryVOS = categoryService.listCategoryForCustomer();
        return ApiRestResponse.success(categoryVOS);
    }

使用postman进行测试,和预期一样

java 电商源码 java电商项目模块介绍_Redis_12

(八)使用springboot集成Redis,将目录存放在Redis中

在pom.xml中引入Redis依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

在application.properties中配置Redis

spring.redis.host=locahost
spring.redis.port=6379
spring.redis.password=

在MallApplication启动方法中加入@EnableCaching注解

在categoryServiceImpl中的listCategoryForCustomer方法添加@Cacheable(value = "listCategoryForCustomer")注解,//是spring所提供的

在config中创建Redis的配置类

package com.hyb.mall.config;

import java.time.Duration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;

/**
 * 描述:Redis的配置类
 */
@Configuration
@EnableCaching
public class CachingConfig {
    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {

        RedisCacheWriter redisCacheWriter = RedisCacheWriter
                .lockingRedisCacheWriter(connectionFactory);
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        cacheConfiguration = cacheConfiguration.entryTtl(Duration.ofSeconds(30));

        RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,
                cacheConfiguration);
        return redisCacheManager;
    }

}

使用postman进行测试,这个步骤比较恶心,我还踩了下坑,因为原来我的注解不是spring的

先在categoryServiceImpl的方法中打个断点

java 电商源码 java电商项目模块介绍_xml_13

进入postman,访问http://localhost:8080/category/list,第一次访问会比较慢,会进入断点,30秒后,或者取消这个断点,再次进行访问就会发现只用10几毫秒就能访问(我这个是38毫秒,直接访问数据库是100多毫秒),说明,存入到Redis成功

java 电商源码 java电商项目模块介绍_java 电商源码_14

还有一种方法就是打开Redis,输入keys *查看是否有数据输入,上面的注解value就显示到了Redis中。

java 电商源码 java电商项目模块介绍_java 电商源码_15