四.商品分类管理模块的开发
1.分类模块地主要功能
第一个,我们先要实现分类数据的设置,这个设置包括通常增删改。除了增删改之外,分类模块的一大重点就在于查找。查找的主要难点就在于他的分类的父一级目录查找和递归查找。比如说车厘子的父目录为进口水果,而进口水果的父目录为新鲜水果。而对于列表而言,他的顺序相反,他是先从形象水果开始递归。先查询到进口水果,再往下一层到车厘子。并且前后台展现是不一样的,后台商品平铺,前台要有层级分类。
2.后台新增分类功能的实现
我们先来实现后台新增分类。
要创建的代码和类如下,没有什么新知识点的类我就不进行解释了。
先要创建一个request包,用来存放请求参数的封装的类。里面新增一个AddCategoryReq类:
package com.haiexijun.mall.model.request; /** * 描述: 添加目录时的一个请求类 */ public class AddCategoryReq { private String name; private Integer type; private Integer parentId; private Integer 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; } }
CategoryMapper接口中新增一个selectByName方法,用来后面CategoryServiceImpl校验要添加的目录是否存在。
package com.haiexijun.mall.model.dao; import com.haiexijun.mall.model.pojo.Category; import org.springframework.stereotype.Repository; @Repository public interface CategoryMapper { int deleteByPrimaryKey(Integer id); int insert(Category record); int insertSelective(Category record); Category selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Category record); int updateByPrimaryKey(Category record); Category selectByName(String name); }
这个方法的xml映射为:
<select id="selectByName" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from mall_category where name=#{name,jdbcType=VARCHAR} </select>
然后下面是CategoryService和CategoryServiceImpl的相关代码:
package com.haiexijun.mall.service; import com.haiexijun.mall.exception.MallException; import com.haiexijun.mall.model.request.AddCategoryReq; /** * 商品分类目录Service */ public interface CateGoryService { void add(AddCategoryReq addCateGoryReq) throws MallException; }
package com.haiexijun.mall.service.impl; import com.haiexijun.mall.exception.MallException; import com.haiexijun.mall.exception.MallExceptionEnum; import com.haiexijun.mall.model.dao.CategoryMapper; import com.haiexijun.mall.model.pojo.Category; import com.haiexijun.mall.model.request.AddCategoryReq; import com.haiexijun.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 public void add(AddCategoryReq addCateGoryReq) throws MallException { Category category=new Category(); //通过spring提供的BeanUtils.copyProperties(),把addCateGoryReq拷贝到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.CREATE_FAILED); } } }
下面就是CategoryController的相关代码:
package com.haiexijun.mall.controller; import com.haiexijun.mall.common.ApiRestResponse; import com.haiexijun.mall.common.Constant; import com.haiexijun.mall.exception.MallException; import com.haiexijun.mall.exception.MallExceptionEnum; import com.haiexijun.mall.model.pojo.User; import com.haiexijun.mall.model.request.AddCategoryReq; import com.haiexijun.mall.service.CateGoryService; import com.haiexijun.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; //新增目录 // 传入一个Httpsession,判断用户是否登录了 @PostMapping("/admin/category/add") @ResponseBody public ApiRestResponse addCategory(HttpSession session,@RequestBody AddCategoryReq addCategoryReq){ // 判断传来的参数有没有空的 if (addCategoryReq.getName()==null||addCategoryReq.getOrderNum()==null||addCategoryReq.getType()==null||addCategoryReq.getParentId()==null){ //参数不能为空 return ApiRestResponse.error(MallExceptionEnum.PARAM_NOT_NULL); } //判断用户是否为管理员,只有管理员才能对后台目录进行操作 User currentUser=(User) session.getAttribute(Constant.MALL_USER); boolean adminRole=userService.checkAdminRole(currentUser); if (adminRole){ //是管理员 try { cateGoryService.add(addCategoryReq); } catch (MallException e) { e.printStackTrace(); } return ApiRestResponse.success(); }else { //不是管理员 return ApiRestResponse.error(MallExceptionEnum.NEED_ADMIN); } } }
3. 使用@Valid注解进行参数校验
之前我们controller里面是一个一个参数进行校验是否传入空值,这一点都不优雅。下面学习几个常用的用于参数校验的注解。
当然,@Valid校验肯定不止这么点注解,我们用到去查就行了。
首先第一个就是@Valid注解,一旦我们给某一个传入的参数加上这一个注解之后,那就意味着他需要校验。当加上这个注解之后,我们再去给他的字段加下面的3个注解。
下面我们来实际操作一下,这些注解如何使用,我们把之前校验目录传参是否为空进行改造一下:
先要导入一个依赖:
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.0.Final</version> </dependency>
一定要导入这个依赖不然会报错。
之后,就要在controller的请求参数前添加@Valid注解:
public ApiRestResponse addCategory(HttpSession session,@Valid @RequestBody AddCategoryReq addCategoryReq){····省略···}
我们还要在AddCategoryReq这个请求参数类中添加其他一些校验注解:
package com.haiexijun.mall.model.request; import javax.validation.constraints.Max; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * 描述: 添加目录时的一个请求类 */ public class AddCategoryReq { @Size(min = 2,max = 5,message = "name应该大于2位小于5位") @NotNull(message = "name不能为null") private String name; @NotNull(message = "type不能为null") @Max(value = 3,message = "type最大值为3") private Integer type; @NotNull(message = "parentId不能为null") private Integer parentId; @NotNull(message = "order不能为null") private Integer 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; } }
既然@Valid会帮助我们检查出参数的错误并抛出异常,但是异常结果会直接返回为系统异常。我们先要在GlobalExceptionHandler类中编写他的异常返回
package com.haiexijun.mall.exception; import com.haiexijun.mall.common.ApiRestResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.bind.BindResult; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.naming.Binding; import java.util.ArrayList; import java.util.List; /** * 描述 : 用于统一处理异常的类 */ //@ControllerAdvice的作用就是拦截异常的 @ControllerAdvice public class GlobalExceptionHandler { private final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class); //处理@Valid处理的异常 @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public ApiRestResponse HandleMethodArgumentNotValidException(MethodArgumentNotValidException e){ log.error("MethodArgumentNotValidException : "+e); return handleBindingResult(e.getBindingResult()); } private ApiRestResponse handleBindingResult(BindingResult result){ //把异常处理为对外暴露的提示 List<String> list=new ArrayList<String>(); if (result.hasErrors()) { List<ObjectError> allErrors= result.getAllErrors(); for (int i=0;i<allErrors.size();i++){ ObjectError objectError=allErrors.get(i); String message= objectError.getDefaultMessage(); list.add(message); } } if (list.size()==0){ //参数异常 return ApiRestResponse.error(MallExceptionEnum.REQUEST_PARAM_ERROR); }else { //@Valid的参数异常 return ApiRestResponse.error(MallExceptionEnum.REQUEST_PARAM_ERROR.getCode(),list.toString()); } } }
这样就可以了。