苍穹外卖-day01

HM大约 36 分钟苍穹外卖软件开发流程登录自定义异常MD5JWT

苍穹外卖-day01

课程内容

  • 软件开发整体介绍
  • 苍穹外卖项目介绍
  • 开发环境搭建
  • 导入接口文档
  • Swagger

知识储备

  1. 熟悉Maven项目的聚合和继承 👈
  2. 了解Maven依赖的版本锁定
  3. 了解前后端分离开发的好处以及项目开发的流程
  4. 了解分模块开发的作用
  5. 熟悉Jwt令牌的特性和作用 👈
  6. 能独立利用SpringBoot和Mybatis开发单表的接口

项目整体效果展示:

管理端原型open in new window

用户端原型open in new window

image-20221106160753928

当我们完成该项目的学习,可以培养以下能力:🎯

image-20221106161507973

1. 软件开发整体介绍 🍐

软件开发整体介绍

前置知识

作为一名软件开发工程师,我们需要了解:

  1. 开发流程
  2. 岗位角色,角色的分工、职责
  3. 开发中涉及到的三种软件环境

1.1 软件开发流程

image-20221106162815172

1). 第1阶段: 需求分析产品经理

完成需求规格说明书、产品原型编写。

需求规格说明书, 一般来说就是使用 Word 文档来描述当前项目的各个组成部分,如:系统定义、应用环境、功能规格、性能需求等,都会在文档中描述。例如:

image-20221106163758703

产品原型,一般是通过网页(html)的形式展示当前的页面展示什么样的数据, 页面的布局是什么样子的,点击某个菜单,打开什么页面,点击某个按钮,出现什么效果,都可以通过产品原型看到。 例如:

image-20221106163925031

1.2 角色分工

角色分工

在对整个软件开发流程熟悉后, 我们还有必要了解一下在整个软件开发流程中涉及到的岗位角色,以及各个角色的职责分工。

image-20210725234015404
image-20210725234015404
岗位/角色对应阶段职责/分工
项目经理全阶段对整个项目负责,任务分配、把控进度
产品经理需求分析进行需求调研,输出需求调研文档、产品原型等
UI设计师设计根据产品原型输出界面效果图
架构师设计项目整体架构设计、技术选型等
测试工程师测试编写测试用例,输出测试报告
运维工程师上线运维软件环境搭建、项目上线

在实际的项目中, 有一些项目组由于人员配置紧张, 可能并没有专门的架构师或测试人员, 这个时候可能需要有项目经理或者程序员兼任身兼多职

1.3 软件环境 🍐

软件环境

作为软件开发工程师,在编码的过程中就不可避免地会接触多种软件环境,我们主要来分析在工作中经常遇到的三套环境, 分别是: 开发环境、测试环境、生产环境

1). 开发环境(development)开发工程师

我们作为软件开发人员,在开发阶段使用的环境,就是开发环境,一般外部用户无法访问。

比如,我们在开发中使用的MySQL数据库和其他的一些常用软件,我们可以安装在本地, 也可以安装在一台专门的服务器中, 这些应用软件仅仅在软件开发过程中使用, 项目测试、上线时,我们不会使用这套环境了,这个环境就是开发环境。

开发环境---------------------------------------测试环境 ---------------------------------生产环境

image-20221106182912829
image-20221106182912829

环境切换流程:

  • 1️⃣ 首先,会在开发环境中进行项目开发,往往开发环境大多数都是本地的电脑环境和局域网内的环境,
  • 2️⃣ 当开发完毕后,然后会把项目部署到测试环境,测试环境一般是一台独立测试服务器的环境,
  • 3️⃣ 项目测试通过后,最终把项目部署到生产环境,生产环境可以是机房或者云服务器等线上环境。🎉

2. 苍穹外卖项目介绍 📟

2.1 项目介绍

项目介绍

  1. 本项目(苍穹外卖)是专门为餐饮企业(餐厅、饭店)定制的一款软件产品,包括 系统管理后台 💻 和 小程序端应用📱 两部分。
  2. 💻其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的分类、菜品、套餐、订单、员工等进行管理维护,对餐厅的各类数据进行统计,同时也可进行来单语音播报功能。
  3. 📱小程序端主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单、支付、催单等。
image-20221106185252326
image-20221106185252326

接下来,通过功能架构图来展示管理端用户端的具体业务功能模块。

image-20221106194424735

2.2 产品原型

产品原型

产品原型,用于展示项目的业务功能,一般由产品经理进行设计。

