spring boot 完整后端接口案例

spring boot 完整后端接口案例_第1张图片

 

第一章 会员管理项目父模块搭建

1.1 创建模块mengxuegu-member

 mengxuegu-member 作为所有工程的父工程,用于管理项目的所有依赖。

spring boot 完整后端接口案例_第2张图片

 1.2 添加pom依赖,pom.xml

  文件位于:会员管理系统/03-配套资料/pom文件/member-pom.xml



    4.0.0

    com.cc
    cc-member
    1.0-SNAPSHOT
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.0.RELEASE
    

    
    
        3.3.2
        1.1.21
        1.2.8
        2.6
        3.2.2
        2.6
        UTF-8
        1.8
    

    
    
        
        
            com.baomidou
            mybatis-plus-boot-starter
            ${mybatis-plus.version}
        
        
            mysql
            mysql-connector-java
        
        
        
            org.projectlombok
            lombok
        
        
        
            com.alibaba
            druid-spring-boot-starter
            ${druid.version}
        

        
        
            org.springframework.security
            spring-security-crypto
        

        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

        
        
            com.alibaba
            fastjson
            ${fastjson.version}
        
        
            commons-lang
            commons-lang
            ${commons-lang.version}
        
        
            commons-collections
            commons-collections
            ${commons-collections.version}
        
        
            commons-io
            commons-io
            ${commons-io.version}
        
    


第二章 公共工具模块搭建 

用于管理通用的工具类 

2.1  创建模块 mengxuegu-member-util

spring boot 完整后端接口案例_第3张图片 (点击父模块,创建子模块)

 2.2 添加自定义的日志模块配置

   1.logback.xml 日志配置文件添加到 resources 目录下

spring boot 完整后端接口案例_第4张图片




    
    
    
    
    
    
    
    
    
        
                ${CONSOLE_LOG_PATTERN}
        
    
    
        
    

   文件位于:会员管理系统/03-配套资料/logback.xml

2.3 整合 Lombok

Lombok 介绍

官方网址: https://www.projectlombok.org/features/all

Lombok 工具提供一系列的注解,使用这些注解可以不用定义gettersetterequals、构造方法等,可以消除java代码的臃肿,它会在编译时在字节码文件自动生成这些通用的方法,简化开发 人员的工作

spring boot 完整后端接口案例_第5张图片

  Lombok 使用

 1.我们在父模块中已经添加了相关的依赖,无需再次添加 

 
        
            org.projectlombok
            lombok
        

2.IDEA 安装 lombok 插件

    作用: 使用IDEA开发时,使用 Lombok 注解生成方法不报错

    先点击file->settings 

2.4 规范统一响应枚举 ResultEnum

 ResultEnum 枚举类是为了搭配 Result 规范响应的结果。 

mengxuegu-member-util 模块创建 com.mengxuegu.member.base.ResultEnum 响应结果枚举

spring boot 完整后端接口案例_第6张图片

package com.cc.member.base;


import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ResultEnum {

    // 成功
    SUCCESS(2000, "成功"),
    // 错误
    ERROR(999, "错误");

    private Integer code;

    public String desc;

}

   文件位于:会员管理系统/03-配套资料/工具类/ResultEnum.java

 2.5 规范统一响应结果 Result

 1.说明:为了规范响应的结果,创建一个Result 类来统一响应JSON格式:

    code 操作代码、flag 是否成功、message 提示信息、 data 自定义数据。spring boot 完整后端接口案例_第7张图片

 2. 在 mengxuegu-member-util 创建com.mengxuegu.member.base.Result用于封装接口统一响应结果

spring boot 完整后端接口案例_第8张图片

package com.cc.member.base;


import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;

/**
 * 用于封装接口统一响应结果
 */
@Data
public class Result implements Serializable {

    private static final Logger logger = LoggerFactory.getLogger(Result.class);

    private static final long serialVersionUID = 1L;

    /**
     * 响应业务状态码
     */
    private Integer code;

    /**
     * 是否正常
     */
    private Boolean flag;

    /**
     * 响应信息
     */
    private String message;

    /**
     * 响应中的数据
     */
    private Object data;

    public Result(Integer code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
        this.flag = code == ResultEnum.SUCCESS.getCode() ? true: false;
    }

    public static Result ok() {
        return new Result(ResultEnum.SUCCESS.getCode(),  ResultEnum.SUCCESS.getDesc(), null);
    }

    public static Result ok(Object data) {
        return new Result(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getDesc(), data);
    }

    public static Result ok(String message, Object data) {
        return new Result(ResultEnum.SUCCESS.getCode(), message, data);
    }

    public static Result error(String message) {
        logger.debug("返回错误:code={}, message={}", ResultEnum.ERROR.getCode(), message);
        return new Result(ResultEnum.ERROR.getCode(), message, null);
    }

    public static Result build(int code, String message) {
        logger.debug("返回结果:code={}, message={}", code, message);
        return new Result(code, message, null);
    }

    public static Result build(ResultEnum resultEnum) {
        logger.debug("返回结果:code={}, message={}", resultEnum.getCode(), resultEnum.getDesc());
        return new Result(resultEnum.getCode(), resultEnum.getDesc(), null);
    }

    public String toString() {
        return JSON.toJSONString(this);
    }


}


第三章 API接口模块搭建

(与第二章工具模块同一级) 

  编写会员管理系统业务逻辑并向外提供 RESTful 风格接口给前端调用

3.1 创建接口模块

  创建mengxuegu-member-api 接口模块

