基于spring cloud + nacos + gateway + ssm+的学生管理系统

180 阅读8分钟
  • 前端:vue + spa + axios
  • 后端:spring cloud + nacos + gateway + ssm+

1.环境搭建

1.0 数据库

#学生数据库
CREATE DATABASE nacos_ssm_student;
USE nacos_ssm_student;

CREATE TABLE tb_city(
  c_id VARCHAR(32) PRIMARY KEY COMMENT '城市ID',
  city_name VARCHAR(20) COMMENT '城市名称' ,
  parent_id VARCHAR(32) COMMENT '父ID'
);

INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320000','江苏省','0');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140000','山西省','0');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130000','河北省','0');

INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320100','南京市','320000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320102','玄武区','320100');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('320103','白下区','320100');

INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321300','宿迁市','320000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321322','沭阳县','321300');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('321323','泗阳县','321300');


INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140100','太原市','140000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140106','迎泽区','140100');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140108','尖草坪区','140100');

INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140800','运城市','140000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140823','闻喜县','140800');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('140828','夏 县','140800');

INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130100','石家庄市','130000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130127','高邑县','130100');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('130185','鹿泉市','130100');

INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131000','廊坊市','130000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131003','广阳区','131000');
INSERT INTO tb_city(c_id,city_name,parent_id) VALUES('131022','固安县','131000');


