瑞吉外卖-项目优化

2 篇文章 0 订阅
2 篇文章 0 订阅

第一部分

问题说明

在这里插入图片描述

环境搭建

用Git管理项目

1.切换git仓库
因为我之前对这个项目进行版本管理了,连接的仓库里边还有别的代码,这样就比较混乱,所以我就重新创建了一个gitee仓库,然后再idea里边切换 git 远程仓库 具体操作请点击这个链接idea切换git远程仓库
2.提交到远程仓库master分支
commit push
在这里插入图片描述

3.创建新的分支,然后再提交到新的分支(目的就是在这个分支上做修改,如果修改有错误的话可以再重新拉取master分支上的代码,如果修改的满意,那么可以合并分支)
4.然后导入一个redis的依赖,出现Unresolved dependency:org.spring-boot-starter-data-redis jar:2.5.2 这个bug, 解决办法
redis依赖:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

5.导入redis配置类在config目录下命名为RedisConfig

package com.itheima.reggie.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

        //默认的Key序列化器为:JdkSerializationRedisSerializer
        redisTemplate.setKeySerializer(new StringRedisSerializer()); // key序列化
        //redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // value序列化

        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

6.commit and push到v1.0分支去,以后开发都在这个分支,开发完成后再与master分支合并

缓存短信验证码

实现思路

在这里插入图片描述

代码改造

1.注入对象
在这里插入图片描述

2.发送验证码,并设置有效期5分钟
在这里插入图片描述
3.删除验证码
在这里插入图片描述

功能测试

1.输入手机号并获取验证码
在这里插入图片描述
2.进入客户端查看
在这里插入图片描述
3.登录成功后删除验证码,redis客户端里边的电话号码也不见了

在这里插入图片描述

在这里插入图片描述
刷新后客户端看不见电话号码
在这里插入图片描述

缓存菜品

实现思路

前面我们已经实现了移动端菜品查看功能,对应的服务端方法为DishController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统的性能。
具体实现实路
在这里插入图片描述
在这里插入图片描述

代码改造

1.注入redis对象RedisTemplate
在这里插入图片描述

2.在list方法里改造代码
在这里插入图片描述
在这里插入图片描述
对应代码:

@GetMapping("/list")
    public R<List<DishDto>> list(Dish dish){
        //1.创建一个列表对象
        List<DishDto> dishDtoList=null;
        //2.这是菜品种类id 字符串的形式为 dish_1391233423412_1  动态构造key
        String key="dish_"+dish.getCategoryId()+"_"+dish.getStatus();
//        3、先从Redis中获取缓存数据
        dishDtoList= (List<DishDto>) redisTemplate.opsForValue().get(key);
//        4.如果存在,直接返回,无需查询数据库
        if (dishDtoList!=null){
            return R.success(dishDtoList);
        }
//       5.1 如果不存在,需要查询数据库,将查询到的菜品数据缓存到Redis
        //构造查询条件
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        //添加条件,查询状态为1(1为起售,0为停售)的菜品
        queryWrapper.eq(Dish::getStatus,1);

        List<Dish> list = dishService.list(queryWrapper);

         dishDtoList = list.stream().map((item) -> {
            DishDto dishDto = new DishDto();
            //对象拷贝(每一个list数据)
            BeanUtils.copyProperties(item,dishDto);
            Long categoryId = item.getCategoryId();  //分类id
            //通过categoryId查询到category内容
            Category category = categoryService.getById(categoryId);
            //判空
            if(category != null){
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }
            //获取当前菜品id
            Long dishId = item.getId();
            //构造条件构造器
            LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper= new LambdaQueryWrapper<>();
            //添加查询条件
            dishFlavorLambdaQueryWrapper.eq(dishId != null,DishFlavor::getDishId,dishId);
            //select * from dish_flavors where dish_id = ?
            List<DishFlavor> dishFlavors = dishFlavorService.list(dishFlavorLambdaQueryWrapper);

            dishDto.setFlavors(dishFlavors);

            return dishDto;
        }).collect(Collectors.toList());
//        5.2 如果不存在,将查询到的菜品数据缓存到Redis
        redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.MINUTES);

        return R.success(dishDtoList);
    }

功能测试