3.2 配置接口模块中的pom.xml

 mengxuegu-member-api pom.xml 中添加 第二章的工具模块 和 web启动器依赖。



    
        cc-member
        com.cc
        1.0-SNAPSHOT
    
    4.0.0

    cc-member-api

    

        
        
            cc-member-util
            com.cc
            1.0-SNAPSHOT
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        
    

    

        
            
                
                src/main/java
                
                    **/*.xml
                
            
            
                src/main/resources
            
        

        
            
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                    com.cc.member.MemberApplication
                
            
        


    


 位于:会员管理系统/03-配套资料/pom文件/api-pom.xml

3.3 创建启动类

 mengxuegu-member-api 模块的src/main/java下创建   com.mengxuegu.member.MemberApplication

package com.cc.member;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

 3.4 配置application.yml

  接口模块下的resources下创建

spring boot 完整后端接口案例_第9张图片

 application.yml

server:
  port: 6666

# 数据源配置
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/mxg_member?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    #mysql8版本以上驱动包指定新的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    #   数据源其他配置, 在 DruidConfig配置类中手动绑定
    initialSize: 8
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL

3.5 创建数据库

1.创建mxg_member数据库

2.导入数据库脚本:文件位于 会员管理系统/03-配套资料/mxg_member.sql

3.6 创建会员管理启动类 Controller

spring boot 完整后端接口案例_第10张图片

  MemberController.java

package com.cc.member.controller;

import com.cc.member.base.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;



@RestController
@RequestMapping("/member")
public class MemberController {
    Logger logger= LoggerFactory.getLogger(getClass());

    @PostMapping("/list/search/{page}/{size}")
    public Result search(@PathVariable("page") long page,@PathVariable("size") long size){
      logger.info("分类查询列表参数,page={},size={}",page,size);
      return Result.ok();
    }

}

@PathVariable注解的作用是从URL路径中获取参数并将其绑定到控制器方法的参数上。它可以用来捕获URL中的占位符并将其传递给控制器方法。

3.7 测试

启动项目,使用 postman 发送 POST 请求,访问 localhost:6666/member/list/search/1/20 


第四章 整合 Mybatis-plus

 官网地址:Redirect 

4.1 创建 Mybatis-Plus 配置类 

api模块中创建config包,config/MybatisPlusConfig

spring boot 完整后端接口案例_第11张图片

 添加 Mybatis-Plus 配置类开启事务管理、Mapper接口扫描、 分页功能。 config/MybatisPlusConfig.java 代码:

package com.cc.member.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement // 开启事务管理
@MapperScan("com.cc.member.mapper") // 扫描mapper接口
@Configuration
public class MybatisPlusConfig {

    /**
     * 分页插件
     * @return
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }

}

创建 com.mengxuegu.member.entity com.mengxuegu.member.mapper

4.2编译 xxxMapper.xml 文件 

我们将 xxxMapper.xml 会放到 src/main/java 目录下,当文件编译时,默认情况下不会将 Mapper.xml文件编译到 classes 中,需要指定 **/*.xml,编译打包时,将xml一起打包。

mengxuegu-member-api/pom.xml 添加如下 resources 标签: (前期已经添加)



    
        cc-member
        com.cc
        1.0-SNAPSHOT
    
    4.0.0

    cc-member-api

    

        
        
            cc-member-util
            com.cc
            1.0-SNAPSHOT
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        
    

    

        
            
                
                src/main/java
                
                    **/*.xml
                
            
            
                src/main/resources
            
        

        
            
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                    com.cc.member.MemberApplication
                
            
        


    


 添加后记得pom.xml 文件中任意地方右击,Maven > reimport 才会生效

spring boot 完整后端接口案例_第12张图片


第五章 会员管理服务端-分页条件查询

 需求:通过会员姓名、卡号、支付类型、会员生日 条件查询列表数据,并实现分页功能。

5.1创建会员实体类 Member

spring boot 完整后端接口案例_第13张图片  Member.java 代码

package com.cc.member.entity;


import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Date;

/**
 * 会员信息表对应实体类
 *
 */
@Accessors(chain = true)
@Data
//当数据库名与实体类名不一致或不符合驼峰命名时,需要在此注解指定表名(不加这个注解默认将实体类的小写形式在db中寻找)
@TableName("tb_member")
public class Member implements Serializable {

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 会员卡号
     */
    private String cardNum;

    /**
     * 会员名字
     */
    private String name;

    /**
     * 生日
     */
    private Date birthday;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 可用积分
     */
    private Integer integral;

    /**
     * 可用金额
     */
    private Double money;

    /**
     * 支付类型('1'现金, '2'微信, '3'支付宝, '4'银行卡)
     */
    private String payType;

    /**
     * 会员地址
     */
    private String address;
}

使用到了Lombok

5.2 创建会员请求类 MemberREQ

REQ:作为 request 简写,主要作用是把将查询条件请求参数封装为一个对象。比如:会员姓名、卡号、支付类型、会员生日 作为条件,查询出对应分类数据。

spring boot 完整后端接口案例_第14张图片  MemberReq.java 代码

package com.cc.member.req;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 接收会员查询条件
 */
@Data
public class MemberREQ implements Serializable {

    /**
     * 会员卡号
     */
    private String cardNum;

    /**
     * 会员名字
     */
    private String name;

    /**
     * 生日
     */
    private Date birthday;

    /**
     * 支付类型('1'现金, '2'微信, '3'支付宝, '4'银行卡)
     */
    private String payType;
}

5.3 编写 MemberMapper

注:mapper对表的数据操作;

1.创建接口 com.mengxuegu.member.mapper.MemberMapper 继承 BaseMapper接口。MyBatis-plus的 BaseMapper接口提供了很多对T表的数据操作方法

spring boot 完整后端接口案例_第15张图片

 接口MemberMapper的页面代码:

package com.cc.member.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cc.member.entity.Member;


/**
 * 会员信息表的Mapper接口
 */

public interface MemberMapper extends BaseMapper {

}

2.java目录下创建映射文件 com/mengxuegu/member/mapper/xml/MemberMapper.xml

spring boot 完整后端接口案例_第16张图片

 MemberMapper.xml代码





5.4 创建业务层

 创建接口 com.mengxuegu.member.service.IMemberService 继承 IService接口

 实现Iservice接口,提供了常用更复杂的对T数据表的操作,比如:支持Lambda表达式,批量   删除、自动新增或更新操作等方法

1.定义一个通过分页条件查询方法search

spring boot 完整后端接口案例_第17张图片

 IMemberService接口代码:

package com.cc.member.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.cc.member.base.Result;
import com.cc.member.entity.Member;
import com.cc.member.req.MemberREQ;



public interface IMemberService extends IService {

    /**
     * 分布条件查询
     * @param req 查询条件
     * @return
     */
    Result search(long page, long size, MemberREQ req);

   
}

  2.创建一个实体类 com.memgxuegu.member.service.impl.MemberServiceImpl 继承         ServiceImpl类,并且实现IMemberService接口

 ServiceImpl,T>是对Iservice 接口中方法的实现

第一个泛型M指定继承了BaseMapper接口的子接口

第二个泛型T指定实体类

注意:类上不要少@Service(不添加会报错),baseMapper引用的就是MemberMapper实例

spring boot 完整后端接口案例_第18张图片

MemberServiceImpl代码 

使用到了mybatis:   CRUD 接口 | MyBatis-PlusMyBatis-Plus 官方文档https://www.baomidou.com/pages/49cc81/#list

package com.cc.member.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cc.member.base.Result;
import com.cc.member.entity.Member;
import com.cc.member.mapper.MemberMapper;
import com.cc.member.req.MemberREQ;
import com.cc.member.service.IMemberService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