CREATE TABLE `tb_student` (
  `s_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID',
  `sname` VARCHAR(50) DEFAULT NULL COMMENT '姓名',
  `age` INT(11) DEFAULT NULL COMMENT '年龄',
  `birthday` DATETIME DEFAULT NULL COMMENT '生日',
  `gender` CHAR(1) DEFAULT NULL COMMENT '性别',
  `c_id` INT DEFAULT NULL,
  `city_ids` VARCHAR(32) DEFAULT NULL COMMENT '城市:320000,321300,321322'
);

INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (1,'赵三33',21,'2001-01-17 00:00:00','1',1,'320000,321300,321322');
INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (2,'钱四444',1900,'2001-05-16 00:00:00','1',2,'320000,321300,321322');
INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (3,'孙五56',189,'2022-03-15 00:00:00','0',1,'320000,321300,321322');
INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (4,'张三',20,'2020-12-21 00:00:00','0',2,'320000,321300,321322');
INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (5,'xxx',18,'2020-12-21 00:00:00','0',2,'140000,140800,140823');
INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (6,'123',18,'2020-11-01 00:00:00','0',3,'130000,130100,130127');
INSERT  INTO `tb_student`(`s_id`,`sname`,`age`,`birthday`,`gender`,`c_id`,`city_ids`) VALUES (7,'xx',18,'2020-11-02 00:00:00','0',1,'130000,131000,131003');

1.1 后端环境

1.1.1 父项目

  • 坐标

        <!-- 1 确定spring boot的版本-->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.5.RELEASE</version>
        </parent>
    
        <!--2  确定版本-->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <java.version>1.8</java.version>
            <spring-cloud-release.version>Hoxton.SR3</spring-cloud-release.version>
            <nacos.version>1.1.0</nacos.version>
            <alibaba.cloud.version>2.2.1.RELEASE</alibaba.cloud.version>
            <mybatis.starter.version>1.3.2</mybatis.starter.version>
            <mapper.starter.version>2.0.2</mapper.starter.version>
            <mysql.version>5.1.32</mysql.version>
            <pageHelper.starter.version>1.2.5</pageHelper.starter.version>
            <durid.starter.version>1.1.10</durid.starter.version>
            <swagger.version>2.7.0</swagger.version>
            <jwt.jjwt.version>0.9.0</jwt.jjwt.version>
            <jwt.joda.version>2.9.7</jwt.joda.version>
            <beanutils.version>1.9.3</beanutils.version>
            <student.version>1.0-SNAPSHOT</student.version>
        </properties>
    
        <!-- 3 锁定版本-->
        <dependencyManagement>
            <dependencies>
                <!-- sprig cloud-->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud-release.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!--nacos -->
                <dependency>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                    <version>${nacos.version}</version>
                </dependency>
    
                <!--nacos cloud 发现 -->
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
                    <version>${alibaba.cloud.version}</version>
                </dependency>
    
                <!--nacos cloud 配置 -->
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
                    <version>${alibaba.cloud.version}</version>
                </dependency>
    
                <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel -->
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                    <version>${alibaba.cloud.version}</version>
                </dependency>
    
                <!-- mybatis plus启动器 -->
                <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                    <version>3.4.0</version>
                </dependency>
                <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-annotation</artifactId>
                    <version>3.4.0</version>
                </dependency>
    
                <!-- mysql驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql.version}</version>
                </dependency>
                <!-- Druid连接池 -->
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid-spring-boot-starter</artifactId>
                    <version>${durid.starter.version}</version>
                </dependency>
    
                <!--swagger2-->
                <dependency>
                    <groupId>io.springfox</groupId>
                    <artifactId>springfox-swagger2</artifactId>
                    <version>${swagger.version}</version>
                </dependency>
                <dependency>
                    <groupId>io.springfox</groupId>
                    <artifactId>springfox-swagger-ui</artifactId>
                    <version>${swagger.version}</version>
                </dependency>
    
                <!--jwt-->
                <!--JavaBean工具类,用于JavaBean数据封装-->
                <dependency>
                    <groupId>commons-beanutils</groupId>
                    <artifactId>commons-beanutils</artifactId>
                    <version>${beanutils.version}</version>
                </dependency>
    
                <!--jwt工具-->
                <dependency>
                    <groupId>io.jsonwebtoken</groupId>
                    <artifactId>jjwt</artifactId>
                    <version>${jwt.jjwt.version}</version>
                </dependency>
    
                <!--joda 时间工具类 -->
                <dependency>
                    <groupId>joda-time</groupId>
                    <artifactId>joda-time</artifactId>
                    <version>${jwt.joda.version}</version>
                </dependency>
    
                <!-- 自定义项目 -->
                <dependency>
                    <groupId>com.czxy</groupId>
                    <artifactId>nacos-ssm-fx-domain-bg12</artifactId>
                    <version>${student.version}</version>
                </dependency>
    
            </dependencies>
    
        </dependencyManagement>
    

1.1.2 domain项目

  • 坐标

        <dependencies>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!--jackson-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-json</artifactId>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-annotation</artifactId>
            </dependency>
    
        </dependencies>
    
  • JavaBean

    package com.czxy.domain;
    
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonFormat;
    import lombok.Data;
    
    import java.util.Date;
    
    
    @TableName("tb_student")
    @Data
    public class Student {
        @TableId("s_id")
        private Integer sid;
    
        @TableField("sname")
        private String sname;
    
        @TableField("age")
        private Integer age;
    
        @TableField("birthday")
        @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
        private Date birthday;
    
        @TableField("gender")
        private String gender;
    
        @TableField("c_id")
        private Integer cid;
    
        @TableField("city_ids")
        private String cityIds;
    
    }
    
    /*
    CREATE TABLE `tb_student` (
      `s_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID',
      `sname` VARCHAR(50) DEFAULT NULL COMMENT '姓名',
      `age` INT(11) DEFAULT NULL COMMENT '年龄',
      `birthday` DATETIME DEFAULT NULL COMMENT '生日',
      `gender` CHAR(1) DEFAULT NULL COMMENT '性别',
      `c_id` INT DEFAULT NULL,
      `city_ids` VARCHAR(32) DEFAULT NULL COMMENT '城市:320000,321300,321322'
    );
     */
    
  • 拷贝:BaseResult

    package com.czxy.vo;
    
    import lombok.Getter;
    
    import java.util.HashMap;
    import java.util.Map;
    
    
    @Getter
    public class BaseResult<T> {
    
        //成功状态码
        public static final int OK = 20000;
        //失败状态码
        public static final int ERROR = 0;
    
        //返回码
        private Integer code;
        //返回消息
        private String message;
    
        //存放数据
        private T data;
        //其他数据
        private Map<String,Object> other = new HashMap<>();
    
        public BaseResult() {
    
        }
    
        public BaseResult(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
        public BaseResult(Integer code, String message, T data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
    
        /**
         * 快捷成功BaseResult对象
         * @param message
         * @return
         */
        public static BaseResult ok(String message){
            return new BaseResult(BaseResult.OK , message);
        }
    
        public static BaseResult ok(String message, Object data){
            return new BaseResult(BaseResult.OK , message, data );
        }
    
        /**
         * 快捷失败BaseResult对象
         * @param message
         * @return
         */
        public static BaseResult error(String message){
            return new BaseResult(BaseResult.ERROR , message);
        }
    
        /**
         * 自定义数据区域
         * @param key
         * @param msg
         * @return
         */
        public BaseResult append(String key , Object msg){
            other.put(key , msg);
            return this;
        }
    
    //    public Integer getCode() {
    //        return code;
    //    }
    //
    //    public String getMessage() {
    //        return message;
    //    }
    //
    //    public T getData() {
    //        return data;
    //    }
    //
    //    public Map<String, Object> getOther() {
    //        return other;
    //    }
    }
    

1.1.3 网关gateway

  • 坐标

        <dependencies>
            <!-- 网关 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
    
            <!-- nacos 服务发现 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
    
    
            <!--JavaBean工具类,用于JavaBean数据封装-->
            <dependency>
                <groupId>commons-beanutils</groupId>
                <artifactId>commons-beanutils</artifactId>
            </dependency>
    
                   <!--jwt工具-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
            </dependency>
    
            <!--joda 时间工具类 -->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
            </dependency>
    
    
        </dependencies>
    
  • yml配置文件:application.yml

    #端口号
    server:
      port: 10010
    spring:
      application:
        name: student-gateway
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848   #nacos服务地址
        gateway:
          discovery:
            locator:
              enabled: true               #开启服务注册和发现的功能,自动创建router以服务名开头的请求路径转发到对应的服务
              lowerCaseServiceId: true    #将请求路径上的服务名配置为小写
    
  • 启动类

    package com.czxy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    
    @SpringBootApplication
    @EnableDiscoveryClient  //服务发现
    public class GatewayApplication {
        public static void main(String[] args) {
            SpringApplication.run(GatewayApplication.class,args);
        }
    }
    
  • 跨域配置类:config/GlobalGatewayCorsConfig.java

    package com.czxy.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.cors.reactive.CorsUtils;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.WebFilter;
    import org.springframework.web.server.WebFilterChain;
    import reactor.core.publisher.Mono;
    
    
    @Configuration
    public class GlobalGatewayCorsConfig {
    
        @Bean
        public WebFilter corsFilter2() {
            return (ServerWebExchange ctx, WebFilterChain chain) -> {
                ServerHttpRequest request = ctx.getRequest();
                if (CorsUtils.isCorsRequest(request)) {
                    HttpHeaders requestHeaders = request.getHeaders();
                    ServerHttpResponse response = ctx.getResponse();
                    HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
                    HttpHeaders headers = response.getHeaders();
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
                    headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
                            requestHeaders.getAccessControlRequestHeaders());
                    if (requestMethod != null) {
                        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
                    }
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                    headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                    if (request.getMethod() == HttpMethod.OPTIONS) {
                        response.setStatusCode(HttpStatus.OK);
                        return Mono.empty();
                    }
                }
                return chain.filter(ctx);
            };
        }
    
    }
    

1.1.4 学生服务

  • 坐标

        <dependencies>
            <!--web起步依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- nacos 客户端 -->
            <dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-client</artifactId>
            </dependency>
    
            <!-- nacos 服务发现 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
    
            <!-- mybatis plus启动器 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
            </dependency>
    
            <!-- mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!-- Druid连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
            </dependency>
    
            <!--swagger2-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
            </dependency>
            <!-- feign 远程调用 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <!-- 自定义项目 -->
            <dependency>
                <groupId>com.czxy</groupId>
                <artifactId>nacos-ssm-fx-domain-bg12</artifactId>
            </dependency>
    
        </dependencies>
    
  • yml配置文件

    #端口号
    server:
      port: 9001
    
    spring:
      application:
        name: student-plus-service          #服务名
      datasource:
        driverClassName: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/nacos_ssm_student?useUnicode=true&characterEncoding=utf8
        username: root
        password: 1234
        druid:    #druid 连接池配置
          initial-size: 1       #初始化连接池大小
          min-idle: 1           #最小连接数
          max-active: 20        #最大连接数
          test-on-borrow: true  #获取连接时候验证,会影响性能
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848   #nacos服务地址
        sentinel:
          transport:
            dashboard: 127.0.0.1:8080
    
  • 启动类

    package com.czxy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class StudentServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(StudentServiceApplication.class,args);
        }
    }
    
  • 配置类:config/Swagger2ConfigurationV3.java

    package com.czxy.config;
    
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.*;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spi.service.contexts.SecurityContext;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Swagger2 配置类,
     * 访问路径:swagger-ui.html
     * 自动注册:
     *     位置:resources/META-INF/spring.factories
     *     内容:
     *        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
     *          com.czxy.config.Swagger2Configuration
     */
    @Configuration
    @EnableSwagger2
    public class Swagger2ConfigurationV3 {
    
        @Bean
        public Docket createRestApi() {
            // 1 确定文档Swagger版本
            Docket docket = new Docket(DocumentationType.SWAGGER_2);
            // 2 设置 api基本信息
            docket.apiInfo(apiInfo());
            // 3 设置自定义加载路径
            docket = docket.select()
                    .apis(RequestHandlerSelectors.basePackage("com.czxy"))
                    .paths(PathSelectors.any())
                    .build();
            //4 设置权限
            docket.securitySchemes(securitySchemes());
            docket.securityContexts(securityContexts());
    
            return docket;
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("API")
                    .description("基于swagger接口文档")
                    .contact(new Contact("梁桐","http://www.javaliang.com","liangtong@itcast.cn"))
                    .version("1.0")
                    .build();
        }
    
        private List<ApiKey> securitySchemes() {
            List<ApiKey> list = new ArrayList<>();
            // name 为参数名  keyname是页面传值显示的 keyname, name在swagger鉴权中使用
            list.add(new ApiKey("Authorization", "Authorization", "header"));
            return list;
        }
    
        private List<SecurityContext> securityContexts() {
            List<SecurityContext> list = new ArrayList<>();
            list.add(SecurityContext.builder()
                    .securityReferences(defaultAuth())
                    .forPaths(PathSelectors.regex("^(?!auth).*$"))
                    .build());
            return list;
        }
    
        private List<SecurityReference> defaultAuth() {
            AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
            AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
            authorizationScopes[0] = authorizationScope;
            List<SecurityReference> list = new ArrayList();
            list.add(new SecurityReference("Authorization", authorizationScopes));
            return list;
        }
    
    }
    

