前后端分离:SpringBoot治好了我的时间内耗

1.前后端分离为了什么

1.1前言

​ 本文作为系列文章的第一篇,是铁柱在工作之余对自我学习的总结,以下内容是搭建基础的前后端分离的Demo来展开的,需要对MySQL,Spring,SpringMVC,Mybatis,Vue,Node.js有一定的前置掌握,也可以先行进行简单的学习,下面也会贴出以上几种技术推荐的学习教程供小伙伴们参考,后续还会有更多干货文章,JUC,多线程,集合,Netty,Redis,中间件技术等,全部结合实际展开,感兴趣的小伙伴可以关注点赞走起。

本文全部代码已上传到Gitee仓库: https://gitee.com/lu-tiezhu/separationVue-boot.git

喜欢的小伙伴可以一键start,感谢支持。

1.2前后端分离的前因后果

​ 前后端分离已成为互联网项目开发的业界标准使用方式,通过nginx+tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。这个步骤是系统架构从猿进化成人的必经之路。

​ 核心思想是前端html页面通过http请求调用后端的restuful 风格接口并使用json数据进行交互。以前的JavaWeb项目大多数都是java程序员又当爹又当妈,又搞前端,又搞后端。

​ 随着时代的发展,渐渐的许多大中小公司开始把前后端的界限分的越来越明确,前端工程师只管前端的事情,后端工程师只管后端的事情。正所谓术业有专攻,一个人如果什么都会,那么他毕竟什么都不精。

对于后端java工程师:

​ 把精力放在java基础,设计模式,jvm原理,spring+springmvc原理及源码,linux,mysql事务隔离与锁机制,mongodb,http/tcp,多线程,分布式架构,弹性计算架构,微服务架构,java性能优化,以及相关的项目管理等等。

后端追求的是:三高(高并发,高可用,高性能),安全,存储,业务等等。

对于前端工程师:

​ 把精力放在html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化等等。

前端追求的是:页面表现,速度流畅,兼容性,用户体验等等。

1.3 使用SpringBoot框架开发前后端分离的原因

来看看百度百科关于SpringBoot的介绍:

在这里插入图片描述

SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置。开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

SpringBoot应用系统开发模板的基本架构设计从前端到后台进行说明:前端常使用模板引擎,主要有FreeMarker和Thymeleaf,它们都是用Java语言编写的,渲染模板并输出相应文本,使得界面的设计与应用的逻辑分离,同时前端开发还会使用到Bootstrap、AngularJS、JQuery等;在浏览器的数据传输格式上采用Json,非xml,同时提供RESTfulAPI;SpringMVC框架用于数据到达服务器后处理请求;到数据访问层主要有Hibernate、MyBatis、JPA等持久层框架;数据库常用MySQL;开发工具推荐IntelliJIDEA。

用一句话来说:SpringBoot可以快速搭建后端来实现一个前后端分离的项目,避免大量繁琐的配置,且由于天然良好的支持SpringMVC,可以非常敏捷的开发接口

2.搭建后端基础环境

2.1 技术选型

Java企业级开发常见技术栈:

在这里插入图片描述

开发工具:IDEA 2020,VS Code

Java version “1.8.0_301”
Java™ SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot™ 64-Bit Server VM (build 25.301-b09, mixed mode)

后端框架:

  • Spring 5.x
  • SpringMVC 5.x
  • MySQL 5.7
  • SpringBoot 2.2.11.RELEASE
  • Redis 5.5
  • MybatisPlus 3.4.0
  • spring-boot-devtools
  • hutool
  • swagger-ui
  • Git

前端:

  • Node.js

  • Vue2

  • ElementUI

  • Axios

  • 后续随着文章的进度,将会整合更多技术,争取一个项目吃透Java开发常用技术

2.2搭建数据表

