Spring Boot电商项目(五)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Spring Boot电商项目

4.Swagger自动生成API文档


下面我们简单学习一下如何使用Swagger自动生成API文档,这里只做简单的使用,并没有详细的教学。如果要看更详细的教程,请移步其他Swagger的入门教程。而且现在国内也有其他比Swagger好用的接口框架,不一定要用Swagger。


我先引入Swagger的相关依赖:


        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>


然后,要在MallApplication这个启动类的类名上面添加一个@EnableSwagger2注解和@EnableWebMvc注解。


之后,我们要对其进行简单的配置,我们新建一个包,叫做config,这个包专门用来存储我们的配置文件的。

在里面创建一个SpringFoxConfig类:


package com.haiexijun.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.haiexijun.mall.config;
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/");
    }
}


配置类这里已经写好了。我们要给controller里面哪个接口生成,就在接口的方法上添加@ApiOperation(“接口描述”)注解就行了,如@ApiOperation(“后台新增商品分类目录”)。


之后我们运行项目,然后访问http://localhost:8083/swagger-ui.html就可以查看到我们编写的接口文档了。我们还可以在里面输入参数调试。


e2d9d48986864e6294521f98c66e87f6.png


5.后台更新分类目录接口的开发


Controller的方法:


    @ApiOperation("后台更新分类目录")
    @PostMapping("/admin/category/update")
    @ResponseBody
    public ApiRestResponse updateCategory(@Valid @RequestBody UpdateCategoryReq updateCategoryReq,HttpSession session) throws MallException {
        User currentUser=(User) session.getAttribute(Constant.MALL_USER);
        if (currentUser==null){
            return ApiRestResponse.error(MallExceptionEnum.NEED_LOGIN);
        }
        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);
        }
    }


对应的Service方法:


    @Override
    public void update(Category updateCategory) throws MallException {
        //校验分类目录的名字是否有冲突
        if(updateCategory.getName()!=null){
            Category categoryOld=categoryMapper.selectByName(updateCategory.getName());
            if (categoryOld !=null && !categoryOld.getId().equals(updateCategory.getId())){
                //如果通过传入的信息查到的id存在,则抛出显示不允许重名
                throw new MallException(MallExceptionEnum.NAME_EXISTED);
            }
        }
        int count=categoryMapper.updateByPrimaryKeySelective(updateCategory);
        if (count==0){
            throw new MallException(MallExceptionEnum.UPDATE_FAILED);
        }
    }


6. 统一校验管理员身份


之前每一个接口都要写相同的代码来验证管理员,如果有非常多的接口要实现的话,那就会有很多的重复代码。这一节优化一下之前的验证管理员的代码。

我们要新建一个过滤器AdminFilter,编写如下的代码


package com.haiexijun.mall.filter;
import com.haiexijun.mall.common.ApiRestResponse;
import com.haiexijun.mall.common.Constant;
import com.haiexijun.mall.exception.MallExceptionEnum;
import com.haiexijun.mall.model.pojo.User;
import com.haiexijun.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
    UserService userService;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        HttpSession session=request.getSession();
        User currentUser=(User) session.getAttribute(Constant.MALL_USER);
        if (currentUser==null){
            PrintWriter out =new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
            out.write("{\n" +
                    "    \"status\": 10008,\n" +
                    "    \"msg\": \"用户未登录\",\n" +
                    "    \"data\": null\n" +
                    "}");
            out.flush();
            out.close();
            return;
        }
        boolean adminRole=userService.checkAdminRole(currentUser);
        if (adminRole){
            //如果是管理员登录,就放行
                filterChain.doFilter(servletRequest,servletResponse);
        }else {
            PrintWriter out =new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
            out.write("{\n" +
                    "    \"status\": 10010,\n" +
                    "    \"msg\": \"无管理员权限\",\n" +
                    "    \"data\": null\n" +
                    "}");
            out.flush();
            out.close();
            return;
        }
    }
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}


写好filter后,下一步就是配置使用这个filter,我们创建一个AdminFilterConfig.


package com.haiexijun.mall.config;
import com.haiexijun.mall.filter.AdminFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 描述:AdminFilter的配置
 */
@Configuration
public class AdminFilterConfig {
    @Bean
    public AdminFilter adminFilter(){
        return new AdminFilter();
    }
    @Bean(name = "adminFilterConf")
    public FilterRegistrationBean adminFilterConfig(){
        FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
        filterRegistrationBean.setFilter(adminFilter());
        filterRegistrationBean.addUrlPatterns("/admin/category/*");
        filterRegistrationBean.addUrlPatterns("/admin/product/*");
        filterRegistrationBean.addUrlPatterns("/admin/order/*");
        filterRegistrationBean.setName("adminFilterConfig");
        return filterRegistrationBean;
    }
}


7.删除分类目录接口


server新增加一个delete方法:


    @Override
    public void delete(Integer id) throws MallException {
        Category categoryOld=categoryMapper.selectByPrimaryKey(id);
        //如果没有查到记录
        if (categoryOld==null){
            throw new MallException(MallExceptionEnum.DELETE_FAILED);
        }
        int count=categoryMapper.deleteByPrimaryKey(id);
        if (count==0){
            throw new MallException(MallExceptionEnum.DELETE_FAILED);
        }
    }


然后编写controller:


    @ApiOperation("后台删除目录")
    @PostMapping("/admin/category/delete")
    @ResponseBody
    public ApiRestResponse deleteCategory(@RequestParam("id") Integer id) throws MallException {
        cateGoryService.delete(id);
        return ApiRestResponse.success();
    }


