前后端分离开发

SSM整合环境搭建(Axios异步请求——前后端分离)以员工管理为例

一、maven聚合工程的搭建

1、创建父工程

SSZipArchive如何实现异步压缩 ssm异步请求_spring

 

 

SSZipArchive如何实现异步压缩 ssm异步请求_maven_02

 2、删除父工程中的src文件夹,右键工程名,新建子工程

SSZipArchive如何实现异步压缩 ssm异步请求_xml_03

 

 

SSZipArchive如何实现异步压缩 ssm异步请求_xml_04

 

 

SSZipArchive如何实现异步压缩 ssm异步请求_spring_05

 重复上述步骤创建其他子工程,如若出现pom.xml 变灰 出现删除线,请参考BUG- IDEA Maven pom.xml 变灰 出现删除线

3、修改子工程employee_web,将其变成web工程

  3.1、选择employee_web添加web

SSZipArchive如何实现异步压缩 ssm异步请求_maven_06

 

SSZipArchive如何实现异步压缩 ssm异步请求_xml_07

 

 

SSZipArchive如何实现异步压缩 ssm异步请求_xml_08

 修改成功后如图

SSZipArchive如何实现异步压缩 ssm异步请求_spring_09

   3.2、更改打包方式为war

SSZipArchive如何实现异步压缩 ssm异步请求_maven_10

二、maven的父工程做版本管理

  给相应的子工程添加依赖,以employee_common的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>employee-parent</artifactId>
        <groupId>com.shangma.cn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>employee_common</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>dysmsapi20170525</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>

</project>

       添加依赖时,使用父工程管理依赖的版本

<?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.shangma.cn</groupId>
    <artifactId>employee_root</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>employee_common</module>
        <module>employee_entity</module>
        <module>employee_mapper</module>
        <module>employee_service</module>
        <module>employee_web</module>
    </modules>
    <properties>
        <lombok-version>1.18.12</lombok-version>
        <mybatis-version>3.5.4</mybatis-version>
        <mysql-version>5.1.47</mysql-version>
        <druid-version>1.1.22</druid-version>
        <spring-version>5.1.9.RELEASE</spring-version>
        <mybatis-spring-version>2.0.3</mybatis-spring-version>
        <log4j-version>1.2.17</log4j-version>
        <aspectjweaver-version>1.9.5</aspectjweaver-version>
        <jackson-databind-version>2.10.2</jackson-databind-version>
        <dysmsapi20170525-version>2.0.4</dysmsapi20170525-version>
        <spring-data-redis-version>2.1.3.RELEASE</spring-data-redis-version>
        <jedis-version>2.9.1</jedis-version>
        <slf4j-simple-version>1.7.30</slf4j-simple-version>
        <pagehelper-version>5.1.10</pagehelper-version>
        <aliyun-sdk-oss-version>3.10.2</aliyun-sdk-oss-version>
    </properties>
