Part02 ☀️
Part02 ☀️
课程内容
- 软件开发整体 🍐
- 瑞吉外卖项目介绍
- 开发环境搭建 ✏️
- 后台登录功能开发 ✏️
- 后台退出功能开发 ✏️
- 完善登录功能 ✏️
1. 软件开发整体介绍 🍐
软件开发整体介绍
作为一名软件开发工程师,我们需要了解在软件开发过程中的开发流程, 以及软件开发过程中涉及到的岗位角色,角色的分工、职责, 并了解软件开发中涉及到的三种软件环境。那么这一小节,我们将从 三个方面,来整体上介绍一下软件开发。
软件开发流程 🍐

岗位/角色 | 职责/分工 |
---|---|
项目经理 | 对整个项目负责,任务分配、把控进度 |
产品经理 | 进行需求调研,输出需求调研文档、产品原型等 |
UI 设计师 | 根据产品原型输出界面效果图 |
架构师 | 项目整体架构设计、技术选型等 |
开发工程师 | 功能代码实现 |
测试工程师 | 编写测试用例,输出测试报告 |
运维工程师 | 软件环境搭建、项目上线 |
角色分工是在一个项目组中比较标准的角色分工, 但是在实际的项目中, 有一些项目组由于人员配置紧张, 可能并没有专门的架构师或测试人员, 这个时候可能需要有项目经理或者程序员兼任
软件环境 🍐

1). 开发环境(development)
我们作为软件开发人员,在开发阶段使用的环境,就是开发环境,一般外部用户无法访问 。
比如,我们在开发中使用的 MySQL 数据库和其他的一些常用软件,我们可以安装在本地, 也可以安装在一台专门的服务器中, 这些应用软件仅仅在软件开发过程中使用, 项目测试、上线时,我们不会使用这套环境了,这个环境就是开发环境。
2). 测试环境(testing)
当软件开发工程师,将项目的功能模块开发完毕,并且单元测试通过后,就需要将项目部署到测试服务器上,让测试人员对项目进行测试。那这台测试服务器就是专门给测试人员使用的环境, 也就是测试环境,用于项目测试,一般外部用户无法访问 。
3). 生产环境(production)
当项目开发完毕,并且由测试人员测试通过之后,就可以上线项目,将项目部署到线上环境,并正式对外提供服务, 这个线上环境也称之为生产环境。
2. 瑞吉外卖项目介绍 🍐
瑞吉外卖项目介绍
在开发瑞吉外卖这个项目之前,我们需要全方位的来介绍一下当前我们学习的这个项目。接下来,我们将从以下的五个方面 👇, 来介绍瑞吉外卖这个项目。
2.1 项目介绍

本项目(瑞吉外卖)是专门为餐饮企业(餐厅、饭店) 定制的一款软件产品,包括 系统管理后台 和 移动端应用 两部分。其中系统管理后台 主要提供给餐饮企业内部员工使用,可以对餐厅的分类、菜品、套餐、订单、员工等进行管理维护。移动端应用主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等。
本项目共分为 2 期进行开发:
阶段 | 功能实现 |
---|---|
第一期 | 主要实现基本需求,其中移动端应用通过 H5 实现,用户可以通过手机浏览器访问 |
第二期 | 主要针对系统进行优化升级,提高系统的访问性能 |
|
2.2 产品原型
产品原型,就是一款产品成型之前,由产品经理绘制的一个简单的框架,就是将页面的排版布局展现出来,使产品的初步构思有一个可视化的展示。通过原型展示,可以更加直观的了解项目的需求和提供的功能。
注意事项: 产品原型主要用于展示项目的功能,并不是最终的页面效果。
在课程资料的产品原型文件夹下,提供了两份产品原型。
管理端原型可点击
用户端原型可点击

