• 【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机器目录


    前言:前后端分离开发

    一、问题说明

    二、前后端分离开发

    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

    1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    3. <modelVersion>4.0.0modelVersion>
    4. <groupId>com.runagroupId>
    5. <artifactId>reggie_take_out_spuerartifactId>
    6. <version>1.0-SNAPSHOTversion>
    7. <packaging>jarpackaging>
    8. <parent>
    9. <groupId>org.springframework.bootgroupId>
    10. <artifactId>spring-boot-starter-parentartifactId>
    11. <version>2.4.5version>
    12. <relativePath/>
    13. parent>
    14. <name>reggie_take_out_spuername>
    15. <url>http://maven.apache.orgurl>
    16. <properties>
    17. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    18. <java.version>1.8java.version>
    19. properties>
    20. <dependencies>
    21. <dependency>
    22. <groupId>com.aliyungroupId>
    23. <artifactId>aliyun-java-sdk-coreartifactId>
    24. <version>4.5.16version>
    25. dependency>
    26. <dependency>
    27. <groupId>com.aliyungroupId>
    28. <artifactId>aliyun-java-sdk-dysmsapiartifactId>
    29. <version>2.1.0version>
    30. dependency>
    31. <dependency>
    32. <groupId>org.springframework.bootgroupId>
    33. <artifactId>spring-boot-starterartifactId>
    34. dependency>
    35. <dependency>
    36. <groupId>org.springframework.bootgroupId>
    37. <artifactId>spring-boot-starter-testartifactId>
    38. <scope>testscope>
    39. dependency>
    40. <dependency>
    41. <groupId>org.springframework.bootgroupId>
    42. <artifactId>spring-boot-starter-webartifactId>
    43. <scope>compilescope>
    44. dependency>
    45. <dependency>
    46. <groupId>com.baomidougroupId>
    47. <artifactId>mybatis-plus-boot-starterartifactId>
    48. <version>3.4.2version>
    49. dependency>
    50. <dependency>
    51. <groupId>org.projectlombokgroupId>
    52. <artifactId>lombokartifactId>
    53. <version>1.18.20version>
    54. dependency>
    55. <dependency>
    56. <groupId>com.alibabagroupId>
    57. <artifactId>fastjsonartifactId>
    58. <version>1.2.76version>
    59. dependency>
    60. <dependency>
    61. <groupId>commons-langgroupId>
    62. <artifactId>commons-langartifactId>
    63. <version>2.6version>
    64. dependency>
    65. <dependency>
    66. <groupId>mysqlgroupId>
    67. <artifactId>mysql-connector-javaartifactId>
    68. <scope>runtimescope>
    69. dependency>
    70. <dependency>
    71. <groupId>com.alibabagroupId>
    72. <artifactId>druid-spring-boot-starterartifactId>
    73. <version>1.1.23version>
    74. dependency>
    75. <dependency>
    76. <groupId>junitgroupId>
    77. <artifactId>junitartifactId>
    78. <version>3.8.1version>
    79. <scope>testscope>
    80. dependency>
    81. <dependency>
    82. <groupId>com.github.xiaoymingroupId>
    83. <artifactId>knife4j-spring-boot-starterartifactId>
    84. <version>3.0.2version>
    85. dependency>
    86. dependencies>
    87. <build>
    88. <plugins>
    89. <plugin>
    90. <groupId>org.springframework.bootgroupId>
    91. <artifactId>spring-boot-maven-pluginartifactId>
    92. <version>2.4.5version>
    93. plugin>
    94. plugins>
    95. build>
    96. project>

    2.2 导入knife4j相关配置(WebConfig)

    1. package com.runa.reggie.config;
    2. import com.fasterxml.jackson.databind.DeserializationFeature;
    3. import com.fasterxml.jackson.databind.ObjectMapper;
    4. import com.fasterxml.jackson.databind.module.SimpleModule;
    5. import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
    6. import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    7. import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
    8. import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
    9. import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
    10. import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
    11. import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
    12. import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
    13. import com.runa.reggie.common.JacksonObjectMapper;
    14. import com.runa.reggie.entity.Employee;
    15. import lombok.extern.slf4j.Slf4j;
    16. import org.springframework.context.annotation.Bean;
    17. import org.springframework.context.annotation.Configuration;
    18. import org.springframework.http.converter.HttpMessageConverter;
    19. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    20. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    21. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    22. import springfox.documentation.builders.ApiInfoBuilder;
    23. import springfox.documentation.builders.PathSelectors;
    24. import springfox.documentation.builders.RequestHandlerSelectors;
    25. import springfox.documentation.service.ApiInfo;
    26. import springfox.documentation.spi.DocumentationType;
    27. import springfox.documentation.spring.web.plugins.Docket;
    28. import springfox.documentation.swagger2.annotations.EnableSwagger2;
    29. import java.math.BigInteger;
    30. import java.time.LocalDate;
    31. import java.time.LocalDateTime;
    32. import java.time.LocalTime;
    33. import java.time.format.DateTimeFormatter;
    34. import java.util.List;
    35. import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
    36. @Slf4j
    37. @Configuration
    38. @EnableSwagger2
    39. @EnableKnife4j
    40. public class WebMvcConfig extends WebMvcConfigurationSupport {
    41. /**
    42. * 设置静态资源映射
    43. * @param registry
    44. */
    45. @Override
    46. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    47. log.info("~~~开始进行静态资源映射...~~");
    48. registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
    49. registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    50. registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
    51. registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    52. }
    53. /**
    54. * 扩展MVC框架的消息转换器
    55. * @param converters
    56. */
    57. @Override
    58. protected void extendMessageConverters(List> converters) {
    59. log.info("扩展消息转换器。。。。");
    60. // 创建消息转换器对象
    61. MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    62. //设置对象转换器,底层使用Jackson将java对象转为json
    63. messageConverter.setObjectMapper(new JacksonObjectMapper());
    64. //将上面的消息转换器对象追加到MVC框架转换器集合中
    65. converters.add(0,messageConverter);
    66. }
    67. @Bean
    68. public Docket createRestApi() {
    69. // 文档类型
    70. return new Docket(DocumentationType.SWAGGER_2)
    71. .apiInfo(apiInfo())
    72. .select()
    73. .apis(RequestHandlerSelectors.basePackage("com.runa.reggie.controller"))
    74. .paths(PathSelectors.any())
    75. .build();
    76. }
    77. private ApiInfo apiInfo() {
    78. return new ApiInfoBuilder()
    79. .title("瑞吉外卖")
    80. .version("1.0")
    81. .description("瑞吉外卖接口文档")
    82. .build();
    83. }
    84. }

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

    1. package com.runa.reggie.filter;
    2. import com.alibaba.fastjson.JSON;
    3. import com.runa.reggie.common.BaseContext;
    4. import com.runa.reggie.common.R;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.springframework.util.AntPathMatcher;
    7. import javax.servlet.*;
    8. import javax.servlet.annotation.WebFilter;
    9. import javax.servlet.http.HttpServletRequest;
    10. import javax.servlet.http.HttpServletResponse;
    11. import java.io.IOException;
    12. @Slf4j
    13. @WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
    14. public class LoginCheckFilter implements Filter {
    15. // 路径匹配器
    16. public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    17. @Override
    18. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    19. HttpServletRequest request = (HttpServletRequest) servletRequest;
    20. HttpServletResponse response = (HttpServletResponse) servletResponse;
    21. // 1 获取本次请求的URI
    22. // request.getRequestURL()返回的是全路径。
    23. // request.getRequestURI()返回的是除去协议,域名(IP+端口号)之后的路由部分。
    24. String requestURI = request.getRequestURI();
    25. log.info("拦截到请求:【{}】",requestURI);
    26. // 定义一些不需要处理的路径
    27. String[] urls = new String[]{
    28. "/employee/login",
    29. "/employee/logout",
    30. "/backend/**",
    31. "/front/**",
    32. "/common/**",
    33. "/user/sendMsg",
    34. "/user/login",
    35. "/doc.html",
    36. "/webjars/**",
    37. "/swagger-resources",
    38. "/v2/api-docs"
    39. };
    40. // 2 判断本次请求是否需要处理
    41. boolean check = check(urls, requestURI);
    42. // 3 如果不需要处理,则直接放行
    43. if(check){
    44. log.info("本次请求【 {} 】 不需要处理",requestURI);
    45. filterChain.doFilter(request,response);
    46. return;
    47. }
    48. // 4 -1(员工端) 判断登录状态,如果已登录,则直接放行
    49. if(request.getSession().getAttribute("employee") != null){
    50. log.info("用户已登录,请求url为:【{}】, 用户id为:【 {} 】 ",requestURI,request.getSession().getAttribute("employee"));
    51. // 获取ID 塞进线程
    52. Long empId = (Long) request.getSession().getAttribute("employee");
    53. BaseContext.setCurrentId(empId);
    54. filterChain.doFilter(request,response);
    55. return;
    56. }
    57. // 4 -2(移动端) 判断登录状态,如果已登录,则直接放行
    58. if(request.getSession().getAttribute("user") != null){
    59. log.info("用户已登录,请求url为:【{}】, 用户id为:【 {} 】 ",requestURI,request.getSession().getAttribute("user"));
    60. // 获取ID 塞进线程
    61. Long userId = (Long) request.getSession().getAttribute("user");
    62. BaseContext.setCurrentId(userId);
    63. filterChain.doFilter(request,response);
    64. return;
    65. }
    66. log.info("用户未登录");
    67. // 5 如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
    68. response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
    69. return;
    70. }
    71. /**
    72. * 路径匹配,检查本次请求是否需要放行
    73. * @param urls
    74. * @param requestURI
    75. * @return
    76. */
    77. public boolean check(String[] urls, String requestURI){
    78. for (String url : urls) {
    79. boolean match = PATH_MATCHER.match(url, requestURI);
    80. if(match){
    81. return true;
    82. }
    83. }
    84. // 循环都匹配不上就返回false
    85. return false;
    86. }
    87. }

      

    2.4 验证

    启动服务

    http://localhost:8080/doc.html

     

    2.5 调试接口

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

    2.6 下载接口文档

    3、常用注解

    3.1 Setmeal实体添加注解

    1. package com.runa.reggie.entity;
    2. import com.baomidou.mybatisplus.annotation.FieldFill;
    3. import com.baomidou.mybatisplus.annotation.IdType;
    4. import com.baomidou.mybatisplus.annotation.TableField;
    5. import com.baomidou.mybatisplus.annotation.TableId;
    6. import io.swagger.annotations.ApiModel;
    7. import io.swagger.annotations.ApiModelProperty;
    8. import lombok.Data;
    9. import java.io.Serializable;
    10. import java.math.BigDecimal;
    11. import java.time.LocalDateTime;
    12. /**
    13. * 套餐
    14. */
    15. @Data
    16. @ApiModel("套餐")
    17. public class Setmeal implements Serializable {
    18. private static final long serialVersionUID = 1L;
    19. @ApiModelProperty("主键")
    20. private Long id;
    21. //分类id
    22. @ApiModelProperty("分类ID")
    23. private Long categoryId;
    24. //套餐名称
    25. @ApiModelProperty("套餐名称")
    26. private String name;
    27. //套餐价格
    28. @ApiModelProperty("套餐价格")
    29. private BigDecimal price;
    30. //状态 0:停用 1:启用
    31. @ApiModelProperty("状态")
    32. private Integer status;
    33. //编码
    34. @ApiModelProperty("编码")
    35. private String code;
    36. //描述信息
    37. @ApiModelProperty("描述信息")
    38. private String description;
    39. //图片
    40. @ApiModelProperty("图片")
    41. private String image;
    42. @TableField(fill = FieldFill.INSERT)
    43. private LocalDateTime createTime;
    44. @TableField(fill = FieldFill.INSERT_UPDATE)
    45. private LocalDateTime updateTime;
    46. @TableField(fill = FieldFill.INSERT)
    47. private Long createUser;
    48. @TableField(fill = FieldFill.INSERT_UPDATE)
    49. private Long updateUser;
    50. //是否删除
    51. private Integer isDeleted;
    52. }

     3.2 R 添加注解

    1. package com.runa.reggie.common;
    2. import io.swagger.annotations.ApiModel;
    3. import io.swagger.annotations.ApiModelProperty;
    4. import lombok.Data;
    5. import java.util.HashMap;
    6. import java.util.Map;
    7. /**
    8. * 通用返回结果,服务端响应的数据最终都会封装成此对象
    9. * @param
    10. */
    11. @Data
    12. @ApiModel("返回结果")
    13. public class R {
    14. @ApiModelProperty("编码")
    15. private Integer code; //编码:1成功,0和其它数字为失败
    16. @ApiModelProperty("错误信息")
    17. private String msg; //错误信息
    18. @ApiModelProperty("数据")
    19. private T data; //数据
    20. @ApiModelProperty("动态数据")
    21. private Map map = new HashMap(); //动态数据
    22. public static R success(T object) {
    23. R r = new R();
    24. r.data = object;
    25. r.code = 1;
    26. return r;
    27. }
    28. public static R error(String msg) {
    29. R r = new R();
    30. r.msg = msg;
    31. r.code = 0;
    32. return r;
    33. }
    34. public R add(String key, Object value) {
    35. this.map.put(key, value);
    36. return this;
    37. }
    38. }

    3.3 SetmealController添加注解 

    1. package com.runa.reggie.controller;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.runa.reggie.common.R;
    5. import com.runa.reggie.dto.SetmealDto;
    6. import com.runa.reggie.entity.Category;
    7. import com.runa.reggie.entity.Setmeal;
    8. import com.runa.reggie.service.CategoryService;
    9. import com.runa.reggie.service.SetmealDishService;
    10. import com.runa.reggie.service.SetmealService;
    11. import io.swagger.annotations.Api;
    12. import io.swagger.annotations.ApiImplicitParam;
    13. import io.swagger.annotations.ApiImplicitParams;
    14. import io.swagger.annotations.ApiOperation;
    15. import lombok.extern.slf4j.Slf4j;
    16. import org.springframework.beans.BeanUtils;
    17. import org.springframework.beans.factory.annotation.Autowired;
    18. import org.springframework.web.bind.annotation.*;
    19. import java.util.List;
    20. import java.util.stream.Collectors;
    21. @RestController
    22. @RequestMapping("/setmeal")
    23. @Slf4j
    24. @Api(tags = "套餐相关接口")
    25. public class SetmealController {
    26. @Autowired
    27. private SetmealService setmealService;
    28. @Autowired
    29. private CategoryService categoryService;
    30. @Autowired
    31. private SetmealDishService setmealDishService;
    32. /**
    33. * 新增套餐
    34. * @param setmealDto
    35. * @return
    36. */
    37. @PostMapping
    38. @ApiOperation(value = "新增套餐接口")
    39. public R save(@RequestBody SetmealDto setmealDto){
    40. log.info("套餐信息:{}",setmealDto);
    41. setmealService.saveWithDish(setmealDto);
    42. return R.success("新增套餐成功");
    43. }
    44. /**
    45. * 套餐分页查询
    46. * @param page
    47. * @param pageSize
    48. * @param name
    49. * @return
    50. */
    51. @GetMapping("/page")
    52. @ApiOperation(value = "套餐分页查询")
    53. @ApiImplicitParams({
    54. @ApiImplicitParam(name = "page", value = "页码", required = true),
    55. @ApiImplicitParam(name = "pageSize", value = "每页记录数", required = true),
    56. @ApiImplicitParam(name = "name", value = "套餐名称", required = false)
    57. })
    58. public R page(int page, int pageSize, String name){
    59. //分页构造器对象
    60. Page pageInfo = new Page<>(page,pageSize);
    61. Page dtoPage = new Page<>();
    62. LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    63. //添加查询条件,根据name进行like模糊查询
    64. queryWrapper.like(name != null,Setmeal::getName,name);
    65. //添加排序条件,根据更新时间降序排列
    66. queryWrapper.orderByDesc(Setmeal::getUpdateTime);
    67. setmealService.page(pageInfo,queryWrapper);
    68. //对象拷贝
    69. BeanUtils.copyProperties(pageInfo,dtoPage,"records");
    70. List records = pageInfo.getRecords();
    71. List list = records.stream().map((item) -> {
    72. SetmealDto setmealDto = new SetmealDto();
    73. //对象拷贝
    74. BeanUtils.copyProperties(item,setmealDto);
    75. //分类id
    76. Long categoryId = item.getCategoryId();
    77. //根据分类id查询分类对象
    78. Category category = categoryService.getById(categoryId);
    79. if(category != null){
    80. //分类名称
    81. String categoryName = category.getName();
    82. setmealDto.setCategoryName(categoryName);
    83. }
    84. return setmealDto;
    85. }).collect(Collectors.toList());
    86. dtoPage.setRecords(list);
    87. return R.success(dtoPage);
    88. }
    89. /**
    90. * 删除套餐
    91. * @param ids
    92. * @return
    93. */
    94. @DeleteMapping
    95. @ApiOperation(value = "删除套餐接口")
    96. public R delete(@RequestParam List ids){
    97. log.info("ids:{}",ids);
    98. setmealService.removeWithDish(ids);
    99. return R.success("套餐数据删除成功");
    100. }
    101. /**
    102. * 根据条件查询套餐数据
    103. * @param setmeal
    104. * @return
    105. */
    106. @GetMapping("/list")
    107. @ApiOperation(value = "根据条件查询套餐数据接口")
    108. public R> list(Setmeal setmeal){
    109. LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    110. queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId());
    111. queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());
    112. queryWrapper.orderByDesc(Setmeal::getUpdateTime);
    113. List list = setmealService.list(queryWrapper);
    114. return R.success(list);
    115. }
    116. }

     

    五、项目部署

    1、部署架构

    2、部署环境说明

    3、部署前端项目

    3.1 第一步

     3.2 第二步

     

    4、部署后端项目

    4.1 第一步

     4.2 第二步  reggieStart.sh 

    1. #!/bin/sh
    2. echo =================================
    3. echo 自动化部署脚本启动
    4. echo =================================
    5. echo 停止原来运行中的工程
    6. APP_NAME=reggie_take_out
    7. tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
    8. if [ ${tpid} ]; then
    9. echo 'Stop Process...'
    10. kill -15 $tpid
    11. fi
    12. sleep 2
    13. tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
    14. if [ ${tpid} ]; then
    15. echo 'Kill Process!'
    16. kill -9 $tpid
    17. else
    18. echo 'Stop Success!'
    19. fi
    20. echo 准备从Git仓库拉取最新代码
    21. cd /usr/local/javaapp/reggie_take_out
    22. echo 开始从Git仓库拉取最新代码
    23. git pull
    24. echo 代码拉取完成
    25. echo 开始打包
    26. output=`mvn clean package -Dmaven.test.skip=true`
    27. cd target
    28. echo 启动项目
    29. nohup java -jar reggie_take_out-1.0-SNAPSHOT.jar &> reggie_take_out.log &
    30. echo 项目启动完成

    4.3 第三步 

    六、图片报错问题

    tail -f reggie_take_out.log

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

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

  • 相关阅读:
    [C++] 小游戏 斗破苍穹 2.2.1至2.11.5所有版本(中) zty出品
    OpenGL绘制三角形
    c#装饰器模式详解
    05 【函数(上)】
    软件测试 | 当面试时被问到“搭建过测试环境吗”, 身为小白要怎么回答?
    数据结构——二叉排序树的删除操作(非递归)
    vue2.深入响应式原理
    【深入理解TcaplusDB技术】如何实现Tmonitor单机安装
    行为型模式-观察者模式
    图解kd树+Python实现
  • 原文地址:https://blog.csdn.net/legend818/article/details/132674344