title: 瑞吉外卖-Day01
abbrlink: ‘0’
date: 2023-04-1 18:00:00
瑞吉外卖-Day01
课程内容
-
软件开发整体介绍
-
瑞吉外卖项目介绍
-
开发环境搭建
-
后台登录功能开发
-
后台退出功能开发
1. 软件开发整体介绍
作为一名软件开发工程师,我们需要了解在软件开发过程中的开发流程, 以及软件开发过程中涉及到的岗位角色,角色的分工、职责, 并了解软件开发中涉及到的三种软件环境。那么这一小节,我们将从 软件开发流程、角色分工、软件环境 三个方面,来整体上介绍一下软件开发。
1.1 软件开发流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LvVl7cpa-1688460704766)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031359778.png)]
1). 第1阶段: 需求分析
完成产品原型、需求规格说明书的编写。
产品原型,一般是通过网页(html)的形式展示当前的页面展示什么样的数据, 页面的布局是什么样子的,点击某个菜单,打开什么页面,点击某个按钮,出现什么效果,都可以通过产品原型看到。
需求规格说明书, 一般来说就是使用 Word 文档来描述当前项目有哪些功能,每一项功能的需求及业务流程是什么样的,都会在文档中描述。
2). 第2阶段: 设计
设计的内容包含 产品设计、UI界面设计、概要设计、详细设计、数据库设计。
在设计阶段,会出具相关的UI界面、及相关的设计文档。比如数据库设计,需要设计当前项目中涉及到哪些数据库,每一个数据库里面包含哪些表,这些表结构之间的关系是什么样的,表结构中包含哪些字段,字段类型都会在文档中描述清楚。
3). 第3阶段: 编码
编写项目代码、并完成单元测试。
作为软件开发工程师,我们主要的工作就是在该阶段, 对分配给我们的模块功能,进行编码实现。编码实现完毕后,进行单元测试,单元测试通过后再进入到下一阶段。
4). 第4阶段: 测试
在该阶段中主要由测试人员, 对部署在测试环境的项目进行功能测试, 并出具测试报告。
5). 第5阶段: 上线运维
在项目上线之前, 会由运维人员准备服务器上的软件环境安装、配置, 配置完毕后, 再将我们开发好的项目,部署在服务器上运行。
我们作为软件开发工程师, 我们主要的任务是在编码阶段, 但是在一些小的项目组当中, 也会涉及到数据库的设计、测试等方面的工作。
1.2 角色分工
学习了软件开发的流程之后, 我们还有必要了解一下在整个软件开发过程中涉及到的岗位角色,以及各个角色的职责分工。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hQ0JvDNx-1688460704767)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031359918.png)]
岗位/角色 | 职责/分工 |
---|---|
项目经理 | 对整个项目负责,任务分配、把控进度 |
产品经理 | 进行需求调研,输出需求调研文档、产品原型等 |
UI设计师 | 根据产品原型输出界面效果图 |
架构师 | 项目整体架构设计、技术选型等 |
开发工程师 | 功能代码实现 |
测试工程师 | 编写测试用例,输出测试报告 |
运维工程师 | 软件环境搭建、项目上线 |
上述我们讲解的角色分工, 是在一个项目组中比较标准的角色分工, 但是在实际的项目中, 有一些项目组由于人员配置紧张, 可能并没有专门的架构师或测试人员, 这个时候可能需要有项目经理或者程序员兼任。
1.3 软件环境
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ufgdMASK-1688460704768)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031400614.png)]
在我们日常的软件开发中,会涉及到软件开发中的三套环境, 那么这三套环境分别是: 开发环境、测试环境、生产环境。 接下来,我们分别介绍一下这三套环境的作用和特点。
1). 开发环境(development)
我们作为软件开发人员,在开发阶段使用的环境,就是开发环境,一般外部用户无法访问。
比如,我们在开发中使用的MySQL数据库和其他的一些常用软件,我们可以安装在本地, 也可以安装在一台专门的服务器中, 这些应用软件仅仅在软件开发过程中使用, 项目测试、上线时,我们不会使用这套环境了,这个环境就是开发环境。
2). 测试环境(testing)
当软件开发工程师,将项目的功能模块开发完毕,并且单元测试通过后,就需要将项目部署到测试服务器上,让测试人员对项目进行测试。那这台测试服务器就是专门给测试人员使用的环境, 也就是测试环境,用于项目测试,一般外部用户无法访问。
3). 生产环境(production)
当项目开发完毕,并且由测试人员测试通过之后,就可以上线项目,将项目部署到线上环境,并正式对外提供服务,这个线上环境也称之为生产环境。
拓展知识:
准生产环境: 对于有的公司来说,项目功能开发好, 并测试通过以后,并不是直接就上生产环境。为了保证我们开发的项目在上线之后能够完全满足要求,就需要把项目部署在真实的环境中, 测试一下是否完全符合要求啊,这时候就诞生了准生产环境,你可以把他当做生产环境的克隆体,准生产环境的服务器配置, 安装的应用软件(JDK、Tomcat、数据库、中间件 …) 的版本都一样,这种环境也称为 “仿真环境”。
ps.由于项目的性质和类型不同,有的项目可能不需要这个环境
2. 瑞吉外卖项目介绍
在开发瑞吉外卖这个项目之前,我们需要全方位的来介绍一下当前我们学习的这个项目。接下来,我们将从以下的五个方面, 来介绍瑞吉外卖这个项目。
2.1 项目介绍
本项目(瑞吉外卖)是专门为餐饮企业(餐厅、饭店)定制的一款软件产品,包括 系统管理后台 和 移动端应用 两部分。其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的分类、菜品、套餐、订单、员工等进行管理维护。移动端应用主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等。
本项目共分为3期进行开发:
阶段 | 功能实现 |
---|---|
第一期 | 主要实现基本需求,其中移动端应用通过H5实现,用户可以通过手机浏览器访问 |
第二期 | 主要针对移动端应用进行改进,使用微信小程序实现,用户使用起来更加方便 |
第三期 | 主要针对系统进行优化升级,提高系统的访问性能 |
2.2 产品原型
产品原型,就是一款产品成型之前,由产品经理绘制的一个简单的框架,就是将页面的排版布局展现出来,使产品的初步构思有一个可视化的展示。通过原型展示,可以更加直观的了解项目的需求和提供的功能。
注意事项: 产品原型主要用于展示项目的功能,并不是最终的页面效果。
在课程资料的产品原型文件夹下,提供了两份产品原型。
1). 管理端
餐饮企业内部员工使用。 主要功能有:
模块 | 描述 |
---|---|
登录/退出 | 内部员工必须登录后,才可以访问系统管理后台 |
员工管理 | 管理员可以在系统后台对员工信息进行管理,包含查询、新增、编辑、禁用等功能 |
分类管理 | 主要对当前餐厅经营的 菜品分类 或 套餐分类 进行管理维护, 包含查询、新增、修改、删除等功能 |
菜品管理 | 主要维护各个分类下的菜品信息,包含查询、新增、修改、删除、启售、停售等功能 |
套餐管理 | 主要维护当前餐厅中的套餐信息,包含查询、新增、修改、删除、启售、停售等功能 |
订单明细 | 主要维护用户在移动端下的订单信息,包含查询、取消、派送、完成,以及订单报表下载等功能 |
2). 用户端
移动端应用主要提供给消费者使用。主要功能有:
模块 | 描述 |
---|---|
登录/退出 | 在移动端, 用户也需要登录后使用APP进行点餐 |
点餐-菜单 | 在点餐界面需要展示出菜品分类/套餐分类, 并根据当前选择的分类加载其中的菜品信息, 供用户查询选择 |
点餐-购物车 | 用户选中的菜品就会加入用户的购物车, 主要包含 查询购物车、加入购物车、删除购物车、清空购物车等功能 |
订单支付 | 用户选完菜品/套餐后, 可以对购物车菜品进行结算支付, 这时就需要进行订单的支付 |
个人信息 | 在个人中心页面中会展示当前用户的基本信息, 用户可以管理收货地址, 也可以查询历史订单数据 |
2.3 技术选型
关于本项目的技术选型, 我们将会从 用户层、网关层、应用层、数据层 这几个方面进行介绍,而对于我们服务端开发工程师来说,在项目开发过程中,我们主要关注应用层及数据层技术的应用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h4qRCqXx-1688460704770)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031401261.png)]
1). 用户层
本项目中在构建系统管理后台的前端页面,我们会用到H5、Vue.js、ElementUI等技术。而在构建移动端应用时,我们会使用到微信小程序。
2). 网关层
Nginx是一个服务器,主要用来作为Http服务器,部署静态资源,访问性能高。在Nginx中还有两个比较重要的作用: 反向代理和负载均衡, 在进行项目部署时,要实现Tomcat的负载均衡,就可以通过Nginx来实现。
3). 应用层
SpringBoot: 快速构建Spring项目, 采用 “约定优于配置” 的思想, 简化Spring项目的配置开发。
Spring: 统一管理项目中的各种资源(bean), 在web开发的各层中都会用到。
SpringMVC:SpringMVC是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合,可以无缝集成。
SpringSession: 主要解决在集群环境下的Session共享问题。
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 数据库环境搭建
3.1.1 创建数据库
可以通过以下两种方式中的任意一种, 来创建项目的数据库:
1).图形界面
创建连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xax97tMS-1688460704772)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032234141.png)]
新建数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IADMQofY-1688460704773)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032235755.png)]
注意: 本项目数据库的字符串, 选择 utf8mb4
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DiLOn8MS-1688460704774)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032237124.png)]
2).命令行
3.1.2 数据库表导入
项目的数据库创建好了之后, 可以直接将 资料/数据模型/db_reggie.sql 直接导入到数据库中, 也可以通过两种方式实现:
1).图形界面
刷新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecK696Cz-1688460704776)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032340176.png)]
2).命令行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zya4Jz1-1688460704777)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031401510.png)]
注意: 通过命令导入表结构时,注意sql文件不要放在中文目录中
3.1.3 数据库表介绍
数据库表导入之后, 接下来介绍一下本项目中所涉及到的表结构:
序号 | 表名 | 说明 |
---|---|---|
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 | 订单明细表 |
上述的表结构, 我们目前先简单的结合页面原型了解一下, 大概有那些表, 每张表结构中存储什么样的数据, 有一个印象。对于具体的表结构, 以及表结构中的字段, 在讲解具体的功能开发时, 我们再详细介绍。
3.2 Maven项目搭建
3.2.1 创建maven项目
1). 在idea中创建maven project, 项目名称 reggie_take_out
2). 检查项目编码
3). 检查maven配置
4). 检查JDK版本
JDK的版本选择1.8;
视频笔记:
新建项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MuJoxdid-1688460704777)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032341650.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-er7HGaVp-1688460704778)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032341130.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5er0KHK-1688460704779)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032351122.png)]
检查
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J1I8yzDb-1688460704780)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032351441.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJ7ucQY7-1688460704781)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304032352561.png)]
3.2.2 搭建基础环境
1).在pom.xml中导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>reggie_take_out</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
</project>
2).在工程的resources目录下创建application.yml文件,并引入配置
server:
port: 8080
spring:
application:
#应用的名称,可选
name: reggie_take_out
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
mybatis-plus:
configuration:
# address_book(表名) -----> AddressBook(实体类名)
# user_name(表名)------> userName(实体类名)
#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: ASSIGN_ID #主健生成策略
3).创建包 com.itheima.reggie , 并编写启动类
package com.itheima.reggie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class ReggieApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class, args);
log.info("项目启动成功...");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aE0BToJ8-1688460704782)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304040127441.png)]
补充笔记:
安装快速生成yml和main启动类的插件JBL SpringbootAppGen
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62o7QqBi-1688460704783)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304040128218.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SezU7fcU-1688460704783)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304040128095.png)]
@Slf4j :
是lombok中提供的注解, 用来通过slf4j记录日志。
这边要注意 如果当lon,info没有起到作用的时候 可以检查lombok插件有没有安装 如果没有安装 就先安装 lombok插件
当搭建完上述的基础环境之后, 就可以通过引导类, 启动该项目。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bs2KJlD5-1688460704784)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304040128800.png)]
3.2.3 前端静态资源导入
我们作为服务端开发工程师, 我们课程学习的重心应该放在后端的业务代码上, 前端的页面我们只需要导入课程资料中的前端资源, 前端页面的代码我们只需要能看懂即可。
1). 导入静态资源
前端资源存放位置为 资料/前端资源 :
将上述两个目录中的静态资源文件, 导入到项目的resources目录下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WRNxMU2o-1688460704785)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304040152095.png)]
2). 创建配置类WebMvcConfig,设置静态资源映射
用于在Springboot项目中, 默认静态资源的存放目录为 :
"classpath:/resources/", "classpath:/static/", "classpath:/public/" ;
而在我们的项目中静态资源存放在 backend, front 目录中, 那么这个时候要想访问到静态资源, 就需要设置静态资源映射。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DI5QreW-1688460704786)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042145090.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NpVYVmiS-1688460704787)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042145042.png)]
这个方法传进来一个参数叫做registry 然后我们直接使用registry来设置我们要访问的路径 对于这此访问路径映射到哪些资源路径
所以我们就可以通过registry调用addResourceHandler();
registry.addResourceHandler();
addResourceHandler():资源处理器
代码解释:
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
classpath:指的是在resources文件下面
解释:
这段代码是用来配置 Spring Web 应用程序中资源处理器的,它的作用是将 “/backend/” 开头的所有 URL 请求映射到应用程序中的某个资源位置。
具体来说,它通过 ResourceHandlerRegistry 对象将 “/backend/**” 映射到 “classpath:/backend/” 目录下的所有资源。这意味着,当用户访问以 “/backend/” 开头的 URL 时,Spring 应用程序将查找该目录下的所有资源,并返回这些资源的 HTML 页面或其他响应。
同时,该代码还将 “classpath:/backend/” 目录下的所有资源映射到 /backend/ 目录下。这意味着,当用户访问 /backend/ 目录下的 URL 时,Spring 应用程序将查找该目录下的所有资源,并返回这些资源的 HTML 页面或其他响应。
总之,这段代码的作用是将 Spring Web 应用程序中资源处理器的配置信息添加到应用程序上下文中,以便处理以 “/backend/” 开头的所有 URL 请求。
简单来说 如果看到backend 就找resources文件文件下面的资源
代码解释
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
解释
这段代码是用来配置 Spring Web 应用程序的资源处理器,以便处理所有以 “/front/” 开头的 URL 请求。
具体来说,代码中使用了 Spring 的 ResourceHandlerRegistry 对象来配置资源处理器。registry 对象表示一个 ResourceHandlerRegistry 实例,它管理着所有资源处理器的注册。
addResourceHandler() 方法用于添加资源处理器,该方法接受一个字符串参数,表示要处理的请求路径。在这里,该方法将 /front/** 映射到 classpath:/front/ 目录下的所有资源。/** 表示匹配所有子目录和文件。
addResourceLocations() 方法用于指定资源位置,该方法也接受一个字符串参数,表示要映射的资源位置。在这里,该方法将 classpath:/front/ 目录下的所有资源映射到 /front/ 目录下。
因此,这段代码的作用是将一个资源处理器注册到 Spring 应用程序上下文中,以处理以 /front/ 开头的所有 URL 请求,并将这些请求映射到 classpath:/front/ 目录下的所有资源。
package com.itheima.reggie.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Slf4j
@Configuration //表示他是一个配置类
public class WebMvcConfig extends WebMvcConfigurationSupport {
/*
* 设置静态资源映射
* */
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始进行静态资源映射...");
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6JZHepbV-1688460704788)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042145546.png)]
3). 访问测试
http://localhost:8080/backend/index.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AgXsFI9N-1688460704789)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031402973.png)]
4. 后台系统登录功能
4.1 需求分析
1). 页面原型展示
2). 登录页面成品展示
登录页面存放目录 /resources/backend/page/login/login.html
3). 查看登录请求
通过浏览器调试工具(F12) 先打开F12
可以发现点击登录按钮时,页面会发送请求(请求地址为http://localhost:8080/employee/login)并提交参数 username和password, 请求参数为json格式数据 {“username”:“admin”,“password”:“123456”}。
第一步:
输入地址:http://localhost:8080/backend/page/login/login.html
第二步 打开F12
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hNtw7qN7-1688460704790)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042206495.png)]
第三步 点击登录
账号:admin
密码:123456
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iVH3MrUa-1688460704791)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042205238.png)]
第四步
页面会发送请求 并提交参数 username和password, 请求参数为json格式数据 {“username”:“admin”,“password”:“123456”}。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XdpvztTx-1688460704791)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042206583.png)]
请求地址为http://localhost:8080/employee/login
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Af4lIJb-1688460704793)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031403789.png)]
此时报404,是因为我们的后台系统还没有响应此请求的处理器,所以我们需要创建相关类来处理登录请求 ;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DxZ9zguT-1688460704793)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304042205855.png)]
4). 数据模型(employee表)
5). 前端页面分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-77fvFx1A-1688460704794)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031403285.png)]
当点击 “登录” 按钮, 会触发Vue中定义的 handleLogin 方法:
<script>
new Vue({
el: '#login-app',
data() {
return {
loginForm:{
username: 'admin',
password: '123456'
},
loading: false
}
},
computed: {
loginRules() {
const validateUsername = (rule, value, callback) => {
if (value.length < 1 ) {
callback(new Error('请输入用户名'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('密码必须在6位以上'))
} else {
callback()
}
}
return {
'username': [{ 'validator': validateUsername, 'trigger': 'blur' }],
'password': [{ 'validator': validatePassword, 'trigger': 'blur' }]
}
}
},
created() {
},
methods: {
async handleLogin() {
this.$refs.loginForm.validate(async (valid) => {
if (valid) {
this.loading = true
let res = await loginApi(this.loginForm)
if (String(res.code) === '1') {
localStorage.setItem('userInfo',JSON.stringify(res.data))
window.location.href= '/backend/index.html'
} else {
this.$message.error(res.msg)
this.loading = false
}
}
})
}
}
})
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8i56Qna6-1688460704796)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050046133.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5qeIrozs-1688460704796)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050050022.png)]
到达handleLogin()方法之后 先找到表单 然后调用validate()进行表单检验 主要检验我们的用户名和密码是否为空,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KFbHLc9Y-1688460704797)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050047556.png)]
如果检验通过后 我们就把loading改成true
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ht1YinPR-1688460704797)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050054811.png)]
这个时候
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HGeLKFkf-1688460704799)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050054868.png)]
【this.loading = true】 当把loading改成true 时 就一直属于登录中 如下图所示
重点在于如果检验通过之后 就会调用loginApi() 这个方法关联在login.js里面 我们鼠标放在loginApi()上 按住Ctrl+左健 就可以跳转到如下页面
通过axios向我们的后端发送请求 我们的请求是’url’: ‘/employee/login’, 而且是一个post请求
通过F12我们也可以看到
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XfALlU6X-1688460704801)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050043140.png)]
function loginApi(data) {
return $axios({
'url': '/employee/login',
'method': 'post',
data
})
}
function logoutApi(){
return $axios({
'url': '/employee/logout',
'method': 'post',
})
}
methods: {
async handleLogin() {
this.$refs.loginForm.validate(async (valid) => {
if (valid) {
this.loading = true
let res = await loginApi(this.loginForm)
if (String(res.code) === '1') { //1表示登录成功
localStorage.setItem('userInfo',JSON.stringify(res.data))
window.location.href= '/backend/index.html'
} else {
this.$message.error(res.msg)
this.loading = false
}
}
})
}
}
通过axios向我们的后端服务来发送请求 请求为’/employee/login’ 是一个’post’, 并且把数据 data带过来 这个数据就是我们的loginForm loginForm里面话的就是我们的用户名和密码 这个时候我们的请求就发出去了 请求发出去之后 如果我们的服务端接收到请求 就要在服务端进行相对应的处理 处理完之后 在给前端页面 一个响应的结果 而这个就是通过res来进行接收( let res = await loginApi(this.loginForm)) 接收好后 我们在进行判断 如果 String(res.code) === ‘1’ 如果等于1 就表示登录成功 当登录成功之后 将数据转换成JSON 然后保存在浏览器中(存储在客户端的 localStorage 中)(通过k-v的方式转成JSDN)localStorage.setItem(‘userInfo’,JSON.stringify(res.data)) 然后在做页面跳转 跳转的页面为’/backend/index.html’【 window.location.href= ‘/backend/index.html’】 跳转到如下页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rtAZT41X-1688460704801)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050043556.png)]
如果登录失败 也就是说String(res.code) != ‘1’ 会提供一个错误信息 this.$message.error(res.msg)
然后把loading 改成 【falseloading = false】 这个时候我们的前端页面又会变成登录按钮 如下图所示
这个时候 我们就可以在重新登录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTRXxwWj-1688460704803)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304050043551.png)]
在上述的前端代码中, 大家可以看到, 发送登录的异步请求之后, 获取到响应结果, 在响应结果中至少包含三个属性: code、data、msg 。
从响应返回结果里面获取到的值 在响应结果中至少包含三个属性: code、data、msg 。
也就是说 要求我们服务端处理完之后 数据里面应该要有code、data、msg 并且是JSON的形式 这样前端页面才能获取到
由前端代码,我们也可以看到,在用户登录成功之后,服务端会返回用户信息,而前端是将这些用户信息,存储在客户端的 localStorage 中了。
localStorage.setItem('userInfo',JSON.stringify(res.data))
4.2 代码开发
4.2.1 基础准备工作
在进行登录功能的代码实现之前, 首先在我们的工程下创建包结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O0kgoUFC-1688460704822)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031403158.png)]
1). 创建实体类Employee
该实体类主要用于和员工表 employee 进行映射。 该实体类, 也可以直接从资料( 资料/实体类 )中拷贝工程中。
所属包: com.itheima.reggie.entity
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@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;
}
注意:
实体类和表的名称不同 要开启驼峰命名法
实体类
private String idNumber; //驼峰命名法 ---> 映射的字段名为 id_number
private LocalDateTime createTime;
private LocalDateTime updateTime;
数据库中的表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tnwobFgt-1688460704823)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304061338379.png)]
这个时候要开启驼峰命名
2). 定义Mapper接口
在MybatisPlus中, 自定义的Mapper接口, 需要继承自 BaseMapper。
所属包: com.itheima.reggie.mapper
package com.itheima.reggie.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7YbG0i5-1688460704824)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304061358550.png)]
注解理解
java中的Mapper接口中@Mappe注解的理解
在Java中,Mapper接口主要用于MyBatis框架,用来定义与数据库交互的方法。@Mapper注解在MyBatis框架中用于标识这个接口为一个Mapper接口,以便MyBatis在启动时能够自动扫描并加载这个接口。在MyBatis中,我们使用这些Mapper接口与数据库进行交互,以实现SQL查询、插入、更新和删除等操作。
@Mapper注解的作用如下:
- 标识接口:使用@Mapper注解可以明确地表示这个接口是一个MyBatis的Mapper接口,使得其他开发者更容易理解这个接口的用途。
- 自动扫描与加载:当你在应用中使用MyBatis时,你需要让MyBatis知道哪些接口是Mapper接口。通过在接口上添加@Mapper注解,MyBatis在启动时会自动扫描并加载这些接口。
- 生成Mapper代理对象:当MyBatis加载了@Mapper注解的接口后,它会为这些接口生成代理对象。这些代理对象负责将接口方法与相应的SQL语句关联起来,从而实现数据库操作。
- 简化配置:在MyBatis的早期版本中,你需要在XML配置文件中显式地声明Mapper接口与对应的SQL映射文件。但是通过使用@Mapper注解,你可以省略这些显式声明,使配置更简洁。
需要注意的是,使用@Mapper注解的Mapper接口通常需要与一个XML映射文件关联。这个映射文件包含了接口方法与相应的SQL语句之间的映射关系。映射文件的命名和位置要与Mapper接口保持一致,以便MyBatis能够正确地找到并加载它们。
此外,还可以使用@MapperScan注解来扫描特定包路径下的所有@Mapper注解的接口,这样就不需要在每个接口上都添加@Mapper注解。
java中的MyBatisPlus 中的Mapper接口中@Mappe注解的理解
MyBatisPlus(简称MP)是一个在MyBatis基础上进行扩展的框架,提供了许多方便实用的功能,如通用Mapper、通用Service等。在MyBatisPlus中,Mapper接口依然是用于定义与数据库交互的方法,但在MyBatisPlus中通常会继承自BaseMapper接口,从而自动获得一些通用的数据库操作方法。
在MyBatisPlus中,@Mapper注解的作用与MyBatis中的@Mapper注解相似,但有一些区别:
- 在MyBatisPlus中,使用@Mapper注解标识的接口通常继承自BaseMapper接口。BaseMapper接口为用户提供了一些通用的CRUD操作方法,如selectById、insert、update等。这些方法可以满足大部分简单的数据库操作需求,从而减少了开发者需要手动编写的SQL语句数量。
- MyBatisPlus中的@Mapper注解仍然负责标识接口、自动扫描与加载、生成Mapper代理对象等功能。在某些情况下,你可能需要自定义Mapper接口中的方法,此时你可以在接口中添加自定义方法,并在对应的XML映射文件中编写相应的SQL语句。
- MyBatisPlus还提供了一些额外的注解,如@Table、@TableId等,这些注解可以用于实体类的字段与数据库表中的列之间的映射关系。通过这些注解,MyBatisPlus可以更智能地处理数据库操作,从而减少了手动编写SQL语句的需求。
- 与MyBatis中的@MapperScan注解类似,MyBatisPlus中也提供了@MapperScan注解。你可以使用@MapperScan注解来指定需要扫描的包路径,从而自动加载该包路径下的所有@Mapper注解的接口。
综上所述,MyBatisPlus中的@Mapper注解仍然用于标识Mapper接口,但与MyBatis中的@Mapper注解相比,MyBatisPlus中的@Mapper注解更侧重于与BaseMapper接口的结合,以实现更高效的数据库操作。
3).Service接口
本项目的Service接口, 在定义时需要继承自MybatisPlus提供的Service层接口 IService, 这样就可以直接调用 父接口的方法直接执行业务操作, 简化业务层代码实现。
所属包: com.itheima.reggie.service
package com.itheima.reggie.Service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Employee;
public interface EmployeeService extends IService<Employee> {
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jR6DA3Kc-1688460704825)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304081430659.png)]
4). Service实现类
所属包: com.itheima.reggie.service.impl
package com.itheima.reggie.Service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.Mapper.EmployeeMapper;
import com.itheima.reggie.Service.EmployeeService;
import com.itheima.reggie.entity.Employee;
import org.springframework.stereotype.Service;
@Service //交给spring管理
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}
5). Controller基础代码
所属包: com.itheima.reggie.controller
package com.itheima.reggie.controller;
import com.itheima.reggie.Service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFlCNAhp-1688460704827)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304062120161.png)]
解释注解
@RestController注解的使用
@RestController
注解是 Spring 框架中用于标记 RESTful API 控制器的重要注解之一。这个注解可以让我们将一个普通的 Java 类标记为 Spring MVC 的控制器,以便 Spring 能够自动注入一个 RequestMappingHandlerMapping
对象,将请求 URL 和控制器方法之间的映射关系弄清楚。
具体来说,使用 @RestController
注解标记的控制器类将会被 Spring MVC 自动注入一个 RequestMappingHandlerMapping
对象,这个对象可以帮助我们将请求 URL 和控制器方法映射起来,使得我们能够快速响应不同的请求方式。
举个例子,假设我们有一个 PersonController
类,它使用 @RestController
注解标记。如果我们向这个控制器类发送一个 GET 请求,Spring 将会把这个请求映射到 person/{id}
方法上,而这个方法是 @GetMapping
注解指定的。如果我们发送一个 POST 请求,Spring 将会把这个请求映射到 person/{id}
方法上,而这个方法是 @PostMapping
注解指定的。
总之,@RestController
注解可以帮助我们方便地构建 Spring MVC 中的 RESTful API,因为它可以自动注入一个 RequestMappingHandlerMapping
对象,从而简化控制器类的编写,同时也提供了一些预定义的映射关系,使得我们能够更加快速地响应不同的请求方式。
代码解释
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
}
这段代码定义了一个名为 EmployeeController 的 Java 类,它具有以下注解:
- @Slf4j:这是一个注解,用于指示 Java 类应该使用 slf4j 作为日志实现。slf4j 是一种流行的日志库,它支持多种日志输出方式,例如日志输出到控制台、日志输出到文件、日志输出到 Splunk 等等。
- @RestController:这是一个注解,用于指示这个类是一个 RestController,它代表了一种 Web 应用的基本组件,用于处理 HTTP 请求。
- @RequestMapping(“/employee”):这是一个注解,用于指示这个类处理以/employee 开头的 HTTP 请求。@RequestMapping 注解可以用于处理 URL 映射,将请求映射到 Java 类中的具体方法上。
接下来,我们来解释 EmployeeController 类中的一些成员变量和方法:
- @Autowired:这是一个注解,用于指示 Spring 容器应该自动注入一个依赖。在这个例子中,employeeService 是一个依赖,它由 Spring 容器注入。
- private EmployeeService employeeService:这是一个私有成员变量,它表示一个 EmployeeService 对象。EmployeeService 是一个接口,它定义了处理 Employee 对象的业务逻辑。在这个例子中,我们使用 Spring 的注入功能,将一个实现了 EmployeeService 接口的实现注入到 EmployeeController 中。
最后,EmployeeController 类中有一个@RequestMapping(“/employee”) 注解修饰的方法,它代表处理以/employee 开头的 HTTP 请求的方法。在这个例子中,这个方法应该处理以/employee 开头的 HTTP 请求,并将请求参数传递给 EmployeeService 接口的相应方法。
这个代码片段是一个简单的 EmployeeController 类,它具有以下功能:
- 处理 HTTP 请求:这个 EmployeeController 类被设计为处理 HTTP 请求的基本组件。我们可以看到它被注解为 @RestController,这意味着它是一个 Restful 控制器。它还被注解为 @RequestMapping(“/employee”),这意味着它将处理以 /employee 开头的 HTTP 请求。
- 自动注入依赖:EmployeeController 类中有一个私有成员变量 employeeService,它表示一个 EmployeeService 对象。这个对象由 Spring 容器自动注入。Spring 是一个流行的 Java 应用程序框架,它提供了一种简单、灵活的方式来管理应用程序中的依赖关系。
- 处理 HTTP 请求:EmployeeController 类中有一个处理 HTTP 请求的方法 @RequestMapping(“/employee”)。这个方法将被 Spring 容器自动调用,以处理以 /employee 开头的 HTTP 请求。在这个方法中,我们可以使用 @Autowired 注解来注入 EmployeeService 对象。
- 实现业务逻辑:EmployeeService 接口定义了处理 Employee 对象的业务逻辑。在这个例子中,我们将使用一个实现了 EmployeeService 接口的实现来处理 HTTP 请求。这个实现将通过 Spring 的注入功能被注入到 EmployeeController 类中。
总之,这个代码片段是一个简单的 EmployeeController 类,它使用了 Spring 的注入功能以及 slf4j 日志库来处理 HTTP 请求。它处理以 /employee 开头的 HTTP 请求,并将请求参数传递给 EmployeeService 接口的相应方法。
6). 导入通用结果类R
此类是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面。
后面我们要写很多的controller controller也有很多方法 这些方法全都是响应我们客户端页面发过来的一些请求 然后controller处理完成之后 要给页面一个结果 而这个结果封装的话都统一把所有的返回结果全都封装成一个R对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MFGvHVxr-1688460704828)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304062143501.png)]
所属包: com.itheima.reggie.common
package com.itheima.reggie.common;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/*通用返回结果,服务端响应的数据最终都会封装成此对象*/
@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 方法, 传递错误提示信息即可。
4.2.2 登录逻辑分析
处理逻辑如下:
①. 将页面提交的密码password进行md5加密处理, 得到加密后的字符串
②. 根据页面提交的用户名username查询数据库中员工数据信息
③. 如果没有查询到, 则返回登录失败结果
④. 密码比对,如果不一致, 则返回登录失败结果
⑤. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
⑥. 登录成功,将员工id存入Session, 并返回登录成功结果
4.2.3 代码实现
技术点说明:
A. 由于需求分析时, 我们看到前端发起的请求为post请求, 所以服务端需要使用注解 @PostMapping
B. 由于前端传递的请求参数为json格式的数据, 这里使用Employee对象接收, 但是将json格式数据封装到实体类中, 在形参前需要加注解@RequestBody
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zKs9nEE-1688460704830)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304072158631.png)]
package com.itheima.reggie.controller;
import com.itheima.reggie.Service.EmployeeService;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
/*员工登录*/
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
return null;
}
}
代码解释:
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
国内镜像解释
这行代码创建了一个 LambdaQueryWrapper 对象 queryWrapper。LambdaQueryWrapper 是一个用于执行 Lambda 表达式的查询 wrapper,它支持多个查询条件,并且可以自动执行聚合操作。
queryWrapper 的构造函数需要一个泛型参数 Employee,表示要查询的实体类型。它首先创建一个空的 LambdaQueryWrapper 对象,然后使用 new 关键字创建 LambdaQueryWrapper 对象。这个对象将使用 Employee 类型来解析查询条件。
在 LambdaQueryWrapper 中,查询条件可以使用 Lambda 表达式来实现。在这种情况下,查询条件是一个 Employee 类型的变量,可以使用 Lambda 表达式来指定它。由于 LambdaQueryWrapper 支持多个查询条件,因此可以使用逗号分隔多个条件。例如,以下代码将返回所有名字包含 “Smith” 的 Employee 对象:
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq("name", "Smith");
List<Employee> employees = employeeDao.query(queryWrapper);
其中,eq() 方法用于指定查询条件,这里使用了 “name” 和 “Smith” 两个查询条件,并且使用了逗号来分隔它们。employeeDao 是一个 EmployeeDao 的实现类,它实现了查询操作。在这个例子中,查询操作将使用 LambdaQueryWrapper 对象来执行。
gpt4解释
这行代码是在使用 Java 编程语言中的 MyBatis-Plus 查询构建器来创建一个查询条件封装对象。让我们一步步地分解这个代码行,以便更好地理解它:
- LambdaQueryWrapper:这是 MyBatis-Plus 提供的一个类,用于构建基于 Lambda 表达式的查询条件。这使得编写查询条件更加简洁和便捷。
- Employee:这是一个 Java 类,通常用于表示数据库中的一个表。在这个例子中,它表示一个员工表。LambdaQueryWrapper 类使用泛型来确定它将操作哪个实体类,以便正确地映射表字段和 Java 类属性。在这个例子中,泛型参数是 Employee,表示这个查询构建器将用于构建针对 Employee 表的查询条件。
- queryWrapper:这是一个变量名,表示我们创建的 LambdaQueryWrapper 实例。我们将用这个变量来操作查询条件。
- new LambdaQueryWrapper<>():这是使用 Java 的 new 关键字创建一个新的 LambdaQueryWrapper 实例。注意,尖括号(<>)内没有指定泛型类型。这是因为从 Java 7 开始,你可以使用所谓的 “钻石操作符” 来省略尖括号内的泛型类型。编译器会自动推断出正确的类型(在这个例子中,是 Employee)。
总的来说,这行代码创建了一个新的 LambdaQueryWrapper 实例,用于构建针对 Employee 类(或者说,针对员工表)的查询条件。你可以通过在 queryWrapper 实例上调用各种方法来添加查询条件,例如 equalTo,like 等。最后,将 queryWrapper 实例传递给 MyBatis-Plus 提供的通用 Mapper 方法,例如 selectList,来执行查询并获取结果。
queryWrapper.eq(Employee::getUsername,employee.getUsername());
国内镜像
这段代码使用 QueryWrapper 类来创建查询条件,其中:
queryWrapper
是 QueryWrapper 类的实例,它是一个用于编写 SQL 查询条件的自定义包装类。eq
方法用于编写等于条件,其中第一个参数是比较对象,第二个参数是要比较的值。在这个例子中,比较对象是 Employee 类的getUsername
方法,它将返回 employee 对象的用户名。Employee::getUsername
是一个静态方法,它调用 Employee 类的getUsername
方法并将返回值存储在eq
方法的第二个参数中。这种方式可以使用类名和方法名来表示相等比较。employee.getUsername()
是获取 employee 对象的用户名的方法调用,它将存储在eq
方法的第一个参数中,用于构建查询条件。
因此,这段代码将创建一个查询条件,该条件将返回 employee 对象中具有相同用户名的行。这个查询条件将以 WHERE 子句的形式添加到 SQL 查询中,以便筛选 employee 表中的数据。
gpt4
这行代码是Java中的一段,它使用了一个名为queryWrapper的对象来构建一个查询条件。这段代码涉及到Lambda表达式和方法引用,可能在处理数据库查询时被用作过滤条件。
首先,让我们分解这个代码行:
queryWrapper
是一个对象,它的类型可能是一个泛型类,如QueryWrapper<Employee>
。这个类通常在MyBatis-Plus等ORM框架中用于构建查询条件。.eq()
是queryWrapper
对象的一个方法,用于添加一个"等于"条件。这个方法需要两个参数:一个表示数据库字段的表达式,另一个表示字段值。Employee::getUsername
是一个方法引用,它指向Employee
类中的getUsername
方法。这里使用了Java的双冒号操作符(::),表示我们引用了该方法,而不是调用它。在这种情况下,它用于表示我们希望在数据库中查询的字段(即username
)。employee.getUsername()
是一个方法调用,它获取了employee
对象的username
属性值。这个值将被用作过滤条件,即我们希望从数据库中查找具有相同username
值的记录。
综合起来,这行代码表示:在查询时,将 Employee
表中的 username
字段与给定 employee
对象的 username
属性值进行比较,查找具有相同 username
值的记录。
Employee emp = employeeService.getOne(queryWrapper);
这行代码 Employee emp = employeeService.getOne(queryWrapper);
涉及到两个对象:Employee 和 employeeService。
首先,Employee
是一个类,它表示一个 employee 对象。它是一个对象,因为它表示一个具体的数据实体。它可能包含用户名、密码、部门名称等等信息。
employeeService
是一个对象,它表示一个服务层对象,它负责处理与数据库之间的通信。它可能包含一个查询方法 getOne
,该方法接收一个 QueryWrapper 对象作为参数,该对象描述了查询条件。
查询方法是服务层对象中的常见方法之一,用于根据特定条件检索数据。在这个例子中,getOne
方法接收一个 QueryWrapper 对象作为参数,该对象描述了查询条件。QueryWrapper 对象是一个自定义的包装类,用于编写 SQL 查询条件。它提供了一些方法,例如 eq
用于编写等于条件,ne
用于编写不等于条件,等等。
getOne
方法返回一个 Employee 对象,如果查询条件得到满足,就会返回该 Employee 对象。如果查询条件没有得到满足,则返回一个空对象。
因此,Employee emp = employeeService.getOne(queryWrapper);
表示要使用 getOne
方法检索一个满足查询条件的 Employee 对象,并将其存储在 emp
变量中。queryWrapper
变量是一个 QueryWrapper 对象的实例,它描述了查询条件。
git3.5
这段代码是在Java中使用了一种叫做对象导向编程(Object-Oriented Programming,简称OOP)的技术,用来创建一个名为“emp”的Employee类型的对象。
更具体地说,这段代码中:
- Employee是一个Java类,表示一个员工对象;
- emp是一个变量名,可以被用来引用这个员工对象;
- employeeService是一个服务类(Service),其中的getOne方法会根据传入的queryWrapper参数返回一个符合条件的Employee对象;
- queryWrapper是一个查询包装器(QueryWrapper)对象,用来封装查询条件。
简而言之,这段代码的作用是从一个服务类中获取符合某个条件的员工对象,并将其存储在一个变量中,以便于在后续的代码中使用。
//2、根据页面提交的用户名username查询数据库(MP的内容)
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
代码解释
这段代码使用了 Spring 框架中的 LambdaQueryWrapper 和 EmployeeService 两个类。LambdaQueryWrapper 是 Spring 提供的查询包装器类,可以将 Lambda 表达式包装在 Query 对象中,从而方便地实现查询操作。
具体解释如下:
- 创建 LambdaQueryWrapper 对象。LambdaQueryWrapper 是一个查询包装器类,它允许在查询语句中使用 Lambda 表达式。这里使用了 new LambdaQueryWrapper<>(); 创建了一个 LambdaQueryWrapper 对象。
- 使用 LambdaQueryWrapper 对象编写查询语句。在查询语句中,使用了 eq() 方法来指定查询条件,其中 Employee::getUsername 是一个 Lambda 表达式,它表示获取 Employee 类的 getUsername() 方法返回的值。这里将 employee.getUsername() 的值作为查询条件。
- 使用 employeeService.getOne() 方法获取 Employee 对象。employeeService 是一个 EmployeeService 接口,它定义了获取 Employee 对象的方法 getOne()。这里使用了 getOne() 方法来获取 Employee 对象。
总的来说,这段代码的作用是根据页面提交的用户名 username 查询数据库中的 Employee 对象,并将查询结果返回。其中 LambdaQueryWrapper 和 EmployeeService 是两个重要的类。
详细解释
我可以详细解释一下这段代码的含义和实现过程。
首先,这段代码涉及到三个对象:LambdaQueryWrapper、EmployeeService 和 Employee。LambdaQueryWrapper 是 Spring 框架中的查询包装器类,它允许在查询语句中使用 Lambda 表达式。EmployeeService 是一个 EmployeeService 接口,它定义了获取 Employee 对象的方法 getOne()。Employee 是数据库中的 Employee 对象。
这段代码的主要目的是根据页面提交的用户名 username 查询数据库中的 Employee 对象,并将查询结果返回。为了实现这个目的,代码中做了以下几件事情:
- 创建 LambdaQueryWrapper 对象。LambdaQueryWrapper 是一个查询包装器类,它允许在查询语句中使用 Lambda 表达式。这里使用了 new LambdaQueryWrapper<>(); 创建了一个 LambdaQueryWrapper 对象。
- 使用 LambdaQueryWrapper 对象编写查询语句。在查询语句中,使用了 eq() 方法来指定查询条件。eq() 方法用于指定查询条件,它可以接受两个参数:一个字符串类型的参数,表示查询条件;另一个是 Lambda 表达式,表示查询条件的逻辑运算符。在这里,使用了 Employee::getUsername 表示获取 Employee 类的 getUsername() 方法返回的值,它将作为查询条件。
- 使用 employeeService.getOne() 方法获取 Employee 对象。employeeService 是一个 EmployeeService 接口,它定义了获取 Employee 对象的方法 getOne()。这里使用了 getOne() 方法来获取 Employee 对象。
具体来说,代码的执行过程如下:
- 用户在页面上提交了一个用户名 username。
- 页面跳转到登录页面,登录成功后返回主界面。
- 在主界面中,调用了 employeeService.getOne() 方法。
- employeeService.getOne() 方法获取了一个 LambdaQueryWrapper 对象。
- LambdaQueryWrapper 对象编写了查询语句,其中使用了 Employee::getUsername 表示获取 Employee 类的 getUsername() 方法返回的值,它将作为查询条件。
- LambdaQueryWrapper 对象将查询条件包装在一个 Query 对象中,并返回给调用者。
- 调用者使用 employeeService.getOne() 方法获取 Employee 对象,并将查询结果返回给调用者。
总的来说,这段代码实现了根据页面提交的用户名 username 查询数据库中的 Employee 对象,并将查询结果返回给调用者的功能。
为什么调用getOne方法 原因分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VkBwKodF-1688460704830)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091351661.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uy9lw01i-1688460704831)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091352400.png)]
//为什么调用getOne方法 加了唯一的约束
package com.itheima.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.reggie.Service.EmployeeService;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
/*员工登录*/
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
//1、将页面提交的密码password进行md5加密处理
//密码封装到employee对象里面去了 所以我们直接通过 employee.getPassword() 就可以获取到密码;
String password = employee.getPassword();
//用工具类进行md5加密 加密完重新赋值给 password
password = DigestUtils.md5DigestAsHex(password.getBytes());
//2、根据页面提交的用户名username查询数据库(MP的内容)
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并返回登录成功结果
//setAttribute() :保存数据
request.getSession().setAttribute("employee",emp.getId());
return R.success(emp);
}
}
注:LambdaQueryWrapper理解
MyBatisPlus官网:MyBatis-Plus (baomidou.com)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMVBCNcc-1688460704832)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091359325.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RpP5jtji-1688460704832)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091400361.png)]
条件构造器:https://baomidou.com/pages/10c804/
//2、根据页面提交的用户名username查询数据库(MP的内容)
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
其中Employee::getUsername的意思就相当于:
// 1.1 实例化一个Employee对象
// Employee employee = new employee;
// 1.2 调用对象Employee的get方法,这里调用的是getUsername:
// employee.getUsername();
// 2.eq方法相当于赋值“=”
根据员工用户名提交上来的名字去数据库里面去查找
QueryWrapper其他方法参考下图: 以下例子来自官网
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7J9Meyz-1688460704833)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091404511.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bgy5aUx4-1688460704834)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091405519.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ug3ccY9z-1688460704835)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091407776.png)]
其他的更多请查看官网
4.3 功能测试
代码实现完毕后, 启动项目, 访问url: http://localhost:8080/backend/page/login/login.html , 进行登录测试。
在测试过程中, 可以通过debug断点调试的方式来跟踪程序的执行过程,并且可以查看程序运行时各个对象的具体赋值情况。而且需要注意, 在测试过程中,需要将所有的情况都覆盖到。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6QN8opzW-1688460704837)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091411033.png)]
用Dug的方式启动
F12 刷新
我们输入一个错误的密码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fIstZKzm-1688460704838)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091706913.png)]
然后我们来看一下数据的封装有没有问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wXGvK5lx-1688460704838)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091706462.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IxIoeLpa-1688460704839)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091706020.png)]
一直F8到这里面
放行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kMPepmsh-1688460704845)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091706509.png)]
1). 问题说明
当我们在进行debug端点调试时, 前端可能会出现如下问题: 前端页面的控制台报出错误-超时;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wQJDyEy0-1688460704846)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091422766.png)]
2). 解决方案
前端进行异步请求时, 默认超时10000ms , 可以将该值调大一些。
(function (win) {
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: '/',
// 超时
timeout: 10000
})
现在我们把他的时间增长二个0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hW6mrp1x-1688460704847)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091453146.png)]
重新启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5entO8n7-1688460704847)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091459338.png)]
重新Debug
刷新重新登录
F8调试后
放行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xuNgIPAk-1688460704849)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091503043.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0W3HaZ1-1688460704851)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091741918.png)]
如果跳转不过来 就清除一下浏览器的缓存
由于修改了JS文件,需要手动清理一下浏览器缓存,避免缓存影响,JS不能及时生效。
因为我们的密码是正确 因为前端给了一个默认值 所以我们刷新后 就又重新默认密码是123456
这个时候我们就登录成功了
这时候我们可以来看一下我们的登录页面
methods: {
async handleLogin() {
this.$refs.loginForm.validate(async (valid) => {
if (valid) {
this.loading = true
let res = await loginApi(this.loginForm)
if (String(res.code) === '1') {
localStorage.setItem('userInfo',JSON.stringify(res.data))
window.location.href= '/backend/index.html'
} else {
this.$message.error(res.msg)
this.loading = false
}
}
})
}
登录成功之后 调用了localStorage.setItem(‘userInfo’,JSON.stringify(res.data)) 然后把我们返回来的数据data给转换成JSON 然后写到浏览器里面 具体存放的位置如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVLLjpDY-1688460704852)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091518537.png)]
如果登录失败呢
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BX4Kvg7s-1688460704852)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091518212.png)]
放行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chIXd9I1-1688460704853)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091518673.png)]
如果密码错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOcWccnY-1688460704854)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091518442.png)]
手动修改数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UuRW6Qdl-1688460704854)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091518427.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GCGqvlOA-1688460704855)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091518316.png)]
注意:如果修改完成时间 还是超时时 就清除浏览器的缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-54K7RSDk-1688460704855)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091517331.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ldHQY6Ep-1688460704856)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091517131.png)]
5. 后台系统退出功能
5.1 需求分析
在后台管理系统中,管理员或者员工,登录进入系统之后,页面跳转到后台系统首页面(backend/index.html),此时会在系统的右上角显示当前登录用户的姓名。
如果员工需要退出系统,直接点击右侧的退出按钮即可退出系统,退出系统后页面应跳转回登录页面。
1). 退出页面展示
确认先来分析一下管理员三个字 是动态显示的还是固定写s的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CB65M7xK-1688460704857)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091741079.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OHlKBho6-1688460704857)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091741784.png)]
是一个动态展示的
userInfo从哪里来呢 他其实是从我们的数据模型里面去拿
在我们VUE对象创建的时候 应该是准备了userInfo这样的一个数据 他应该从created()拿的 当VUE对象创建的时候 就会调用created()方法 userInfo是从 const userInfo = window.localStorage.getItem(‘userInfo’)来的 把我们存的userInfo取出来 如果不等于空 就在数据模型里面添加了userInfo 所以拿出来之后 就可以在82行那里动态展示
created() {
const userInfo = window.localStorage.getItem('userInfo')
if (userInfo) {
this.userInfo = JSON.parse(userInfo)
}
this.closeLoading()
},
我们也可以F12 来查看我产页面上的一些相当的信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BiO743Fj-1688460704858)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091531196.png)]
然后 我们在讲一下退出 84行 我们一点击退出 他会报404
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kkzWzQBH-1688460704858)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091605220.png)]
他其实是发了一个请求
那为什么 我们一点击退出按钮 会发出请求呢
接下来 我们来研究一下前端代码84行<img src=“images/icons/btn_close@2x.png” class=“outLogin” alt=“退出” @click=“logout” /> 给这个图片加了一个@click事件 也就是一个单击事件 当我们点击退出的时候 他会执行 一个叫做logou的方法 而这个方法在181行定义了
logout() {
logoutApi().then((res)=>{
if(res.code === 1){
localStorage.removeItem('userInfo')
window.location.href = '/backend/page/login/login.html'
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kqgGo3wc-1688460704860)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091605604.png)]
logout会调用 logoutApi()方法 logoutApi()这个方法在我们的Js文件里面
function loginApi(data) {
return $axios({
'url': '/employee/login',
'method': 'post',
data
})
}
function logoutApi(){
return $axios({
'url': '/employee/logout',
'method': 'post',
})
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKMbU7Hx-1688460704860)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091605557.png)]
2).前端页面分析
点击 将会调用一个js方法logout, 在logout的方法中执行如下逻辑:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CimyUwMr-1688460704861)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031404669.png)]
A. 发起post请求, 调用服务端接口 /employee/logout 执行退出操作 ;
B. 删除客户端 localStorage 中存储的用户登录信息, 跳转至登录页面 ;
5.2 代码实现
需要在Controller中创建对应的处理方法, 接收页面发送的POST请求 /employee/logout ,具体的处理逻辑:
A. 清理Session中的用户id
B. 返回结果
/**
* 员工退出
* @param request
* @return
*/
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
代码解释
gpt官网
这段代码是一个 Java Spring Boot Web 应用程序的 Controller 类中的一个方法,方法名为 logout
,使用 @PostMapping
注解标记了一个 POST 请求处理方法。
该方法接收一个 HttpServletRequest
对象作为参数,它代表当前的 HTTP 请求对象。该方法的主要功能是清除当前登录员工的会话信息,从而实现员工退出登录的功能。具体来说,该方法通过 request.getSession().removeAttribute("employee")
代码行,删除了名为 "employee"
的 Session 属性,该属性存储了当前登录员工的 ID。当该 Session 属性不存在时,该方法不会有任何影响。最后,该方法返回了一个 R
对象,表示退出操作成功,并带有一条退出成功的消息字符串。
详细解释
/**
* 员工退出
* @param request
* @return
*/
这部分是一个注释块,它描述了该方法的作用和参数。注释以 /**
开头,是 JavaDoc 风格的注释,可以被 Java 编译器和开发工具解析。在这个注释块中,该方法被描述为“员工退出”,并且接收一个 HttpServletRequest
对象作为参数。
@PostMapping("/logout")
这是一个 Spring MVC 的注解 @PostMapping
,它表示该方法将处理 HTTP POST 请求,并且请求的 URL 路径是 /logout
。当 Web 应用程序接收到该 URL 路径的 POST 请求时,该方法将被调用。
public R<String> logout(HttpServletRequest request){
这是该方法的签名。它定义了方法名、参数和返回值类型。该方法的返回类型是 R<String>
,表示一个通用的响应对象,它包含一个字符串类型的数据。HttpServletRequest
参数表示当前的 HTTP 请求对象。
request.getSession().removeAttribute("employee");
这是方法的主要逻辑。request.getSession()
返回当前 HTTP 请求的 Session 对象。Session 是服务器端用于跟踪用户会话状态的一种机制,可以通过它来存储和获取会话信息。在这个方法中,通过调用 removeAttribute()
方法,删除了 Session 中名为 "employee"
的属性,即当前登录员工的 ID。这样就实现了员工退出登录的功能。
return R.success("退出成功");
最后,该方法返回一个 R
对象,表示退出操作成功,并且附带一条消息字符串。R
对象通常是自定义的响应对象,可以包含一些状态码、消息和数据等信息,用于表示请求的处理结果。在这个方法中,R.success()
方法返回一个成功状态的 R
对象,它包含了一条 "退出成功"
的消息。这个 R
对象将被 Spring MVC 自动转换为 JSON 格式的响应数据,返回给客户端。
国内镜像解释
具体解释如下:
- @PostMapping(“/logout”):映射到名为"logout"的 POST 请求。
- logout(HttpServletRequest request):方法名指定了请求的类型是 POST,并且参数是 HttpServletRequest request。
- request.getSession().removeAttribute(“employee”);:这句代码会清理 Session 中保存的当前登录员工的 id。其中,"employee"是保存在 Session 中的属性名称,用于保存员工的 ID。removeAttribute 方法用于从 Session 中移除指定属性。
- return R.success(“退出成功”);:返回一个 R 对象,其中 R 代表 result,success 代表成功状态,用于告诉客户端退出成功。在这里,使用了@ResponseBody 注解,可以将 R 对象转换为 JSON 格式返回给客户端。
在详细一点解释
当客户端发送一个 POST 请求到指定的 URL 时,Spring MVC 会检查请求体中是否有一个名为"employee"的属性,如果有,则会清理 Session 中保存的当前登录员工的 id。具体实现如下:
- 首先,通过 @PostMapping 注解映射到名为"logout"的 POST 请求。
- 然后,在 logout(HttpServletRequest request) 方法中,接收客户端发送的请求并检查请求体中是否有一个名为"employee"的属性。
- 如果请求体中存在"employee"属性,则调用 request.getSession().removeAttribute(“employee”); 来从 Session 中移除指定属性。
- 最后,返回一个 R 对象,其中 R 代表 result,success 代表成功状态,用于告诉客户端退出成功。在这里,使用了@ResponseBody 注解,可以将 R 对象转换为 JSON 格式返回给客户端。
这样,当客户端再次访问该 URL 时,会检测到 Session 已经失效,从而自动退出登录。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gzmlf09K-1688460704861)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091740567.png)]
然后我们在结合前端分析一下
因为我们这里面调用的是success方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJ1HZbKZ-1688460704862)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091740063.png)]
就会把code 改成1;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EBAdV1Lh-1688460704863)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304091740343.png)]
这样子的话 我们页面上就可以判断了 如果code === 1 就执行删除 localStorage
中的 userInfo
,并将浏览器的 location
对象设置为跳转到后台管理系统登录页面。
前端代码解释
logout() {
logoutApi().then((res)=>{
if(res.code === 1){
localStorage.removeItem('userInfo')
window.location.href = '/backend/page/login/login.html'
}
})
},
这段代码是一个名为 logout
的函数,该函数调用了一个名为 logoutApi
的异步函数,并且在 logoutApi
函数返回后执行了一个回调函数。回调函数检查了 logoutApi
的返回值 res
的 code
属性是否等于 1
,如果等于,则删除 localStorage
中的 userInfo
,并将浏览器的 location
对象设置为跳转到后台管理系统登录页面。
因此,可以猜测该代码用于实现用户登出功能,通过删除本地存储的用户信息并跳转到登录页面实现用户退出登录的操作。
5.3 功能测试
1). 代码实现完毕后, 重启服务, 访问登录界面 http://localhost:8080/backend/page/login/login.html ;
2). 登录完成之后, 进入到系统首页 backend/index.html, 点击右上角 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UG7yqCjp-1688460704864)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031404205.png)] 按钮 执行退出操作, 完成后看看是否可以跳转到登录页面 , 并检查localStorage。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UEUaJgT1-1688460704864)(https://cdn.jsdelivr.net/gh/zyziga/picodemo/takeaway/day01/202304031404990.png)]
ut"的 POST 请求。
- logout(HttpServletRequest request):方法名指定了请求的类型是 POST,并且参数是 HttpServletRequest request。
- request.getSession().removeAttribute(“employee”);:这句代码会清理 Session 中保存的当前登录员工的 id。其中,"employee"是保存在 Session 中的属性名称,用于保存员工的 ID。removeAttribute 方法用于从 Session 中移除指定属性。
- return R.success(“退出成功”);:返回一个 R 对象,其中 R 代表 result,success 代表成功状态,用于告诉客户端退出成功。在这里,使用了@ResponseBody 注解,可以将 R 对象转换为 JSON 格式返回给客户端。
在详细一点解释
当客户端发送一个 POST 请求到指定的 URL 时,Spring MVC 会检查请求体中是否有一个名为"employee"的属性,如果有,则会清理 Session 中保存的当前登录员工的 id。具体实现如下:
- 首先,通过 @PostMapping 注解映射到名为"logout"的 POST 请求。
- 然后,在 logout(HttpServletRequest request) 方法中,接收客户端发送的请求并检查请求体中是否有一个名为"employee"的属性。
- 如果请求体中存在"employee"属性,则调用 request.getSession().removeAttribute(“employee”); 来从 Session 中移除指定属性。
- 最后,返回一个 R 对象,其中 R 代表 result,success 代表成功状态,用于告诉客户端退出成功。在这里,使用了@ResponseBody 注解,可以将 R 对象转换为 JSON 格式返回给客户端。
这样,当客户端再次访问该 URL 时,会检测到 Session 已经失效,从而自动退出登录。
[外链图片转存中…(img-Gzmlf09K-1688460704861)]
然后我们在结合前端分析一下
[外链图片转存中…(img-kcIRDnmd-1688460704862)]
因为我们这里面调用的是success方法
[外链图片转存中…(img-dJ1HZbKZ-1688460704862)]
就会把code 改成1;
[外链图片转存中…(img-EBAdV1Lh-1688460704863)]
这样子的话 我们页面上就可以判断了 如果code === 1 就执行删除 localStorage
中的 userInfo
,并将浏览器的 location
对象设置为跳转到后台管理系统登录页面。
前端代码解释
logout() {
logoutApi().then((res)=>{
if(res.code === 1){
localStorage.removeItem('userInfo')
window.location.href = '/backend/page/login/login.html'
}
})
},
这段代码是一个名为 logout
的函数,该函数调用了一个名为 logoutApi
的异步函数,并且在 logoutApi
函数返回后执行了一个回调函数。回调函数检查了 logoutApi
的返回值 res
的 code
属性是否等于 1
,如果等于,则删除 localStorage
中的 userInfo
,并将浏览器的 location
对象设置为跳转到后台管理系统登录页面。
因此,可以猜测该代码用于实现用户登出功能,通过删除本地存储的用户信息并跳转到登录页面实现用户退出登录的操作。
5.3 功能测试
1). 代码实现完毕后, 重启服务, 访问登录界面 http://localhost:8080/backend/page/login/login.html ;
[外链图片转存中…(img-TECalq2y-1688460704863)]
2). 登录完成之后, 进入到系统首页 backend/index.html, 点击右上角 [外链图片转存中…(img-UG7yqCjp-1688460704864)] 按钮 执行退出操作, 完成后看看是否可以跳转到登录页面 , 并检查localStorage。
[外链图片转存中…(img-UEUaJgT1-1688460704864)]
[外链图片转存中…(img-kQJ3SEyo-1688460704865)]