1). 管理端
餐饮企业内部员工使用。 主要功能有:
模块 | 描述 |
---|---|
登录/退出 | 内部员工必须登录后,才可以访问系统管理后台 |
员工管理 | 管理员可以在系统后台对员工信息进行管理,包含查询、新增、编辑、禁用等功能 |
分类管理 | 主要对当前餐厅经营的 菜品分类 或 套餐分类 进行管理维护, 包含查询、新增、修改、删除等功能 |
菜品管理 | 主要维护各个分类下的菜品信息,包含查询、新增、修改、删除、启售、停售等功能 |
套餐管理 | 主要维护当前餐厅中的套餐信息,包含查询、新增、修改、删除、启售、停售等功能 |
订单明细 | 主要维护用户在移动端下的订单信息,包含查询、取消、派送、完成,以及订单报表下载等功能 |
2). 用户端
移动端应用主要提供给消费者使用。主要功能有:
模块 | 描述 |
---|---|
登录/退出 | 在移动端, 用户也需要登录后使用 APP 进行点餐 |
点餐-菜单 | 在点餐界面需要展示出菜品分类/套餐分类, 并根据当前选择的分类加载其中的菜品信息, 供用户查询选择 |
点餐-购物车 | 用户选中的菜品就会加入用户的购物车, 主要包含 查询购物车、加入购物车、删除购物车、清空购物车等功能 |
订单支付 | 用户选完菜品/套餐后, 可以对购物车菜品进行结算支付, 这时就需要进行订单的支付 |
个人信息 | 在个人中心页面中会展示当前用户的基本信息, 用户可以管理收货地址, 也可以查询历史订单数据 |
2.3 技术选型 🍐
关于本项目的技术选型, 我们将会从 用户层、网关层、应用层、数据层 这几个方面进行介绍,而对于我们服务端开发工程师来说,在项目开发过程中,我们主要关注应用层 及数据层 技术的应用。

1). 用户层
本项目中在构建系统管理后台的前端页面,我们会用到 H5、Vue.js、ElementUI 等技术。而在构建移动端应用时,我们会使用到微信小程序。
2). 网关层
Nginx 是一个服务器,主要用来作为 Http 服务器,部署静态资源,访问性能高。在 Nginx 中还有两个比较重要的作用: 反向代理和负载均衡, 在进行项目部署时,要实现 Tomcat 的负载均衡,就可以通过 Nginx 来实现。
3). 应用层
- SpringBoot: 快速构建 Spring 项目, 采用 "约定优于配置" 的思想, 简化 Spring 项目的配置开发。
- Spring: 统一管理项目中的各种资源(bean), 在 web 开发的各层中都会用到。
- SpringMVC(由 SpringBoot‘简化’):SpringMVC 是 spring 框架的一个模块,springmvc 和 spring 无需通过中间整合层进行整合,可以无缝集成。
- lombok:能以简单的注解形式来简化 java 代码,提高开发人员的开发效率。例如开发中经常需要写的 javabean,都需要花时间去添加相应的 getter/setter,也许还要去写构造器、equals 等方法。
- Swagger: 可以自动的帮助开发人员生成接口文档,并对接口进行测试。
4). 数据层
- MySQL: 关系型数据库, 本项目的核心业务数据都会采用 MySQL 进行存储。
- MybatisPlus: 本项目持久层将会使用 MybatisPlus 来简化开发, 基本的单表增删改查直接调用框架提供的方法即可。
- Redis: 基于 key-value 格式存储的内存数据库, 访问速度快, 经常使用它做缓存(降低数据库访问压力, 提供访问效率), 在后面的性能优化中会使用。
5). 工具
- git: 版本控制工具, 在团队协作中, 使用该工具对项目中的代码进行管理。(仅作了解)
- maven: 项目构建工具。
- junit:单元测试工具,开发人员功能实现完毕后,需要通过 junit 对功能进行单元测试。
2.4 功能架构 🍐

1). 移动端前台功能
手机号登录 , 微信登录 , 收件人地址管理 , 用户历史订单查询 , 菜品规格查询 , 购物车功能 , 下单 , 分类及菜品浏览。
2). 系统管理后台功能
员工登录/退出 , 员工信息管理 , 分类管理 , 菜品管理 , 套餐管理 , 菜品口味管理 , 订单管理 。
2.5 角色
在瑞吉外卖这个项目中,存在以下三种用户,这三种用户对应三个角色: 后台系统管理员、后台系统普通员工、C 端(移动端)用户。
角色 | 权限操作 |
---|---|
后台系统管理员 | 登录后台管理系统,拥有后台系统中的所有操作权限 |
后台系统普通员工 | 登录后台管理系统,对菜品、套餐、订单等进行管理 (不包含员工管理) |
C 端用户 | 登录移动端应用,可以浏览菜品、添加购物车、设置地址、在线下单等 |
3. 开发环境搭建 ✏️
3.1 数据库环境搭建
数据库环境搭建
创建数据库和数据库表导入
可以通过以下两种方式中的任意一种, 来创建项目的数据库和数据库表导入:

1).图形界面 使用Idea导入数据库资料

项目的数据库reggie必须先创建, 才可以直接将 资料/数据模型/db_reggie.sql 直接导入到数据库中
2).命令行(了解) 🚀

项目的数据库创建好了之后, 可以直接将 资料/数据模型/db_reggie.sql 直接导入到数据库中

注意: 通过命令导入表结构时,注意 sql 文件不要放在中文目录中
数据库表介绍
数据库表导入之后, 接下来介绍一下本项目中所涉及到的表结构:
序号 | 表名 | 说明 |
---|---|---|
1 | employee | 员工表 |
2 | category | 菜品和套餐分类表 |
3 | dish | 菜品表 |
4 | setmeal | 套餐表 |
5 | setmeal_dish | 套餐菜品关系表 |
6 | dish_flavor | 菜品口味关系表 |
7 | user | 用户表(C 端) |
8 | address_book | 地址簿表 |
9 | shopping_cart | 购物车表 |
10 | orders | 订单表 |
11 | order_detail | 订单明细表 |
总结
课堂作业
- 根据上述提示,使用Idea创建reggie数据库后,完成数据库脚本的导入🎤
- 根据上述提示,熟悉每一张表的业务意义
3.2 Maven 项目导入(12项目) ✏️
Maven 项目导入

导入步骤:

按照上述步骤,导入已有的Maven工作
注意 :导入成功后,需要检查Maven的配置,并且初次导入时间较长,可能是因为需要下载jar包,后续不会重复下载!
- 修改数据库名字为reggie,用户名和密码和自己的对应

- 找到ReggieApplication启动类,点击启动


- 打开浏览器,输入地址,访问测试
测试地址:http://localhost:8080/backend/index.html

4. 后台系统登录退出功能 🍐 ✏️
4.1 后台项目介绍和准备工作 🍐
后台项目介绍和准备工作
1). 页面原型展示

2). 登录页面成品展示
登录页面存放目录 /resources/backend/page/login/login.html

3). 查看登录请求
通过浏览器调试工具(F12),可以发现,点击登录按钮时,页面会发送请求(请求地址为 http://localhost:8080/employee/login)并提交参数 username 和 password, 请求参数为 json 格式数据 {"username":"admin","password":"123456"}。

此时报 404,是因为我们的后台系统还没有响应此请求的处理器,所以我们需要创建相关类来处理登录请求 ;
4). 数据模型(employee 表)

代码操作
1. 工程说明:
- common --放一些通用的类
- config --配置文件
- controller --放一些控制类,用来处理前端请求
- entity --对象实体类
- mapper --操作数据库的接口类
- service --业务层代码
调用顺序: 控制层(controller) 调用业务层(service)--->调用持久层(mapper)
2). 创建实体类 Employee
该实体类主要用于和员工表 employee 进行映射。 该实体类, 也可以直接从资料( 资料/实体类 )中拷贝工程中。
所属包: com.itheima.reggie.entity
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber; //驼峰命名法 ---> 映射的字段名为 id_number
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
}
3). 定义 Mapper 接口和Service接口
在 MybatisPlus 中, 自定义的 Mapper 接口, 需要继承自 BaseMapper。
所属包: com.itheima.reggie.mapper
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee>{
}
本项目的 Service 接口, 在定义时需要继承自 MybatisPlus 提供的 Service 层接口 IService, 这样就可以直接调用 父接口的方法直接执行业务操作, 简化业务层代码实现。
所属包: com.itheima.reggie.service
public interface EmployeeService extends IService<Employee> {
}

4). Service 实现类
所属包: com.itheima.reggie.service.impl
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper,Employee> implements EmployeeService{
}

5). 导入通用结果类 R
此类是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面。

