1. 文件的上传下载 4-2
这里解释文件的上传时上传到我们后端指定的路径,下载时下到前端页面去
1.1 文件上传介绍 4-2
文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。
文件上传时,对页面的form表单有如下要求:
●method="post" 采用post方式提交数据
●enctype="multipart/ form-data" 采用multipart格式上传文件
●type="file" 使用input的file控件.上传
举例:
<form method="post" action=" /common/upload" enctype="multipart/form-data">
<input name="myFile" type="file" />
<input type-="submit" value="提交" />
</form>
1.1.1 服务端文件上传 4-2
服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
●commons-fileupload
● commons-io
Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如:
1.2 文件下载介绍 4-2
文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程。
通过浏览器进行文件下载,通常有两种表现形式:
● 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
●直接在浏览器中打开
通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。
2. 文件上传代码实现 4-3
文件上传,页面端可以使用ElementUl提供的上传组件。
可以直接使用资料中提供的上传页面,位置
E:\java学习\瑞吉外卖\资料\1 瑞吉外卖项目\资料\文件上传下载页面
启动项目测试访问一下(这是没有写Controller类呢)
http://localhost:8080/backend/page/demo/upload.html
完成功能
文件上传和下载的控制器类CommonController 4-3
package com.itheima.reggie.controller;
import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
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;
//专门负责我们的文件上传和下载的控制器类 4-3
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {
//文件上传 4-3
@PostMapping("/upload")
public R<String> upload(MultipartFile file){
//file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
log.info(file.toString());
return null;
}
}
这里测试要先登录,在去访问http://localhost:8080/backend/page/demo/upload.html
然后上传图片,才可使我们的file形参拿到图片资源(解释为什么,肯定有疑问不可以直接访问http://localhost:8080/backend/page/demo/upload.html吗,因为我们的demo/upload.html也是放在backend目录下的呀,而且backend目录还是被我们指定了静态资源路径映射的直接访问不就行了,其实这么理解是完全错误的,我们的目的是让我们的Controller类拿到图片资源(其实就是访问common/upload路径)而不是访问它(upload.html页面),应为我们设置过滤器的所以当我们访问上述路径添加图片时会访问common/upload路径(这个upload路径就是我们的Controller类upload方法上的,所以你要访问这个,就必须过过滤器这一关) 但是 这个路径在过滤器中会被拦截(因为只有过滤器中放行的路径可以通过,具体参考过滤器代码),所以我们就无法让我们的Controller类拿到图片资源
2.1 指定临时文件转存 4-4
因为我们去添加图片每次都要先登录,在访问路径,在添加巴拉巴拉,很麻烦,所以重点我们直接在过滤器中设置放行common/upload路径(再次强调我们的目的是让Controller拿到图片资源,而不是访问它所以肯定要放行/common/**路径)
//定义不需要处理的请求路径 2-3
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**",
"/common/**"
};
application.properties
#配置文件下载的路径 4-4
reggie:
path: E:\java\ruijiwaimai_download_picture_temp\
测试随机生成文件名 在test中测试
UploadFileTest
package com.itheima.test;
import org.junit.jupiter.api.Test;
public class UploadFileTest {
//测试随机生成的文件名 4-4
@Test
public void test1(){
String fileName = "ererewe.jpg";
String suffix = fileName.substring(fileName.lastIndexOf("."));
System.out.println(suffix);
}
}
没问题
继续完善文件上传功能CommonController 4-4
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.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;
//专门负责我们的文件上传和下载的控制器类 4-3
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {
//动态注入文件下载路径 4-4
@Value("${reggie.path}")
private String basePath;
//文件上传 4-3
@PostMapping("/upload")
public R<String> upload(MultipartFile file){
//file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
log.info(file.toString());
//原始文件名
String originalFilename = file.getOriginalFilename();//xx.jpg
//截取原始文件名后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//使用UUID随机重新生成文件名,防止文件名称重复造成文件覆盖
String fileName = UUID.randomUUID().toString()+suffix;
//创建一个目录对象 4-4
File dir = new File(basePath);
//判断当前目录是否存在
if(!dir.exists()){
//目录不存在,需要创建
dir.mkdirs();
}
try {
//将临时文件转存到指定位置 4-4
file.transferTo(new File(basePath + originalFilename));
} catch (IOException e) {
e.printStackTrace();
}
return R.success(fileName);
}
}
访问http://localhost:8080/backend/page/demo/upload.html添加图片 临时文件转存成功
3. 文件的下载 4-5
温馨提示,输入流是读文件将一个文件读到一个数组中,输出流是写文件将文件从一个数组写到浏览器
//文件下载 4-5
@GetMapping("/download")
public void download(String name, HttpServletResponse response){//这里这个name我们是从上传方法中返回给前端的
try {
//输入流,通过输入流读取到文件内容
//basePath + name组合起来的是我们保存文件的路径
FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
//输出流,通过输出流将文件写回浏览器,在浏览器展示图片了
ServletOutputStream outputStream = response.getOutputStream();
//设置响应给浏览器是什么类型的资源,这里我们设置成图片资源
response.setContentType("image/jpeg");
int len = 0;
byte[] bytes = new byte[1024];//定义一个数组
//使用输入流来读文件读到bytes数组中,只要不得-1就一直读,为-1就代表读完了
while ((len = fileInputStream.read(bytes)) != -1){
//受用输出流来写到浏览器,从bytes数组中来写
outputStream.write(bytes,0,len);
outputStream.flush();//刷新一下
}
//关闭资源
outputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
4. 新增菜品 4-6
4.1 需求分析 4-6
后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。
4.2 数据模型 4-6
新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_ flavor表插入数据。
所以在新增菜品时,涉及到两个表:
●dish 菜品表
●dish_ flavor 菜品口味表
dish表
dish_ flavor表
4.3 代码开发 4-7
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类DishFlavor (直接从课程资料中导入即可,Dish实体前面课程中已经导入过了)
●Mapper接口 DishFlavorMapper
业务层接口DishFlavorService
●业务层实现类DishFlavorServicelmpl
●控制层DishController
菜品口味DishFlavor
package com.itheima.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
菜品口味 4-7
*/
@Data
public class DishFlavor implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//菜品id
private Long dishId;
//口味名称
private String name;
//口味数据list
private String value;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
//是否删除
private Integer isDeleted;
}
菜品口味 持久层接口 DishFlavorMapper
package com.itheima.reggie.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;
//菜品口味 持久层接口 4-7
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}
菜品口味 业务层接口DishFlavorService
package com.itheima.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.DishFlavor;
//菜品口味 业务层接口 4-7
public interface DishFlavorService extends IService<DishFlavor> {
}
菜品口味 业务层接口实现类DishFlavorServiceImpl
package com.itheima.reggie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.mapper.DishFlavorMapper;
import com.itheima.reggie.service.DishFlavorService;
import org.springframework.stereotype.Service;
//菜品口味 业务层接口实现类 4-7
@Service
public class DishFlavorServiceImpl extends ServiceImpl<DishFlavorMapper,DishFlavor> implements DishFlavorService {
}
4.4 新增菜品功能开发 4-7
代码开发梳理交互过程
在开发代码之前,需要梳理一下新增菜品时前端页面和服务端的交互过程:
1.页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
2、页面发送请求进行图片上传,请求服务端将图片保存到服务器
3、页面发送请求进行图片下载,将上传的图片进行回显
4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端
开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可。
第一步 4-8
1.页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
CategoryController 根据条件查询分类数据 4-8
/**
* 根据条件查询分类数据 4-8
* @param category
* @return
*/
@GetMapping("/list")
public R<List<Category>> list(Category category){
//条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
//添加条件
queryWrapper.eq(category.getType() != null,Category::getType,category.getType());
//添加排序条件
queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);
List<Category> list = categoryService.list(queryWrapper);
return R.success(list);
}
第二三步
2、页面发送请求进行图片上传,请求服务端将图片保存到服务器
3、页面发送请求进行图片下载,将上传的图片进行回显
我们已经在上面实现了
第四步 4-10
4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端
因为上图可知前端传来的数据既有dish表的还有dish_flavors表的,所以我们不能简单的只用dish实体来接收,需要导入DTO
导入DTO 资料路径E:\java学习\瑞吉外卖\资料\1 瑞吉外卖项目\资料\dto
数据传输对象 DishDto
package com.itheima.reggie.dto;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
//数据传输对象DTO 4-9
@Data
public class DishDto extends Dish {
private List<DishFlavor> flavors = new ArrayList<>();
private String categoryName;
private Integer copies;
}
debug,发现口味表中的dishId并没有赋值只是name和value赋值了,这个重点,在我们将数保存到数据库时要注意
菜品管理业务层接口实现类 DishServiceImpl
我们在DishServiceImpl业务层中自己实现数据保存(因为涉及两张表操作框架无法为我们提供合适的方法)
package com.itheima.reggie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.mapper.DishMapper;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
//菜品管理业务层接口实现类 3-8
@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService {
@Autowired
private DishFlavorService dishFlavorService;
//新增菜品,同时保存对应的口味数据 4-10
@Transactional //控制事务 4-10
public void saveWithFlavor(DishDto dishDto) {
//保存菜品的基本信息到菜品表dish
this.save(dishDto);
//解释为什么还要设置菜品id,因为我们在保存菜品口味表dish_flavor时,是没有保存菜品id的
//原因在于,我们用的是数据传输对象DTO而DishDto没有做保存dishId(在我们通过debug时
// 发现的)所以我们需要对DishDto中封装的Flavors做一些改动,首先遍历
// 我们通过dishDto.getFlavors()得到DishDto中封装的Flavors集合,然后取出集合中的
//元素(也就是Flavors对象),给他们的dishId赋值,结束后在在将其封装成list集合
// (因为我们改了原来集合,需要将改变了dishId属性的Flavors对象也添加进list集合)
//获取菜品id
Long dishId = dishDto.getId();
//得到集合
List<DishFlavor> flavors = dishDto.getFlavors();
//遍历集合
//.stream()是将集合转化为流,.map是将流中的元素计算或者转换(此处用map是为流中的
// dishId赋值) .collect是将流转化为集合
flavors = flavors.stream().map((item)->{
item.setDishId(dishId);
return item;
}).collect(Collectors.toList());//.collect(Collectors.toList())改成list集合
//保存菜品口味数据到菜品口味表dish_flavor
dishFlavorService.saveBatch(flavors);
}
}
是事务起作用
菜品管理 DishController
package com.itheima.reggie.controller;
import com.itheima.reggie.common.R;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
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;
//菜品管理 4-7
@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private DishFlavorService dishFlavorService;
/**
* 新增菜品 4-9 4-10
* @param dishDto
* @return
*/
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
log.info(dishDto.toString());
//调用我们自己在DishServiceImpl实现的方法 4-11
//我们在DishServiceImpl业务层中自己实现数据保存(因为涉及两张
// 表操作框架无法为我们提供合适的方法)
dishService.saveWithFlavor(dishDto);
return R.success("新增菜品成功");
}
}
测试成功 4-11
菜品表dish
口味表dish_flavor
5. 菜品信息分页查询 4-12
5.1 需求分析 4-12
5.2 代码开发-梳理交互过程 4-13
在开发代码之前,需要梳理一下菜品分页查询时前端页面和服务端的交互过程:
1、页面(backend/ page/food/list.html)发送ajax请求,将分页查询参数(page、pageSize、 name)提交到服务端,获取分页数据
2、页面发送请求,请求服务端进行图片下载,用于页面图片展示
开发菜品信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
//菜品分页查询 4-13
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);
//构造分页构造器
//泛型只简单的使用Dish是不行的,因为没办法把菜品分类名称展示出来,所以还需要数据传输对象DTO 4-14
Page<Dish> pageInfo = new Page<>(page, pageSize);
//构造条件构造器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
//添加过滤条件
queryWrapper.like(name!=null,Dish::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Dish::getUpdateTime);
//执行分页查询
//这条语句会将我们查询到的数据进行封装 全部封装进Page的对象pageInfo
dishService.page(pageInfo,queryWrapper);
return R.success(dishDtoPage);
}
前端需要的数据
后端响应的数据
小提示 (这块相对复杂不明白就看视频) 4-14
由结果可以看出,我们的菜品分类名称并没有展示出来,这是为什么呢?因为前端需要的是菜品分 类name属性,而我们后端给他传过去的是菜品分类的id,原因就在于我们用的是dish作为泛型来接收(Page dishDtoPage = new Page<>();
重点的我们的目的就是得到一个泛型为DishDto的Page对象即dishDtoPage
图解
代码实现 4-14
首先声明注入CategoryService属性
DishController
//菜品分页查询 4-13
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);
//构造分页构造器
//泛型只简单的使用Dish是不行的,因为没办法把菜品分类名称展示出来,所以还需要数据传输对象DTO 4-14
Page<Dish> pageInfo = new Page<>(page, pageSize);
Page<DishDto> dishDtoPage = new Page<>();
//构造条件构造器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
//添加过滤条件
queryWrapper.like(name!=null,Dish::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Dish::getUpdateTime);
//执行分页查询
//这条语句会将我们查询到的数据进行封装 全部封装进Page的对象pageInfo
dishService.page(pageInfo,queryWrapper);
/*
解释
由结果可以看出,我们的菜品分类名称并没有展示出来,这是为什么呢?因为前端需要的是菜品分
类name属性,而我们后端给他传过去的是菜品分类的id,原因就在于我们用的是dish作为泛型来
接收(Page<Dish> pageInfo = new Page<>(page, pageSize);),而dish里面没
有name属性,但是dishdto里面由categoryName属性,所以我们在
写一个Page<DishDto> dishDtoPage = new Page<>();
重点的我们的目的就是得到一个泛型为DishDto的Page对象即dishDtoPage
需要将pageInfo拷贝给dishDtoPage但是但是不拷贝"records" 属性 因为这个records对
应的泛型是dish而我们需要的泛型是dishDto records集合
(集合里的每一个对象都是dish表的一条数据)在拿到records集合后 遍历 records集合—>stream流->map(取出集合数据)
将item对象(item就是records的一条记录)赋值给新new的dishDto对象,利用item的到分类id
根据id查询分类对象进而取出name属性值,将name设置给给dishDto的categoryName属性
最后再将流转换为DishDto泛型的list集合,再将带有categoryName属性的list集合设置
给dishDtoPage对象的records属性
*/
//对象(都是Page对象)拷贝 但是不拷贝"records" 属性 因为这个records对应的泛型是dish
// 而我们需要的泛型是dishDto 4-14
BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");
//得到records集合
List<Dish> records = pageInfo.getRecords();
//使用 流 遍历集合
List<DishDto> list = records.stream().map((item) -> {
DishDto dishDto = new DishDto();
//因为上面new的DishDto的属性都是空值,需要将item对象赋值给dishDto
BeanUtils.copyProperties(item,dishDto);
Long categoryId = item.getCategoryId();//分类id
//根据id查询分类对象
Category category = categoryService.getById(categoryId);
if(category != null){
//得到分类名称
String categoryName = category.getName();
//给dishDto的categoryName属性设置值
dishDto.setCategoryName(categoryName);
}
return dishDto;
}).collect(Collectors.toList());//将dishDto对象收集起来编程list集合
//经过上述操作,就得到了泛型为DishDto的list集合,而DishDto里还有categoryName属性
//再将带有categoryName属性的list集合设置给dishDtoPage对象的records属性
dishDtoPage.setRecords(list);
return R.success(dishDtoPage);
}
6. 修改菜品 4-16
6.1 需求分析 4-16
6.2 代码开发 4-16 4-17
代码开发-梳理交互过程
在开发代码之前,需要梳理一下修改菜品时前端页面(add.html) 和服务端的交互过程:
1、页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示
2、页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
3、页面发送请求,请求服务端进行图片下载,用于页图片回显
4、点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端
开发修改菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可。
根据菜品id查询数据
2、页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
菜品管理业务层接口DishService
//根据菜品id来查询菜品信息和口味信息 4-17
public DishDto getByIdWithFlavor(Long id);
业务层接口实现类DishServiceImpl
/**
* 根据菜品id查询菜品信息和对应的口味信息 4-17
* @param id
* @return
*/
public DishDto getByIdWithFlavor(Long id) {
//查询菜品基本信息,从dish表查询
Dish dish = this.getById(id);
//对象拷贝,这里拷贝的是普通属性,不包括flavor
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(dish,dishDto);
//DishFlavor byId = dishFlavorService.getById(id);
//这里解释为什么不能直接用这行代码查口味表数据,因为这个id是菜品id不是口味表id,这样查查不到的
//所有要用下面的方法构造查询条件,查询条件就是口味对应的菜品id
//查询当前菜品对应的口味信息,从dish_flavor表查询
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,dish.getId());//getDishId菜品id
List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
//这里将flavor设置进去
dishDto.setFlavors(flavors);
return dishDto;
}
菜品管理 DishController
//修改菜品 之回显数据 4-17
//根据菜品id来查询菜品信息和口味信息
@GetMapping("/{id}")
public R<DishDto> get(@PathVariable Long id){
//这里使用我们自己的方法,因为DishDto中包含了dish_flavor表
DishDto dishDto = dishService.getByIdWithFlavor(id);
return R.success(dishDto);
}
测试回显成功
4、点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端
更新菜品 保存修改 4-19
菜品管理业务层接口DishService
//更新菜品信息 同时更新口味信息 4-19
public void updateWithFlavor(DishDto dishDto);
业务层接口实现类DishServiceImpl
//修改菜品 4-19
@PutMapping
public R<String> update(@RequestBody DishDto dishDto){
log.info(dishDto.toString());
dishService.updateWithFlavor(dishDto);
return R.success("新增菜品成功");
}
菜品管理 DishController
//更新菜品信息 同时更新口味信息 4-19
@Override
@Transactional
public void updateWithFlavor(DishDto dishDto) {
//更新dish表基本信息
this.updateById(dishDto);
//这里解释为什么不能直接修改口味表,因为一个菜品可能对应多个口味而且,修改后的也可能是修改多个口味
// ,直接修改不知道修改哪一条,更不知道修改的哪条对应数据库的哪条记录,所以直接删除在添加
//清理当前菜品对应口味数据---dish_flavor表的delete操作
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
dishFlavorService.remove(queryWrapper);
//添加当前提交过来的口味数据---dish_flavor表的insert操作
List<DishFlavor> flavors = dishDto.getFlavors();
//这里和添加一样的问题 继续为口味表添加菜品id
flavors = flavors.stream().map((item) -> {
item.setDishId(dishDto.getId());
return item;
}).collect(Collectors.toList());
dishFlavorService.saveBatch(flavors);
}
测试成功
7. 菜品售卖状态 自己实现
停售 起售(起售传来的请求几乎和停售一样,只是 0-->1 也就是状态值的差异而已)
批量停售 批量起售 (批量起售传来的请求几乎和批量停售一样,只是 0-->1 也就是状态值的差异而已)
由上图分析可知,四种功能的请求路径一样,且都使用了restful风格,因此可以封装成一个代码实现
DishController
//修改菜品售卖状态 ,自己实现的 可以批量可以单个
@PostMapping("/status/{status}")
public R<String> stop(@PathVariable int status,@RequestParam List<Long> ids){
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(Dish::getId,ids);//先构造id(可能一个也可能多个,因为我们单独停售起售和批量写一起了)
//在构造售卖状态
Dish dish = new Dish();
dish.setStatus(status);
dishService.update(dish,queryWrapper);
return R.success("菜品售卖状态修改成功");
}
测试成功
8. 删除和批量删除 自己实现
删除一个
批量删除
由上图可知,删除和批量删除请求路径是一样的,所以可以封装成一个方法
我们设置处在售卖状态的菜品不能删除
菜品管理业务层接口DishService
//删除菜品同时删除和菜品关联的口味表数据 自己实现
public void removeWithDish(List<Long> ids);
菜品管理业务层接口实现类DishServiceImpl
//删除菜品同时删除和菜品关联的口味表数据 自己实现
@Override
public void removeWithDish(List<Long> ids) {
//查询菜品状态看看是否处于售卖状态,看看是否可以删除
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(Dish::getId,ids);
queryWrapper.eq(Dish::getStatus,1);
//这里的思想是根据id和status查询,如果查询出来的数据大于0,就证明处于售卖状态,否则为停售状态
int count = this.count(queryWrapper);
if(count > 0){
//如果不能删除,抛出一个业务异常
throw new CustomException("菜品正在售卖中,不能删除");
}
//如果可以删除,先删除菜品表中的数据---dish
this.removeByIds(ids);
//接着删除口味表章关联的数据
LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishFlavorLambdaQueryWrapper.in(DishFlavor::getDishId,ids);
dishFlavorService.remove(dishFlavorLambdaQueryWrapper);
}
DishController
//删除菜品,自己实现 可以批量可以单个
@DeleteMapping
public R<String> delete(@RequestParam List<Long> ids){
//调用我们自己的方法,因为删除操作涉及了两张表菜品表和口味表
dishService.removeWithDish(ids);
return R.success("删除菜品成功");
}
测试成功
有菜品在售卖不能删除
标签:dishDto,业务,开发,菜品,dish,import,com,id From: https://blog.51cto.com/u_15784725/6508345