可以用debug模式进行测试
也可以直接在前端页面输入电话号码,验证码,然后再redis客户端查看
在这里插入图片描述
当点击一种菜品类别的时候,可以看到Redis客户端会有对应的菜品种类id返回,然后多测试几组。

缓存菜品数据(对新增修改操作)

对修改功能进行改造:

 @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());
//        清除所有菜品的缓存数据
//        Set keys = redisTemplate.keys("dish_*");
//        redisTemplate.delete(keys);

//        清理一部分缓存数据,某个分类下面的菜品缓存数据
        String key="dish_"+dishDto.getCategoryId()+"_1";
        redisTemplate.delete(key);
//        查询
        dishService.updateWithFlavor(dishDto);
        return R.success("新增菜品成功!");
    }

在这里插入图片描述

对新增功能进行修改:

 @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        log.info("接收的dishDto数据:{}",dishDto.toString());
        //保存数据到数据库
        dishService.saveWithFlavor(dishDto);
        //        清除所有菜品的缓存数据
//        Set keys = redisTemplate.keys("dish_*");
//        redisTemplate.delete(keys);

//        清理一部分缓存数据,某个分类下面的菜品缓存数据
        String key="dish_"+dishDto.getCategoryId()+"_1";
        redisTemplate.delete(key);
        return R.success("新增菜品成功");
    }

在这里插入图片描述

Spring Cache

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

在save方法上加了一个CachePut注解

在这里插入图片描述
在这里插入图片描述
测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在delete方法上加CacheEvict注解

在这里插入图片描述

/**
     * CacheEvict:清理指定缓存
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     */
    @CacheEvict(value = "userCache",key = "#p0")
    //@CacheEvict(value = "userCache",key = "#root.args[0]")
    //@CacheEvict(value = "userCache",key = "#id")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }

在update方法上加CacheEvict注解

    //@CacheEvict(value = "userCache",key = "#p0.id")
    //@CacheEvict(value = "userCache",key = "#user.id")
    //@CacheEvict(value = "userCache",key = "#root.args[0].id")
    @CacheEvict(value = "userCache",key = "#result.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

在getByid方法和list方法上添加Cacheable注解

 /**
     * Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     * condition:条件,满足条件时才缓存数据
     * unless:满足条件则不缓存
     */
    @Cacheable(value = "userCache",key = "#id",unless = "#result == null")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

    @Cacheable(value = "userCache",key = "#user.id + '_' + #user.name")
    @GetMapping("/list")
    public List<User> list(User user){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getId() != null,User::getId,user.getId());
        queryWrapper.eq(user.getName() != null,User::getName,user.getName());
        List<User> list = userService.list(queryWrapper);
        return list;
    }

在这里插入图片描述
测试:
启动debug模式,在postman软件上第一次查询id的时候它会跳转到idea,第二次在postman上查询同样的id,直接就返回数据了

SpringCache使用方式

在这里插入图片描述

1.pom坐标代码:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.配置application.yml文件(要注意代码加的位置)
在这里插入图片描述
前边都已经测试过了,直接在pom文件中加入依赖后,通过在redis客户端观察就行啦

 redis:
    host: 127.0.0.1
    port: 6379
    password:
    database: 0
  cache:
    redis:
      time-to-live: 1800000 #设置缓存过期时间,可选

缓存套餐数据

实现思路

在这里插入图片描述

实现过程

1.导入坐标

<groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.配置yml

redis:
    host: 127.0.0.1
    port: 6379
    password:
    database: 0
  cache:
    redis:
      time-to-live: 1800000 #设置缓存过期时间,可选
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: ASSIGN_ID

3.在SetmealController方法中对list方法进行修改
在这里插入图片描述
代码段:

/**
     * 根据条件查询套餐数据
     * @param setmeal
     * @return
     */
    @GetMapping("/list")
    @Cacheable(value = "setmealChache",key="#setmeal.categoryId + '_' + #setmeal.status")
    public R<List<Setmeal>> list(Setmeal setmeal){
        LambdaQueryWrapper<Setmeal> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(setmeal.getCategoryId()!=null,Setmeal::getCategoryId,setmeal.getCategoryId());
        queryWrapper.eq(setmeal.getStatus()!=null,Setmeal::getStatus,setmeal.getStatus());
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);

        List<Setmeal> list =setmealService.list(queryWrapper);

        return R.success(list);
    }