@Service
public class MemberServiceImpl extends ServiceImpl implements IMemberService {
    @Override
    public Result search(long page, long size, MemberREQ req) {
        //        封装查询条件
        QueryWrapper query=new QueryWrapper();

        if(req!=null){
            if(StringUtils.isNotBlank(req.getName())){
                query.like("name",req.getName());
            }

            if(StringUtils.isNotBlank(req.getCardNum())){
                query.like("card_num",req.getCardNum());
            }

            if(req.getBirthday()!=null){
                query.eq("birthday",req.getBirthday());
            }

            if(StringUtils.isNotBlank(req.getPayType())){
                query.eq("pay_type",req.getPayType());
            }
        }
        IPage p=new Page<>(page,size);
        IPage data=baseMapper.selectPage(p,query);
        return Result.ok(data);
    }
}

5.5 创建控制层

创建控制层类 com.mengxuegu.member.controller.MemberController

MemberController.java代码:

package com.cc.member.controller;

import com.cc.member.base.Result;
import com.cc.member.req.MemberREQ;
import com.cc.member.service.impl.MemberServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/member")
public class MemberController {
    Logger logger= LoggerFactory.getLogger(getClass());


    @Autowired
    private MemberServiceImpl memberService;

    @PostMapping("/list/search/{page}/{size}")
    public Result search(@PathVariable("page") long page, @PathVariable("size") long size,@RequestBody(required = false) MemberREQ req){
      logger.info("分类查询列表参数,page={},size={}",page,size);
      return memberService.search(page,size,req);
    }

}

 5.6 启动测试

 发送 POST 请求,访问 localhost:6666/member/list/search/1/20 spring boot 完整后端接口案例_第19张图片

出现问题: 

 项目启动报错:The bean ‘xxxServiceImpl’ could not be injected as a ‘cn.xxxx.service.xxxServiceImpl’ because it is a JDK dynamic proxy that implements:
 解释:大概意思是默认采用jdk动态代理因为动态代理需要类实现接口,通过接口进行代理,但是我们通过 @Autowired 注解进行注入bean的时候采用了它的实现类而没有采用它的接口

 解决方法:在启动类事务注解上添加proxyTargetClass=true,强迫事务使用CGLib代理方式。
@EnableTransactionManagement(proxyTargetClass=true) 

spring boot 完整后端接口案例_第20张图片

5.7 封装Page 分页类--(可以不需要)

 主要是做什莫:前端要求返回 rows, 而默认是在 records 中,下面自定义一个 Page 类继承  Mybatis-Plus 的提供的Page类,将数据返回到rows里面,不在乎这些接口的不需要去看。

1.mengxuegu-member-util 模块中创建 com.mengxuegu.member.base.Page

spring boot 完整后端接口案例_第21张图片

Page.java代码

package com.cc.member.base;

import lombok.Data;

import java.util.List;

/**
 * 因为前端分页数据是 rows ,不是recodrs,在这里转换下
 * @param 
 */
@Data
public class Page extends com.baomidou.mybatisplus.extension.plugins.pagination.Page {

    /**
     * 因为前端分页数据是 rows ,不是recodrs
     * @return
     */
    public List getRows() {
        // 调用父类的
        return super.getRecords();
    }

    public List getRecords() {
        return null;
    }

    public Page(long current, long size) {
        super(current, size);
    }

}

2.将 MemberServiceImpl 类导入的

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;替换import com.baomidou.mybatisplus.core.metadata.IPage;

spring boot 完整后端接口案例_第22张图片

 3.重启项目,重新发送 POST 请求 localhost:6666/member/list/search/1/20

spring boot 完整后端接口案例_第23张图片


第六章 会员管理服务端-增删改查

6.1 新增和删除会员 

新增与删除会员只要对 tb_member 单表操作,并且我们可以直接使用 mybatis-plus 提供的 IMemberService 方法进行操作即可。

 ctrl+鼠标点击 IService可以看到分装好的方法

添加控制层方法

com.mengxuegu.member.controller.MemberController 类中添加 add delete 方法:

spring boot 完整后端接口案例_第24张图片 代码:


//    添加会员
    @PostMapping // /member
    public  Result add(@RequestBody Member member){
        boolean b=memberService.save(member);
        if(b) {
            return Result.ok();
        }
        return Result.error("新增会员信息失败");

    }

//    删除会员
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable("id") int id){
       boolean b= memberService.removeById(id);
       if(b){
           return  Result.ok();
       }
       return Result.error("删除会员信息失败");
    }

 测试:

1.新增会员,发送 POST 请求 localhost:6666/member

 spring boot 完整后端接口案例_第25张图片

 2.删除会员,发送 DELETE 请求 localhost:6666/member/2

spring boot 完整后端接口案例_第26张图片

 6.2 查询和修改会员

添加控制层方法

com.mengxuegu.member.controller.MemberController 类中添加 get update 方法:


//    通过id查询详情
    @GetMapping("/{id}")
    public Result getById( @PathVariable("id") int id){
       Member mem= memberService.getById(id);
       if(mem!=null){
           return Result.ok(mem);
       }
       return Result.error("查询失败");
    }

    @PutMapping("/{id}")
    public Result update(@PathVariable("id") int id,@RequestBody Member m){
        if(m.getId()==null){
            m.setId(id);
        }
        boolean b=memberService.updateById(m);
        if(b){
            return  Result.ok();
        }
        return  Result.error("修改失败");
    }

测试:

 ​​​​​​1.查询会员详情,发送 GET 请求 localhost:6666/member/2

 2.修改会员,发送 PUT 请求 localhost:6666/member/2

spring boot 完整后端接口案例_第27张图片

 另一种修改会员的方法:(相当于自己service封装了方法,上一个方法相当于直接调用人家封装好的方法)

com.mengxuegu.member.service.IMemberService添加 update 接口方法

    Result update(int id,Member member);

 com.mengxuegu.member.service.impl.MemberServiceImpl 实现 update 方法

package com.cc.member.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cc.member.base.Page;
import com.cc.member.base.Result;
import com.cc.member.entity.Member;
import com.cc.member.mapper.MemberMapper;
import com.cc.member.req.MemberREQ;
import com.cc.member.service.IMemberService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