先创建数据库 separation_boot,再创建数据表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for db_scores
-- ----------------------------
DROP TABLE IF EXISTS `db_scores`;
CREATE TABLE `db_scores`  (
  `id` bigint(20) NOT NULL COMMENT '主键id',
  `subjects` varchar(50)  COMMENT '考试科目',
  `stu_number` varchar(100)  COMMENT '学号',
  `score` int(11) NULL DEFAULT NULL COMMENT '分数',
  `describe` varchar(255)  COMMENT '考试说明',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;

-- ----------------------------
-- Records of db_scores
-- ----------------------------
INSERT INTO `db_scores` VALUES (3458451354, '数学', '413026200510101213', 135, '高三年级十月月中考试', '2022-10-10 21:38:37');
INSERT INTO `db_scores` VALUES (3458451351236, '数学', '413026200510101216', 120, '高三年级十月月中考试', '2022-10-10 21:38:37');
INSERT INTO `db_scores` VALUES (3458451354112, '数学', '413026200510101214', 95, '高三年级十月月中考试', '2022-10-10 21:38:37');
INSERT INTO `db_scores` VALUES (3458451389471, '数学', '413026200510101212', 115, '高三年级十月月中考试', '2022-10-10 21:38:37');

-- ----------------------------
-- Table structure for db_students
-- ----------------------------
DROP TABLE IF EXISTS `db_students`;
CREATE TABLE `db_students`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `stu_name` varchar(50)  COMMENT '姓名',
  `stu_
number` varchar(100)  COMMENT '学号',
  `sex` varchar(10)  COMMENT '性别',
  `birth` datetime(0) NULL DEFAULT NULL COMMENT '出生日期',
  `address` varchar(255)  COMMENT '地址',
  `stu_class` varchar(50)  COMMENT '班级',
  `stu_grade` varchar(50)  COMMENT '年级',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;

-- ----------------------------
-- Records of db_students
-- ----------------------------
INSERT INTO `db_students` VALUES (357453451, '刘玉兰', '413026200510101213', '女', '2005-10-10 21:33:57', '河南省郑州市高新区', '02班', '高中三年级', '2022-10-10 21:35:15');
INSERT INTO `db_students` VALUES (357453454, '李辉', '413026200510101214', '男', '2005-10-10 21:33:57', '河南省郑州市高新区', '01班', '高中三年级', '2022-10-10 21:35:15');
INSERT INTO `db_students` VALUES (357453455, '张梦怡', '413026200510101212', '女', '2005-10-10 21:33:57', '河南省郑州市高新区', '01班', '高中三年级', '2022-10-10 21:35:15');
INSERT INTO `db_students` VALUES (357453457, '王明明', '413026200510101216', '男', '2005-10-10 21:33:57', '河南省郑州市高新区', '04班', '高中三年级', '2022-10-10 21:35:15');

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for db_dictionary
-- ----------------------------
DROP TABLE IF EXISTS `db_dictionary`;
CREATE TABLE `db_dictionary`  (
  `id` int(11) NOT NULL COMMENT '主键',
  `code_name` varchar(100)  COMMENT '字典名称',
  `fid` int(11) NULL DEFAULT NULL COMMENT '父id',
  `describe` varchar(255)  DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB ;

-- ----------------------------
-- Records of db_dictionary
-- ----------------------------
INSERT INTO `db_dictionary` VALUES (101, '年级', 1, NULL);
INSERT INTO `db_dictionary` VALUES (102, '班级', 1, NULL);
INSERT INTO `db_dictionary` VALUES (103, '学科', 1, NULL);
INSERT INTO `db_dictionary` VALUES (10101, '高中一年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10102, '高中二年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10103, '高中三年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10104, '初中一年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10105, '初中二年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10106, '初中三年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10201, '01班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10202, '02班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10203, '03班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10204, '04班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10205, '05班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10301, '语文', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10302, '数学', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10303, '英语', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10304, '政治', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10305, '历史', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10306, '地理', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10307, '物理', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10308, '化学', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10309, '生物', 103, NULL);
INSERT INTO `db_dictionary` VALUES (103010, '体育', 103, NULL);

SET FOREIGN_KEY_CHECKS = 1;

2.3 添加maven依赖和配置文件

IDEA创建项目

在这里插入图片描述
在这里插入图片描述

废话不多说,直接上POM文件内容

    <groupId>com.tiezhu</groupId>
    <artifactId>separationVue-boot</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.11.RELEASE</version>
        <relativePath/>
    </parent>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.2.11.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.4</version>
        </dependency>
        <!--代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.2</version>
        </dependency>
        <!--swagger接口注解插件-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>
        
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes> <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

下面是application.yml文件的内容

# 应用服务 WEB 访问端口
server:
  port: 9090
# 应用名称
spring:
  application:
     name: separationVue-boot
  devtools:
    restart:
      enabled: false
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/separation_boot?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: com.tiezhu.separation.pojo
  mapper-locations: classpath:com/tiezhu/separation/mapper/xml/*.xml

2.4 构建基础目录

目录如下图:

在这里插入图片描述
在这里插入图片描述

2.5 整合MybatisPlus和代码生成器

对MybatisPlus不熟悉的小伙伴可以阅读我的这篇入门教程:

Mybatis-Plus入门(一)_道上叫我卢铁柱的博客-CSDN博客

MybatisPlus的相关配置参数已经放在yml文件当中:

mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 配置打印sql执行日志
type-aliases-package: com.tiezhu.separation.pojo 实体类路径
mapper-locations: classpath:com/tiezhu/separation/mapper/xml/*.xml 各个类的Mybatis实现文件

代码生成器,名如其字(也可以说顾名思义),这玩意是根据数据库的表生成相关增删改查代码的,这样避免了繁琐的重复代码编写,前面提到我们使用的是MybatisPlus框架,它是Mybatis的超集,就是说我们可以同时使用Mybatis,在代码生成器的加持上,我们可以减少编写基础的Mybatis代码,相关依赖已经贴在maven中,废话不多说,直接上代码

package com.tiezhu.separation;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;

/**
 * @author tiezhu
 * @since 2021/8/7
 */
public class CodeGenerator {

    @Test
    public void run() {

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("tiezhu");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖
        gc.setServiceName("%sService");    //去掉Service接口的首字母I
        gc.setIdType(IdType.ID_WORKER); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式

        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/separation_boot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("separation"); //模块名
        pc.setParent("com.tiezhu");
        pc.setController("controller");
        pc.setEntity("pojo");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("db_students");//此处为表名,将需要生成的数据表放在这里即可
        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix("db_"); //生成实体时去掉表前缀

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);


        // 6、执行
        mpg.execute();
    }
}

2.6 整合Redis

Redis作为一个快速的缓存数据库,使用键值对结构可以完成很多操作,如分布式锁,秒杀,页面缓存,消息传输等功能,Redis也是将在本项目发挥很多种可能。

在配置文件中添加

redis:
  host: 127.0.0.1
  port: 6379
  lettuce:
    shutdown-timeout: 2000
  timeout: 2000
  jedis:
    pool:
      max-wait: -1

在config文件夹下面添加redis的优化配置类:

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

为了一步到位我们直接整上Redisson的配置类,在config文件夹下面添加:

package com.tiezhu.separation.config;


import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfiguration {
    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        // 可以用"rediss://"来启用SSL连接
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }

}

在项目和日常使用中我们可以通过两种方法来注入使用redis

@Autowired
private RedissonClient redissonClient;

//需要配置类RedissonConfiguration
@Autowired
private RedissonClient redissonClient;

2.7配置SwaggerUI

先来讲讲Swagger是什么:

使用Swagger根据在代码中使用自定义的注解来生成接口文档,这个在前后端分离的项目中很重要。这样做的好处是 在开发接口时可以通过swagger 将接口文档定义好,同时也方便以后的维护。

  • 号称时最流行的 API 框架
  • 接口文档在线生成,避免同步的麻烦
  • 可以支持在线对接口执行测试
  • 支持多语言

同样的在config文件里添加Swagger配置类:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket adminApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("indexApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.and(PathSelectors.regex("/index/.*")))
                .build();

    }

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("separation-SpringBoot前后端分离学习项目API文档")
                .description("本文描述了separationVue-boot接口的定义")
                .version("1.0")
                .contact(new Contact("tiezhu", "http://127.0.0.1", "1766419110@qq.com"))
                .build();
    }
}