产品原型主要用于展示项目的功能,并不是最终的页面效果。

在课程资料的产品原型文件夹下,提供了两份产品原型。

image-20221106195023552
image-20221106195023552

管理端原型open in new window 👈

用户端原型open in new window 👈

管理端原型图:

image-20221106195259858
点击查看管理端功能

1). 管理端

餐饮企业内部员工使用。 主要功能有:

模块描述
登录/退出内部员工必须登录后,才可以访问系统管理后台
员工管理管理员可以在系统后台对员工信息进行管理,包含查询、新增、编辑、禁用等功能
分类管理主要对当前餐厅经营的 菜品分类 或 套餐分类 进行管理维护, 包含查询、新增、修改、删除等功能
菜品管理主要维护各个分类下的菜品信息,包含查询、新增、修改、删除、启售、停售等功能
套餐管理主要维护当前餐厅中的套餐信息,包含查询、新增、修改、删除、启售、停售等功能
订单管理主要维护用户在移动端下的订单信息,包含查询、取消、派送、完成,以及订单报表下载等功能
数据统计主要完成对餐厅的各类数据统计,如营业额、用户数量、订单等

用户端原型图:

image-20221106195354556
点击查看用户端功能

2). 用户端

移动端应用主要提供给消费者使用。主要功能有:

模块描述
登录/退出用户需要通过微信授权后登录使用小程序进行点餐
点餐-菜单在点餐界面需要展示出菜品分类/套餐分类, 并根据当前选择的分类加载其中的菜品信息, 供用户查询选择
点餐-购物车用户选中的菜品就会加入用户的购物车, 主要包含 查询购物车、加入购物车、删除购物车、清空购物车等功能
订单支付用户选完菜品/套餐后, 可以对购物车菜品进行结算支付, 这时就需要进行订单的支付
个人信息在个人中心页面中会展示当前用户的基本信息, 用户可以管理收货地址, 也可以查询历史订单数据

2.3 技术选型 🍐

技术选型

关于本项目的技术选型, 我们将会从 用户层、网关层、应用层、数据层 这几个方面进行介绍,主要用于展示项目中使用到的技术框架和中间件等。

image-20221106185646994
image-20221106185646994

1). 用户层前端工程师

本项目中在构建系统管理后台的前端页面,我们会用到H5、Vue.js、ElementUI、apache echarts(展示图表) 等技术。而在构建移动端应用时,我们会使用到微信小程序。

3. 开发环境搭建

3.1 前端和后端搭建

开发环境搭建

image-20221106200821282
  1. 开发环境搭建主要包含前端环境后端环境两部分。
  2. 作为服务端开发工程师, 重心放在后端的业务代码上, 前端的页面我们只需要导入资料中的nginx

前端环境搭建 👇 👇

相关信息

  1. 前端工程基于 nginx
  2. 启动nginx,访问测试

1). 前端工程基于 nginx

从资料中找到前端运行环境的nginx,移动到非中文目录下。

image-20221106202239828
image-20221106202239828

sky目录中存放了管理端的前端资源,具体如下:

image-20221106202517738

后端环境搭建 👇👇

熟悉项目结构

后端工程基于 maven 进行项目构建,并且进行分模块开发。

1️⃣ 1). 从当天资料中找到后端初始工程:
image-20221107092342140
2️⃣ 2). 用 IDEA 打开初始工程,了解项目的整体结构:
image-20221107092540194

对工程的每个模块作用说明:

序号名称说明
1sky-take-outmaven父工程 ,统一管理依赖版本,聚合其他子模块
2sky-common子模块,存放公共类,例如:工具类、常量类、异常类等
3sky-pojo子模块,存放实体类、VO、DTO等
4sky-server子模块,后端服务,存放配置文件、Controller、Service、Mapper等

对项目整体结构了解后,接下来我们详细分析上述的每个子模块:

  1. maven父工程统一管理依赖版本 ,聚合其他子模
  2. 没有代码 ,只有一个pom.xml文件

总结

课堂作业

  1. 苍穹外卖的前后端分离了吗?前端使用什么服务器,后端采用什么结构设计?🎤
  2. Maven父工程和聚合工程的打包方式是什么? 父工程主要由什么组成?每个模块含有什么内容?🎤

3.2 Git版本控制

Git版本控制

使用Git进行项目代码的版本控制,具体操作:

  1. 创建Git本地仓库
  2. 创建Git远程仓库
  3. 将本地文件推送到Git远程仓库

1). 创建Git本地仓库

