spring boot 完整后端接口案例

 

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

1.1 创建模块mengxuegu-member

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

 1.2 添加pom依赖,pom.xml

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cc</groupId>
    <artifactId>cc-member</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- springboot依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
    </parent>

    <!--依赖版本管理-->
    <properties>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
        <druid.version>1.1.21</druid.version>
        <fastjson.version>1.2.8</fastjson.version>
        <commons-lang.version>2.6</commons-lang.version>
        <commons-collections.version>3.2.2</commons-collections.version>
        <commons-io.version>2.6</commons-io.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <!--实际依赖-->
    <dependencies>
        <!--mybatis-plus启动器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--生成setter,getter-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--Spring Seucrity 加密模块-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
        </dependency>

        <!-- yml配置处理器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 工具类依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>${commons-lang.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>${commons-collections.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commons-io.version}</version>
        </dependency>
    </dependencies>

</project>


第二章 公共工具模块搭建 

用于管理通用的工具类 

2.1  创建模块 mengxuegu-member-util

 (点击父模块,创建子模块)

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

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

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <!-- 彩色日志 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </layout>
    </appender>
    <root level="info">
        <appender-ref ref="stdout" />
    </root>
</configuration>

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

2.3 整合 Lombok

Lombok 介绍

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

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

  Lombok 使用

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

 <!--生成setter,getter-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2.IDEA 安装 lombok 插件

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

    先点击file->settings 

2.4 规范统一响应枚举 ResultEnum

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

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

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 自定义数据。

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

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启动器依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cc-member</artifactId>
        <groupId>com.cc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cc-member-api</artifactId>

    <dependencies>

        <!--依赖工具模块-->
        <dependency>
            <artifactId>cc-member-util</artifactId>
            <groupId>com.cc</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--web启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>

        <resources>
            <resource>
                <!--编译时,默认情况下不会将 mapper.xml文件编译进去,
                    src/main/java 资源文件的路径,
                    **/*.xml 需要编译打包的文件类型是xml文件 -->
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>

        <plugins>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!--指定启动类-->
                    <mainClass>com.cc.member.MemberApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>


    </build>

</project>

 位于:会员管理系统/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下创建

 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

  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

 添加 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 标签: (前期已经添加)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cc-member</artifactId>
        <groupId>com.cc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cc-member-api</artifactId>

    <dependencies>

        <!--依赖工具模块-->
        <dependency>
            <artifactId>cc-member-util</artifactId>
            <groupId>com.cc</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--web启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>

        <resources>
            <resource>
                <!--编译时,默认情况下不会将 mapper.xml文件编译进去,
                    src/main/java 资源文件的路径,
                    **/*.xml 需要编译打包的文件类型是xml文件 -->
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>

        <plugins>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!--指定启动类-->
                    <mainClass>com.cc.member.MemberApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>


    </build>

</project>

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


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

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

5.1创建会员实体类 Member

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

  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<Member>接口。MyBatis-plus的 BaseMapper<T>接口提供了很多对T表的数据操作方法

 接口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<Member> {

}

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

 MemberMapper.xml代码

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cc.member.mapper.MemberMapper">

</mapper>

5.4 创建业务层

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

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

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

 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<Member> {

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

   
}

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

 ServiceImpl<M extends BaseMapper<T>,T>是对Iservice 接口中方法的实现

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

第二个泛型T指定实体类

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

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<MemberMapper, Member> 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<Member> p=new Page<>(page,size);
        IPage<Member> 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 

出现问题: 

 项目启动报错: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) 

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

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

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

Page.java代码

package com.cc.member.base;

import lombok.Data;

import java.util.List;

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

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

    public List<T> 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;

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


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

6.1 新增和删除会员 

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

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

添加控制层方法

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

 代码:


//    添加会员
    @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

 

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

 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

 另一种修改会员的方法:(相当于自己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<MemberMapper, Member> 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<Member> p=new Page<>(page,size);
        IPage<Member> 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 

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

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cc-member</artifactId>
        <groupId>com.cc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cc-member-generator</artifactId>
    <dependencies>
        <!-- 代码生成器核心依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.1</version>
        </dependency>
        <!--mybatis-plus必须要配置模板引擎-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
    </dependencies>

</project>

7.3 编写自动生成器代码

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

 代码:

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();
    }


    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    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 + "!");
    }

}

代码要进行一定的修改

 

 

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

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

 最终效果:(自动生成的)


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

8.1 创建供应商请求类 SupplierREQ 

 代码:

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编写方法

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

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;

/**
 * <p>
 * 供应商信息表 服务类
 * </p>
 *
 * @author cc-www.cc.com
 * @since 2023-05-18
 */
public interface ISupplierService extends IService<Supplier> {

//    分页

    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;

/**
 * <p>
 * 供应商信息表 服务实现类
 * </p>
 *
 * @author cc-www.cc.com
 * @since 2023-05-18
 */
@Service
public class SupplierServiceImpl extends ServiceImpl<SupplierMapper, Supplier> 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<Supplier> p=new Page<>(page,size);
        IPage<Supplier>data=  baseMapper.selectPage(p,query);
        return Result.ok(data);
    }
}

8.3 编写控制层

 代码:

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.*;