最终启动起来是这样的效果(后面的代码还没有写,所以现在还是启动不了的,后续跟上就ok):

在这里插入图片描述

3.实现业务代码

基本的东西料理妥当大概这样:

在这里插入图片描述

3.1 实体类和工具类

实体类由代码生成器生成之后可不用修改,在createTime上加入如下代码:

@TableField(fill = FieldFill.INSERT_UPDATE)

Result类是实现前后端分离的核心关键代码,后端所查询的数据均通过它渲染成JSON传输到前端。

@Data
public class Result {

    @ApiModelProperty(value = "是否成功")
    private Boolean success;

    @ApiModelProperty(value = "状态码")
    private Integer code;
    @ApiModelProperty(value = "返回消息")
    private String message;
    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data=new HashMap<>();

    private final static int SUCCESS=200;
    private final static int ERROR=404;
    private Result(){}

    public static Result ok(){
        Result r= new Result();
        r.setSuccess(true);
        r.setCode(SUCCESS);
        r.setMessage("success");
        return r;
    }
    public static Result error(){
        Result r = new Result();
        r.setSuccess(false);
        r.setCode(ERROR);
        r.setMessage("error");
        return r;
    }

    public Result success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public Result message(String message){
        this.setMessage(message);
        return this;
    }

    public Result code(Integer code){
        this.setCode(code);
        return this;
    }

    public Result data(String key, Object value){
        this.data.put(key, value);
        return this;
    }

    public Result data(Map<String, Object> map){
        this.setData(map);
        return this;
    }
}

3.2 业务层和数据层

业务层和数据层使用代码生成器生成的即可,可以直接在Controller层调用简单的增删改查操作,阅读本文的小伙伴们也可以尝试使用Mybatis去实现一些自己的想实现的功能。

在这里插入图片描述

3.3 SpringMVC和前后端分离的关系

学过SpringMVC的小伙伴都知道,它是一个实现MVC模式的Web开发框架,通过DispatchServlet实现对用户请求的解析,调用业务层实现数据的增删改查,将返回的数据渲染的前端,在传统的前后端不分离的项目中,Controller层一般使用的注解为 @Controller ,前端一般使用模板引擎来渲染数据。

当项目来到前后端分离的模式后,我们就需要在controller层加入更多的注解,如 @CrossOrigin @RestController @RequestMapping,其中RestController 注解尤其重要,配合restful风格的接口,将数据转化为JSON或者其他格式返回给前端,这些结果的如何渲染到页面上去就是前端的事情了,SpringMVC完美的支持了Http的几种请求方式,POST,GET,DELETE,PUT等,这些配合JQuery或者Axios等前端请求技术,构建起一个完美的前后端开发技术栈。

3.4 基础CRUD接口开发

对于我们已经建的三个数据库且已经有了基础的代码,剩下的就是开发一些简单的接口,足以本篇文章使用即可,有创造力的小伙伴也可以着急实现更多复杂的功能。

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author tiezhu
 * @since 2022-10-10
 */