image-20221107164030050image-20221107135640100

当Idea中出现:

image-20221107135819662

说明本地仓库创建成功。

总结

课堂作业

  1. 为什么要创建git仓库? 🎤
  2. 每完成一个接口,要提交git吗?要写message信息吗? 为什么要写? 🎤
  3. 项目总的文件,所有都要提交吗?🎤

3.3 数据库环境搭建

数据库环境搭建

  1. 从资料中找到sky.sql
image-20221107101030138

直接打开sky.sql文件

image-20221107101238205
image-20221107101238205

通过该sql文件直接可创建数据库,所以不需要提前创建数据库,直接导入该文件执行即可。

  1. 执行sky.sql文件

image-20221107102152285image-20221107102416034image-20221107104738200

执行完成后,共创建出11张表

image-20221107105103816

每张表的说明:

序号表名中文名
1employee员工表
2category分类表
3dish菜品表
4dish_flavor菜品口味表
5setmeal套餐表
6setmeal_dish套餐菜品关系表
7user用户表
8address_book地址表
9shopping_cart购物车表
10orders订单表
11order_detail订单明细表

我们目前先简单了解大概有哪些表, 每张表存储什么数据, 有一个印象。对于具体的表结构, 以及表结构中的字段, 可以参考资料中的 数据库设计文档 👈 👈,同时在讲解具体的功能开发时, 我们也会再详细介绍。

image-20221107142344642

总结

课堂作业

  1. 数据库设计的工作一般是什么角色进行的 🎤
  2. 数据库表设计是依据什么来进行设计的?🎤
  3. 数据库表中的字段,在前端全部都会展示吗?🎤

3.4 前后端联调

前后端联调

后端的初始工程中已经实现了登录功能,直接进行前后端联调测试即可

实现思路:

image-20221107110539832

1.Controller层

在sky-server模块中,com.sky.controller.admin.EmployeeController