/**
 * <p>
 * 供应商信息表 前端控制器
 * </p>
 *
 * @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 


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

9.1 新增供应商 

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

1.添加控制层方法

部分代码:


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

2.测试 

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

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

编写商品的业务层

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

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

 代码:

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

 2.实现商品的业务层

代码 


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

    }

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

编写供应商业务层 

 1.定义方法

 代码:

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

2.实现方法

 代码:


//    删除供应商

    @Autowired
    private IGoodsService iGoodsService;
    @Override
    public Result deleteSupplier(int id) {
      // 1. 通过供应商id查询是否被商品引用,
       List<Goods> 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),调用的是方法,而不是关注方法是如何实现的;

 编写控制层

 代码:


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

 测试

9.3 查询和修改供应商

查询供应商 

单表查询,只需要控制层

 代码:


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

    }

 测试:

 修改供应商

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

1.编写业务层 

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

 2.实现业务层

//    修改供应商
    @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.测试


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

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

创建商品请求类 GoodsREQ 

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 方法

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;

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


}

 

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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cc.member.mapper.GoodsMapper">

        <!--分页条件查询商品列表-->
        <select id="searchPage" resultType="Goods" >
            SELECT
            t1.*,
            t2.NAME AS supplierName
            FROM
            tb_goods t1
            LEFT JOIN tb_supplier t2 ON t1.supplier_id = t2.id WHERE 1=1
            <if test="req.name != null and req.name !=''">
                AND t1.name LIKE CONCAT('%', #{req.name}, '%')
            </if>
            <if test="req.code != null and req.code !=''">
                AND t1.code LIKE CONCAT('%', #{req.code}, '%')
            </if>
            <if test="req.supplierId != null">
                AND t1.supplier_id = #{req.supplierId}
            </if>
        </select>


</mapper>

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

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

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

编写业务层

1.编写业务层

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

 2.实现业务层

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

 编写控制层

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.*;

/**
 * <p>
 * 商品信息表 前端控制器
 * </p>
 *
 * @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);

   }
}

测试


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

11.1 新增和删除商品

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

添加控制层方法

//    新增商品
    @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 查询和修改商品 

添加业务层

添加方法 

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

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

 实现方法


    @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();
    }

添加控制层方法


//    查询商品
    @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

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

创建商品请求类 

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.创建方法 

public interface IStaffService extends IService<Staff> {

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

 2.实现方法


//    分页条件查询
    @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<Staff> p=new Page<>(page,size);
        IPage<Staff> data=baseMapper.selectPage(p,query);
        return Result.ok(data);
    }

 编写控制层

  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) 表示查询条件可以为空

测试 


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

13.1 新增员工 

需求:

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

 编写业务层

 1.编写方法

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

2.实现方法

//    新增员工
    @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);
    }

编写控制层

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

 测试

13.2 删除员工

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

添加控制层

/    删除员工
    @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 查询和修改员工 

添加控制层 

//    查询员工
    @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.添加方法

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

 2.实现方法

//    修改员工
    @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 

package com.cc.member.req;

import lombok.Data;

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

}

编写业务层

1.编写方法

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

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

2.实现方法

//    检查原始密码是否正确
    @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 

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


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

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

14.1 安装依赖 

  1. mengxuegu-member-util/pom.xml 添加jjwt依赖(jdk8以下)
<dependencies>
        <!--jwt令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>
    </dependencies>

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

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

<dependencies>
<!--jwt令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>

 14.2 创建JWT工具类

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

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.添加方法

    Result login(String username, String password);

2.实现方法 

 @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<String, String> 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 方法 

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

 测试

 14.4 通过token获取用户信息

业务层 

1.创建方法

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

2.实现方法

   @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 方法

 测试 

 14.5 退出系统

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

只需要添加控制层

  /**
     *	退出
     *	@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类上不要少了 @Component

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/login

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测试


15.部署 

服务端部署 

  1. 打包前将 mengxuegu-member-api 工程下的 application.yml 中的数据库连接信息改为服务器的信息
  2. 针对 mengxuegu-member 执行 clean 和 install

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

4.找到生成的项目jar包,上传到服务器目录 

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

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

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个简单的Spring Boot整合MyBatis和Vue的案例: 1. 前端Vue部分 首先,我们创建一个Vue项目。在命令行中输入以下命令: ``` vue create vue-mybatis-demo ``` 接着,在`vue-mybatis-demo`目录下执行以下命令: ``` npm install axios --save ``` 这将安装`axios`,一个用于发送HTTP请求的JavaScript库。 接下来,我们在`src`目录下创建一个`components`目录,并在其中创建一个`UserList.vue`组件。在该组件中,我们将使用`axios`从后端获取用户列表。以下是`UserList.vue`组件的代码: ```html <template> <div> <h2>User List</h2> <table> <thead> <tr> <th>ID</th> <th>Name</th> <th>Age</th> </tr> </thead> <tbody> <tr v-for="user in users" :key="user.id"> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.age }}</td> </tr> </tbody> </table> </div> </template> <script> import axios from 'axios'; export default { data() { return { users: [] } }, mounted() { axios.get('/api/users') .then(response => { this.users = response.data; }) .catch(error => { console.log(error); }); } } </script> ``` 2. 后端Spring Boot部分 我们使用Spring Boot创建一个RESTful API,用于从数据库中获取用户列表。首先,在`pom.xml`文件中添加以下依赖项: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies> ``` 然后,在`application.properties`文件中添加以下配置: ``` spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= mybatis.mapper-locations=classpath:mapper/*.xml ``` 接下来,我们创建一个`User`实体类: ```java public class User { private Long id; private String name; private Integer age; // getters and setters } ``` 然后,我们创建一个`UserMapper`接口和对应的XML文件,用于从数据库中获取用户列表: ```java @Mapper public interface UserMapper { @Select("SELECT * FROM user") List<User> findAll(); } ``` ```xml <mapper namespace="com.example.demo.mapper.UserMapper"> <select id="findAll" resultType="com.example.demo.entity.User"> SELECT * FROM user </select> </mapper> ``` 最后,我们创建一个`UserController`类,用于处理从前端发送的请求: ```java @RestController @RequestMapping("/api") public class UserController { @Autowired private UserMapper userMapper; @GetMapping("/users") public List<User> findAllUsers() { return userMapper.findAll(); } } ``` 3. 整合前后端 现在,我们需要将前端Vue项目打包并将其静态文件放入Spring Boot项目的`resources/static`目录下。在`vue-mybatis-demo`目录下执行以下命令: ``` npm run build ``` 这将生成一个`dist`目录,其中包含前端Vue项目的静态文件。将该目录下的所有文件复制到Spring Boot项目的`resources/static`目录下。 最后,我们启动Spring Boot应用程序,并在浏览器中访问`http://localhost:8080`,即可看到从数据库中获取的用户列表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值