所属包: com.itheima.reggie.common
/**
* 通用返回结果,服务端响应的数据最终都会封装成此对象
* @param <T>
*/
@Data
public class R<T> {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> R<T> success(T object) {
R<T> r = new R<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> R<T> error(String msg) {
R r = new R();
r.msg = msg;
r.code = 0;
return r;
}
public R<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
A. 如果业务执行结果为成功, 构建 R 对象时, 只需要调用 success 方法; 如果需要返回数据传递 object 参数, 如果无需返回, 可以直接传递 null。
B. 如果业务执行结果为失败, 构建 R 对象时, 只需要调用 error 方法, 传递错误提示信息即可。
总结
课堂作业
- 实体类和数据库中的表有什么关系?🎤
- 使用MybatisPlus有什么优势!!
- 为什么要完成12项目?
4.2 后台项目登录接口开发 ✏️
后台项目登录接口开发

处理逻辑
①. 将页面提交的密码 password 进行 md5 加密(DigestUtils)处理 , 得到加密后的字符串
②. 根据页面提交的用户名 username 查询数据库中员工数据信息
③. 如果没有查询到, 则返回登录失败结果
④. 密码比对,如果不一致, 则返回登录失败结果
⑤. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
⑥. 登录成功,将员工 id 存入 Session, 并返回登录成功结果
代码操作
技术点说明:
- 由于需求分析时, 我们看到前端发起的请求为 post 请求, 所以服务端需要使用注解 @PostMapping
- 由于前端传递的请求参数为 json 格式的数据, 这里使用
Employee 对象
接收, 但是将 json 格式数据封装到实体类中, 在形参前需要加注解@RequestBody

/**
* 员工登录
* @param request
* @param employee
* @return
*/
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request,@RequestBody Employee employee){
//1、将页面提交的密码password进行md5加密处理
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
//2、根据页面提交的用户名username查询数据库
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
//3、如果没有查询到则返回登录失败结果
if(emp == null){
return R.error("登录失败");
}
//4、密码比对,如果不一致则返回登录失败结果
if(!emp.getPassword().equals(password)){
return R.error("登录失败");
}
//5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
if(emp.getStatus() == 0){
return R.error("账号已禁用");
}
//6、登录成功,将员工id存入Session并返回登录成功结果
request.getSession().setAttribute("employee",emp.getId());
return R.success(emp);
}
代码实现完毕后, 启动项目, 访问 url: http://localhost:8080/backend/page/login/login.html , 进行登录测试。
密码错误:
用户名错误:
用户名密码正确:

总结
课堂作业
- 根据截图,完成登录代码的书写🎤
4.3 后台系统退出功能 ✏️
后台系统退出功能
在后台管理系统中,管理员或者员工,登录进入系统之后,页面跳转到后台系统首页面(backend/index.html),此时会在系统的右上角显示当前登录用户的姓名。
如果员工需要退出系统,直接点击右侧的退出按钮即可退出系统,退出系统后页面应跳转回登录页面。
1). 退出页面展示

2).前端请求分析

退出发出了一个POST请求,没有参数
代码操作
需要在 Controller 中创建对应的处理方法, 接收页面发送的 POST 请求 /employee/logout ,具体的处理逻辑:
- 清理 Session 中的用户 id
- 返回结果
/**
* 员工退出
* @param request
* @return
*/
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
功能测试
1). 代码实现完毕后, 重启服务, 访问登录界面 http://localhost:8080/backend/page/login/login.html ;
2). 登录完成之后, 进入到系统首页 backend/index.html, 点击右上角 按钮 执行退出操作, 完成后看看是否可以跳转到登录页面 , 并检查 localStorage。


注意
用户如果不登录,直接访问系统首页面,照样可以正常访问。 如:不登录,直接访问:http://localhost:8080/backend/index.html
总结
课堂作业
- 根据上述提示,完成退出的书写,并且思考注意的问题? 不登录下直接访问首页?🎤
4.4. 完善登录功能 ✏️ 🍐
完善登录功能
前面我们已经完成了后台系统的员工登录功能开发,但是目前还存在一个问题,接下来我们来说明一个这个问题, 以及如何处理。
1). 目前现状
用户如果不登录,直接访问系统首页面,照样可以正常访问。