1.2 前端环境

1.2.1 创建项目:

vue create nacos-fx-spa-bg12

1.2.2 整合axios

  • 下载axios

    cnpm install axios --save
    
  • 抽取工具 request.js

    npm i element-ui --save
    

    src/utils/request.js

    import axios from 'axios'
    import { MessageBox, Message } from 'element-ui'
    
    // 方式1:设置基本路径
    // axios.defaults.baseURL='http://localhost:10010/api'
    
    // 方式2:create an axios instance,并设置基本路径
    const service = axios.create({
      baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
      // withCredentials: true, // send cookies when cross-domain requests
      timeout: 5000 // request timeout
    })
    
    
    
    // request interceptor
    service.interceptors.request.use(
      config => {
        // 请求头中追加token
        let token = localStorage.getItem('token')
        if (token) {
          config.headers.Authorization = token
        }
        return config
      },
      error => {
        // do something with request error
        console.log(error) // for debug
        return Promise.reject(error)
      }
    )
    
    // response interceptor
    service.interceptors.response.use(
    
      response => {
        const res = response.data
    
        // if the custom code is not 20000, it is judged as an error.
        if (res.code !== 20000) {
          Message({
            message: res.message || 'Error',
            type: 'error',
            duration: 5 * 1000
          })
    
          // ajax异常提示信息 (路径信息,数据)
          console.info(response.config, response.data )
          return Promise.reject(new Error(res.message || 'Error'))
        } else {
          return res
        }
      },
      error => {
        console.log('err' + error) // for debug
        if(error.response.status == 401) {
          MessageBox.confirm(error.response.data, '重新登录确认框', {
            confirmButtonText: '重新登录',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            // 删除token
            localStorage.removeItem('token')
            location.reload()
          })
        } else {
          Message({
            message: error.message,
            type: 'error',
            duration: 5 * 1000
          })
        }
        return Promise.reject(error)
      }
    )
    
    export default service
    
  • 抽取api:

    • 将ajax抽取到一个student.js文件中,student.js调用request.js(使用增强后的axios)

      // 导入工具 request.js
      import axios from '@/utils/request.js'
      
      // 编写功能方法
      export function selectAll() {
          return axios.get('/student-plus-service/student')
      }
      
    • request.js ajax访问路径的前缀baseURL,抽取到配置文件中 .env.development