/**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        log.info("员工登录:{}", employeeLoginDTO);
		//调用service方法查询数据库
        Employee employee = employeeService.login(employeeLoginDTO);

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                .id(employee.getId())
                .userName(employee.getUsername())
                .name(employee.getName())
                .token(token)
                .build();

        return Result.success(employeeLoginVO);
    }

总结

课堂作业

  1. 前后端联调这项工作时什么时候进行的? 🎤 open in new window
  2. 前后端联调工作的主要内容是什么?或者说为什么要进行前后端联调 🎤

3.5 nginx反向代理和负载均衡 🚀

nginx反向代理和负载均衡

对登录功能测试完毕后,接下来,我们思考一个问题:前端发送的请求,是如何请求到后端服务的?

前端请求地址:http://localhost/api/employee/loginopen in new window

后端接口地址:http://localhost:8080/admin/employee/loginopen in new window

前端请求地址 后端接口地址

image-20221107151607921 image-20221107151623005

很明显,两个地址不一致,那是如何请求到后端服务的呢?

image-20221107152041371
image-20221107152041371

1). nginx反向代理

nginx 反向代理,就是将前端发送的动态请求由 nginx 转发到后端服务器

image-20221107152112092

那为什么不直接通过浏览器直接请求后台服务端,需要通过nginx反向代理呢?

反向代理的好处:

  1. nginx 反向代理可以提高访问速度、进行负载均衡、保证后端服务安全
点击查看
  • 提高访问速度

    因为nginx本身可以进行缓存,如果访问的同一接口,并且做了数据缓存,nginx就直接可把数据返回,不需要真正地访问服务端,从而提高访问速度。

  • 进行负载均衡

    所谓负载均衡,就是把大量的请求按照我们指定的方式均衡的分配给集群中的每台服务器。

  • 保证后端服务安全

    因为一般后台服务地址不会暴露,所以使用浏览器不能直接访问,可以把nginx作为请求访问的入口,请求到达nginx后转发到具体的服务中,从而保证后端服务的安全。

image-20221107153808368

反向代理的配置方式

  1. 可以在nginx的配置文件中配置 反向代理
点击查看

nginx 反向代理的配置方式:

server{
    listen 80;
    server_name localhost;
    
    location /api/{
        proxy_pass http://localhost:8080/admin/; #反向代理
    }
}

proxy_pass: 该指令是用来设置代理服务器的地址,可以是主机名称,IP地址加端口号等形式。

如上代码的含义是:监听80端口号, 然后当我们访问 http://localhost:80/api/../..这样的接口的时候,它会通过 location /api/ {} 这样的反向代理到 http://localhost:8080/admin/上来。

接下来,进到nginx-1.20.2\conf,打开nginx配置 👇

# 反向代理,处理管理端发送的请求
location /api/ {
	proxy_pass   http://localhost:8080/admin/;
    #proxy_pass   http://webservers/admin/;
}

当在访问http://localhost/api/employee/login,nginx接收到请求后转到http://localhost:8080/admin/,故最终的请求地址为http://localhost:8080/admin/employee/login,和后台服务的访问地址一致。

nginx 负载均衡

当如果服务以集群的方式进行部署时,那nginx在转发请求到服务器时就需要做相应的负载均衡。其实,负载均衡从本质上来说也是基于反向代理来实现的,最终都是转发请求。

点击查看

nginx 负载均衡的配置方式:

#集群信息
upstream webservers{
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}
server{
    listen 80;
    server_name localhost;
    
    location /api/{
        proxy_pass http://webservers/admin;#负载均衡
    }
}

 
 










upstream: 如果代理服务器是一组服务器的话,我们可以使用upstream指令配置后端服务器组。

如上代码的含义是:监听80端口号, 然后当我们访问 http://localhost:80/api/../..这样的接口的时候,它会通过 location /api/ {} 这样的反向代理到 http://webservers/admin,根据webservers名称找到一组服务器,根据设置的负载均衡策略(默认是轮询)转发到具体的服务器。

注: upstream后面的名称可自定义,但要上下保持一致。

nginx 负载均衡策略:

名称说明
轮询默认方式
weight权重方式,默认为1,权重越高,被分配的客户端请求就越多
ip_hash依据ip分配方式,这样每个访客可以固定访问一个后端服务
least_conn依据最少连接方式,把请求优先分配给连接数少的后端服务
url_hash依据url分配方式,这样相同的url会被分配到同一个后端服务
fair依据响应时间方式,响应时间短的服务将会被优先分配

具体配置方式:

轮询:

upstream webservers{
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}

weight:

upstream webservers{
    server 192.168.100.128:8080 weight=90;
    server 192.168.100.129:8080 weight=10;
}

ip_hash:

upstream webservers{
    ip_hash;
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}

least_conn:

upstream webservers{
    least_conn;
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}

url_hash:

upstream webservers{
    hash &request_uri;
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
}

fair:

upstream webservers{
    server 192.168.100.128:8080;
    server 192.168.100.129:8080;
    fair;
}

总结

课堂作业

  1. 反向代理的好处是什么? 🎤
  2. nginx 负载均衡默认是什么策略?🎤

3.6 完善登录功能 ✏️

完善登录功能

  1. 员工表中的密码是明文存储,安全性太低
image-20221107160529803
image-20221107160529803

解决思路:

  1. 将密码加密后存储,提高安全性

    image-20221107161918913
  2. 使用MD5加密方式对明文密码加密

    image-20221107160739680

    MD5在线加密open in new window 👇

实现步骤
  1. 修改数据库中明文密码,改为MD5加密后的密文

    打开employee表,修改密码

    image-20221107161446710
    image-20221107161446710
  2. 修改Java代码,前端提交的密码进行MD5加密后再跟数据库中密码比对

    打开EmployeeServiceImpl.java,修改比对密码

    /**
         * 员工登录
         *
         * @param employeeLoginDTO
         * @return
         */
        public Employee login(EmployeeLoginDTO employeeLoginDTO) {
    
            //1、根据用户名查询数据库中的数据
           
            //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
            //.......
            //密码比对
            // TODO 后期需要进行md5加密,然后再进行比对
            password = DigestUtils.md5DigestAsHex(password.getBytes());
            if (!password.equals(employee.getPassword())) {
                //密码错误
                throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
            }
    
            //........
    
            //3、返回实体对象
            return employee;
        }
    














     
     
     
     







4. 接口文档相关 ✏️

导入接口文档

相关信息

  1. 开发方式就是基本当前企业主流的前后端分离开发方式
  2. 先定义接口,前后端人员并行开发

其实,在真实的企业开发中,接口设计过程其实是一个非常漫长的过程,可能需要多次开会讨论调整,甚至在开发的过程中才会发现某些接口定义还需要再调整,这种情况其实是非常常见的.

前后端分离开发流程

image-20221107181446913
  1. 第一步:定义接口,确定接口的路径、请求方式、传入参数、返回参数已经完成
  2. 第二步:前端开发人员和后端开发人员并行开发,同时,也可自测🎯。
  3. 第三步:前后端人员进行连调测试。
  4. 第四步:提交给测试人员进行最终测试。