2). 理想效果
上述这种设计并不合理,我们希望看到的效果应该 是,只有登录成功后才可以访问系统中的页面,如果没有登录, 访问系统中的任何界面都直接跳转到登录页面。

那么,具体应该怎么实现呢?
可以使用我们之前讲解过的 过滤器、拦截器来实现,在过滤器、拦截器中拦截前端发起的请求,判断用户是否已经完成登录,如果没有登录则返回提示信息,跳转到登录页面。

过滤器具体的处理逻辑如下:
A. 获取本次请求的 URI
B. 判断本次请求, 是否需要登录, 才可以访问
C. 如果不需要,则直接放行
D. 判断登录状态,如果已登录,则直接放行
E. 如果未登录, 则返回未登录结果
如果未登录,我们需要给前端返回什么样的结果呢? 这个时候, 我们可以去看看前端是如何处理的 ?

代码操作
1). 定义登录校验过滤器
自定义一个过滤器 LoginCheckFilter 并实现 Filter 接口, 在 doFilter 方法中完成校验的逻辑。 那么接下来, 我们就根据上述分析的步骤, 来完成具体的功能代码实现:

所属包: com.itheima.reggie.filter
/**
* 检查用户是否已经完成登录
* filterName:过滤器在对象容器中的名字
* urlPatterns:匹配路径 /* 匹配所有的路径
*/
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
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; //获取session中值
HttpServletResponse response= (HttpServletResponse) servletResponse; //重定向
//1、获取本次请求的URI
String requestURI = request.getRequestURI();
// {}占位符
log.info("接受到的请求路径:{}",requestURI);
//定义不需要处理的请求路径--白名单
String[] urls = new String[]{
"/employee/login", // 登陆页 直接放行
"/employee/logout",//注销页 直接放行
"/backend/**", //所有的pc端的 前端资源 放行
"/front/**" //所有的移动端 前端资源 放行
};
//2、判断本次请求是否需要处理
boolean check = check(urls, requestURI);
//3、如果不需要处理,则直接放行
if(check){
//filterChain.doFilter 放行
filterChain.doFilter(servletRequest,servletResponse);
//直接返回该方法,下面的代码不要执行了
return;
}
//4、判断登录状态,如果已登录,则直接放行
HttpSession session = request.getSession();
Object employee = session.getAttribute("employee");
//如果用户id不等于空,说明已经登陆过,直接放行
if (employee!=null){
log.info("用户已经登录,直接放行");
filterChain.doFilter(servletRequest,servletResponse);
//直接返回该方法,下面的代码不要执行了
return;
}
log.info("用户未登录");
//5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
//JSON.toJSONString 将对象 转成 JSON字符串 给前端使用 返回的数据一定是NOTLOGIN 且code 为0
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
}
/**
* 路径匹配,检查本次请求是否需要放行
* @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;
}
}
return false;
}
}
AntPathMatcher 拓展:介绍: Spring 中提供的路径匹配器 ; 通配符规则:
符号 含义 ?
匹配一个字符 *
匹配 0 个或多个字符 **
匹配 0 个或多个目录/字符
2). 开启组件扫描
需要在引导类上, 加上 Servlet 组件扫描的注解, 来扫描过滤器配置的@WebFilter
注解, 扫描上之后, 过滤器在运行时就生效了。
@Slf4j
@SpringBootApplication
@ServletComponentScan
public class ReggieApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class,args);
log.info("项目启动成功...");
}
}
@ServletComponentScan 的作用:
在 SpringBoot 项目中, 在引导类/配置类上加了该注解后, 会自动扫描项目中(当前包及其子包下)的@WebServlet , @WebFilter , @WebListener 注解, 自动注册 Servlet 的相关组件 ;
代码编写完毕之后,我们需要将工程重启一下,然后在浏览器地址栏直接输入系统管理后台首页,然后看看是否可以跳转到登录页面即可。我们也可以通过 debug 的形式来跟踪一下代码执行的过程。

对于前端的代码, 也可以进行 debug 调试。
F12 打开浏览器的调试工具, 找到我们前面提到的 request.js, 在 request.js 的响应拦截器位置打上断点。
