【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)

目录

一、问题说明

二、前后端分离开发

1、介绍

2、开发流程

3、前端技术栈

三、Yapi

1、介绍

2、部署

3、使用

3.1 添加项目​编辑

 3.2 添加分类​编辑

3.3 添加接口

3.4 运行 

3.5 导出接口

3.6 导入数据

四、Swagger

1、介绍

2、使用方式

 2.1 pom

2.2 导入knife4j相关配置(WebConfig)

2.3 LoginCheckFilter设置不需要处理的请求路径

2.4 验证

2.5 调试接口

2.6 下载接口文档

3、常用注解

3.1 Setmeal实体添加注解

 3.2 R 添加注解

3.3 SetmealController添加注解 

五、项目部署

1、部署架构

2、部署环境说明

3、部署前端项目

3.1 第一步

 3.2 第二步

4、部署后端项目

4.1 第一步

 4.2 第二步  reggieStart.sh 

4.3 第三步 

六、图片报错问题

6.1 修改图片存储为linu目录,并提交代码,重新执行脚本文件

6.2  将本地img图片上传到Linux机器目录


前言:前后端分离开发

一、问题说明

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第1张图片

二、前后端分离开发

1、介绍

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第2张图片

2、开发流程

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第3张图片

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第4张图片

3、前端技术栈

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第5张图片

三、Yapi

1、介绍

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第6张图片

2、部署

博主很久之前写的文章

3、使用

 【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第7张图片

 【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第8张图片

 

 

3.1 添加项目【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第9张图片

 3.2 添加分类【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第10张图片

3.3 添加接口

 【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第11张图片

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第12张图片

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第13张图片

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第14张图片

3.4 运行 

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第15张图片

3.5 导出接口

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第16张图片

3.6 导入数据

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第17张图片

四、Swagger

1、介绍

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第18张图片

2、使用方式

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第19张图片

 2.1 pom


  4.0.0

  com.runa
  reggie_take_out_spuer
  1.0-SNAPSHOT
  jar

  
    org.springframework.boot
    spring-boot-starter-parent
    2.4.5
     
  

  reggie_take_out_spuer
  http://maven.apache.org

  
    UTF-8
    1.8
  

  
    
    
      com.aliyun
      aliyun-java-sdk-core
      4.5.16
    
    
      com.aliyun
      aliyun-java-sdk-dysmsapi
      2.1.0
    
    
      org.springframework.boot
      spring-boot-starter
    

    
      org.springframework.boot
      spring-boot-starter-test
      test
    

    
      org.springframework.boot
      spring-boot-starter-web
      compile
    

    
      com.baomidou
      mybatis-plus-boot-starter
      3.4.2
    

    
      org.projectlombok
      lombok
      1.18.20
    

    
      com.alibaba
      fastjson
      1.2.76
    

    
      commons-lang
      commons-lang
      2.6
    

    
      mysql
      mysql-connector-java
      runtime
    

    
      com.alibaba
      druid-spring-boot-starter
      1.1.23
    


    
      junit
      junit
      3.8.1
      test
    

    
      com.github.xiaoymin
      knife4j-spring-boot-starter
      3.0.2
    
  

  
    
      
        org.springframework.boot
        spring-boot-maven-plugin
        2.4.5
      
    
  

2.2 导入knife4j相关配置(WebConfig)

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第20张图片

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第21张图片

package com.runa.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.runa.reggie.common.JacksonObjectMapper;
import com.runa.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> 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.runa.reggie.controller"))
                .paths(PathSelectors.any())
                .build();
    }

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

}

2.3 LoginCheckFilter设置不需要处理的请求路径

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第22张图片