@Slf4j
@Api(description = "成绩数据操作接口")
@CrossOrigin
@RestController
@RequestMapping("/index/score")
public class ScoresController {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ScoresService scoresService;
    @Autowired
    private DictionaryService dictionaryService;

    @ApiOperation("查询成绩信息列表")
    @GetMapping("/list")
    public Result selectStudentList(){
        List<Scores> list = scoresService.list();
        return Result.ok().data("list",list);
    }
    @ApiOperation("根据学号查询学生成绩")
    @GetMapping("/get/{key}")
    public Result selectByIdOrName(@ApiParam(name = "key", value = "查询关键字", required = true) @PathVariable String key){
        QueryWrapper<Scores> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("stu_number",key);
        List<Scores> list = scoresService.list(queryWrapper);
        if (list==null||list.size()==0){
            return Result.error().message("您输入的数据有误,请重新输入");
        }else {
            return Result.ok().data("scores",list);
        }
    }
    @ApiOperation("添加学生成绩信息")
    @PostMapping("/insert")
    public Result saveStudentInfo(@ApiParam(value = "插入数据") Scores score ){
        if (score!=null){
            boolean save = this.scoresService.save(score);
            if (save){
                return Result.ok();
            }else {
                return Result.error();
            }
        }else {
            return Result.error().message("插入失败");
        }
    }

    @ApiOperation("修改学生成绩信息")
    @PostMapping("/update")
    public Result editStudentInfo(@ApiParam(value = "插入数据") Scores sc ){
        if (sc!=null){
            UpdateWrapper<Scores> updateWrapper=new UpdateWrapper<>();
            updateWrapper.eq("id",sc.getId());
            boolean flag = this.scoresService.update(sc,updateWrapper);
            if (flag){
                return Result.ok();
            }else {
                return Result.error();
            }
        }else {
            return Result.error().message("修改失败");
        }
    }

    @ApiOperation("删除学生成绩信息")
    @DeleteMapping("/delete/{number}")
    public Result delScores(@PathVariable Long number ) {
        if (this.scoresService.removeById(number)){
            return Result.ok();
        }else {
            return Result.error();
        }
    }
    @GetMapping("/dic/get/{mode}")
    public Result selectDictionaryList(
            @ApiParam(name = "mode", value = "查询关键字", required = true)
            @PathVariable Integer mode
    ){
        //传入0或者null就是查询字典里所有内容,101 年级 102班级 103 学科
        QueryWrapper<Dictionary> queryWrapper=new QueryWrapper<>();
        if (mode==null||mode==0){
            queryWrapper.eq("fid",101);
            List<Dictionary> list0 = this.dictionaryService.list(queryWrapper);
            queryWrapper.eq("fid",102);
            List<Dictionary> list1 = this.dictionaryService.list(queryWrapper);
            queryWrapper.eq("fid",103);
            List<Dictionary> list2 = this.dictionaryService.list(queryWrapper);
            Map<String, Object> maps=new HashMap<>();
            maps.put("grade",list0);
            maps.put("class",list1);
            maps.put("subject",list2);
            return Result.ok().data(maps);
        }else{
            queryWrapper.eq("fid",mode);
            List<Dictionary> list0 = this.dictionaryService.list(queryWrapper);
            if (mode==101){
                return Result.ok().data("grade",list0);
            }else if (mode==102){
                return Result.ok().data("class",list0);
            }else if (mode==102){
                return Result.ok().data("subject",list0);
            }
        }
        return Result.error().message("输入的查询条件有误!");
    }
}
@Slf4j
@Api(description = "学生数据操作接口")
@CrossOrigin
@RestController
@RequestMapping("/index/stu")
public class StudentsController {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StudentsService studentsService;

    @ApiOperation("查询学生信息列表")
    @GetMapping("/list")
    public Result selectStudentList(){
        List<Students> list = studentsService.list();
        return Result.ok().data("list",list);
    }
    @ApiOperation("根据条件查询学生信息")
    @GetMapping("/get/{key}")
    public Result selectByIdOrName(@ApiParam(name = "key", value = "查询关键字", required = true) @PathVariable String key){
        Students stu = studentsService.getByNumberOrName(key);
        if (stu==null){
            return Result.error().message("您输入的数据有误,请重新输入");
        }else {
            return Result.ok().data("stu",stu);
        }
    }
    @ApiOperation("添加学生信息")
    @PostMapping("/insert")
    public Result saveStudentInfo(@ApiParam(value = "插入数据") Students students ){
        if (students!=null){
            boolean save = this.studentsService.save(students);
            if (save){
                return Result.ok();
            }else {
                return Result.error();
            }
        }else {
            return Result.error().message("插入失败");
        }
    }