@Service
public class MemberServiceImpl extends ServiceImpl implements IMemberService {
    @Override
    public Result search(long page, long size, MemberREQ req) {
        //        封装查询条件
        QueryWrapper query=new QueryWrapper();

        if(req!=null){
            if(StringUtils.isNotBlank(req.getName())){
                query.like("name",req.getName());
            }

            if(StringUtils.isNotBlank(req.getCardNum())){
                query.like("card_num",req.getCardNum());
            }

            if(req.getBirthday()!=null){
                query.eq("birthday",req.getBirthday());
            }

            if(StringUtils.isNotBlank(req.getPayType())){
                query.eq("pay_type",req.getPayType());
            }
        }
        IPage p=new Page<>(page,size);
        IPage data=baseMapper.selectPage(p,query);
        return Result.ok(data);
    }

//    修改会员
    @Override
    public Result update(int id, Member member) {
        if(member.getId()==null){
            member.setId(id);
        }
        int b=baseMapper.updateById(member);
        if(b<1){
            return Result.error("修改失败");
        }
        return Result.ok();
    }
}

memberController.java中运行

  @PutMapping("/{id}")
    public Result update(@PathVariable("id") int id,@RequestBody Member m){

        return memberService.update(id,m);
    }


第七章 MyBatis-plus 代码生成器

7.1创建代码生成器模块 mengxuegu-member-generator 

选中父模块,new-module 

spring boot 完整后端接口案例_第28张图片

 7.2 生成器模块中添加 pom.xml依赖

  文件位于:会员管理系统/03-配套资料/pom文件/generator-pom.xml



    
        cc-member
        com.cc
        1.0-SNAPSHOT
    
    4.0.0

    cc-member-generator
    
        
        
            com.baomidou
            mybatis-plus-generator
            3.3.1
        
        
        
            org.freemarker
            freemarker
        
    

7.3 编写自动生成器代码

代码生成器模块中创建类 com.mengxuegu.generator.CodeGenerator 

spring boot 完整后端接口案例_第29张图片

 代码:

package com.cc.generator;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.commons.lang.StringUtils;

import java.util.Scanner;

public class CodeGenerator {


    // 生成的代码放到哪个工程中
    private static String PROJECT_NAME = "cc-member-api";

    // 数据库名称
    private static String DATABASE_NAME = "mxg-member";

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/"+ DATABASE_NAME +"?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useTimezone=true");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir") + "/";
        gc.setOutputDir(projectPath + PROJECT_NAME +"/src/main/java");
        gc.setIdType(IdType.AUTO); // 分布式id
        gc.setAuthor("cc-www.cc.com");
        gc.setFileOverride(true); //覆盖现有的
        gc.setOpen(false); //是否生成后打开
        gc.setDateType(DateType.ONLY_DATE);
//        gc.setSwagger2(true); //实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.cc.member"); //父包名
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true); //使用lombok
        strategy.setEntitySerialVersionUID(true);// 实体类的实现接口Serializable
        strategy.setRestControllerStyle(true); // @RestController
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("tb_"); // 去掉表前缀
        mpg.setStrategy(strategy);

        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }


    /**
     * 

* 读取控制台内容 *

*/ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } }

代码要进行一定的修改

spring boot 完整后端接口案例_第30张图片

 

 spring boot 完整后端接口案例_第31张图片

 7.4 生成供应商&商品&员工管理代码

1. 右键执行 main 方法,控制台输入表名回车,会自动生成对应项目目录中

spring boot 完整后端接口案例_第32张图片

spring boot 完整后端接口案例_第33张图片 最终效果:(自动生成的)

spring boot 完整后端接口案例_第34张图片


第八章 供应商管理服务端-分页条件查询

8.1 创建供应商请求类 SupplierREQ 

spring boot 完整后端接口案例_第35张图片

 代码:

package com.cc.member.req;

import lombok.Data;

import java.io.Serializable;


@Data
public class SupplierREQ implements Serializable {

    /**
     * 供应商名称
     */
    private String name;

    /**
     * 联系人
     */
    private String linkman;

    /**
     * 联系电话
     */
    private String mobile;

}

8.2 编写业务层

1.service编写方法

spring boot 完整后端接口案例_第36张图片

 代码:(定义分页条件查询方法)

package com.cc.member.service;

import com.cc.member.base.Result;
import com.cc.member.entity.Supplier;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cc.member.req.SupplierREQ;