在这里插入图片描述

~~~
# just a flag
ENV = 'development'

# base api
VUE_APP_BASE_API = 'http://localhost:10010/'
~~~

2. 学生管理

2.1 查询所有的学生

2.1.1 后端

  • 步骤:

    • 步骤1:编写mapper,通用mapper
    • 步骤2:编写service,通用service 接口 + 实现类
    • 步骤3:编写controller
  • 步骤1:编写mapper,通用mapper

    package com.czxy.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.domain.Student;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface StudentMapper extends BaseMapper<Student> {
    }
    
  • 步骤2:编写service,通用service 接口 + 实现类

    • 接口

      package com.czxy.service;
      
      import com.baomidou.mybatisplus.extension.service.IService;
      import com.czxy.domain.Student;
      
      
      public interface StudentService extends IService<Student> {
      }
      
    • 实现类

      package com.czxy.service.impl;
      
      import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
      import com.czxy.domain.Student;
      import com.czxy.mapper.StudentMapper;
      import com.czxy.service.StudentService;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;
      
      
      @Service
      @Transactional
      public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {
      }
      
  • 步骤3:编写controller, GET请求 + BaseResult

    package com.czxy.controller;
    
    import com.czxy.domain.Student;
    import com.czxy.service.StudentService;
    import com.czxy.vo.BaseResult;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    
    @RestController
    @RequestMapping("/student")
    public class StudentController {
        @Resource
        private StudentService studentService;
    
        @GetMapping
        public BaseResult selectAll() {
            // 1.请求数据
            // 2 查询
            List<Student> list = studentService.list();
            // 3 响应结果
            return BaseResult.ok("查询成功", list);
            /*
            BaesResult baseResult = new BaseResult();
            baseResult.setCode(20000);          //自定义编码
            baseResult.setMessage("查询成功");  //提示信息
            baseResult.setData( list );         //结果数据
             */
        }
    }
    