    @ApiOperation("修改学生信息")
    @PostMapping("/update")
    public Result editStudentInfo(@ApiParam(value = "插入数据") Students students ){
        if (students!=null){
            UpdateWrapper<Students> updateWrapper=new UpdateWrapper<>();
            updateWrapper.eq("id",students.getId());
            boolean flag = this.studentsService.update(students,updateWrapper);
            if (flag){
                return Result.ok();
            }else {
                return Result.error();
            }
        }else {
            return Result.error().message("修改失败");
        }
    }

    @ApiOperation("删除学生信息")
    @DeleteMapping("/delete/{stuId}")
    public Result delStudents(@PathVariable Long stuId ) {
        if (this.studentsService.removeById(stuId)){
            return Result.ok();
        }else {
            return Result.error();
        }
    }
}

对SeparationVueApplication类中代码做一些补充:

@Slf4j
@MapperScan(basePackages = {"com.tiezhu.separation.mapper"})
@SpringBootApplication
public class SeparationVueApplication {
    public static void main(String[] args) throws UnknownHostException {

        ConfigurableApplicationContext application = SpringApplication.run(SeparationVueApplication.class,args);

        Environment env = application.getEnvironment();
        String ip = InetAddress.getLocalHost().getHostAddress();
        String port = env.getProperty("server.port");
        String contextPath = env.getProperty("server.servlet.context-path");
        if (contextPath == null) {
            contextPath = "";
        }
        log.info("\n----------------------------------------------------------\n\t" +
                "Application is running! Access URLs:\n\t" +
                "Local: \t\thttp://127.0.0.1:" + port + "/swagger-ui.html" + "\n\t" +
                "External: \thttp://" + ip + ':' + port + contextPath + '\n' +
                "----------------------------------------------------------");
    }
}

启动项目:

在这里插入图片描述

点击debug启动,下图为启动成功的控制台:

在这里插入图片描述

3.5 接口测试,使用SwaggerUI

打开浏览器访问:Swagger UI http://127.0.0.1:9090/swagger-ui.html 即可看到可以操作的接口的网页:

在这里插入图片描述

代码已经编写的有十一个接口,本节只会拿部分接口测试一下效果,其他接口都一样的测试方法。

查询全部学生信息:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.搭建前端基础框架代码

4.1 电脑需要具备的前端环境

前端开发软件方面常用的都是VS code,大家可以去官网下载免费的。

前端基础和前端进阶方面推荐看黑马程序员或者尚硅谷的,直接在哔哩哔哩可以搜到很多。

https://www.bilibili.com/video/BV1YW411T7GX

在这里插入图片描述

电脑需要安装好node环境,没有按照过的可以先进按照菜鸟教程上的步骤操作,非常简单。

Node.js 安装配置 | 菜鸟教程 (runoob.com)

安装完成之后在CMD命令框输入node -v 即可查看版本

在这里插入图片描述

node环境安装完成之后安装pnpm也是在cmd中执行一条命令即可:

npm i pnpm -g

在这里插入图片描述

安装完成后可以看到一些信息,也可以查看pnpm的镜像源。

4.2 导入前端项目和安装依赖

separationVue-boot的前端直接采用现成的前端脚手架不从零搭建,感兴趣的小伙伴可以学习一下Vue和ElementUI,电脑如果没有Git环境可以通过项目的Git链接下载代码。

img

vue-admin-template这是一个极简的 vue admin 管理后台。它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。

https://gitee.com/panjiachen/vue-admin-template.git

也可以选择直接解压本项目代码下载后note目录里面的separationVue-web.zip文件,该文件为已开发的前端代码。

下载后的代码通过vs code打开代码文件夹。

# 克隆项目
git clone https://github.com/PanJiaChen/vue-admin-template.git

# 进入项目目录
cd vue-admin-template

# 安装依赖
npm install

# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org

# 启动服务
npm run dev

运行成功后效果如下:

在这里插入图片描述

4.3 整合Vue编写简易页面

在前端基础框架的代码运行成功后,就可以开始写前端的页面代码了,先找到前端代码目录下 src/router/index.js,修改其中一部分内容。

{

​ path: ‘/login’,

​ component: () => import(‘@/views/login/index’),

​ hidden: true

},

{

​ path: ‘/404’,

​ component: () => import(‘@/views/404’),

​ hidden: true

},

{

​ path: ‘/’,

​ component: Layout,

​ redirect: ‘/dashboard’,

​ children: [{

​ path: ‘dashboard’,

​ name: ‘Dashboard’,

​ component: () => import(‘@/views/dashboard/index’),

​ meta: { title: ‘Dashboard’, icon: ‘dashboard’ }

​ }]

},