package com.runa.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.runa.reggie.common.BaseContext;
import com.runa.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    // 路径匹配器
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 1  获取本次请求的URI
        //        request.getRequestURL()返回的是全路径。
        //        request.getRequestURI()返回的是除去协议,域名(IP+端口号)之后的路由部分。
        String requestURI = request.getRequestURI();
        log.info("拦截到请求:【{}】",requestURI);

        // 定义一些不需要处理的路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                "/user/sendMsg",
                "/user/login",
                "/doc.html",
                "/webjars/**",
                "/swagger-resources",
                "/v2/api-docs"
        };

        // 2  判断本次请求是否需要处理
        boolean check = check(urls, requestURI);

        // 3 如果不需要处理,则直接放行
        if(check){
            log.info("本次请求【 {} 】 不需要处理",requestURI);
            filterChain.doFilter(request,response);
            return;
        }

        // 4 -1(员工端) 判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee") != null){
            log.info("用户已登录,请求url为:【{}】, 用户id为:【 {} 】 ",requestURI,request.getSession().getAttribute("employee"));
            // 获取ID 塞进线程
            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);

            filterChain.doFilter(request,response);
            return;
        }

        // 4 -2(移动端) 判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("user") != null){
            log.info("用户已登录,请求url为:【{}】, 用户id为:【 {} 】 ",requestURI,request.getSession().getAttribute("user"));
            // 获取ID 塞进线程
            Long userId = (Long) request.getSession().getAttribute("user");
            BaseContext.setCurrentId(userId);

            filterChain.doFilter(request,response);
            return;
        }

        log.info("用户未登录");
        // 5 如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;

    }

    /**
     * 路径匹配,检查本次请求是否需要放行
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String[] urls, String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match){
                return true;
            }
        }
        // 循环都匹配不上就返回false
        return false;
    }
}

  

2.4 验证

启动服务

http://localhost:8080/doc.html

 【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第23张图片

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第24张图片

2.5 调试接口

这里存在需要登录问题,这里先登录一下,再调试

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第25张图片

2.6 下载接口文档

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第26张图片

3、常用注解

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第27张图片

3.1 Setmeal实体添加注解

package com.runa.reggie.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 套餐
 */
@Data
@ApiModel("套餐")
public class Setmeal implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty("主键")
    private Long id;


    //分类id
    @ApiModelProperty("分类ID")
    private Long categoryId;


    //套餐名称
    @ApiModelProperty("套餐名称")
    private String name;


    //套餐价格
    @ApiModelProperty("套餐价格")
    private BigDecimal price;


    //状态 0:停用 1:启用
    @ApiModelProperty("状态")
    private Integer status;


    //编码
    @ApiModelProperty("编码")
    private String code;


    //描述信息
    @ApiModelProperty("描述信息")
    private String description;


    //图片
    @ApiModelProperty("图片")
    private String image;


    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;


    //是否删除
    private Integer isDeleted;
}

 3.2 R 添加注解

package com.runa.reggie.common;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;


/**
 * 通用返回结果,服务端响应的数据最终都会封装成此对象
 * @param 
 */
@Data
@ApiModel("返回结果")
public class R {
    @ApiModelProperty("编码")
    private Integer code; //编码:1成功,0和其它数字为失败

    @ApiModelProperty("错误信息")
    private String msg; //错误信息

    @ApiModelProperty("数据")
    private T data; //数据

    @ApiModelProperty("动态数据")
    private Map map = new HashMap(); //动态数据