2.1.2 前端

1)访问页面
  • 步骤1:修改App.vue页面,确定访问路径

    <template>
      <div id="app">
        <!-- 导航条,切换路由 -->
        <div id="nav">
          <router-link to="/">Home</router-link> |
          <router-link to="/studentList">学生列表</router-link>
        </div>
        <!-- 路由视图,用于显示内容 -->
        <router-view/>
      </div>
    </template>
    
    <style>
    
    </style>
    
  • 步骤2:修改路由 src/router/index.js 编写访问路径

    import Home from '../views/Home.vue'
    const routes = [
      {
        path: '/',
        name: 'Home',
        component: Home
      },
      {
        path: '/studentList',
        name: '学生列表',
        component: () => import('../views/StudentList.vue')
      }
    ]
    
  • 步骤3:创建StudentList.vue页面

    <template>
      <div>
        学生列表页面
      </div>
    </template>
    
    <script>
    export default {
    
    }
    </script>
    
    <style>
    
    </style>
    
2)显示查询结果
  • 步骤:

    • 步骤1:导入api,获得查询所有函数
    • 步骤2:编写查询所有学生函数,调用导入api函数,完成ajax操作,查询结果保存studentList
    • 步骤3:页面加载成功后,查询
    • 步骤4:遍历展示
<template>
  <div>
    <!-- 4 展示数据 -->
    <table border="1">
      <tr>
        <td>编号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>生日</td>
        <td>性别</td>
        <td>班级</td>
        <td>操作</td>
      </tr>
      <tr v-for="(student,index) in studentList" :key="index">
        <td>{{student.sid}}</td>
        <td>{{student.sname}}</td>
        <td>{{student.age}}</td>
        <td>{{student.birthday}}</td>
        <td>{{student.gender == 1 ? '男' : '女'}}</td>
        <td>{{student.cid}}</td>
        <td>
          修改
          删除
        </td>
      </tr>
    </table>
  </div>
</template>

<script>
//1 导入查询函数
import {selectAll} from '../api/student'
export default {
  data() {
    return {
      studentList: []
    }
  },
  methods: {
    async selectAllStudent() {  
      //2 ajax查询 -- request.js 中对响应结果response进行再处理,返回的是 response.data
      let {data} = await selectAll()   // {data} 从 baseResult中结果data,也就是查询结果
      // 将查询结果保存数据区域
      this.studentList = data
    }
  },  
  mounted() {
    // 3 页面加载成功,查询所有
    this.selectAllStudent()
  },
}
</script>

<style>

</style>

2.2 条件查询 + 分页

2.2.1 后端步骤

  • 确定访问路径

    http://localhost:10010/student-plus-service/student/condition/{size}/{current}
    
  • 步骤1:准备条件封装StudentVo

  • 步骤2:编写controller,获得分页参数+条件,返回 Page进行结果封装

  • 步骤3:编写service

    • 1)查询条件
    • 2)分页
    • 3)查询
    • 4)关联
    • 5)处理/返回结果
  • 步骤4:拷贝MyBatisPlus分页配置类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pEssSDxP-1645576553138)(assets/image-20220222150608792.png)]