{

​ path: ‘/student’,

​ component: Layout,

​ redirect: ‘/student/list’,

​ name: ‘Student’,

​ meta: { title: ‘学生信息管理’, icon: ‘el-icon-s-help’ },

​ children: [

​ {

​ path: ‘/student/list’,

​ name: ‘StuList’,

​ component: () => import(‘@/views/stu/list’),

​ meta: { title: ‘学生信息列表’, icon: ‘table’ }

​ },

​ {

​ path: ‘/student/add’,

​ name: ‘StuAdd’,

​ component: () => import(‘@/views/stu/form’),

​ meta: { title: ‘添加学生’, icon: ‘form’ }

​ },

​ {

​ path: ‘/student/edit/:id’,

​ name: ‘StuEdit’,

​ component: () => import(‘@/views/stu/form’),

​ meta: { title: ‘编辑学生’ },

​ hidden: true

​ },

​ {

​ path: ‘/score/list’,

​ name: ‘ScoreList’,

​ component: () => import(‘@/views/scores/list’),

​ meta: { title: ‘学生成绩信息列表’, icon: ‘nested’ }

​ },

​ {

​ path: ‘/score/look/:id’,

​ name: ‘ScoreList’,

​ component: () => import(‘@/views/scores/list’),

​ meta: { title: ‘查看学生成绩’, icon: ‘nested’ },

​ hidden: true

​ },

​ {

​ path: ‘/score/add/:id’,

​ name: ‘ScoreAdd’,

​ component: () => import(‘@/views/scores/form’),

​ meta: { title: ‘添加学生成绩’, icon: ‘nested’ },

​ hidden: true

​ }

​ ]

},

此部分修改是为前端的路由添加内容,修改后的效果如下:

在这里插入图片描述

在src/view/ 路径下添加两个文件夹,stu和scores文件夹。

stu下为学生的相关页面,在该文件夹下添加 form.vue 和list.vue 两个vue页面文件。

## list.vue页面代码

<template>
  <div class="app-container">
    <el-table
      v-loading="listLoading"
      :data="list"
      stripe
      element-loading-text="Loading"
      border
      highlight-current-row
    >
      <el-table-column align="center" label="ID" width="95">
        <template slot-scope="scope">
          {{ scope.$index+2-1 }}
        </template>
      </el-table-column>
      <el-table-column label="姓名" width="110">
        <template slot-scope="scope">
          {{ scope.row.stuName }}
        </template>
      </el-table-column>
      <el-table-column label="学号" width="180" align="center">
        <template slot-scope="scope">
          <span>{{ scope.row.stuNumber }}</span>
        </template>
      </el-table-column>
      <el-table-column label="性别" width="95" align="center">
        <template slot-scope="scope">
          {{ scope.row.sex }}
        </template>
      </el-table-column>
      <el-table-column align="center" label="出生日期" width="250">
        <template slot-scope="scope">
          <i class="el-icon-time" />
          <span>{{ scope.row.birth }}</span>
        </template>
      </el-table-column>
      <el-table-column label="地址" width="200" align="center">
        <template slot-scope="scope">
          {{ scope.row.address }}
        </template>
      </el-table-column>
      <el-table-column label="班级" width="150" align="center">
        <template slot-scope="scope">
          {{ scope.row.stuGrade }}{{ scope.row.stuClass }}
        </template>
      </el-table-column>
      <el-table-column label="操作" width="350" align="center">
        <template slot-scope="scope">
          <router-link :to="'/score/look/'+scope.row.stuNumber">
            <el-button type="primary" size="small">查看成绩</el-button>
          </router-link>
          <router-link :to="'/score/add/'+scope.row.stuNumber">
            <el-button type="success" size="small">录入成绩</el-button>
          </router-link>
          <router-link :to="'/student/edit/'+scope.row.id">
            <el-button type="primary" size="small">修改</el-button>
          </router-link>
          <el-button type="warning" size="small" @click="deleteStu(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import stuApi from '@/api/students'

export default {
  filters: {
    statusFilter(status) {
      const statusMap = {
        published: 'success',
        draft: 'gray',
        deleted: 'danger'
      }
      return statusMap[status]
    }
  },
  data() {
    return {
      list: null,
      listLoading: true
    }
  },
  created() {
    this.fetchData()
  },
  methods: {
    fetchData() {
      this.listLoading = true
      stuApi.getList().then(response => {
        console.log(response.data.list)
        this.list = response.data.list
        this.listLoading = false
      })
    },
    deleteStu(stuId) {
      this.$confirm('此操作将永久删除该学生信息, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        stuApi.deleteStuent(stuId).then(response => {
          this.fetchData()
          this.$message.success(response.message)
        })
      }).then(response => {
        this.fetchData()
        this.$message.success(response.message)
      })
    }
  }
}
</script>

## form.vue页面代码
<template>
  <div class="app-container">
    <el-form ref="form" :model="form" label-width="80px">
      <el-form-item label="学生姓名">
        <el-input v-model="form.stuName" />
      </el-form-item>
      <el-form-item label="学号">
        <el-input v-model="form.stuNumber" />
      </el-form-item>

      <el-form-item label="性别">
        <el-select v-model="form.sex" placeholder="请选择性别">
          <el-option key="男" label="男" value="男" />
          <el-option key="女" label="女" value="女" />
        </el-select>
      </el-form-item>

      <el-form-item label="出生时间">
        <el-date-picker v-model="form.birth" value-format="yyyy-MM-dd" />
      </el-form-item>
      <el-form-item label="所属年级">
        <el-select v-model="form.stuGrade">
          <el-option
            v-for="item in optionsGrade"
            :key="item.codeName"
            :label="item.codeName"
            :value="item.codeName"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="所属班级">
        <el-select v-model="form.stuClass">
          <el-option
            v-for="item in optionsClass"
            :key="item.codeName"
            :label="item.codeName"
            :value="item.codeName"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="地址">
        <el-input v-model="form.address" type="textarea" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="saveOrUpdate()">立即创建</el-button>
        <el-button>取消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
