day2 员工管理
完善登录
问题:用户不登录,直接访问系统首页,照样可以正常访问。我们希望,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面
怎么实现? 用过滤器或拦截器,在过滤器或拦截器中判断用户是否已完成登录,如果没有则跳转到登录页面
package com.itheima.reggie.filter;
import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 检查用户是否已经完成登录
*/
@Slf4j
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
//路径匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
/**
* 1. 获取请求的uri
* 2. 判断本次请求是否需要处理,处理指检查用户登录状态
* 3. 若不需要处理,则直接放行
* 4. 判断登录状态,若已登录,则直接放行
* 5. 如果未登录,则返回未登录结果
*/
1 获取请求的uri
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}",requestURI);
//2判断本次请求是否需要处理
//定义不需要处理的请求路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**", //** 为通配符,表示backend下面的所有文件
"/front/**"
};
//检查本次请求是否需要放行
boolean check = check(requestURI, urls);
//3.如果不需要处理,则直接放行
if (check){
log.info("本次请求{}不需要处理",requestURI);
filterChain.doFilter(request, response);
return;
}
//4.判断登录状态,若已登录,则直接放行
if (request.getSession().getAttribute("employee") != null){
log.info("用户已登录,用户id为{}",request.getSession().getAttribute("employee"));
filterChain.doFilter(request, response);
return;
}
//5.如果未登录,则返回未登录结果。通过输出流方式向客户端页面响应数据
log.info("用户未登录");
String notlogin = JSON.toJSONString(R.error("NOTLOGIN"));
response.getWriter().write(notlogin);
return;
}
/**
* 路径匹配,检查本次请求是否需要放行
* @param requestURI
* @param urls
* @return
*/
public boolean check(String requestURI,String[] urls){
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if (match){
return true;
}
}
return false;
}
}
新增员工
![image-20231010110313372](https://gitee.com/zwx0203/cloudimage/raw/master/202310101103478.png
![image-20231010111521959](https://gitee.com/zwx0203/cloudimage/raw/master/202310101115148.png
全局异常捕获
员工信息分页查询
启用/禁用员工账号
需求分析
代码开发
对于后端,只需更改employee表的status属性
js对Long型数据处理丢失精度
解决方案:服务端给页面响应json数据时,将long型数据统一转为String字符串,如下
怎么做?在配置类中扩展Spring mvc消息转换器
编辑员工信息
day3 分类管理
公共字段自动填充
在实体类的属性上加入@TableField注解
分类 分页查询
删除分类
当分类关联了菜品或者套餐时,此分类不允许删除
删除分类功能完善--------当分类关联了菜品或者套餐时,此分类不允许删除
修改分类
day4 菜品管理
文件上传下载
文件上传介绍
后端文件上传要求
文件下载介绍
文件上传与下载代码
import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.UUID;
/**
* 文件上传和下载
*/
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${reggie.path}")
private String bashPath;
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
//MultipartFile 参数名必须跟前端的Form Data中的name保存一致,而不能乱取名字
public R<String> upload(MultipartFile file){
//file是一个临时文件,需要转存到指定位置,因为本次请求完成后临时文件会自动删除
//将临时文件转存到指定位置,
//原始文件名
String originalFilename = file.getOriginalFilename();
//原始文件后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// 直接用上传文件的原名,不建议这么做,因为可能有重名的,则重名的后面会覆盖前面的
//file.transferTo(new File(bashPath+ originalFilename));
// 为了防止覆盖问题,我们使用uuid来重新生成文件名, 但需要把原始文件名的后缀拿过来
String filename = UUID.randomUUID().toString() + suffix;//dfsdfdfd.jpg
//创建一个目录对象
File dir = new File(bashPath);
//判断当前目录是否存在
if (!dir.exists()){
//目录不存在,需要创建
dir.mkdirs();
}
try {
file.transferTo(new File(bashPath + filename));
} catch (IOException e) {
throw new RuntimeException(e);
}
log.info("上传的文件被命名为:{}",filename.toString());
return R.success(filename);
}
/**
* 文件下载
* @param name
* @param response
*/
@GetMapping("/download")
public void download(String name, HttpServletResponse response){
//为什么需要HttpServletResponse?
//答:要将图片展示在前端页面上,输出流需通过response来获得,而不是自己new
try {
//输入流,通过输入流读取文件内容
FileInputStream fileInputStream = new FileInputStream(new File(bashPath + name));
//输出流,通过输出流将文件写回浏览器,在浏览器展示图片
ServletOutputStream outputStream = response.getOutputStream();
//设置响应回去的是图片类型的文件 "image/jpeg"表示图片类型
response.setContentType("image/jpeg");
int len = 0;
byte[] bytes = new byte[1024];
//fileInputStream.read(bytes) 表示 通过输入流来读,将读到的数据存放到bytes数组中
//正常返回读了多少数据到bytes数组,返回-1表示输入流读完了,跳出循环
while ( (len = fileInputStream.read(bytes)) != -1){
//当fileInputStream.read(bytes)返回的值不为-1,说明还有数据,一直读
outputStream.write(bytes,0,len);
outputStream.flush();
}
//关闭资源
outputStream.close();
fileInputStream.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
新增菜品
需求分析
导入DTO:Data Transfer Object,即数据传输对象
一般用于展示层(前端页面)与服务层(后端)之间的消息传输
菜品信息分页查询
特殊点:页面发送请求,请求服务端下载图片,用于页面图片展示
修改菜品
day5 套餐管理
一个套餐可能包含:主食,菜,饮品
新增套餐
setmeal_dish 套餐菜品关系表
假如一个套餐对应3个菜品,则在setmeal_dish表中就有三条记录
day6 菜品展示、购物车、下单(移动端)
用户地址相关代码
需求分析
数据模型
导入功能代码
菜品展示
需求分析:用户登录成功后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息,需要展示“选择规格”按钮,否则显示“+”按钮
购物车
同一用户购物车数据在表中有多条
购物车交互过程
用户下单
数据模型:用户下单业务对应的数据表为orders 订单表和order_detail 订单明细表
day7 缓存优化
当用户数量多,系统访问量大,频繁访问数据库,系统性能会下降,用户体验差
缓存短信验证码
之前短信验证码是存在session中,而session中存放的有效期为30分钟,而一般的短信验证码有效期设置为5分钟
缓存菜品数据
当数据库表中更新菜品数据后,要将redis中相关的菜品数据删除,否则会导致缓存中的数据和数据库的数据不一致(脏数据)
Spring Cache
springcache 通过注解的方式实现缓存等功能,可以大大减少缓存操作的代码
SpringCache 常用注解
@CachePut 一般用于更新缓存数据
@CacheEvict 清理指定缓存 适合放在删除,新增上面,当数据库删除一条消息后,将对应的缓存也删除
@Cacheable 适合放在查询接口上面
使用Redis为SpringCache底层实现
<!--spring data redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring data cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
使用spring cache来缓存套餐数据
6、返回类 要实现序列化接口
day 8 读写分离
读:查
写:增删改
主从模式
写的是主库,读的是从库,通过Mysql主从复制实现主从之间数据同步