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();@Overridepublic 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 获取请求的uriString 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来获得,而不是自己newtry {//输入流,通过输入流读取文件内容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主从复制实现主从之间数据同步