/**
 * 

* 供应商信息表 服务类 *

* * @author cc-www.cc.com * @since 2023-05-18 */ public interface ISupplierService extends IService { // 分页 public Result search(int page, int size, SupplierREQ req); }

2.serviceImpl实现创建的方法

 代码:

注意:Page导包要导入自定义的 import com.mengxuegu.member.base.Page;

package com.cc.member.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cc.member.base.Page;
import com.cc.member.base.Result;
import com.cc.member.entity.Supplier;
import com.cc.member.mapper.SupplierMapper;
import com.cc.member.req.SupplierREQ;
import com.cc.member.service.ISupplierService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

/**
 * 

* 供应商信息表 服务实现类 *

* * @author cc-www.cc.com * @since 2023-05-18 */ @Service public class SupplierServiceImpl extends ServiceImpl implements ISupplierService { @Override public Result search(int page, int size, SupplierREQ req) { QueryWrapper query=new QueryWrapper(); if(req!=null){ if(StringUtils.isNotBlank(req.getName())){ query.like("name",req.getName()); } if(StringUtils.isNotBlank(req.getLinkman())) { query.like("linkman", req.getLinkman()); } if(StringUtils.isNotBlank(req.getMobile())) { query.like("mobile", req.getMobile()); } } // 分装分页 IPage p=new Page<>(page,size); IPagedata= baseMapper.selectPage(p,query); return Result.ok(data); } }

8.3 编写控制层

spring boot 完整后端接口案例_第37张图片

 代码:

package com.cc.member.controller;


import com.cc.member.base.Result;
import com.cc.member.req.SupplierREQ;
import com.cc.member.service.impl.SupplierServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 

* 供应商信息表 前端控制器 *

* * @author cc-www.cc.com * @since 2023-05-18 */ @RestController @RequestMapping("/supplier") public class SupplierController { Logger logger= LoggerFactory.getLogger(getClass()); @Autowired private SupplierServiceImpl supplierService; // 分页 @PostMapping("/list/search/{page}/{size}") public Result search(@PathVariable("page") int page,@PathVariable("size") int size,@RequestBody SupplierREQ req){ logger.info("分页数据:{},{}",page,size); return supplierService.search(page,size,req); } }

8.4 启动测试

分页条件查询供应商列表,发送 POST 请求 localhost:6666/supplier/list/search/1/20 

spring boot 完整后端接口案例_第38张图片


 第九章 供应商管理服务端-增删改查

9.1 新增供应商 

供应商只要对 tb_supplier 单表操作,并且我们可以直接使用 mybatis-plus 提供的 ISupplierService 方法进行操作即可。 

1.添加控制层方法

spring boot 完整后端接口案例_第39张图片

部分代码:


//    新增
    @PostMapping
    public Result add(@RequestBody Supplier supplier){
        boolean b=supplierService.save(supplier);
        if(b){
            return Result.ok();
        }
        return  Result.error("新增失败");
    }

2.测试 

spring boot 完整后端接口案例_第40张图片

spring boot 完整后端接口案例_第41张图片

 9.2 删除供应商(需要关联查询)

 通过供应商id删除供应商数据,在删除前判断该供应商是否已经被  tb_goods  商品引用了,如果 被引用则不允许删除。

编写商品的业务层

 通过供应商id查询商品表 tb_goods 是否存在数据,存在则供应商被引用

 1.在商品的业务层定义一个 方法 

spring boot 完整后端接口案例_第42张图片 代码:

//    通过供应商的id查询商品列表
    List getBySupplier(int supplierId);

 2.实现商品的业务层

spring boot 完整后端接口案例_第43张图片

代码 


    @Override
    public List getBySupplier(int supplierId) {
        QueryWrapper query=new QueryWrapper();
        query.eq("supplier_id", supplierId);
         return baseMapper.selectList(query);

    }

使用 supplier_id的原因是:数据可tb_goods中的字段是supplier_id,要相对应

编写供应商业务层 

 1.定义方法spring boot 完整后端接口案例_第44张图片

 代码:

//    删除供应商(需要关联表,所以重新编写业务层的方法)
    Result deleteSupplier(int id);

2.实现方法

spring boot 完整后端接口案例_第45张图片

 代码:


//    删除供应商

    @Autowired
    private IGoodsService iGoodsService;
    @Override
    public Result deleteSupplier(int id) {
      // 1. 通过供应商id查询是否被商品引用,
       List goodsListBySupplier= iGoodsService.getBySupplier(id);
      // 2. 如果被商品引用,则不让删除供应商
       if(CollectionUtils.isNotEmpty(goodsListBySupplier)){
           return Result.error("供应商有关联的商品,不允许被删除");
       }
        // 3. 如果没有被引用,直接删除
      int b =baseMapper.deleteById(id);
       if(b<1){
           return Result.error("删除供应商失败");
       }
        return Result.ok();
    }

 需要调用商品业务层的方法(查看供应商是否与商品相关联,注意:IGoodsService不是 GoodsServiceImpl),调用的是方法,而不是关注方法是如何实现的;

 编写控制层

spring boot 完整后端接口案例_第46张图片 代码:


//    删除供应商
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable("id") int id){
        return supplierService.deleteSupplier(id);
    }

 测试

spring boot 完整后端接口案例_第47张图片

9.3 查询和修改供应商

查询供应商 

单表查询,只需要控制层

spring boot 完整后端接口案例_第48张图片 代码:


//    查询供应商
    @GetMapping("/{id}")
    public Result GetById(@PathVariable("id") int id){
        Supplier supplier=supplierService.getById(id);
        return Result.ok(supplier);

    }

 测试:

spring boot 完整后端接口案例_第49张图片

 修改供应商

需要id和修改的供应商内容

1.编写业务层 

spring boot 完整后端接口案例_第50张图片

//    修改供应商
     Result updateSupplier(int id,Supplier supplier);

 2.实现业务层

spring boot 完整后端接口案例_第51张图片

//    修改供应商
    @Override
    public Result updateSupplier(int id, Supplier supplier) {
        if(supplier.getId()==null){
            supplier.setId(id);
        }
      int b=  baseMapper.updateById(supplier);
        if(b<1){
            return Result.error("修改供应商失败");
        }
        return Result.ok();
    }

3.测试

spring boot 完整后端接口案例_第52张图片


第十章 商品管理服务端-分页条件查询

商品列表要显示供应商名称,而在 tb_goods 表中只有供应商id,要显示供应商名称就要tb_goods 关联tb_supplier 表查询。

创建商品请求类 GoodsREQ spring boot 完整后端接口案例_第53张图片

package com.cc.member.req;

import lombok.Data;

import java.io.Serializable;

@Data
public class GoodsREQ implements Serializable {
    /**
     * 商品名称
     */
    private String name;

    /**
     * 商品编码
     */
    private String code;

    /**
     * 供应商id
     */
    private String supplierId;

}

 编写数据访问层Mapper

  tb_goods 关联 tb_supplier 表条件查询商品分页数据

 1.GoodsMapper 接口中定义 searchPage 方法

spring boot 完整后端接口案例_第54张图片

package com.cc.member.mapper;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cc.member.entity.Goods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cc.member.req.GoodsREQ;
import org.apache.ibatis.annotations.Param;

/**
 * 

* 商品信息表 Mapper 接口 *

* * @author cc-www.cc.com * @since 2023-05-18 */ public interface GoodsMapper extends BaseMapper { /** * 不需要手动去分页,而mybaits-plus会自动实现分页 * 但是你必须第1个参数传入IPage对象,第2个参数通过 @Param 取别名, * 最终查询到的数据会被封装到IPage实现里面 * @param page * @param req * @return */ IPage searchPage(IPage page, @Param("req") GoodsREQ req); }

 spring boot 完整后端接口案例_第55张图片

2.在 com/mengxuegu/member/mapper/xml/GoodsMapper.xml 添加查询的sql实现

spring boot 完整后端接口案例_第56张图片





        
        



3.检查 application.yml 中的包名是否正确(根据自己创建的文件目录比对)

spring boot 完整后端接口案例_第57张图片

 4.上面sql查询结果有 supplierName 商品名称,我们在 com.mengxuegu.member.entity.Goods 类添加一个supplierName属性

/**
* 供应商名称
*/
// 标识它不是tb_goods表中字段,不然会报错
@TableField(exist = false) 
private String supplierName;

编写业务层

1.编写业务层

spring boot 完整后端接口案例_第58张图片

//    分页查询商品
    Result search(int page, int size, GoodsREQ req);

 2.实现业务层

spring boot 完整后端接口案例_第59张图片

//    分页查询
    @Override
    public Result search(int page, int size, GoodsREQ req) {
//        如果请求条件为空,就全部查询
        if(req==null){
            req=new GoodsREQ();
        }
        IPage p=new Page<>(page,size);
        IPage data=baseMapper.searchPage(p,req);
        return  Result.ok(data);
    }

 编写控制层

spring boot 完整后端接口案例_第60张图片

package com.cc.member.controller;


import com.cc.member.base.Result;
import com.cc.member.req.GoodsREQ;
import com.cc.member.service.IGoodsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 

* 商品信息表 前端控制器 *

* * @author cc-www.cc.com * @since 2023-05-18 */ @RestController @RequestMapping("/goods") public class GoodsController { Logger logger= LoggerFactory.getLogger(getClass()); @Autowired private IGoodsService iGoodsService; // 分页查询 @PostMapping("/list/search/{page}/{size}") public Result search(@PathVariable("page") int page,@PathVariable("size") int size, @RequestBody GoodsREQ req){ logger.info("分页数据,page={},size={}",page,size); return iGoodsService.search(page,size,req); } }

测试spring boot 完整后端接口案例_第61张图片


第十一章 商品管理服务端-增删改查

11.1 新增和删除商品

 新增与删除商品只要对 tb_goods 单表操作,我们可以直接使用 mybatis-plus 提供的 IMemberService 方法进行操作即可。

添加控制层方法

spring boot 完整后端接口案例_第62张图片

//    新增商品
    @PostMapping
    public Result add(@RequestBody Goods goods){
      boolean b=  iGoodsService.save(goods);
      if(b){
          return Result.ok();
      }
      return Result.error("新增商品失败");
    }

//    删除商品
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable("id") int id){
       boolean b= iGoodsService.removeById(id);
       if(b){
           return Result.ok();
       }
       return Result.error("删除失败");
    }

     测试

  1. 新增商品,发送 POST 请求 localhost:6666/goods
  2. 删除商品,发送 DELETE 请求 localhost:6666/goods/2

11.2 查询和修改商品 

添加业务层

添加方法 

spring boot 完整后端接口案例_第63张图片

//    查询商品
    Result findById(int id);

//    修改商品
    Result update(int id,Goods goods);

 实现方法spring boot 完整后端接口案例_第64张图片


    @Autowired
    private ISupplierService iSupplierService;
    //    查询商品
    @Override
    public Result findById(int id) {
//        通过id查询到商品
        Goods goods=baseMapper.selectById(id);
//        如果有供应商,则将供应商的名字查询出来
        if(goods!= null && goods.getSupplierId()!=null  ){
            Supplier supplier=iSupplierService.getById(goods.getSupplierId());
            if(supplier!=null){
                goods.setSupplierName(supplier.getName());
            }
        }
        return Result.ok(goods);
    }

//    修改商品
    @Override
    @PutMapping("/{id}}")
    public Result update(@PathVariable("id") int id,@RequestBody Goods goods) {

        if(goods.getId()==null){
            goods.setId(id);
        }

        int b=baseMapper.updateById(goods);
        if(b<1){
            return Result.error("修改失败");
        }
        return Result.ok();
    }

添加控制层方法

spring boot 完整后端接口案例_第65张图片


//    查询商品
    @GetMapping("/{id}")
    public Result find(@PathVariable("id") int id){
        return iGoodsService.findById(id);
    }

//    修改商品
    @PutMapping("/{id}")
    public Result update(@PathVariable("id") int id,@RequestBody Goods goods){
        return iGoodsService.update(id,goods);
    }

测试

  1. 查询商品详情,发送 GET 请求 localhost:6666/goods/2
  2. 修改商品,发送 PUT 请求 localhost:6666/goods/2

第十二章 员工管理服务端-分页条件查询 

创建商品请求类 

spring boot 完整后端接口案例_第66张图片

package com.cc.member.req;

import lombok.Data;

import java.io.Serializable;

@Data
public class StaffREQ implements Serializable {

    private String name;
    private String username;
}

 编写业务层

1.创建方法spring boot 完整后端接口案例_第67张图片 

public interface IStaffService extends IService {

//   员工分页查询
    Result search(long page, long size, StaffREQ req);
}

 2.实现方法

spring boot 完整后端接口案例_第68张图片


//    分页条件查询
    @Override
    public Result search(long page, long size, StaffREQ req) {
        QueryWrapper query=new QueryWrapper();
        if(req!=null){
            if(StringUtils.isNotBlank(req.getName())){
                query.like("name",req.getName());
            }
            if(StringUtils.isNotBlank(req.getUsername())){
                query.like("username",req.getUsername());
            }
        }
        IPage p=new Page<>(page,size);
        IPage data=baseMapper.selectPage(p,query);
        return Result.ok(data);
    }

 编写控制层spring boot 完整后端接口案例_第69张图片

  Logger logger= LoggerFactory.getLogger(getClass());

    @Autowired
    private StaffServiceImpl staffService;

//    员工分页接口
    @PostMapping("/list/search/{page}/{size}")
    public Result search(@PathVariable("page") long page,@PathVariable("size") long size,@RequestBody(required = false) StaffREQ req){
        logger.info("分页数据。page={},size={}",page,size);
        return staffService.search(page,size,req);
    }

 注意:@RequestBody(required=false) 表示查询条件可以为空

测试 

spring boot 完整后端接口案例_第70张图片


第十三章 员工管理服务端-增删改查

13.1 新增员工 

需求:

  1. 用户名不允许为空
  2. 查询员工用户名是否存在,存在不允许新增
  3. 对密码进行加密保存
  4. 提交数据到 tb_staff

 编写业务层

 1.编写方法spring boot 完整后端接口案例_第71张图片

//    新增员工
    Result add(Staff staff);

2.实现方法

spring boot 完整后端接口案例_第72张图片

//    新增员工
    @Override
    public Result add(Staff staff) {
//      1.新增员工不允许为空
        if(staff==null || StringUtils.isEmpty(staff.getUsername())){
            return Result.error("用户名不允许为空");
        }
//      2.查询用户名是否存在,存在不允许添加
        Staff s=getUserName(staff.getUsername());
        if(s!=null){
            return Result.error("用户名已存在");
        }
//      3.对密码进行保密
        String pas=new BCryptPasswordEncoder().encode(staff.getPassword());
        staff.setPassword(pas);

//      4.保存到数据库
        boolean b=this.save(staff);
        if(b){
            return Result.ok();
        }
        return Result.error("添加失败");
    }

//    通过用户名查询staff
    public Staff getUserName(String username){
        QueryWrapper query=new QueryWrapper();
        query.eq("username",username);
        return baseMapper.selectOne(query);
    }

编写控制层

spring boot 完整后端接口案例_第73张图片

//    添加员工
    @PostMapping
    public Result add(@RequestBody Staff staff){
        return staffService.add(staff);
    }

 测试

spring boot 完整后端接口案例_第74张图片

13.2 删除员工

删除员工只要对 tb_staff 单表操作,我们可以直接使用 mybatis-plus 提供的 IStaffService 方法进行操作即可。 

添加控制层

spring boot 完整后端接口案例_第75张图片

/    删除员工
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable("id") int id){
      boolean b=staffService.removeById(id);
      if(b){
          return Result.ok();
      }
      return Result.error("删除失败");
    }

测试

1. 删除员工 ,发送 DELETE 请求 localhost:6666/staff/2

13.3 查询和修改员工 

添加控制层 

spring boot 完整后端接口案例_第76张图片

//    查询员工
    @GetMapping("/{id}")
    public Result select(@PathVariable("id") int id){
      Staff staff=  staffService.getById(id);
      return Result.ok(staff);
    }

//    修改员工
    @PutMapping("/{id}")
    public  Result update(@PathVariable("id") int id,@RequestBody Staff staff){
        return staffService.update(id,staff);
    }

 添加业务层

1.添加方法

spring boot 完整后端接口案例_第77张图片

//    修改员工
    Result update(int id,Staff staff);

 2.实现方法

spring boot 完整后端接口案例_第78张图片

//    修改员工
    @Override
    public Result update(int id, Staff staff) {
        if(staff.getId()==null){
            staff.setId(id);
        }
       int b= baseMapper.updateById(staff);
        if(b<1){
            return Result.error("修改失败");
        }
        return Result.ok();
    }

测试

1. 修改员工,发送 PUT 请求 localhost:6666/staff/2

13.4 修改密码 

需求:

  1. 校验原密码是否正确
  2. 提交新密码

创建密码请求类 PasswordREQ 

spring boot 完整后端接口案例_第79张图片

package com.cc.member.req;

import lombok.Data;

@Data
public class PasswordREQ {
    /**
     * 用户id
     */
    private Integer userId;
    /**
     * 原密码 or 新密码
     */
    private String password;

}

编写业务层

1.编写方法

spring boot 完整后端接口案例_第80张图片

//    检查原始密码是否正确
    Result checkPassword(PasswordREQ passwordREQ);

//    更新密码
    Result updatePassword(PasswordREQ passwordREQ);

2.实现方法

spring boot 完整后端接口案例_第81张图片

//    检查原始密码是否正确
    @Override
    public Result checkPassword(PasswordREQ passwordREQ) {
        if(passwordREQ == null || StringUtils.isEmpty(passwordREQ.getPassword())) {
            return Result.error("原密码不能为空");
        }

        Staff staff=baseMapper.selectById(passwordREQ.getUserId());
        if(!new BCryptPasswordEncoder().matches(passwordREQ.getPassword(),staff.getPassword())){
            return Result.error("原始密码错误");
        }
        return Result.ok();
    }

//    更新密码
    @Override
    public Result updatePassword(PasswordREQ passwordREQ) {
        if(passwordREQ == null || StringUtils.isEmpty(passwordREQ.getPassword())) {
            return Result.error("新密码不能为空");
        }

        Staff staff=baseMapper.selectById(passwordREQ.getUserId());
        String pas=new BCryptPasswordEncoder().encode(passwordREQ.getPassword());
        staff.setPassword(pas);
        int b=baseMapper.updateById(staff);
        if(b<1){
            return Result.error("更新失败");
        }
        return Result.ok();
    }

创建控制层

因为 修改密码URL是 /user 开头的,而StaffController 是 /staff 开头,我们就创建一个的控制类 AuthController spring boot 完整后端接口案例_第82张图片

package com.cc.member.controller;

import com.cc.member.base.Result;
import com.cc.member.req.PasswordREQ;
import com.cc.member.service.impl.StaffServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/user")
public class AuthController {

    @Autowired
    private StaffServiceImpl staffService;

//    检查密码
    @PostMapping("/pwd")
    public Result check(@RequestBody PasswordREQ req){
        return staffService.checkPassword(req);
    }

//    修改密码
    @PutMapping("/pwd")
    public Result updatePwd(@RequestBody PasswordREQ passwordREQ){
        return staffService.updatePassword(passwordREQ);
    }
}

测试

  1. 校验原密码,发送 POST 请求 localhost:6666/user/pwd
  2. 修改新密码,发送 PUT 请求 localhost:6666/user/pwd

spring boot 完整后端接口案例_第83张图片


第十四章 基于 JWT 令牌实现身份认证

JSON Web Token( JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用 户和服务器之间传递安全可靠的信息。其中 JWT 中可以包含用户信息。 

14.1 安装依赖 

  1. mengxuegu-member-util/pom.xml 添加jjwt依赖(jdk8以下)

        
        
            io.jsonwebtoken
            jjwt
            0.6.0
        
    

 2.如果使用JDK9还要添加以下依赖

默认情况下,在java SE 9.0 中 将不再包含java EE Jar包,而 JAXB APIjava EE API,因此我们要手动导入这个 Jar 包 。




io.jsonwebtoken
jjwt
0.6.0


javax.xml.bind
jaxb-api
2.3.0


com.sun.xml.bind
jaxb-impl
2.3.0


com.sun.xml.bind
jaxb-core
2.3.0


javax.activation
activation
1.1.1


 14.2 创建JWT工具类

1.在 mengxuegu-member-util 模块中创建工具类 com.mengxuegu.member.util.JwtUtil 

spring boot 完整后端接口案例_第84张图片

2.JwtUtil 代码实现如下:

package com.cc.member.base;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@ConfigurationProperties(prefix = "cc.jwt.config")
public class JwtUtil {

    // 密钥
    private String secretKey;

    //单位秒,默认7天
    private long expires = 60*60*24*7;

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public long getExpires() {
        return expires;
    }

    public void setExpires(long expires) {
        this.expires = expires;
    }

    /**
     * 生成JWT
     * @param id
     */
    public String createJWT(String id, String subject, Boolean isLogin) {
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        JwtBuilder builder = Jwts.builder().setId(id) //字符串
                .setSubject(subject) //主题 如用户名
                .setIssuedAt(now)  //签发时间
                .signWith(SignatureAlgorithm.HS256, secretKey) //签名密钥
                .claim("isLogin", isLogin);
        if (expires > 0) {
            // expires乘以1000是毫秒转秒
            builder.setExpiration(new Date(nowMillis + expires*1000));
        }
        return builder.compact();
    }

    /**
     * 解析JWT
     * @param jwtToken
     */
    public Claims parseJWT(String jwtToken){
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken).getBody();
    }

}

3.mengxuegu-member-api 模块的 application.yml 中添加配置

cc:
  jwt:
    config:
      secretKey: mengxuegu # jwt令牌密钥
      expires: 604800 # 单位秒,7天

 14.3 login业务实现

业务层

1.添加方法

spring boot 完整后端接口案例_第85张图片

    Result login(String username, String password);

2.实现方法 

spring boot 完整后端接口案例_第86张图片

 @Autowired
    JwtUtil jwtUtil;

//    登录接口
    @Override
    public Result login(String username, String password) {
      //   用户名和密码不能为空
        if(StringUtils.isBlank(username)
                || StringUtils.isBlank(password)) {
            return Result.error("用户名或密码错误");
        }
      // 1. 通过用户名查询
        Staff staff = getUserName(username); if(staff == null) {
            return Result.error("用户名或密码错误");
        }
      // 2. 存在,判断密码是否正确(输入的密码,数据库加密的密码)
        if( !new BCryptPasswordEncoder().matches(password, staff.getPassword())) {
            return Result.error("用户名或密码错误");
        }
       // 3. 生成token 响应
        String jwt = jwtUtil.createJWT(staff.getId() + "", staff.getUsername(), true);

        // 手 动 封 装 个 json 对 象 {token: jwt}
        Map map = new HashMap<>();
        map.put("token", jwt);
        return Result.ok(map);

    }

    //    通过用户名查询staff
    public Staff getUserName(String username){
        QueryWrapper query=new QueryWrapper();
        query.eq("username",username);
        return baseMapper.selectOne(query);
    }

控制层添加 login 方法 spring boot 完整后端接口案例_第87张图片

   /**
     *	登录
     *	@return
     */
    @PostMapping("/login")
    public Result login(@RequestBody Staff staff) {
        return staffService.login(staff.getUsername(), staff.getPassword());
    }

 测试spring boot 完整后端接口案例_第88张图片

 14.4 通过token获取用户信息

业务层 

1.创建方法

spring boot 完整后端接口案例_第89张图片

    /**
     *	通过token获取用户信息
     *	@param token
     *	@return
     */
    Result getUserInfo(String token);

2.实现方法spring boot 完整后端接口案例_第90张图片

   @Override
    public Result getUserInfo(String token) {
        // 解析jwt
        Claims claims = jwtUtil.parseJWT(token);
        if(claims == null || StringUtils.isBlank(claims.getSubject())) { 
           return Result.error("获取用户信息失败");
        }

        // 获取用户名
                String username = claims.getSubject();
        // 1. 通过用户名查询
        Staff staff = getUserName(username);

        if(staff == null) {
            return Result.error("用户不存在");
        }

      // 2. 将密码设置为null,不响应给前端
        staff.setPassword(null);

        return Result.ok(staff);

    }

    //    通过用户名查询staff
    public Staff getUserName(String username){
        QueryWrapper query=new QueryWrapper();
        query.eq("username",username);
        return baseMapper.selectOne(query);
    }

 控制层添加 getUserInfo 方法

spring boot 完整后端接口案例_第91张图片 测试 

spring boot 完整后端接口案例_第92张图片 14.5 退出系统

jwt令牌无法手动让它失效,在前端点击退出时直接删除localStorage中的数据,如果需要可以使用将jwt通过redis存储,每次请求时从redis查询是否存在,如果不存在,则认为未登录已退出。

只需要添加控制层

spring boot 完整后端接口案例_第93张图片

  /**
     *	退出
     *	@return
     */ @PostMapping("/logout")
    public Result logout() {
        return Result.ok();

    }

 14.6  拦截器方式实现 token 鉴权

我们自定一个拦截器,只有当用户登录后,才可以访问资源接口(会员、商品、供应商、员工),没有登录则要求   登录。

其中判断是否登录,要求客户端请求接口时,在请求头上带上 token ,然后在拦截器拦截到请求后,校验 token是否有效,有效才让访问,否则无法访问。

请求头信息 Authorization: Bearer jwtToken

创建拦截器

  1. 创建一个 HandlerInterceptorAdapter 拦截适配器的子类,并且重写它里面的 preHandle 方法,在请求目标接口时进行拦截。
  2. 创建自定义拦截器类 com.mengxuegu.member.filter.AuthenticationFilter类上不要少了 @Componentspring boot 完整后端接口案例_第94张图片

package com.cc.member.filter;


import com.cc.member.base.JwtUtil;
import io.jsonwebtoken.Claims;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 认证拦截器
 */
@Component
public class AuthenticationFilter extends HandlerInterceptorAdapter {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 是否登录
        boolean isLogin = false;

        // 请求头带上令牌 Authorization: Bearer jwtToken
        final String authHeader = request.getHeader("Authorization");

        if(StringUtils.isNotBlank(authHeader) && authHeader.startsWith("Bearer ")) {
            // 截取token,
            final String token = authHeader.substring(7);
            // 解析token
            Claims claims = jwtUtil.parseJWT(token);
            if(claims != null) {
                // 是否登录
                Boolean b = (Boolean) claims.get("isLogin");
                if(b) {
                    // 已经登录,放行请求
                    isLogin = true;
                }
            }
        }

        if(!isLogin) {
            // 未登录,则响应信息
            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setStatus(401);
            response.getWriter().write("未通过认证,请在登录进行登录");
        }
        // 不放行
        return isLogin;
    }
}

配置拦截器类

1. 创建一个配置类 com.mengxuegu.member.config.WebMvcConfig 其中要放行登录请求 /user/loginspring boot 完整后端接口案例_第95张图片

package com.cc.member.config;


import com.cc.member.filter.AuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    @Autowired
    private AuthenticationFilter authenticationFilter;

    protected void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authenticationFilter).
//                拦截所有请求
                addPathPatterns("/**").
//                登录请求排除,不被拦截
                excludePathPatterns("/user/login");
    }

}

postman测试spring boot 完整后端接口案例_第96张图片

spring boot 完整后端接口案例_第97张图片


15.部署 

服务端部署 

  1. 打包前将 mengxuegu-member-api 工程下的 application.yml 中的数据库连接信息改为服务器的信息
  2. 针对 mengxuegu-member 执行 clean 和 installspring boot 完整后端接口案例_第98张图片

3.针对 mengxuegu-member-api 执行 clean install

spring boot 完整后端接口案例_第99张图片

4.找到生成的项目jar包,上传到服务器目录spring boot 完整后端接口案例_第100张图片 

5.先执行 java -jar 打包好的名字.jar   使用postman,仍然可以测试 

6.不报错,则以后台进程方式启动

spring boot 完整后端接口案例_第101张图片

你可能感兴趣的:(Spring,Boot,spring,boot,java,后端)