2.2.2 后端实现

  • 步骤1:准备条件封装StudentVo

    package com.czxy.vo;
    
    import lombok.Data;
    
    
    @Data
    public class StudentVo {
        private String cid;
        private String sname;
        private String startAge;
        private String endAge;
    }
    
  • 步骤2:编写controller,获得分页参数+条件,返回 Page进行结果封装

        /**
         *
         * @param studentVo 条件
         * @param size 每页个数
         * @param current 第几页
         * @return
         */
        @PostMapping("/condtion/{size}/{current}")
        public BaseResult condition(
                @RequestBody StudentVo studentVo,
                @PathVariable("size") Integer size,
                @PathVariable("current") Integer current
    
                ){
            // 2 查询
            Page<Student> page = studentService.condition(studentVo,size,current);
    
            // 3 返回
            return BaseResult.ok("查询成功", page);
        }
    
  • 步骤3:编写service

    • 接口

      package com.czxy.service;
      
      import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
      import com.baomidou.mybatisplus.extension.service.IService;
      import com.czxy.domain.Student;
      import com.czxy.vo.StudentVo;
      
      
      public interface StudentService extends IService<Student> {
          /**
           * 分页 + 条件 查询
           * @param studentVo
           * @param size
           * @param current
           * @return
           */
          Page<Student> condition(StudentVo studentVo, Integer size, Integer current);
      }
      
    • 实现类

          @Override
          public Page<Student> condition(StudentVo studentVo, Integer size, Integer current) {
              //1 条件
              QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
              // 1.1 班级
              queryWrapper.eq(StringUtils.isNotBlank(studentVo.getCid()), "c_id", studentVo.getCid());
              // 1.2 姓名 - like %张%
              queryWrapper.like(StringUtils.isNotBlank(studentVo.getSname()),"sname", studentVo.getSname());
              // 1.3 开始年龄
              queryWrapper.ge(StringUtils.isNotBlank(studentVo.getStartAge()),"age", studentVo.getStartAge());
              // 1.4 结束年龄
              queryWrapper.le(StringUtils.isNotBlank(studentVo.getEndAge()),"age", studentVo.getEndAge());
      
              // 2 分页
              Page page = new Page(current, size);
      
              // 3 查询
              this.baseMapper.selectPage(page, queryWrapper);
      
              //TODO 4 关联--查询班级
      
              // 5 返回
              return page;
          }
      
  • 步骤4:拷贝MyBatisPlus分页配置类

    package com.czxy.config;
    
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    @Configuration
    public class MybatisPlusConfig {
    
        /**
         * 配置插件
         * @return
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            // 分页插件
            mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    
            return mybatisPlusInterceptor;
        }
    }
    

2.2.3 前端步骤

  • 步骤1:编写页面 StudentCondition.vue + 路由

  • 步骤2:编写ajax student.js 写 condition函数

  • 步骤3:修改StudentCondition.vue

    • 基本查询:page、studentVo
    • 条件查询
    • 分页查询

2.2.4 前端实现

  • 步骤1:编写页面 StudentCondition.vue + 路由
    在这里插入图片描述
  • 步骤2:编写ajax student.js 写 condition函数

在这里插入图片描述

// 查询所有学生+条件+分页
export function condition(page,studentVo) {
    var url = `/student-plus-service/student/condtion/${page.size}/${page.current}`
    return axios.post(url, studentVo)
}
  • 步骤3:修改StudentCondition.vue

    • 基本查询:page、studentVo

      <template>
        <div>
          <!-- 列表展示 -->
          <table border="1">
            <tr>
              <td>编号</td>
              <td>姓名</td>
              <td>年龄</td>
              <td>生日</td>
              <td>性别</td>
              <td>班级</td>
              <td>操作</td>
            </tr>
            <tr v-for="(student,index) in page.records" :key="index">
              <td>{{student.sid}}</td>
              <td>{{student.sname}}</td>
              <td>{{student.age}}</td>
              <td>{{student.birthday}}</td>
              <td>{{student.gender == 1 ? '男' : '女'}}</td>
              <td>{{student.cid}}</td>
              <td>
                修改
                删除
              </td>
            </tr>
          </table>
        </div>
      </template>
      
      <script>
      // 导入ajax函数
      import {condition} from '../api/student'
      export default {
        data() {
          return {
            page: {     //分页
              size: 3,
              current: 1
            },
            studentVo: {  //条件
              cid: '',
              sname: '',
              startAge: '',
              endAge: '',
            }
          }
        },
        methods: {
          async conditionFn(num) {
            // 设置第几页
            if(num) {
              this.page.current = num
            }
      
            // ajax查询
            let { data } = await condition(this.page, this.studentVo)
      
            // 获得结果
            this.page = data
          }
        },
        mounted() {
          // 查询第一页
          this.conditionFn(1)
        },
      }
      </script>
      
      <style>
      
      </style>
      
    • 条件查询:(使用模拟数据显示班级列表)

            classesList: [ //班级的模拟数据
              {cid:1, cname: 'Java56'},
              {cid:2, cname: 'Java78'},
              {cid:3, cname: 'Java12'},
              {cid:4, cname: 'Java34'},
            ]
      
          <!-- 条件查询表单 -->
          班级 <select v-model="studentVo.cid">
              <option value="">--选择班级--</option>
              <option v-for="(classes,index) in classesList" :key="index" :value="classes.cid">{{classes.cname}}</option>
          </select>
          姓名:<input type="text" size="8" v-model="studentVo.sname"/>
          年龄:<input type="text" size="8" v-model="studentVo.startAge"/> -- 
                <input type="text" size="8" v-model="studentVo.endAge"/>
          <input type="button" value="查询" @click="conditionFn(1)" />
      
      <!--条件查询的完整版-->
      <template>
        <div>
          <!-- 条件查询 -->
          班级 <select v-model="studentVo.cid">
              <option value="">--选择班级--</option>
              <option v-for="(classes,index) in classesList" :key="index" :value="classes.cid">{{classes.cname}}</option>
          </select>
          姓名:<input type="text" size="8" v-model="studentVo.sname"/>
          年龄:<input type="text" size="8" v-model="studentVo.startAge"/> -- 
                <input type="text" size="8" v-model="studentVo.endAge"/>
          <input type="button" value="查询" @click="conditionFn(1)" />
          <!-- 列表展示 -->
          <table border="1">
            <tr>
              <td>编号</td>
              <td>姓名</td>
              <td>年龄</td>
              <td>生日</td>
              <td>性别</td>
              <td>班级</td>
              <td>操作</td>
            </tr>
            <tr v-for="(student,index) in page.records" :key="index">
              <td>{{student.sid}}</td>
              <td>{{student.sname}}</td>
              <td>{{student.age}}</td>
              <td>{{student.birthday}}</td>
              <td>{{student.gender == 1 ? '男' : '女'}}</td>
              <td>{{student.cid}}</td>
              <td>
                修改
                删除
              </td>
            </tr>
          </table>
        </div>
      </template>
      
      <script>
      // 导入ajax函数
      import {condition} from '../api/student'
      export default {
        data() {
          return {
            page: {     //分页
              size: 3,
              current: 1
            },
            studentVo: {  //条件
              cid: '',
              sname: '',
              startAge: '',
              endAge: '',
            },
            classesList: [ //班级的模拟数据
              {cid:1, cname: 'Java56'},
              {cid:2, cname: 'Java78'},
              {cid:3, cname: 'Java12'},
              {cid:4, cname: 'Java34'},
            ]
          }
        },
        methods: {
          async conditionFn(num) {
            // 设置第几页
            if(num) {
              this.page.current = num
            }
      
            // ajax查询
            let { data } = await condition(this.page, this.studentVo)
      
            // 获得结果
            this.page = data
          }
        },
        mounted() {
          // 查询第一页
          this.conditionFn(1)
        },
      }
      </script>
      
      <style>
      
      </style>
      
    • 分页查询

          <!-- 分页条 -->
          每页
          <select v-model="page.size" @change="conditionFn(1)">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="5">5</option>
            <option value="10">10</option>
          </select>
          条,
          <a href="" @click.prevent="conditionFn(num)" v-for="(num,index) in page.pages" :key="index">{{num}}</a>,
          跳转到第
          <input type="text" size="4" v-model="page.current" @keyup.enter="conditionFn()">
          页
      

2.2.5 前端完整代码

<template>
  <div>
    <!-- 条件查询 -->
    班级 <select v-model="studentVo.cid">
        <option value="">--选择班级--</option>
        <option v-for="(classes,index) in classesList" :key="index" :value="classes.cid">{{classes.cname}}</option>
    </select>
    姓名:<input type="text" size="8" v-model="studentVo.sname"/>
    年龄:<input type="text" size="8" v-model="studentVo.startAge"/> -- 
          <input type="text" size="8" v-model="studentVo.endAge"/>
    <input type="button" value="查询" @click="conditionFn(1)" />
    <!-- 列表展示 -->
    <table border="1">
      <tr>
        <td>编号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>生日</td>
        <td>性别</td>
        <td>班级</td>
        <td>操作</td>
      </tr>
      <tr v-for="(student,index) in page.records" :key="index">
        <td>{{student.sid}}</td>
        <td>{{student.sname}}</td>
        <td>{{student.age}}</td>
        <td>{{student.birthday}}</td>
        <td>{{student.gender == 1 ? '男' : '女'}}</td>
        <td>{{student.cid}}</td>
        <td>
          修改
          删除
        </td>
      </tr>
    </table>
    <!-- 分页条 -->
    每页
    <select v-model="page.size" @change="conditionFn(1)">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
      <option value="5">5</option>
      <option value="10">10</option>
    </select>
    条,
    <a href="" @click.prevent="conditionFn(num)" v-for="(num,index) in page.pages" :key="index">{{num}}</a>,
    跳转到第
    <input type="text" size="4" v-model="page.current" @keyup.enter="conditionFn()">
    页
  </div>
</template>

<script>
// 导入ajax函数
import {condition} from '../api/student'
export default {
  data() {
    return {
      page: {     //分页
        size: 3,
        current: 1
      },
      studentVo: {  //条件
        cid: '',
        sname: '',
        startAge: '',
        endAge: '',
      },
      classesList: [ //班级的模拟数据
        {cid:1, cname: 'Java56'},
        {cid:2, cname: 'Java78'},
        {cid:3, cname: 'Java12'},
        {cid:4, cname: 'Java34'},
      ]
    }
  },
  methods: {
    async conditionFn(num) {
      // 设置第几页
      if(num) {
        this.page.current = num
      }

      // ajax查询
      let { data } = await condition(this.page, this.studentVo)

      // 获得结果
      this.page = data
    }
  },
  mounted() {
    // 查询第一页
    this.conditionFn(1)
  },
}
</script>

<style>

</style>

2.3 添加或修改

2.3.1 后端实现

    @PostMapping
    public BaseResult saveOrUpdate(@RequestBody Student student) {
        try {
            // 保存或更新:如果没有id进行添加操作;如果有id进行更新操作
            boolean result = studentService.saveOrUpdate(student);
            //处理
            if(result) {
                return BaseResult.ok("编辑成功");
            }

            return BaseResult.error("编辑失败");
        } catch (Exception e) {
            e.printStackTrace();
            return BaseResult.error(e.getMessage());
        }

    }
  • 数据库的id为自动增强,如果JavaBean没有设置,会出现类型不匹配异常。

3. 班级管理

3.1 查询所有班级

3.1.1 后端

  • 步骤:

    • 步骤1:编写JavaBean,Classes
    • 步骤2:编写Mapper,ClassesMapper,实现通用mapper
    • 步骤3:编写service,接口+实现类+通用
    • 步骤4:编写controller
  • 步骤1:编写JavaBean,Classes

    package com.czxy.domain;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    @TableName("tb_class")
    @Data
    public class Classes {
        @TableId(value = "cid", type = IdType.AUTO)
        private Integer cid;
        @TableField("cname")
        private String cname;
    }
    
    /*
    CREATE TABLE `tb_class` (
      `cid` int(11) NOT NULL AUTO_INCREMENT,
      `cname` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`cid`)
    )
     */
    
  • 步骤2:编写Mapper,ClassesMapper,实现通用mapper

    package com.czxy.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.domain.Classes;
    import org.apache.ibatis.annotations.Mapper;
    
    
    @Mapper
    public interface ClassesMapper extends BaseMapper<Classes> {
    }
    
  • 步骤3:编写service,接口+实现类+通用

    • 接口

      package com.czxy.service;
      
      import com.baomidou.mybatisplus.extension.service.IService;
      import com.czxy.domain.Classes;
      
      
      public interface ClassesService extends IService<Classes> {
      }
      
    • 实现类

      package com.czxy.service.impl;
      
      import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
      import com.czxy.domain.Classes;
      import com.czxy.mapper.ClassesMapper;
      import com.czxy.service.ClassesService;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;
      
      
      @Service
      @Transactional
      public class ClassesServiceImpl extends ServiceImpl<ClassesMapper, Classes> implements ClassesService {
      }
      
  • 步骤4:编写controller

    package com.czxy.controller;
    
    import com.czxy.domain.Classes;
    import com.czxy.service.ClassesService;
    import com.czxy.vo.BaseResult;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    
    @RestController
    @RequestMapping("/classes")
    public class ClassesController {
    
        @Resource
        private ClassesService classesService;
    
        /**
         * 查询所有
         * @return
         */
        @GetMapping
        public BaseResult findAll() {
            // 2 查询
            List<Classes> list = classesService.list();
            // 3 返回
            return BaseResult.ok("查询成功", list );
        }
    }