测试发现有错误
在这里插入图片描述
在通用返回类加入如下代码
在这里插入图片描述
修改后测试成功

然后再删除和新增方法上加入EvitCache注解
删除方法:
在这里插入图片描述

 /**
     * 删除套餐
     * @param ids
     * @return
     */
    @DeleteMapping
  //  @CacheEvict(value = "setmealCache",allEntries = true)
    @CacheEvict(value = "setmealChache",allEntries = true)
    public R<String> delete(@RequestParam List<Long> ids){
        log.info("ids为:",ids);
        setmealService.removeWithDish(ids);
        return R.success("套餐数据删除成功");

    }

新增方法:
在这里插入图片描述

 @PostMapping
  //  @CacheEvict(value = "setmealCache",allEntries = true)
    @CacheEvict(value = "setmealChache",allEntries = true)
    public R<String> save(@RequestBody SetmealDto setmealDto){
        log.info("数据传输对象setmealDto:{}",setmealDto.toString());

        setmealService.saveWithDish((setmealDto));
        return R.success("新增套餐成功!");
    }

测试:
在这里插入图片描述

新增一个套餐
在这里插入图片描述
刷新后缓存被清理掉
在这里插入图片描述

第二部分

2.1内容介绍

在这里插入图片描述

2.2Mysql主从复制

从库可以有多个
在这里插入图片描述

2.2.1准备条件

Mysql主从复制
在这里插入图片描述

2.2.2配置-主库Master

启用日志就能看到增删改的操作信息,server-id是服务器唯一的标识
第一步
在这里插入图片描述
第二步
在这里插入图片描述
第三步
在这里插入图片描述
第四步
在这里插入图片描述以上步骤执行完毕,就相当于主库Master这一端配好了,就不要再在主库上进行操作了
在这里插入图片描述

2.2.3配置-从库Slave

第一步

在这里插入图片描述

第二步

重启mysql
命令systemctl restart mysqld

第三步

在这里插入图片描述

第四步

在这里插入图片描述
遇到的问题:配置好上边的步骤但是还不能实现主从同步
可能原因:mysql的uuid相同
在这里插入图片描述
上图是配置好的,两个应该都显示yes,而我的两个都显示no
可以 参考链接

一些linux命令:

find /-iname "filename" //表示查找文件
systemctl restart mysqld  //表示重启mysql
mysql -uroot -proot       //表示登录mysql
show slave status \G;     //表示查看slave的状态
测试

根据以上步骤配置过后,在navicat中,主库添加表信息等,在从库中会进行同步

在这里插入图片描述


2.3读写分离

2.3.1背景

在这里插入图片描述

2.3.2Sharding-JDBC介绍

在这里插入图片描述

2.3.3入门案例

在这里插入图片描述
1.maven坐标

<dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>

2.在配置文件中配置读写分离规则(导入后有些配置信息会报红,但是不影响运行。)

server:
  port: 8080
spring:
  shardingsphere:
    datasource:
      names: master,slave
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.201.130:3306/rw?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
        username: root
        password: root
      # 从数据源
      slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.201.131:3306/rw?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
        username: root
        password: root
    masterslave:
      # 读写分离配置 //轮寻
      load-balance-algorithm-type: round_robin
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: master
      # 从库数据源名称列表,多个逗号分隔
      slave-data-source-names: slave
    props:
      sql:
        show: true #开启SQL显示,默认false
  main:
    allow-bean-definition-overriding: true
#mybatis-plus:
#  configuration:
#    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
#    map-underscore-to-camel-case: true
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#  global-config:
#    db-config:
#      id-type: ASSIGN_ID

遇到的bug:java.sql.SQLException: null, message from server: “Host ‘xx‘ is blocked because of many co
解决方案:

2.3.4功能测试

新增操作
在这里插入图片描述
新增、修改、删除应该走的都是master库
在这里插入图片描述
对应的查询操作,应该走slave库
在这里插入图片描述

2.4 项目实现读写分离

数据库环境准备(主从复制)

之前在虚拟机已经搭建好两台服务器,来实现主从复制
在这里插入图片描述

代码改造

在这里插入图片描述
就是以上这三步

功能测试