操作步骤 👇

将课程资料中提供的项目接口导入YApi(或者Apifox)。访问地址:http://yapi.smart-xwork.cn/open in new window

1). 从资料中找到项目接口文件

image-20221107184823104

2). 导入到APifox

点击查看详情

另一个用户端json文件也执行相同操作。


如果导入到APIfox中,步鄹如下:

总结

课堂作业

  1. 接口文档主要是在软件开发流程的什么阶段进行的?🎤
  2. 接口文档导入到APifox平台管理有什么好处?🎤
  3. 为什么前端后端要按照接口文档书写代码?

5. Swagger

Swagger

1️⃣ 介绍

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(https://swagger.io/open in new window)。

image
image

它的主要作用是:

  1. 使得前后端分离开发更加方便,有利于团队协作
  2. 接口的文档在线自动生成,降低后端开发人员编写接口文档的负担
  3. 功能测试

Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox。通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!

目前,一般都使用knife4j框架

2️⃣ 使用步骤 👇

  1. 导入 knife4j 的maven坐标

    在pom.xml中添加依赖

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

思考

  1. 通过 Swagger 就可以生成接口文档,那么我们就不需要 Yapi/Apifox 了?
点击查看代码
  1. Yapi/Apifox 是设计阶段使用的工具,管理和维护接口

  2. Swagger 在开发阶段使用的框架,帮助后端开发人员做后端的接口测试

3️⃣ 常用注解 👇

通过注解可以控制生成的接口文档,使接口文档拥有更好的可读性,常用注解如下:

注解说明
@Api用在类上,例如Controller,表示对类的说明
@ApiModel用在类上,例如entity、DTO、VO
@ApiModelProperty用在属性上,描述属性信息
@ApiOperation用在方法上,例如Controller的方法,说明方法的用途、作用

接下来,使用上述注解,生成可读性更好的接口文档

在sky-pojo模块中

EmployeeLoginDTO.java

package com.sky.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

@Data
@ApiModel(description = "员工登录时传递的数据模型")
public class EmployeeLoginDTO implements Serializable {

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("密码")
    private String password;

}










 










EmployeeLoginVo.java

package com.sky.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "员工登录返回的数据格式")
public class EmployeeLoginVO implements Serializable {

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

    @ApiModelProperty("用户名")
    private String userName;

    @ApiModelProperty("姓名")
    private String name;

    @ApiModelProperty("jwt令牌")
    private String token;

}















 


 


 


 


 



启动服务:访问http://localhost:8080/doc.htmlopen in new window

image-20221107175649468

总结

课堂作业

  1. swagger能解决什么问题?🎤
  2. 能否利用swagger生成接口文档?如果要生成合格的接口文档,要做哪些工作?

课后作业

🚩 1. 重点完成上述的课堂作业

  1. 晚自习第一节课的前30分钟,总结完毕之后,每个同学先必须梳理今日知识点 (记得写不知道的,以及感恩三件事);整理好的笔记可以发给组长,组长交给班长,意在培养大家总结的能力)

  2. 晚自习第一节课的后30分钟开始练习(记住:程序员是代码堆起来的):

  3. 剩余的时间:预习第二天的知识,预习的时候一定要注意:

  • 预习不是学习,不要死看第二天的视频(很容易出现看了白看,为了看视频而看视频)
  • 预习看第二天的笔记,把笔记中标注重要的知识,可以找到预习视频,先看一遍,如果不懂的 ,记住做好标注。

今日面试题

回答方式采用结构话回答,点击查看教程

  1. 面试题:某餐饮管理系统的登录密码存储,请设计该模块的密码处理流程。
  1. 面试题:基于MD5加密介绍下登陆密码强化的思路。
  1. 面试题:某项目使用Swagger生成接口文档,请设计该功能的实现思路。
  1. 面试题: 使用Swagger有哪些好处?
  1. 面试题:某项目使用Nginx做反向代理,请设计大致工作流程。
  1. 面试题:Nginx负载均衡的常见算法有哪些?
  1. 面试题:开发一个系统时,如果没有专门的测试角色,如何进行测试?
  1. 面试题:某公司采用敏捷开发,请描述一个sprint的开发流程。
  1. 面试题:接口文档和Swagger的区别是什么?
  1. 面试题:开发过程中代码回滚的重要性?
上次编辑于:
贡献者: huhu520,yange