8.查询用户分类列表接口的开发


我们先来开发一下后台的查询用户分类列表接口:


分页先要引入pagehelper分页插件:


<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>


然后再application.properties文件中添加一行配置:


spring.main.allow-circular-references=true


之后就是编写相关代码就行了:

CategoryMapper新增一个selectList方法,然后给这个方法编写对应的xml映射:


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


之后就是要新建一个vo包,里面新建一个CategoryVO类,类里面只不过是比Category类多了一个List类型的属性:


package com.haiexijun.mall.vo;
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;
    }
}


之后就是Service里面的方法:


    @Override
    public PageInfo listForAdmin(Integer pageNum,Integer pageSize){
        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);
    }


这就编写好后台的目录分页查询的接口了。

下面要来编写用于前台展示的目录分页的接口:

要用到递归查询,下面是代码:

CategoryMapper新增一个selectCategoriesByParentId方法,并编写对应的xml映射:


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


然后就编写service类的两个方法:


    @Override
    public List<CategoryVO> listCategoryForCustomer(){
         ArrayList<CategoryVO> categoryVOList = new ArrayList<CategoryVO>();
         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());
            }
        }
    }


最后controller的方法:


    @ApiOperation("查询前台目录列表")
    @PostMapping("/category/list")
    @ResponseBody
    public ApiRestResponse listCategoryForCustomer(){
        List<CategoryVO> categoryVOS =cateGoryService.listCategoryForCustomer();
        return ApiRestResponse.success(categoryVOS);
    }


9.利用Redis缓存加速


考虑到目录变化其实不是很频繁,所以使用Redis缓存来提高我们整体的效率。这一节主要学习用Spring-boot来集成redis。

先引入两个我们要用到的依赖:


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-cache -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>


在引入好依赖之后,我们还要在application.properties文件中对redis进行配置,添加如下的配置:


spring.redis.host=192.168.172.129
spring.redis.port=6379
spring.redis.password=zc20020106

之后要在springboot的启动类的类名上添加如下的注解:


@EnableCaching


之后还要在我们希望用缓存的那个serviceImpl类的方法上面加上一个注解:


    @Override
    @Cacheable(value = "listCategoryForCustomer")
    public List<CategoryVO> listCategoryForCustomer(){
         ArrayList<CategoryVO> categoryVOList = new ArrayList<CategoryVO>();
         recursivelyFindCategories(categoryVOList,0);
         return categoryVOList;
    }


之后还要建一个redis的配置类CachingConfig:


package com.haiexijun.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;
/**
 * 描述:     缓存的配置类
 */
@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;
    }
}


还要给之前的CategoryVO类实现Serializable接口:


public class CategoryVO implements Serializable {················}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
25天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
38 2
|
2月前
|
设计模式 前端开发 Java
Spring MVC——项目创建和建立请求连接
MVC是一种软件架构设计模式,将应用分为模型、视图和控制器三部分。Spring MVC是基于MVC模式的Web框架,通过`@RequestMapping`等注解实现URL路由映射,支持GET和POST请求,并可传递参数。创建Spring MVC项目与Spring Boot类似,使用`@RestController`注解标记控制器类。
39 1
Spring MVC——项目创建和建立请求连接
|
2月前
|
Java 关系型数据库 MySQL
Maven——创建 Spring Boot项目
Maven 是一个项目管理工具,通过配置 `pom.xml` 文件自动获取所需的 jar 包,简化了项目的构建和管理过程。其核心功能包括项目构建和依赖管理,支持创建、编译、测试、打包和发布项目。Maven 仓库分为本地仓库和远程仓库,远程仓库包括中央仓库、私服和其他公共库。此外,文档还介绍了如何创建第一个 SpringBoot 项目并实现简单的 HTTP 请求响应。
142 1
Maven——创建 Spring Boot项目
|
2月前
|
Java 关系型数据库 MySQL
如何使用 maven 创建一个 Spring Boot项目
Maven 是一个强大的项目管理工具,通过配置 `pom.xml` 文件自动获取所需的 jar 包,提高开发效率。其核心功能包括项目构建和依赖管理。项目构建支持编译、测试、打包和发布等流程,而依赖管理则通过中央仓库、本地仓库和私有服务器获取和管理项目依赖。示例中展示了如何创建第一个 SpringBoot 项目并实现简单接口。
40 1
如何使用 maven 创建一个 Spring Boot项目
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
65 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
2月前
|
Java 应用服务中间件 Android开发
Eclipse创建Spring项目
本文介绍了在Eclipse中创建Spring项目的步骤,包括如何配置Tomcat服务器、创建项目、部署项目到Tomcat以及添加Spring框架所需的JAR包。
71 1
Eclipse创建Spring项目
|
2月前
|
Java Apache Maven
Java/Spring项目的包开头为什么是com?
本文介绍了 Maven 项目的初始结构,并详细解释了 Java 包命名惯例中的域名反转规则。通过域名反转(如 `com.example`),可以确保包名的唯一性,避免命名冲突,提高代码的可读性和逻辑分层。文章还讨论了域名反转的好处,包括避免命名冲突、全球唯一性、提高代码可读性和逻辑分层。最后,作者提出了一个关于包名的问题,引发读者思考。
Java/Spring项目的包开头为什么是com?
|
2月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
60 2
|
2月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
204 2
|
2月前
|
前端开发 安全 Java
【Spring】Spring Boot项目创建和目录介绍
【Spring】Spring Boot项目创建和目录介绍
87 2