配置完成后启动项目
打开网页端
在这里插入图片描述
测试成功
在这里插入图片描述

2.5Nginx

2.5.1Nginx概述

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

下面这种树形结构
在这里插入图片描述
通过命令 yum install tree 来安装

安装完毕后输入 tree 即可展示

2.5.2Nginx命令

查看版本

./nginx -v

在这里插入图片描述
检查配置文件正确性

./nginx -t

在这里插入图片描述
启动和停止
在这里插入图片描述

./nginx                    //启动nginx服务
systemctl stop firewalld   //关闭防火墙
ps -ef | grep 进程名       //表示查询正在运行的进程
./nginx -s stop           //停止nginx进程

重新加载配置文件

./nginx -s reload  //重新加载配置文件,不用再先关闭,在启动了

在这里插入图片描述

为了让我们启动更加方便nginx,可以配置环境变量

[root@localhost /]# vim /etc/profile

在这里插入图片描述
配置好后,让文件立即生效
source /etc/profile

在这里插入图片描述
配置好后就可以直接使用 nginx -s reload 命令来启动

nginx -s reload

2.5.3Nginx配置文件结构

在这里插入图片描述
全局块
events块
http块:里边有Server全局块,和location块

2.5.4Nginx具体应用

部署静态资源

在这里插入图片描述

cp 文件一名  目录一   //把文件一拷贝到目录一下边
set nu  //在vim模式下显示行号
反向代理

在这里插入图片描述

在这里插入图片描述
配置方向代理服务器
用户不需要知道目标服务器的地址,反向代理配置
在这里插入图片描述

负载均衡

在这里插入图片描述
具体应用
在这里插入图片描述

1.在conf文件里边配置如下内容:

upstream targetserver{
server 192.168.201.131:8080;
server 192.168.201.131:8081;
}
server {
        listen 8080;
        server_name localhost;
        location / {
                proxy_pass http://targetserver; //反向代理配置,将请求转发到指定服务
        }
}

在这里插入图片描述
2.保存退出,重新加载

:wq   //保存退出
nginx -s reload  // 重新加载

在这里插入图片描述

负载均衡策略:
在这里插入图片描述

第三部分

问题说明

在这里插入图片描述

前后端分离开发

介绍

在这里插入图片描述

开发流程

在这里插入图片描述

前端技术栈

在这里插入图片描述

YApi

介绍

在这里插入图片描述

使用方式

还没有配置
在这里插入图片描述

Swagger

介绍

在这里插入图片描述

使用方式

在这里插入图片描述

shutdown

1.导入maven坐标
在这里插入图片描述

<dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>3.0.2</version>
    </dependency>

2.导入knife4j相关配置(WebMvcConfig)
在这里插入图片描述
3.设置静态资源映射
在这里插入图片描述
4.在LoginCheckFilter中设置不需要处理的请求路径
在这里插入图片描述

配置类代码:

package com.itheima.reggie.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.itheima.reggie.common.JacksonObjectMapper;
import com.itheima.reggie.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {

    /**
     * 设置静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始进行静态资源映射...");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }

    /**
     * 扩展mvc框架的消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        converters.add(0,messageConverter);
    }

    @Bean
    public Docket createRestApi() {
        // 文档类型
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("瑞吉外卖")
                .version("1.0")
                .description("瑞吉外卖接口文档")
                .build();
    }
}

常用注解

加在属性上,可以更加清晰的查看
在这里插入图片描述

项目部署

部署架构

在这里插入图片描述

部署环境说明

在这里插入图片描述

部署前端项目

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

部署后端项目

在这里插入图片描述

一个脚本文件,开始上传到自己的虚拟机,是没有权限执行它的,需要执行以下命令:
在这里插入图片描述
在这里插入图片描述

启动项目

在这里插入图片描述
启动成功

在网页查看

输入自己的服务器地址:http://192.168.201.130/

查看

在这里插入图片描述

在这里插入图片描述

图片不能加载的问题

解决步骤:
1.在配置文件里改图片的路径
在这里插入图片描述
2.创建文件夹,并上传图片资源
在这里插入图片描述

在这里插入图片描述
3.把idea里边改过得代码commit and push到远程仓库,然后再执行虚拟机上的脚本文件
在这里插入图片描述
4. 网页端查看
加载成功
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值