import stuApi from '@/api/students'
import ScoreApi from '@/api/scores'

export default {
  data() {
    return {
      form: { },
      optionsClass: [],
      optionsGrade: [],
      saveBtnDisabled: false // 保存按钮是否禁用,防止表单重复提交
    }
  },
  // 页面渲染成功
  created() {
    if (this.$route.params.id) {
      this.fetchDataById(this.$route.params.id)
    }
    ScoreApi.getByDictionary(0).then(response => {
      this.optionsGrade = response.data.grade
      this.optionsClass = response.data.class
    })
  },
  methods: {
    fetchDataById(id) {
      stuApi.getById(id).then(response => {
        this.form = response.data.stu
      })
    },
    saveOrUpdate() {
    // 禁用保存按钮
      this.saveBtnDisabled = true
      if (!this.form.id) {
        this.saveData(this.form)
      } else {
        this.updateData(this.form)
      }
    },
    saveData() {
      stuApi.save(this.form).then(response => {
        this.$message({
          type: 'success',
          message: response.message
        })
        this.$router.push({ path: '/student/list' })
      })
    },
    // 根据id更新记录
    updateData() {
      stuApi.updateById(this.form).then(response => {
        this.$message({
          type: 'success',
          message: response.message
        })
        this.$router.push({ path: '/student/list' })
      })
    }
  }
}
</script>

在scores文件夹下,同样新建两个文件 list.vue和form.vue

##list.vue 页面代码:
<template>
  <div class="app-container">
    <el-table
      v-loading="listLoading"
      :data="list"
      stripe
      element-loading-text="Loading"
      border
      highlight-current-row
    >
      <el-table-column align="center" label="ID" width="95">
        <template slot-scope="scope">
          {{ scope.$index+2-1 }}
        </template>
      </el-table-column>
      <el-table-column label="考试科目" width="110" align="center">
        <template slot-scope="scope">
          {{ scope.row.subjects }}
        </template>
      </el-table-column>
      <el-table-column label="学号" width="180" align="center">
        <template slot-scope="scope">
          <span>{{ scope.row.stuNumber }}</span>
        </template>
      </el-table-column>
      <el-table-column label="分数" width="95" align="center">
        <template slot-scope="scope">
          {{ scope.row.score }}
        </template>
      </el-table-column>
      <el-table-column align="center" label="考试说明" width="250">
        <template slot-scope="scope">
          <span>{{ scope.row.description }}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="100" align="center">
        <template slot-scope="scope">
          <el-button type="warning" size="small" @click="deleteScore(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
import ScoreApi from '@/api/scores'
export default {
  data() {
    return {
      list: null,
      listLoading: true
    }
  }, created() {
    this.fetchData()
  },
  methods: {
    fetchData() {
      this.listLoading = true
      if (this.$route.params.id) {
        ScoreApi.getListByStuNumber(this.$route.params.id).then(response => {
          this.list = response.data.list
          this.listLoading = false
        })
      } else {
        ScoreApi.getList().then(response => {
          console.log(response.data.list)
          this.list = response.data.list
          this.listLoading = false
        })
      }
    },
    deleteScore(param) {
      this.$confirm('此操作将永久删除该条考试信息, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        ScoreApi.deleteScore(param).then(response => {
          this.fetchData()
          this.$message.success(response.message)
        })
      }).then(response => {
        this.fetchData()
        this.$message.success(response.message)
      })
    }
  }
}
</script>

## form.vue 页面代码
<template>
  <div class="app-container">
    <el-form ref="form" :model="form" label-width="80px">
      <el-form-item label="考试科目">
        <el-select v-model="form.subjects">
          <el-option
            v-for="item in optionsSubject"
            :key="item.codeName"
            :label="item.codeName"
            :value="item.codeName"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="学号">
        <el-input v-model="form.stuNumber" readonly="true" />
      </el-form-item>

      <el-form-item label="分数">
        <el-input
          v-model="form.score"
          type="text"
          placeholder="请输入分数"
          maxlength="10"
          max="150"
          show-word-limit
        /></el-form-item>
      <el-form-item label="考试说明">
        <el-input v-model="form.description" type="textarea" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="saveOrUpdate()">立即创建</el-button>
        <el-button>取消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
import ScoreApi from '@/api/scores'