    public static  R success(T object) {
        R r = new R();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static  R error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

3.3 SetmealController添加注解 

package com.runa.reggie.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.runa.reggie.common.R;
import com.runa.reggie.dto.SetmealDto;
import com.runa.reggie.entity.Category;
import com.runa.reggie.entity.Setmeal;
import com.runa.reggie.service.CategoryService;
import com.runa.reggie.service.SetmealDishService;
import com.runa.reggie.service.SetmealService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/setmeal")
@Slf4j
@Api(tags = "套餐相关接口")
public class SetmealController {

    @Autowired
    private SetmealService setmealService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SetmealDishService setmealDishService;

    /**
     * 新增套餐
     * @param setmealDto
     * @return
     */
    @PostMapping
    @ApiOperation(value = "新增套餐接口")
    public R save(@RequestBody SetmealDto setmealDto){
        log.info("套餐信息:{}",setmealDto);

        setmealService.saveWithDish(setmealDto);

        return R.success("新增套餐成功");
    }

    /**
     * 套餐分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    @ApiOperation(value = "套餐分页查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "页码", required = true),
            @ApiImplicitParam(name = "pageSize", value = "每页记录数", required = true),
            @ApiImplicitParam(name = "name", value = "套餐名称", required = false)
    })
    public R page(int page, int pageSize, String name){
        //分页构造器对象
        Page pageInfo = new Page<>(page,pageSize);
        Page dtoPage = new Page<>();

        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件,根据name进行like模糊查询
        queryWrapper.like(name != null,Setmeal::getName,name);
        //添加排序条件,根据更新时间降序排列
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);

        setmealService.page(pageInfo,queryWrapper);

        //对象拷贝
        BeanUtils.copyProperties(pageInfo,dtoPage,"records");
        List records = pageInfo.getRecords();

        List list = records.stream().map((item) -> {
            SetmealDto setmealDto = new SetmealDto();
            //对象拷贝
            BeanUtils.copyProperties(item,setmealDto);
            //分类id
            Long categoryId = item.getCategoryId();
            //根据分类id查询分类对象
            Category category = categoryService.getById(categoryId);
            if(category != null){
                //分类名称
                String categoryName = category.getName();
                setmealDto.setCategoryName(categoryName);
            }
            return setmealDto;
        }).collect(Collectors.toList());

        dtoPage.setRecords(list);
        return R.success(dtoPage);
    }

    /**
     * 删除套餐
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation(value = "删除套餐接口")
    public R delete(@RequestParam List ids){
        log.info("ids:{}",ids);

        setmealService.removeWithDish(ids);

        return R.success("套餐数据删除成功");
    }

    /**
     * 根据条件查询套餐数据
     * @param setmeal
     * @return
     */
    @GetMapping("/list")
    @ApiOperation(value = "根据条件查询套餐数据接口")
    public R> list(Setmeal setmeal){
        LambdaQueryWrapper 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 list = setmealService.list(queryWrapper);
        return R.success(list);

    }

}

 【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第28张图片

五、项目部署

1、部署架构

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第29张图片

2、部署环境说明

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第30张图片

3、部署前端项目

3.1 第一步

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第31张图片

 3.2 第二步

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第32张图片 【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第33张图片

4、部署后端项目

4.1 第一步

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第34张图片

 4.2 第二步  reggieStart.sh 

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第35张图片

#!/bin/sh
echo =================================
echo  自动化部署脚本启动
echo =================================

echo 停止原来运行中的工程
APP_NAME=reggie_take_out

tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
    echo 'Stop Process...'
    kill -15 $tpid
fi
sleep 2
tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
    echo 'Kill Process!'
    kill -9 $tpid
else
    echo 'Stop Success!'
fi

echo 准备从Git仓库拉取最新代码
cd /usr/local/javaapp/reggie_take_out

echo 开始从Git仓库拉取最新代码
git pull
echo 代码拉取完成

echo 开始打包
output=`mvn clean package -Dmaven.test.skip=true`

cd target

echo 启动项目
nohup java -jar reggie_take_out-1.0-SNAPSHOT.jar &> reggie_take_out.log &
echo 项目启动完成

4.3 第三步 

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第36张图片

六、图片报错问题

tail -f reggie_take_out.log

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第37张图片

6.1 修改图片存储为linu目录,并提交代码,重新执行脚本文件

【java】【项目实战】[外卖十二]【完结】项目优化(前后端分离开发)_第38张图片

6.2  将本地img图片上传到Linux机器目录

你可能感兴趣的:(java,开发语言)