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中。
主要功能
- 商品的增删改查
- 分类的父一级目录、递归
主要会使用的新技术和重难点:
- swagger2的使用
- 以注解的方式进行参数校验
- 管理员的后天登录校验重构
- Redis的使用
- 统一鉴权
- 调试功能
涉及到的接口
- 增加目录分类(后台)
- 更新目录分类(后台)
- 删除分类(后台)
- 分类列表(后台)
- 分类列表(前台)
(一)开发添加商品分类目录的接口
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) {
= name;
this.type = type;
this.parentId = parentId;
this.orderNum = orderNum;
}
public String getName() {
return name;
}
public void setName(String 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进行接口的测试,添加新的商品类别,如下图:
categoryController中在// 1.判断商品类别这个判断语句中,过于冗长,阅读代码不好,需要进行优化重构
参数校验
- @Valid 需要校验
- NotNull 非空
- Max(value) 最大值
- 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发生异常
控制台打印的信息如下
但是我们可以发现,在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的异常,进行接口的测试,提示的错误由系统的错误改变成了更加直观易懂的错误提示,如果想在进行校验,或者更改校验信息,直接可以在实体类的属性中添加参数注解。
(二)使用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就成功了
(三)更新商品分类目录接口
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) {
= 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) {
= 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的名字来检查是否能进行同名操作
(四)统一校验管理员身份
经过在商品的分类目录中的添加方法和更新方法中,重复出现了校验管理员身份的代码,造成代码复用冗余,不易阅读的修改的情况,所以还需要对校验管理员身份再次进行迭代优化。
在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接口,返回的接口为空,则测试成功,用普通管理员身份登录,测报错,提示预期错误,则测试成功
(五)对商品分类目录进行删除接口的编写
在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进行删除商品目录接口测试
(六)查询后台商品分类的接口(难点)
在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) {
= 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进行接口测试,和预期一样,老铁没毛病
(七)给前台用户看的分类列表
在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进行测试,和预期一样
(八)使用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的方法中打个断点
进入postman,访问http://localhost:8080/category/list
,第一次访问会比较慢,会进入断点,30秒后,或者取消这个断点,再次进行访问就会发现只用10几毫秒就能访问(我这个是38毫秒,直接访问数据库是100多毫秒),说明,存入到Redis成功
还有一种方法就是打开Redis,输入keys *
查看是否有数据输入,上面的注解value就显示到了Redis中。