目录
将 upload.html 放在 backend/page/demo 目录下,上传图片的接口如下
① 将文件上传和下载页面取消检查登录:在 LoginCheckFilter 中将对应路径加入免查数组
- // 定义不需要处理的请求路径
- String[] urls = new String[]{
- "/employee/login", // 登录请求
- "/employee/logout", // 登出请求
- "/backend/**", // 前端资源
- "/front/**", // 前端资源
- "/common/**" // 文件上传和下载
- //"/employee/page" // 前端资源
- };
② 在 application.yml 中设置文件存放位置
- reggie:
- path: C:\Users\zhang\Desktop\2\
③ 创建 CommonController 处理文件上传请求
- package com.itheima.reggie.controller;
-
- import com.itheima.reggie.common.R;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Controller;
- 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 java.io.File;
- import java.io.IOException;
- import java.util.UUID;
-
- /**
- * @Author zhang
- * @Date 2022/9/2 - 21:54
- * @Version 1.0
- */
- // 文件上传和下载
- @RestController
- @Slf4j
- @RequestMapping("/common")
- public class CommonController {
-
- @Value("${reggie.path}")
- private String basePath;
-
- /**
- * 文件上传
- * @param file 这个参数名字必须与前端保持一直
- * @return
- */
- @PostMapping("/upload")
- public R
upload(MultipartFile file){ - //log.info(file.toString());
- // file是一个临时文件,需要转存到指定位置,否则本次请求完后临时文件会删除
- // 原始文件名
- String originalFilename = file.getOriginalFilename();
- // 获取原始文件的后缀名
- String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
- // 使用UUID生成文件名
- String fileName = UUID.randomUUID().toString() + suffix;
- // 创建一个目录对象
- File dir = new File(basePath);
- if(!dir.exists()){
- // 目录不存在,需要创建
- dir.mkdirs();
- }
- try {
- // 将临时文件转存到指定位置
- file.transferTo(new File(basePath + fileName));
- } catch (IOException e) {
- e.printStackTrace();
- }
- return R.success(fileName);
- }
-
- }
前端在文件上传成功后,通过 handleAvatarSuccess 方法使自定义的参数 imageUrl 存储服务器保存图片的路径,再通过 imageUrl 发送请求下载文件
- /**
- * 文件下载
- * @param name
- * @param response
- */
- @GetMapping("/download")
- public void download(
- String name,
- HttpServletResponse response
- ){
- try {
- // 通过输入流读取文件内容
- FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
- // 通过输出流将文件写回浏览器
- ServletOutputStream outputStream = response.getOutputStream();
- response.setContentType("image/jpeg"); // 设置文件格式
- byte[] bytes = new byte[1024];
- int len = 0;
- while((len = fileInputStream.read(bytes)) != -1){
- outputStream.write(bytes, 0, len);
- outputStream.flush();
- }
- // 关闭资源
- outputStream.close();
- fileInputStream.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
dish 表
dish_flavor 表
获取菜品分类数据:
图片上传与下载与上面使用的接口相同,所以已经实现
保存菜品信息:
在 created 生命周期,先通过 getDishList 方法获取菜品分类信息并存储到自定义数据 dishList
在 CategoryController 中添加方法
- /**
- * 根据条件查询分类数据
- * @param category
- * @return
- */
- @GetMapping("list")
- public R
> list(Category category){
- LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(category.getType() != null, Category::getType, category.getType());
- queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);
- List
categoryList = categoryService.list(queryWrapper); - return R.success(categoryList);
- }
DTO,全称为 Data Transfer Object,即数据传输对象,一般用于展示层与服务层之间的数据传输。
由于保存菜品信息时,页面发送了 flavors 信息,而在 Dish 中没有该属性,无法匹配,所以导入 DishDto(位置:资料/dto) ,用于封装页面提交的数据
- @Data
- public class DishDto extends Dish {
-
- private List
flavors = new ArrayList<>(); -
- private String categoryName;
-
- private Integer copies;
- }
页面发来的数据不仅有菜品信息,还有对应的口味信息
① 在 DishSerivce 中添加同时存储两种信息的方法
- public interface DishSerivce extends IService
{ -
- // 新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish、dish_flavor
- public void saveWithFlavor(DishDto dishDto);
-
- }
② 在 DishServiceImpl 添加实现方法
- @Service
- public class DishServiceImpl extends ServiceImpl
implements DishSerivce { -
- @Autowired
- private DishFlavorService dishFlavorService;
-
- /**
- * 新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish、dish_flavor
- * @param dishDto
- */
- @Transactional
- @Override
- public void saveWithFlavor(DishDto dishDto) {
- // 保存菜品的基本信息
- this.save(dishDto);
-
- Long dishId = dishDto.getId(); // 菜品id
- List
flavors = dishDto.getFlavors(); // 菜品口味 - flavors.stream().map((item) -> {
- item.setDishId(dishId);
- return item;
- }).collect(Collectors.toList());
-
- // 保存菜品口味信息到dish_flavor
- dishFlavorService.saveBatch(flavors);
-
- }
-
- }
③ 在启动类添加注解开启事务
@EnableTransactionManagement // 开启事务
④ 在 DishController 中添加方法
- @RestController
- @Slf4j
- @RequestMapping("/dish")
- public class DishController {
-
- @Autowired
- private DishSerivce dishSerivce;
-
- @Autowired
- private DishFlavorService dishFlavorService;
-
- /**
- * 新增菜品
- * @param dishDto
- * @return
- */
- @PostMapping
- public R
save(@RequestBody DishDto dishDto){ - //log.info(dishDto.toString());
- dishSerivce.saveWithFlavor(dishDto);
- return R.success("新增菜品成功");
- }
-
- }
分页获取菜品信息请求:
- /**
- * 菜品信息分页查询
- * @param page
- * @param pageSize
- * @param name
- * @return
- */
- @GetMapping("/page")
- public R
page( - int page,
- int pageSize,
- String name
- ){
- // 分页构造器对象
- Page
pageInfo = new Page<>(page, pageSize); - Page
dishDtoPage = new Page<>(); -
- // 添加条件
- LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.like(name != null && !"".equals(name), Dish::getName, name);
- queryWrapper.orderByDesc(Dish::getUpdateTime);
-
- // 分页查询
- dishSerivce.page(pageInfo, queryWrapper);
-
- // 对象拷贝,不拷贝records属性
- BeanUtils.copyProperties(pageInfo, dishDtoPage, "records");
-
- // 将对应的分类名称填入
- List
records = pageInfo.getRecords(); - List
list = records.stream().map((item) -> { - DishDto dishDto = new DishDto();
- BeanUtils.copyProperties(item, dishDto);
- Long categoryId = item.getCategoryId(); // 分类id
- Category category = categoryService.getById(categoryId);
- if(category != null) {
- String categoryName = category.getName();
- dishDto.setCategoryName(categoryName);
- }
- return dishDto;
- }).collect(Collectors.toList());
- dishDtoPage.setRecords(list);
-
- return R.success(dishDtoPage);
- }
根据 id 查询当前菜品信息的请求:
保存修改后的信息的请求:
菜品信息的回显需要获取菜品的基本信息及其口味
① 在 DishSerivce 添加方法
- // 根据id查询菜品信息及其口味信息
- public DishDto getByIdWithFlavor(Long id);
② 在 DishServiceImpl 实现方法
- /**
- * 根据id查询菜品信息及其口味信息
- * @param id
- * @return
- */
- @Override
- public DishDto getByIdWithFlavor(Long id) {
- DishDto dishDto = new DishDto();
-
- // 查询菜品基本信息
- Dish dish = this.getById(id);
-
- // 查询菜品对应的口味信息
- LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(DishFlavor::getDishId, id);
- List
flavors = dishFlavorService.list(queryWrapper); -
- // 将查询到的数据封装
- BeanUtils.copyProperties(dish, dishDto);
- dishDto.setFlavors(flavors);
-
- return dishDto;
- }
③ 在 DishController 中使用方法
- /**
- * 根据id查询菜品信息及其口味信息
- * @param id
- * @return
- */
- @GetMapping("/{id}")
- public R
get(@PathVariable Long id){ - DishDto dishDto = dishSerivce.getByIdWithFlavor(id);
- return R.success(dishDto);
- }
① 在 DishSerivce 添加方法
- // 更新菜品,同时更新对应的口味信息
- public void updateWithFlavor(DishDto dishDto);
② 在 DishServiceImpl 实现方法
- /**
- * 更新菜品,同时更新对应的口味信息
- * @param dishDto
- */
- @Override
- @Transactional
- public void updateWithFlavor(DishDto dishDto) {
- // 更新dish菜品表信息
- this.updateById(dishDto);
-
- // 清理当前菜品对应的口味
- LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(DishFlavor::getDishId, dishDto.getCategoryId());
- dishFlavorService.remove(queryWrapper);
-
- // 添加当前提交过来的口味信息
- List
flavors = dishDto.getFlavors(); - flavors = flavors.stream().map((item) -> {
- item.setDishId(dishDto.getCategoryId());
- return item;
- }).collect(Collectors.toList());
- dishFlavorService.saveBatch(flavors);
- }
③ 在 DishController 中使用方法
- /**
- * 修改菜品
- * @return
- */
- @PutMapping
- public R
update(@RequestBody DishDto dishDto){ - dishSerivce.updateWithFlavor(dishDto);
- return R.success("修改菜品成功");
- }
首先查看单个停售或起售的请求
再看一下批量的请求
可以将他们写为一个方法
在 DishController 添加方法
- @PostMapping("/status/{status}")
- public R
updateStatus( - @PathVariable("status") int status,
- @RequestParam List
ids - ){
- List
dishes = new ArrayList<>(); - for (Long id : ids) {
- Dish dish = new Dish();
- dish.setStatus(status);
- dish.setId(id);
- dishes.add(dish);
- }
- dishSerivce.updateBatchById(dishes, dishes.size());
- return R.success("菜品状态修改成功");
- }
首先看一下单个删除的请求
再来看一下批量删除的请求
可以看到与修改售卖状态的请求大致相同。
流程:
1、获取请求中要删除的 id,并获取他们的基本信息
2、逐个判断他们的售卖状态,若在启售状态,则无法删除
由于 Dish 和 DishFlavor 中有逻辑删除的字段,所以这里使用逻辑删除。
在 Dish 和 DishFlavor 类的 isDeleted 字段加上 @TableLogic 注解
- //是否删除
- @TableLogic // 逻辑删除
- private Integer isDeleted;