<!--    版本控制-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok-version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>${mybatis-version}</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql-version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${spring-version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>${mybatis-spring-version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring-version}</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>${aspectjweaver-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring-version}</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>${jackson-databind-version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>dysmsapi20170525</artifactId>
                <version>${dysmsapi20170525-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-redis</artifactId>
                <version>${spring-data-redis-version}</version>
            </dependency>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>${jedis-version}</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>${slf4j-simple-version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>${pagehelper-version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun-sdk-oss-version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>


三、maven的配置类加注解的方式进行SSM整合

在Mapper层和Service层配置Spring,在web层配置Spring MVC和web.xml

1、MpperConfig类(spring)

1.1、加载外部配置文件(没写)

1.2、配置数据源

1.3、配置SQLSessionFactoryBean

1.4、配置MapperScanConfiguar

package com.shangma.cn.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MapperConfig {



    /**
     * 配置数据源
     * @return
     */
    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource dataSource =new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mavenssm");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    /**
     * 配置SQLSessionFactory,加载mybatis配置文件
     * @return
     */
    @Bean
    public SqlSessionFactoryBean sessionFactory(){
        SqlSessionFactoryBean sessionFactory=new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());

        return sessionFactory;
    }

    /**
     * 配置mapper扫描
     * @return
     */
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.shangma.cn.mapper");
        return mapperScannerConfigurer;
    }

}


2、ServiceConfig类(spring)

2.1、配置包扫描

2.2、配置事务管理器

2.3、配置事务驱动

package com.shangma.cn.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @author: JAVASM
 * @className: ServiceConfig
 * @description:
 * @date: 2021/6/14 13:03
 * @version: 0.1
 * @since: jdk1.8
 */
@Configuration
@ComponentScan(basePackages = {"com.shangma.cn.service"})//包扫描
@EnableTransactionManagement//开启事务注解驱动
public class ServiceConfig {

    @Autowired
    private DruidDataSource dataSource;

    /**
     * 事务管理器
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactionManager(){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

}


3、WebConfig类(spring mvc)

3.1、配置包扫描

3.2、配置mvc的注解驱动

package com.shangma.cn.config;

import com.shangma.cn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author: JAVASM
 * @className: WebConfig
 * @description:
 * @date: 2021/6/14 13:04
 * @version: 0.1
 * @since: jdk1.8
 */
@ComponentScan(basePackages = {"com.shangma.cn.controller", "com.shangma.cn.exception"})
@EnableWebMvc//开启MVC注解驱动
@Configuration//加入容器
public class WebConfig implements WebMvcConfigurer {


}


4、MyWebApplnitalizer类(web.xml)

4.1、加载spring的配置类

4.2、加载spring mvc的配置类

4.3、设置开启文件上传

4.4、设置Filter解决乱码问题(没写)

package com.shangma.cn;

import com.shangma.cn.config.MapperConfig;
import com.shangma.cn.config.ServiceConfig;
import com.shangma.cn.config.WebConfig;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration;

/**
 * @author: JAVASM
 * @className: MyWebAppInitializer
 * @description:
 * @date: 2021/6/14 19:16
 * @version: 0.1
 * @since: jdk1.8
 */
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     * 加载父容器的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{MapperConfig.class, ServiceConfig.class};
    }

    /**
     * 加载子容器的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    /**
     * 表示设置DispatcherServlet路径
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /**
     * 文件上传
     * @param registration
     */
    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {
        registration.setMultipartConfig(new MultipartConfigElement(""));
    }


}


四、逆向工程生成和mybatis的日志打印

  使用逆向工程,能够一键生成entity层和mapper层的代码,CV大法到相应的位置

  因为使用的是配置类的方式进行SSM的整合,所以没有了配置文件,因此在配置SQLSessionFactoryBean时并没有对mybatis的日志打印进行设置

配置文件在配置类中都有对应的写法,导入log4j的配置文件,修改后的MpperConfig类如下:

package com.shangma.cn.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: JAVASM
 * @className: MapperConfig
 * @description:
 * @date: 2021/6/14 13:02
 * @version: 0.1
 * @since: jdk1.8
 */
@Configuration
public class MapperConfig {

    /**
     * 配置数据源
     * @return
     */
    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource dataSource =new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mavenssm");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    /**
     * 配置SQLSessionFactory,加载mybatis配置文件
     * @return
     */
    @Bean
    public SqlSessionFactoryBean sessionFactory(){
        SqlSessionFactoryBean sessionFactory=new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        //设置mybatis的日志
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(Log4jImpl.class);
        sessionFactory.setConfiguration(configuration);

        return sessionFactory;
    }

    /**
     * 配置mapper扫描
     * @return
     */
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.shangma.cn.mapper");
        return mapperScannerConfigurer;
    }

}


五、发送手机验证码

package com.shangma.cn.controller;

import com.shangma.cn.common.AxiosResult;
import com.shangma.cn.common.AxiosStatus;
import com.shangma.cn.entity.Employee;
import com.shangma.cn.exception.MyLoginException;
import com.shangma.cn.factory.AsyncFactory;
import com.shangma.cn.pool.AsyncManager;
import com.shangma.cn.service.EmployeeService;
import com.shangma.cn.utils.RandomCodeUtil;
import com.shangma.cn.utils.SmsUtil;
import com.shangma.cn.utils.UploadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: JAVASM
 * @className: CommonController
 * @description:
 * @date: 2021/6/14 19:15
 * @version: 0.1
 * @since: jdk1.8
 */
@RestController
@RequestMapping("common")
public class CommonController {

    @Autowired
    private EmployeeService employeeService;

    @RequestMapping("getCode/{phone}")
    public String getCode(@PathVariable String phone){
        System.out.println(phone);
        Employee employee = employeeService.findByPhone(phone);
        System.out.println(employee);
        if (employee==null) {
            return "NOT_Employee";
        }
        //生成验证码
        String code = RandomCodeUtil.munCode();
        //发送短信,单独的Java不能实现发送手机验证码
        //所以需要依赖于第三方的SDK,本案例使用的是阿里云所提供的服务
                SmsUtil.sendSms(phone,code);
        return "success";
    }
}


六、使用异步线程池发送手机验证码

  发送短信是一个很耗时间的操作,非常容易造成线程的阻塞,所以我们可以把发送这个操作发到子线程中,并且发送验证码这个操作过程复杂

需要 一定的代码量,而且耦合度高、使用的次数多,所以我们可以使用工厂模式降低代码的重复和解耦。

  还有就是返回给前端的值,一般都会返回json形式的字符串,并且在前端用于条件判断的多是数字(状态码),因此,我们先设置好返回值类,定义好返回值(枚举类)

定义状态码


定义返回值类

package com.shangma.cn.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

@Data
public class AxiosResult<T> {

    private int status;
    private String message;
    private T data;

    /**
     * 私有化构造函数
     */
    private AxiosResult() {

    }

    /**
     * 返回状态码不带数据
     * @param <T>
     * @return
     */
    public static <T> AxiosResult<T> success() {
        return setData(AxiosStatus.OK, null);
    }

    /**
     * 返回状态码带数据
     * @param t
     * @param <T>
     * @return
     */
    public static <T> AxiosResult<T> success(T t) {
        return setData(AxiosStatus.OK, t);
    }

    /**
     * 返回别的类型的状态码不带数据
     * @param axiosStatus
     * @param <T>
     * @return
     */
    public static <T> AxiosResult<T> success(AxiosStatus axiosStatus) {
        return setData(axiosStatus, null);
    }

    /**
     * 返回别的类型的状态码带数据
     * @param axiosStatus
     * @param t
     * @param <T>
     * @return
     */
    public static <T> AxiosResult<T> success(AxiosStatus axiosStatus, T t) {
        return setData(axiosStatus, t);
    }

    public static <T> AxiosResult<T> error() {
        return setData(AxiosStatus.ERROR, null);
    }

    public static <T> AxiosResult<T> error(AxiosStatus axiosStatus) {
        return setData(axiosStatus, null);
    }

    public static <T> AxiosResult<T> error(AxiosStatus axiosStatus, T t) {
        return setData(axiosStatus, t);
    }

    public static <T> AxiosResult<T> setData(AxiosStatus axiosStatus, T t) {
        AxiosResult<T> tAxiosResult = new AxiosResult<>();
        tAxiosResult.setStatus(axiosStatus.getStatus());
        tAxiosResult.setMessage(axiosStatus.getMessage());
        tAxiosResult.setData(t);
        return tAxiosResult;
    }
}


线程池

package com.shangma.cn.pool;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 使用单例模式设计异步线程池
 */
public class AsyncManager {

    private static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

    private static AsyncManager asyncManager;

    private AsyncManager (){
        scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(50);
    }

    /**
     * 创建单例对象
     * @return
     */
    public static AsyncManager getInstance(){
        if (asyncManager==null) {
            return new AsyncManager();
        }
        return asyncManager;
    }


    /**
     * 使用线程池执行内容
     * 这种执行没有延迟时间
     *
     * @param runnable
     */
    public void execute(Runnable runnable) {
        scheduledThreadPoolExecutor.execute(runnable);
    }

    /**
     * 使用线程执行代码
     * schedule方法可以延迟一段时间后再去执行Runnable中的代码
     * @param runnable 要执行的代码
     * @param seconds 延迟的时间
     */
    public void schedule(Runnable runnable,long seconds){
        scheduledThreadPoolExecutor.schedule(runnable,seconds, TimeUnit.SECONDS);
    }

    /**
     * 关闭线程池
     */
    public void release(){
        scheduledThreadPoolExecutor.shutdownNow();
    }
}


工厂类

package com.shangma.cn.factory;

import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.teaopenapi.models.Config;
import com.shangma.cn.utils.SmsUtil;
import lombok.extern.log4j.Log4j;

import java.io.IOException;
import java.util.Properties;

/**
 * 异步工厂(使用工厂模式完成功能)
 * 1、发送手机验证码
 */
@Log4j
public class AsyncFactory {

    private static Properties properties;

    static {
        //加载配置文件
        try {
            properties = new Properties();
            properties.load(SmsUtil.class.getClassLoader().getResourceAsStream("aliyun.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param phone
     * @param code
     * @return
     */
    public static Runnable sendPhoneCode(String phone, String code){
        Config config = new Config()
                // 您的AccessKey ID
                .setAccessKeyId(properties.getProperty("aliyun.accessKeyId"))
                // 您的AccessKey Secret
                .setAccessKeySecret(properties.getProperty("aliyun.accessKeySecret"));
        // 访问的域名
        config.endpoint = properties.getProperty("aliyun.endpoint");
        try {
            Client client = new Client(config);
            SendSmsRequest sendSmsRequest = new SendSmsRequest()
                    .setPhoneNumbers(phone)
                    .setSignName(properties.getProperty("aliyun.signName"))
                    .setTemplateCode(properties.getProperty("aliyun.templateCode"))
                    .setTemplateParam("{\"code\":" + code + "}");
            // 复制代码运行请自行打印 API 的返回值
            Runnable runnable = ()->{
                //当前线程名
                System.out.println("当前线程为:"+Thread.currentThread().getName());
                try {
                    client.sendSms(sendSmsRequest);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
            return runnable;
        } catch (Exception e) {
            log.error(phone + "+手机验证码发送失败:" + e.getMessage());
            e.printStackTrace();
        }
        return ()->{};
    }

}


修改后的发送手机验证码

@RequestMapping("getCode/{phone}")
    public AxiosResult<Void> getCode(@PathVariable String phone){
        System.out.println(phone);
        Employee employee = employeeService.findByPhone(phone);
        System.out.println(employee);
        if (employee==null) {
            return AxiosResult.error();
        }
        //生成验证码
        String code = String.valueOf((int) (Math.random() * (999999 - 100000 + 1) + 100000));
        //发送短信,放入子线程中
        AsyncManager.getInstance().schedule(AsyncFactory.sendPhoneCode(phone,code),2L);
        return AxiosResult.success();
    }


七、SpringDataRedis的使用

  在进行登录验证的时候,我们不仅需要从前端传递过来的验证码,还需要我们自己生成的验证码进行对照,对于验证码我们不可能永久保存,若是放到MySQL中,不仅是存取麻烦,还有对数据库的大量操作会造成数据库的崩溃,这时就需要用到对临时数据的存储Redis数据库。

  在spring mvc中对Redis的操作有两种,使用RedisTemplate操作Redis和使用StringRedisTemplate操作Redis,这两种无论是哪一种都需要Redis工厂的支持。

在WebConfig类中配置

package com.shangma.cn.config;

import com.shangma.cn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import redis.clients.jedis.JedisPoolConfig;


@ComponentScan(basePackages = {"com.shangma.cn.controller", "com.shangma.cn.exception"})
@EnableWebMvc//开启MVC注解驱动
@Configuration//加入容器
public class WebConfig implements WebMvcConfigurer {

    /**
     * Redis工厂
     * 如果是本机,可以什么都不配置
     * @return
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        return jedisConnectionFactory;
    }

    /**
     * 使用RedisTemplate操作Redis
     * @return
     */
//    @Bean
//    public RedisTemplate redisTemplate(){
//        RedisTemplate redisTemplate = new RedisTemplate();
//        redisTemplate.setConnectionFactory(redisConnectionFactory());
//        return redisTemplate;
//    }

    /**
     * 使用StringRedisTemplate操作Redis
     * @return
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
        return stringRedisTemplate;
    }

}


登录验证

//在发送验证码方法中添加
//在生成验证码之后   
//生成验证码,存入Redis,设置2分钟自动销毁
   stringRedisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES);

    /**
     * 登录验证
     * @param map
     * @param session
     * @return
     */
    @RequestMapping("doLogin")
    public AxiosResult<Void> doLogin(@RequestBody Map<String,String>map, HttpSession session){
        System.out.println(map);
        String phone = map.get("phone");
        String code = map.get("code");
        String s = stringRedisTemplate.opsForValue().get(phone);
        if (s.equals(code)) {
            Employee employee = employeeService.findByPhone(phone);
            //将登陆信息放入session
            session.setAttribute("user",employee);
            //清除
            stringRedisTemplate.delete(phone);
            System.out.println(AxiosResult.success().getData());
            return AxiosResult.success();
        }
         return AxiosResult.error();
    }


八、Redis的连接池

在WebConfig类中配制

package com.shangma.cn.config;

import com.shangma.cn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author: JAVASM
 * @className: WebConfig
 * @description:
 * @date: 2021/6/14 13:04
 * @version: 0.1
 * @since: jdk1.8
 */
@ComponentScan(basePackages = {"com.shangma.cn.controller", "com.shangma.cn.exception"})
@EnableWebMvc//开启MVC注解驱动
@Configuration//加入容器
public class WebConfig implements WebMvcConfigurer {

    /**
     * jedis线程池
     * @return
     */
    @Bean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(50);
        jedisPoolConfig.setMaxIdle(50);
        return jedisPoolConfig;
    }

    /**
     * Redis工厂
     * 如果是本机,可以什么都不配置
     * @return
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig());
        return jedisConnectionFactory;
    }

    /**
     * 使用RedisTemplate操作Redis
     * @return
     */
//    @Bean
//    public RedisTemplate redisTemplate(){
//        RedisTemplate redisTemplate = new RedisTemplate();
//        redisTemplate.setConnectionFactory(redisConnectionFactory());
//        return redisTemplate;
//    }

    /**
     * 使用StringRedisTemplate操作Redis
     * @return
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
        return stringRedisTemplate;
    }


}


九、使用异常处理的方式来解决不满足条件的问题

  异常可以阻止程序的执行,我们通过自定异常,在捕捉处理这个异常,就能达到解决不满足条件时程序的执行问题

定义异常

package com.shangma.cn.exception;

import com.shangma.cn.common.AxiosStatus;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class MyLoginException extends RuntimeException{

    private AxiosStatus axiosStatus;

}


捕捉处理异常

package com.shangma.cn.exception;

import com.shangma.cn.common.AxiosResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


@RestControllerAdvice
public class MyHandler {

    @ExceptionHandler(MyLoginException.class)
    public AxiosResult<Void> myHandler(MyLoginException e){
        return AxiosResult.error(e.getAxiosStatus());
    }
}


完整版的登录

package com.shangma.cn.controller;

import com.shangma.cn.common.AxiosResult;
import com.shangma.cn.common.AxiosStatus;
import com.shangma.cn.entity.Employee;
import com.shangma.cn.exception.MyLoginException;
import com.shangma.cn.factory.AsyncFactory;
import com.shangma.cn.pool.AsyncManager;
import com.shangma.cn.service.EmployeeService;
import com.shangma.cn.utils.RandomCodeUtil;
import com.shangma.cn.utils.SmsUtil;
import com.shangma.cn.utils.UploadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: JAVASM
 * @className: CommonController
 * @description:
 * @date: 2021/6/14 19:15
 * @version: 0.1
 * @since: jdk1.8
 */
@RestController
@RequestMapping("common")
public class CommonController {

    @Autowired
    private EmployeeService employeeService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 发送验证码
     * @param phone
     * @return
     */
    @RequestMapping("getCode/{phone}")
    public AxiosResult<Void> getCode(@PathVariable String phone){
        System.out.println(phone);
        Employee employee = employeeService.findByPhone(phone);
        System.out.println(employee);
        if (employee==null) {
            throw new MyLoginException(AxiosStatus.PHONE_NOT_FOUND);
        }
        //生成验证码,存入Redis,设置2分钟自动销毁
        String code = RandomCodeUtil.munCode();
        stringRedisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES);
        //发送短信,放入子线程中
        AsyncManager.getInstance().schedule(AsyncFactory.sendPhoneCode(phone,code),2L);
        return AxiosResult.success();
    }

    /**
     * 登录验证
     * @param map
     * @param session
     * @return
     */
    @RequestMapping("doLogin")
    public AxiosResult<Void> doLogin(@RequestBody Map<String,String>map, HttpSession session){
        System.out.println(map);
        String phone = map.get("phone");
        String code = map.get("code");
        String s = stringRedisTemplate.opsForValue().get(phone);
        if (s.equals(code)) {
            Employee employee = employeeService.findByPhone(phone);
            //将登陆信息放入session
            session.setAttribute("user",employee);
            //清除
            stringRedisTemplate.delete(phone);
            System.out.println(AxiosResult.success().getData());
            return AxiosResult.success();
        }
        throw new MyLoginException(AxiosStatus.CODE_CHECK_ERROR);
    }
}


十、手动打印日志

  在项目发布后没有控制台让我们查看BUG,此时我们就需要记录程序的报错信息

  一般我们使用log4j来记录错误信息

先导入lombok的jar,在需要记录日志的类上加上@Log4j就可以直接使用了

例如

try {
                    client.sendSms(sendSmsRequest);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(phone + "+手机验证码发送失败:" + e.getMessage());
                }

十一、跨域问题的解决以及登录拦截的实现


十二、分页查询以及日期时区问题的解决

前端部分页面

<div class="box-body">
                        <table id="example2" class="table table-bordered table-hover">
                            <thead>
                            <tr>
                                <th><input type="checkbox" @change="chooseAll"/>全选</th>
                                <th>id</th>
                                <th>头像</th>
                                <th>姓名</th>
                                <th>手机号</th>
                                <th>工资</th>
                                <th>地址</th>
                                <th>入职时间</th>
                                <th>操作</th>
                            </tr>
                            </thead>
                            <tbody ref="table_Body">
                            <tr v-for="(item,index) in tableData" :key="index">
                                <td><input type="checkbox" class="check_td"
                                           @click="chooseDeleteItem(item.employeeId,$event)"></td>
                                <td class="id_td">{{item.employeeId}}</td>
                                <td><img :src="item.employeeAvatar" width="35px" height="35px"></td>
                                <td>{{item.employeeName}}</td>
                                <td>{{item.employeePhone}}</td>
                                <td>{{item.employeeSalary}}</td>
                                <td>{{item.employeeAddress}}</td>
                                <td>{{item.employeeTime}}</td>
                                <td>
                                    <button class="btn btn-sm  btn-info" @click="findById(item.employeeId)"
                                            data-toggle="modal" data-target="#editDialog">
                                        编辑
                                    </button>
                                    <button class="btn btn-sm  btn-warning" @click="doDeleteBtn(item.employeeId)"
                                            data-toggle="modal" data-target="#delModal">
                                        删除
                                    </button>
                                </td>
                            </tr>
                            </tbody>

                        </table>
                    </div>


前端部分JS代码

let vue = new Vue({
    el: "#app",
    data: {
        tableData: [],
        currentPage: 1,
        pageSize: 5,
        total: 0,
        imgUrl: '',
        formData: {},
        deleteIds: []
    },
    created() {
        this.findPage();
    },
    methods: {
        //查询全部
        findAll() {
            myaxios.get(`employee/findAll`).then(response => {
                let {data} = response;
                this.tableData = data;
            })
        },
        //分页查询
        findPage() {
            myaxios.get(`employee/findPage?currentPage=${this.currentPage}&pageSize=${this.pageSize}`).then(response => {
                let {total, list} = response;
                this.total = total;
                this.tableData = list;
            })
        },
        //分页回掉函数
        pageChange(page) {
            this.currentPage = page;
            var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
            for (let i = 0; i < elementsByClassName.length; i++) {
                elementsByClassName[i].checked=false;
            }
            this.findPage();
        }
}


后台

Service层

@Override
    public List<Employee> findAll() {
        List<Employee> employees = employeeMapper.selectByExample(null);
        return employees;
    }

    @Override
    public PageBean<Employee> findPage() {
        List<Employee> employees = employeeMapper.selectByExample(null);
        PageInfo<Employee> pageInfo = new PageInfo<>(employees);
        return PageBean.init(pageInfo.getTotal(),employees);
    }


Controller层

/**
     * 查询全部
     *
     * @return
     */
    @GetMapping("findAll")
    public AxiosResult<List<Employee>> findAll() {
        List<Employee> all = employeeService.findAll();
        return AxiosResult.success(all);
    }

    /**
     * 分页查询
     *
     * @param currentPage 当前页
     * @param pageSize    每一页大小
     * @return
     */
    @GetMapping("findPage")
    public AxiosResult<PageBean<Employee>> findPage(int currentPage, int pageSize) {
        //开启分页
        PageHelper.startPage(currentPage, pageSize);
        PageBean<Employee> all = employeeService.findPage();
        return AxiosResult.success(all);
    }


 这里我们查询后,会发现时间会和数据库中的不同,这是因为数据库的DateTime类型没有时区(time zone)信息,因此,存入的是本地时间,并且丢掉了时区信息。如果你把数据库服务器的时区改了,或者把应用服务器的时区改了,读出来的日期和时间就是错误的。所以这里我们要上设置时区,

在实体类相应的时间属性上加上注解

@JsonFormat(pattern = "yyyy-MM-dd",timezone = "CMT+8")

因为是maven工程,在依赖了相关Jackson的jar后,会自动帮我们依赖

十三、文件上传——使用阿里云对象存储

前端部分JS代码

//选择图片上传
        chooseAvatar(e) {
            let file = e.target.files[0];
            let formData = new FormData();
            formData.append("avatar", file);
            myaxios.post("common/upload/", formData, {headers: {"Content-Type": "multipart/form-data"}})
                .then(response => {
                    // console.log(response)
                    this.imgUrl = response;
                    this.formData.employeeAvatar = response;
                })
        }


 工具类(需要依赖阿里云相关jar)

package com.shangma.cn.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author: JAVASM
 * @className: UploadUtil
 * @description:
 * @date: 2021/6/15 21:34
 * @version: 0.1
 * @since: jdk1.8
 */
public class UploadUtil {

    private static Properties properties;

    static {
        //加载配置文件
        try {
            properties = new Properties();
            properties.load(SmsUtil.class.getClassLoader().getResourceAsStream("aliyun-oss.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件上传
     * @param fileName 文件名
     * @param in 流
     * @return
     */
    public static String upload(String fileName, InputStream in) {
        String endpoint = properties.getProperty("aliyun-oss.endpoint");
        String accessKeyId = properties.getProperty("aliyun-oss.accessKeyId");
        String accessKeySecret = properties.getProperty("aliyun-oss.accessKeySecret");
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        //第一参数: 表示bucket名称
        //第二个参数: 文件名称 携带后缀
        ossClient.putObject(properties.getProperty("aliyun-oss.bucketName"), fileName, in);
        ossClient.shutdown();
        String url = properties.getProperty("aliyun-oss.imgUrl") + fileName;
        return url;
    }
}


 Controller层

/**
     * 上传文件
     * @param avatar
     * @return
     * @throws IOException
     */
    @RequestMapping("upload")
    public AxiosResult<String> upload(@RequestPart Part avatar) throws IOException {
        //重命名
        // TODO: 2021/6/15 不能上传文件
        System.out.println(avatar);
        String fileName = System.nanoTime() + "." + StringUtils.getFilenameExtension(avatar.getSubmittedFileName());
        String url = UploadUtil.upload(fileName, avatar.getInputStream());
        return AxiosResult.success(url);
    }


十四、添加员工信息

前端页面

<div class="box-header">
                        <div class="form-group form-inline">
                            <div class="btn-group">
                                <button type="button" class="btn  btn-danger" title="新建" @click="formData={},imgUrl=''"
                                        data-toggle="modal"
                                        data-target="#editDialog">新建
                                </button>
                                <button type="button" class="btn  btn-success"
                                        data-toggle="modal"
                                        data-target="#delModal">批量删除
                                </button>
                                <button type="button" class="btn  btn-primary">导入</button>
                                <button type="button" class="btn  btn-info">导出</button>
                            </div>
                        </div>
                    </div>





模态框
    <!--新建 编辑弹框-->
    <div class="modal fade" id="editDialog">
        <div class="modal-dialog modal-lg edit-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h4 class="modal-title">员工操作</h4>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">×</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="card-body">
                        <div class="form-group row">
                            <label class="col-sm-2 col-form-label">员工姓名:</label>
                            <div class="col-sm-10">
                                <input type="text" v-model="formData.employeeName" class="form-control"
                                       placeholder="请输入员工姓名">
                            </div>
                        </div>
                        <div class=" form-group row">
                            <label class="col-sm-2 col-form-label">员工手机:</label>
                            <div class="col-sm-10">
                                <input type="text" v-model="formData.employeePhone" class="form-control"
                                       placeholder="请输入员工手机"/>
                            </div>
                        </div>

                        <div class=" form-group row">
                            <label class="col-sm-2 col-form-label">员工工资:</label>
                            <div class="col-sm-10">
                                <input type="text" v-model="formData.employeeSalary" class="form-control"
                                       placeholder="请输入员工工资"/>
                            </div>
                        </div>


                        <div class=" form-group row">
                            <label class="col-sm-2 col-form-label">员工地址:</label>
                            <div class="col-sm-10">
                                <input type="text" v-model="formData.employeeAddress" class="form-control"
                                       placeholder="请输入员工地址"/>
                            </div>
                        </div>


                        <div class=" form-group row">
                            <label class="col-sm-2 col-form-label">入职时间:</label>
                            <div class="col-sm-10">
                                <input id="date" v-model="formData.employeeTime" type="date" class="form-control"/>
                            </div>
                        </div>
                        <div class=" form-group row">
                            <label class="col-sm-2 col-form-label">员工头像</label>
                            <div class="col-sm-10">
                                <label class="btn btn-primary">
                                    <input type="file" @change="chooseAvatar" style="display:none;" id="avater">
                                    上传图片
                                </label>
                                <img :src="imgUrl" alt="" width="100px" height="100px"
                                     style="border: 1px solid #ccc; margin-left: 100px">
                            </div>
                        </div>
                    </div>

                </div>
                <div class="modal-footer ">
                    <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-success" @click="addOrEdit" data-dismiss="modal">确定</button>
                </div>
            </div>
        </div>
    </div>


前端JS

//添加或修改确认按钮
        addOrEdit() {

            if (this.formData.employeeId) {
                //修改
            } else {
                //添加
                //如果没有图片,添加默认图片
                this.formData.employeePassword='123456';
                if (this.formData.employeeAvatar) {
                    myaxios.post(`employee/addEmployee/`, this.formData)
                        .then(response => {
                            this.findPage();
                        });
                } else {
                    this.formData.employeeAvatar = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
                    myaxios.post(`employee/addEmployee/`, this.formData)
                        .then(response => {
                            this.findPage();
                        });
                }
            }
        }


Service

@Override
    public void addEmployee(Employee employee) {
        employeeMapper.insert(employee);
    }

Controller

/**
     * 添加
     * @param employee
     * @return
     */
    @PostMapping("addEmployee")
    public AxiosResult<Void> addEmployee(@RequestBody Employee employee) {
        employeeService.addEmployee(employee);
        return AxiosResult.success();
    }

十五、改造前端Axios以及解决返回值的问题

自定义Axios,可以解决重复写基本路径、设置是否携带cookie,设置请求时间,设置拦截器(请求之前拦截,返回值响应拦截),

let myaxios = axios.create({
    //设置基础路径
    baseURL:'http://localhost:8080/',
    //设置携带cookie
    withCredentials:true,
    //设置请求时间
    timeout:5000
});

// 请求之前拦截
myaxios.interceptors.request.use(function (config) {
    // config.baseURL='http://localhost:8080/';
    return config;
}, function (error) {
    return Promise.reject(error);
});

// 响应拦截器
myaxios.interceptors.response.use(function (response) {
    console.log(response);
    let{status,massage,data}=response.data;
    if (status==2000){
        //console.log(data)
        return data;
    }
    if (status==4004){
        alert("登录过期,请重新登录")
        location.replace("./login.html");
    }
    else {
        console.log(massage);
        //阻止代码向下执行
        return Promise.reject(false);
    }
}, function (error) {
    return Promise.reject(error);
});


  当我们给前端返回数据的时候,data有可能是null,这就有可能在前端产生错误,因此,我们需要在返回数据时进行一次判断,返回不为空的值

  我们在返回值进行json序列化的时候,只序列化有数据的,在返回值类AxiosResult上加@JsonInclude(JsonInclude.Include.NON_NULL)就可以进行过滤。

十六、修改员工信息

前端页面同查询和添加

前端JS

//添加或修改确认按钮
        addOrEdit() {

            if (this.formData.employeeId) {
                //修改
                myaxios.put(`employee/updateEmployee/`, this.formData).then(() => {
                    this.findPage();
                })
            } else {
                //添加
                //如果没有图片,添加默认图片
                this.formData.employeePassword='123456';
                if (this.formData.employeeAvatar) {
                    myaxios.post(`employee/addEmployee/`, this.formData)
                        .then(response => {
                            this.findPage();
                        });
                } else {
                    this.formData.employeeAvatar = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
                    myaxios.post(`employee/addEmployee/`, this.formData)
                        .then(response => {
                            this.findPage();
                        });
                }
            }
        },
        //修改按钮点击事件
        findById(id) {
            myaxios.get(`employee/findById/${id}`).then(response => {
                this.formData = response;
                //没有图片添加默认图片,
                if (response.employeeAvatar) {
                    this.imgUrl = response.employeeAvatar;
                } else {
                    this.imgUrl = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
                    this.formData.employeeAvatar = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
                }
            })
        }


Service

@Override
    public Employee findById(Integer id) {
        Employee employee = employeeMapper.selectByPrimaryKey(id);
        return employee;
    }

    @Override
    public void update(Employee employee) {
        employeeMapper.updateByPrimaryKey(employee);
    }


Controller

/**
     * 根据id查询信息
     * @param id
     * @return
     */
    @GetMapping("findById/{id}")
    public AxiosResult<Employee> findById(@PathVariable Integer id) {
        Employee employee = employeeService.findById(id);
        return AxiosResult.success(employee);
    }

    /**
     * 修改
     * @param employee
     * @return
     */
    @RequestMapping("updateEmployee")
    public AxiosResult<Void> updateEmployee(@RequestBody Employee employee){
        employeeService.update(employee);
        return AxiosResult.success();
    }


十七、删除和批量删除员工信息

前端页面同修改,多了一个模态框

<!--删除弹框-->
    <div class="modal fade" id="delModal">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h4 class="modal-title">温馨提示</h4>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">×</span>
                    </button>
                </div>
                <div class="modal-body">
                    你确定要删除吗?
                </div>
                <div class="modal-footer ">
                    <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                    <button type="button" @click="delBtn" class="btn btn-success delSure" data-dismiss="modal">确定
                    </button>
                </div>
            </div>

        </div>

    </div>


前端JS

//获取删除元素
        chooseDeleteItem(id, e) {
            if (e.target.checked) {
                this.deleteIds.push(id);
            } else {
                this.deleteIds.splice(this.deleteIds.findIndex(item => item == id, 1))
            }
        },
        //点击删除按钮
        doDeleteBtn(id) {
            //获取checkbox的值
            var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
            for (let i = 0; i < elementsByClassName.length; i++) {
                elementsByClassName[i].checked=false;
            }
            this.deleteIds=[];
            this.deleteIds.push(id);
        },
        //删除确认按钮
        delBtn(){
            myaxios.delete(`employee/deleteByIds/${this.deleteIds}`).then(()=>{
                console.log(this.tableData.length)
                if (this.tableData.length==1){
                    if (this.currentPage>1){
                        this.currentPage=this.currentPage-1
                    }
                }
                this.findPage()
            })
        },
        //全选
        chooseAll(e){
            if (e.target.checked) {
                var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
                for (let i = 0; i < elementsByClassName.length; i++) {
                    elementsByClassName[i].checked=true;
                }
                //清空数组
                this.deleteIds=[];
                //添加到数组
                var elementsByClassName1 = this.$refs.table_Body.getElementsByClassName("id_td");
                for (let i = 0; i < elementsByClassName1.length; i++) {
                    this.deleteIds.push(elementsByClassName1[i].outerText);
                }
                console.log(this.deleteIds)
            } else {
                var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
                for (let i = 0; i < elementsByClassName.length; i++) {
                    elementsByClassName[i].checked=false;
                }
                //清空数组
                this.deleteIds=[];
            }
        }


Service

@Override
    public void deleteByIds(List<Integer> ids) {
        EmployeeExample employeeExample = new EmployeeExample();
        EmployeeExample.Criteria criteria = employeeExample.createCriteria();
        criteria.andEmployeeIdIn(ids);
        employeeMapper.deleteByExample(employeeExample);
    }

Controller

@DeleteMapping("deleteByIds/{ids}")
    public AxiosResult<Void> deleteByIds(@PathVariable List<Integer> ids){
        employeeService.deleteByIds(ids);
        return AxiosResult.success();
    }

十八、Excel的导出和导入

引入依赖

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>

 导出前端代码

<a type="button"href="http://localhost:8080/common/writeExcel" class="btn  btn-info">导出</a>

导出后台代码

@GetMapping("writeExcel")
    public ResponseEntity<byte []> writeExcel (HttpSession session) throws Exception {
        //1、拿到数据
        List<Employee> list = employeeService.findAll();
        //2、创建workBook
        Workbook workbook = new XSSFWorkbook();
        //3、创建sheet(员工信息sheet)
        Sheet sheet = workbook.createSheet("员工信息表");

        //7、设置一些样式(可选)
        //修改列宽
        sheet.setColumnWidth(3,22*256);
        sheet.setColumnWidth(4,22*256);
        sheet.setColumnWidth(7,60*256);

        //设置单元格样式
        CellStyle cellStyle = workbook.createCellStyle();
        //垂直居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //水平居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        //字体cellStyle.setFont();

        //4、创建行(数据集合的长度)

        //创建标题行
        Row titleRow = sheet.createRow(0);
        List<String> titles = Arrays.asList("员工ID", "员工名称", "员工地址", "员工手机", "入职时间", "员工密码", "员工工资", "员工头像地址");
        for (int i = 0; i < titles.size(); i++) {
            Cell cell =titleRow.createCell(i);
            cell.setCellValue(titles.get(i));
            cell.setCellStyle(cellStyle);
        }


        //创建内容行
        for (int i = 0; i < list.size(); i++) {
            Employee employee = list.get(i);
            Row row = sheet.createRow(i+1);

            //5、创建每一行中的单元格(一个属性一个单元格)
            Cell idCell = row.createCell(0);
            Cell nameCell = row.createCell(1);
            Cell addressCell = row.createCell(2);
            Cell phoneCell = row.createCell(3);
            Cell timeCell = row.createCell(4);
            Cell passwordCell = row.createCell(5);
            Cell salaryCell = row.createCell(6);
            Cell avatarCell = row.createCell(7);

            //6、填充单元格中的文字
            idCell.setCellValue(employee.getEmployeeId());
            nameCell.setCellValue(employee.getEmployeeName());
            addressCell.setCellValue(employee.getEmployeeAddress());
            phoneCell.setCellValue(employee.getEmployeePhone());
            timeCell.setCellValue(sdf.format(employee.getEmployeeTime()));
            passwordCell.setCellValue(employee.getEmployeePassword());
            salaryCell.setCellValue(employee.getEmployeeSalary());
            avatarCell.setCellValue(employee.getEmployeeAvatar());

        }
//        //8、写到本地
//        FileOutputStream out =new FileOutputStream("E:\\employee.xlsx");
//        workbook.write(out);
//
//        //就近原则关闭资源
//        out.close();
//        workbook.close();


        //在浏览器下载
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        workbook.write(stream);

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentDispositionFormData("attachment", URLEncoder.encode("员工信息表.xlsx","utf-8"));
        byte [] bytes = stream.toByteArray();
        ResponseEntity<byte []> responseEntity = new ResponseEntity<byte[]>(bytes,httpHeaders, HttpStatus.OK);

        stream.close();
        workbook.close();
        return responseEntity;

    }


导入前端页面

<label class="btn btn-primary">
        <input type="file" @change="importExcel" style="display:none;">
        导入
</label>

导入前端JS代码

//导入文件
        importExcel(){
            let file = e.target.files[0];
            let formData = new FormData();
            formData.append("excel", file);
            myaxios.post("common/importExcel/", formData, {headers: {"Content-Type": "multipart/form-data"}})
                .then(response => {
                    this.findPage();
                })
        }

 导入后台代码

@PostMapping("importExcel")
    public AxiosResult<Void> importExcel(@RequestPart Part part) throws IOException, ParseException {
        //1、把上传的文件变成一个workBook
        Workbook workbook = new XSSFWorkbook(part.getInputStream());
        //2、获取第一页
        Sheet sheetAt = workbook.getSheetAt(0);
        //3、获取页中的每一行(每一行都是一个Employee对象)
        //获取最后一行的索引
        int lastRowNum = sheetAt.getLastRowNum();
        for (int i = 1; i < lastRowNum; i++) {
            Row row = sheetAt.getRow(i);
            //获取单元格
            Cell idCell = row.getCell(0);
            Cell nameCell = row.getCell(1);
            Cell addressCell = row.getCell(2);
            Cell phoneCell = row.getCell(3);
            Cell timeCell = row.getCell(4);
            Cell passwordCell = row.getCell(5);
            Cell salaryCell = row.getCell(6);
            Cell avatarCell = row.getCell(7);

            Integer id = (int) idCell.getNumericCellValue();
            String name = nameCell.getStringCellValue();
            String address = addressCell.getStringCellValue();
            String phone = phoneCell.getStringCellValue();
            Date time = sdf.parse(timeCell.getStringCellValue());
            String password = passwordCell.getStringCellValue();
            Double salary = salaryCell.getNumericCellValue();
            String avatar = avatarCell.getStringCellValue();

            //4、每一个单元格都是这个对象的属性值(给属性赋值)
            Employee employee = new Employee(id,name,address,phone,time,password,salary,avatar);
            //5、把对象插入到数据库
            employeeService.addEmployee(employee);

        }
        //6、释放资源

        return AxiosResult.success();
    }