export default {
  data() {
    return {
      form: { },
      optionsSubject: [],
      saveBtnDisabled: false // 保存按钮是否禁用,防止表单重复提交
    }
  },
  // 页面渲染成功
  created() {
    if (this.$route.params.id) {
      this.fetchDataById(this.$route.params.id)
    }
    ScoreApi.getByDictionary(103).then(response => {
      this.optionsSubject = response.data.subject
    })
  },
  methods: {
    fetchDataById(id) {
      this.form.stuNumber = this.$route.params.id
    },
    saveOrUpdate() {
      // 禁用保存按钮
      this.saveBtnDisabled = true
      this.saveData()
    },
    saveData() {
      ScoreApi.save(this.form).then(response => {
        this.$message({
          type: 'success',
          message: response.message
        })
        this.$router.push({ path: '/student/list' })
      })
    }
  }
}
</script>

一共四个vue文件需要新添加

4.4 使用Axios进行接口请求

vue前端项目的请求一般都写在src/api文件夹下面,可以根据自己的后端接口来自定义请求的js方法。

在src/api文件夹下面新增两个js文件分别为,scores.js 和students.js 文件,两个文件的内容如下:

//scores.js文件内容

import request from '@/utils/request'

const api_name = 'http://127.0.0.1:9090/index/score/'
//请求的接口的基础路径

export default {
  getList(params) {
    return request({
      url: `${api_name}/list`,
      method: 'get',
      params
    })
  },
  getByDictionary(key) {
    return request({
      url: `${api_name}/dic/get/${key}`,
      method: 'get'
    })
  },
  getListByStuNumber(number) {
    return request({
      url: `${api_name}/get/${number}`,
      method: 'get'
    })
  },
  deleteScore(id) {
    return request({
      url: `${api_name}/delete/${id}`,
      method: 'delete'
    })
  },
  save(info) {
    return request({
      url: `${api_name}/insert`,
      method: `post`,
      data: info
    })
  }
}

//students.js文件内容
import request from '@/utils/request'

const api_name = 'http://127.0.0.1:9090/index/stu/'

export default {
  getList(params) {
    return request({
      url: `${api_name}/list`,
      method: 'get',
      params
    })
  },

  deleteStuent(stuId) {
    return request({
      url: `${api_name}/delete/${stuId}`,
      method: 'delete'
    })
  },

  getById(id) {
    return request({
      url: `${api_name}/get/${id}`,
      method: 'get'
    })
  },

  save(student) {
    return request({
      url: `${api_name}/insert`,
      method: `post`,
      data: student
    })
  },
  updateById(student) {
    return request({
      url: `${api_name}/update`,
      method: `post`,
      data: student
    })
  }
}

以上代码全部完成后,就可以在vs code 的终端中输入 npm run dev 然后回车启动项目(启动之前请先关闭运行,Ctrl+C 关闭)。启动成功及各个页面的效果如下。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

到这里我们就实现了一个简单的前后端分离的Demo了,所有的代码均上传Gitee仓库,

https://gitee.com/lu-tiezhu/separationVue-boot.git小伙伴们可以通过直接下载代码或者Git到IDEA里,如果觉得这篇文章尚可,可以收藏点赞转发哦。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一个完整的外卖系统,包括手机,后台管理,api 基于spring boot和vue的前后分离的外卖系统 包含完整的手机,后台管理功能 技术选型 核心框架:Spring Boot 数据库层:Spring data jpa/Spring data mongodb 数据库连接池:Druid 缓存:Ehcache 前:Vue.js 数据库:mysql5.5以上,Mongodb4.0(不要使用最新版4.2) 模块 flash-waimai-mobile 手机站点 flash-waimai-manage后台管理系统 flash-waimai-api java接口服务 flash-waimai-core 底层核心模块 flash-waimai-generate 代码生成模块 快速开始 数据存储采用了mysql和mongodb,其中基础管理配置功能数据使用mysql,业务数据使用mongodb存储。 创建mysql数据库 CREATE DATABASE IF NOT EXISTS waimai DEFAULT CHARSET utf8 COLLATE utf8_general_ci; CREATE USER 'waimai'@'%' IDENTIFIED BY 'waimai123'; GRANT ALL privileges ON waimai.* TO 'waimai'@'%'; flush privileges; mysql数据库创建好了之后,启动flash-waimai-api服务,会自动初始化数据,无需开发人员自己手动初始化数据 安装mongodb并创建数据库:flash-waimai 使用mongorestore命令 导入mongodb数据,由于测试数据量较大,打包放在了百度云盘:链接:https://pan.baidu.com/s/1mfO7yckFL7lMb_O0BPsviw 提取码:apgd 下载后将文件解压到d:\elm,如下命令导入数据: mongorestore.exe -d flash-waimai d:\\elm 下载项目测试数据的图片(商家和食品图片):链接:https://pan.baidu.com/s/1rvZDspoapWa6rEq2D_5kzw 提取码:urzw ,将图片存放到t_sys_cfg表中system.file.upload.path配置的目录下 启动管理平台:进入flash-waimai-manage目录:运行 npm install --registry=https://registry.npm.taobao.org运行npm run dev启动成功后访问 http://localhost:9528 ,登录,用户名密码:admin/admin 启动手机:进入flash-waimai-mobile目录:运行 npm install --registry=https://registry.npm.taobao.org运行npm run local启动成功后访问 http://localhost:8000

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值