Spring Boot电商项目(五)

简介: 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 {················}
相关文章
|
8月前
|
人工智能 Java 数据库
飞算 JavaAI:革新电商订单系统 Spring Boot 微服务开发
在电商订单系统开发中,传统方式耗时约30天,需应对复杂代码、调试与测试。飞算JavaAI作为一款AI代码生成工具,专注于简化Spring Boot微服务开发。它能根据业务需求自动生成RESTful API、数据库交互及事务管理代码,将开发时间缩短至1小时,效率提升80%。通过减少样板代码编写,提供规范且准确的代码,飞算JavaAI显著降低了开发成本,为软件开发带来革新动力。
|
8月前
|
前端开发 安全 Java
Spring Boot 便利店销售系统项目分包设计解析
本文深入解析了基于Spring Boot的便利店销售系统分包设计,通过清晰的分层架构(表现层、业务逻辑层、数据访问层等)和模块化设计,提升了代码的可维护性、复用性和扩展性。具体分包结构包括`controller`、`service`、`repository`、`entity`、`dto`、`config`和`util`等模块,职责分明,便于团队协作与功能迭代。该设计为复杂企业级应用开发提供了实践参考。
341 0
|
5月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
537 3
|
5月前
|
Java 测试技术 Spring
简单学Spring Boot | 博客项目的测试
本内容介绍了基于Spring Boot的博客项目测试实践,重点在于通过测试驱动开发(TDD)优化服务层代码,提升代码质量和功能可靠性。案例详细展示了如何为PostService类编写测试用例、运行测试并根据反馈优化功能代码,包括两次优化过程。通过TDD流程,确保每项功能经过严格验证,增强代码可维护性与系统稳定性。
270 0
|
5月前
|
存储 Java 数据库连接
简单学Spring Boot | 博客项目的三层架构重构
本案例通过采用三层架构(数据访问层、业务逻辑层、表现层)重构项目,解决了集中式开发导致的代码臃肿问题。各层职责清晰,结合依赖注入实现解耦,提升了系统的可维护性、可测试性和可扩展性,为后续接入真实数据库奠定基础。
492 0
|
5月前
|
前端开发 Java API
酒店管理系统基于 JavaFX Spring Boot 和 React 经典项目重构实操
本文介绍了基于现代技术栈的酒店管理系统开发方案,整合了JavaFX、Spring Boot和React三大技术框架。系统采用前后端分离架构,JavaFX构建桌面客户端,React开发Web管理界面,Spring Boot提供RESTful API后端服务。核心功能模块包括客房管理和客户预订流程,文中提供了JavaFX实现的客房管理界面代码示例和React开发的预订组件代码,展示了如何实现客房信息展示、添加修改操作以及在线预订功能。
355 1
|
5月前
|
Java 应用服务中间件 Maven
第01课:Spring Boot开发环境搭建和项目启动
第01课:Spring Boot开发环境搭建和项目启动
921 0
|
8月前
|
SQL 前端开发 Java
深入理解 Spring Boot 项目中的分页与排序功能
本文深入讲解了在Spring Boot项目中实现分页与排序功能的完整流程。通过实际案例,从Service层接口设计到Mapper层SQL动态生成,再到Controller层参数传递及前端页面交互,逐一剖析每个环节的核心逻辑与实现细节。重点包括分页计算、排序参数校验、动态SQL处理以及前后端联动,确保数据展示高效且安全。适合希望掌握分页排序实现原理的开发者参考学习。
574 4
|
8月前
|
Java Spring 容器
两种Spring Boot 项目启动自动执行方法的实现方式
在Spring Boot项目启动后执行特定代码的实际应用场景中,可通过实现`ApplicationRunner`或`CommandLineRunner`接口完成初始化操作,如系统常量或配置加载。两者均支持通过`@Order`注解控制执行顺序,值越小优先级越高。区别在于参数接收方式:`CommandLineRunner`使用字符串数组,而`ApplicationRunner`采用`ApplicationArguments`对象。注意,`@Order`仅影响Bean执行顺序,不影响加载顺序。
654 2